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
33 changes: 19 additions & 14 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")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
groupthinking marked this conversation as resolved.


@router.post("/analyze/video", response_model=VideoAnalysisResponse)
Expand All @@ -255,20 +255,25 @@ async def analyze_video(request: VideoAnalysisRequest):
# Format and return response
return format_analysis_result(result)

except CloudAIError as e:
logger.error(f"Cloud AI analysis failed: {e}")
raise HTTPException(status_code=503, detail=f"AI analysis failed: {str(e)}")
except RateLimitError as e:
# RateLimitError subclasses CloudAIError, so it MUST be caught before the
# base class or it would be swallowed and mis-reported as a 503.
logger.warning(f"Rate limit exceeded: {e}")
raise HTTPException(status_code=429, detail=f"Rate limit exceeded: {str(e)}")
raise HTTPException(status_code=429, detail="Rate limit exceeded")
except ConfigurationError as e:
logger.error(f"Configuration error: {e}")
raise HTTPException(status_code=500, detail=f"Configuration error: {str(e)}")
# ConfigurationError subclasses CloudAIError, so it MUST be caught before
# the base class. Its message can embed internal configuration detail, so
# the client body stays generic (CWE-209); the full error is logged below.
logger.error(f"Configuration error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")
Comment thread
groupthinking marked this conversation as resolved.
Comment on lines 263 to +268
Comment thread
vercel[bot] marked this conversation as resolved.
except CloudAIError as e:
logger.error(f"Cloud AI analysis failed: {e}", exc_info=True)
raise HTTPException(status_code=503, detail="AI service temporarily unavailable")
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")
Comment thread
groupthinking marked this conversation as resolved.


@router.post("/analyze/batch")
Expand Down Expand Up @@ -300,8 +305,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 +329,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")
Comment thread
groupthinking marked this conversation as resolved.


@router.get("/analysis-types")
Expand Down
56 changes: 32 additions & 24 deletions src/youtube_extension/backend/cloud_api_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@
router = APIRouter()


def _client_safe_error(error_message: Optional[str]) -> Optional[str]:
"""Return a client-safe error string.

Processing state persisted by the video processor can embed raw exception
text (e.g. ``Error processing video ...: <exception>``). That text must never
be echoed to clients through the status/result/process responses (CWE-209).
The full message stays in server-side state and logs; the client only learns
that an error occurred.
"""
return "Internal server error" if error_message else None



# Pydantic models for API requests/responses
class CloudVideoProcessingRequest(BaseModel):
Expand Down Expand Up @@ -138,22 +150,13 @@ async def process_video_cloud(
ai_analysis=result.ai_analysis,
processing_time=result.processing_time,
from_cache=result.from_cache,
error=result.error_message,
error=_client_safe_error(result.error_message),
)

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

raise HTTPException(
status_code=500,
detail={
"error": "cloud_processing_failed",
"message": error_msg,
"video_url": request.video_url,
"timestamp": datetime.now(timezone.utc).isoformat()
}
)
# Exception text is logged server-side only; never returned to the client.
logger.error(f"Cloud processing failed: {e}", exc_info=True)
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 @@ -215,21 +218,23 @@ async def process_video_task_handler(
}

except Exception as e:
error_msg = f"Task processing failed: {str(e)}"
logger.error(error_msg)
# Exception text is logged server-side only. It must NOT be persisted to
# task state: get_video_status / get_video_result echo error_message back
# to clients, so a raw str(e) there would re-expose internal detail (CWE-209).
Comment thread
groupthinking marked this conversation as resolved.
logger.error(f"Task processing failed: {e}", exc_info=True)

# Update state with error
# Update state with a user-safe message
try:
firestore_service = await get_firestore_service()
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)
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 +265,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 All @@ -287,15 +293,16 @@ async def get_video_status(video_id: str):
created_at=state.created_at,
updated_at=state.updated_at,
processing_time=state.processing_time,
error_message=state.error_message,
error_message=_client_safe_error(state.error_message),
)

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 @@ -324,15 +331,16 @@ async def get_video_result(video_id: str):
"processing_time": state.processing_time,
"created_at": state.created_at,
"updated_at": state.updated_at,
"error_message": state.error_message,
"error_message": _client_safe_error(state.error_message),
}

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
36 changes: 19 additions & 17 deletions src/youtube_extension/backend/real_api_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,12 @@ async def process_video_real_api(request: VideoProcessingRequest, background_tas

return response

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

raise HTTPException(
status_code=500,
detail={
"error": "video_processing_failed",
"message": error_msg,
"video_url": request.video_url,
"timestamp": datetime.now(timezone.utc).isoformat()
}
)
# Exception text is logged server-side only; never returned to the client.
logger.error(f"Real API processing failed: {e}", exc_info=True)
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 +143,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 +170,13 @@ async def batch_process_videos(request: BatchProcessingRequest):

return result

except HTTPException:
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"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)

@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,13 @@ async def search_youtube_videos(
"timestamp": datetime.now(timezone.utc).isoformat()
}

except HTTPException:
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