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
5 changes: 4 additions & 1 deletion src/uvai/ml/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,11 @@ async def __call__(
"ranker_samples": ranker_state.get("training_samples", 0),
})
except Exception as exc:
# Log the full error server-side; return a static body so the
# exception text does not leak to the client (CWE-209).
logger.error(f"Checkpoint save failed: {exc}", exc_info=True)
return JSONResponse(
{"error": str(exc)},
{"error": "Internal server error"},
status_code=500,
)
else:
Expand Down
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")
20 changes: 10 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 @@ -324,8 +324,8 @@ async def analyze_video_multi_provider(request: VideoAnalysisRequest):
return formatted_results

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
34 changes: 16 additions & 18 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}")
logger.error(f"Failed to update error state: {state_error}", exc_info=True)

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