Subscription dashboard improvements, AI text generation limit, and other fixes.

This commit is contained in:
ajaysi
2025-11-01 18:01:14 +05:30
parent cdb41aec1b
commit de4328175d
64 changed files with 5809 additions and 444 deletions

View File

@@ -5,10 +5,11 @@ Main router for blog writing operations including research, outline generation,
content creation, SEO analysis, and publishing.
"""
from fastapi import APIRouter, HTTPException
from typing import Any, Dict, List
from fastapi import APIRouter, HTTPException, Depends
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
from loguru import logger
from middleware.auth_middleware import get_current_user
from models.blog_models import (
BlogResearchRequest,
@@ -64,10 +65,21 @@ class SEOApplyRecommendationsRequest(BaseModel):
@router.post("/seo/apply-recommendations")
async def apply_seo_recommendations(request: SEOApplyRecommendationsRequest) -> Dict[str, Any]:
async def apply_seo_recommendations(
request: SEOApplyRecommendationsRequest,
current_user: Dict[str, Any] = Depends(get_current_user)
) -> Dict[str, Any]:
"""Apply actionable SEO recommendations and return updated content."""
try:
result = await recommendation_applier.apply_recommendations(request.dict())
# Extract Clerk user ID (required)
if not current_user:
raise HTTPException(status_code=401, detail="Authentication required")
user_id = str(current_user.get('id', ''))
if not user_id:
raise HTTPException(status_code=401, detail="Invalid user ID in authentication token")
result = await recommendation_applier.apply_recommendations(request.dict(), user_id=user_id)
if not result.get("success"):
raise HTTPException(status_code=500, detail=result.get("error", "Failed to apply recommendations"))
return result
@@ -87,13 +99,24 @@ async def health() -> Dict[str, Any]:
# Research Endpoints
@router.post("/research/start")
async def start_research(request: BlogResearchRequest) -> Dict[str, Any]:
async def start_research(
request: BlogResearchRequest,
current_user: Dict[str, Any] = Depends(get_current_user)
) -> Dict[str, Any]:
"""Start a research operation and return a task ID for polling."""
try:
# TODO: Get user_id from authentication context
user_id = "anonymous" # This should come from auth middleware
# Extract Clerk user ID (required)
if not current_user:
raise HTTPException(status_code=401, detail="Authentication required")
user_id = str(current_user.get('id', ''))
if not user_id:
raise HTTPException(status_code=401, detail="Invalid user ID in authentication token")
task_id = await task_manager.start_research_task(request, user_id)
return {"task_id": task_id, "status": "started"}
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to start research: {e}")
raise HTTPException(status_code=500, detail=str(e))
@@ -107,6 +130,50 @@ async def get_research_status(task_id: str) -> Dict[str, Any]:
if status is None:
raise HTTPException(status_code=404, detail="Task not found")
# If task failed with subscription error, return HTTP error so frontend interceptor can catch it
if status.get('status') == 'failed' and status.get('error_status') in [429, 402]:
error_data = status.get('error_data', {}) or {}
error_status = status.get('error_status', 429)
if not isinstance(error_data, dict):
logger.warning(f"Research task {task_id} error_data not dict: {error_data}")
error_data = {'error': str(error_data)}
# Determine provider and usage info
stored_error_message = status.get('error', error_data.get('error'))
provider = error_data.get('provider', 'unknown')
usage_info = error_data.get('usage_info')
if not usage_info:
usage_info = {
'provider': provider,
'message': stored_error_message,
'error_type': error_data.get('error_type', 'unknown')
}
# Include any known fields from error_data
for key in ['current_tokens', 'requested_tokens', 'limit', 'current_calls']:
if key in error_data:
usage_info[key] = error_data[key]
# Build error message for detail
error_msg = error_data.get('message', stored_error_message or 'Subscription limit exceeded')
# Log the subscription error with all context
logger.warning(f"Research task {task_id} failed with subscription error {error_status}: {error_msg}")
logger.warning(f" Provider: {provider}, Usage Info: {usage_info}")
# Use JSONResponse to ensure detail is returned as-is, not wrapped in an array
from fastapi.responses import JSONResponse
return JSONResponse(
status_code=error_status,
content={
'error': error_data.get('error', stored_error_message or 'Subscription limit exceeded'),
'message': error_msg,
'provider': provider,
'usage_info': usage_info
}
)
logger.info(f"Research status request for {task_id}: {status['status']} with {len(status.get('progress_messages', []))} progress messages")
return status
except HTTPException:
@@ -310,20 +377,46 @@ async def hallucination_check(request: HallucinationCheckRequest) -> Hallucinati
# SEO Endpoints
@router.post("/seo/analyze", response_model=BlogSEOAnalyzeResponse)
async def seo_analyze(request: BlogSEOAnalyzeRequest) -> BlogSEOAnalyzeResponse:
async def seo_analyze(
request: BlogSEOAnalyzeRequest,
current_user: Dict[str, Any] = Depends(get_current_user)
) -> BlogSEOAnalyzeResponse:
"""Analyze content for SEO optimization opportunities."""
try:
return await service.seo_analyze(request)
# Extract Clerk user ID (required)
if not current_user:
raise HTTPException(status_code=401, detail="Authentication required")
user_id = str(current_user.get('id', ''))
if not user_id:
raise HTTPException(status_code=401, detail="Invalid user ID in authentication token")
return await service.seo_analyze(request, user_id=user_id)
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to perform SEO analysis: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.post("/seo/metadata", response_model=BlogSEOMetadataResponse)
async def seo_metadata(request: BlogSEOMetadataRequest) -> BlogSEOMetadataResponse:
async def seo_metadata(
request: BlogSEOMetadataRequest,
current_user: Dict[str, Any] = Depends(get_current_user)
) -> BlogSEOMetadataResponse:
"""Generate SEO metadata for the blog post."""
try:
return await service.seo_metadata(request)
# Extract Clerk user ID (required)
if not current_user:
raise HTTPException(status_code=401, detail="Authentication required")
user_id = str(current_user.get('id', ''))
if not user_id:
raise HTTPException(status_code=401, detail="Invalid user ID in authentication token")
return await service.seo_metadata(request, user_id=user_id)
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to generate SEO metadata: {e}")
raise HTTPException(status_code=500, detail=str(e))