Commit Graph
37 Commits
Author SHA1 Message Date
SimoandCursor e00e9ca2dc refactor: centralize hotel name fallback via FALLBACK_HOTEL_NAME
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 2m6s
Route all user-facing Atom hotel defaults through resolveHotelName (settings then HOTEL_NAME env then brand constant). Exclude Playwright e2e from tsconfig until deps are installed.

Co-authored-by: Cursor <[email protected]>
2026-07-22 18:45:59 +02:00
SimoandCursor 2ff08e5127 chore: patch deps, CSP style nonces, otplib 13, and PR CI
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 1m35s
Co-authored-by: Cursor <[email protected]>
2026-07-21 20:46:02 +02:00
SimoandCursor 9b47668fe9 chore: CSP script nonces, deploy health check, dead-code cleanup
Add per-request CSP nonces (drop script unsafe-inline), post-deploy /api/health gate, bump next-auth to beta.32, and remove unused motion/cache/permission helpers.

Co-authored-by: Cursor <[email protected]>
2026-07-21 20:27:08 +02:00
openhands f53d25e0a5 feat: add view transitions, smooth scroll, bundle analyzer, SSE radio stream, and AnimatePresence animations
Local Build and Deploy / deploy (push) Successful in 1m24s
2026-07-20 14:06:43 +02:00
SimoandCursor 367f69a439 Decouple admin from public max-w-7xl via (site) route group.
Local Build and Deploy / deploy (push) Successful in 54s
Public chrome and width live only under (site). /admin and /client inherit the root layout alone, so housekeeping width is no longer tied to the public template.

Co-authored-by: Cursor <[email protected]>
2026-07-15 21:12:52 +02:00
SimoandCursor c186f51ae0 Widen admin by escaping the public max-w-7xl chrome.
Local Build and Deploy / deploy (push) Successful in 54s
Render /admin outside the site header/nav/footer grid so housekeeping uses the full viewport width.

Co-authored-by: Cursor <[email protected]>
2026-07-15 20:59:53 +02:00
openhands df38dccbf1 style: format code biome
Local Build and Deploy / deploy (push) Failing after 46s
2026-07-13 21:57:41 +02:00
openhands eba61d2fb9 Fix mobile responsive issues
Local Build and Deploy / deploy (push) Successful in 53s
- Remove duplicate home link in mobile nav
- Remove overflow-hidden clipping main content
- Improve mobile menu scrolling and spacing
- Make top-header currencies wrap on small screens
- Reduce site-header height on mobile
- Add global mobile CSS overrides for tables, padding, fonts
2026-07-13 18:10:16 +02:00
Simo 5b4228261a Reapply "Add missing admin action files and navigation links"
This reverts commit 4d515bc400.
2026-07-11 20:52:56 +02:00
Simo 4d515bc400 Revert "Add missing admin action files and navigation links"
This reverts commit 41be6835bf.
2026-07-11 20:37:56 +02:00
openhands 41be6835bf Add missing admin action files and navigation links
- Add 11 missing server action files: badges, bulk-users, catalog, catalog-bc, catalog-items, import-badges, import-furni, multi-account-detect, permissions, rooms, soundtracks
- Add missing admin navigation links: tickets, sounds, translations, import, radio sub-pages
- Add translation keys for all new navigation items
2026-07-11 12:01:05 +02:00
openhands 942bc6fc8d Security hardening, code quality, and ESLint setup
- Remove production DB dump (db_backup_*.sql) and update.log from git tracking
- Add DB backups to .gitignore
- Replace all console.log/console.error with structured logger module
- Translate Dutch error messages to English (link-discord.ts)
- Remove dead code blocks (register-form.tsx false && pattern)
- Add ESLint flat config with TypeScript, React, Next.js, jsx-a11y, and security plugins
- Add Prettier config
- Add eslint-plugin-security for security-aware linting
- Fix all 119+ ESLint warnings across the codebase:
  - Resolve security/detect-object-injection with safe access patterns
  - Resolve security/detect-non-literal-fs-filename with path traversal validation
  - Replace <img> with next/image <Image> component
  - Remove unused variables and imports
  - Replace non-null assertions with proper type guards
  - Replace <a> with <Link> for internal navigation
  - Use next/script Script component for external scripts
- Fix setState-in-useEffect anti-patterns (navbar-color-picker, logo-generator, theme-switcher)
- Add lint and format scripts to package.json

