refactor: unify project structure, improve security, and enhance deployment
- Fix WireGuard private key leak in API responses and config updates - Update systemd service to serve from repo root with adjusted sandbox - Add CLI flags, idempotency, and dev mode to install.sh - Extract common utilities to lib/common.py and webui/api/common.py - Migrate frontend to htmx for simpler, more maintainable UI - Update docs to reflect current architecture and deployment model - Vendor htmx dependencies per project requirements
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
## What This Is
|
||||
|
||||
SSL proxy / firewall appliance. Python 3 Flask WebUI behind nginx reverse proxy.
|
||||
Deploys on Debian 13 (trixie). Install dir: `/opt/vacuum-wall`.
|
||||
Deploys on Debian 13 (trixie). Serves from repo root by default.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -14,16 +14,26 @@ Flask ──→ lib/*.py ──→ sudo <cmd> ──→ system service
|
||||
|
||||
- `webui/server.py` — Flask app entry point. **Only** file that creates the `app`.
|
||||
- `webui/api/*.py` — Flask blueprints, one per subsystem. Routes prefix `/api/<subsystem>/`.
|
||||
- `lib/*.py` — Backend modules. Wrap system commands via `subprocess.run(["sudo", ...])`.
|
||||
- `webui/api/common.py` — Shared `_ok()` / `_error()` response helpers used by all blueprints.
|
||||
- `lib/common.py` — Shared utilities: `run()`, `run_proc()`, `load_json()`, `save_json()`, `deep_merge()`, `ensure_dirs()`. All `lib/` modules use these instead of defining local helpers.
|
||||
- `lib/*.py` — Backend modules. All have full type hints and `__all__` exports.
|
||||
- `data/` — Runtime artifacts (generated .confs, `.htpasswd`, ACME certs, firewall backup, dnsmasq fragments).
|
||||
- `config/<subsystem>/config.json` — Declarative JSON configs (source of truth). Generated `.conf` in `data/nginx/sites-enabled/`. Certs in `data/acme/`.
|
||||
- `system/` — System file templates. `systemd/` (service units installed to `/etc/systemd/system/`), `sudoers.d/`, `nginx/`.
|
||||
- `webui/static/` — Vendored frontend libraries (JS + CSS). Flask auto-serves at `/static/`.
|
||||
|
||||
Project uses `.venv`. Install deps with `pip install -e .` (from `pyproject.toml`). `__init__.py` files in `webui/` and `lib/` are intentionally empty — no `sys.path` boilerplate needed.
|
||||
|
||||
**No CDN packages.** All frontend libraries (JS and CSS) must be vendored in `webui/static/`. Never reference `unpkg.com`, `cdn.jsdelivr.net`, or similar. To add/update a library, edit the version in `scripts/update-vendor.sh` and run it.
|
||||
|
||||
| Library | Version | Local file | CDN source |
|
||||
| ------- | ------- | ----------------------------------- | ---------- |
|
||||
| htmx | 2.0.4 | `webui/static/htmx.min.js` | `npm:htmx.org@2.0.4` |
|
||||
| htmx-ext-json-enc | 2.0.0 | `webui/static/json-enc.js` | `npm:htmx-ext-json-enc@2.0.0` |
|
||||
|
||||
## Deployment
|
||||
|
||||
`install.sh` deploys to `/opt/vacuum-wall` by rsyncing the repo. The `vacuum-wall` system user has `HOME=/opt/vacuum-wall` but no actual home directory (`--no-create-home`).
|
||||
`install.sh` installs only system components and configures them; the project serves from the repo root by default. All options can be set via env vars or CLI flags (CLI takes precedence). Set `INSTALL_DIR` or `--path` to override install directory. Use `--dev` to auto-detect repo owner as service user. The `vacuum-wall` system user has `HOME=$INSTALL_DIR` but no actual home directory (`--no-create-home`).
|
||||
|
||||
All Python modules use `Path(__file__).resolve().parent.parent` for `PROJECT_DIR` — no hardcoded paths. ACME certs live at `PROJECT_DIR/data/acme/`.
|
||||
|
||||
@@ -56,8 +66,9 @@ Adding a new privileged command requires a sudoers entry **and** the `lib/` code
|
||||
|
||||
## API Response Contract
|
||||
|
||||
- Success: `{"ok": true, "data": <value>}` — helper `_ok(data)`
|
||||
- Error: `{"ok": false, "error": "msg"}` — helper `_error(msg, code=400)`
|
||||
- Success: `{"ok": true, "data": <value>}` — helper `_ok(data)` from `webui.api.common`
|
||||
- Error: `{"ok": false, "error": "msg"}` — helper `_error(msg, code=400)` from `webui.api.common`
|
||||
- `acme.issue()` / `acme.renew()` raise `RuntimeError` on failure — API layer wraps in try/except
|
||||
- HTTP codes: `400` bad request, `404` not found, `500` internal failure
|
||||
- Full spec: `docs/api.md`
|
||||
|
||||
@@ -78,11 +89,17 @@ Adding a new privileged command requires a sudoers entry **and** the `lib/` code
|
||||
```bash
|
||||
.venv/bin/ruff check lib/ webui/ tests/ # lint
|
||||
.venv/bin/ruff format lib/ webui/ tests/ # format
|
||||
.venv/bin/python -m pytest tests/ -v # test (154 tests)
|
||||
.venv/bin/python -m pytest tests/ -v # test (192 tests)
|
||||
```
|
||||
|
||||
Install dev tooling with `pip install -e ".[dev]"`.
|
||||
|
||||
## Docs
|
||||
|
||||
`docs/` contains the authoritative reference. `docs/architecture.md` covers request flow, zone model, and data directory layout in detail.
|
||||
`docs/` contains the authoritative reference. `docs/architecture.md` covers request flow, zone model, data directory layout, and shared utility patterns in detail.
|
||||
|
||||
## Important Rules
|
||||
1. Ask, don't assume. If something is unclear, ask before writing a single line. Never make silent assumptions about intent, architecture, or requirements.
|
||||
2. Simplest solution first. Always implement the simplest thing that could work. Do not add abstractions or flexibility that weren't explicitly requested.
|
||||
3. Don't touch unrelated code. If a file or function is not directly part of the current task, do not modify it, even if you think it could be improved.
|
||||
4. Flag uncertainty explicitly. If you are not confident about an approach or technical detail, say so before proceeding. Confidence without certainty causes more damage than admitting a gap.
|
||||
|
||||
Reference in New Issue
Block a user