Commit
·
5f4a2fe
1
Parent(s):
11d45f5
Add Bearer token authentication to all API endpoints
Browse files- Added HTTPBearer authentication with password: logicgo_videoswap@153
- All API endpoints now require Authorization header with Bearer token
- Test endpoint /api/test remains public for verification
- Password can be set via API_PASSWORD environment variable
- api_server.py +60 -13
api_server.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
-
from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 3 |
from pydantic import BaseModel
|
| 4 |
from typing import Optional, List
|
| 5 |
import os
|
|
@@ -22,6 +23,20 @@ from DeepFakeAI.processors.frame.modules import face_swapper as DF_FS
|
|
| 22 |
|
| 23 |
app = FastAPI(title="Face Swap Video API", version="1.0.0")
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
# CORS middleware
|
| 26 |
app.add_middleware(
|
| 27 |
CORSMiddleware,
|
|
@@ -232,7 +247,10 @@ async def process_face_swap(job_id: str, source_image_path: str, target_video_pa
|
|
| 232 |
# API Endpoints
|
| 233 |
|
| 234 |
@app.post("/api/source-image", response_model=SourceImageResponse)
|
| 235 |
-
async def upload_source_image(
|
|
|
|
|
|
|
|
|
|
| 236 |
"""Upload and store source image in MongoDB"""
|
| 237 |
if not file.content_type.startswith('image/'):
|
| 238 |
raise HTTPException(status_code=400, detail="File must be an image")
|
|
@@ -265,7 +283,10 @@ async def upload_source_image(file: UploadFile = File(...)):
|
|
| 265 |
raise HTTPException(status_code=500, detail=f"Error uploading source image: {str(e)}")
|
| 266 |
|
| 267 |
@app.post("/api/target-video", response_model=TargetVideoResponse)
|
| 268 |
-
async def upload_target_video(
|
|
|
|
|
|
|
|
|
|
| 269 |
"""Upload and store target video in MongoDB"""
|
| 270 |
if not file.content_type.startswith('video/'):
|
| 271 |
raise HTTPException(status_code=400, detail="File must be a video")
|
|
@@ -298,7 +319,11 @@ async def upload_target_video(file: UploadFile = File(...)):
|
|
| 298 |
raise HTTPException(status_code=500, detail=f"Error uploading target video: {str(e)}")
|
| 299 |
|
| 300 |
@app.post("/api/face-swap", response_model=JobStatus)
|
| 301 |
-
async def start_face_swap(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
"""Start face swap processing"""
|
| 303 |
try:
|
| 304 |
# Get source image and target video from MongoDB
|
|
@@ -341,7 +366,7 @@ async def start_face_swap(request: FaceSwapRequest, background_tasks: Background
|
|
| 341 |
raise HTTPException(status_code=500, detail=f"Error starting face swap: {str(e)}")
|
| 342 |
|
| 343 |
@app.get("/api/job/{job_id}", response_model=JobStatus)
|
| 344 |
-
async def get_job_status(job_id: str):
|
| 345 |
"""Get job status"""
|
| 346 |
job = await jobs_collection.find_one({"job_id": job_id})
|
| 347 |
if not job:
|
|
@@ -361,7 +386,7 @@ async def get_job_status(job_id: str):
|
|
| 361 |
)
|
| 362 |
|
| 363 |
@app.get("/api/result-video/{result_video_id}")
|
| 364 |
-
async def get_result_video(result_video_id: str):
|
| 365 |
"""Get result video file"""
|
| 366 |
result = await result_videos_collection.find_one({"_id": ObjectId(result_video_id)})
|
| 367 |
if not result:
|
|
@@ -377,7 +402,7 @@ async def get_result_video(result_video_id: str):
|
|
| 377 |
)
|
| 378 |
|
| 379 |
@app.get("/api/source-images", response_model=List[SourceImageResponse])
|
| 380 |
-
async def list_source_images():
|
| 381 |
"""List all source images"""
|
| 382 |
cursor = source_images_collection.find().sort("uploaded_at", -1)
|
| 383 |
images = []
|
|
@@ -392,7 +417,7 @@ async def list_source_images():
|
|
| 392 |
return images
|
| 393 |
|
| 394 |
@app.get("/api/target-videos", response_model=List[TargetVideoResponse])
|
| 395 |
-
async def list_target_videos():
|
| 396 |
"""List all target videos"""
|
| 397 |
cursor = target_videos_collection.find().sort("uploaded_at", -1)
|
| 398 |
videos = []
|
|
@@ -407,7 +432,7 @@ async def list_target_videos():
|
|
| 407 |
return videos
|
| 408 |
|
| 409 |
@app.get("/api/result-videos", response_model=List[ResultVideoResponse])
|
| 410 |
-
async def list_result_videos():
|
| 411 |
"""List all result videos"""
|
| 412 |
cursor = result_videos_collection.find().sort("created_at", -1)
|
| 413 |
results = []
|
|
@@ -424,8 +449,8 @@ async def list_result_videos():
|
|
| 424 |
return results
|
| 425 |
|
| 426 |
@app.get("/api/health")
|
| 427 |
-
async def api_health():
|
| 428 |
-
"""Health check endpoint with GPU status"""
|
| 429 |
import onnxruntime
|
| 430 |
available_providers = onnxruntime.get_available_providers()
|
| 431 |
gpu_available = 'CUDAExecutionProvider' in available_providers
|
|
@@ -439,8 +464,30 @@ async def api_health():
|
|
| 439 |
|
| 440 |
@app.get("/")
|
| 441 |
async def root():
|
| 442 |
-
"""
|
| 443 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 444 |
|
| 445 |
if __name__ == "__main__":
|
| 446 |
import uvicorn
|
|
|
|
| 1 |
+
from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks, Depends, Security
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
| 4 |
from pydantic import BaseModel
|
| 5 |
from typing import Optional, List
|
| 6 |
import os
|
|
|
|
| 23 |
|
| 24 |
app = FastAPI(title="Face Swap Video API", version="1.0.0")
|
| 25 |
|
| 26 |
+
# Authentication
|
| 27 |
+
API_PASSWORD = os.getenv("API_PASSWORD", "logicgo_videoswap@153")
|
| 28 |
+
security = HTTPBearer()
|
| 29 |
+
|
| 30 |
+
def verify_api_key(credentials: HTTPAuthorizationCredentials = Security(security)):
|
| 31 |
+
"""Verify API key from Bearer token"""
|
| 32 |
+
if credentials.credentials != API_PASSWORD:
|
| 33 |
+
raise HTTPException(
|
| 34 |
+
status_code=401,
|
| 35 |
+
detail="Invalid authentication credentials",
|
| 36 |
+
headers={"WWW-Authenticate": "Bearer"},
|
| 37 |
+
)
|
| 38 |
+
return credentials.credentials
|
| 39 |
+
|
| 40 |
# CORS middleware
|
| 41 |
app.add_middleware(
|
| 42 |
CORSMiddleware,
|
|
|
|
| 247 |
# API Endpoints
|
| 248 |
|
| 249 |
@app.post("/api/source-image", response_model=SourceImageResponse)
|
| 250 |
+
async def upload_source_image(
|
| 251 |
+
file: UploadFile = File(...),
|
| 252 |
+
api_key: str = Depends(verify_api_key)
|
| 253 |
+
):
|
| 254 |
"""Upload and store source image in MongoDB"""
|
| 255 |
if not file.content_type.startswith('image/'):
|
| 256 |
raise HTTPException(status_code=400, detail="File must be an image")
|
|
|
|
| 283 |
raise HTTPException(status_code=500, detail=f"Error uploading source image: {str(e)}")
|
| 284 |
|
| 285 |
@app.post("/api/target-video", response_model=TargetVideoResponse)
|
| 286 |
+
async def upload_target_video(
|
| 287 |
+
file: UploadFile = File(...),
|
| 288 |
+
api_key: str = Depends(verify_api_key)
|
| 289 |
+
):
|
| 290 |
"""Upload and store target video in MongoDB"""
|
| 291 |
if not file.content_type.startswith('video/'):
|
| 292 |
raise HTTPException(status_code=400, detail="File must be a video")
|
|
|
|
| 319 |
raise HTTPException(status_code=500, detail=f"Error uploading target video: {str(e)}")
|
| 320 |
|
| 321 |
@app.post("/api/face-swap", response_model=JobStatus)
|
| 322 |
+
async def start_face_swap(
|
| 323 |
+
request: FaceSwapRequest,
|
| 324 |
+
background_tasks: BackgroundTasks,
|
| 325 |
+
api_key: str = Depends(verify_api_key)
|
| 326 |
+
):
|
| 327 |
"""Start face swap processing"""
|
| 328 |
try:
|
| 329 |
# Get source image and target video from MongoDB
|
|
|
|
| 366 |
raise HTTPException(status_code=500, detail=f"Error starting face swap: {str(e)}")
|
| 367 |
|
| 368 |
@app.get("/api/job/{job_id}", response_model=JobStatus)
|
| 369 |
+
async def get_job_status(job_id: str, api_key: str = Depends(verify_api_key)):
|
| 370 |
"""Get job status"""
|
| 371 |
job = await jobs_collection.find_one({"job_id": job_id})
|
| 372 |
if not job:
|
|
|
|
| 386 |
)
|
| 387 |
|
| 388 |
@app.get("/api/result-video/{result_video_id}")
|
| 389 |
+
async def get_result_video(result_video_id: str, api_key: str = Depends(verify_api_key)):
|
| 390 |
"""Get result video file"""
|
| 391 |
result = await result_videos_collection.find_one({"_id": ObjectId(result_video_id)})
|
| 392 |
if not result:
|
|
|
|
| 402 |
)
|
| 403 |
|
| 404 |
@app.get("/api/source-images", response_model=List[SourceImageResponse])
|
| 405 |
+
async def list_source_images(api_key: str = Depends(verify_api_key)):
|
| 406 |
"""List all source images"""
|
| 407 |
cursor = source_images_collection.find().sort("uploaded_at", -1)
|
| 408 |
images = []
|
|
|
|
| 417 |
return images
|
| 418 |
|
| 419 |
@app.get("/api/target-videos", response_model=List[TargetVideoResponse])
|
| 420 |
+
async def list_target_videos(api_key: str = Depends(verify_api_key)):
|
| 421 |
"""List all target videos"""
|
| 422 |
cursor = target_videos_collection.find().sort("uploaded_at", -1)
|
| 423 |
videos = []
|
|
|
|
| 432 |
return videos
|
| 433 |
|
| 434 |
@app.get("/api/result-videos", response_model=List[ResultVideoResponse])
|
| 435 |
+
async def list_result_videos(api_key: str = Depends(verify_api_key)):
|
| 436 |
"""List all result videos"""
|
| 437 |
cursor = result_videos_collection.find().sort("created_at", -1)
|
| 438 |
results = []
|
|
|
|
| 449 |
return results
|
| 450 |
|
| 451 |
@app.get("/api/health")
|
| 452 |
+
async def api_health(api_key: str = Depends(verify_api_key)):
|
| 453 |
+
"""Health check endpoint with GPU status (requires authentication)"""
|
| 454 |
import onnxruntime
|
| 455 |
available_providers = onnxruntime.get_available_providers()
|
| 456 |
gpu_available = 'CUDAExecutionProvider' in available_providers
|
|
|
|
| 464 |
|
| 465 |
@app.get("/")
|
| 466 |
async def root():
|
| 467 |
+
"""Root endpoint - shows API is running"""
|
| 468 |
+
return {
|
| 469 |
+
"message": "Face Swap Video API is running",
|
| 470 |
+
"version": "1.0.0",
|
| 471 |
+
"docs": "/docs",
|
| 472 |
+
"health": "/api/health"
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
# Test endpoint to verify API is accessible (no auth required for testing)
|
| 477 |
+
@app.get("/api/test")
|
| 478 |
+
async def test_endpoint():
|
| 479 |
+
"""Test endpoint to verify API is running (public endpoint)"""
|
| 480 |
+
return {
|
| 481 |
+
"status": "ok",
|
| 482 |
+
"message": "API is accessible",
|
| 483 |
+
"authentication": "Bearer token required for all endpoints except /api/test",
|
| 484 |
+
"endpoints": {
|
| 485 |
+
"upload_source": "POST /api/source-image",
|
| 486 |
+
"upload_target": "POST /api/target-video",
|
| 487 |
+
"face_swap": "POST /api/face-swap",
|
| 488 |
+
"health": "GET /api/health"
|
| 489 |
+
}
|
| 490 |
+
}
|
| 491 |
|
| 492 |
if __name__ == "__main__":
|
| 493 |
import uvicorn
|