8292cc8ffb
revert fix(infra): serve full TLS chain for epicnabbo.nl to resolve Cloudflare 525 Traefik's ACME resolver stored the epicnabbo.nl leaf certificate without the Let's Encrypt intermediate. With Cloudflare in Full (strict) mode the origin TLS handshake failed (HTTP 525), breaking every asset and the whole site layout (JS/CSS chunks, Nitro client, toolbar). Configure the epicnabbo router to use a file-based certificate that includes the full chain (leaf + LE YR2 intermediate), referenced from dynamic/epicnabbo-tls.yml. Add a regeneration script and README.
EpicNext-CMS .
A modern, high-performance content management system for Habbo hotel emulators, built on Next.js 16 (App Router) with Prisma 7. Designed to integrate seamlessly with the Polaris MySQL/MariaDB database.
Features full public-facing website, administrative panel, NextAuth authentication (argon2id/bcrypt with MD5-to-argon2id upgrade path), real-time RCON communication with the emulator, and extensive extensibility.
Requirements
| Component | Version |
|---|---|
| Node.js | >= 22 |
| pnpm | >= 10.33.4 |
| MySQL / MariaDB | 8.0+ / 10.6+ |
| Redis | Optional (caching / rate limiting) |
1. Database Setup
EpicNext-CMS shares a database with the Polaris emulator. You may use an existing or empty database.
Create a new database:
CREATE DATABASE IF NOT EXISTS epicnext_cms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Use an existing emulator database:
Point DATABASE_URL to your existing database — the CMS reads emulator tables (users, users_currency, bans, etc.) directly.
Create CMS tables:
After configuring .env, run the migration script (step 5). This creates only CMS-owned tables (website_*, radio_*, etc.), leaving the emulator schema untouched.
2. Environment Variables
Copy .env.example to .env and configure:
cp .env.example .env
Required
DATABASE_URL=mysql://user:[email protected]:3306/epicnext_cms
AUTH_SECRET= # minimum 32 characters, random
HOTEL_NAME=YourHotel
APP_URL=http://localhost:3000
Database Pool (Tunable)
DATABASE_POOL_SIZE=40
DATABASE_IDLE_TIMEOUT_MS=300000
DATABASE_CONNECT_TIMEOUT_MS=10000
Password Settings
CONVERT_PASSWORDS=false # enable to upgrade legacy MD5 hashes to argon2id on login
PASSWORD_HASH=bcrypt # bcrypt (default) or argon2id
Laravel APP_KEY (2FA Migration)
Required only for reading existing Laravel-encrypted 2FA secrets:
APP_KEY=base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
RCON (Emulator Bridge)
Enables live credits, badges, motto changes, rank changes, kick/ban:
RCON_HOST=127.0.0.1
RCON_PORT=3001
Badge Upload (Emulator Directory)
BADGE_UPLOAD_DIR=/path/to/emulator/assets/c_images/album1584
Email (Password Reset / Notifications)
Two options — Resend (recommended) or SMTP:
# Resend (preferred)
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx
# OR SMTP:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=user
SMTP_PASSWORD=password
[email protected]
OAuth (Discord / Google)
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
PayPal (Credit Purchases)
PAYPAL_CLIENT_ID=
PAYPAL_SECRET=
PAYPAL_API=https://api-m.sandbox.paypal.com # sandbox for testing; live: https://api-m.paypal.com
Redis (Caching / Rate Limiting)
REDIS_URL=redis://127.0.0.1:6379
AI Content Moderation (Comments / Guestbook)
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
Notifications & Logging
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
[email protected]
LOG_LEVEL=info # debug | info | warn | error
Emulator JAR Backup (Jobs Worker)
EMULATOR_JAR_PATH=/path/to/emulator.jar
EMULATOR_BACKUP_DIR=/path/to/backups
EMULATOR_BACKUP_KEEP=7 # number of backups to retain
3. Install Dependencies
pnpm install
4. Generate Prisma Client
pnpm prisma:generate
Generates TypeScript types in src/generated/prisma/.
5. Run CMS Migrations
pnpm db:migrate
Executes SQL files in prisma/migrations/ against the database, creating all CMS-owned tables.
Check migration status:
pnpm db:migrate:status
6. Configure Website Settings
After starting the server (step 7) and logging in as an administrator:
- Navigate to Admin → CMS Settings (
/admin/settings). - Configure the following essentials:
hotel_name— your hotel namehabbo_imaging_url— avatar image URL (default:https://www.habbo.com/habbo-imaging/avatarimage)nitro_client_url— Nitro client URL (refer toupdate-Nitrov3.sh)logo_url— path to your logocms_favicon— favicon URLmin_staff_rank— minimum rank required for admin access (default: 7)- Optionally: theme colors, CAPTCHA, VPN blocking, and more.
7. Start the Server
Development (Hot Reload)
pnpm dev
Production
pnpm build && pnpm start
TypeScript Check
pnpm typecheck
Run Tests
pnpm test
8. Jobs Worker (Background Tasks)
Run as a persistent process (e.g., via systemd, screen, or PM2):
pnpm jobs:worker
Scheduled tasks:
- Daily at 03:00 — Emulator JAR backup (when
EMULATOR_JAR_PATHandEMULATOR_BACKUP_DIRare configured) - Daily at 04:00 — Cleanup of login logs older than 30 days and expired password reset tokens older than 7 days
9. First Login
- Open
http://localhost:3000in your browser. - Register an account via
/register, or log in with an existing account. - To obtain administrator access, your rank must equal or exceed
min_staff_rank(default: 7).- Set it directly in the database:
UPDATE users SET rank = 7 WHERE username = 'yourname';
- Set it directly in the database:
- Access the admin panel at
/admin.
Optional Integrations
Emulator — RCON
For live actions (credits, badges, rank changes, kick/ban), the Polaris emulator must be running with RCON enabled and accessible at the configured RCON_HOST:RCON_PORT.
Client — Nitro
The /client page loads the Nitro client. Configure the client URL via Admin → CMS Settings (nitro_client_url).
Radio
For use with a streaming radio (e.g., Azuracast):
radio_now_playing_api_urlradio_listeners_api_url
CAPTCHA
Supports Cloudflare Turnstile and Google reCAPTCHA. Configure via Admin → CMS Settings:
captcha_provider—turnstileorrecaptchaturnstile_site_key/turnstile_secretrecaptcha_site_key/recaptcha_secret
Theming
12 preset themes are available via Admin → Theme (/admin/theme), along with fully customizable color schemes.
Database Architecture
| Component | Type | Migrations |
|---|---|---|
Emulator tables (users, items, rooms, etc.) |
Existing Polaris schema | None — read-only for CMS |
CMS tables (website_*, radio_*, etc.) |
CMS-owned | prisma/migrations/*.sql (9 files) |
| Migration tracking | cms_migrations table |
Auto-created |
Project Structure
├── prisma/
│ ├── schema.prisma # ~190 models (emulator + CMS)
│ └── migrations/ # 9 SQL migrations for CMS tables
├── scripts/
│ ├── apply-migrations.ts # migration runner
│ └── jobs-worker.ts # background task scheduler
├── src/
│ ├── app/ # Next.js App Router (pages & API routes)
│ ├── actions/ # Server Actions
│ ├── components/ # UI components (header, navigation, etc.)
│ ├── lib/
│ │ ├── auth/ # NextAuth with password hashing & 2FA
│ │ ├── services/ # RCON, email, currency, PayPal, etc.
│ │ ├── prisma.ts # database connection singleton
│ │ └── redis.ts # Redis client abstraction
│ ├── messages/ # i18n translations (en, nl, de, fr, es, it)
│ └── env.ts # Zod-validated environment schema
├── public/assets/ # images, icons, fonts
├── .env.example # environment template
└── update-Nitrov3.sh # emulator & Nitro updater utility
License
This project is licensed under the CC BY-NC-SA 4.0 license. See LICENSE for details.