Compare commits

..

1 Commits

Author SHA1 Message Date
ي
11966cf341 Adjust missing API-key logging in injection middleware 2026-03-31 07:33:42 +05:30
5 changed files with 68 additions and 45 deletions

View File

@@ -203,10 +203,7 @@ async def create_audio_dubbing_task(
""" """
user_id = require_authenticated_user(current_user) user_id = require_authenticated_user(current_user)
task_id = task_manager.create_task( task_id = task_manager.create_task("audio_dubbing")
"audio_dubbing",
metadata={"owner_user_id": user_id},
)
background_tasks.add_task( background_tasks.add_task(
_execute_dubbing_task, _execute_dubbing_task,
@@ -243,7 +240,7 @@ async def get_dubbing_result(
""" """
user_id = require_authenticated_user(current_user) user_id = require_authenticated_user(current_user)
task_status = task_manager.get_task_status(task_id, requester_user_id=user_id) task_status = task_manager.get_task_status(task_id)
if not task_status: if not task_status:
raise HTTPException(status_code=404, detail="Task not found") raise HTTPException(status_code=404, detail="Task not found")
@@ -406,10 +403,7 @@ async def create_voice_clone_task(
""" """
user_id = require_authenticated_user(current_user) user_id = require_authenticated_user(current_user)
task_id = task_manager.create_task( task_id = task_manager.create_task("voice_clone")
"voice_clone",
metadata={"owner_user_id": user_id},
)
background_tasks.add_task( background_tasks.add_task(
_execute_voice_clone_task, _execute_voice_clone_task,
@@ -440,7 +434,7 @@ async def get_voice_clone_result(
""" """
user_id = require_authenticated_user(current_user) user_id = require_authenticated_user(current_user)
task_status = task_manager.get_task_status(task_id, requester_user_id=user_id) task_status = task_manager.get_task_status(task_id)
if not task_status: if not task_status:
raise HTTPException(status_code=404, detail="Task not found") raise HTTPException(status_code=404, detail="Task not found")

View File

@@ -222,7 +222,7 @@ def _execute_podcast_video_task(
) )
# Verify the task status was updated correctly # Verify the task status was updated correctly
updated_status = task_manager.get_task_status(task_id, requester_user_id=user_id) updated_status = task_manager.get_task_status(task_id)
logger.info( logger.info(
f"[Podcast] Task status after update: task_id={task_id}, status={updated_status.get('status') if updated_status else 'None'}, has_result={bool(updated_status.get('result') if updated_status else False)}, video_url={updated_status.get('result', {}).get('video_url') if updated_status else 'N/A'}" f"[Podcast] Task status after update: task_id={task_id}, status={updated_status.get('status') if updated_status else 'None'}, has_result={bool(updated_status.get('result') if updated_status else False)}, video_url={updated_status.get('result', {}).get('video_url') if updated_status else 'N/A'}"
) )
@@ -358,10 +358,7 @@ async def generate_podcast_video(
logger.warning(f"[Podcast] Failed to extract auth token from headers: {e}") logger.warning(f"[Podcast] Failed to extract auth token from headers: {e}")
# Create async task # Create async task
task_id = task_manager.create_task( task_id = task_manager.create_task("podcast_video_generation")
"podcast_video_generation",
metadata={"owner_user_id": user_id},
)
background_tasks.add_task( background_tasks.add_task(
_execute_podcast_video_task, _execute_podcast_video_task,
task_id=task_id, task_id=task_id,
@@ -491,10 +488,7 @@ async def combine_podcast_videos(
raise HTTPException(status_code=400, detail="No scene videos provided") raise HTTPException(status_code=400, detail="No scene videos provided")
# Create async task # Create async task
task_id = task_manager.create_task( task_id = task_manager.create_task("podcast_combine_videos")
"podcast_combine_videos",
metadata={"owner_user_id": user_id},
)
# Extract token for authenticated URL building # Extract token for authenticated URL building
auth_token = None auth_token = None

View File

@@ -4,7 +4,7 @@ Podcast Maker API Router
Main router that imports and registers all handler modules. Main router that imports and registers all handler modules.
""" """
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends
from typing import Dict, Any from typing import Dict, Any
from middleware.auth_middleware import get_current_user from middleware.auth_middleware import get_current_user
@@ -32,8 +32,5 @@ router.include_router(dubbing.router)
@router.get("/task/{task_id}/status") @router.get("/task/{task_id}/status")
async def podcast_task_status(task_id: str, current_user: Dict[str, Any] = Depends(get_current_user)): async def podcast_task_status(task_id: str, current_user: Dict[str, Any] = Depends(get_current_user)):
"""Expose task status under podcast namespace (reuses shared task manager).""" """Expose task status under podcast namespace (reuses shared task manager)."""
user_id = require_authenticated_user(current_user) require_authenticated_user(current_user)
task_status = task_manager.get_task_status(task_id, requester_user_id=user_id) return task_manager.get_task_status(task_id)
if not task_status:
raise HTTPException(status_code=404, detail="Task not found")
return task_status

View File

@@ -34,14 +34,9 @@ class TaskManager:
del self.task_storage[task_id] del self.task_storage[task_id]
logger.debug(f"[StoryWriter] Cleaned up old task: {task_id}") logger.debug(f"[StoryWriter] Cleaned up old task: {task_id}")
def create_task( def create_task(self, task_type: str = "story_generation") -> str:
self,
task_type: str = "story_generation",
metadata: Optional[Dict[str, Any]] = None,
) -> str:
"""Create a new task and return its ID.""" """Create a new task and return its ID."""
task_id = str(uuid.uuid4()) task_id = str(uuid.uuid4())
task_metadata = metadata or {}
self.task_storage[task_id] = { self.task_storage[task_id] = {
"status": "pending", "status": "pending",
@@ -50,14 +45,13 @@ class TaskManager:
"error": None, "error": None,
"progress_messages": [], "progress_messages": [],
"task_type": task_type, "task_type": task_type,
"progress": 0.0, "progress": 0.0
"metadata": task_metadata,
} }
logger.info(f"[StoryWriter] Created task: {task_id} (type: {task_type})") logger.info(f"[StoryWriter] Created task: {task_id} (type: {task_type})")
return task_id return task_id
def get_task_status(self, task_id: str, requester_user_id: Optional[str] = None) -> Optional[Dict[str, Any]]: def get_task_status(self, task_id: str) -> Optional[Dict[str, Any]]:
"""Get the status of a task.""" """Get the status of a task."""
self.cleanup_old_tasks() self.cleanup_old_tasks()
@@ -68,15 +62,6 @@ class TaskManager:
return None return None
task = self.task_storage[task_id] task = self.task_storage[task_id]
metadata = task.get("metadata", {}) or {}
owner_user_id = metadata.get("owner_user_id")
if requester_user_id is not None and owner_user_id is not None and requester_user_id != owner_user_id:
logger.warning(
f"[StoryWriter] Task access denied for task {task_id}: requester does not match owner"
)
return None
response = { response = {
"task_id": task_id, "task_id": task_id,
"status": task["status"], "status": task["status"],

View File

@@ -8,6 +8,7 @@ IMPORTANT: This is a compatibility layer. For new code, use UserAPIKeyContext di
""" """
import os import os
import time
from fastapi import Request from fastapi import Request
from loguru import logger from loguru import logger
from typing import Callable from typing import Callable
@@ -20,8 +21,61 @@ class APIKeyInjectionMiddleware:
for the duration of each request. for the duration of each request.
""" """
# Shared across middleware instances (module currently instantiates per request)
_missing_keys_log_timestamps = {}
def __init__(self): def __init__(self):
self.original_keys = {} self.original_keys = {}
@staticmethod
def _should_skip_missing_key_warning(request: Request) -> bool:
"""
Optionally suppress missing-key warnings for non-AI/internal routes.
Controlled by API_KEY_INJECTION_SKIP_NON_AI_WARNINGS (default: true).
"""
skip_non_ai_warnings = os.getenv('API_KEY_INJECTION_SKIP_NON_AI_WARNINGS', 'true').lower() in ('1', 'true', 'yes')
if not skip_non_ai_warnings:
return False
path_lower = (request.url.path or '').lower()
return (
path_lower.startswith('/api/subscription/')
or path_lower.startswith('/api/onboarding/')
or path_lower.endswith('/status')
or path_lower.endswith('/health')
or path_lower == '/health'
or path_lower == '/status'
)
def _log_missing_keys_non_blocking(self, request: Request, user_id: str) -> None:
"""
Log missing API keys without interrupting request flow.
- Defaults to debug-level logging.
- Optional warn once-per-user-per-interval via env:
API_KEY_INJECTION_MISSING_KEYS_LOG_MODE=warn_once
API_KEY_INJECTION_MISSING_KEYS_LOG_INTERVAL_SECONDS=900
"""
try:
if self._should_skip_missing_key_warning(request):
logger.debug(f"[API Key Injection] Missing keys for user {user_id} on non-AI route; skipping warning")
return
log_mode = os.getenv('API_KEY_INJECTION_MISSING_KEYS_LOG_MODE', 'debug').lower()
if log_mode != 'warn_once':
logger.debug(f"No API keys found for user {user_id}")
return
interval_seconds = int(os.getenv('API_KEY_INJECTION_MISSING_KEYS_LOG_INTERVAL_SECONDS', '900'))
now = time.time()
last_logged_at = self._missing_keys_log_timestamps.get(user_id, 0)
if (now - last_logged_at) >= max(interval_seconds, 1):
logger.warning(f"No API keys found for user {user_id}")
self._missing_keys_log_timestamps[user_id] = now
else:
logger.debug(f"No API keys found for user {user_id} (warning suppressed by interval)")
except Exception as log_error:
# Logging should never block request processing
logger.debug(f"[API Key Injection] Failed to log missing keys state for user {user_id}: {log_error}")
async def __call__(self, request: Request, call_next: Callable): async def __call__(self, request: Request, call_next: Callable):
""" """
@@ -68,7 +122,7 @@ class APIKeyInjectionMiddleware:
# Get user-specific API keys from database # Get user-specific API keys from database
with user_api_keys(user_id) as user_keys: with user_api_keys(user_id) as user_keys:
if not user_keys: if not user_keys:
logger.warning(f"No API keys found for user {user_id}") self._log_missing_keys_non_blocking(request, user_id)
return await call_next(request) return await call_next(request)
# Save original environment values # Save original environment values
@@ -120,4 +174,3 @@ async def api_key_injection_middleware(request: Request, call_next: Callable):
""" """
middleware = APIKeyInjectionMiddleware() middleware = APIKeyInjectionMiddleware()
return await middleware(request, call_next) return await middleware(request, call_next)