/** * GET /api/stats * * Statistiques d'usage pour le bandeau bas (transparence IA + compteurs semaine). * * Query params : * periode = "mois" (défaut) | "semaine" * * Payload retourné : * cout_mois_eur number — somme cout_eur du mois courant * budget_mois 20 — budget fixe mensuel * tokens_mois number — somme tokens_in + tokens_out du mois * co2_kg number — estimation CO2 (tokens × 0.000001 × 0.052 kgCO2eq/kWh mix RTE) * requetes_mois number — count rows du mois * fiches_semaine number — orgs approuvées créées dans les 7 derniers jours * requetes_chatbot_semaine number — count stats_usage endpoint=chatbot dans les 7j */ export default defineEventHandler(async (event) => { const config = useRuntimeConfig() const nocodbUrl = config.nocodbUrl as string const nocodbToken = config.nocodbToken as string const statsTableId = (config.statsTableId as string) || 'mbbq7n47ixy19mc' const orgTableId = config.orgTableId as string const nocoBaseId = process.env.NOCODB_BASE_ID || 'p_nav_v2' const headers = { 'xc-token': nocodbToken } // ── Dates ──────────────────────────────────────────────────────────────── const now = new Date() const moisDebut = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10) const semaineDebut = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10) // ── Helpers fetch NocoDB ───────────────────────────────────────────────── async function fetchStats(where: string): Promise<{ list: Record[] }> { try { return await $fetch(`${nocodbUrl}/api/v1/db/data/noco/${nocoBaseId}/${statsTableId}`, { headers, query: { where, limit: 1000, fields: 'cout_eur,tokens_in,tokens_out,endpoint,created_at' }, }) as { list: Record[] } } catch { return { list: [] } } } async function fetchOrgsRecentes(since: string): Promise { if (!orgTableId) return 0 try { const res = await $fetch<{ pageInfo?: { totalRows?: number } }>( `${nocodbUrl}/api/v1/db/data/noco/${nocoBaseId}/${orgTableId}`, { headers, query: { where: `(moderation_status,eq,approved)~and(created_at,gte,${since})`, limit: 1, fields: 'Id', }, } ) return (res as any)?.pageInfo?.totalRows ?? 0 } catch { return 0 } } // ── Fetch parallèle ────────────────────────────────────────────────────── const [statsMois, statsSemaine, fichesAjoutees] = await Promise.all([ fetchStats(`(created_at,gte,${moisDebut})`), fetchStats(`(created_at,gte,${semaineDebut})`), fetchOrgsRecentes(semaineDebut), ]) // ── Agrégation mois ────────────────────────────────────────────────────── let cout_mois_eur = 0 let tokens_mois = 0 const requetes_mois = statsMois.list.length for (const row of statsMois.list) { cout_mois_eur += Number(row.cout_eur ?? 0) tokens_mois += Number(row.tokens_in ?? 0) + Number(row.tokens_out ?? 0) } const co2_kg = tokens_mois * 0.000001 * 0.052 // ── Agrégation semaine chatbot ─────────────────────────────────────────── const requetes_chatbot_semaine = statsSemaine.list.filter( (r) => (r.endpoint ?? '') === 'chatbot' ).length // ── Réponse ────────────────────────────────────────────────────────────── return { cout_mois_eur: Math.round(cout_mois_eur * 1000) / 1000, budget_mois: 20, tokens_mois, co2_kg: Math.round(co2_kg * 1e6) / 1e6, requetes_mois, fiches_semaine: fichesAjoutees, requetes_chatbot_semaine, } })