Routes 8 call sites through the cm6 controller alias: - 5 reads (byte-count, save POST, dirty checks at 306, 481, 496, 511, 528) → getEditorValue() helper, falling back to editorEls.contentBox.value if window.__filesEditor isn't mounted (no-JS / pre-mount path). - 3 writes (clear, "Loading…" placeholder, fetched body content at 362, 395, 402) → setEditorValue() helper with the same fallback. The two helpers live inline next to editorEls so the rest of the module's call sites stay close to existing style. Known regressions (out of scope for v2, candidate follow-ups): - Byte-count badge updates only on file-open / setContent calls, not live on every keystroke. Needs a controller.onChange(cb) hook. - Ctrl+S inside cm6 doesn't trigger the modal Save. cm6 owns the keymap in its editing surface; users can still click the Save button. Adding a custom cm6 keymap entry would restore the shortcut. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| alembic | ||
| l4d2web | ||
| scripts | ||
| tests | ||
| alembic.ini | ||
| pyproject.toml | ||
| README.md | ||
l4d2-web-app
Flask web app for managing L4D2 servers through user-private blueprints.
Key v1 behaviors
- Local username/password login; no public signup
- Admin-managed overlay catalog
- Private blueprints per user
- Server creation from blueprints (live-linked; no per-server blueprint overrides)
- Async job model with persisted command logs in
job_logs - Desired vs actual state model
- Live logs for jobs and servers via SSE endpoints
- Host operations go through
l4d2ctlvia a local host command runner, not directl4d2hostimports
Frontend constraints
- Server-rendered templates (Jinja)
- Vendored HTMX (
static/vendor/htmx.min.js) - Custom CSS only
- Tokenized, consistent link and accent colors
Development
From the workspace root (../):
uv sync # creates .venv, installs l4d2host + l4d2web editable, plus dev deps
uv run pytest l4d2web/tests -q
Configuration
The web app reads these settings from the environment:
DATABASE_URL: SQLAlchemy database URL, for examplesqlite:////var/lib/left4me/left4me.db.SECRET_KEY: Flask secret key used for sessions and CSRF-sensitive state.JOB_WORKER_THREADS: number of background job worker threads.
In the systemd deployment, environment is loaded from /etc/left4me/host.env and /etc/left4me/web.env.
Admin Bootstrap
Create the first admin account with the Flask CLI. Provide the password through LEFT4ME_ADMIN_PASSWORD:
LEFT4ME_ADMIN_PASSWORD='change-me' flask create-user <username> --admin