From d5efbba9f699a5085f25cd2da772632e68fbd7e0 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 28 Jun 2026 16:14:40 +0200 Subject: [PATCH] Harden DB-reading pages with graceful fallback (no 500 without DB) /news and six admin pages issued raw prisma reads with no try/catch, so a DB outage rendered a 500 instead of an empty state. Wrap each read (try/catch or .catch(() => fallback), preserving select() row types and notFound() on the user-detail page). Matches the fail-soft pattern used across the rest of the app. Verified: tsc 0, next build 0, all public pages 200 against a dead DB. --- src/app/admin/articles/page.tsx | 14 +++++++++----- src/app/admin/bans/page.tsx | 15 ++++++++++----- src/app/admin/page.tsx | 17 ++++++++++++----- src/app/admin/settings/page.tsx | 7 ++++++- src/app/admin/users/[id]/page.tsx | 10 ++++++---- src/app/admin/users/page.tsx | 12 +++++++----- src/app/news/page.tsx | 14 ++++++++++---- 7 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/app/admin/articles/page.tsx b/src/app/admin/articles/page.tsx index ae42e10b..a3acd7f0 100644 --- a/src/app/admin/articles/page.tsx +++ b/src/app/admin/articles/page.tsx @@ -4,11 +4,15 @@ import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; export default async function AdminArticles() { - const articles = await prisma.websiteArticles.findMany({ - orderBy: { createdAt: "desc" }, - select: { id: true, title: true, slug: true, createdAt: true }, - take: 50, - }); + // .catch(() => []) keeps the precise selected-row type while falling back to + // an empty list when the DB is unavailable (instead of a 500). + const articles = await prisma.websiteArticles + .findMany({ + orderBy: { createdAt: "desc" }, + select: { id: true, title: true, slug: true, createdAt: true }, + take: 50, + }) + .catch(() => []); return (
diff --git a/src/app/admin/bans/page.tsx b/src/app/admin/bans/page.tsx index 241e5060..1b53def2 100644 --- a/src/app/admin/bans/page.tsx +++ b/src/app/admin/bans/page.tsx @@ -5,11 +5,16 @@ export const dynamic = "force-dynamic"; export default async function AdminBans() { const now = Math.floor(Date.now() / 1000); - const bans = await prisma.ban.findMany({ - where: { banExpire: { gt: now } }, - orderBy: { timestamp: "desc" }, - take: 100, - }); + let bans: Awaited> = []; + try { + bans = await prisma.ban.findMany({ + where: { banExpire: { gt: now } }, + orderBy: { timestamp: "desc" }, + take: 100, + }); + } catch { + bans = []; + } return (
diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 86410174..5eeb1a81 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -3,11 +3,18 @@ import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; export default async function AdminDashboard() { - const [users, online, articles] = await Promise.all([ - prisma.user.count(), - prisma.user.count({ where: { online: "1" } }), - prisma.websiteArticles.count(), - ]); + let users = 0; + let online = 0; + let articles = 0; + try { + [users, online, articles] = await Promise.all([ + prisma.user.count(), + prisma.user.count({ where: { online: "1" } }), + prisma.websiteArticles.count(), + ]); + } catch { + // DB unavailable — show zeroed stats instead of a 500. + } const stats = [ { label: "Users", value: users }, diff --git a/src/app/admin/settings/page.tsx b/src/app/admin/settings/page.tsx index 1dbea986..32060f34 100644 --- a/src/app/admin/settings/page.tsx +++ b/src/app/admin/settings/page.tsx @@ -4,7 +4,12 @@ import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; export default async function AdminSettings() { - const settings = await prisma.websiteSetting.findMany({ orderBy: { key: "asc" } }); + let settings: Awaited> = []; + try { + settings = await prisma.websiteSetting.findMany({ orderBy: { key: "asc" } }); + } catch { + settings = []; + } return (
diff --git a/src/app/admin/users/[id]/page.tsx b/src/app/admin/users/[id]/page.tsx index f2b4887a..6ea3fb2b 100644 --- a/src/app/admin/users/[id]/page.tsx +++ b/src/app/admin/users/[id]/page.tsx @@ -18,10 +18,12 @@ export default async function AdminUserDetail({ }) { const { id } = await params; const userId = Number(id); - const user = await prisma.user.findUnique({ - where: { id: userId }, - select: { id: true, username: true, motto: true, rank: true, credits: true, look: true, online: true }, - }); + const user = await prisma.user + .findUnique({ + where: { id: userId }, + select: { id: true, username: true, motto: true, rank: true, credits: true, look: true, online: true }, + }) + .catch(() => null); if (!user) notFound(); return ( diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index a2009408..295e3de1 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -15,16 +15,18 @@ export default async function AdminUsers({ const page = Math.max(1, Number(sp.page ?? "1") || 1); const where = q ? { username: { contains: q } } : {}; - const [users, total] = await Promise.all([ - prisma.user.findMany({ + // .catch(...) keeps the precise selected-row type while falling back to an + // empty page when the DB is unavailable (instead of a 500). + const users = await prisma.user + .findMany({ where, select: { id: true, username: true, rank: true, credits: true, online: true }, orderBy: { id: "desc" }, skip: (page - 1) * PER_PAGE, take: PER_PAGE, - }), - prisma.user.count({ where }), - ]); + }) + .catch(() => []); + const total = await prisma.user.count({ where }).catch(() => 0); const pages = Math.max(1, Math.ceil(total / PER_PAGE)); return ( diff --git a/src/app/news/page.tsx b/src/app/news/page.tsx index eed8a959..38cd3add 100644 --- a/src/app/news/page.tsx +++ b/src/app/news/page.tsx @@ -9,10 +9,16 @@ function formatDate(d: Date | null): string { } export default async function NewsPage() { - const articles = await prisma.websiteArticles.findMany({ - orderBy: { createdAt: "desc" }, - take: 30, - }); + let articles: Awaited> = []; + try { + articles = await prisma.websiteArticles.findMany({ + orderBy: { createdAt: "desc" }, + take: 30, + }); + } catch { + // DB unavailable — render the empty state rather than a 500. + articles = []; + } return (