Session 20: Provider intelligence — quota tracker, gateway with fallback cascade, admin quota dashboard (1476 tests)

This commit is contained in:
Kev
2026-06-12 00:54:39 -04:00
parent 56392ec8f4
commit 9b10bb4138
17 changed files with 1422 additions and 15 deletions
+50
View File
@@ -48,6 +48,21 @@ interface AdminStats {
sports: Array<{ sport: string; status: 'ok' | 'error' | 'empty'; quota?: number | null; props?: number; error?: string }>;
odds_quota_remaining: number | null;
};
// Session 20 — per-provider quota snapshot. Pulled through the
// internal /quota endpoint so the admin page sees the same view
// the gateway makes routing decisions against.
provider_quotas: Array<{
provider: string;
name?: string;
used: number;
limit: number;
remaining: number;
pct: number;
period: string;
quotaType: string;
allowed: boolean;
degraded?: boolean;
}>;
notes: string[];
}
@@ -186,6 +201,40 @@ export async function GET(req: NextRequest): Promise<Response> {
// same quota number for every sport since they share the account.
const oddsQuotaRemaining = sports.map((s) => s.quota).find((q) => typeof q === 'number') ?? null;
// Session 20 — fetch the per-provider quota snapshot from the
// backend's internal endpoint. Best-effort: a failure to reach
// the backend or a missing internal key leaves provider_quotas
// empty and surfaces a note instead of blanking the dashboard.
const internalKey = process.env.VYNDR_INTERNAL_KEY || '';
let providerQuotas: AdminStats['provider_quotas'] = [];
if (internalKey) {
try {
const quotaController = new AbortController();
const quotaTimer = setTimeout(() => quotaController.abort(), HEALTH_PROBE_TIMEOUT_MS);
try {
const quotaRes = await fetch(`${BACKEND_URL}/api/internal/quota`, {
signal: quotaController.signal,
headers: { 'x-internal-key': internalKey, Accept: 'application/json' },
cache: 'no-store',
});
if (quotaRes.ok) {
const quotaBody = (await quotaRes.json().catch(() => null)) as { providers?: AdminStats['provider_quotas'] } | null;
if (quotaBody && Array.isArray(quotaBody.providers)) {
providerQuotas = quotaBody.providers;
}
} else {
notes.push(`quota fetch returned ${quotaRes.status}`);
}
} finally {
clearTimeout(quotaTimer);
}
} catch (err) {
notes.push(`quota fetch failed: ${err instanceof Error ? err.message : 'unknown'}`);
}
} else {
notes.push('VYNDR_INTERNAL_KEY unset — provider quotas hidden');
}
const payload: AdminStats = {
generated_at: new Date().toISOString(),
users: {
@@ -201,6 +250,7 @@ export async function GET(req: NextRequest): Promise<Response> {
sports,
odds_quota_remaining: oddsQuotaRemaining,
},
provider_quotas: providerQuotas,
notes,
};