# 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
```