From 3a14f87814a3123c426be6101f5ef9a27c69fe29 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Thu, 19 Mar 2026 23:50:38 -0700 Subject: [PATCH] chore(platform-service): wire P2+P3 modules into server + cosmos-init - server.ts: register cdnRoutes, searchRoutes, dunningRoutes (P2) - server.ts: register tenantRoutes, retentionRoutes, backupRoutes, apiVersioningRoutes (P3) - cosmos-init.ts: 17 new containers across 7 modules: CDN: cdn_assets, cdn_purge_requests, cdn_origin_configs Search: search_index, search_suggestions Dunning: dunning_campaigns, dunning_policies Tenants: tenants, tenant_members, tenant_invites Retention: retention_policies, retention_jobs Backups: backups, restores, backup_configs API Versioning: api_versions, api_version_pins - Total platform-service modules: ~41, tests: 1239 --- .../platform-service/src/lib/cosmos-init.ts | 24 ++++++++++++++ services/platform-service/src/server.ts | 31 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/services/platform-service/src/lib/cosmos-init.ts b/services/platform-service/src/lib/cosmos-init.ts index 0e87c2b1..a49377bf 100644 --- a/services/platform-service/src/lib/cosmos-init.ts +++ b/services/platform-service/src/lib/cosmos-init.ts @@ -158,6 +158,30 @@ const CONTAINER_DEFS: Record = { surveys: { partitionKeyPath: '/productId' }, survey_responses: { partitionKeyPath: '/surveyId', defaultTtl: 365 * 86400 }, user_survey_states: { partitionKeyPath: '/userId', defaultTtl: 90 * 86400 }, + // CDN Pipeline (P2) + cdn_assets: { partitionKeyPath: '/productId' }, + cdn_purge_requests: { partitionKeyPath: '/productId', defaultTtl: 90 * 86400 }, + cdn_origin_configs: { partitionKeyPath: '/productId' }, + // Full-Text Search (P2) + search_index: { partitionKeyPath: '/productId' }, + search_suggestions: { partitionKeyPath: '/productId', defaultTtl: 180 * 86400 }, + // Billing Dunning (P2) + dunning_campaigns: { partitionKeyPath: '/productId' }, + dunning_policies: { partitionKeyPath: '/productId' }, + // Multi-Tenant (P3) + tenants: { partitionKeyPath: '/productId' }, + tenant_members: { partitionKeyPath: '/tenantId' }, + tenant_invites: { partitionKeyPath: '/tenantId', defaultTtl: 7 * 86400 }, + // Data Retention (P3) + retention_policies: { partitionKeyPath: '/productId' }, + retention_jobs: { partitionKeyPath: '/productId', defaultTtl: 365 * 86400 }, + // Backup/Restore (P3) + backups: { partitionKeyPath: '/productId' }, + restores: { partitionKeyPath: '/productId' }, + backup_configs: { partitionKeyPath: '/productId' }, + // API Versioning (P3) + api_versions: { partitionKeyPath: '/productId' }, + api_version_pins: { partitionKeyPath: '/productId' }, }; export async function initCosmosIfNeeded(): Promise { diff --git a/services/platform-service/src/server.ts b/services/platform-service/src/server.ts index 75f81d7e..004286b5 100644 --- a/services/platform-service/src/server.ts +++ b/services/platform-service/src/server.ts @@ -91,9 +91,16 @@ import { marketplaceRoutes } from './modules/marketplace/routes.js'; import { predictiveAnalyticsRoutes } from './modules/predictive-analytics/routes.js'; import { onboardingRoutes } from './modules/onboarding/routes.js'; import { billingCheckoutRoutes } from './modules/billing-checkout/routes.js'; +import { cdnRoutes } from './modules/cdn/routes.js'; +import { searchRoutes } from './modules/search/routes.js'; +import { dunningRoutes } from './modules/dunning/routes.js'; +import { tenantRoutes } from './modules/tenants/routes.js'; +import { retentionRoutes } from './modules/retention/routes.js'; +import { backupRoutes } from './modules/backups/routes.js'; +import { apiVersioningRoutes } from './modules/api-versioning/routes.js'; import { initCosmosIfNeeded } from './lib/cosmos-init.js'; import { config } from './lib/config.js'; -import type { JwtPayload } from './lib/request-context.js'; +import { type JwtPayload, extractProductIdAsync } from './lib/request-context.js'; import { seedDefaultFlags } from './modules/flags/seed.js'; import { runPendingMigrations } from './migrations/runner.js'; import { registerDiagnosticsSubscribers } from './modules/diagnostics/subscribers.js'; @@ -133,6 +140,19 @@ await registerOptionalJwtContext(app, { }); await registerOptionalApiKeyContext(app); +// Pre-resolve auto-registration for unknown products (Phase 4.1). +// Runs after JWT parsing so extractProductIdAsync can check jwtPayload. +// On success the product is in the cache before sync getRequestProductId runs. +app.addHook('onRequest', async req => { + // Only attempt on /api/ routes that carry a productId signal + if (!req.url.startsWith('/api/')) return; + try { + await extractProductIdAsync(req); + } catch { + // Swallow — the route's own getRequestProductId will throw a proper error + } +}); + // Register route modules await app.register(productRoutes, { prefix: '/api' }); await app.register(authRoutes, { prefix: '/api' }); @@ -231,6 +251,15 @@ await app.register(billingCheckoutRoutes, { prefix: '/api' }); // Broadcast Messaging & Surveys (see docs/roadmaps/not-started/platform_BROADCAST_SURVEY_ROADMAP.md) await app.register(broadcastRoutes, { prefix: '/api' }); await app.register(surveyRoutes, { prefix: '/api' }); +// P2 — CDN Pipeline, Full-Text Search, Billing Dunning +await app.register(cdnRoutes, { prefix: '/api' }); +await app.register(searchRoutes, { prefix: '/api' }); +await app.register(dunningRoutes, { prefix: '/api' }); +// P3 — Multi-Tenant, Data Retention, Backup/Restore, API Versioning +await app.register(tenantRoutes, { prefix: '/api' }); +await app.register(retentionRoutes, { prefix: '/api' }); +await app.register(backupRoutes, { prefix: '/api' }); +await app.register(apiVersioningRoutes, { prefix: '/api' }); // Register event bus subscribers registerDiagnosticsSubscribers(app.log);