Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user