2025-09-21 13:27:50 +02:00
"""
Subscription routes for API Gateway - Direct subscription endpoints
"""
from fastapi import APIRouter , Request , Response , HTTPException , Path
from fastapi . responses import JSONResponse
import httpx
import logging
from typing import Optional
from app . core . config import settings
2026-01-12 22:15:11 +01:00
from app . core . header_manager import header_manager
2025-09-21 13:27:50 +02:00
logger = logging . getLogger ( __name__ )
router = APIRouter ( )
# ================================================================
# SUBSCRIPTION ENDPOINTS - Direct routing to tenant service
# ================================================================
2025-10-07 07:15:07 +02:00
@router.api_route ( " /tenants/subscriptions/ {tenant_id} / { path:path} " , methods = [ " GET " , " POST " , " PUT " , " DELETE " , " OPTIONS " ] )
2025-09-21 13:27:50 +02:00
async def proxy_subscription_endpoints ( request : Request , tenant_id : str = Path ( . . . ) , path : str = " " ) :
""" Proxy subscription requests directly to tenant service """
2025-10-07 07:15:07 +02:00
target_path = f " /api/v1/tenants/subscriptions/ { tenant_id } / { path } " . rstrip ( " / " )
2025-09-21 13:27:50 +02:00
return await _proxy_to_tenant_service ( request , target_path )
@router.api_route ( " /subscriptions/plans " , methods = [ " GET " , " OPTIONS " ] )
async def proxy_subscription_plans ( request : Request ) :
""" Proxy subscription plans request to tenant service """
2025-10-15 16:12:49 +02:00
target_path = " /plans "
2025-09-25 14:30:47 +02:00
return await _proxy_to_tenant_service ( request , target_path )
@router.api_route ( " /plans " , methods = [ " GET " , " OPTIONS " ] )
async def proxy_plans ( request : Request ) :
""" Proxy plans request to tenant service """
2025-10-15 16:12:49 +02:00
target_path = " /plans "
2025-09-21 13:27:50 +02:00
return await _proxy_to_tenant_service ( request , target_path )
2025-10-31 11:54:19 +01:00
@router.api_route ( " /subscriptions/ {tenant_id} /invoices " , methods = [ " GET " , " OPTIONS " ] )
async def proxy_invoices ( request : Request , tenant_id : str = Path ( . . . ) ) :
""" Proxy invoices request to tenant service """
target_path = f " /api/v1/subscriptions/ { tenant_id } /invoices "
return await _proxy_to_tenant_service ( request , target_path )
@router.api_route ( " /subscriptions/ {tenant_id} /status " , methods = [ " GET " , " OPTIONS " ] )
async def proxy_subscription_status ( request : Request , tenant_id : str = Path ( . . . ) ) :
""" Proxy subscription status request to tenant service """
target_path = f " /api/v1/subscriptions/ { tenant_id } /status "
return await _proxy_to_tenant_service ( request , target_path )
@router.api_route ( " /subscriptions/cancel " , methods = [ " POST " , " OPTIONS " ] )
async def proxy_subscription_cancel ( request : Request ) :
""" Proxy subscription cancellation request to tenant service """
target_path = " /api/v1/subscriptions/cancel "
return await _proxy_to_tenant_service ( request , target_path )
2026-01-13 22:22:38 +01:00
@router.api_route ( " /subscriptions/create-for-registration " , methods = [ " POST " , " OPTIONS " ] )
async def proxy_create_for_registration ( request : Request ) :
""" Proxy create-for-registration request to tenant service """
target_path = " /api/v1/subscriptions/create-for-registration "
return await _proxy_to_tenant_service ( request , target_path )
@router.api_route ( " /payment-customers/create " , methods = [ " POST " , " OPTIONS " ] )
async def proxy_payment_customer_create ( request : Request ) :
""" Proxy payment customer creation request to tenant service """
target_path = " /api/v1/payment-customers/create "
return await _proxy_to_tenant_service ( request , target_path )
2025-10-31 11:54:19 +01:00
@router.api_route ( " /subscriptions/reactivate " , methods = [ " POST " , " OPTIONS " ] )
async def proxy_subscription_reactivate ( request : Request ) :
""" Proxy subscription reactivation request to tenant service """
target_path = " /api/v1/subscriptions/reactivate "
return await _proxy_to_tenant_service ( request , target_path )
2026-01-11 19:38:54 +01:00
@router.api_route ( " /usage-forecast " , methods = [ " GET " , " OPTIONS " ] )
async def proxy_usage_forecast ( request : Request ) :
""" Proxy usage forecast request to tenant service """
target_path = " /api/v1/usage-forecast "
return await _proxy_to_tenant_service ( request , target_path )
@router.api_route ( " /usage-forecast/track-usage " , methods = [ " POST " , " OPTIONS " ] )
async def proxy_track_usage ( request : Request ) :
""" Proxy track usage request to tenant service """
target_path = " /api/v1/usage-forecast/track-usage "
return await _proxy_to_tenant_service ( request , target_path )
2025-09-21 13:27:50 +02:00
# ================================================================
# PROXY HELPER FUNCTIONS
# ================================================================
async def _proxy_to_tenant_service ( request : Request , target_path : str ) :
""" Proxy request to tenant service """
return await _proxy_request ( request , target_path , settings . TENANT_SERVICE_URL )
async def _proxy_request ( request : Request , target_path : str , service_url : str ) :
""" Generic proxy function with enhanced error handling """
# Handle OPTIONS requests directly for CORS
if request . method == " OPTIONS " :
return Response (
status_code = 200 ,
headers = {
" Access-Control-Allow-Origin " : settings . CORS_ORIGINS_LIST ,
" Access-Control-Allow-Methods " : " GET, POST, PUT, DELETE, OPTIONS " ,
" Access-Control-Allow-Headers " : " Content-Type, Authorization, X-Tenant-ID " ,
" Access-Control-Allow-Credentials " : " true " ,
" Access-Control-Max-Age " : " 86400 "
}
)
try :
url = f " { service_url } { target_path } "
2026-01-12 22:15:11 +01:00
# Use unified HeaderManager for consistent header forwarding
headers = header_manager . get_all_headers_for_proxy ( request )
# Debug logging
user_context = getattr ( request . state , ' user ' , None )
if user_context :
logger . info ( f " Forwarding subscription request to { url } with user context: user_id= { user_context . get ( ' user_id ' ) } , email= { user_context . get ( ' email ' ) } , subscription_tier= { user_context . get ( ' subscription_tier ' , ' not_set ' ) } " )
2025-09-21 13:27:50 +02:00
else :
logger . warning ( f " No user context available when forwarding subscription request to { url } " )
# Get request body if present
body = None
if request . method in [ " POST " , " PUT " , " PATCH " ] :
body = await request . body ( )
# Add query parameters
params = dict ( request . query_params )
timeout_config = httpx . Timeout (
connect = 30.0 ,
read = 60.0 ,
write = 30.0 ,
pool = 30.0
)
async with httpx . AsyncClient ( timeout = timeout_config ) as client :
response = await client . request (
method = request . method ,
url = url ,
headers = headers ,
content = body ,
params = params
)
# Handle different response types
if response . headers . get ( " content-type " , " " ) . startswith ( " application/json " ) :
try :
content = response . json ( )
except :
content = { " message " : " Invalid JSON response from service " }
else :
content = response . text
return JSONResponse (
status_code = response . status_code ,
content = content
)
except Exception as e :
logger . error ( f " Unexpected error proxying subscription request to { service_url } { target_path } : { e } " )
raise HTTPException (
status_code = 500 ,
detail = " Internal gateway error "
)