From d13cce7a4646de9fbab4762e48cf15b6725f39f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D9=8A?= Date: Mon, 30 Mar 2026 07:38:19 +0530 Subject: [PATCH] Ensure subscription router is always mounted without duplicates --- backend/alwrity_utils/router_manager.py | 21 +++++++++++++++------ backend/app.py | 3 +++ backend/main.py | 3 +++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/backend/alwrity_utils/router_manager.py b/backend/alwrity_utils/router_manager.py index e582767b..d4010398 100644 --- a/backend/alwrity_utils/router_manager.py +++ b/backend/alwrity_utils/router_manager.py @@ -16,15 +16,22 @@ class RouterManager: self.app = app self.included_routers = [] self.failed_routers = [] + self._included_router_names = set() def include_router_safely(self, router, router_name: str = None) -> bool: """Include a router safely with error handling.""" verbose = os.getenv("ALWRITY_VERBOSE", "false").lower() == "true" + router_name = router_name or getattr(router, 'prefix', 'unknown') + + if router_name in self._included_router_names: + if verbose: + logger.info(f"↩️ Router already included, skipping duplicate: {router_name}") + return True try: self.app.include_router(router) - router_name = router_name or getattr(router, 'prefix', 'unknown') self.included_routers.append(router_name) + self._included_router_names.add(router_name) if verbose: logger.info(f"✅ Router included successfully: {router_name}") return True @@ -40,19 +47,21 @@ class RouterManager: # Import os locally to avoid UnboundLocalError if it's shadowed import os verbose = os.getenv("ALWRITY_VERBOSE", "false").lower() == "true" + demo_mode = os.getenv("ALWRITY_DEMO_MODE", "false").lower() == "true" try: if verbose: - logger.info("Including core routers...") + logger.info(f"Including core routers (demo_mode={demo_mode})...") + + # Subscription router MUST always be included (including demo mode) so + # payment/preflight/subscription endpoints remain available. + from api.subscription import router as subscription_router + self.include_router_safely(subscription_router, "subscription") # Component logic router from api.component_logic import router as component_logic_router self.include_router_safely(component_logic_router, "component_logic") - # Subscription router - from api.subscription import router as subscription_router - self.include_router_safely(subscription_router, "subscription") - # Step 3 Research router (core onboarding functionality) from api.onboarding_utils.step3_routes import router as step3_research_router self.include_router_safely(step3_research_router, "step3_research") diff --git a/backend/app.py b/backend/app.py index 5f636a09..1345914f 100644 --- a/backend/app.py +++ b/backend/app.py @@ -260,6 +260,9 @@ async def onboarding_status(): # Include routers using modular utilities router_manager.include_core_routers() +# Safety net: keep subscription routes available even if core inclusion flow changes +# in special modes (e.g., demo mode). De-dup is handled by RouterManager. +router_manager.include_router_safely(subscription_router, "subscription") router_manager.include_optional_routers() # Include assets serving router (must be mounted to serve generated images) diff --git a/backend/main.py b/backend/main.py index 7f5ee43f..049a6aeb 100644 --- a/backend/main.py +++ b/backend/main.py @@ -244,6 +244,9 @@ async def onboarding_status(): # Include routers using modular utilities router_manager.include_core_routers() +# Safety net: keep subscription routes available even if core inclusion flow changes +# in special modes (e.g., demo mode). De-dup is handled by RouterManager. +router_manager.include_router_safely(subscription_router, "subscription") router_manager.include_optional_routers() # SEO Dashboard endpoints