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
+68
View File
@@ -36,6 +36,18 @@ interface AdminStats {
sports: Array<{ sport: string; status: 'ok' | 'error' | 'empty'; quota?: number | null; props?: number; error?: string }>;
odds_quota_remaining: number | null;
};
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[];
}
@@ -255,6 +267,62 @@ export default function AdminPage() {
</table>
</section>
{/* Session 20 — Provider Quotas. Pulled from
/api/internal/quota; rendered as a per-provider table with
a usage bar + status indicator. When the array is empty,
the section auto-hides (likely VYNDR_INTERNAL_KEY unset
on the Next.js side — surfaced in the notes section). */}
{!!stats?.provider_quotas?.length && (
<section style={{ ...cellStyle, marginTop: 24 }}>
<h2 style={{ fontSize: 14, fontWeight: 700, marginBottom: 14, letterSpacing: '-0.01em' }}>Provider quotas</h2>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
<thead>
<tr style={{ textAlign: 'left', color: 'var(--text-tertiary, #6B6B7B)', fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.08em' }}>
<th style={{ padding: '6px 0' }}>Provider</th>
<th style={{ padding: '6px 0' }}>Used</th>
<th style={{ padding: '6px 0' }}>Type</th>
<th style={{ padding: '6px 0', textAlign: 'right' }}>Status</th>
</tr>
</thead>
<tbody>
{stats.provider_quotas.map((p) => {
const pctNum = Math.round((p.pct || 0) * 100);
const color = !p.allowed
? 'var(--grade-d, #FF6B6B)'
: pctNum >= 80
? 'var(--grade-c, #FFD93D)'
: 'var(--grade-a, #00D4A0)';
const indicator = !p.allowed
? `❌ BLOCKED ${pctNum}%`
: pctNum >= 80
? `⚠️ ${pctNum}%`
: `${pctNum}%`;
return (
<tr key={p.provider} style={{ borderBottom: '1px solid var(--border, #1A1A24)' }}>
<td style={{ padding: '8px 0', color: 'var(--text-0, #F0F0F5)' }}>
<div style={{ fontWeight: 600 }}>{p.name || p.provider}</div>
<div className="mono" style={{ fontSize: 10, color: 'var(--text-tertiary, #6B6B7B)', marginTop: 2 }}>{p.period}</div>
</td>
<td className="mono" style={{ padding: '8px 0', color: 'var(--text-0, #F0F0F5)', fontVariantNumeric: 'tabular-nums' }}>
<div>{p.used}/{p.limit}</div>
<div style={{ marginTop: 4, background: 'var(--bg-2, #15151F)', height: 4, borderRadius: 2, overflow: 'hidden', width: 90 }}>
<div style={{ width: `${Math.min(100, Math.max(2, pctNum))}%`, height: '100%', background: color }} />
</div>
</td>
<td className="mono" style={{ padding: '8px 0', color: 'var(--text-secondary, #8A8A9A)', fontSize: 11, textTransform: 'uppercase' }}>
{p.quotaType}
</td>
<td className="mono" style={{ padding: '8px 0', textAlign: 'right', color, fontSize: 12 }}>
{indicator}{p.degraded ? ' (degraded)' : ''}
</td>
</tr>
);
})}
</tbody>
</table>
</section>
)}
{!!stats?.notes?.length && (
<section style={{ ...cellStyle, marginTop: 24, fontSize: 12, color: 'var(--grade-c, #FFD93D)' }}>
<h2 style={{ fontSize: 12, fontWeight: 700, marginBottom: 8, letterSpacing: '-0.01em' }}>Query notes</h2>