107 lines
4.3 KiB
TypeScript
107 lines
4.3 KiB
TypeScript
/**
|
||
* 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,
|
||
}
|
||
})
|