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.
This commit is contained in:
1 parent
3e8fb794d9
commit
d5efbba9f6
7 files changed
+60
-29
No files matched your search
@@ -4,11 +4,15 @@ import { prisma } from "@/lib/prisma";
|
|||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function AdminArticles() {
|
export default async function AdminArticles() {
|
||||||
const articles = await prisma.websiteArticles.findMany({
|
// .catch(() => []) keeps the precise selected-row type while falling back to
|
||||||
orderBy: { createdAt: "desc" },
|
// an empty list when the DB is unavailable (instead of a 500).
|
||||||
select: { id: true, title: true, slug: true, createdAt: true },
|
const articles = await prisma.websiteArticles
|
||||||
take: 50,
|
.findMany({
|
||||||
});
|
orderBy: { createdAt: "desc" },
|
||||||
|
select: { id: true, title: true, slug: true, createdAt: true },
|
||||||
|
take: 50,
|
||||||
|
})
|
||||||
|
.catch(() => []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ export const dynamic = "force-dynamic";
|
|||||||
|
|
||||||
export default async function AdminBans() {
|
export default async function AdminBans() {
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const bans = await prisma.ban.findMany({
|
let bans: Awaited<ReturnType<typeof prisma.ban.findMany>> = [];
|
||||||
where: { banExpire: { gt: now } },
|
try {
|
||||||
orderBy: { timestamp: "desc" },
|
bans = await prisma.ban.findMany({
|
||||||
take: 100,
|
where: { banExpire: { gt: now } },
|
||||||
});
|
orderBy: { timestamp: "desc" },
|
||||||
|
take: 100,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
bans = [];
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
+12
-5
@@ -3,11 +3,18 @@ import { prisma } from "@/lib/prisma";
|
|||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function AdminDashboard() {
|
export default async function AdminDashboard() {
|
||||||
const [users, online, articles] = await Promise.all([
|
let users = 0;
|
||||||
prisma.user.count(),
|
let online = 0;
|
||||||
prisma.user.count({ where: { online: "1" } }),
|
let articles = 0;
|
||||||
prisma.websiteArticles.count(),
|
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 = [
|
const stats = [
|
||||||
{ label: "Users", value: users },
|
{ label: "Users", value: users },
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import { prisma } from "@/lib/prisma";
|
|||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function AdminSettings() {
|
export default async function AdminSettings() {
|
||||||
const settings = await prisma.websiteSetting.findMany({ orderBy: { key: "asc" } });
|
let settings: Awaited<ReturnType<typeof prisma.websiteSetting.findMany>> = [];
|
||||||
|
try {
|
||||||
|
settings = await prisma.websiteSetting.findMany({ orderBy: { key: "asc" } });
|
||||||
|
} catch {
|
||||||
|
settings = [];
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ export default async function AdminUserDetail({
|
|||||||
}) {
|
}) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
const userId = Number(id);
|
const userId = Number(id);
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user
|
||||||
where: { id: userId },
|
.findUnique({
|
||||||
select: { id: true, username: true, motto: true, rank: true, credits: true, look: true, online: true },
|
where: { id: userId },
|
||||||
});
|
select: { id: true, username: true, motto: true, rank: true, credits: true, look: true, online: true },
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
if (!user) notFound();
|
if (!user) notFound();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -15,16 +15,18 @@ export default async function AdminUsers({
|
|||||||
const page = Math.max(1, Number(sp.page ?? "1") || 1);
|
const page = Math.max(1, Number(sp.page ?? "1") || 1);
|
||||||
const where = q ? { username: { contains: q } } : {};
|
const where = q ? { username: { contains: q } } : {};
|
||||||
|
|
||||||
const [users, total] = await Promise.all([
|
// .catch(...) keeps the precise selected-row type while falling back to an
|
||||||
prisma.user.findMany({
|
// empty page when the DB is unavailable (instead of a 500).
|
||||||
|
const users = await prisma.user
|
||||||
|
.findMany({
|
||||||
where,
|
where,
|
||||||
select: { id: true, username: true, rank: true, credits: true, online: true },
|
select: { id: true, username: true, rank: true, credits: true, online: true },
|
||||||
orderBy: { id: "desc" },
|
orderBy: { id: "desc" },
|
||||||
skip: (page - 1) * PER_PAGE,
|
skip: (page - 1) * PER_PAGE,
|
||||||
take: 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));
|
const pages = Math.max(1, Math.ceil(total / PER_PAGE));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+10
-4
@@ -9,10 +9,16 @@ function formatDate(d: Date | null): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function NewsPage() {
|
export default async function NewsPage() {
|
||||||
const articles = await prisma.websiteArticles.findMany({
|
let articles: Awaited<ReturnType<typeof prisma.websiteArticles.findMany>> = [];
|
||||||
orderBy: { createdAt: "desc" },
|
try {
|
||||||
take: 30,
|
articles = await prisma.websiteArticles.findMany({
|
||||||
});
|
orderBy: { createdAt: "desc" },
|
||||||
|
take: 30,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// DB unavailable — render the empty state rather than a 500.
|
||||||
|
articles = [];
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
Reference in new issue
Block a user