/** * Per-user daily extraction quota enforcement. * * Plan tiers: * free: 10 extractions/day * pro: 100 extractions/day * enterprise: unlimited * * Usage tracked in Cosmos `extraction_usage` container (partition: /userId). */ import { z } from 'zod'; // ── Quota tiers ────────────────────────────────────────────────── const PLAN_QUOTAS: Record = { free: 10, pro: 100, enterprise: Infinity, }; export function getQuota(plan: string): number { return PLAN_QUOTAS[plan] ?? PLAN_QUOTAS.free; } // ── Usage document schema ──────────────────────────────────────── export const ExtractionUsageSchema = z.object({ id: z.string(), userId: z.string(), productId: z.string(), date: z.string(), // YYYY-MM-DD count: z.number().int().min(0), plan: z.string(), updatedAt: z.string(), }); export type ExtractionUsage = z.infer; // ── In-memory usage tracker (no Cosmos dependency for now) ─────── const usageStore = new Map(); function todayKey(): string { return new Date().toISOString().slice(0, 10); } function storeKey(userId: string): string { return `${userId}:${todayKey()}`; } /** * Check if user is within their daily quota. * Returns { allowed, remaining, limit, used }. */ export function checkQuota( userId: string, plan: string = 'free' ): { allowed: boolean; remaining: number; limit: number; used: number } { const limit = getQuota(plan); if (limit === Infinity) { return { allowed: true, remaining: Infinity, limit, used: 0 }; } const key = storeKey(userId); const entry = usageStore.get(key); const today = todayKey(); // Reset if new day const used = entry && entry.date === today ? entry.count : 0; const remaining = Math.max(0, limit - used); return { allowed: used < limit, remaining, limit, used }; } /** * Increment usage counter for user. Call after successful extraction. */ export function incrementUsage(userId: string, _plan: string = 'free'): void { const key = storeKey(userId); const today = todayKey(); const entry = usageStore.get(key); if (entry && entry.date === today) { entry.count++; } else { usageStore.set(key, { count: 1, date: today }); } } /** * Get usage summary for a user (for the usage reporting endpoint). */ export function getUsageSummary( userId: string, plan: string = 'free' ): { userId: string; date: string; used: number; limit: number; remaining: number; plan: string; } { const { used, limit, remaining } = checkQuota(userId, plan); return { userId, date: todayKey(), used, limit: limit === Infinity ? -1 : limit, remaining: remaining === Infinity ? -1 : remaining, plan, }; }