# MC-Manager — Copilot Instructions ## Project Overview Minecraft Server Manager — a full-stack Next.js 15+ (App Router) web application for managing Minecraft server instances. Built following Rezzect's organization-wide Next.js standards. ## Tech Stack - **Framework:** Next.js 15+ (App Router, Turbopack), React 19+, TypeScript 5.9+ (strict mode) - **Styling:** Tailwind CSS 3.4+ with dark glassmorphism design system (cyan-500 primary, `bg-gray-900/80 backdrop-blur-lg`) - **Database:** MongoDB 6+ via Mongoose 8+ with serverless-safe cached connections (`src/lib/mongodb.ts`) - **Auth:** Dual-token JWT (1h access + 7d refresh) in HTTP-only cookies, mandatory 2FA via email, bcryptjs (12 rounds) - **Containers:** Docker via dockerode — each MC server runs as its own container - **Icons:** Lucide React only — no other icon libraries - **Email:** Microsoft Graph API primary, SMTP (nodemailer) fallback — always implement dual-provider ## Project Structure ``` src/app/ — Pages ('use client' on every page) and API routes src/components/ — Shared components; templates/ for reusable CRUD patterns (DataManagementTemplate) src/contexts/ — AuthContext, ToastContext, ConfirmationContext src/lib/ — Server utilities: auth.ts, mongodb.ts, models.ts, docker.ts, date-utils.ts, input-validation.ts, audit.ts src/hooks/ — Custom React hooks src/types/ — TypeScript interfaces and types ``` ## Critical Conventions - **Every page** must start with `'use client'`; add `export const dynamic = 'force-dynamic'` if using `useSearchParams` - **Date formatting:** NEVER create custom formatters — always use `formatDate()`, `formatDateTime()`, `formatDateForInput()` from `@/lib/date-utils` - **Images:** Always `next/image` — never raw `` tags - **CRUD pages:** Use `DataManagementTemplate` from `@/components/templates/` for consistent data management UIs - **Drawers** over modals for detail views (`max-w-3xl`, `animate-slide-in-right`) ## API Route Pattern (every route must follow this order) 1. `validateSession(request)` → 401 if missing 2. `getClientIP(request)` for audit trail 3. `sanitizeObject(await request.json())` — sanitize ALL input 4. `isValidObjectId()` / `isValidEmail()` — validate params 5. Permission check → 403 + audit log if denied 6. `connectToDatabase()` then business logic 7. `createAuditLog()` for every CREATE/UPDATE/DELETE (success AND failure) 8. Return proper HTTP status: 200/201/400/401/403/404/409/500 ## Security (non-negotiable) - Server-side permission checks are **authoritative**; client-side checks are UI hints only - Permission format: `resource:action` (e.g., `servers:edit`, `servers:view:department`) - Sanitize all inputs via `sanitizeObject()` from `@/lib/input-validation.ts` - Parameterized DB queries only — never string concatenation - Audit log ALL mutations with previous/new values and client IP ## UI Patterns (Dark Theme) - **Base background:** `bg-gray-950` (page), `bg-gray-900` (surfaces), `bg-gray-800` (elevated elements) - **Color semantics:** cyan=primary, emerald=success/active, amber=warning/pending, red=error/danger, gray=neutral - **Text colors:** `text-gray-100` (primary), `text-gray-400` (secondary), `text-gray-500` (muted) - **Animations:** Only animate `transform` and `opacity`, keep under 300ms - **Buttons:** `bg-cyan-500 hover:bg-cyan-600 text-white rounded-lg` (primary), `bg-red-500 hover:bg-red-600` (danger), `bg-gray-700 hover:bg-gray-600 text-gray-200` (secondary) - **Inputs:** `bg-gray-800 border-gray-700 text-gray-100 rounded-lg focus:ring-2 focus:ring-cyan-500 focus:border-transparent placeholder-gray-500` - **Cards:** `bg-gray-900/80 backdrop-blur-lg rounded-lg shadow-lg border border-gray-700/50 p-6` - **Glassmorphism card:** `bg-gray-800/60 backdrop-blur-lg rounded-lg border border-gray-700/50 shadow-xl` - **Tables:** `bg-gray-900` body, `bg-gray-800/50` header, `divide-gray-700`, `hover:bg-gray-800` rows - **Badges:** `bg-cyan-500/20 text-cyan-400` (info), `bg-emerald-500/20 text-emerald-400` (success), `bg-amber-500/20 text-amber-400` (warning), `bg-red-500/20 text-red-400` (error) - **Borders:** `border-gray-700` (standard), `border-gray-700/50` (subtle) - **Import order:** React hooks → Next.js hooks → contexts → lib utilities → components ## Minecraft Domain & Server Types Each server instance has a **type** that determines its capabilities: | Type | Examples | Supports Plugins | Supports Mods | |------|----------|:-:|:-:| | `vanilla` | Official Mojang JAR | ✗ | ✗ | | `bukkit` | Spigot, PaperMC, Purpur, etc. | ✓ | ✗ | | `forge` | Forge | ✗ | ✓ | | `fabric` | Fabric | ✗ | ✓ | **Feature matrix by type — gate UI and API logic on `server.type`:** - **All types:** start/stop/restart, console streaming, command execution, server.properties editing, JVM args, backup/restore, player management (whitelist, ops, bans) - **Bukkit-based only:** plugin install/remove/enable/disable (JAR-based in `plugins/` directory) - **Forge/Fabric only:** mod install/remove/enable/disable (JAR-based in `mods/` directory) - **Vanilla:** no extension management — only core server features ### Core Feature Areas 1. **Server Lifecycle** — create, start, stop, restart, delete instances; real-time status (online/offline/starting/stopping) 2. **Console** — live log streaming (tail server stdout), send commands to server stdin 3. **Configuration** — edit server.properties, JVM memory/flags, world settings per instance 4. **Backups** — manual + scheduled backups of world data; restore to point-in-time 5. **Plugins** (bukkit only) — upload/install/remove JARs; enable/disable without removing 6. **Mods** (forge/fabric only) — upload/install/remove JARs; enable/disable without removing 7. **Player Management** — whitelist add/remove, op/deop, ban/unban, view online players ## Data Models (Mongoose schemas in `src/lib/models.ts`) ### User - `username`, `email`, `passwordHash` (bcrypt 12 rounds) - `roles: [ObjectId]` → references Role - `twoFactorCode`, `twoFactorExpiry` — for mandatory 2FA - `loginAttempts`, `lockUntil` — account lockout (5 attempts → 30 min) - `status`: active | inactive | locked - `lastLogin`, `createdAt`, `updatedAt` ### Role - `name` (e.g., Admin, Operator, Viewer) - `permissions: [{ resource: string, actions: string[] }]` - `description`, `isDefault`, `createdAt` ### Server - `name`, `type`: vanilla | bukkit | forge | fabric - `version` (MC version, e.g., "1.21.4") - `dockerImage` (default `itzg/minecraft-server`, or a custom image) - `containerId` (Docker container ID — set after creation) - `containerName` (e.g., `mc-{serverId}`) - `port` (host port mapped to container 25565) - `rconPort` (host port mapped to container 25575, optional) - `status`: online | offline | starting | stopping | crashed - `maxPlayers`, `memory` (min/max heap in MB) - `jvmArgs: [string]` - `autoStart: boolean`, `autoRestart: boolean` - `backupSchedule`: cron expression (e.g., `0 */6 * * *` = every 6h) or `null` if manual-only - `backupRetention`: max number of backups to keep per server (oldest auto-deleted) - `createdBy: ObjectId` → User who created it - `createdAt`, `updatedAt` ### Backup - `serverId: ObjectId` → Server - `filename`, `filePath`, `fileSize` - `type`: manual | scheduled - `status`: completed | in_progress | failed - `createdBy: ObjectId`, `createdAt` ### AuditLog - Standard org pattern: `action`, `entityType`, `entityId`, `entityName`, `userId`, `userName`, `userEmail`, `previousValues`, `newValues`, `changes`, `clientIP`, `status`, `statusCode` ## Permissions Format: `resource:action`. Relevant resources for this project: ``` servers:view servers:create servers:edit servers:delete servers:start servers:stop servers:restart servers:console backups:view backups:create backups:restore backups:delete plugins:view plugins:install plugins:remove plugins:toggle mods:view mods:install mods:remove mods:toggle players:view players:whitelist players:op players:ban users:view users:create users:edit users:delete roles:view roles:create roles:edit roles:delete audit:view ``` Admin role gets `*:*` (wildcard). Always check `server.type` before allowing plugin/mod actions — return 400 if mismatched (e.g., plugin install on a Forge server). ## Docker Architecture Every Minecraft server runs as an isolated Docker container. Use **dockerode** (`src/lib/docker.ts`) to interact with the Docker Engine API via Unix socket. ### Docker Images - **Default:** `itzg/minecraft-server` — supports all server types via `TYPE` env var (e.g., `VANILLA`, `PAPER`, `SPIGOT`, `FORGE`, `FABRIC`) - **Custom:** Users can specify any Docker image in `server.dockerImage`; when custom, the app still bind-mounts the same volume layout but skips `itzg`-specific env vars — the user is responsible for image compatibility - Store `dockerImage` on the Server model; default to `itzg/minecraft-server` if not provided ### Container Lifecycle - **Create:** `docker.createContainer()` with `server.dockerImage`, bind-mount a host volume for persistent data - **Start/Stop/Restart:** `container.start()`, `container.stop()`, `container.restart()` — update `server.status` in MongoDB to match - **Delete:** `container.remove({ force: true })`, then optionally clean up host volume - **Status sync:** On app startup and periodically, reconcile `server.status` with `container.inspect()` state ### Volume Layout Each server gets a host-mounted volume at a configurable base path (env `MC_SERVERS_PATH`, default `/opt/mc-servers/`): ``` /opt/mc-servers/{serverId}/ ├── server.properties # MC config (editable via API) ├── world/ # World data (backed up) ├── plugins/ # Bukkit-type only ├── mods/ # Forge/Fabric only ├── logs/ # Server logs └── backups/ # Backup archives ``` ### Console & Logs - **Log streaming:** `container.logs({ follow: true, stdout: true, stderr: true })` → stream to client via SSE or WebSocket - **Command execution:** `container.exec()` to run `rcon-cli` or attach to stdin to send commands - Alternative: use RCON protocol directly on the `rconPort` if RCON is enabled in server.properties ### Key Patterns for `src/lib/docker.ts` - Export a singleton `getDockerClient()` that returns a cached dockerode instance - All container operations must catch Docker API errors and map to proper HTTP status codes - Container names follow convention: `mc-{server._id}` for easy lookup - Always set resource limits: `--memory` from `server.memory.max`, CPU shares as needed - Use `RestartPolicy: { Name: 'unless-stopped' }` when `server.autoRestart` is true ### Backups - Pause world saving (`save-off` + `save-all` via RCON/exec) → tar/gzip the `world/` directory → resume (`save-on`) - Store backup archives in `{serverDir}/backups/` and record metadata in the Backup model - Restore: stop container → replace `world/` with extracted backup → start container ### Scheduled Backups (node-cron) - Use **node-cron** running inside the Next.js process — no external scheduler needed - Each server stores a `backupSchedule` cron expression (e.g., `0 */6 * * *`) and `backupRetention` count - On app startup, query all servers with a `backupSchedule` and register cron jobs via `cron.schedule()` - When a server's schedule is created/updated/deleted via API, dynamically add/update/remove the cron job - Keep a `Map` in memory for lifecycle management - `backupRetention`: after each successful backup, delete oldest archives exceeding the limit - Scheduled backups set `backup.type = 'scheduled'`; manual backups set `backup.type = 'manual'` ### Environment Variables (Docker-specific) ```env MC_SERVERS_PATH=/opt/mc-servers # Base path for server volumes DOCKER_SOCKET=/var/run/docker.sock # Docker socket path (default) ``` ## Commands ```bash npm run dev # Start dev server (port 3000, Turbopack) npm run build # Production build npm run lint # ESLint check ```