fix(admin): retry GoTrue admin reads on transient empty-body responses (bulk-load robustness)
This commit is contained in:
+22
-7
@@ -1,5 +1,6 @@
|
||||
import type { User } from '@supabase/supabase-js';
|
||||
import { getSupabaseAdmin } from '@/lib/supabase/admin';
|
||||
import { withAdminRetry } from '@/lib/admin/retry';
|
||||
import {
|
||||
parseOrder,
|
||||
parseSort,
|
||||
@@ -128,10 +129,14 @@ export async function getUsersList(opts: {
|
||||
// paginate the filtered+sorted set.
|
||||
const matched: User[] = [];
|
||||
for (let p = 1; p <= USER_SCAN_MAX_PAGES; p++) {
|
||||
const { data, error } = await admin.auth.admin.listUsers({
|
||||
page: p,
|
||||
perPage: USER_SCAN_PER_PAGE,
|
||||
});
|
||||
// Retry transient empty-body GoTrue responses so a burst-induced flake
|
||||
// doesn't abort the full directory scan mid-way.
|
||||
const { data, error } = await withAdminRetry(() =>
|
||||
admin.auth.admin.listUsers({
|
||||
page: p,
|
||||
perPage: USER_SCAN_PER_PAGE,
|
||||
}),
|
||||
);
|
||||
if (error) throw new Error(error.message);
|
||||
const us = data.users;
|
||||
if (us.length === 0) break;
|
||||
@@ -147,8 +152,12 @@ export async function getUsersList(opts: {
|
||||
const from = (page - 1) * perPage;
|
||||
pageUsers = matched.slice(from, from + perPage);
|
||||
} else {
|
||||
// Common no-search, default-sort path: cheap single-page lookup.
|
||||
const { data, error } = await admin.auth.admin.listUsers({ page, perPage });
|
||||
// Common no-search, default-sort path: cheap single-page lookup. Retry
|
||||
// transient empty-body responses so the post-mutation auto-refresh that
|
||||
// hits this path doesn't intermittently 500.
|
||||
const { data, error } = await withAdminRetry(() =>
|
||||
admin.auth.admin.listUsers({ page, perPage }),
|
||||
);
|
||||
if (error) throw new Error(error.message);
|
||||
pageUsers = data.users;
|
||||
total = (data as unknown as { total?: number }).total ?? pageUsers.length;
|
||||
@@ -281,10 +290,16 @@ export async function getTunnelsList(opts: {
|
||||
}
|
||||
|
||||
// Resolve owner emails (per-row getUserById; acceptable for current scale).
|
||||
// The user_id comes from an existing tunnel row, so an empty body here is a
|
||||
// transient burst flake rather than a genuine not-found — retry it. The
|
||||
// try/catch null fallback remains as a last resort so one bad row can never
|
||||
// 500 the whole list (it surfaces as "—" only if every retry still fails).
|
||||
const emails = await Promise.all(
|
||||
rows.map(async (t) => {
|
||||
try {
|
||||
const { data: u } = await admin.auth.admin.getUserById(t.user_id);
|
||||
const { data: u } = await withAdminRetry(() =>
|
||||
admin.auth.admin.getUserById(t.user_id),
|
||||
);
|
||||
return u.user?.email ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user