Files
EpicNext-Cms/README.md
T
openhands 56061e41d4
CI / check (push) Failing after 12s
CI / deploy (push) Skipped
CI / release (push) Skipped
refactor: replace Prisma ORM runtime with Drizzle ORM facade
- Replace Prisma client runtime with Drizzle ORM (zero Prisma engine/query engine in production)
- Add Prisma-compatible facade (@/lib/prisma-facade.ts) backed by Drizzle for backwards compatibility
- Runtime queries route through Drizzle ORM; @prisma/client is now devDependency (types only)
- Remove @prisma/adapter-mariadb dependency; delete prisma-pool.ts and types/prisma.ts
- New Drizzle schema layer: src/db/schema.ts (176 tables) and src/lib/db.ts (connection)
- Update README documenting the dual-layer ORM architecture
- Restore src/generated/ gitignore (build artifact for local type generation)
- 0 TypeScript errors, 583 tests passing

The facade intentionally uses `any` types to match the Prisma Client API surface,
allowing existing code to run unmodified while routing queries through Drizzle at runtime.
2026-07-31 14:11:03 +02:00

291 lines
12 KiB
Markdown

# EpicNext-CMS v1.0.1
A modern, high-performance content management system for Habbo hotel emulators, built on **Next.js 16** (App Router) with **Drizzle ORM** and **React 19**. Designed to integrate seamlessly with Polaris / Arcturus Morningstar MySQL/MariaDB databases.
Features a premium animated homepage (typewriter hero, floating orbs, scroll counters), a full admin panel, NextAuth authentication (bcrypt with MD5-to-bcrypt upgrade), real-time RCON communication, Server-Sent Events for live radio data, smooth page transitions, and PM2 production deployment.
---
## System Requirements
| Component | Version | Notes |
| --------------- | -------------- | ---------------------------------------- |
| Node.js | >= 22 | Required by Next.js 16 |
| pnpm | >= 10.33.4 | Package manager (npm/yarn not supported) |
| MySQL / MariaDB | 8.0+ / 10.6+ | Shared with the emulator |
| Redis | 7.x+ | Optional — caching, rate limiting, SSE |
| Java | 17+ | Required only if building the emulator |
| Maven | 3.9+ | Required only if building the emulator |
---
## Quick Start
### 1. Clone & Install
```bash
git clone https://gitlab.epicnabbo.nl/remco/EpicNext-Cms.git
cd EpicNext-Cms
pnpm install
```
### 2. Database Setup
The CMS shares a database with the Polaris/Arcturus emulator. Use an existing database or create a new one:
```sql
CREATE DATABASE IF NOT EXISTS epicnext_cms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
The CMS reads emulator-owned tables (`users`, `items`, `rooms`, `bans`, etc.) directly. It never creates, alters, or drops them. The Drizzle schema in `src/db/schema.ts` is generated from the existing database structure and does not modify it.
> **Note:** The CMS does **not** own the database schema — it maps to tables that are managed by the emulator. All Drizzle schema definitions use `drizzle-orm`'s runtime mapping (no `drizzle-kit push/migrate` is ever run against the emulator schema). CMS-owned tables (`website_*`, `radio_*`, etc.) are created via idempotent SQL files in `prisma/migrations/`.
### 3. Configure Environment
```bash
cp .env.example .env
```
Edit `.env` with at minimum:
```dotenv
DATABASE_URL=mysql://user:[email protected]:3306/epicnext_cms
AUTH_SECRET=<random 32+ character string>
HOTEL_NAME=YourHotel
APP_URL=http://localhost:3000
```
See `.env.example` for all optional variables (RCON, email, Redis, OAuth, PayPal, etc.).
### 4. Type Generation (Optional for Development)
```bash
pnpm prisma:generate
```
Generates TypeScript types in `src/generated/prisma/`. This is only needed for developer type-checking of the `prisma` facade — it does not add any Prisma runtime overhead in production. The facade (`src/lib/prisma-facade.ts`) routes all queries through Drizzle ORM at runtime.
Alternatively, you can work with Drizzle directly via the typed schema in `src/db/schema.ts`:
```ts
import { db } from "@/lib/db";
import { users } from "@/db/schema";
const result = await db.select().from(users).where(eq(users.username, "hello"));
```
### 5. Run CMS Migrations
```bash
pnpm db:migrate
```
Creates all CMS-owned tables (`website_*`, `radio_*`, `acl_*`, `admin_audit_log`, etc.) via idempotent SQL files in `prisma/migrations/`. Emulator tables are never touched.
Check migration status:
```bash
pnpm db:migrate:status
```
### 6. Build & Start
```bash
# Development (hot reload)
pnpm dev
# Production
pnpm build && pnpm start
```
Open `http://localhost:3000` in your browser.
### 7. First Login
1. Register an account at `/register`, or log in with an existing emulator account.
2. Grant yourself admin access: `UPDATE users SET rank = 7 WHERE username = 'yourname';`
3. Visit `/admin` and configure your hotel via **Admin → CMS Settings**.
---
## Production Deployment (PM2)
```bash
pnpm build
pm2 start pnpm --name "next" -- start
pm2 save
```
Restart after updates:
```bash
git pull
pnpm install
pnpm build
pm2 restart next
```
The CMS runs behind an nginx reverse proxy on the default port 3000. Static assets (media uploads) are persisted via `/api/media/*` and survive rebuilds.
---
## Scripts
| Command | Description |
| ---------------------- | -------------------------------------------------- |
| `pnpm dev` | Start development server (hot reload) |
| `pnpm build` | Production build |
| `pnpm start` | Start production server |
| `pnpm typecheck` | Run TypeScript type checking |
| `pnpm test` | Run all tests (Vitest) |
| `pnpm db:migrate` | Apply pending SQL migrations |
| `pnpm db:migrate:status` | Show migration status |
| `pnpm analyze` | Build + open bundle analyzer |
| `pnpm jobs:worker` | Start background task worker (systemd / PM2) |
| `pnpm biome:check` | Lint and format code |
---
## Performance Features
| Feature | Description |
| ------------------------------ | -------------------------------------------------------------- |
| **React Compiler** | Automatic memoization — reduces unnecessary re-renders |
| **View Transitions API** | Native browser transitions between page navigations |
| **Lenis Smooth Scroll** | Fluid, customizable scrolling (respects `prefers-reduced-motion`) |
| **Server-Sent Events** | Real-time radio now-playing & listeners via SSE (no polling) |
| **Redis Caching** | Caches API responses (home, radio config) up to 30s in Redis |
| **Bundle Analyzer** | Run `pnpm analyze` to visualize and optimize bundle sizes |
| **RCON (TCP Socket)** | Live commands to the emulator (credits, badges, kick, ban) |
| **Streaming & Suspense** | Next.js App Router streaming for fast page loads |
| **Automatic Image Optimization** | `next/image` with Sharp for resizing and WebP/AVIF |
| **CSS-based Animations** | `input-glow`, `btn-shine`, `Reveal` scroll animations, floating orbs |
| **Compression** | Gzip compression enabled on all responses |
| **Stale Times** | Optimized router cache (30s dynamic, 180s static) |
| **PWA** | Service worker with skip-waiting, stale CSS chunk recovery |
| **Biome** | Fast linting and formatting (replaces ESLint + Prettier) |
---
## Architecture
```
├── prisma/
│ ├── schema.prisma # ~190 models (emulator + CMS) — used for type generation only
│ └── migrations/ # 16 SQL migrations for CMS tables
├── src/
│ ├── db/
│ │ ├── schema.ts # Drizzle ORM schema (176 tables — runtime data layer)
│ │ └── migrations/ # Drizzle migration files (if used)
│ ├── app/ # Next.js App Router (pages & API routes)
│ ├── actions/ # Server Actions
│ ├── components/ # UI components
│ ├── lib/
│ │ ├── auth/ # NextAuth, password hashing, 2FA, SSO tickets
│ │ ├── services/ # RCON, email, currency, PayPal, alerts
│ │ ├── prisma.ts # Prisma-compatible facade (routes to Drizzle at runtime)
│ │ ├── prisma-facade.ts # Drizzle-backed implementation of Prisma API surface
│ │ ├── db.ts # Drizzle connection singleton (runtime)
│ │ ├── redis.ts # Redis client (ioredis)
│ │ ├── redis-cache.ts # Redis caching utility for API routes
│ │ ├── cache.ts # In-memory cache fallback
│ │ ├── motion.ts # Framer Motion animation variants
│ │ └── use-event-source.ts # React hook for SSE subscriptions
│ ├── messages/ # i18n translations (en, nl, de, fr, es, it, pt, da, no, sv)
│ └── env.ts # Zod-validated environment schema
├── public/
│ ├── assets/ # Images, icons, fonts
│ └── scripts/ # Client-side scripts (theme-init.js)
├── update-Nitrov3.sh # Emulator & Nitro updater utility
├── next.config.ts # Next.js configuration
└── .env.example # Environment template with all options
```
### Database Ownership
| Component | Type | Migrations |
| ------------------------------------------------- | ----------------------- | ----------------------------------- |
| Emulator tables (`users`, `items`, `rooms`, etc.) | Existing Polaris schema | None — CMS reads/writes only |
| CMS tables (`website_*`, `radio_*`, etc.) | CMS-owned | `prisma/migrations/*.sql` (16 files) |
| Migration tracking | `cms_migrations` table | Auto-created by migration runner |
### ORM Architecture
The CMS uses a dual-layer approach:
- **Runtime (Drizzle ORM)**: `@/lib/db` exposes a Drizzle singleton connected to the MySQL/MariaDB database. All new code should use this directly. The schema is defined in `src/db/schema.ts` with 176 tables typed against the existing database columns.
- **Legacy Compatibility (Prisma Facade)**: `@/lib/prisma` provides a Prisma-compatible API surface backed by Drizzle. This allows existing code to continue working without refactoring. The facade (`@/lib/prisma-facade.ts`) implements the Prisma client API (`findMany`, `findUnique`, `create`, `$transaction`, `$queryRaw`, etc.) but routes all queries through Drizzle at runtime — **no Prisma client engine or query engine is loaded in production**.
- **Type Generation**: `src/generated/prisma/` (regenerated via `pnpm prisma:generate`) exists solely for TypeScript type-checking. It is `gitignore`d and is never bundled in the production build.
Migration path: new database access should use `import { db } from "@/lib/db"` with queries built via `src/db/schema.ts`. The facade is maintained for backwards compatibility but is not recommended for new code.
---
## Optional Integrations
### Emulator — RCON
For live actions (credits, badges, rank changes, kick, ban, hotel alerts). The emulator must be running with RCON enabled at `RCON_HOST:RCON_PORT`.
### Client — Nitro
The `/client` page loads the Nitro client. Configure the client URL via **Admin → CMS Settings** (`nitro_client_url`). See `update-Nitrov3.sh` for deployment.
### Radio
Supports any streaming radio (e.g., Azuracast). Configure endpoints via CMS Settings:
- `radio_now_playing_api_url`
- `radio_listeners_api_url`
The radio player uses SSE for real-time updates (10s interval, no polling).
### Background Jobs
Run as a persistent process:
```bash
pnpm jobs:worker
```
Scheduled tasks:
- **Daily 03:00** — Emulator JAR backup (requires `EMULATOR_JAR_PATH` + `EMULATOR_BACKUP_DIR`)
- **Daily 04:00** — Cleanup login logs (>30 days) and expired password reset tokens (>7 days)
### CAPTCHA
Supports Cloudflare Turnstile and Google reCAPTCHA. Configure via CMS Settings.
### Theming
12 preset themes with fully customizable colors via **Admin → Theme** (`/admin/theme`).
### AI Content Moderation
Optional OpenAI-powered moderation for comments and guestbook posts. Set `OPENAI_API_KEY`.
---
## Development
```bash
pnpm dev # Start with hot reload
pnpm typecheck # Type check all files
pnpm test # Run test suite
pnpm analyze # Build and analyze bundle sizes
pnpm biome:check # Lint and format
```
### Contributing
1. Ensure typecheck and tests pass: `pnpm typecheck && pnpm test`
2. Follow existing code conventions (Server Components where possible, minimal client boundaries)
3. Use the `src/lib/motion.ts` animation variants for consistent animations
4. SQL migrations in `prisma/migrations/` must be idempotent
---
## License
CC BY-NC-SA 4.0. See `LICENSE` for details.