WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(web): wrap modlogRes.json() in try-catch for non-JSON AppView responses (ATB-48)

A proxy returning HTML with HTTP 200 would cause Response.json() to throw
SyntaxError, which isProgrammingError() re-throws, producing an unhandled crash
instead of a 500 error page. Wrap with the same pattern used in the members
and role-assignment handlers.

Malpercio 793b7f63 c0146e3d

+41 -6
+17
apps/web/src/routes/__tests__/admin.test.tsx
··· 2016 2016 expect(html).toContain("error-display"); 2017 2017 }); 2018 2018 2019 + it("returns 500 when AppView returns non-JSON response body", async () => { 2020 + setupSession(["space.atbb.permission.banUsers"]); 2021 + mockFetch.mockResolvedValueOnce({ 2022 + ok: true, 2023 + status: 200, 2024 + statusText: "OK", 2025 + json: () => Promise.reject(new SyntaxError("Unexpected token '<' in JSON")), 2026 + }); 2027 + const routes = await loadAdminRoutes(); 2028 + const res = await routes.request("/admin/modlog", { 2029 + headers: { cookie: "atbb_session=token" }, 2030 + }); 2031 + expect(res.status).toBe(500); 2032 + const html = await res.text(); 2033 + expect(html).toContain("error-display"); 2034 + }); 2035 + 2019 2036 it("redirects to /login when AppView returns 401", async () => { 2020 2037 setupSession(["space.atbb.permission.banUsers"]); 2021 2038 mockFetch.mockResolvedValueOnce(mockResponse({}, false, 401));
+24 -6
apps/web/src/routes/admin.tsx
··· 1372 1372 ); 1373 1373 } 1374 1374 1375 - const data = (await modlogRes.json()) as { 1376 - actions: ModLogEntry[]; 1377 - total: number; 1378 - offset: number; 1379 - limit: number; 1380 - }; 1375 + let data: { actions: ModLogEntry[]; total: number; offset: number; limit: number }; 1376 + try { 1377 + data = (await modlogRes.json()) as { 1378 + actions: ModLogEntry[]; 1379 + total: number; 1380 + offset: number; 1381 + limit: number; 1382 + }; 1383 + } catch (error) { 1384 + if (!(error instanceof SyntaxError)) throw error; 1385 + logger.error("Malformed JSON from AppView mod action log response", { 1386 + operation: "GET /admin/modlog", 1387 + }); 1388 + return c.html( 1389 + <BaseLayout title="Mod Action Log — atBB Forum" auth={auth}> 1390 + <PageHeader title="Mod Action Log" /> 1391 + <ErrorDisplay 1392 + message="Something went wrong" 1393 + detail="Could not load mod action log. Please try again." 1394 + /> 1395 + </BaseLayout>, 1396 + 500 1397 + ); 1398 + } 1381 1399 1382 1400 const { actions, total } = data; 1383 1401 const totalPages = total === 0 ? 1 : Math.ceil(total / limit);