Spaces:
Sleeping
Sleeping
GitHub Actions commited on
Commit Β·
da64db2
1
Parent(s): 727aa6e
π Auto-deploy from GitHub
Browse files- app/api/v1/endpoints/download.py +3 -1
- app/api/v1/endpoints/generate.py +3 -1
- app/api/v1/endpoints/health.py +16 -3
- app/core/auth.py +63 -0
- curl_test_commands.md +0 -121
- static/images/generated/375677ce-67f8-4afc-aeae-7b993441b2a5.png +0 -0
- static/images/generated/7b7c33a0-fdb1-44ff-8642-64b6adca011c.png +0 -0
- static/images/qr/c01a736a-0618-44cf-ae9e-65189c0bc752.png +0 -0
- static/images/qr/d5983203-7dce-4d8b-97c1-fe40517dace5.png +0 -0
- tests/test_authentication.py +183 -0
app/api/v1/endpoints/download.py
CHANGED
|
@@ -3,6 +3,7 @@ from fastapi.responses import FileResponse
|
|
| 3 |
from pathlib import Path
|
| 4 |
from ....services.database import get_supabase_client # Corrected import
|
| 5 |
from ....core.config import settings # Import settings
|
|
|
|
| 6 |
from supabase import Client
|
| 7 |
|
| 8 |
router = APIRouter()
|
|
@@ -10,7 +11,8 @@ router = APIRouter()
|
|
| 10 |
@router.get("/download/image/{card_id}") # Changed to path parameter
|
| 11 |
async def download_generated_image(
|
| 12 |
card_id: str, # card_id from path
|
| 13 |
-
supabase: Client = Depends(get_supabase_client)
|
|
|
|
| 14 |
):
|
| 15 |
"""
|
| 16 |
Download a custom generated image using the card_id to find the image path from Supabase.
|
|
|
|
| 3 |
from pathlib import Path
|
| 4 |
from ....services.database import get_supabase_client # Corrected import
|
| 5 |
from ....core.config import settings # Import settings
|
| 6 |
+
from ....core.auth import verify_hf_token
|
| 7 |
from supabase import Client
|
| 8 |
|
| 9 |
router = APIRouter()
|
|
|
|
| 11 |
@router.get("/download/image/{card_id}") # Changed to path parameter
|
| 12 |
async def download_generated_image(
|
| 13 |
card_id: str, # card_id from path
|
| 14 |
+
supabase: Client = Depends(get_supabase_client),
|
| 15 |
+
authenticated: bool = Depends(verify_hf_token)
|
| 16 |
):
|
| 17 |
"""
|
| 18 |
Download a custom generated image using the card_id to find the image path from Supabase.
|
app/api/v1/endpoints/generate.py
CHANGED
|
@@ -10,6 +10,7 @@ from ....services.database import get_supabase_client, save_card
|
|
| 10 |
from ....core.config import settings
|
| 11 |
from ....core.model_loader import get_generator
|
| 12 |
from ....core.constraints import generate_with_retry, check_constraints
|
|
|
|
| 13 |
from fastapi.concurrency import run_in_threadpool # Importieren
|
| 14 |
|
| 15 |
load_dotenv()
|
|
@@ -26,7 +27,8 @@ async def generate_qr_code_async(*args, **kwargs):
|
|
| 26 |
@router.post("/generate", response_model=CardGenerateResponse)
|
| 27 |
async def generate_endpoint(
|
| 28 |
request: CardGenerateRequest,
|
| 29 |
-
supabase: Client = Depends(get_supabase_client)
|
|
|
|
| 30 |
):
|
| 31 |
try:
|
| 32 |
lang = request.lang or "de"
|
|
|
|
| 10 |
from ....core.config import settings
|
| 11 |
from ....core.model_loader import get_generator
|
| 12 |
from ....core.constraints import generate_with_retry, check_constraints
|
| 13 |
+
from ....core.auth import verify_hf_token
|
| 14 |
from fastapi.concurrency import run_in_threadpool # Importieren
|
| 15 |
|
| 16 |
load_dotenv()
|
|
|
|
| 27 |
@router.post("/generate", response_model=CardGenerateResponse)
|
| 28 |
async def generate_endpoint(
|
| 29 |
request: CardGenerateRequest,
|
| 30 |
+
supabase: Client = Depends(get_supabase_client),
|
| 31 |
+
authenticated: bool = Depends(verify_hf_token)
|
| 32 |
):
|
| 33 |
try:
|
| 34 |
lang = request.lang or "de"
|
app/api/v1/endpoints/health.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException
|
| 2 |
from ..schemas.health_schema import HealthResponse
|
|
|
|
| 3 |
import httpx
|
| 4 |
import asyncio
|
| 5 |
from typing import Optional
|
|
@@ -25,7 +26,7 @@ async def check_huggingface_space():
|
|
| 25 |
return "unreachable"
|
| 26 |
|
| 27 |
@router.get("/health", response_model=HealthResponse)
|
| 28 |
-
async def health_check():
|
| 29 |
"""
|
| 30 |
Health check endpoint that verifies server status, model loading status and HuggingFace space availability
|
| 31 |
"""
|
|
@@ -51,4 +52,16 @@ async def health_check():
|
|
| 51 |
huggingface_space_url=HF_SPACE_URL
|
| 52 |
)
|
| 53 |
except Exception as e:
|
| 54 |
-
raise HTTPException(status_code=500, detail=f"Health check failed: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, HTTPException, Depends
|
| 2 |
from ..schemas.health_schema import HealthResponse
|
| 3 |
+
from ....core.auth import verify_hf_token
|
| 4 |
import httpx
|
| 5 |
import asyncio
|
| 6 |
from typing import Optional
|
|
|
|
| 26 |
return "unreachable"
|
| 27 |
|
| 28 |
@router.get("/health", response_model=HealthResponse)
|
| 29 |
+
async def health_check(authenticated: bool = Depends(verify_hf_token)):
|
| 30 |
"""
|
| 31 |
Health check endpoint that verifies server status, model loading status and HuggingFace space availability
|
| 32 |
"""
|
|
|
|
| 52 |
huggingface_space_url=HF_SPACE_URL
|
| 53 |
)
|
| 54 |
except Exception as e:
|
| 55 |
+
raise HTTPException(status_code=500, detail=f"Health check failed: {str(e)}")
|
| 56 |
+
|
| 57 |
+
@router.get("/health/public")
|
| 58 |
+
async def public_health_check():
|
| 59 |
+
"""
|
| 60 |
+
Public health check endpoint (no authentication required)
|
| 61 |
+
Returns basic server status only
|
| 62 |
+
"""
|
| 63 |
+
return {
|
| 64 |
+
"status": "healthy",
|
| 65 |
+
"server": "running",
|
| 66 |
+
"message": "Server is operational"
|
| 67 |
+
}
|
app/core/auth.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Authentication middleware for HuggingFace API token validation
|
| 3 |
+
"""
|
| 4 |
+
from fastapi import HTTPException, status, Depends
|
| 5 |
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
| 6 |
+
from typing import Optional
|
| 7 |
+
import os
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
+
|
| 10 |
+
load_dotenv()
|
| 11 |
+
|
| 12 |
+
security = HTTPBearer(auto_error=False)
|
| 13 |
+
|
| 14 |
+
def get_hf_api_key() -> str:
|
| 15 |
+
"""Get HuggingFace API key from environment"""
|
| 16 |
+
return os.getenv("HF_API_KEY", "")
|
| 17 |
+
|
| 18 |
+
def verify_hf_token(credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)) -> bool:
|
| 19 |
+
"""
|
| 20 |
+
Verify HuggingFace API token
|
| 21 |
+
Returns True if no authentication is required (public space) or if token is valid
|
| 22 |
+
"""
|
| 23 |
+
expected_token = get_hf_api_key()
|
| 24 |
+
|
| 25 |
+
# If no HF_API_KEY is set, allow public access
|
| 26 |
+
if not expected_token:
|
| 27 |
+
return True
|
| 28 |
+
|
| 29 |
+
# If HF_API_KEY is set but no credentials provided, deny access
|
| 30 |
+
if not credentials:
|
| 31 |
+
raise HTTPException(
|
| 32 |
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
| 33 |
+
detail="Authentication required. Please provide a valid HuggingFace API token.",
|
| 34 |
+
headers={"WWW-Authenticate": "Bearer"},
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
# Verify the token matches
|
| 38 |
+
if credentials.credentials != expected_token:
|
| 39 |
+
raise HTTPException(
|
| 40 |
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
| 41 |
+
detail="Invalid authentication token",
|
| 42 |
+
headers={"WWW-Authenticate": "Bearer"},
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
return True
|
| 46 |
+
|
| 47 |
+
def optional_auth(credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)) -> bool:
|
| 48 |
+
"""
|
| 49 |
+
Optional authentication - doesn't raise errors if no token provided
|
| 50 |
+
Used for endpoints that can work with or without authentication
|
| 51 |
+
"""
|
| 52 |
+
expected_token = get_hf_api_key()
|
| 53 |
+
|
| 54 |
+
# If no HF_API_KEY is set, allow public access
|
| 55 |
+
if not expected_token:
|
| 56 |
+
return True
|
| 57 |
+
|
| 58 |
+
# If no credentials provided, still allow access (optional auth)
|
| 59 |
+
if not credentials:
|
| 60 |
+
return False
|
| 61 |
+
|
| 62 |
+
# If credentials provided, verify them
|
| 63 |
+
return credentials.credentials == expected_token
|
curl_test_commands.md
DELETED
|
@@ -1,121 +0,0 @@
|
|
| 1 |
-
# Direct curl commands for testing the /generate endpoint
|
| 2 |
-
|
| 3 |
-
## Standard test (German):
|
| 4 |
-
curl -X POST "http://localhost:8000/api/v1/generate" \
|
| 5 |
-
-H "Content-Type: application/json" \
|
| 6 |
-
-H "Accept: application/json" \
|
| 7 |
-
-d '{
|
| 8 |
-
"terms": ["Liebe", "Erfolg", "GlΓΌck", "Herausforderung", "Wachstum"],
|
| 9 |
-
"card_date": "1990-05-15",
|
| 10 |
-
"lang": "de",
|
| 11 |
-
"card_design_id_override": 1,
|
| 12 |
-
"symbol_ids_override": [1, 2]
|
| 13 |
-
}'
|
| 14 |
-
|
| 15 |
-
## English test:
|
| 16 |
-
curl -X POST "http://localhost:7860/api/v1/generate" \
|
| 17 |
-
-H "Content-Type: application/json" \
|
| 18 |
-
-H "Accept: application/json" \
|
| 19 |
-
-d '{
|
| 20 |
-
"terms": ["Love", "Success", "Happiness", "Challenge", "Growth"],
|
| 21 |
-
"card_date": "1985-12-25",
|
| 22 |
-
"lang": "en",
|
| 23 |
-
"card_design_id_override": 2,
|
| 24 |
-
"symbol_ids_override": [3, 4, 5]
|
| 25 |
-
}'
|
| 26 |
-
|
| 27 |
-
## Minimal test (only required fields):
|
| 28 |
-
curl -X POST "http://localhost:7860/api/v1/generate" \
|
| 29 |
-
-H "Content-Type: application/json" \
|
| 30 |
-
-d '{
|
| 31 |
-
"terms": ["Test1", "Test2", "Test3", "Test4", "Test5"],
|
| 32 |
-
"card_date": "2000-01-01"
|
| 33 |
-
}'
|
| 34 |
-
|
| 35 |
-
## Pretty printed JSON response:
|
| 36 |
-
curl -X POST "http://localhost:7860/api/v1/generate" \
|
| 37 |
-
-H "Content-Type: application/json" \
|
| 38 |
-
-d '{
|
| 39 |
-
"terms": ["Mut", "Hoffnung", "Freude", "Inspiration", "KreativitΓ€t"],
|
| 40 |
-
"card_date": "1995-07-20",
|
| 41 |
-
"lang": "de"
|
| 42 |
-
}' | jq .
|
| 43 |
-
|
| 44 |
-
# HUGGING FACE SPACE TESTS
|
| 45 |
-
# HOST_URL: https://ch404-cardserver.hf.space/
|
| 46 |
-
|
| 47 |
-
## Health Check (Hugging Face):
|
| 48 |
-
curl -X GET "https://ch404-cardserver.hf.space/api/v1/health" \
|
| 49 |
-
-H "Accept: application/json"
|
| 50 |
-
|
| 51 |
-
## Standard test (German) - Hugging Face:
|
| 52 |
-
curl -X POST "https://ch404-cardserver.hf.space/api/v1/generate" \
|
| 53 |
-
-H "Content-Type: application/json" \
|
| 54 |
-
-H "Accept: application/json" \
|
| 55 |
-
-d '{
|
| 56 |
-
"terms": ["Liebe", "Erfolg", "GlΓΌck", "Herausforderung", "Wachstum"],
|
| 57 |
-
"card_date": "1990-05-15",
|
| 58 |
-
"lang": "de",
|
| 59 |
-
"card_design_id_override": 1,
|
| 60 |
-
"symbol_ids_override": [1, 2]
|
| 61 |
-
}'
|
| 62 |
-
|
| 63 |
-
## English test - Hugging Face:
|
| 64 |
-
curl -X POST "https://ch404-cardserver.hf.space/api/v1/generate" \
|
| 65 |
-
-H "Content-Type: application/json" \
|
| 66 |
-
-H "Accept: application/json" \
|
| 67 |
-
-d '{
|
| 68 |
-
"terms": ["Love", "Success", "Happiness", "Challenge", "Growth"],
|
| 69 |
-
"card_date": "1985-12-25",
|
| 70 |
-
"lang": "en",
|
| 71 |
-
"card_design_id_override": 2,
|
| 72 |
-
"symbol_ids_override": [3, 4, 5]
|
| 73 |
-
}'
|
| 74 |
-
|
| 75 |
-
## Minimal test (only required fields) - Hugging Face:
|
| 76 |
-
curl -X POST "https://ch404-cardserver.hf.space/api/v1/generate" \
|
| 77 |
-
-H "Content-Type: application/json" \
|
| 78 |
-
-d '{
|
| 79 |
-
"terms": ["Test1", "Test2", "Test3", "Test4", "Test5"],
|
| 80 |
-
"card_date": "2000-01-01"
|
| 81 |
-
}'
|
| 82 |
-
|
| 83 |
-
## Pretty printed JSON response - Hugging Face:
|
| 84 |
-
curl -X POST "https://ch404-cardserver.hf.space/api/v1/generate" \
|
| 85 |
-
-H "Content-Type: application/json" \
|
| 86 |
-
-d '{
|
| 87 |
-
"terms": ["Mut", "Hoffnung", "Freude", "Inspiration", "KreativitΓ€t"],
|
| 88 |
-
"card_date": "1995-07-20",
|
| 89 |
-
"lang": "de"
|
| 90 |
-
}' | jq .
|
| 91 |
-
|
| 92 |
-
## Debug: Raw response without jq - Hugging Face:
|
| 93 |
-
curl -X POST "https://ch404-cardserver.hf.space/api/v1/generate" \
|
| 94 |
-
-H "Content-Type: application/json" \
|
| 95 |
-
-d '{
|
| 96 |
-
"terms": ["Mut", "Hoffnung", "Freude", "Inspiration", "KreativitΓ€t"],
|
| 97 |
-
"card_date": "1995-07-20",
|
| 98 |
-
"lang": "de"
|
| 99 |
-
}' -v
|
| 100 |
-
|
| 101 |
-
## Debug: Check if service is running - Hugging Face:
|
| 102 |
-
curl -X GET "https://ch404-cardserver.hf.space/" -v
|
| 103 |
-
|
| 104 |
-
## Debug: Check specific health endpoint - Hugging Face:
|
| 105 |
-
curl -X GET "https://ch404-cardserver.hf.space/api/v1/health" -v
|
| 106 |
-
|
| 107 |
-
## Debug: Test different port (7860) - Hugging Face:
|
| 108 |
-
curl -X GET "https://ch404-cardserver.hf.space:7860/api/v1/health" -v
|
| 109 |
-
|
| 110 |
-
## Debug: Check docs endpoint - Hugging Face:
|
| 111 |
-
curl -X GET "https://ch404-cardserver.hf.space/docs" -v
|
| 112 |
-
|
| 113 |
-
## Download test - Hugging Face:
|
| 114 |
-
curl -X GET "https://ch404-cardserver.hf.space/api/v1/download/YOUR_CARD_ID" \
|
| 115 |
-
-H "Accept: image/png" \
|
| 116 |
-
--output card_from_hf.png
|
| 117 |
-
|
| 118 |
-
## Status check with verbose output - Hugging Face:
|
| 119 |
-
curl -X GET "https://ch404-cardserver.hf.space/api/v1/health" \
|
| 120 |
-
-H "Accept: application/json" \
|
| 121 |
-
-v
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static/images/generated/375677ce-67f8-4afc-aeae-7b993441b2a5.png
ADDED
|
static/images/generated/7b7c33a0-fdb1-44ff-8642-64b6adca011c.png
ADDED
|
static/images/qr/c01a736a-0618-44cf-ae9e-65189c0bc752.png
ADDED
|
static/images/qr/d5983203-7dce-4d8b-97c1-fe40517dace5.png
ADDED
|
tests/test_authentication.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test script to verify bearer token authentication for all endpoints
|
| 4 |
+
"""
|
| 5 |
+
import requests
|
| 6 |
+
import os
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
|
| 9 |
+
load_dotenv()
|
| 10 |
+
|
| 11 |
+
# Configuration
|
| 12 |
+
BASE_URL = "http://127.0.0.1:8000" # Change to your deployment URL when testing live
|
| 13 |
+
HF_API_KEY = os.getenv("HF_API_KEY")
|
| 14 |
+
|
| 15 |
+
def test_endpoint(endpoint, method="GET", headers=None, json_data=None):
|
| 16 |
+
"""Test an endpoint and return the response"""
|
| 17 |
+
url = f"{BASE_URL}{endpoint}"
|
| 18 |
+
try:
|
| 19 |
+
if method == "GET":
|
| 20 |
+
response = requests.get(url, headers=headers)
|
| 21 |
+
elif method == "POST":
|
| 22 |
+
response = requests.post(url, headers=headers, json=json_data)
|
| 23 |
+
|
| 24 |
+
return {
|
| 25 |
+
"status_code": response.status_code,
|
| 26 |
+
"success": response.status_code < 400,
|
| 27 |
+
"response": response.json() if response.headers.get("content-type", "").startswith("application/json") else response.text
|
| 28 |
+
}
|
| 29 |
+
except Exception as e:
|
| 30 |
+
return {
|
| 31 |
+
"status_code": None,
|
| 32 |
+
"success": False,
|
| 33 |
+
"error": str(e)
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
def main():
|
| 37 |
+
"""Run authentication tests"""
|
| 38 |
+
print("π Testing Bearer Token Authentication")
|
| 39 |
+
print("=" * 50)
|
| 40 |
+
|
| 41 |
+
if not HF_API_KEY:
|
| 42 |
+
print("β HF_API_KEY not found in environment variables")
|
| 43 |
+
return
|
| 44 |
+
|
| 45 |
+
# Test headers
|
| 46 |
+
auth_headers = {
|
| 47 |
+
"Authorization": f"Bearer {HF_API_KEY}",
|
| 48 |
+
"Content-Type": "application/json"
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
no_auth_headers = {
|
| 52 |
+
"Content-Type": "application/json"
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
invalid_auth_headers = {
|
| 56 |
+
"Authorization": "Bearer invalid_token_123",
|
| 57 |
+
"Content-Type": "application/json"
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
tests = [
|
| 61 |
+
# Public endpoint (should work without auth)
|
| 62 |
+
{
|
| 63 |
+
"name": "Public Health Check (No Auth)",
|
| 64 |
+
"endpoint": "/api/v1/health/public",
|
| 65 |
+
"method": "GET",
|
| 66 |
+
"headers": no_auth_headers,
|
| 67 |
+
"should_succeed": True
|
| 68 |
+
},
|
| 69 |
+
|
| 70 |
+
# Protected endpoints without auth (should fail)
|
| 71 |
+
{
|
| 72 |
+
"name": "Protected Health Check (No Auth)",
|
| 73 |
+
"endpoint": "/api/v1/health",
|
| 74 |
+
"method": "GET",
|
| 75 |
+
"headers": no_auth_headers,
|
| 76 |
+
"should_succeed": False
|
| 77 |
+
},
|
| 78 |
+
|
| 79 |
+
# Protected endpoints with invalid auth (should fail)
|
| 80 |
+
{
|
| 81 |
+
"name": "Protected Health Check (Invalid Auth)",
|
| 82 |
+
"endpoint": "/api/v1/health",
|
| 83 |
+
"method": "GET",
|
| 84 |
+
"headers": invalid_auth_headers,
|
| 85 |
+
"should_succeed": False
|
| 86 |
+
},
|
| 87 |
+
|
| 88 |
+
# Protected endpoints with valid auth (should succeed)
|
| 89 |
+
{
|
| 90 |
+
"name": "Protected Health Check (Valid Auth)",
|
| 91 |
+
"endpoint": "/api/v1/health",
|
| 92 |
+
"method": "GET",
|
| 93 |
+
"headers": auth_headers,
|
| 94 |
+
"should_succeed": True
|
| 95 |
+
},
|
| 96 |
+
|
| 97 |
+
# Test generate endpoint with auth
|
| 98 |
+
{
|
| 99 |
+
"name": "Generate Endpoint (Valid Auth)",
|
| 100 |
+
"endpoint": "/api/v1/generate",
|
| 101 |
+
"method": "POST",
|
| 102 |
+
"headers": auth_headers,
|
| 103 |
+
"json_data": {
|
| 104 |
+
"terms": ["test", "card"],
|
| 105 |
+
"card_date": "2024-01-01",
|
| 106 |
+
"lang": "en"
|
| 107 |
+
},
|
| 108 |
+
"should_succeed": True
|
| 109 |
+
},
|
| 110 |
+
|
| 111 |
+
# Test generate endpoint without auth
|
| 112 |
+
{
|
| 113 |
+
"name": "Generate Endpoint (No Auth)",
|
| 114 |
+
"endpoint": "/api/v1/generate",
|
| 115 |
+
"method": "POST",
|
| 116 |
+
"headers": no_auth_headers,
|
| 117 |
+
"json_data": {
|
| 118 |
+
"terms": ["test", "card"],
|
| 119 |
+
"card_date": "2024-01-01",
|
| 120 |
+
"lang": "en"
|
| 121 |
+
},
|
| 122 |
+
"should_succeed": False
|
| 123 |
+
}
|
| 124 |
+
]
|
| 125 |
+
|
| 126 |
+
results = []
|
| 127 |
+
for test in tests:
|
| 128 |
+
print(f"\nπ§ͺ Testing: {test['name']}")
|
| 129 |
+
|
| 130 |
+
result = test_endpoint(
|
| 131 |
+
test["endpoint"],
|
| 132 |
+
test["method"],
|
| 133 |
+
test["headers"],
|
| 134 |
+
test.get("json_data")
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
expected_success = test["should_succeed"]
|
| 138 |
+
actual_success = result["success"]
|
| 139 |
+
|
| 140 |
+
if expected_success == actual_success:
|
| 141 |
+
status = "β
PASS"
|
| 142 |
+
else:
|
| 143 |
+
status = "β FAIL"
|
| 144 |
+
|
| 145 |
+
print(f" {status} - Status: {result['status_code']}")
|
| 146 |
+
|
| 147 |
+
if not result["success"] and "error" in result:
|
| 148 |
+
print(f" Error: {result['error']}")
|
| 149 |
+
elif "response" in result:
|
| 150 |
+
# Print first few lines of response for debugging
|
| 151 |
+
response_str = str(result["response"])
|
| 152 |
+
if len(response_str) > 100:
|
| 153 |
+
response_str = response_str[:100] + "..."
|
| 154 |
+
print(f" Response: {response_str}")
|
| 155 |
+
|
| 156 |
+
results.append({
|
| 157 |
+
"test": test["name"],
|
| 158 |
+
"passed": expected_success == actual_success,
|
| 159 |
+
"status_code": result["status_code"]
|
| 160 |
+
})
|
| 161 |
+
|
| 162 |
+
# Summary
|
| 163 |
+
print("\n" + "=" * 50)
|
| 164 |
+
print("π Test Summary")
|
| 165 |
+
print("=" * 50)
|
| 166 |
+
|
| 167 |
+
passed = sum(1 for r in results if r["passed"])
|
| 168 |
+
total = len(results)
|
| 169 |
+
|
| 170 |
+
print(f"β
Passed: {passed}/{total}")
|
| 171 |
+
print(f"β Failed: {total - passed}/{total}")
|
| 172 |
+
|
| 173 |
+
if passed == total:
|
| 174 |
+
print("\nπ All authentication tests passed!")
|
| 175 |
+
else:
|
| 176 |
+
print("\nβ οΈ Some tests failed. Check the output above.")
|
| 177 |
+
print("\nFailed tests:")
|
| 178 |
+
for result in results:
|
| 179 |
+
if not result["passed"]:
|
| 180 |
+
print(f" - {result['test']}")
|
| 181 |
+
|
| 182 |
+
if __name__ == "__main__":
|
| 183 |
+
main()
|