Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions src/youtube_extension/backend/cloud_ai_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ async def get_provider_status():
)

except Exception as e:
logger.error(f"Failed to get provider status: {e}")
raise HTTPException(status_code=500, detail=f"Failed to get provider status: {str(e)}")
logger.error(f"Failed to get provider status: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/analyze/video", response_model=VideoAnalysisResponse)
Expand Down Expand Up @@ -262,13 +262,13 @@ async def analyze_video(request: VideoAnalysisRequest):
logger.warning(f"Rate limit exceeded: {e}")
raise HTTPException(status_code=429, detail=f"Rate limit exceeded: {str(e)}")
except ConfigurationError as e:
logger.error(f"Configuration error: {e}")
raise HTTPException(status_code=500, detail=f"Configuration error: {str(e)}")
logger.error(f"Configuration error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")
except HTTPException:
raise
except Exception as e:
logger.error(f"Unexpected error during video analysis: {e}")
raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}")
logger.error(f"Unexpected error during video analysis: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/analyze/batch")
Expand Down Expand Up @@ -300,8 +300,8 @@ async def analyze_batch_videos(request: BatchAnalysisRequest, background_tasks:
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to start batch analysis: {e}")
raise HTTPException(status_code=500, detail=f"Batch analysis failed: {str(e)}")
logger.error(f"Failed to start batch analysis: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/analyze/multi-provider", response_model=list[VideoAnalysisResponse])
Expand All @@ -323,9 +323,13 @@ async def analyze_video_multi_provider(request: VideoAnalysisRequest):

return formatted_results

except HTTPException:
# Preserve client-facing status codes (e.g. 400 from parse_analysis_types);
# do not let the broad handler below convert them into a generic 500.
raise
except Exception as e:
logger.error(f"Multi-provider analysis failed: {e}")
raise HTTPException(status_code=500, detail=f"Multi-provider analysis failed: {str(e)}")
logger.error(f"Multi-provider analysis failed: {e}", exc_info=True)
Comment thread
vercel[bot] marked this conversation as resolved.
raise HTTPException(status_code=500, detail="Internal server error")


@router.get("/analysis-types")
Expand Down
32 changes: 15 additions & 17 deletions src/youtube_extension/backend/cloud_api_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,10 @@ async def process_video_cloud(
)

except Exception as e:
error_msg = f"Cloud processing failed: {str(e)}"
logger.error(error_msg)
logger.error(f"Cloud processing failed: {e}", exc_info=True)

raise HTTPException(
status_code=500,
detail={
"error": "cloud_processing_failed",
"message": error_msg,
"video_url": request.video_url,
"timestamp": datetime.now(timezone.utc).isoformat()
}
)
# detail must be a static string — the exception is logged above only.
raise HTTPException(status_code=500, detail="Internal server error")

@router.post("/api/v3/process-video-task")
async def process_video_task_handler(
Expand Down Expand Up @@ -216,20 +208,23 @@ async def process_video_task_handler(

except Exception as e:
error_msg = f"Task processing failed: {str(e)}"
logger.error(error_msg)
logger.error(error_msg, exc_info=True)

# Update state with error
try:
firestore_service = await get_firestore_service()
# error_message is returned to clients by the status/result endpoints,
# so it must stay generic — the full error_msg is in the logs above only.
await firestore_service.update_state(
payload.video_id,
status='failed',
error_message=error_msg
error_message="Internal server error"
)
except Exception as state_error:
logger.error(f"Failed to update error state: {state_error}")

raise HTTPException(status_code=500, detail=error_msg)
# detail must be a static string — error_msg (with the exception) is logged above only.
raise HTTPException(status_code=500, detail="Internal server error")

@router.post("/api/v3/batch-process")
async def batch_process_videos_cloud(request: BatchCloudProcessingRequest):
Expand Down Expand Up @@ -260,9 +255,10 @@ async def batch_process_videos_cloud(request: BatchCloudProcessingRequest):
except HTTPException:
raise
except Exception as e:
logger.error(f"Unhandled error in batch_process_videos_cloud: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Batch processing failed: {str(e)}"
detail="Internal server error"
)

@router.get("/api/v3/videos/{video_id}/status", response_model=VideoStatusResponse)
Expand Down Expand Up @@ -293,9 +289,10 @@ async def get_video_status(video_id: str):
except HTTPException:
raise
except Exception as e:
logger.error(f"Unhandled error in get_video_status: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Error retrieving status: {str(e)}"
detail="Internal server error"
)

@router.get("/api/v3/videos/{video_id}/result")
Expand Down Expand Up @@ -330,9 +327,10 @@ async def get_video_result(video_id: str):
except HTTPException:
raise
except Exception as e:
logger.error(f"Unhandled error in get_video_result: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Error retrieving result: {str(e)}"
detail="Internal server error"
)

@router.get("/api/v3/queue/stats")
Expand Down
13 changes: 8 additions & 5 deletions src/youtube_extension/backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,21 +444,24 @@ async def value_error_handler(request, exc):

@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
"""Global exception handler with enhanced error details"""
"""Global handler for unhandled exceptions.

The full exception — type, message, and traceback — is logged server-side
only. The client receives a static body: neither the exception message
(``str(exc)``) nor its class name may be disclosed, as both leak internal
state to the caller (CWE-209 information disclosure).
"""
logger.error(f"Unhandled exception: {exc}", exc_info=True)

error_detail = {
"error": "Internal server error",
"detail": str(exc),
"detail": "Internal server error",
"timestamp": datetime.now().isoformat(),
"path": str(request.url) if hasattr(request, "url") else "unknown",
"version": "2.0.0",
"architecture": "service-oriented",
}

if hasattr(exc, "__class__"):
error_detail["error_type"] = exc.__class__.__name__

return JSONResponse(status_code=500, content=error_detail)


Expand Down
35 changes: 19 additions & 16 deletions src/youtube_extension/backend/real_api_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,10 @@ async def process_video_real_api(request: VideoProcessingRequest, background_tas
return response

except Exception as e:
error_msg = f"Real API processing failed: {str(e)}"
logger.error(error_msg)
logger.error(f"Real API processing failed: {e}", exc_info=True)

raise HTTPException(
status_code=500,
detail={
"error": "video_processing_failed",
"message": error_msg,
"video_url": request.video_url,
"timestamp": datetime.now(timezone.utc).isoformat()
}
)
# detail must be a static string — the exception is logged above only.
raise HTTPException(status_code=500, detail="Internal server error")

@app.post("/api/v2/validate-video")
async def validate_video_url(request: VideoValidationRequest):
Expand All @@ -150,9 +142,10 @@ async def validate_video_url(request: VideoValidationRequest):
}

except Exception as e:
logger.error(f"Unhandled error in validate_video_url: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Validation failed: {str(e)}"
detail="Internal server error"
)

@app.post("/api/v2/batch-process")
Expand All @@ -176,10 +169,14 @@ async def batch_process_videos(request: BatchProcessingRequest):

return result

except HTTPException:
# Preserve intentional client errors (e.g. the 400 above).
raise
except Exception as e:
logger.error(f"Unhandled error in batch_process_videos: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Batch processing failed: {str(e)}"
detail="Internal server error"
)

@app.get("/api/v2/videos/list")
Expand Down Expand Up @@ -252,9 +249,10 @@ async def get_video_analysis(video_id: str):
except HTTPException:
raise
except Exception as e:
logger.error(f"Unhandled error in get_video_analysis: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Error retrieving video analysis: {str(e)}"
detail="Internal server error"
)

@app.get("/api/v2/cost-dashboard")
Expand Down Expand Up @@ -384,9 +382,10 @@ async def clear_processing_cache():
}

except Exception as e:
logger.error(f"Unhandled error in clear_processing_cache: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Failed to clear cache: {str(e)}"
detail="Internal server error"
)

@app.post("/api/v2/search-videos")
Expand Down Expand Up @@ -431,10 +430,14 @@ async def search_youtube_videos(
"timestamp": datetime.now(timezone.utc).isoformat()
}

except HTTPException:
# Preserve intentional client errors (e.g. the 400 above).
raise
except Exception as e:
logger.error(f"Unhandled error in search_youtube_videos: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Search failed: {str(e)}"
detail="Internal server error"
)

logger.info("🚀 Real API endpoints setup complete")
Expand Down
Loading
Loading