All checks: typecheck ✓, tests 58/58 ✓, lint 0 errors 0 warnings ✓
2026-07-10 22:48:22 +02:00
openhands c68fccceeb Fix: only preconnect nitro URL if absolute (prevents crash on relative URLs like /nitro-client/) 2026-07-09 19:52:19 +02:00
openhands cfb36e8007 Preconnect to Nitro client URL for faster client page load 2026-07-09 18:35:18 +02:00
openhands deac10e00a Add in-memory caching for online count, enable compression, and add staleTimes for router cache 2026-07-09 18:24:58 +02:00
openhands 4a80742e1c Add default SVG favicon fallback 2026-07-05 23:25:36 +02:00
openhands 25c3040949 Add favicon upload option in admin panel 2026-07-05 23:22:44 +02:00
openhands 8bcbc501ba Performance, SEO, a11y, and code quality improvements
CI / check (push) Has been cancelled
1. Performance: 25 pages switched from force-dynamic to revalidate=300 (ISR);
   2 pages (community, developers) now fully static (SSG)
2. DB indexes: Added @@index on foreign keys for WebsiteArticles,
   WebsiteArticleReactions, WebsiteArticleComments, WebsiteHelpCenterTickets,
   WebsiteShopArticles, RadioSongRequests, StaffActivities
3. SEO: Added robots.ts, sitemap.ts, canonical URLs, Open Graph + Twitter
   Card metadata on root layout and news articles
4. A11Y: Replaced <details>/<summary> dropdowns with accessible button-based
   NavDropdown (aria-expanded, aria-haspopup, role=menu). MobileNav now
   uses translated aria-label, aria-expanded, aria-controls, role=menu
5. Code quality: Added try/catch to updateArticle/deleteArticle; deleteArticle
   now uses prisma. for atomicity
6. CI/CD: Added GitHub Actions workflow (typecheck + test)
7. i18n: Added openMenu/closeMenu keys to all 6 locales
8. Observability: Health endpoint now checks SMTP reachability when configured
9. Loading states: Added loading.tsx for root, admin, and news sections
10. Word filter cache: Added 60s TTL auto-refresh instead of manual cache bust
2026-07-04 19:41:04 +02:00
openhands 10523e58ce Fix remaining security vulnerabilities
- H1: Add missing sanitize() to help center content rendering
- H2: Tighten CSP by removing unsafe-inline/unsafe-eval from script-src;
  move theme init to external JS file with meta tag for defaultDark
- M1: Add SSRF protection for radio API URLs (block private IPs)
- M2: Add rate limiting to SSO ticket endpoint (5 req/30s per user)
- M4: Document locale validation safety in i18n dynamic import
- L1: Truncate stacktraces in admin commandocentrum to first 20 lines
2026-07-04 19:10:43 +02:00
openhands 4f79d5a0ae feat: add pixel font for hotel name, toggle text/logo on click in site header 2026-07-03 16:31:17 +02:00
openhands 01e7f84d18 feat: append '- Hotel' to hotel name in title bar, site header and client header 2026-07-03 16:22:42 +02:00
openhands 187af2e147 fix: strip existing sso param from clientUrl, add aria roles to dropdowns, add x-pathname fallback, move .dropdown-menu into @layer components 2026-07-03 16:17:42 +02:00
openhands 98c9b951c4 fix: remove duplicate site-bg, handle fullscreen promise rejection, unify tooltip language, improve dropdown CSS compat and accessibility 2026-07-03 16:03:09 +02:00
openhands e5b0649bf1 fix: remove debug console.log from root layout 2026-07-03 15:51:33 +02:00
openhands 7e5813bd97 Fix client loading as atomcms normal 2026-07-03 15:16:47 +02:00
remco 5b7bd1ebb0 fix: pre-hydration navbar color to prevent flash, fix hydration mismatch in color picker 2026-07-01 16:02:01 +02:00
Simo 54ec99de6d 101%: app-level DDoS guard, PWA, /api/health, API docs, worker JAR backup
Beyond parity — the web-feasible versions of the "host-only" items plus
extras AtomCMS doesn't have:
- App-level abuse/DDoS guard (src/lib/services/abuse-guard.ts): counts
  requests per IP and auto-adds flooders to website_ip_blacklist (enforced
  by the access guard) + fires ddosDetected(). OFF by default, tunable via
  settings. The iptables layer stays host-only; this is the real app-tier
  mitigation. Access guard now also enforces the IP blacklist (cached).
