12 KiB
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'; addexport const dynamic = 'force-dynamic'if usinguseSearchParams - Date formatting: NEVER create custom formatters — always use
formatDate(),formatDateTime(),formatDateForInput()from@/lib/date-utils - Images: Always
next/image— never raw<img>tags - CRUD pages: Use
DataManagementTemplatefrom@/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)
validateSession(request)→ 401 if missinggetClientIP(request)for audit trailsanitizeObject(await request.json())— sanitize ALL inputisValidObjectId()/isValidEmail()— validate params- Permission check → 403 + audit log if denied
connectToDatabase()then business logiccreateAuditLog()for every CREATE/UPDATE/DELETE (success AND failure)- 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
transformandopacity, 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-900body,bg-gray-800/50header,divide-gray-700,hover:bg-gray-800rows - 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
- Server Lifecycle — create, start, stop, restart, delete instances; real-time status (online/offline/starting/stopping)
- Console — live log streaming (tail server stdout), send commands to server stdin
- Configuration — edit server.properties, JVM memory/flags, world settings per instance
- Backups — manual + scheduled backups of world data; restore to point-in-time
- Plugins (bukkit only) — upload/install/remove JARs; enable/disable without removing
- Mods (forge/fabric only) — upload/install/remove JARs; enable/disable without removing
- 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 RoletwoFactorCode,twoFactorExpiry— for mandatory 2FAloginAttempts,lockUntil— account lockout (5 attempts → 30 min)status: active | inactive | lockedlastLogin,createdAt,updatedAt
Role
name(e.g., Admin, Operator, Viewer)permissions: [{ resource: string, actions: string[] }]description,isDefault,createdAt
Server
name,type: vanilla | bukkit | forge | fabricversion(MC version, e.g., "1.21.4")dockerImage(defaultitzg/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 | crashedmaxPlayers,memory(min/max heap in MB)jvmArgs: [string]autoStart: boolean,autoRestart: booleanbackupSchedule: cron expression (e.g.,0 */6 * * *= every 6h) ornullif manual-onlybackupRetention: max number of backups to keep per server (oldest auto-deleted)createdBy: ObjectId→ User who created itcreatedAt,updatedAt
Backup
serverId: ObjectId→ Serverfilename,filePath,fileSizetype: manual | scheduledstatus: completed | in_progress | failedcreatedBy: 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 viaTYPEenv 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 skipsitzg-specific env vars — the user is responsible for image compatibility - Store
dockerImageon the Server model; default toitzg/minecraft-serverif not provided
Container Lifecycle
- Create:
docker.createContainer()withserver.dockerImage, bind-mount a host volume for persistent data - Start/Stop/Restart:
container.start(),container.stop(),container.restart()— updateserver.statusin MongoDB to match - Delete:
container.remove({ force: true }), then optionally clean up host volume - Status sync: On app startup and periodically, reconcile
server.statuswithcontainer.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 runrcon-clior attach to stdin to send commands - Alternative: use RCON protocol directly on the
rconPortif 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:
--memoryfromserver.memory.max, CPU shares as needed - Use
RestartPolicy: { Name: 'unless-stopped' }whenserver.autoRestartis true
Backups
- Pause world saving (
save-off+save-allvia RCON/exec) → tar/gzip theworld/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
backupSchedulecron expression (e.g.,0 */6 * * *) andbackupRetentioncount - On app startup, query all servers with a
backupScheduleand register cron jobs viacron.schedule() - When a server's schedule is created/updated/deleted via API, dynamically add/update/remove the cron job
- Keep a
Map<serverId, CronJob>in memory for lifecycle management backupRetention: after each successful backup, delete oldest archives exceeding the limit- Scheduled backups set
backup.type = 'scheduled'; manual backups setbackup.type = 'manual'
Environment Variables (Docker-specific)
MC_SERVERS_PATH=/opt/mc-servers # Base path for server volumes
DOCKER_SOCKET=/var/run/docker.sock # Docker socket path (default)
Commands
npm run dev # Start dev server (port 3000, Turbopack)
npm run build # Production build
npm run lint # ESLint check