Commit Graph
28 Commits
Author SHA1 Message Date
Simo 60278b9e71 feat: add admin operations suite
Local Build and Deploy / deploy (push) Successful in 50s
2026-07-12 20:11:28 +02:00
Simo c0ffc74f9b fix: externalize lzma for import build
Local Build and Deploy / deploy (push) Successful in 49s
2026-07-12 19:15:08 +02:00
Simo 3497df9dfd feat: add asset import services 2026-07-12 19:12:25 +02:00
Simo 4b596226e0 fix: complete acl management 2026-07-12 19:12:24 +02:00
Simo 8d37ae9ae0 style: normalize restored source endings
Remote Build and Deploy / deploy (push) Has been cancelled
2026-07-11 21:19:54 +02:00
Simo 0cd753c735 fix: restore complete admin feature dependencies 2026-07-11 21:15:54 +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
Simo 4a1e1115b3 Harden CMS security and theme contrast 2026-07-11 20:27:20 +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 5c638cd6bc perf: add bans.user_id index, Redis cache layer, rate-limit improvements, radio contest/giveaway columns, and tests
- Add DB index on bans.user_id to speed up per-request ban lookups (migration 0008)
- Replace in-process rate limiter with Redis-backed implementation with in-memory fallback
- Add Redis caching layer for site settings with TTL invalidation (migration 0009)
- Add rate limiting to resetPassword to prevent token brute-force attacks
- Update all rateLimit callers to await the now-async function
- Flesh out RadioContests and RadioGiveaways models with title, description, prize, date, and winner columns
- Update radio contest/giveaway pages to display new fields
- Add tests for rate limiter (4 tests) and password-reset actions (3 tests)
- Add REDIS_URL environment variable (optional, falls back to in-memory)
2026-07-08 12:49:24 +02:00
openhands eb01b14379 Add ultimate file-based email fallback so mailer always works without any configuration 2026-07-07 20:18:30 +02:00
openhands 065215b4dc Add local sendmail fallback so mailer works internally without external services 2026-07-07 20:15:29 +02:00
openhands bf4075233d Add Resend mailer as primary with SMTP fallback for reliable email delivery 2026-07-07 20:12:56 +02:00
openhands 7412bd2dda Remove outfit selection from register form and fix terms checkbox not toggling 2026-07-07 18:53:13 +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
remco 1de72863db latest changes 2026-07-01 15:06:52 +02:00
Simo 56cd6fd058 Fix Nitro client launcher: read the nitro_path setting
The /client launcher read a non-existent `nitro_client_url` setting, so it
always fell through to "No client configured" and the client never launched.
Faithful to AtomCMS's NitroController + nitro.blade.php, build the launch URL
as {nitro_path}/index.html?sso=<ticket> plus the toolbar colour params, via a
shared buildNitroClientUrl helper. The Flash launcher's Nitro fallback used the
same dead key and is fixed too.
2026-06-29 21:10:44 +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 80f591a343 Add public REST API, anti-abuse protections, radio/GitHub cron jobs
Phase A — Public REST API (was the biggest gap). 20 JSON endpoints under
/api mirroring AtomCMS: users/[username], online(+/count), me, articles
(+/[slug]), photos, home, staff, teams, leaderboard, shop(+/categories),
values(+/categories), settings, radio/{config,now-playing,listeners,
shouts}. Shared src/lib/api.ts (apiJson — BigInt-safe + CORS, pagination).
Read-only, fail-soft, and field-safe (never exposes password/auth_ticket/
2FA secrets/mail).

Phase B — Anti-abuse on registration: CAPTCHA (Cloudflare Turnstile /
Google reCAPTCHA, settings-driven, widget rendered on the register page),
VPN/proxy detection (proxycheck.io / IPQualityScore via /admin/vpn
settings), and max-accounts-per-IP. All fail-open when unconfigured.
src/lib/services/{captcha,ip-lookup}.ts.

Phase C — jobs-worker cron suite: radio-record-songs (30s, logs track
changes to radio_song_plays), radio-auto-dj (rotates radio_auto_dj_playlist
when no live DJ), github-update-check (hourly, sets update_available).
Shared src/lib/services/radio.ts (now-playing/listeners parsing).

Verified live (prod, amx_test): /api/* return real JSON (leaderboard 6
users, settings carry no secrets, user endpoint hides password). tsc 0,
vitest 49/49, next build 0 (20 new API routes).
2026-06-28 21:44:02 +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 e668fa85ec Add 2FA, email + password reset, and batch-7 pages
Auth (hand-built on the auth core):
- 2FA: User model gains two_factor_secret/recovery_codes/confirmed_at (+ idempotent
  MariaDB migration). authorize() requires a valid TOTP code when 2FA is confirmed
  (secret decrypted via Laravel APP_KEY, fail-closed). Two-step login (precheckLogin
  reveals the code field). /settings/2fa enable/confirm/disable flow.
- Password reset: nodemailer email service; PasswordReset model + migration;
  /forgot (request, generic response) + /reset (token sha256 + 1h TTL, sets argon2id
  hash). Login links to forgot.

Batch 7 (parallel agents): /admin/commandocentrum (RCON controls + emulator_errors),
social write actions (friend request + guild forum new thread), /help/[category],
/badges (public). env: APP_KEY, APP_URL, SMTP_*. Nav extended.

Verified: tsc exit 0, vitest 48/48, next build exit 0 (64 page routes).
2026-06-28 14:25:19 +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 8fca407f0f Add RCON client + SendCurrency service (AtomCMS-faithful)
- rcon.ts: Node net.Socket transport speaking AtomCMS's exact protocol (raw
  JSON {"key","data"} over rcon_ip:rcon_port, fire-and-forget) with a bounded
  timeout + backoff retry; typed commands matching RconService EXACTLY
  (sendgift `itemid`, disconnect `username`, no-data => data:null, givepoints
  numeric `type`, forwarduser, setmotto/setrank/alertuser, give*). Injectable
  transport keeps command shapes unit-testable.
- send-currency.ts: mirrors the SendCurrency action — RCON deliver, else DB
  fallback (credits -> users.credits; duckets/diamonds/points -> users_currency
  by type 0/5/101).
- env: RCON_HOST/PORT/TIMEOUT/RETRIES. vitest: SKIP_ENV_VALIDATION for unit tests.

Verified: tsc exit 0, vitest 39/39, next build exit 0. Live TCP test deferred
(needs a running emulator).
2026-06-27 16:37:42 +02:00
Simo b5bc7f6daf Rebuild atomcms-next foundation on Prisma (align to habbo-next reference)
Replace the superseded Drizzle monorepo scaffold with a single-app Prisma 7
data layer, mirroring the proven habbo-next approach to the shared Arcturus
emulator DB (introspect/conform only, idempotent SQL migrations via a custom
runner; never migrate-diff).

- prisma/schema.prisma: User, UsersCurrency (composite PK), Ban, WebsiteSetting
  mapped to AtomCMS's default.sql columns; client generated to src/generated/prisma
- src/lib/prisma.ts: PrismaMariaDb adapter singleton; src/env.ts zod env
- src/lib/services: siteSettings (cached setting() equivalent) + CurrencyType enum
- prisma/migrations + scripts/apply-migrations.ts: idempotent CMS-table runner

Verified: prisma generate OK, tsc --noEmit exit 0, vitest 6/6 pass.
2026-06-27 15:26:40 +02:00