learning_ai_common_plat/services/platform-service/src/server.ts

126 lines
5.6 KiB
TypeScript

/**
* 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 { 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' });
await startService(app, { port: config.PORT, host: config.HOST });