From a264538c5eb7e76218af6a9841bd54949a3a3879 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Sun, 15 Feb 2026 14:44:31 -0800 Subject: [PATCH] feat(platform-service): register hook provisions subscription + license from product config - /auth/register now validates product from products cache - Automatically provisions initial subscription using product defaultPlan + trialDays - Automatically provisions initial license using product licensePrefix + deviceLimits - Keeps auth user creation as primary flow while adding provisioning side-effects - Verified: tsc --noEmit clean, 19 test files / 178 tests passing --- .../src/modules/auth/routes.ts | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/services/platform-service/src/modules/auth/routes.ts b/services/platform-service/src/modules/auth/routes.ts index e03ba0dd..418c8345 100644 --- a/services/platform-service/src/modules/auth/routes.ts +++ b/services/platform-service/src/modules/auth/routes.ts @@ -9,8 +9,10 @@ */ import type { FastifyInstance } from 'fastify'; -import { getRequestProductConfig } from '../../lib/request-context.js'; import { BadRequestError, UnauthorizedError } from '../../lib/errors.js'; +import { getProduct } from '../products/cache.js'; +import * as subscriptionRepo from '../subscriptions/repository.js'; +import * as licenseRepo from '../licenses/repository.js'; import * as repo from './repository.js'; import * as jwt from './jwt.js'; import { LoginSchema, RegisterSchema, RefreshSchema, type UserDoc } from './types.js'; @@ -61,7 +63,10 @@ export async function authRoutes(app: FastifyInstance) { throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; ')); } const { email, password, displayName, role, productId } = parsed.data; - const product = getRequestProductConfig(req); + const product = getProduct(productId); + if (!product || product.status !== 'active') { + throw new BadRequestError(`Unknown or disabled product: ${productId}`); + } const existing = await repo.getByEmail(email, productId); if (existing) throw new BadRequestError('Email already registered'); @@ -82,6 +87,46 @@ export async function authRoutes(app: FastifyInstance) { }; await repo.create(user); + const nowDate = new Date(); + const nowIso = nowDate.toISOString(); + const trialEnd = new Date(nowDate); + trialEnd.setDate(trialEnd.getDate() + product.trialDays); + const hasTrial = product.trialDays > 0; + const initialPlan = product.defaultPlan; + + // Registration hook: initialize subscription + license using product defaults. + // Best-effort during migration; auth account creation remains primary. + await subscriptionRepo.createSubscription({ + id: `sub_${user.id}_${Date.now()}`, + productId, + userId: user.id, + plan: initialPlan, + status: hasTrial ? 'trialing' : 'active', + currentPeriodStart: nowIso, + currentPeriodEnd: hasTrial ? trialEnd.toISOString() : nowIso, + cancelAtPeriodEnd: false, + monthlyPrice: 0, + tokensIncluded: 0, + tokensUsed: 0, + createdAt: nowIso, + updatedAt: nowIso, + }); + + await licenseRepo.create({ + id: `lic_${crypto.randomUUID()}`, + productId, + key: licenseRepo.generateKey(product.licensePrefix), + userId: user.id, + plan: initialPlan, + status: 'active', + activatedAt: null, + expiresAt: hasTrial ? trialEnd.toISOString() : null, + deviceIds: [], + maxDevices: product.deviceLimits[initialPlan], + createdAt: nowIso, + updatedAt: nowIso, + }); + const accessToken = await jwt.createAccessToken({ sub: user.id, email: user.email,