- New /admin/theme: recolour the whole site from housekeeping. 6 atom-
faithful presets (Atom/Midnight/Ocean/Forest/Sunset/Candy) + per-colour
pickers for the 12 settings ThemeVars injects + border radius. Writes to
website_settings, busts the siteSettings cache, and revalidates the
layout so the new palette applies live with no rebuild. Constants live
in src/lib/theme-presets.ts (a "use server" file can't export objects).
Added to the admin sidebar (System).
- radio/contests/[id] + giveaways/[id] wrapped in ContentCard to match
the public design system.
- Skipped a separate VPN page: /admin/ip already manages the IP
white/blacklist, so it would only duplicate it.
Verified live (prod, amx_test): applied the Ocean preset → home renders
--color-primary #0ea5e9 site-wide; reverted the test rows. tsc 0,
vitest 49/49, next build 0.
- Custom not-found (404) + error / global-error boundaries, styled with
the public design system; raw errors logged, never shown to users.
- In-process rate limiter (src/lib/rate-limit.ts) wired into the abuse-
prone flows: login (10/5min/IP), register (5/10min/IP), password-reset
request (3/15min/IP), keyed by the proxy-forwarded client IP.
- SEO/metadata: root generateMetadata sets a `%s · {hotel}` title
template from the live hotel_name; dynamic generateMetadata on
news/[slug] (article title + excerpt) and u/[username] (name + motto);
static titles on 12 primary public pages.
- env.ts: added the vars introduced since (PASSWORD_HASH, OPENAI_API_KEY,
DISCORD_WEBHOOK_URL, ALERT_EMAIL, PAYPAL_*) so env stays authoritative.
Verified on the prod server: /missing → 404 card, news title renders
"News · Habbo". tsc 0, vitest 49/49, next build 0.
Introspected the live DB and fixed real drift between the hand-modeled
schema and the actual columns:
- Added missing @map("snake_case") on camelCase fields that silently
failed at query time: website_teams (rank_name/hidden_rank/...),
website_paypal_transactions/user_guestbooks/help_center_tickets(+replies)/
used_shop_vouchers/maintenance_tasks (user_id), website_rare_values
(currency_type).
- website_permissions was modeled as key/value/comment but is actually
permission/min_rank/description — fixed the model + the admin page/action.
- website_shop_articles.icon -> icon_url (+ added category_id, is_giftable);
updated the shop page + admin shop CRUD.
- website_shop_categories: dropped non-existent slug/timestamps, added the
real description/order columns; updated the shop page.
- website_shop_article_features.features(Json) -> content(Text).
Verified against amx_test: all website_* query errors gone; home renders
the real hotel_name ("Habbo"), rankings shows real users, /staff + /shop
200. tsc 0, vitest 49/49, next build 0. Remaining missing tables
(radio_*/game_*/staff_activities/alert_logs/article_comments+reactions)
don't exist in this DB and degrade gracefully via try/catch.
Security (launch blockers):
- src/middleware.ts (edge): forwards x-pathname + real client IP.
- access-guard.ts (Node, from root layout): routes non-staff to /maintenance
when maintenance mode is on, banned users to /banned. New /banned + /maintenance
pages (the consumers the admin toggle was missing). Admin layout enforces
force_staff_2fa before /admin.
- staff-activity.ts audit log wired into ban/lift/give-currency/set-rank actions.
Infra (parallel agents): alert service (alert_logs + Discord embed + email),
PayPal top-up (create/capture API routes + /shop/topup), cron worker
(scripts/jobs-worker.ts via croner: emulator-ping->alert, maintenance-check,
bans-cleanup), social connections page, admin radio settings/banners/ranks.
Public radio subsystem: /radio (+schedule, shouts+post, contests, giveaways,
apply, leaderboard) and /apply/staff + /apply/team submission forms. Radio nav
link added. .env.example documents the new optional vars.
(radio song-requests dropped: its table is a stub in AtomCMS — columns added by
un-modeled alter-migrations.)
Verified: tsc exit 0, vitest 48/48, next build exit 0 (82 page routes).
/admin/settings: list all website_settings, inline value edit, add/overwrite,
delete; staff-gated server actions that bust the siteSettings cache after each
write. Admin nav extended (Settings).
Verified: tsc exit 0, vitest 48/48, next build exit 0.