Skip to content
16 changes: 8 additions & 8 deletions src/youtube_extension/backend/api/advanced_video_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ async def analyze_segment(request: TemporalSegmentRequest):
}
except Exception as e:
logger.error(f"Segment analysis failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/temporal/events")
Expand Down Expand Up @@ -211,7 +211,7 @@ async def extract_temporal_events(request: TemporalEventsRequest):
}
except Exception as e:
logger.error(f"Event extraction failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/temporal/question")
Expand Down Expand Up @@ -244,7 +244,7 @@ async def answer_temporal_question(request: TemporalQuestionRequest):
}
except Exception as e:
logger.error(f"Temporal question failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/temporal/timeline")
Expand Down Expand Up @@ -285,7 +285,7 @@ async def create_timeline(request: TimelineRequest):
raise
except Exception as e:
logger.error(f"Timeline creation failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/temporal/compare-segments")
Expand Down Expand Up @@ -319,7 +319,7 @@ async def compare_segments(request: SegmentComparisonRequest):
}
except Exception as e:
logger.error(f"Segment comparison failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/temporal/tutorial-steps")
Expand Down Expand Up @@ -347,7 +347,7 @@ async def extract_tutorial_steps(request: TutorialStepsRequest):
}
except Exception as e:
logger.error(f"Tutorial extraction failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")


# ============ Structured Output Endpoint ============
Expand Down Expand Up @@ -435,7 +435,7 @@ async def analyze_with_schema(request: StructuredAnalysisRequest):
}
except Exception as e:
logger.error(f"Structured analysis failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")


# ============ CloudEvents Publishing Endpoint ============
Expand Down Expand Up @@ -488,4 +488,4 @@ async def publish_video_event(
}
except Exception as e:
logger.error(f"Event publishing failed: {e}", exc_info=True)
raise HTTPException(500, str(e))
raise HTTPException(status_code=500, detail="Internal server error")
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)
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"
Comment on lines 218 to +221
)
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
Comment on lines +172 to +174
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
Comment on lines +433 to +435
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