- PWA: a themeable web manifest (src/app/manifest.ts) + a service worker
  (public/sw.js, cache-first assets / network-first pages) registered after
  hydration — the hotel is now installable.
- /api/health: DB + emulator(RCON) + runtime status probe.
- /developers: a public API documentation page covering every REST endpoint
  with its method, path and auth requirement.
- jobs-worker: daily emulator JAR backup (runs host-side in the worker, like
  AtomCMS's backup command) — copies + prunes; no-ops unless EMULATOR_JAR_PATH
  + EMULATOR_BACKUP_DIR are set.

Verified live (prod, amx_test): /api/health ok, manifest + sw served, docs
page renders, normal pages unaffected by the guard. tsc 0, vitest 49/49,
next build 0.
2026-06-29 18:39:23 +02:00
Simo f7b3845131 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.
2026-06-29 18:15:01 +02:00
Simo 8cedf5614e UI gaps: radio player widget, logo generator, public room page
Phase D of the parity push:
- Radio player: fixed bottom-right widget (port of atom's radio-player.blade)
  that streams the hotel radio via <audio>, with play/pause, volume, current
  DJ / now-playing and live listener count, polling the public API every 15s.
  A server gate (RadioPlayerGate) only mounts it when radio is enabled + a
  stream URL is set, so no widget JS ships when off. Mounted in the layout.
- Logo generator (/logo): client tool — hotel name + font/colour/size pickers
  with a live preview and "download PNG" via canvas.
- Public room page (/room/[id]): renders an emulator room (name, owner,
  users/max, state, category) in a ContentCard.

Verified live (prod, amx_test): /logo renders the generator, /room/50 shows
the real room "Dark Elegant Bundle", and with radio enabled the player
mounts fixed bottom-right (audio + play/pause + Test FM); reverted the test
settings. tsc 0, vitest 49/49, next build 0.
2026-06-28 21:48:42 +02:00
Simo 6f15e0c8a3 Production hardening: error pages, rate limiting, metadata
- 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.
2026-06-28 20:12:12 +02:00
Simo 7daeccb832 Add dark mode, i18n, messenger/moderation/verify, admin CRUD parity
Web-tier features completing the AtomCMS→Next.js conversion (slice 2):

UI/UX:
- Dark mode: html.dark CSS-var overrides + ThemeSwitcher (localStorage,
  no-flash boot script) wired into the nav.
- i18n (next-intl, cookie-based / no URL routing): en + it catalogs,
  request.ts, provider in root layout, LanguageSwitcher; shell (nav,
  header, footer) fully translated. URLs + access-guard unchanged.
- globals.css: --muted/--border aliases used across admin pages.

User features:
- /messages: offline messages + friend-request accept (server action
  re-reads session, two directional rows, idempotent).
- Email verification: signed-token /verify route + sendVerification wired
  into register (best-effort, never blocks signup).
- Article reactions: toggle UI on news/[slug] + server action.
- Content moderation service (website_wordfilter + optional OpenAI
  moderations, fail-open) wired into article comments + guestbook.

Admin CRUD parity (Filament replacement):
- /admin/shop (+ new/[id]) packages CRUD + read-only orders.
- /admin/transactions read-only PayPal log.
- /admin/permissions, /admin/tags, /admin/ads (+ new/[id]),
  /admin/help-questions (+ new/[id]), /admin/radio/history,
  /admin/users/[id]/edit. All gated by requireStaff + logStaffActivity.

Verified: tsc 0, vitest 48/48, next build 0 (all routes incl. new
admin CRUD + /messages + /verify).
2026-06-28 16:06:42 +02:00
Simo 22d53d0e9c Add security middleware, audit log, alerts, PayPal, cron, radio + apps
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).
2026-06-28 15:10:19 +02:00
Simo e19debb795 Port AtomCMS atom theme faithfully (Tailwind v4 + real CSS/assets + shell)
Make the template match the atom theme, not just approximate it:
- Add Tailwind v4 (+forms/typography plugins, postcss) and port the real atom
  CSS (global.css + atom app.css) into globals.css: nav-item underline, currency
  + navigation icon classes, site-bg, card-base/hover-lift, text utils — asset
  paths adapted to /assets.
- Copy the real theme assets (backgrounds, icons/currency/navigation, profile,
  leaderboards) into public/assets.
- Rebuild the shell 1:1 from the atom Blade: TopHeader (currency pills +
  user/admin dropdowns, auth-only), SiteHeader (header image + black/50 overlay,
  logo+online+Nitro client / guest Login+Create-account CTA), Navigation (white
  bar, nav-item + Community/Assistance dropdowns), Footer. ThemeVars injects the
  DB-driven CSS custom properties into :root like app.blade.php. Layout uses the
  atom body/site-bg + grid-cols-12 max-w-7xl content wrapper.

Verified: tsc 0, next build exit 0; curl confirms the atom markup, compiled CSS
references the assets, and background-light.jpg + currency/navigation icons all
serve 200.
2026-06-28 14:50:09 +02:00
Simo e8be0461d8 Add niche admin + public expansions + self-host Nunito font (batches 3-4)
Built via two more parallel agent workflows (read schema -> return files),
integrated + verified.

Admin: /admin/emulator (emulator_settings + emulator_texts key/value editor),
/admin/badges (give-badge via RCON), /admin/rare-values (CRUD), /admin/housekeeping
(CRUD), /admin/email-templates (CRUD), /admin/photos (moderation).
Public: /rares (+[category]), /leaderboard (credits/diamonds/duckets),
/guilds (+[id] members), /redeem (voucher -> sendCurrency).
Expanded: /u/[username] now shows badges + photos + guestbook (post form);
/news/[slug] now shows reactions + comments (comment form). Reactions tallied
in JS (Prisma groupBy typing avoided).
Self-hosted Nunito via next/font/google wired to --font-nunito (the atom theme
font, no runtime external fetch). Header + admin nav extended.

Verified: tsc exit 0, vitest 48/48, next build exit 0.
2026-06-28 13:44:23 +02:00
Simo 9a79acebe7 Add atom-theme visual design (header/footer, theme system, restyled pages)
Faithful port of AtomCMS's "atom" theme look (light, Habbo-retro, golden
#eeb425 / amber #f59e0b accents, white cards, 12px radii, Nunito-first stack):
- globals.css: full token set + components (.site-header/.nav-item, .btn-*,
  .card, .hero, currency pills, article cards, inputs, tables, footer).
- SiteHeader (brand + nav + currency pills + auth box, staff-aware) + SiteFooter;
  layout wraps header/content/footer. Removed the bare SiteNav.
- Restyled home (hero + article cards), login (centered card).
- siteSettings now falls back to DEFAULTS on missing key OR DB error, so pages
  render without a DB; DATABASE_CONNECT_TIMEOUT_MS env + pool acquireTimeout make
  the no-DB fallback fast.

Verified in a real browser (computed styles): header white/sticky, golden logo
gradient, uppercase nav, amber primary button @12px radius, golden outline,
hero gradient — all correct. tsc exit 0, vitest 48/48, next build exit 0.
2026-06-28 13:13:00 +02:00
Simo 6e34d485d4 Add data-driven public pages (home, news, profile)
Real App Router pages reading the converted Prisma models:
- home: latest 4 articles (websiteArticles) + hotel_name from settings
- /news + /news/[slug]: article index and detail
- /u/[username]: profile (avatar via imager helper, motto, rank, credits, online)
- shared SiteNav (reads session via auth() + hotel_name), globals.css
- src/lib/format.ts: avatarImageUrl + excerpt helpers (unit-tested)

Verified: tsc exit 0, vitest 43/43, next build exit 0 (7 routes). Pages render
against a live DB (deferred until DATABASE_URL is provided). i18n to be layered
on next.
2026-06-27 16:42:59 +02:00
Simo 443d908909 Scaffold Next.js 16 app + wire NextAuth Credentials to auth core
Minimal but real App Router app that builds (next build exit 0):
- src/lib/auth.ts: NextAuth v5 Credentials provider calling checkLogin()
  (argon2id/bcrypt + md5->argon2id upgrade gated by CONVERT_PASSWORDS), JWT
  session, /api/auth/[...nextauth] route handler.
- src/app: root layout, home (force-dynamic, reads hotel_name via siteSettings),
  /login client form (signIn).
- next.config.ts: pinned turbopack.root, serverExternalPackages for the Prisma
  MariaDB adapter; tsconfig set up for Next.

Routes: / (dynamic), /login, /api/auth. Verified: next build exit 0, 28 tests.
Still needs DB+APP_KEY to run auth end-to-end. i18n/middleware/pages to follow.
2026-06-27 16:11:52 +02:00