mc-manager/.github/copilot-instructions.md
2026-02-07 12:20:12 -08:00

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'; 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 <img> 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<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 set backup.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