Files
electric-cherry/README.md
T

144 lines
5.6 KiB
Markdown

# electric-cherry
Web app that manages a fleet of T5 Plutonium servers running under Komodo.
Successor to `push_scripts.sh` — same job (push shared GSC scripts, run beta
tests, restart empty servers, track deployments) with a live UI and real
audit diffs.
## Stack
- SvelteKit (adapter-node) + TypeScript
- SQLite via better-sqlite3 (replaces `script_metadata.json` as the source of
truth)
- Server-sent events for live pipeline / server-status streams
- RCON (UDP) direct-to-game-server for player counts
- Komodo API for restarts and per-server game-port discovery
- Runs as a single Komodo-managed container on the same host as the game
servers, with `/root/zombies` bind-mounted in
## Layout
- `src/lib/server/db/` — schema, migrations, better-sqlite3 handle
- `src/lib/server/fleet/` — file walking, push/beta engines
- `src/lib/server/integrations/` — Komodo, RCON
- `src/lib/server/pipeline/` — sweep loop, event bus, worker singleton
- `src/routes/api/` — REST + SSE endpoints
- `src/routes/` — UI pages (dashboard, deployments, betas, per-server)
- `Dockerfile`, `docker-compose.yml`, `entrypoint.sh` — container build
- `komodo/` — Komodo stack definition
- `scripts/` — legacy JSON importer, fixture seeder
- `tests/` — vitest unit tests
## How restarts stay safe
Every push (or beta open) creates a `deployment` row and marks each affected
server `pending`. A background sweep runs every 15s and:
1. Asks each server over RCON: how many players are on right now?
2. If 0 → tell Komodo to restart the server → row goes `done`.
3. If >0 → hold the row `pending` with the player count; try again next tick.
4. If RCON unreachable → hold `pending` with `error='rcon unreachable'`.
No token, no session, no auth freeze. If Komodo or RCON aren't configured,
the sweep marks pending rows `manual` so nothing hangs forever.
## Local dev
Windows or macOS — no `/root/zombies` on disk, so seed a fixture tree first.
```bash
npm install
npm run seed:fixture
# Point env at the fixture (PowerShell)
$env:SHARED_SCRIPTS = "$PWD/test-fixtures/zombies/shared/scripts"
$env:SERVERS_ROOT = "$PWD/test-fixtures/zombies/t5"
$env:DB_PATH = "$PWD/data/cherry.db"
npm run dev
# open http://localhost:3000
```
Without RCON_PASSWORD / KOMODO_* set, the pipeline marks pending rows
`manual` after a sweep — the UI still works, you just don't get real
restarts. Enough to develop the push/beta flow.
## Map selection and configs
The map selected when deploying a T5 zombies server controls its startup
rotation automatically. No map-choice config overlay is required. New servers
use the promoted game baseline and matching overlays when available; otherwise
Plutainer seeds its usual defaults and Electric Cherry applies the selected map
through a generated `configs/electric_cherry_map.cfg`, loaded after the main
config and before map rotation. This file is tracked and restored by drift
reconciliation; operators do not need to create or promote it. The launch order
uses Plutainer's [extra arguments and map-rotation controls](https://github.com/Ayymoss/Plutainer/blob/main/docs/configuration.md).
Plutainer's
default main config is preserved until a game baseline is promoted.
Beta previews, promotion, and config drift repair all apply the server's stored
catalog map after composing the baseline and overlays. An old `sv_maprotation`
overlay cannot override that selection, but its other settings still apply.
Existing servers receive this behavior on their next config deployment or drift
reconciliation; this change does not immediately rewrite or restart them.
Servers with an unknown map retain their existing config behavior. Rollback
restores the exact prior config bytes.
## Tests
```bash
npm test
```
Covers push (add/update/skip), beta lifecycle (open → rollback, open →
promote, conflict blocks), sweep (integrations-off manual branch), and
RCON parsing (fake UDP socket).
## Deploy to the Komodo host
1. Build the image once locally or on the host:
```bash
docker compose build
```
2. Import `komodo/stack.toml` into Komodo (or point Komodo at the
`docker-compose.yml` at repo root).
3. Set the env vars in Komodo:
```
KOMODO_ADDRESS, KOMODO_API_KEY, KOMODO_API_SECRET
RCON_PASSWORD # single shared password across every T5 server
RCON_HOST # optional; defaults to host.docker.internal
```
4. First boot triggers the legacy import: if
`/root/zombies/script_metadata.json` exists and `/data/cherry.db` is
empty, `entrypoint.sh` runs `scripts/import-legacy.ts` before starting
the server. The source JSON gets renamed to `.migrated`.
5. Load the app on the LAN or over tailscale (no web-app auth by design).
## What still lives in the shell script
Nothing that's needed at runtime. Move `push_scripts.sh` to `archive/` once
you've run a side-by-side parity check between the shell's dry-run and the
app's `POST /api/push { dryRun: true }`.
## Non-goals
- **Editing GSC files in-browser.** Do that in your editor of choice; the
web app only cares about what's on disk in `SHARED_SCRIPTS`.
- **Multi-user auth.** LAN/tailscale scoping is the security model.
- **Two-way sync with `script_metadata.json`.** The importer runs once. If
the shell script is ever used again after that, it'll write to the JSON
file and the web app won't see it.
## References into the old script
Every port carries a `// Ports push_scripts.sh:NN` comment pointing back to
the original for the parts that had non-obvious behavior. Grep those if a
behavior seems wrong — the shell script is the reference implementation
until the parity check passes.