A self-contained copy of the UI that answers its own API calls from fixtures, for hosting as static files at joulenap.com/demo: real clock, fixtures slid onto today's calendar, an orange banner making clear the data is fake, and a scripted backup cycle (demoTimeline.ts) replayed live when the visitor presses Run backup now. Only --mode demo ships the stub; the default build that CI and the Docker image run drops it entirely. Brand asset paths now honour BASE_URL so the demo can live under /demo/.
4.0 KiB
Contributing to Joulenap
Thanks for your interest in improving Joulenap! This guide covers the local dev setup, how to run the checks CI runs, and the conventions we follow.
By contributing you agree that your contributions are licensed under the project's AGPL-3.0.
Project layout
backend/— Python 3.12 + FastAPI + APScheduler. The app package isbackend/app.frontend/— React + TypeScript SPA (Vite). Built output is served by the backend.docs/— design and setup docs. Start withARCHITECTURE.mdandCONFIG-WIZARD.md.
Dev setup
You need Python 3.12+ and Node 20+.
Backend
cd backend
python -m venv .venv
# Windows: .venv\Scripts\activate | macOS/Linux: source .venv/bin/activate
pip install -e ".[dev]"
ruff check . # lint
pytest # tests
Frontend
cd frontend
npm ci
npm run dev # Vite dev server (proxies /api to the backend on :8080)
npm run build # type-check (tsc --noEmit) + production build
For a running app, copy the config first and start the backend:
cp config.example.yaml config.yaml
cd backend && python -m app.main # serves the API (and the built SPA if you ran `npm run build`)
The example config ships unconfigured, so the app drops you into the first-run registration and setup wizard — no real Proxmox needed to click around the UI.
Frontend without a backend
To work on the UI alone — layout, styling, i18n — you can run the SPA against a built-in stub instead of a real backend and a real Proxmox:
cd frontend
npm run dev -- --mode stub
frontend/src/devStub.ts answers every /api/* request from fixtures (a configured install,
three guests, a few log lines, and enough wizard responses to click through the whole setup
flow) and pins the clock, so the UI renders exactly the same on every run — handy for
screenshots and layout comparisons. It is loaded only when VITE_STUB_API=1, which
frontend/.env.stub sets for the stub mode, and Vite eliminates it from production builds.
Add --host 0.0.0.0 to reach the dev server from a phone on the same network.
The public demo
npm run build:demo bundles that same stub into frontend/dist-demo/, which is what the
demo at joulenap.com/demo serves: real clock, fixtures slid onto today's calendar, a banner
saying the data is fake, and a scripted backup cycle (frontend/src/demoTimeline.ts) that
plays out when you press Run backup now. --mode demo is the only build that ships the
stub; npm run build — what CI and the Docker image run — never does.
Before you open a PR
Run what CI runs (see .github/workflows/ci.yml); all of it must pass:
- Backend:
ruff check .andpytest(CI runs the suite on Python 3.12 and 3.13). - Frontend:
npm run build(this type-checks withtsc --noEmit).
CI also runs dependency (pip-audit / npm audit) and Docker image (Trivy) security scans.
Conventions
- Small, reviewable commits; work on a feature branch and open a PR against
main. - Tests for the connectors and backup-cycle logic — that's where correctness matters most.
- Keep it config-driven — nothing hard-coded (no specific IPs/MACs in code); validate config with pydantic and fail clearly.
- Secrets never get committed.
config.yamlis git-ignored; don't add real hosts/tokens/MACs to code, tests, or docs — use placeholder/192.0.2.x(TEST-NET) values. - i18n: user-facing UI strings go through
t('key')with entries in bothfrontend/src/i18n/en.jsonandfrontend/src/i18n/it.json(English is the source language). Backend-facing strings (errors, notifications) use the server-side dictionary. Never concatenate translated strings — use interpolation.
Reporting bugs and requesting features
Use the issue templates (bug report / feature request). For security vulnerabilities, do not
open a public issue — follow SECURITY.md instead.