fix(faza-e): refresh dedup, fetch timeout, error surfacing
4 fixes for the symptoms Marius hit on Faza E pilot — search returning
"Eroare la căutare" after sessions got stale, even after relogin:
1. Refresh deduplication
Authentik rotates refresh_tokens — exchange-once. Parallel map +
search + parcela.get all hit jwt callback concurrently, each fires
its own refresh, the first wins, the rest get invalid_grant and
poison the JWT with token.error=RefreshAccessTokenError → user
appears logged out for no good reason. Cache the inflight refresh
promise in-memory keyed by refresh_token so concurrent callers
share one Authentik exchange.
2. Fetch timeout in gis-api-client
AbortSignal.timeout(30s) on every api.gis.ac call. Without it, a
slow upstream (ANCPI scrape, orchestrator hiccup) hangs the route
for the full Next.js default → Marius saw 10s gaps with no
feedback. Throws GisApiError(504, upstream_timeout) instead.
3. Better error surfacing
/api/gis/* routes return { error, hint: <first 200 chars> } on
non-GisApiError throws instead of a bare "internal_error". Easier
to triage from browser DevTools without paging through container
logs.
4. Remove diagnostic [gis-search] logs
Diagnostic served its purpose (identified the stale-token cause
pre-refresh-fix). Now noise; keep only [auth] refresh success/fail
+ per-route internal_error.
Also adds AbortSignal.timeout(8s) on the Authentik refresh fetch
itself to keep the jwt callback bounded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,8 +26,7 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await gisApi.parcel.tech(body);
|
||||
return NextResponse.json(data);
|
||||
return NextResponse.json(await gisApi.parcel.tech(body));
|
||||
} catch (err) {
|
||||
if (err instanceof GisApiError) {
|
||||
return NextResponse.json(
|
||||
@@ -35,6 +34,11 @@ export async function POST(request: Request) {
|
||||
{ status: err.status },
|
||||
);
|
||||
}
|
||||
return NextResponse.json({ error: "internal_error" }, { status: 500 });
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
console.error("[gis-parcel-tech] internal error:", msg);
|
||||
return NextResponse.json(
|
||||
{ error: "internal_error", hint: msg.slice(0, 200) },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ export async function GET(
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await gisApi.parcela.get(id);
|
||||
return NextResponse.json(data);
|
||||
return NextResponse.json(await gisApi.parcela.get(id));
|
||||
} catch (err) {
|
||||
if (err instanceof GisApiError) {
|
||||
return NextResponse.json(
|
||||
@@ -29,6 +28,11 @@ export async function GET(
|
||||
{ status: err.status },
|
||||
);
|
||||
}
|
||||
return NextResponse.json({ error: "internal_error" }, { status: 500 });
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
console.error("[gis-parcela] internal error:", msg);
|
||||
return NextResponse.json(
|
||||
{ error: "internal_error", hint: msg.slice(0, 200) },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,6 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const session = await getAuthSession();
|
||||
const hasSession = !!session;
|
||||
const sessionUserEmail = session?.user?.email ?? null;
|
||||
const hasAccessToken = !!(session as { accessToken?: string } | null)
|
||||
?.accessToken;
|
||||
console.log(
|
||||
"[gis-search] hasSession=%s email=%s hasAccessToken=%s",
|
||||
hasSession,
|
||||
sessionUserEmail,
|
||||
hasAccessToken,
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
@@ -32,18 +21,19 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await gisApi.search(q, limit);
|
||||
console.log("[gis-search] OK q=%s uats=%d features=%d", q, data.uats?.length ?? 0, data.features?.length ?? 0);
|
||||
return NextResponse.json(data);
|
||||
return NextResponse.json(await gisApi.search(q, limit));
|
||||
} catch (err) {
|
||||
if (err instanceof GisApiError) {
|
||||
console.error("[gis-search] gis-api error code=%s status=%d body=%j", err.code, err.status, err.body);
|
||||
return NextResponse.json(
|
||||
{ error: err.code, status: err.status },
|
||||
{ status: err.status },
|
||||
);
|
||||
}
|
||||
console.error("[gis-search] internal error:", err);
|
||||
return NextResponse.json({ error: "internal_error" }, { status: 500 });
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
console.error("[gis-search] internal error:", msg);
|
||||
return NextResponse.json(
|
||||
{ error: "internal_error", hint: msg.slice(0, 200) },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user