Files
Samuel AmarandClaude Haiku 4.5 77d7a50fa9 Initial commit: API gateway with admin console
- FastAPI async gateway with httpx proxying to multiple upstreams
- SQLite database with SQLAlchemy ORM
- Admin console: manage services, users, API keys, endpoint access
- Per-key, per-endpoint granular access control
- OpenAPI document sync and caching (5-minute TTL)
- Request/response logging with full transaction inspection
- In-memory rate limiting (per-key, fixed-window)
- Tiered log retention (7d payloads, 90d rows, incremental vacuum)
- TLS verification toggle per service (for self-signed certificates)
- Service connectivity validation with automatic endpoint refresh
- Request browser with filters and deep-link inspection
- Docker setup with persistent volume
- Modal forms for create/edit flows

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-29 14:00:22 +02:00

96 lines
3.7 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Dashboard · API Gateway{% endblock %}
{% block content %}
<h1>Dashboard</h1>
<div class="tiles" id="tiles">
<div class="tile"><div class="label">Requests (24h)</div><div class="value" id="t-total"></div></div>
<div class="tile"><div class="label">Error rate (24h)</div><div class="value" id="t-errors"></div></div>
<div class="tile"><div class="label">Avg latency (24h)</div><div class="value" id="t-latency"></div></div>
<div class="tile"><div class="label">Active services</div><div class="value" id="t-services"></div></div>
<div class="tile"><div class="label">Active API keys</div><div class="value" id="t-keys"></div></div>
</div>
<div class="card">
<h2>Traffic — last 24 hours</h2>
<div class="chart-box"><canvas id="chart-traffic"></canvas></div>
</div>
<div class="grid cols-2">
<div class="card">
<h2>Requests by service (24h)</h2>
<div class="chart-box"><canvas id="chart-services"></canvas></div>
</div>
<div class="card">
<h2>Recent requests</h2>
<table>
<thead><tr><th>Time (UTC)</th><th>Service</th><th>Path</th><th>Status</th><th>ms</th></tr></thead>
<tbody>
{% for log in recent %}
<tr>
<td>{{ log.timestamp.strftime("%H:%M:%S") }}</td>
<td class="strong">{{ log.service.name if log.service else "" }}</td>
<td>{{ log.method }} {{ log.path[:40] }}</td>
<td class="status-{{ log.status_code // 100 }}xx">{{ log.status_code }}</td>
<td>{{ "%.0f" | format(log.latency_ms) }}</td>
</tr>
{% else %}
<tr><td colspan="5">No traffic yet. Send a request through <code>/gw/&lt;service&gt;/&lt;path&gt;</code>.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
(async () => {
const s = await GW.fetchJSON('/admin/api/stats/summary?hours=24');
document.getElementById('t-total').textContent = s.total_requests.toLocaleString();
document.getElementById('t-errors').textContent = s.error_rate + '%';
document.getElementById('t-latency').innerHTML = s.avg_latency_ms + '<small> ms</small>';
document.getElementById('t-services').textContent = s.active_services;
document.getElementById('t-keys').textContent = s.active_keys;
const ts = await GW.fetchJSON('/admin/api/stats/timeseries?hours=24');
new Chart(document.getElementById('chart-traffic'), {
type: 'line',
data: {
labels: ts.labels.map(l => l.slice(11)),
datasets: [
{ label: 'Succeeded', data: ts.ok, borderColor: GW.blue, backgroundColor: GW.blue,
borderWidth: 2, pointRadius: 0, pointHoverRadius: 4, tension: 0.3 },
{ label: 'Errors (5xx)', data: ts.errors, borderColor: GW.red, backgroundColor: GW.red,
borderWidth: 2, pointRadius: 0, pointHoverRadius: 4, tension: 0.3 },
],
},
options: {
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
scales: {
x: { grid: { display: false }, ticks: { maxTicksLimit: 12 } },
y: { beginAtZero: true, ticks: { precision: 0 } },
},
},
});
const bs = await GW.fetchJSON('/admin/api/stats/by-service?hours=24');
new Chart(document.getElementById('chart-services'), {
type: 'bar',
data: {
labels: bs.labels,
datasets: [{ label: 'Requests', data: bs.counts, backgroundColor: GW.blue,
borderRadius: 4, maxBarThickness: 26 }],
},
options: {
maintainAspectRatio: false,
indexAxis: 'y',
plugins: { legend: { display: false } },
scales: { x: { beginAtZero: true, ticks: { precision: 0 } }, y: { grid: { display: false } } },
},
});
})();
</script>
{% endblock %}