/** * Platform Service — Fastify server entry point. * * Modules: auth, audit, notifications, feature flags, blob, * invitations, referrals, promos (merged from growth-service), * subscriptions, usage, plans, licenses, stripe (merged from billing-service), * items, comments, votes, public (merged from tracker-service). * Port: 4003 (configurable via PORT env var). */ // Resolve secrets from Azure Key Vault BEFORE config parsing import { resolveKeyVaultSecrets, LYSNR_SECRETS } from '@bytelyst/config'; await resolveKeyVaultSecrets([ LYSNR_SECRETS.COSMOS_KEY, LYSNR_SECRETS.COSMOS_ENDPOINT, LYSNR_SECRETS.JWT_SECRET, LYSNR_SECRETS.STRIPE_SECRET_KEY, LYSNR_SECRETS.STRIPE_WEBHOOK_SECRET, LYSNR_SECRETS.AZURE_BLOB_CONNECTION_STRING, LYSNR_SECRETS.AZURE_BLOB_ACCOUNT_KEY, ]); import { createServiceApp, startService } from '@bytelyst/fastify-core'; import { productRoutes } from './modules/products/routes.js'; import { loadProductCache } from './modules/products/cache.js'; import { authRoutes } from './modules/auth/routes.js'; import { auditRoutes } from './modules/audit/routes.js'; import { notificationRoutes } from './modules/notifications/routes.js'; import { flagRoutes } from './modules/flags/routes.js'; import { rateLimitRoutes } from './modules/ratelimit/routes.js'; import { blobRoutes } from './modules/blob/routes.js'; import { invitationRoutes } from './modules/invitations/routes.js'; import { referralRoutes } from './modules/referrals/routes.js'; import { promoRoutes } from './modules/promos/routes.js'; import { subscriptionRoutes } from './modules/subscriptions/routes.js'; import { usageRoutes } from './modules/usage/routes.js'; import { planRoutes } from './modules/plans/routes.js'; import { licenseRoutes } from './modules/licenses/routes.js'; import { stripeRoutes } from './modules/stripe/routes.js'; import { settingsRoutes } from './modules/settings/routes.js'; import { itemRoutes } from './modules/items/routes.js'; import { commentRoutes } from './modules/comments/routes.js'; import { voteRoutes } from './modules/votes/routes.js'; import { memoryRoutes } from './modules/memory/routes.js'; import { publicRoutes } from './modules/public/routes.js'; import { tokenRoutes } from './modules/tokens/routes.js'; import { themeRoutes } from './modules/themes/routes.js'; import { waitlistRoutes } from './modules/waitlist/routes.js'; import { telemetryRoutes } from './modules/telemetry/routes.js'; import { fastingSessionRoutes } from './modules/fasting-sessions/routes.js'; import { fastingProtocolRoutes } from './modules/fasting-protocols/routes.js'; import { bodyStageRoutes } from './modules/body-stages/routes.js'; import { socialFastingRoutes } from './modules/social-fasting/routes.js'; import { mealLogRoutes } from './modules/meal-log/routes.js'; import { timerRoutes } from './modules/timers/routes.js'; import { routineRoutes } from './modules/routines/routes.js'; import { householdRoutes } from './modules/households/routes.js'; import { sharedTimerRoutes } from './modules/shared-timers/routes.js'; import { webhookRoutes } from './modules/webhooks/routes.js'; import { initCosmosIfNeeded } from './lib/cosmos-init.js'; import { config } from './lib/config.js'; await initCosmosIfNeeded(); await loadProductCache(); const app = await createServiceApp({ name: 'platform-service', version: '0.1.0', description: 'Auth, audit, notifications, feature flags, rate limiting, invitations, referrals, promos, subscriptions, usage, plans, licenses, stripe', corsOrigin: config.CORS_ORIGIN, swagger: { title: 'Platform Service', description: 'Auth, audit, notifications, feature flags, rate limiting, invitations, referrals, promos, subscriptions, usage, plans, licenses, stripe', port: config.PORT, }, metrics: true, }); // Parse JWT on every request (best-effort — doesn't block unauthenticated routes) import { verifyToken } from './modules/auth/jwt.js'; import type { JwtPayload } from './lib/request-context.js'; app.addHook('onRequest', async req => { const auth = req.headers.authorization; if (!auth?.startsWith('Bearer ')) return; try { const payload = await verifyToken(auth.slice(7)); req.jwtPayload = payload as JwtPayload; } catch { // Token invalid/expired — leave jwtPayload undefined. // Auth-required routes will handle this in their own validation. } }); // Register route modules await app.register(productRoutes, { prefix: '/api' }); await app.register(authRoutes, { prefix: '/api' }); await app.register(auditRoutes, { prefix: '/api' }); await app.register(notificationRoutes, { prefix: '/api' }); await app.register(flagRoutes, { prefix: '/api' }); await app.register(rateLimitRoutes, { prefix: '/api' }); await app.register(blobRoutes, { prefix: '/api' }); // Growth modules (merged from growth-service) await app.register(invitationRoutes, { prefix: '/api' }); await app.register(referralRoutes, { prefix: '/api' }); await app.register(promoRoutes, { prefix: '/api' }); // Billing modules (merged from billing-service) await app.register(subscriptionRoutes, { prefix: '/api' }); await app.register(usageRoutes, { prefix: '/api' }); await app.register(planRoutes, { prefix: '/api' }); await app.register(licenseRoutes, { prefix: '/api' }); // Stripe routes outside billing scope (webhook has its own signature verification) await app.register(stripeRoutes, { prefix: '/api' }); // Settings module (user-level global settings + per-device overrides) await app.register(settingsRoutes, { prefix: '/api' }); // Tracker modules (merged from tracker-service) await app.register(itemRoutes, { prefix: '/api' }); await app.register(commentRoutes, { prefix: '/api' }); await app.register(voteRoutes, { prefix: '/api' }); // Mobile capture modules await app.register(memoryRoutes, { prefix: '/api' }); // API tokens module await app.register(tokenRoutes, { prefix: '/api' }); // Themes module await app.register(themeRoutes, { prefix: '/api' }); // Waitlist module (pre-launch signups — public + admin routes) await app.register(waitlistRoutes, { prefix: '/api' }); // Telemetry module (client ingest + admin query + policies) await app.register(telemetryRoutes, { prefix: '/api' }); // Public routes — no auth, registered at top level await app.register(publicRoutes, { prefix: '/api' }); // NomGap fasting modules await app.register(fastingSessionRoutes, { prefix: '/api' }); await app.register(fastingProtocolRoutes, { prefix: '/api' }); await app.register(bodyStageRoutes, { prefix: '/api' }); await app.register(socialFastingRoutes, { prefix: '/api' }); await app.register(mealLogRoutes, { prefix: '/api' }); // ChronoMind modules await app.register(timerRoutes, { prefix: '/api' }); await app.register(routineRoutes, { prefix: '/api' }); await app.register(householdRoutes, { prefix: '/api' }); await app.register(sharedTimerRoutes, { prefix: '/api' }); // Webhooks module (subscriptions + event dispatch) await app.register(webhookRoutes, { prefix: '/api' }); await startService(app, { port: config.PORT, host: config.HOST });