Add subcription feature 3

This commit is contained in:
Urtzi Alfaro
2026-01-15 20:45:49 +01:00
parent a4c3b7da3f
commit b674708a4c
83 changed files with 9451 additions and 6828 deletions

View File

@@ -34,10 +34,15 @@ PUBLIC_ROUTES = [
"/api/v1/auth/register",
"/api/v1/auth/refresh",
"/api/v1/auth/verify",
"/api/v1/auth/start-registration", # Registration step 1 - SetupIntent creation
"/api/v1/auth/complete-registration", # Registration step 2 - Completion after 3DS
"/api/v1/auth/verify-email", # Email verification
"/api/v1/nominatim/search",
"/api/v1/plans",
"/api/v1/demo/accounts",
"/api/v1/demo/sessions"
"/api/v1/demo/sessions",
"/api/v1/webhooks/stripe", # Stripe webhook endpoint - bypasses auth for signature verification
"/api/v1/webhooks/generic" # Generic webhook endpoint
]
# Routes accessible with demo session (no JWT required, just demo session header)
@@ -74,7 +79,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
logger.info(f"Auth check - path: {request.url.path}, demo_header: {demo_session_header}, demo_query: {demo_session_query}, has_demo_state: {hasattr(request.state, 'is_demo_session')}")
# For SSE endpoint with demo_session_id in query params, validate it here
if request.url.path == "/api/events" and demo_session_query and not hasattr(request.state, "is_demo_session"):
if request.url.path == "/api/v1/events" and demo_session_query and not hasattr(request.state, "is_demo_session"):
logger.info(f"SSE endpoint with demo_session_id query param: {demo_session_query}")
# Validate demo session via demo-session service using JWT service token
import httpx
@@ -240,14 +245,14 @@ class AuthMiddleware(BaseHTTPMiddleware):
"""
Extract JWT token from Authorization header or query params for SSE.
For SSE endpoints (/api/events), browsers' EventSource API cannot send
For SSE endpoints (/api/v1/events), browsers' EventSource API cannot send
custom headers, so we must accept token as query parameter.
For all other routes, token must be in Authorization header (more secure).
Security note: Query param tokens are logged. Use short expiry and filter logs.
"""
# SSE endpoint exception: token in query param (EventSource API limitation)
if request.url.path == "/api/events":
if request.url.path == "/api/v1/events":
token = request.query_params.get("token")
if token:
logger.debug("Token extracted from query param for SSE endpoint")

View File

@@ -50,6 +50,7 @@ DEMO_ALLOWED_OPERATIONS = {
"/api/v1/tenants/batch/sales-summary",
"/api/v1/tenants/batch/production-summary",
"/api/v1/auth/me/onboarding/complete", # Allow completing onboarding (no-op for demos)
"/api/v1/tenants/*/notifications/send", # Allow notifications (ML insights, alerts, etc.)
# Note: Forecast generation is explicitly blocked (see DEMO_BLOCKED_PATHS)
],

View File

@@ -33,6 +33,7 @@ READ_ONLY_WHITELIST_PATTERNS = [
r'^/api/v1/tenants/.*/procurement/ml/insights/.*', # Allow ML insights (supplier analysis, price forecasting)
r'^/api/v1/tenants/.*/forecasting/ml/insights/.*', # Allow ML insights (rules generation)
r'^/api/v1/tenants/.*/forecasting/operations/.*', # Allow forecasting operations
r'^/api/v1/webhooks/.*', # Webhook endpoints - no tenant context
]
@@ -55,7 +56,7 @@ class ReadOnlyModeMiddleware(BaseHTTPMiddleware):
try:
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get(
f"{self.tenant_service_url}/api/v1/subscriptions/{tenant_id}/status",
f"{self.tenant_service_url}/api/v1/tenants/{tenant_id}/subscriptions/status",
headers={"Authorization": authorization}
)

View File

@@ -178,6 +178,7 @@ class SubscriptionMiddleware(BaseHTTPMiddleware):
r'/api/v1/auth/.*',
r'/api/v1/subscriptions/.*', # Subscription management itself
r'/api/v1/tenants/[^/]+/members.*', # Basic tenant info
r'/api/v1/webhooks/.*', # Webhook endpoints - no tenant context
r'/docs.*',
r'/openapi\.json',
# Training monitoring endpoints (WebSocket and status checks)