# wutzcalc 🍺 wutzcalc logo > ## ⚠️ AI-generated code > > **Every file in this repository was written by an AI coding assistant.** It > has not been independently audited or reviewed line-by-line by a human. Before > running this in any setting where correctness matters (real money, real > events), read the code yourself, test the edge cases that matter to you, and > assume there are bugs. Use at your own risk. > ## 🔓 No security — trusted networks only > > **This app has essentially no protection. Assume anyone who can reach it can > read and write everything.** > > - 🚫 The tablet UI (`/`, `/api`) has **no authentication** — anyone on the > network can record sales, return Pfand, or reset a cart. > - 🔑 The backoffice (`/admin`) is gated by a **single shared password** in > plaintext (`ADMIN_PASSWORD`), sent and stored as-is — no per-user accounts, > no rate limiting, no audit log. > - 🌐 There is **no HTTPS** built in — traffic (including the admin password) is > plaintext unless you put it behind your own TLS-terminating reverse proxy. > - 🧱 No CSRF tokens (only a `SameSite=Lax` cookie) and no input hardening > beyond basic request validation. > > Run it only on a **trusted, isolated LAN** (e.g. the bar's own Wi-Fi/VLAN), > never exposed to the public internet. See [TODO.md](TODO.md) for the auth and > hardening work deferred from v1. 🎪 Festival drink-sale tracker. See [PLAN.md](PLAN.md) for architecture, [NOTES.md](NOTES.md) for the original requirements, and [TODO.md](TODO.md) for deferred work. 🔗 Source: ## 🔧 Toolchain setup Requires Node.js 20+ and pnpm 9+. Native SQLite bindings install from prebuilt binaries on x86_64 / arm64 — no compiler needed for most setups. ### ❄️ Nix (Linux / macOS) ```sh nix develop # node 20, pnpm, sqlite, build deps ``` ### 🐧 Debian / Ubuntu ```sh # Node 20 from NodeSource curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs sudo corepack enable # provides pnpm # Only needed if a prebuilt better-sqlite3 binary is unavailable for your arch sudo apt install -y build-essential python3 ``` ### 🎩 Fedora ```sh # Node 20 + pnpm (corepack ships with the nodejs package) sudo dnf install -y nodejs sudo corepack enable # provides pnpm # Only needed if a prebuilt better-sqlite3 binary is unavailable for your arch sudo dnf install -y gcc-c++ make python3 ``` ### 🪟 Windows Install Node.js 20 LTS via the official MSI from (this also installs the optional "Tools for Native Modules"). Then in PowerShell: ```powershell corepack enable # provides pnpm ``` Use PowerShell to set env vars on the same line, e.g. `$env:ADMIN_PASSWORD="changeme"; pnpm dev:server`. ## 🛠️ Dev ```sh pnpm install ADMIN_PASSWORD=changeme pnpm dev:server # http://localhost:3000 pnpm dev:client # http://localhost:5173 (proxies /api, /admin) ``` Open `http://localhost:5173/` for the tablet UI and `http://localhost:5173/admin.html` for the backoffice. Running `dev:server` on a non-default `PORT`? Point `dev:client`'s proxy at it with `WUTZ_SERVER_PORT` (same value as `PORT`): ```sh PORT=4000 ADMIN_PASSWORD=changeme pnpm dev:server WUTZ_SERVER_PORT=4000 pnpm dev:client ``` ## 📦 Production build ```sh pnpm install pnpm build ADMIN_PASSWORD=... DB_PATH=/var/lib/wutzcalc/wutz.db node server/dist/index.js ``` Single Node process serves the API, both client entries (`/` tablet, `/admin` backoffice), and writes to one SQLite file. ## 🚀 Run as a systemd service On Fedora, the `Makefile` automates everything below — from a fresh checkout, as root: ```sh sudo make install # system deps + build + service sudoedit /etc/wutzcalc/wutzcalc.env # set ADMIN_PASSWORD sudo systemctl enable --now wutzcalc ``` Override paths with e.g. `make install PREFIX=/srv/wutzcalc SERVICE_USER=wutz`. The manual steps below do the same thing. Template files live in [`deploy/`](deploy/): a unit ([`wutzcalc.service`](deploy/wutzcalc.service)) and an environment file ([`wutzcalc.env.example`](deploy/wutzcalc.env.example)). They assume the built app lives in `/opt/wutzcalc` and the database in `/var/lib/wutzcalc` — adjust paths in the unit if yours differ. ```sh # 1. Dedicated system user (no login, no home) sudo useradd --system --no-create-home --shell /usr/sbin/nologin wutzcalc # 2. Install the built app (run `pnpm install && pnpm build` first) sudo mkdir -p /opt/wutzcalc sudo rsync -a --exclude='.git' --exclude='*.db*' ./ /opt/wutzcalc/ sudo chown -R root:root /opt/wutzcalc # app dir stays read-only to the service # 3. Config + secrets (chmod 600 — holds ADMIN_PASSWORD) sudo mkdir -p /etc/wutzcalc sudo install -m 600 deploy/wutzcalc.env.example /etc/wutzcalc/wutzcalc.env sudoedit /etc/wutzcalc/wutzcalc.env # set ADMIN_PASSWORD # 4. Install and start the unit sudo cp deploy/wutzcalc.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now wutzcalc # Logs / status systemctl status wutzcalc journalctl -u wutzcalc -f ``` Confirm `ExecStart` matches your Node path (`command -v node`) — it defaults to `/usr/bin/node`. The unit creates `/var/lib/wutzcalc` via `StateDirectory`, so the service user owns the database directory automatically. ## ⚙️ Env vars - `PORT` (default `3000`) - `HOST` (default `0.0.0.0`) - `DB_PATH` (default `./wutz.db`) - `ADMIN_PASSWORD` (**required** for backoffice login) - `WUTZ_TZ` (default `Europe/Berlin`) — timezone for stats display and grouping - `WUTZ_DAY_CUTOFF_HOUR` (default `5`) — sales before this local hour count toward the previous business day - `WUTZ_TRUST_PROXY` (default off, set to `1` to enable) — trust `X-Forwarded-*` headers for `req.ip`. Only turn this on if you're actually running behind a reverse proxy (see the systemd/deploy section) — otherwise a client can spoof its own logged IP. - `WUTZ_SERVER_PORT` (default `3000`, **client dev only** — not read by the server) — the port `dev:client`'s Vite proxy targets; set it to match `dev:server`'s `PORT` when running the server on something other than the default