diff --git a/docs/code-bridge/requests/REQ-2026-04-14-server-command-center.md b/docs/code-bridge/requests/REQ-2026-04-14-server-command-center.md new file mode 100644 index 0000000..0ecdb82 --- /dev/null +++ b/docs/code-bridge/requests/REQ-2026-04-14-server-command-center.md @@ -0,0 +1,303 @@ +# Feature Request: Server Command Center Rebuild + +**Date:** 2026-04-14 +**Topic:** Rebuild Trinity Console server module into a full server command center +**Priority:** HIGH — post-launch week, target build start April 15+ +**Filed by:** Chronicler #87 +**Scope:** Large — multiple new files, DB migration, new npm dependency + +--- + +## Background + +The current server matrix page (`/admin/servers`) is functional but minimal: +- Shows online/offline, whitelist status, last sync, Discord channel status +- Sync Now + Toggle Whitelist buttons only +- Discord channel detection is broken for many servers (slug derivation from full name fails) +- No power controls, no restart scheduling, no createserver/delserver from UI +- `_matrix_body.ejs` duplicates all card HTML inline instead of using `_server_card.ejs` + +Michael wants this page to become a **Server Command Center** — the single place to manage every aspect of every game server without touching the Pterodactyl panel UI. + +--- + +## Session Context (Chronicler #87, April 13-14) + +Tonight we: +- Discovered all Discord channel detection is broken due to slug derivation failures + (e.g. "All the Mods 10: To the Sky" → code generates `all-the-mods-10-to-the-sky`, Discord has `atm10-tts`) +- Designed the `short_name` system to fix this permanently +- Added Pterodactyl Admin API key to Arbiter `.env` and ops manual +- Added Uptime Kuma API key to Arbiter `.env` +- Confirmed Uptime Kuma 2.1.0 uses Socket.IO (not REST) for monitor management +- Established that `/createserver` and `/delserver` move to Trinity Console buttons (slash commands remain as fallback only) +- Confirmed 5 Discord channels per server: chat, in-game, forum, status, voice + +--- + +## New Environment Variables (already added to `/opt/arbiter-3.0/.env`) + +``` +UPTIME_KUMA_URL=http://localhost:3001 +UPTIME_KUMA_API_KEY=uk2_-iM8Trb4ftJCedpv2Kcz2JRSD49Zrv-gbNkVyh87 +PANEL_ADMIN_KEY=ptla_4eKCnPBofAmvLDjouTGS5OagDpIra58nRetjnXOeoh5 +``` + +Note: `PANEL_CLIENT_KEY` and `PANEL_APPLICATION_KEY` already exist in `.env`. +Use `PANEL_ADMIN_KEY` for power actions and application-level calls. + +--- + +## Part 1: Database Migration + +### New table: `server_config` + +```sql +CREATE TABLE IF NOT EXISTS server_config ( + server_identifier VARCHAR(36) PRIMARY KEY, + short_name VARCHAR(64) UNIQUE, + short_name_locked BOOLEAN DEFAULT false, + display_name VARCHAR(128), + restart_enabled BOOLEAN DEFAULT true, + restart_offset_minutes INTEGER DEFAULT 0, + node VARCHAR(8), + pterodactyl_name VARCHAR(128), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); +``` + +`restart_enabled` and `restart_offset_minutes` are for Task #94 (Global Restart Scheduler) — include now, use later. + +--- + +## Part 2: New npm Dependency + +```bash +npm install uptime-kuma-api +``` + +This is the official Socket.IO wrapper for Uptime Kuma 2.x. +REST API only exposes status pages — monitor CRUD requires Socket.IO. + +--- + +## Part 3: New Service Files + +### `src/services/uptimeKuma.js` + +Wrapper around `uptime-kuma-api`. Must handle: +- Connect/disconnect lifecycle (don't leave connections open) +- `createMonitor(name, hostname, port, discordChannelId)` — creates a Game monitor type, sets Discord notification to the status channel +- `deleteMonitor(name)` — finds by name, deletes +- Error handling if Kuma is unreachable (don't crash createserver) + +Monitor config to use: +- Type: `port` (TCP port check, works for Minecraft) +- Interval: 60 seconds +- Name format: `{pterodactyl_name} - {node}` (matches existing monitors e.g. "Stoneblock 4 - TX") +- Notification: Discord webhook to the `{short_name}-status` channel + +**Important:** Look at existing monitors in Uptime Kuma to match the exact format. +Run this to see existing monitor names: +```bash +# Check existing monitor names via Kuma API +``` + +### `src/services/pterodactyl.js` + +Consolidate Pterodactyl API calls. Must handle: +- `powerAction(identifier, signal)` — start/stop/restart/kill +- `sendCommand(identifier, command)` — send console command +- `getServerResources(identifier)` — CPU, RAM, player count (via SFTP query or resources endpoint) +- Uses `PANEL_CLIENT_KEY` for client endpoints, `PANEL_ADMIN_KEY` for application endpoints + +--- + +## Part 4: Backend Route Changes + +### `src/routes/admin/servers.js` — major additions + +#### POST `/:identifier/set-short-name` +- Validates `short_name` is URL-safe (lowercase, hyphens, numbers only) +- Checks uniqueness against `server_config` table +- If not locked: saves to DB, sets `short_name_locked = false` +- Returns updated card HTML via HTMX + +#### POST `/:identifier/lock-short-name` +- Sets `short_name_locked = true` — **IRREVERSIBLE** +- Returns updated card HTML (lock button disappears, field becomes read-only) + +#### POST `/:identifier/createserver` +- Requires `short_name_locked = true` — reject if not +- Creates Discord category: `🎮 {pterodactyl_name}` +- Creates 5 channels under category: + - `{short_name}-chat` (text) + - `{short_name}-in-game` (text) + - `{short_name}-forum` (forum) + - `{short_name}-status` (text — Uptime Kuma posts here) + - `{pterodactyl_name}` (voice) +- Creates Uptime Kuma monitor linked to status channel +- Returns success/error to card + +#### POST `/:identifier/delserver` +- Confirmation required (HTMX confirm dialog) +- Deletes all 5 channels + category from Discord +- Deletes Uptime Kuma monitor +- Does NOT delete `server_config` row (keep history) +- Does NOT touch Pterodactyl (server stays in panel) + +#### POST `/:identifier/power` +- Body: `{ signal: 'start' | 'stop' | 'restart' | 'kill' }` +- Calls `pterodactyl.powerAction()` +- Returns inline status to card + +#### POST `/:identifier/console` +- Body: `{ command: string }` +- Calls `pterodactyl.sendCommand()` +- Used for the restart warning title/tellraw commands + +--- + +## Part 5: View Changes + +### `src/views/admin/servers/_server_card.ejs` — full rebuild + +Each card should display: + +**Header row:** +- Server name (bold) +- Node badge (🔥 TX1 or ❄️ NC1) +- Online/offline pill (green pulse or gray) + +**Short name section (shown if not locked):** +- Text input: "Discord short name (e.g. atm10-tts)" +- "Save" button → `hx-post="/:id/set-short-name"` +- "Lock In" button (red, confirm dialog) → `hx-post="/:id/lock-short-name"` +- Warning: "⚠️ Once locked, this cannot be changed" + +**Short name section (shown if locked):** +- Shows `short-name` as read-only badge +- No edit option + +**Stats row (2-col grid):** +- Whitelist: ✅ Enabled / 🔓 Disabled +- Last Sync: timestamp + +**Discord channels status:** +- If locked: show each of 5 channels with ✅/❌ +- If not locked: show "Set short name to enable channel detection" + +**Action buttons:** +- ⚡ Sync Now +- Toggle Whitelist +- 🚀 Create Server (Discord) — only if locked, only if channels missing +- 🗑️ Delete Server (Discord) — only if channels exist, red, confirm required +- Power: ▶️ Start | ⏹ Stop | 🔄 Restart (only if online status known) + +### `src/views/admin/servers/_matrix_body.ejs` + +Refactor to use `<%- include('./_server_card', { server }) %>` instead of duplicating card HTML. + +--- + +## Part 6: Discord Channel Detection Fix + +Update `checkServerChannels()` in `servers.js` to use `short_name` from `server_config` instead of deriving from the full server name. + +```javascript +// NEW: use short_name from DB +function checkServerChannels(shortName, serverName, allChannels) { + if (!shortName) return { missing: [], found: [], complete: false, unconfigured: true }; + + const expectedChannels = [ + { name: `${shortName}-chat`, type: 'text' }, + { name: `${shortName}-in-game`, type: 'text' }, + { name: `${shortName}-forum`, type: 'forum' }, + { name: `${shortName}-status`, type: 'text' }, + { name: serverName.split(' - ')[0].replace(/\s*\([^)]*\)/g, '').trim(), type: 'voice' } + ]; + // ... rest of detection logic +} +``` + +--- + +## Part 7: Existing Short Names to Pre-Populate + +These are already confirmed from Discord channel audit (Chronicler #87): + +| Pterodactyl Name | short_name | Node | +|-----------------|------------|------| +| All the Mods 10: To the Sky | atm10-tts | NC1 | +| All the Mons | all-the-mons | NC1 | +| Mythcraft 5 | mythcraft-5 | NC1 | +| All of Create (Creative) | all-of-create | NC1 | +| DeceasedCraft | deceasedcraft | NC1 | +| Sneak's Pirate Pack | sneaks-pirate-pack | NC1 | +| Otherworld [Dungeons & Dragons] | otherworld | NC1 | +| Farm Crossing 6 | farm-crossing-6 | NC1 | +| Homestead - A Cozy Survival Experience | homestead | NC1 | +| Stoneblock 4 | stoneblock-4 | TX1 | +| Society: Sunlit Valley | society-sunlit-valley | TX1 | +| Submerged 2 | submerged-2 | TX1 | +| Beyond Depth | beyond-depth | TX1 | +| Beyond Ascension | beyond-ascension | TX1 | +| Cottage Witch | cottage-witch | TX1 | +| All The Mons (Private) - TX | all-the-mons-private | TX1 | +| Wold's Vaults | wolds-vaults | TX1 | + +Write a migration script that pre-populates `server_config` with these AND sets `short_name_locked = true` for all of them — they're already live in Discord, locking is appropriate. + +Farm Crossing 6 is missing its `-status` channel — note this in the card UI so Michael can run createserver for just that channel (or we handle partial channel creation). + +--- + +## Part 8: Files to Create/Modify + +| File | Action | +|------|--------| +| `src/services/uptimeKuma.js` | **NEW** | +| `src/services/pterodactyl.js` | **NEW** | +| `src/routes/admin/servers.js` | **MODIFY** (major) | +| `src/views/admin/servers/_server_card.ejs` | **REBUILD** | +| `src/views/admin/servers/_matrix_body.ejs` | **REFACTOR** | +| `package.json` | **MODIFY** (add uptime-kuma-api) | +| `migrations/add-server-config.sql` | **NEW** | +| `migrations/seed-server-config.sql` | **NEW** | + +--- + +## Part 9: What NOT to Build Yet + +These are on the roadmap but NOT in this request: + +- **Restart scheduler UI** — Task #94, separate request after this lands +- **Console command input on card** — Phase 2 +- **RAM/CPU/player count display** — Phase 2 +- **Partial channel creation** (create only missing channels) — Phase 2 + +Build the foundation clean. Phase 2 adds to it. + +--- + +## Deployment Notes + +- Run `npm install` after adding `uptime-kuma-api` +- Run migration SQL before deploying new code +- Pre-populate `server_config` with seed data before first load +- Test `createserver` on a non-live server first (use All of Create Creative as test bed — it has no subscribers) +- Standard deploy pattern: clone to `/tmp`, rsync to `/opt/arbiter-3.0`, restart `arbiter-3` + +--- + +## Questions for Code Before Starting + +1. Does `uptime-kuma-api` npm package support Uptime Kuma 2.1.0? Verify before using. +2. What's the correct Discord category structure — should all 5 channels live under one `🎮 {name}` category, or does the voice channel go in a separate voice category? +3. Farm Crossing 6 is missing only the `-status` channel — should `createserver` handle partial creation (only create missing channels) or always create all 5? + +--- + +*Filed by Chronicler #87 — April 14, 2026* +*This is the foundation of the Server Command Center. Build it clean.*