Fill the remaining gaps: badge upload, radio tools, VPN, writeable boxes
Built the admin tools previously listed as missing: - Badge upload (/admin/badges): uploads a <code>.gif into the emulator's badge dir via BADGE_UPLOAD_DIR (node:fs); validated code/type/size, logged. Made configurable rather than skipped. - Radio tools: /admin/radio/api-keys (CRUD, server-generated keys), /admin/radio/autodj (Auto-DJ playlist CRUD), /admin/radio/embed (embed snippet generator), /admin/radio/points (points settings), /admin/radio/monitoring (live stream/now-playing/listeners status). radio_api_keys + radio_auto_dj_playlist already had real columns. - /admin/vpn: VPN/proxy detection config (block toggle + provider + key), complementing /admin/ip's raw blacklist. - Writeable boxes: new website_writeable_boxes table (model + migration 0006) + /admin/writeable-boxes CRUD; active boxes render on the public home page. env: BADGE_UPLOAD_DIR. Verified live (prod, amx_test): all 8 pages render with real data; a test writeable box appeared on the public home and was reverted. tsc 0, vitest 49/49, next build 0 (7 new admin routes).
This commit is contained in:
1 parent
e004dfedaf
commit
0cd4d06ff6
21 files changed
+2443
-61
No files matched your search
@@ -22,6 +22,15 @@ export default async function HomePage() {
|
||||
// DB unavailable — show the shell without articles.
|
||||
}
|
||||
|
||||
// Staff-authored content boxes (managed at /admin/writeable-boxes).
|
||||
const boxes = await prisma.websiteWriteableBoxes
|
||||
.findMany({
|
||||
where: { isActive: true },
|
||||
orderBy: [{ position: "asc" }, { id: "asc" }],
|
||||
select: { id: true, title: true, icon: true, content: true },
|
||||
})
|
||||
.catch(() => []);
|
||||
|
||||
return (
|
||||
<main style={{ display: "grid", gap: "1.5rem" }}>
|
||||
<ContentCard
|
||||
@@ -81,6 +90,14 @@ export default async function HomePage() {
|
||||
</div>
|
||||
)}
|
||||
</ContentCard>
|
||||
|
||||
{boxes.map((b) => (
|
||||
<ContentCard key={String(b.id)} icon={b.icon ?? "📌"} title={b.title}>
|
||||
{/* Content is authored by staff in housekeeping (trusted HTML). */}
|
||||
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: staff-authored writeable box */}
|
||||
<div dangerouslySetInnerHTML={{ __html: b.content }} />
|
||||
</ContentCard>
|
||||
))}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user