fix(quotas): unifier le décompte IA (BYOK, rollback) et combler les fuites
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m55s
CI / Deploy production (on server) (push) Successful in 36s

Centralise la réserve via ai-quota, corrige admin unavailable (-1), brancher les routes sans quota et le host-pays brainstorm, avec usage-meter élargi, noms de clusters, MCP et ajustements dashboard/insights.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-07-15 20:42:25 +00:00
parent 30da592ba2
commit 4fe31ebc99
75 changed files with 2949 additions and 785 deletions

View File

@@ -76,7 +76,7 @@ export async function GET() {
notebookId: true, updatedAt: true, createdAt: true,
},
orderBy: { updatedAt: 'desc' },
take: 6,
take: 8,
}),
prisma.note.count({

View File

@@ -5,6 +5,8 @@ import { getChatProvider } from '@/lib/ai/factory'
import { getSystemConfig } from '@/lib/config'
import { redis } from '@/lib/redis'
import { detectUserLanguage } from '@/lib/i18n/detect-user-language'
import { withAiQuota, handleQuotaHttpError } from '@/lib/ai-quota'
import { QuotaExceededError } from '@/lib/entitlements'
const CACHE_TTL_SEC = 3600
@@ -70,7 +72,12 @@ Write "summary" and "topTopic" in ${localeLabel} (locale code: ${locale}). Keep
Notes:
${snippets.slice(0, 3000)}`
const result = await provider.generateText(prompt)
const result = await withAiQuota(
userId,
'reformulate',
() => provider.generateText(prompt),
{ lane: 'chat' },
)
const jsonMatch = result.match(/\{[\s\S]*\}/)
if (!jsonMatch) {
return NextResponse.json({ available: false, reason: 'Parse error' })
@@ -81,6 +88,11 @@ ${snippets.slice(0, 3000)}`
try { await redis.setex(cacheKey, CACHE_TTL_SEC, JSON.stringify(payload)) } catch {}
return NextResponse.json(payload)
} catch (error) {
const quotaResp = handleQuotaHttpError(error)
if (quotaResp) return quotaResp
if (error instanceof QuotaExceededError) {
return NextResponse.json(error.toJSON(), { status: 402 })
}
console.error('[briefing/sentiment]', error)
return NextResponse.json({ available: false, reason: 'Analysis failed' })
}