Subscription Guard and Installation Guide
This commit is contained in:
@@ -15,7 +15,7 @@ from services.usage_tracking_service import UsageTrackingService
|
||||
from services.pricing_service import PricingService
|
||||
from models.subscription_models import (
|
||||
APIProvider, SubscriptionPlan, UserSubscription, UsageSummary,
|
||||
APIProviderPricing, UsageAlert, SubscriptionTier, BillingCycle
|
||||
APIProviderPricing, UsageAlert, SubscriptionTier, BillingCycle, UsageStatus
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/api/subscription", tags=["subscription"])
|
||||
|
||||
@@ -97,9 +97,6 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Add API monitoring middleware for subscription enforcement
|
||||
app.middleware("http")(monitoring_middleware)
|
||||
|
||||
# Initialize modular utilities
|
||||
health_checker = HealthChecker()
|
||||
rate_limiter = RateLimiter(window_seconds=60, max_requests=200)
|
||||
@@ -107,17 +104,25 @@ frontend_serving = FrontendServing(app)
|
||||
router_manager = RouterManager(app)
|
||||
onboarding_manager = OnboardingManager(app)
|
||||
|
||||
# Middleware Order (FastAPI executes in REVERSE order of registration - LIFO):
|
||||
# Registration order: 1. Monitoring 2. Rate Limit 3. API Key Injection
|
||||
# Execution order: 1. API Key Injection (sets user_id) 2. Rate Limit 3. Monitoring (uses user_id)
|
||||
|
||||
# 1. FIRST REGISTERED (runs LAST) - Monitoring middleware
|
||||
app.middleware("http")(monitoring_middleware)
|
||||
|
||||
# 2. SECOND REGISTERED (runs SECOND) - Rate limiting
|
||||
@app.middleware("http")
|
||||
async def rate_limit_middleware(request: Request, call_next):
|
||||
"""Rate limiting middleware using modular utilities."""
|
||||
return await rate_limiter.rate_limit_middleware(request, call_next)
|
||||
|
||||
# API key injection middleware for production (user-specific keys)
|
||||
# 3. LAST REGISTERED (runs FIRST) - API key injection
|
||||
@app.middleware("http")
|
||||
async def inject_user_api_keys(request: Request, call_next):
|
||||
"""
|
||||
Inject user-specific API keys into environment for the request duration.
|
||||
This allows existing code using os.getenv() to work in production.
|
||||
Sets request.state.user_id for downstream middleware.
|
||||
"""
|
||||
from middleware.api_key_injection_middleware import api_key_injection_middleware
|
||||
return await api_key_injection_middleware(request, call_next)
|
||||
|
||||
@@ -42,6 +42,9 @@ class APIKeyInjectionMiddleware:
|
||||
# Try different possible keys for user_id
|
||||
user_id = user.get('user_id') or user.get('clerk_user_id') or user.get('id')
|
||||
logger.debug(f"[API Key Injection] Extracted user_id: {user_id}")
|
||||
|
||||
# Store user_id in request.state for monitoring middleware
|
||||
request.state.user_id = user_id
|
||||
except Exception as e:
|
||||
logger.debug(f"[API Key Injection] Could not extract user from token: {e}")
|
||||
|
||||
|
||||
@@ -466,13 +466,18 @@ async def monitoring_middleware(request: Request, call_next):
|
||||
# Extract request details - Enhanced user identification
|
||||
user_id = None
|
||||
try:
|
||||
# Check query parameters
|
||||
if hasattr(request, 'query_params') and 'user_id' in request.query_params:
|
||||
# PRIORITY 1: Check request.state.user_id (set by API key injection middleware)
|
||||
if hasattr(request.state, 'user_id') and request.state.user_id:
|
||||
user_id = request.state.user_id
|
||||
logger.debug(f"Monitoring: Using user_id from request.state: {user_id}")
|
||||
|
||||
# PRIORITY 2: Check query parameters
|
||||
elif hasattr(request, 'query_params') and 'user_id' in request.query_params:
|
||||
user_id = request.query_params['user_id']
|
||||
elif hasattr(request, 'path_params') and 'user_id' in request.path_params:
|
||||
user_id = request.path_params['user_id']
|
||||
|
||||
# Check headers for user identification
|
||||
# PRIORITY 3: Check headers for user identification
|
||||
elif 'x-user-id' in request.headers:
|
||||
user_id = request.headers['x-user-id']
|
||||
elif 'x-user-email' in request.headers:
|
||||
@@ -482,22 +487,24 @@ async def monitoring_middleware(request: Request, call_next):
|
||||
|
||||
# Check for authorization header with user info
|
||||
elif 'authorization' in request.headers:
|
||||
auth_header = request.headers['authorization']
|
||||
# Extract user info from JWT or other auth tokens if needed
|
||||
# For now, use a default user for testing
|
||||
user_id = "default_user"
|
||||
# Auth middleware should have set request.state.user_id
|
||||
# If not, skip usage limits (unauthenticated or auth will handle)
|
||||
user_id = None
|
||||
logger.debug("Monitoring: Auth header present but no user_id in state - skipping limits")
|
||||
|
||||
# For alpha testing, use IP address as user identifier if no other ID found
|
||||
if not user_id and request.client:
|
||||
# But only if there's no auth header (truly anonymous)
|
||||
elif not user_id and request.client and 'authorization' not in request.headers:
|
||||
user_id = f"alpha_user_{request.client.host}"
|
||||
|
||||
# Final fallback for testing
|
||||
if not user_id:
|
||||
user_id = "anonymous_user"
|
||||
# Final fallback: None (skip usage limits for truly anonymous/unauthenticated)
|
||||
# This prevents false positives for authenticated users
|
||||
else:
|
||||
user_id = None
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Error extracting user ID: {e}")
|
||||
user_id = "error_user"
|
||||
user_id = None # On error, skip usage limits
|
||||
|
||||
# Capture request body for usage tracking (read once, safely)
|
||||
request_body = None
|
||||
|
||||
Reference in New Issue
Block a user