Add subcription feature 3
This commit is contained in:
@@ -25,7 +25,7 @@ from app.middleware.rate_limiting import APIRateLimitMiddleware
|
||||
from app.middleware.subscription import SubscriptionMiddleware
|
||||
from app.middleware.demo_middleware import DemoMiddleware
|
||||
from app.middleware.read_only_mode import ReadOnlyModeMiddleware
|
||||
from app.routes import auth, tenant, nominatim, subscription, demo, pos, geocoding, poi_context, webhooks
|
||||
from app.routes import auth, tenant, registration, nominatim, subscription, demo, pos, geocoding, poi_context, webhooks
|
||||
|
||||
# Initialize logger
|
||||
logger = structlog.get_logger()
|
||||
@@ -40,36 +40,61 @@ try:
|
||||
except Exception as e:
|
||||
logger.debug(f"Could not check file descriptor limits: {e}")
|
||||
|
||||
# Redis client for SSE streaming
|
||||
# Global Redis client for SSE streaming
|
||||
redis_client = None
|
||||
|
||||
|
||||
class GatewayService(StandardFastAPIService):
|
||||
"""Gateway Service with standardized monitoring setup"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
# Initialize HeaderManager early
|
||||
header_manager.initialize()
|
||||
logger.info("HeaderManager initialized")
|
||||
|
||||
# Initialize Redis during service creation so it's available when needed
|
||||
try:
|
||||
# We need to run the async initialization in a sync context
|
||||
import asyncio
|
||||
try:
|
||||
# Check if there's already a running event loop
|
||||
loop = asyncio.get_running_loop()
|
||||
# If there is, we'll initialize Redis later in on_startup
|
||||
self.redis_initialized = False
|
||||
self.redis_client = None
|
||||
except RuntimeError:
|
||||
# No event loop running, safe to run the async function
|
||||
import asyncio
|
||||
import nest_asyncio
|
||||
nest_asyncio.apply() # Allow nested event loops
|
||||
|
||||
async def init_redis():
|
||||
await initialize_redis(settings.REDIS_URL, db=0, max_connections=50)
|
||||
return await get_redis_client()
|
||||
|
||||
self.redis_client = asyncio.run(init_redis())
|
||||
self.redis_initialized = True
|
||||
logger.info("Connected to Redis for SSE streaming")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize Redis during service creation: {e}")
|
||||
self.redis_initialized = False
|
||||
self.redis_client = None
|
||||
|
||||
async def on_startup(self, app):
|
||||
"""Custom startup logic for Gateway"""
|
||||
global redis_client
|
||||
|
||||
# Initialize HeaderManager
|
||||
header_manager.initialize()
|
||||
logger.info("HeaderManager initialized")
|
||||
|
||||
# Initialize Redis
|
||||
try:
|
||||
await initialize_redis(settings.REDIS_URL, db=0, max_connections=50)
|
||||
redis_client = await get_redis_client()
|
||||
logger.info("Connected to Redis for SSE streaming")
|
||||
|
||||
# Add API rate limiting middleware with Redis client
|
||||
app.add_middleware(APIRateLimitMiddleware, redis_client=redis_client)
|
||||
logger.info("API rate limiting middleware enabled")
|
||||
|
||||
# NOTE: SubscriptionMiddleware and AuthMiddleware are instantiated without redis_client
|
||||
# They will gracefully degrade (skip Redis-dependent features) when redis_client is None
|
||||
# For future enhancement: consider using lifespan context to inject redis_client into middleware
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to Redis: {e}")
|
||||
# Initialize Redis if not already done during service creation
|
||||
if not self.redis_initialized:
|
||||
try:
|
||||
await initialize_redis(settings.REDIS_URL, db=0, max_connections=50)
|
||||
self.redis_client = await get_redis_client()
|
||||
redis_client = self.redis_client # Update global variable
|
||||
self.redis_initialized = True
|
||||
logger.info("Connected to Redis for SSE streaming")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to Redis during startup: {e}")
|
||||
|
||||
# Register custom metrics for gateway-specific operations
|
||||
if self.telemetry_providers and self.telemetry_providers.app_metrics:
|
||||
@@ -103,6 +128,23 @@ service = GatewayService(
|
||||
# Create FastAPI app
|
||||
app = service.create_app()
|
||||
|
||||
# Add API rate limiting middleware with Redis client - this needs to be done after app creation
|
||||
# but before other middleware that might depend on it
|
||||
# Wait for Redis to be initialized if not already done
|
||||
if not hasattr(service, 'redis_client') or not service.redis_client:
|
||||
# Wait briefly for Redis initialization to complete
|
||||
import time
|
||||
time.sleep(1)
|
||||
# Check again after allowing time for initialization
|
||||
if hasattr(service, 'redis_client') and service.redis_client:
|
||||
app.add_middleware(APIRateLimitMiddleware, redis_client=service.redis_client)
|
||||
logger.info("API rate limiting middleware enabled")
|
||||
else:
|
||||
logger.warning("Redis client not available for API rate limiting middleware")
|
||||
else:
|
||||
app.add_middleware(APIRateLimitMiddleware, redis_client=service.redis_client)
|
||||
logger.info("API rate limiting middleware enabled")
|
||||
|
||||
# Add gateway-specific middleware (in REVERSE order of execution)
|
||||
# Execution order: RequestIDMiddleware -> DemoMiddleware -> AuthMiddleware -> ReadOnlyModeMiddleware -> SubscriptionMiddleware -> APIRateLimitMiddleware -> RateLimitMiddleware -> LoggingMiddleware
|
||||
app.add_middleware(LoggingMiddleware)
|
||||
@@ -115,6 +157,7 @@ app.add_middleware(RequestIDMiddleware)
|
||||
|
||||
# Include routers
|
||||
app.include_router(auth.router, prefix="/api/v1/auth", tags=["authentication"])
|
||||
app.include_router(registration.router, prefix="/api/v1", tags=["registration"])
|
||||
app.include_router(tenant.router, prefix="/api/v1/tenants", tags=["tenants"])
|
||||
app.include_router(subscription.router, prefix="/api/v1", tags=["subscriptions"])
|
||||
# Notification routes are now handled by tenant router at /api/v1/tenants/{tenant_id}/notifications/*
|
||||
@@ -122,9 +165,9 @@ app.include_router(nominatim.router, prefix="/api/v1/nominatim", tags=["location
|
||||
app.include_router(geocoding.router, prefix="/api/v1/geocoding", tags=["geocoding"])
|
||||
app.include_router(pos.router, prefix="/api/v1/pos", tags=["pos"])
|
||||
app.include_router(demo.router, prefix="/api/v1", tags=["demo"])
|
||||
app.include_router(webhooks.router, prefix="/api/v1", tags=["webhooks"])
|
||||
# Also include webhooks at /webhooks prefix to support direct webhook URLs like /webhooks/stripe
|
||||
app.include_router(webhooks.router, prefix="/webhooks", tags=["webhooks-external"])
|
||||
# Include webhooks at the root level to handle /api/v1/webhooks/*
|
||||
# Webhook routes are defined with full /api/v1/webhooks/* paths for consistency
|
||||
app.include_router(webhooks.router, prefix="", tags=["webhooks"])
|
||||
|
||||
|
||||
# ================================================================
|
||||
@@ -257,7 +300,7 @@ def _determine_event_type(event_data: dict) -> str:
|
||||
# SERVER-SENT EVENTS (SSE) ENDPOINT
|
||||
# ================================================================
|
||||
|
||||
@app.get("/api/events")
|
||||
@app.get("/api/v1/events")
|
||||
async def events_stream(
|
||||
request: Request,
|
||||
tenant_id: str,
|
||||
|
||||
Reference in New Issue
Block a user