From 3ac5d6f4f65bdfdbb915078f8607d42adf6c4905 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 30 Jul 2026 21:33:40 +0200 Subject: [PATCH] chore(ops): strip redundant force-dynamic and probe Redis in ops health Co-authored-by: Cursor --- src/app/admin/ads/[id]/page.tsx | 2 -- src/app/admin/ads/page.tsx | 2 -- src/app/admin/applications/page.tsx | 2 -- src/app/admin/articles/[id]/page.tsx | 2 -- src/app/admin/articles/page.tsx | 2 -- src/app/admin/badges/page.tsx | 2 -- src/app/admin/bans/page.tsx | 2 -- src/app/admin/calendar/[id]/page.tsx | 2 -- src/app/admin/calendar/page.tsx | 2 -- src/app/admin/catalog/[id]/page.tsx | 2 -- src/app/admin/commandocentrum/page.tsx | 20 +++++++++++++-- src/app/admin/devops/page.tsx | 30 +++++++++++++++++++++- src/app/admin/emulator/page.tsx | 2 -- src/app/admin/guilds/[id]/page.tsx | 2 -- src/app/admin/guilds/page.tsx | 2 -- src/app/admin/help-questions/[id]/page.tsx | 2 -- src/app/admin/help-questions/page.tsx | 2 -- src/app/admin/help-tickets/[id]/page.tsx | 2 -- src/app/admin/help-tickets/page.tsx | 2 -- src/app/admin/housekeeping/page.tsx | 2 -- src/app/admin/ip/page.tsx | 2 -- src/app/admin/items/[id]/page.tsx | 2 -- src/app/admin/logs/page.tsx | 2 -- src/app/admin/maintenance/page.tsx | 2 -- src/app/admin/marketplace/page.tsx | 2 -- src/app/admin/moderation/cfh/page.tsx | 2 -- src/app/admin/page.tsx | 2 -- src/app/admin/photos/page.tsx | 2 -- src/app/admin/radio/api-keys/page.tsx | 2 -- src/app/admin/radio/autodj/page.tsx | 2 -- src/app/admin/radio/banners/page.tsx | 2 -- src/app/admin/radio/embed/page.tsx | 2 -- src/app/admin/radio/history/page.tsx | 2 -- src/app/admin/radio/moderation/page.tsx | 2 -- src/app/admin/radio/monitoring/page.tsx | 2 -- src/app/admin/radio/page.tsx | 2 -- src/app/admin/radio/points/page.tsx | 2 -- src/app/admin/radio/ranks/page.tsx | 2 -- src/app/admin/radio/settings/page.tsx | 2 -- src/app/admin/rare-values/page.tsx | 2 -- src/app/admin/settings/page.tsx | 1 - src/app/admin/shop/[id]/page.tsx | 2 -- src/app/admin/shop/page.tsx | 2 -- src/app/admin/teams/page.tsx | 2 -- src/app/admin/theme/page.tsx | 1 - src/app/admin/tickets/page.tsx | 2 -- src/app/admin/transactions/page.tsx | 2 -- src/app/admin/users/[id]/edit/page.tsx | 2 -- src/app/admin/vpn/page.tsx | 1 - src/app/admin/writeable-boxes/page.tsx | 2 -- src/app/api/admin/devops/health/route.ts | 1 + src/app/mod/bans/page.tsx | 2 -- src/app/mod/cfh/page.tsx | 2 -- src/app/mod/help-tickets/[id]/page.tsx | 2 -- src/app/mod/help-tickets/page.tsx | 2 -- src/app/mod/tickets/page.tsx | 2 -- src/app/mod/users/[id]/page.tsx | 2 -- src/app/mod/users/page.tsx | 2 -- src/lib/admin/ops-health.ts | 21 ++++++++++++--- src/lib/staff-smoke-contract.test.ts | 30 +++++++++++++++++++++- 60 files changed, 95 insertions(+), 114 deletions(-) diff --git a/src/app/admin/ads/[id]/page.tsx b/src/app/admin/ads/[id]/page.tsx index a9c027a6..70c0f4dc 100644 --- a/src/app/admin/ads/[id]/page.tsx +++ b/src/app/admin/ads/[id]/page.tsx @@ -8,8 +8,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { EditAdDeleteButton } from "../ads-table"; -export const dynamic = "force-dynamic"; - export default async function EditAd({ params, }: { diff --git a/src/app/admin/ads/page.tsx b/src/app/admin/ads/page.tsx index 3dfd659b..aa6b29dd 100644 --- a/src/app/admin/ads/page.tsx +++ b/src/app/admin/ads/page.tsx @@ -8,8 +8,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { AdsTable } from "./ads-table"; -export const dynamic = "force-dynamic"; - export default async function AdminAds() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.PAGES_VIEW, session.user.rank)) { diff --git a/src/app/admin/applications/page.tsx b/src/app/admin/applications/page.tsx index 3610701f..e58f6cc1 100644 --- a/src/app/admin/applications/page.tsx +++ b/src/app/admin/applications/page.tsx @@ -8,8 +8,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type ApplicationRow = { id: bigint; userId: number; diff --git a/src/app/admin/articles/[id]/page.tsx b/src/app/admin/articles/[id]/page.tsx index 4e3f6228..b3c9b248 100644 --- a/src/app/admin/articles/[id]/page.tsx +++ b/src/app/admin/articles/[id]/page.tsx @@ -8,8 +8,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function EditArticle({ params, searchParams, diff --git a/src/app/admin/articles/page.tsx b/src/app/admin/articles/page.tsx index 8496564e..142da4b4 100644 --- a/src/app/admin/articles/page.tsx +++ b/src/app/admin/articles/page.tsx @@ -10,8 +10,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function AdminArticles({ searchParams, }: { diff --git a/src/app/admin/badges/page.tsx b/src/app/admin/badges/page.tsx index bf4f8d62..d46eb338 100644 --- a/src/app/admin/badges/page.tsx +++ b/src/app/admin/badges/page.tsx @@ -6,8 +6,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export const metadata = { title: "Badges" }; export default async function AdminBadges({ diff --git a/src/app/admin/bans/page.tsx b/src/app/admin/bans/page.tsx index 0e55e48d..ade09696 100644 --- a/src/app/admin/bans/page.tsx +++ b/src/app/admin/bans/page.tsx @@ -10,8 +10,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function AdminBans() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.BANS_VIEW, session.user.rank)) { diff --git a/src/app/admin/calendar/[id]/page.tsx b/src/app/admin/calendar/[id]/page.tsx index a09ca438..ffc65a9c 100644 --- a/src/app/admin/calendar/[id]/page.tsx +++ b/src/app/admin/calendar/[id]/page.tsx @@ -5,8 +5,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type Campaign = { id: number; name: string; diff --git a/src/app/admin/calendar/page.tsx b/src/app/admin/calendar/page.tsx index 4cd1c5e0..41cdc87f 100644 --- a/src/app/admin/calendar/page.tsx +++ b/src/app/admin/calendar/page.tsx @@ -6,8 +6,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type Campaign = { id: number; name: string; diff --git a/src/app/admin/catalog/[id]/page.tsx b/src/app/admin/catalog/[id]/page.tsx index 0788aae9..fb8dc805 100644 --- a/src/app/admin/catalog/[id]/page.tsx +++ b/src/app/admin/catalog/[id]/page.tsx @@ -8,8 +8,6 @@ import { loadCatalogItemsData } from "@/lib/services/catalog-items-loader"; import { getAncestors } from "@/lib/services/catalog-tree"; import { CatalogDetailTabs } from "./catalog-detail-tabs"; -export const dynamic = "force-dynamic"; - export default async function CatalogEditPage({ params, }: { diff --git a/src/app/admin/commandocentrum/page.tsx b/src/app/admin/commandocentrum/page.tsx index c4672edf..d6b3442e 100644 --- a/src/app/admin/commandocentrum/page.tsx +++ b/src/app/admin/commandocentrum/page.tsx @@ -17,8 +17,6 @@ import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; import { CommandocentrumControls } from "./commandocentrum-controls"; -export const dynamic = "force-dynamic"; - function decodeStacktrace(raw: unknown): string { if (raw == null) return ""; try { @@ -66,6 +64,7 @@ export default async function CommandoCentrum() { ]); const dbOk = health.dbOk; + const redisOk = health.redisOk; const emulatorOnline = health.emulatorOk; const onlineCount = onlineRoster.count; const onlineUsers = onlineRoster.users; @@ -175,6 +174,23 @@ export default async function CommandoCentrum() { detail={dbOk ? t("connectionHealthy") : t("unreachable")} state={dbOk ? "ok" : "danger"} /> + {/* Status cards */} -
+
Database @@ -67,6 +68,33 @@ export default async function DevOpsPage() { + + + Redis + + + + + {data.redisOk === true + ? "Healthy" + : data.redisOk === false + ? "Error" + : "Not configured"} + +

+ Cache / rate limits / sessions +

+
+
+ Emulator diff --git a/src/app/admin/emulator/page.tsx b/src/app/admin/emulator/page.tsx index 3a74e0aa..b1e8146b 100644 --- a/src/app/admin/emulator/page.tsx +++ b/src/app/admin/emulator/page.tsx @@ -9,8 +9,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - const TEXTS_LIMIT = 300; export default async function AdminEmulator() { diff --git a/src/app/admin/guilds/[id]/page.tsx b/src/app/admin/guilds/[id]/page.tsx index 7813ae97..e1c93934 100644 --- a/src/app/admin/guilds/[id]/page.tsx +++ b/src/app/admin/guilds/[id]/page.tsx @@ -7,8 +7,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function AdminGuildDetailPage({ params, }: { diff --git a/src/app/admin/guilds/page.tsx b/src/app/admin/guilds/page.tsx index eedd7515..b6c5d676 100644 --- a/src/app/admin/guilds/page.tsx +++ b/src/app/admin/guilds/page.tsx @@ -11,8 +11,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function AdminGuildsPage({ searchParams, }: { diff --git a/src/app/admin/help-questions/[id]/page.tsx b/src/app/admin/help-questions/[id]/page.tsx index 9a8d1c06..306945e4 100644 --- a/src/app/admin/help-questions/[id]/page.tsx +++ b/src/app/admin/help-questions/[id]/page.tsx @@ -6,8 +6,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function EditHelpQuestion({ params, searchParams, diff --git a/src/app/admin/help-questions/page.tsx b/src/app/admin/help-questions/page.tsx index 2d55b462..381e6c1d 100644 --- a/src/app/admin/help-questions/page.tsx +++ b/src/app/admin/help-questions/page.tsx @@ -6,8 +6,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function AdminHelpQuestions() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.PAGES_VIEW, session.user.rank)) { diff --git a/src/app/admin/help-tickets/[id]/page.tsx b/src/app/admin/help-tickets/[id]/page.tsx index 4a386b1f..7c8551e8 100644 --- a/src/app/admin/help-tickets/[id]/page.tsx +++ b/src/app/admin/help-tickets/[id]/page.tsx @@ -8,8 +8,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { AdminHelpTicketDetail } from "./admin-help-ticket-detail"; -export const dynamic = "force-dynamic"; - export default async function AdminHelpTicketDetailPage({ params, }: { diff --git a/src/app/admin/help-tickets/page.tsx b/src/app/admin/help-tickets/page.tsx index f2b6444f..f1b24c0a 100644 --- a/src/app/admin/help-tickets/page.tsx +++ b/src/app/admin/help-tickets/page.tsx @@ -10,8 +10,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { type HelpTicketRow, HelpTicketsTable } from "./help-tickets-table"; -export const dynamic = "force-dynamic"; - export default async function AdminHelpTicketsPage({ searchParams, }: { diff --git a/src/app/admin/housekeeping/page.tsx b/src/app/admin/housekeeping/page.tsx index 3456c3a2..91ea6853 100644 --- a/src/app/admin/housekeeping/page.tsx +++ b/src/app/admin/housekeeping/page.tsx @@ -6,8 +6,6 @@ import { prisma } from "@/lib/prisma"; import { getAuditLogs } from "@/lib/services/audit"; import { AuditSection, ExportSection } from "./client"; -export const dynamic = "force-dynamic"; - export default async function AdminHousekeeping({ searchParams, }: { diff --git a/src/app/admin/ip/page.tsx b/src/app/admin/ip/page.tsx index 22bef8e3..8c32a610 100644 --- a/src/app/admin/ip/page.tsx +++ b/src/app/admin/ip/page.tsx @@ -12,8 +12,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type IpRow = { id: bigint; ipAddress: string; diff --git a/src/app/admin/items/[id]/page.tsx b/src/app/admin/items/[id]/page.tsx index 5bd24ead..0370e721 100644 --- a/src/app/admin/items/[id]/page.tsx +++ b/src/app/admin/items/[id]/page.tsx @@ -6,8 +6,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { ItemsBaseEditForm } from "./items-base-edit-form"; -export const dynamic = "force-dynamic"; - export default async function ItemsBaseDetailPage({ params, }: { diff --git a/src/app/admin/logs/page.tsx b/src/app/admin/logs/page.tsx index 7c938e1d..9209124f 100644 --- a/src/app/admin/logs/page.tsx +++ b/src/app/admin/logs/page.tsx @@ -10,8 +10,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { StaffActivitiesTable } from "./staff-activities-table"; -export const dynamic = "force-dynamic"; - const SORT_MAP: Record = { when: "createdAt", staff: "userId", diff --git a/src/app/admin/maintenance/page.tsx b/src/app/admin/maintenance/page.tsx index a638e69d..c0db5635 100644 --- a/src/app/admin/maintenance/page.tsx +++ b/src/app/admin/maintenance/page.tsx @@ -6,8 +6,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - const KEYS = [ "maintenance_enabled", "maintenance_message", diff --git a/src/app/admin/marketplace/page.tsx b/src/app/admin/marketplace/page.tsx index 243a854f..a47abc71 100644 --- a/src/app/admin/marketplace/page.tsx +++ b/src/app/admin/marketplace/page.tsx @@ -12,8 +12,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function AdminMarketplacePage({ searchParams, }: { diff --git a/src/app/admin/moderation/cfh/page.tsx b/src/app/admin/moderation/cfh/page.tsx index d3c24df6..89ed8cb3 100644 --- a/src/app/admin/moderation/cfh/page.tsx +++ b/src/app/admin/moderation/cfh/page.tsx @@ -7,8 +7,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { type CfhRow, CfhTable } from "./cfh-table"; -export const dynamic = "force-dynamic"; - export default async function CfhListPage({ searchParams, }: { diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 90d04776..cc7e324c 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -13,8 +13,6 @@ import { activeBanWhere } from "@/lib/bans"; import { formatDate } from "@/lib/format-date"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type RankCount = { rank: number; count: number }; export default async function AdminDashboard() { diff --git a/src/app/admin/photos/page.tsx b/src/app/admin/photos/page.tsx index d41ae904..50389bf8 100644 --- a/src/app/admin/photos/page.tsx +++ b/src/app/admin/photos/page.tsx @@ -10,8 +10,6 @@ import { calcPagination, parseListParams } from "@/lib/admin-helpers"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function AdminPhotos({ searchParams, }: { diff --git a/src/app/admin/radio/api-keys/page.tsx b/src/app/admin/radio/api-keys/page.tsx index 73ce05a5..876b8b9f 100644 --- a/src/app/admin/radio/api-keys/page.tsx +++ b/src/app/admin/radio/api-keys/page.tsx @@ -9,8 +9,6 @@ import { Button } from "@/components/ui/button"; import { formatDate } from "@/lib/format-date"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export const metadata = { title: "Radio API Keys" }; type ApiKey = { diff --git a/src/app/admin/radio/autodj/page.tsx b/src/app/admin/radio/autodj/page.tsx index f92ea509..c870e7f9 100644 --- a/src/app/admin/radio/autodj/page.tsx +++ b/src/app/admin/radio/autodj/page.tsx @@ -10,8 +10,6 @@ import { Button } from "@/components/ui/button"; import { formatDate } from "@/lib/format-date"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export const metadata = { title: "Radio AutoDJ" }; type Track = { diff --git a/src/app/admin/radio/banners/page.tsx b/src/app/admin/radio/banners/page.tsx index 5544ad50..94b03b29 100644 --- a/src/app/admin/radio/banners/page.tsx +++ b/src/app/admin/radio/banners/page.tsx @@ -10,8 +10,6 @@ import { Button } from "@/components/ui/button"; import { formatDate } from "@/lib/format-date"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type Banner = { id: bigint; userId: bigint; diff --git a/src/app/admin/radio/embed/page.tsx b/src/app/admin/radio/embed/page.tsx index 293691ac..105b84ed 100644 --- a/src/app/admin/radio/embed/page.tsx +++ b/src/app/admin/radio/embed/page.tsx @@ -6,8 +6,6 @@ import { resolveHotelName } from "@/lib/hotel-name"; import { siteSettings } from "@/lib/services/site-settings"; import { EmbedSnippet } from "./embed-snippet"; -export const dynamic = "force-dynamic"; - export const metadata = { title: "Radio Embed" }; export default async function AdminRadioEmbedPage() { diff --git a/src/app/admin/radio/history/page.tsx b/src/app/admin/radio/history/page.tsx index b81a464c..e3ef68a5 100644 --- a/src/app/admin/radio/history/page.tsx +++ b/src/app/admin/radio/history/page.tsx @@ -3,8 +3,6 @@ import { StatusCard } from "@/components/admin/dashboard"; import { formatDate } from "@/lib/format-date"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type History = { id: bigint; userId: bigint; diff --git a/src/app/admin/radio/moderation/page.tsx b/src/app/admin/radio/moderation/page.tsx index 613ba679..150a8d3e 100644 --- a/src/app/admin/radio/moderation/page.tsx +++ b/src/app/admin/radio/moderation/page.tsx @@ -5,8 +5,6 @@ import { Button } from "@/components/ui/button"; import { formatDate } from "@/lib/format-date"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type Shout = { id: bigint; userId: bigint; diff --git a/src/app/admin/radio/monitoring/page.tsx b/src/app/admin/radio/monitoring/page.tsx index 19f4bfcf..8b6b316e 100644 --- a/src/app/admin/radio/monitoring/page.tsx +++ b/src/app/admin/radio/monitoring/page.tsx @@ -2,8 +2,6 @@ import { getTranslations } from "next-intl/server"; import { DiagnosticRow, StatusCard } from "@/components/admin/dashboard"; import { siteSettings } from "@/lib/services/site-settings"; -export const dynamic = "force-dynamic"; - export const metadata = { title: "Radio Monitoring" }; const FETCH_TIMEOUT_MS = 4000; diff --git a/src/app/admin/radio/page.tsx b/src/app/admin/radio/page.tsx index e83dd805..652c0819 100644 --- a/src/app/admin/radio/page.tsx +++ b/src/app/admin/radio/page.tsx @@ -5,8 +5,6 @@ import { Button } from "@/components/ui/button"; import { formatDate } from "@/lib/format-date"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type Shout = { id: bigint; userId: bigint; diff --git a/src/app/admin/radio/points/page.tsx b/src/app/admin/radio/points/page.tsx index 6022197f..8247f2a9 100644 --- a/src/app/admin/radio/points/page.tsx +++ b/src/app/admin/radio/points/page.tsx @@ -4,8 +4,6 @@ import { StatusCard } from "@/components/admin/dashboard"; import { Button } from "@/components/ui/button"; import { siteSettings } from "@/lib/services/site-settings"; -export const dynamic = "force-dynamic"; - export const metadata = { title: "Radio Points" }; const DEFAULTS = { diff --git a/src/app/admin/radio/ranks/page.tsx b/src/app/admin/radio/ranks/page.tsx index efbccab1..757a7f5b 100644 --- a/src/app/admin/radio/ranks/page.tsx +++ b/src/app/admin/radio/ranks/page.tsx @@ -8,8 +8,6 @@ import { StatusCard } from "@/components/admin/dashboard"; import { Button } from "@/components/ui/button"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type Rank = { id: bigint; name: string; diff --git a/src/app/admin/radio/settings/page.tsx b/src/app/admin/radio/settings/page.tsx index 13f92d6a..dd2e7001 100644 --- a/src/app/admin/radio/settings/page.tsx +++ b/src/app/admin/radio/settings/page.tsx @@ -7,8 +7,6 @@ import { StatusCard } from "@/components/admin/dashboard"; import { Button } from "@/components/ui/button"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type Field = { key: string; comment: string }; type Group = { title: string; fields: Field[] }; diff --git a/src/app/admin/rare-values/page.tsx b/src/app/admin/rare-values/page.tsx index 4a63fbd2..e0f13e81 100644 --- a/src/app/admin/rare-values/page.tsx +++ b/src/app/admin/rare-values/page.tsx @@ -11,8 +11,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - const CURRENCY_TYPES = ["diamonds", "credits", "duckets", "points"]; export default async function AdminRareValues() { diff --git a/src/app/admin/settings/page.tsx b/src/app/admin/settings/page.tsx index 8bc9dc72..fabb3561 100644 --- a/src/app/admin/settings/page.tsx +++ b/src/app/admin/settings/page.tsx @@ -8,7 +8,6 @@ import { AdvancedSettingsPanel } from "./advanced-settings-panel"; import { FIELD_DEFAULTS, MANAGED_SETTING_KEYS } from "./cms-settings-config"; import { CmsSettingsForm } from "./cms-settings-form"; -export const dynamic = "force-dynamic"; export const metadata = { title: "CMS Settings" }; export default async function AdminSettings() { diff --git a/src/app/admin/shop/[id]/page.tsx b/src/app/admin/shop/[id]/page.tsx index dcb32617..c578216a 100644 --- a/src/app/admin/shop/[id]/page.tsx +++ b/src/app/admin/shop/[id]/page.tsx @@ -7,8 +7,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function EditShopArticle({ params, }: { diff --git a/src/app/admin/shop/page.tsx b/src/app/admin/shop/page.tsx index 1365e0a1..97521be8 100644 --- a/src/app/admin/shop/page.tsx +++ b/src/app/admin/shop/page.tsx @@ -12,8 +12,6 @@ import { formatDate } from "@/lib/format-date"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - const PAID_STATUS = new Set(["completed", "paid", "success", "approved"]); const RECENT_ORDERS = 25; diff --git a/src/app/admin/teams/page.tsx b/src/app/admin/teams/page.tsx index 93101e54..44f624fb 100644 --- a/src/app/admin/teams/page.tsx +++ b/src/app/admin/teams/page.tsx @@ -6,8 +6,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - type TeamRow = { id: bigint; rankName: string; diff --git a/src/app/admin/theme/page.tsx b/src/app/admin/theme/page.tsx index 159232d3..b19d7104 100644 --- a/src/app/admin/theme/page.tsx +++ b/src/app/admin/theme/page.tsx @@ -15,7 +15,6 @@ import { listCustomThemes } from "@/lib/theme-custom-store"; import { FONTS, PRESETS } from "@/lib/theme-presets"; import { ColorField } from "./color-field"; -export const dynamic = "force-dynamic"; export const metadata = { title: "Theme" }; const COLOR_FIELDS: { diff --git a/src/app/admin/tickets/page.tsx b/src/app/admin/tickets/page.tsx index 32a72de6..2e52f6f8 100644 --- a/src/app/admin/tickets/page.tsx +++ b/src/app/admin/tickets/page.tsx @@ -10,8 +10,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { type TicketRow, TicketsTable } from "./tickets-table"; -export const dynamic = "force-dynamic"; - export default async function AdminTicketsPage({ searchParams, }: { diff --git a/src/app/admin/transactions/page.tsx b/src/app/admin/transactions/page.tsx index 0744789f..0d0fa325 100644 --- a/src/app/admin/transactions/page.tsx +++ b/src/app/admin/transactions/page.tsx @@ -8,8 +8,6 @@ import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { type TransactionRow, TransactionsTable } from "./transactions-table"; -export const dynamic = "force-dynamic"; - const PAID_STATUS = ["completed", "paid", "success", "approved"] as const; const FAILED_STATUS = [ "failed", diff --git a/src/app/admin/users/[id]/edit/page.tsx b/src/app/admin/users/[id]/edit/page.tsx index 5fb7b2e5..3a1a6363 100644 --- a/src/app/admin/users/[id]/edit/page.tsx +++ b/src/app/admin/users/[id]/edit/page.tsx @@ -1,7 +1,5 @@ import { redirect } from "next/navigation"; -export const dynamic = "force-dynamic"; - /** * Legacy URL — the canonical edit UI lives at /admin/users/edit/[id] * and is gated by PERMS.USERS_EDIT. diff --git a/src/app/admin/vpn/page.tsx b/src/app/admin/vpn/page.tsx index 783d3939..eee72e22 100644 --- a/src/app/admin/vpn/page.tsx +++ b/src/app/admin/vpn/page.tsx @@ -7,7 +7,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { siteSettings } from "@/lib/services/site-settings"; -export const dynamic = "force-dynamic"; export const metadata = { title: "VPN" }; const PROVIDERS: { value: string; label: string }[] = [ diff --git a/src/app/admin/writeable-boxes/page.tsx b/src/app/admin/writeable-boxes/page.tsx index 2022846a..2d5d44a6 100644 --- a/src/app/admin/writeable-boxes/page.tsx +++ b/src/app/admin/writeable-boxes/page.tsx @@ -11,8 +11,6 @@ import { Button } from "@/components/ui/button"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export const metadata = { title: "Writeable boxes" }; export default async function AdminWriteableBoxes() { diff --git a/src/app/api/admin/devops/health/route.ts b/src/app/api/admin/devops/health/route.ts index 93ed2996..f9ac1486 100644 --- a/src/app/api/admin/devops/health/route.ts +++ b/src/app/api/admin/devops/health/route.ts @@ -9,6 +9,7 @@ export const GET = withAdmin({ permission: PERMS.DEVOPS_VIEW }, async () => { return NextResponse.json({ database: health.dbOk, dbLatency: health.dbLatencyMs, + redis: health.redisOk, emulator: health.emulatorOk, onlineUsers: health.onlineUsers, timestamp: new Date().toISOString(), diff --git a/src/app/mod/bans/page.tsx b/src/app/mod/bans/page.tsx index 26b77e2d..d11a080f 100644 --- a/src/app/mod/bans/page.tsx +++ b/src/app/mod/bans/page.tsx @@ -6,8 +6,6 @@ import { formatDate } from "@/lib/format-date"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function ModBansPage() { await requireModPermission([PERMS.MOD_BANS_VIEW, PERMS.BANS_VIEW]); diff --git a/src/app/mod/cfh/page.tsx b/src/app/mod/cfh/page.tsx index 73a54217..d5fdb4c8 100644 --- a/src/app/mod/cfh/page.tsx +++ b/src/app/mod/cfh/page.tsx @@ -6,8 +6,6 @@ import { calcPagination, parseListParams } from "@/lib/admin-helpers"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function ModCfhListPage({ searchParams, }: { diff --git a/src/app/mod/help-tickets/[id]/page.tsx b/src/app/mod/help-tickets/[id]/page.tsx index a6dbe421..170fd920 100644 --- a/src/app/mod/help-tickets/[id]/page.tsx +++ b/src/app/mod/help-tickets/[id]/page.tsx @@ -6,8 +6,6 @@ import { positiveBigInt } from "@/lib/api"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function ModHelpTicketDetailPage({ params, }: { diff --git a/src/app/mod/help-tickets/page.tsx b/src/app/mod/help-tickets/page.tsx index 4cbf4a6e..6f1dd12c 100644 --- a/src/app/mod/help-tickets/page.tsx +++ b/src/app/mod/help-tickets/page.tsx @@ -13,8 +13,6 @@ import { formatDate } from "@/lib/format-date"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function ModHelpTicketsPage({ searchParams, }: { diff --git a/src/app/mod/tickets/page.tsx b/src/app/mod/tickets/page.tsx index 1b1305b4..f8f844f4 100644 --- a/src/app/mod/tickets/page.tsx +++ b/src/app/mod/tickets/page.tsx @@ -13,8 +13,6 @@ import { formatDate } from "@/lib/format-date"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function ModTicketsPage({ searchParams, }: { diff --git a/src/app/mod/users/[id]/page.tsx b/src/app/mod/users/[id]/page.tsx index 0c9cc06d..a5c2aab8 100644 --- a/src/app/mod/users/[id]/page.tsx +++ b/src/app/mod/users/[id]/page.tsx @@ -8,8 +8,6 @@ import { getAvatarUrl } from "@/lib/imager"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -export const dynamic = "force-dynamic"; - export default async function ModUserDetailPage({ params, }: { diff --git a/src/app/mod/users/page.tsx b/src/app/mod/users/page.tsx index e358fb35..ea30bff0 100644 --- a/src/app/mod/users/page.tsx +++ b/src/app/mod/users/page.tsx @@ -9,8 +9,6 @@ import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import type { DataTableColumn } from "@/types/common"; -export const dynamic = "force-dynamic"; - type ModUserRow = { id: number; username: string; diff --git a/src/lib/admin/ops-health.ts b/src/lib/admin/ops-health.ts index 8a5a4506..3381ba68 100644 --- a/src/lib/admin/ops-health.ts +++ b/src/lib/admin/ops-health.ts @@ -1,21 +1,25 @@ import "server-only"; +import { env } from "@/env"; import { prisma } from "@/lib/prisma"; +import { redis } from "@/lib/redis"; import { rcon } from "@/lib/services/rcon"; export type OpsHealth = { dbOk: boolean; dbLatencyMs: number; + /** null = REDIS_URL unset (optional); true/false = probe result */ + redisOk: boolean | null; emulatorOk: boolean; onlineUsers: number; }; /** - * Shared DB + emulator health probe for CommandoCentrum, DevOps, and APIs. - * Emulator = real RCON `ping` (same semantics as CommandoCentrum /public health). + * Shared DB + Redis + emulator health probe for CommandoCentrum, DevOps, and APIs. + * Semantics match `/api/health` for Redis (null when not configured). */ export async function fetchOpsHealth(): Promise { - const [db, emulatorOk, onlineUsers] = await Promise.all([ + const [db, redisOk, emulatorOk, onlineUsers] = await Promise.all([ (async () => { try { const start = Date.now(); @@ -25,6 +29,16 @@ export async function fetchOpsHealth(): Promise { return { dbOk: false, dbLatencyMs: 0 }; } })(), + (async (): Promise => { + if (!env.REDIS_URL) return null; + if (!redis) return false; + try { + const pong = await redis.ping(); + return pong === "PONG"; + } catch { + return false; + } + })(), rcon.send("ping", null).catch(() => false), prisma.user.count({ where: { online: "1" } }).catch(() => 0), ]); @@ -32,6 +46,7 @@ export async function fetchOpsHealth(): Promise { return { dbOk: db.dbOk, dbLatencyMs: db.dbLatencyMs, + redisOk, emulatorOk: Boolean(emulatorOk), onlineUsers, }; diff --git a/src/lib/staff-smoke-contract.test.ts b/src/lib/staff-smoke-contract.test.ts index 70bc7967..43020166 100644 --- a/src/lib/staff-smoke-contract.test.ts +++ b/src/lib/staff-smoke-contract.test.ts @@ -1,4 +1,5 @@ -import { existsSync, readFileSync } from "node:fs"; +import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; +import { join } from "node:path"; import { describe, expect, it } from "vitest"; describe("staff smoke contract", () => { @@ -50,10 +51,37 @@ describe("staff smoke contract", () => { it("ships shared ops + ticket queue helpers", () => { expect(existsSync("src/lib/admin/ops-health.ts")).toBe(true); + const ops = readFileSync("src/lib/admin/ops-health.ts", "utf8"); + expect(ops).toContain("redisOk"); + expect(ops).toContain("redis.ping"); expect(existsSync("src/lib/admin/ticket-queue-counts.ts")).toBe(true); expect(existsSync("src/lib/admin/ops-online-users.ts")).toBe(true); }); + it("keeps force-dynamic only on admin/mod layouts", () => { + const hits: string[] = []; + function walk(dir: string) { + for (const name of readdirSync(dir)) { + const p = join(dir, name); + if (statSync(p).isDirectory()) walk(p); + else if (/\.(tsx?|jsx?)$/.test(name)) { + const src = readFileSync(p, "utf8"); + if ( + /export\s+const\s+dynamic\s*=\s*["']force-dynamic["']/.test(src) + ) { + hits.push(p.replace(/\\/g, "/")); + } + } + } + } + walk("src/app/admin"); + walk("src/app/mod"); + expect(hits.sort()).toEqual([ + "src/app/admin/layout.tsx", + "src/app/mod/layout.tsx", + ]); + }); + it("caches analytics via redisCache", () => { for (const path of [ "src/app/admin/analytics/page.tsx",