Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts
This commit is contained in:
@@ -63,8 +63,17 @@ async def get_usage_alerts(
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting usage alerts: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
logger.error(f"Error getting usage alerts: {e}", exc_info=True)
|
||||
# Return empty alerts instead of 500
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"alerts": [],
|
||||
"total": 0,
|
||||
"unread_count": 0,
|
||||
"message": f"Error retrieving alerts: {str(e)}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.post("/alerts/{alert_id}/mark-read")
|
||||
|
||||
@@ -164,7 +164,29 @@ async def get_dashboard_data(
|
||||
return response_payload
|
||||
except Exception as retry_err:
|
||||
logger.error(f"Schema fix and retry failed: {retry_err}")
|
||||
raise HTTPException(status_code=500, detail=f"Database schema error: {str(e)}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(retry_err),
|
||||
"data": {
|
||||
"current_usage": {"total_calls": 0, "total_cost": 0, "usage_status": "error", "provider_breakdown": {}},
|
||||
"trends": [],
|
||||
"limits": {"limits": {"monthly_cost": 0}},
|
||||
"alerts": [],
|
||||
"projections": {"projected_monthly_cost": 0, "cost_limit": 0, "projected_usage_percentage": 0},
|
||||
"summary": {"total_api_calls_this_month": 0, "total_cost_this_month": 0, "usage_status": "error", "unread_alerts": 0}
|
||||
}
|
||||
}
|
||||
|
||||
logger.error(f"Error getting dashboard data: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"data": {
|
||||
"current_usage": {"total_calls": 0, "total_cost": 0, "usage_status": "error", "provider_breakdown": {}},
|
||||
"trends": [],
|
||||
"limits": {"limits": {"monthly_cost": 0}},
|
||||
"alerts": [],
|
||||
"projections": {"projected_monthly_cost": 0, "cost_limit": 0, "projected_usage_percentage": 0},
|
||||
"summary": {"total_api_calls_this_month": 0, "total_cost_this_month": 0, "usage_status": "error", "unread_alerts": 0}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,8 +115,15 @@ async def preflight_check(
|
||||
if op['provider'] in [APIProvider.VIDEO, APIProvider.IMAGE_EDIT, APIProvider.STABILITY]:
|
||||
cost = pricing_info.get('cost_per_request', 0.0) or pricing_info.get('cost_per_image', 0.0) or 0.0
|
||||
elif op['provider'] == APIProvider.AUDIO:
|
||||
# Audio pricing is per character (every character is 1 token)
|
||||
cost = (pricing_info.get('cost_per_input_token', 0.0) or 0.0) * (op['tokens_requested'] / 1000.0)
|
||||
model_lower = (model_name or "").lower()
|
||||
if model_lower == "minimax/voice-clone":
|
||||
cost = pricing_info.get('cost_per_request', 0.5) or 0.5
|
||||
elif model_lower == "wavespeed-ai/qwen3-tts/voice-clone":
|
||||
chars = max(0, int(op.get('tokens_requested') or 0))
|
||||
cost = max(0.005, 0.005 * (chars / 100.0))
|
||||
else:
|
||||
# Audio pricing is per character (every character is 1 token)
|
||||
cost = (pricing_info.get('cost_per_input_token', 0.0) or 0.0) * (op['tokens_requested'] / 1000.0)
|
||||
elif op['tokens_requested'] > 0:
|
||||
# Token-based cost estimation (rough estimate)
|
||||
cost = (pricing_info.get('cost_per_input_token', 0.0) or 0.0) * (op['tokens_requested'] / 1000)
|
||||
|
||||
@@ -12,6 +12,7 @@ import sqlite3
|
||||
from services.database import get_db
|
||||
from services.subscription import UsageTrackingService, PricingService
|
||||
from services.subscription.schema_utils import ensure_subscription_plan_columns
|
||||
from services.user_workspace_manager import UserWorkspaceManager
|
||||
from middleware.auth_middleware import get_current_user
|
||||
from models.subscription_models import (
|
||||
SubscriptionPlan, UserSubscription, UsageSummary,
|
||||
@@ -93,7 +94,23 @@ async def get_user_subscription(
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting user subscription: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"data": {
|
||||
"subscription": None,
|
||||
"plan": {
|
||||
"id": "error_fallback",
|
||||
"name": "Error Fallback",
|
||||
"tier": "free",
|
||||
"price_monthly": 0,
|
||||
"description": "Unable to load subscription details",
|
||||
"is_free": True
|
||||
},
|
||||
"status": "error",
|
||||
"limits": {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.get("/status/{user_id}")
|
||||
@@ -255,11 +272,29 @@ async def get_subscription_status(
|
||||
}
|
||||
}
|
||||
except Exception as retry_err:
|
||||
logger.error(f"Schema fix and retry failed: {retry_err}")
|
||||
raise HTTPException(status_code=500, detail=f"Database schema error: {str(e)}")
|
||||
logger.error(f"Schema fix and retry failed: {retry_err}", exc_info=True)
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"active": False,
|
||||
"plan": "none",
|
||||
"tier": "none",
|
||||
"can_use_api": False,
|
||||
"reason": f"Database schema error: {str(e)}"
|
||||
}
|
||||
}
|
||||
|
||||
logger.error(f"Error getting subscription status: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
logger.error(f"Error getting subscription status: {e}", exc_info=True)
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"active": False,
|
||||
"plan": "none",
|
||||
"tier": "none",
|
||||
"can_use_api": False,
|
||||
"reason": f"Failed to check subscription status: {str(e)}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.post("/subscribe/{user_id}")
|
||||
@@ -383,6 +418,18 @@ async def subscribe_to_plan(
|
||||
auto_renew=True
|
||||
)
|
||||
db.add(subscription)
|
||||
|
||||
# Ensure user workspace exists for new subscribers
|
||||
# MOVED: Workspace creation is now handled exclusively in the onboarding flow
|
||||
# to prevent premature creation before plan selection/onboarding.
|
||||
# See onboarding_control_service.py
|
||||
# try:
|
||||
# logger.info(f"Creating workspace for new subscriber {user_id}")
|
||||
# workspace_manager = UserWorkspaceManager(db)
|
||||
# workspace_manager.create_user_workspace(user_id)
|
||||
# except Exception as ws_error:
|
||||
# logger.error(f"Failed to create workspace for new subscriber {user_id}: {ws_error}")
|
||||
# # Don't fail the subscription if workspace creation fails, but log it
|
||||
|
||||
db.commit()
|
||||
|
||||
@@ -491,6 +538,15 @@ async def subscribe_to_plan(
|
||||
except Exception as reset_err:
|
||||
logger.error(f" ❌ Failed to reset usage after subscribe: {reset_err}", exc_info=True)
|
||||
|
||||
# Ensure user workspace is created/verified upon subscription
|
||||
try:
|
||||
workspace_manager = UserWorkspaceManager(db)
|
||||
workspace_manager.create_user_workspace(user_id)
|
||||
logger.info(f" ✅ User workspace verified/created for user {user_id}")
|
||||
except Exception as ws_err:
|
||||
# Log but don't fail the subscription response, as workspace can be created later
|
||||
logger.error(f" ⚠️ Failed to create user workspace during subscription: {ws_err}")
|
||||
|
||||
logger.info(f" ✅ Renewal completed: User {user_id} → {plan.name} ({billing_cycle})")
|
||||
logger.info("=" * 80)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user