feat(aep): carte AEP — push Gitea 2026-04-28

This commit is contained in:
Jules Neny
2026-04-28 14:00:05 +02:00
commit 21c44d8193
86 changed files with 31855 additions and 0 deletions

106
server/api/stats.get.ts Normal file
View File

@@ -0,0 +1,106 @@
/**
* 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<string, any>[] }> {
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<string, any>[] }
} catch {
return { list: [] }
}
}
async function fetchOrgsRecentes(since: string): Promise<number> {
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,
}
})