Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts

This commit is contained in:
ajaysi
2026-02-08 13:56:57 +05:30
parent 1db10ccd0f
commit e404a86502
333 changed files with 42223 additions and 10875 deletions

View File

@@ -54,7 +54,7 @@ async def accept_autofill_inputs(
"""Persist end-user accepted auto-fill inputs and associate with the strategy."""
try:
logger.info(f"🚀 Accepting autofill inputs for strategy: {strategy_id}")
user_id = int(payload.get('user_id') or 1)
user_id = str(payload.get('user_id') or "")
accepted_fields = payload.get('accepted_fields') or {}
# Optional transparency bundles
sources = payload.get('sources') or {}
@@ -224,4 +224,4 @@ async def refresh_autofill(
)
except Exception as e:
logger.error(f"❌ Error generating fresh auto-fill payload: {str(e)}")
raise ContentPlanningErrorHandler.handle_general_error(e, "refresh_autofill")
raise ContentPlanningErrorHandler.handle_general_error(e, "refresh_autofill")

View File

@@ -11,7 +11,7 @@ import json
from datetime import datetime
# Import database
from services.database import get_db_session
from services.database import get_db
# Import authentication middleware
from middleware.auth_middleware import get_current_user
@@ -31,13 +31,6 @@ from ....utils.data_parsers import parse_strategy_data
router = APIRouter(tags=["Strategy CRUD"])
# Helper function to get database session
def get_db():
db = get_db_session()
try:
yield db
finally:
db.close()
@router.post("/create")
async def create_enhanced_strategy(
@@ -104,7 +97,7 @@ async def create_enhanced_strategy(
@router.get("/")
async def get_enhanced_strategies(
user_id: Optional[int] = Query(None, description="User ID to filter strategies (deprecated - use authenticated user)"),
user_id: Optional[str] = Query(None, description="User ID to filter strategies (deprecated - use authenticated user)"),
strategy_id: Optional[int] = Query(None, description="Specific strategy ID"),
current_user: Dict[str, Any] = Depends(get_current_user),
db: Session = Depends(get_db)
@@ -119,8 +112,7 @@ async def get_enhanced_strategies(
detail="Invalid user ID in authentication token"
)
# Use authenticated user_id (override query parameter for security)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
authenticated_user_id = clerk_user_id
logger.info(f"Getting enhanced strategies for authenticated user: {authenticated_user_id}, strategy: {strategy_id}")
@@ -148,7 +140,6 @@ async def get_enhanced_strategy_by_id(
) -> Dict[str, Any]:
"""Get a specific enhanced strategy by ID."""
try:
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
@@ -156,7 +147,7 @@ async def get_enhanced_strategy_by_id(
detail="Invalid user ID in authentication token"
)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
authenticated_user_id = clerk_user_id
logger.info(f"Getting enhanced strategy by ID: {strategy_id} for authenticated user: {authenticated_user_id}")
@@ -201,7 +192,6 @@ async def update_enhanced_strategy(
) -> Dict[str, Any]:
"""Update an enhanced strategy."""
try:
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
@@ -209,7 +199,7 @@ async def update_enhanced_strategy(
detail="Invalid user ID in authentication token"
)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
authenticated_user_id = clerk_user_id
logger.info(f"Updating enhanced strategy: {strategy_id} for authenticated user: {authenticated_user_id}")
@@ -270,7 +260,7 @@ async def delete_enhanced_strategy(
detail="Invalid user ID in authentication token"
)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
authenticated_user_id = clerk_user_id
logger.info(f"Deleting enhanced strategy: {strategy_id} for authenticated user: {authenticated_user_id}")
@@ -306,4 +296,4 @@ async def delete_enhanced_strategy(
raise
except Exception as e:
logger.error(f"Error deleting enhanced strategy: {str(e)}")
return ContentPlanningErrorHandler.handle_general_error(e, "delete_enhanced_strategy")
return ContentPlanningErrorHandler.handle_general_error(e, "delete_enhanced_strategy")

View File

@@ -78,16 +78,12 @@ async def stream_enhanced_strategies(
async def strategy_generator():
try:
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
yield {"type": "error", "message": "Invalid user ID in authentication token", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
yield {"type": "error", "message": "Invalid user ID format", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = clerk_user_id
logger.info(f"🚀 Starting strategy stream for authenticated user: {authenticated_user_id}, strategy: {strategy_id}")
@@ -145,16 +141,12 @@ async def stream_strategic_intelligence(
async def intelligence_generator():
try:
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
yield {"type": "error", "message": "Invalid user ID in authentication token", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
yield {"type": "error", "message": "Invalid user ID format", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = clerk_user_id
logger.info(f"🚀 Starting strategic intelligence stream for authenticated user: {authenticated_user_id}")
@@ -286,16 +278,12 @@ async def stream_keyword_research(
async def keyword_generator():
try:
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
yield {"type": "error", "message": "Invalid user ID in authentication token", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
yield {"type": "error", "message": "Invalid user ID format", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = clerk_user_id
logger.info(f"🚀 Starting keyword research stream for authenticated user: {authenticated_user_id}")
@@ -396,4 +384,4 @@ async def stream_keyword_research(
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Credentials": "true"
}
)
)

View File

@@ -29,6 +29,7 @@ from ...utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
# Import services
from ...services.ai_analytics_service import ContentPlanningAIAnalyticsService
from middleware.auth_middleware import get_current_user
# Initialize services
ai_analytics_service = ContentPlanningAIAnalyticsService()
@@ -37,14 +38,19 @@ ai_analytics_service = ContentPlanningAIAnalyticsService()
router = APIRouter(prefix="/ai-analytics", tags=["ai-analytics"])
@router.post("/content-evolution", response_model=AIAnalyticsResponse)
async def analyze_content_evolution(request: ContentEvolutionRequest):
async def analyze_content_evolution(
request: ContentEvolutionRequest,
current_user: Dict[str, Any] = Depends(get_current_user)
):
"""
Analyze content evolution over time for a specific strategy.
"""
try:
logger.info(f"Starting content evolution analysis for strategy {request.strategy_id}")
user_id = current_user.get("user_id")
logger.info(f"Starting content evolution analysis for strategy {request.strategy_id} (user {user_id})")
result = await ai_analytics_service.analyze_content_evolution(
user_id=user_id,
strategy_id=request.strategy_id,
time_period=request.time_period
)
@@ -103,14 +109,19 @@ async def predict_content_performance(request: ContentPerformancePredictionReque
)
@router.post("/strategic-intelligence", response_model=AIAnalyticsResponse)
async def generate_strategic_intelligence(request: StrategicIntelligenceRequest):
async def generate_strategic_intelligence(
request: StrategicIntelligenceRequest,
current_user: Dict[str, Any] = Depends(get_current_user)
):
"""
Generate strategic intelligence for content planning.
"""
try:
logger.info(f"Starting strategic intelligence generation for strategy {request.strategy_id}")
user_id = current_user.get("user_id")
logger.info(f"Starting strategic intelligence generation for strategy {request.strategy_id} (user {user_id})")
result = await ai_analytics_service.generate_strategic_intelligence(
user_id=user_id,
strategy_id=request.strategy_id,
market_data=request.market_data
)

View File

@@ -10,6 +10,9 @@ from datetime import datetime
from loguru import logger
import json
# Import auth middleware
from middleware.auth_middleware import get_current_user
# Import database service
from services.database import get_db_session, get_db
from services.content_planning_db import ContentPlanningDBService
@@ -54,12 +57,13 @@ async def create_content_gap_analysis(
@router.get("/", response_model=Dict[str, Any])
async def get_content_gap_analyses(
user_id: Optional[int] = Query(None, description="User ID"),
strategy_id: Optional[int] = Query(None, description="Strategy ID"),
force_refresh: bool = Query(False, description="Force refresh gap analysis")
force_refresh: bool = Query(False, description="Force refresh gap analysis"),
current_user: Dict[str, Any] = Depends(get_current_user)
):
"""Get content gap analysis with real AI insights - Database first approach."""
try:
user_id = str(current_user.get('id'))
logger.info(f"🚀 Starting content gap analysis for user: {user_id}, strategy: {strategy_id}, force_refresh: {force_refresh}")
result = await gap_analysis_service.get_gap_analyses(user_id, strategy_id, force_refresh)
@@ -88,24 +92,27 @@ async def get_content_gap_analysis(
raise ContentPlanningErrorHandler.handle_general_error(e, "get_content_gap_analysis")
@router.post("/analyze", response_model=ContentGapAnalysisFullResponse)
async def analyze_content_gaps(request: ContentGapAnalysisRequest):
async def analyze_content_gaps(
request: ContentGapAnalysisRequest,
current_user: Dict[str, Any] = Depends(get_current_user)
):
"""
Analyze content gaps between your website and competitors.
"""
try:
logger.info(f"Starting content gap analysis for: {request.website_url}")
user_id = str(current_user.get('id'))
request_data = request.dict()
result = await gap_analysis_service.analyze_content_gaps(request_data)
result = await gap_analysis_service.analyze_content_gaps(request_data, user_id)
return ContentGapAnalysisFullResponse(**result)
except HTTPException:
raise
except Exception as e:
logger.error(f"Error analyzing content gaps: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Error analyzing content gaps: {str(e)}"
)
raise ContentPlanningErrorHandler.handle_general_error(e, "analyze_content_gaps")
@router.get("/user/{user_id}/analyses")
async def get_user_gap_analyses(

View File

@@ -3,21 +3,23 @@ API Monitoring Routes
Simple endpoints to expose API monitoring and cache statistics.
"""
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter, HTTPException, Depends
from typing import Dict, Any
from loguru import logger
from services.subscription import get_monitoring_stats, get_lightweight_stats
from services.comprehensive_user_data_cache_service import ComprehensiveUserDataCacheService
from services.database import get_db
from middleware.auth_middleware import get_current_user
router = APIRouter(prefix="/monitoring", tags=["monitoring"])
@router.get("/api-stats")
async def get_api_statistics(minutes: int = 5) -> Dict[str, Any]:
async def get_api_statistics(minutes: int = 5, current_user: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]:
"""Get current API monitoring statistics."""
try:
stats = await get_monitoring_stats(minutes)
user_id = current_user.get('id') or current_user.get('clerk_user_id')
stats = await get_monitoring_stats(minutes=minutes)
return {
"status": "success",
"data": stats,
@@ -28,18 +30,67 @@ async def get_api_statistics(minutes: int = 5) -> Dict[str, Any]:
raise HTTPException(status_code=500, detail="Failed to get API statistics")
@router.get("/lightweight-stats")
async def get_lightweight_statistics() -> Dict[str, Any]:
async def get_lightweight_statistics(current_user: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]:
"""Get lightweight stats for dashboard header."""
try:
stats = await get_lightweight_stats()
logger.info(f"DEBUG: get_lightweight_statistics called. current_user type: {type(current_user)}")
logger.info(f"DEBUG: current_user content: {current_user}")
user_id = current_user.get('id') or current_user.get('clerk_user_id')
logger.info(f"Fetching lightweight stats for user: {user_id}")
if not user_id:
logger.error(f"User ID is missing from current_user: {current_user}")
# Return empty stats instead of 500
return {
"status": "success",
"data": {
"status": "unknown",
"icon": "",
"recent_requests": 0,
"recent_errors": 0,
"error_rate": 0.0,
"timestamp": datetime.utcnow().isoformat()
},
"message": "User ID missing, returning empty stats"
}
try:
stats = await get_lightweight_stats(user_id)
logger.info(f"DEBUG: stats retrieved: {stats}")
except Exception as e:
logger.error(f"Error calling get_lightweight_stats: {str(e)}", exc_info=True)
# Return empty stats instead of 500 to keep frontend alive
stats = {
"status": "unknown",
"icon": "",
"recent_requests": 0,
"recent_errors": 0,
"error_rate": 0.0,
"timestamp": datetime.utcnow().isoformat()
}
return {
"status": "success",
"data": stats,
"message": "Lightweight monitoring statistics retrieved successfully"
}
except Exception as e:
logger.error(f"Error getting lightweight stats: {str(e)}")
raise HTTPException(status_code=500, detail="Failed to get lightweight statistics")
logger.error(f"Error getting lightweight stats: {str(e)}", exc_info=True)
# Even top-level error should not 500 if possible, but at least we log it.
# We'll return a safe response here too.
return {
"status": "success",
"data": {
"status": "error",
"icon": "🔴",
"recent_requests": 0,
"recent_errors": 0,
"error_rate": 0.0,
"timestamp": datetime.utcnow().isoformat()
},
"message": f"Error retrieving stats: {str(e)}"
}
@router.get("/cache-stats")
async def get_cache_statistics(db = None) -> Dict[str, Any]:
@@ -61,14 +112,15 @@ async def get_cache_statistics(db = None) -> Dict[str, Any]:
raise HTTPException(status_code=500, detail="Failed to get cache statistics")
@router.get("/health")
async def get_system_health() -> Dict[str, Any]:
async def get_system_health(current_user: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]:
"""Get overall system health status.
Optimized to fail fast - cache stats are optional and won't block the response.
"""
try:
user_id = current_user.get('id') or current_user.get('clerk_user_id')
# Get lightweight API stats (this is the critical path)
api_stats = await get_lightweight_stats()
api_stats = await get_lightweight_stats(user_id)
# Get cache stats if available (non-blocking - don't fail if unavailable)
cache_stats = {}

View File

@@ -9,8 +9,11 @@ from typing import Dict, Any, List, Optional
from datetime import datetime
from loguru import logger
# Import auth middleware
from middleware.auth_middleware import get_current_user
# Import database service
from services.database import get_db_session, get_db
from services.database import get_db, get_session_for_user
from services.content_planning_db import ContentPlanningDBService
# Import models
@@ -53,21 +56,37 @@ async def create_content_strategy(
@router.get("/", response_model=Dict[str, Any])
async def get_content_strategies(
user_id: Optional[int] = Query(None, description="User ID"),
strategy_id: Optional[int] = Query(None, description="Strategy ID")
strategy_id: Optional[int] = Query(None, description="Strategy ID"),
current_user: Dict[str, Any] = Depends(get_current_user)
):
"""
Get content strategies with comprehensive logging for debugging.
"""
try:
user_id = str(current_user.get('id'))
logger.info(f"🚀 Starting content strategy analysis for user: {user_id}, strategy: {strategy_id}")
# Create a temporary database session for this operation
from services.database import get_db_session
temp_db = get_db_session()
temp_db = get_session_for_user(user_id)
if not temp_db:
raise HTTPException(status_code=500, detail="Database connection failed")
try:
db_service = EnhancedStrategyDBService(temp_db)
strategy_service = EnhancedStrategyService(db_service)
# Pass user_id (as int or str depending on service expectation)
# EnhancedStrategyService.get_enhanced_strategies usually takes user_id but here it seems to filter by strategy_id
# If user_id is needed for filtering by user, we should check the service signature.
# But the service uses the DB session which is already filtered by user (SQLite isolation).
# So passing user_id might be for logging or legacy filtering.
# Note: The original code passed user_id from query param.
# We pass the authenticated user_id.
# Assuming the service can handle string user_id or we convert to int if it expects int.
# Most legacy IDs were ints. Clerk IDs are strings.
# Let's try to convert to int if possible, or pass as is.
# Since SQLite isolation is used, the DB only contains this user's data.
result = await strategy_service.get_enhanced_strategies(user_id, strategy_id, temp_db)
return result
finally: