diff --git a/backend/alwrity_utils/feature_registry.py b/backend/alwrity_utils/feature_registry.py index f633d020..f7bd6119 100644 --- a/backend/alwrity_utils/feature_registry.py +++ b/backend/alwrity_utils/feature_registry.py @@ -58,6 +58,10 @@ FEATURE_GROUPS: Dict[str, FeatureGroup] = { "api.blog_writer.seo_analysis:router", ), ), + "backlinking": FeatureGroup( + features=("backlinking",), + routers=("routers.backlink_outreach:router",), + ), } @@ -67,5 +71,6 @@ PROFILE_GROUP_MAP: Dict[str, Tuple[str, ...]] = { "podcast": ("core", "podcast"), "youtube": ("core", "youtube"), "blog_writer": ("core", "blog_writer"), + "backlinking": ("core", "backlinking"), "planning": ("core", "content_planning"), } diff --git a/backend/alwrity_utils/router_manager.py b/backend/alwrity_utils/router_manager.py index 8856c2d2..7dc8ae86 100644 --- a/backend/alwrity_utils/router_manager.py +++ b/backend/alwrity_utils/router_manager.py @@ -67,6 +67,7 @@ OPTIONAL_ROUTER_REGISTRY = [ {"name": "oauth_token_monitoring", "module": "api.oauth_token_monitoring_routes", "attr": "router", "features": {"all", "core"}}, {"name": "agents", "module": "api.agents_api", "attr": "router", "features": {"all"}}, {"name": "today_workflow", "module": "api.today_workflow", "attr": "router", "features": {"all"}}, + {"name": "backlink_outreach", "module": "routers.backlink_outreach", "attr": "router", "features": {"all", "backlinking"}}, ] OPTIONAL_MODULE_MATRIX = { diff --git a/backend/app.py b/backend/app.py index 4711de78..4b5d157c 100644 --- a/backend/app.py +++ b/backend/app.py @@ -138,7 +138,6 @@ if _is_full_mode(): from routers.image_studio import router as image_studio_router from routers.product_marketing import router as product_marketing_router from routers.campaign_creator import router as campaign_creator_router - from routers.backlink_outreach import router as backlink_outreach_router else: # In feature-only modes, only load essential assets router from api.assets_serving import router as assets_serving_router @@ -147,7 +146,6 @@ else: image_studio_router = None product_marketing_router = None campaign_creator_router = None - backlink_outreach_router = None # Import hallucination detector router try: @@ -683,8 +681,6 @@ if _is_full_mode(): app.include_router(product_marketing_router) if campaign_creator_router: app.include_router(campaign_creator_router) - if backlink_outreach_router: - app.include_router(backlink_outreach_router) router_group_status["platform_extensions"] = { "mounted": True, @@ -799,6 +795,24 @@ async def startup_event(): else: logger.info(f"[FEATURE-MODE] Skipping scheduler startup (features: {enabled_features})") + # Recover stale YouTube tasks on startup + if _is_feature_enabled("youtube"): + try: + from api.youtube.task_manager import task_manager + from services.database import get_all_user_ids + user_ids = get_all_user_ids() + recovered = 0 + for uid in user_ids: + try: + count = task_manager.recover_stale_tasks(uid) + recovered += count + except Exception: + pass + if recovered > 0: + logger.info(f"[STARTUP] Recovered {recovered} stale YouTube tasks across {len(user_ids)} users") + except Exception as e: + logger.warning(f"[STARTUP] YouTube task recovery skipped: {e}") + # Check Wix configuration (OAuth-based, API key optional) wix_api_key = os.getenv('WIX_API_KEY') if wix_api_key: diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 410fcd29..43066444 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -193,7 +193,7 @@ const App: React.FC = () => { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/frontend/src/utils/demoMode.ts b/frontend/src/utils/demoMode.ts index 7a85a8af..2d156c40 100644 --- a/frontend/src/utils/demoMode.ts +++ b/frontend/src/utils/demoMode.ts @@ -28,6 +28,7 @@ export const FEATURE_KEYS = { WIX: 'wix', BING: 'bing', ASSET_LIBRARY: 'asset-library', + BACKLINKING: 'backlinking', } as const; export type FeatureKey = typeof FEATURE_KEYS[keyof typeof FEATURE_KEYS]; @@ -124,6 +125,7 @@ export function getSingleFeature(): string | null { const FEATURE_ROUTE_PRIORITY: [string, string][] = [ ['podcast', '/podcast-maker'], ['blog_writer', '/blog-writer'], + ['backlinking', '/backlink-outreach'], ['story', '/story-writer'], ['image', '/image-studio'], ['video', '/video-studio'],