TypeScript 98.3%
CSS 0.9%
Shell 0.7%
1// Suivi d'usage et budgets : plafonds quotidiens/mensuels par utilisateur + budget global,2// appliqués côté serveur AVANT chaque appel de modèle.34import { get, getSetting, run } from "./db/index.ts";56export type BudgetConfig = {7 dailyPerUserUSD: number;8 monthlyPerUserUSD: number;9 monthlyGlobalUSD: number;10 dailyRequestsPerUser: number;11};1213export const DEFAULT_BUDGETS: BudgetConfig = {14 dailyPerUserUSD: 1.5,15 monthlyPerUserUSD: 15,16 monthlyGlobalUSD: 200,17 dailyRequestsPerUser: 200,18};1920export function getBudgets(): BudgetConfig {21 return { ...DEFAULT_BUDGETS, ...getSetting<Partial<BudgetConfig>>("budgets", {}) };22}2324export function checkBudget(userId: number): { ok: boolean; reason?: string } {25 const b = getBudgets();26 const day = get<{ c: number; n: number }>(27 "SELECT COALESCE(SUM(cost),0) as c, COUNT(*) as n FROM usage_log WHERE user_id = ? AND created_at >= datetime('now','start of day')",28 userId29 );30 if ((day?.n ?? 0) >= b.dailyRequestsPerUser)31 return { ok: false, reason: "Limite quotidienne de requêtes atteinte. Réessayez demain." };32 if ((day?.c ?? 0) >= b.dailyPerUserUSD)33 return { ok: false, reason: "Budget quotidien atteint. Réessayez demain ou choisissez un modèle économique." };34 const month = get<{ c: number }>(35 "SELECT COALESCE(SUM(cost),0) as c FROM usage_log WHERE user_id = ? AND created_at >= datetime('now','start of month')",36 userId37 );38 if ((month?.c ?? 0) >= b.monthlyPerUserUSD) return { ok: false, reason: "Budget mensuel personnel atteint." };39 const global = get<{ c: number }>(40 "SELECT COALESCE(SUM(cost),0) as c FROM usage_log WHERE created_at >= datetime('now','start of month')"41 );42 if ((global?.c ?? 0) >= b.monthlyGlobalUSD)43 return { ok: false, reason: "Budget global de la plateforme atteint ce mois-ci. Contactez le professeur." };44 return { ok: true };45}4647export function logUsage(opts: {48 userId: number;49 model: string;50 kind?: string;51 tokensIn: number;52 tokensOut: number;53 cost: number;54 latencyMs: number;55 ok?: boolean;56 error?: string;57}) {58 run(59 "INSERT INTO usage_log (user_id, model, kind, tokens_in, tokens_out, cost, latency_ms, ok, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",60 opts.userId, opts.model, opts.kind ?? "chat", opts.tokensIn, opts.tokensOut, opts.cost, opts.latencyMs, opts.ok === false ? 0 : 1, opts.error ?? null61 );62}6364export function logActivity(userId: number, kind: string, courseCode?: string | null, durationS = 0, meta: unknown = {}) {65 run(66 "INSERT INTO activity_log (user_id, kind, course_code, duration_s, meta) VALUES (?, ?, ?, ?, ?)",67 userId, kind, courseCode ?? null, durationS, JSON.stringify(meta)68 );69}70