fix(admin): eliminate GoTrue empty-body 500s under bulk load (retry-all + undici keep-alive + sequential bulk), CSV formula-injection guard

This commit is contained in:
Gerhard Scheikl
2026-05-31 17:30:04 +02:00
parent cbd29445bb
commit 8e8df7ae64
12 changed files with 134 additions and 28 deletions
+7 -4
View File
@@ -1,6 +1,7 @@
import Link from 'next/link';
import { computeMetrics } from '@/lib/admin/metrics';
import { getSupabaseAdmin } from '@/lib/supabase/admin';
import { withAdminRetry } from '@/lib/admin/retry';
import { formatBytes, formatDate } from '@/lib/format';
export const dynamic = 'force-dynamic';
@@ -18,10 +19,12 @@ export default async function AdminOverviewPage() {
const admin = getSupabaseAdmin();
// Recent signups (latest 5 users).
const { data: recentUsersData } = await admin.auth.admin.listUsers({
page: 1,
perPage: 5,
});
const { data: recentUsersData } = await withAdminRetry(() =>
admin.auth.admin.listUsers({
page: 1,
perPage: 5,
}),
);
const recentUsers = recentUsersData?.users ?? [];
// Over-quota tunnels (compute in memory).
+5
View File
@@ -237,6 +237,11 @@ export function TunnelsTable({
} catch {
result.fail++;
}
// Tiny inter-op pacing: strictly sequential (concurrency 1) with a small
// gap so we never burst the admin API; single refresh after all settle.
if (i < targets.length - 1) {
await new Promise((r) => setTimeout(r, 75));
}
}
setBulkProgress(null);
setBulkBusy(false);
+6
View File
@@ -136,6 +136,12 @@ export function UsersTable({
} catch {
result.fail++;
}
// Tiny inter-op pacing: keep the per-row mutations strictly sequential
// (concurrency 1) with a small gap so we never burst the GoTrue admin
// API, then refresh ONCE after every op has settled (below).
if (i < targets.length - 1) {
await new Promise((r) => setTimeout(r, 75));
}
}
setBulkProgress(null);
setBulkBusy(false);