Close the web-feasible 100% gaps: REST write/token API, tickets, draw-badge, /me, sanitisation, dusk, radio SSE

Final parity push (web-tier only):
- REST API write + token auth: POST /api/tokens (issue a personal_access_token
  for the session user), Bearer auth via src/lib/api-auth.ts, POST
  /api/articles/[slug]/comment, GET/DELETE /api/me/tokens, full tickets API
  (/api/tickets +[id] +[id]/reply), radio current-dj/points/points-leaderboard/
  embed-config + POST shouts, and a real-time /api/radio/stream (SSE). 31 public
  API routes total.
- Pages: /draw-badge (buy a custom profile badge → credits + RCON), /me
  dashboard (stats + online friends + referral claim). Wired into the nav.
- HTML sanitisation (sanitize-html) — the HTMLPurifier equivalent — applied to
  writeable boxes + article bodies before dangerouslySetInnerHTML.
- "Dusk" dark theme preset + a default-dark site option honoured by the
  no-flash boot script.

Verified live (prod, amx_test): token issue → Bearer endpoint 200, no-token
401; /api/me/tokens lists it; current-dj/leaderboard JSON; /me + /draw-badge
200; reverted the test user + tokens. tsc 0, vitest 49/49, next build 0.
This commit is contained in:
Simo committed 2026-06-29 18:15:01 +02:00
1 parent 8cedf5614e
commit f7b3845131
30 files changed
+1734 -10

No files matched your search

+4 -3
View File
@@ -3,6 +3,7 @@ import Link from "next/link";
import { ContentCard, EmptyState } from "@/components/public/ui";
import { excerpt } from "@/lib/format";
import { prisma } from "@/lib/prisma";
import { sanitize } from "@/lib/sanitize";
import { siteSettings } from "@/lib/services/site-settings";
export const dynamic = "force-dynamic";
@@ -93,9 +94,9 @@ export default async function HomePage() {
{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 }} />
{/* Staff-authored HTML, sanitised server-side before injection. */}
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: sanitised writeable box */}
<div dangerouslySetInnerHTML={{ __html: sanitize(b.content) }} />
</ContentCard>
))}
</main>