Commit Graph
4 Commits
Author SHA1 Message Date
KessinenandEsa Kataja d8810d288e release: repair the catalog 404 and stop the page jumping (#3)
Showstopper. Adding, editing or deleting a dish redirected to /ruoat, which stopped existing when the tab was renamed to Ruuat — every one of those actions ended on a 404. Live in v20260905-4. The tests missed it because they asserted only a 303; a redirect to a dead URL is still a 303. They now assert the target.

The page no longer jumps. Deleting a dish partway down the catalog, or opening a day in Kirjaa, sent the browser to the top. Both now patch in place via Datastar — bin, pencil, day rows, dish pills, save, delete, cancel and Näytä lisää. Links stay links and forms stay forms, so it works without JavaScript.

Non-production tabs are labelled. ENV=dev gives dev · Foodster.

Contributing guide added, and commits now take Conventional Commit types.

⚠️ Breaking: rewrite the server's .env in this deploy. Environment variables lost the FOODSTER_ prefix; the app refuses to start on an unset PASSWORD.

REPO=…  TAG=latest  PASSWORD=…  HOST=foodster.kessinen.com
ENV=prod  PUID=1000  PGID=1000  TZ=Europe/Helsinki

PUID/PGID rather than UID/GID — UID is read-only in bash and would be silently overwritten.

Co-authored-by: Esa Kataja <[email protected]>
Reviewed-on: #3
2026-09-05 21:03:04 +00:00
Esa Kataja eb95bd0a03 Serve behind Traefik and rate limit password guesses
The deployment is a public hostname behind Traefik rather than a LAN-only
box, which changes two things.

TLS is now terminated by the proxy, so Basic credentials are no longer in
cleartext. The container publishes no ports: doing so would leave an
unencrypted copy of the app on the host, bypassing the proxy. The hostname
lives in .env rather than compose.yaml, so no infrastructure detail is
committed and the MIT publication option stays open.

The password is now the only thing between the internet and the app, and a
500 ms sleep is not a defence at that exposure. Wrong guesses are rate
limited per client address: five in a burst, then one per ten seconds,
answered with 429.

Two details that decide whether this works at all:

- a request with no Authorization header is not charged. That is the
  handshake every browser session opens with, and counting it would lock the
  household out for simply opening the app a few times.
- X-Forwarded-For is believed only when the connection arrived from a private
  address, i.e. through the proxy, and then only its last entry, which is the
  one the proxy observed. A direct client could otherwise forge a fresh
  address per attempt and walk past the limiter entirely.

None of this substitutes for a strong password. It removes brute force as a
practical route, nothing more. PRD §3, §9 and §10 are updated: "no external
internet exposure" is no longer true.
2026-09-05 20:12:28 +03:00
Esa Kataja c6f44bb427 Keep the database in ./data and bind-mount it
SQLite writes three files — the database plus its -wal and -shm companions.
Putting them in one directory means a deployment mounts a single path and a
backup copies a single directory.

- FOODSTER_DB defaults to ./data/foodster.db, and openDB creates the parent
  directory on startup rather than failing on a fresh checkout
- compose bind-mounts ./data instead of using a named volume, so the file can
  be listed, copied and opened with any sqlite client without going through
  the container engine
- the image runs as UID 65534, which cannot write to a host directory owned
  by someone else, so compose now sets user: from FOODSTER_UID/FOODSTER_GID
- `make up` creates ./data first: left to the engine it appears root-owned
  and the app silently cannot write to it
2026-09-05 18:17:14 +03:00
Esa Kataja 6ed047aafd Initial commit: PRD, build tooling, and design mockups
Foodster is a self-hosted dinner log and meal suggester for one household.
Stage 1 (eating history) is in development; stage 2 (the suggester) follows
once there is enough history to weight against.

Replace the PRD's original stack (Nuxt, Postgres, Drizzle, Pico CSS) with
Go 1.27, net/http, templ, Datastar and SQLite — one static binary, no
Node.js in the build. Sections 3, 4, 5, 9, 10 and 11 are rewritten to match.

Record the decisions made while reviewing mockups:

- the UI is written in Finnish; PRD, code and comments stay English
- access is one shared password over HTTP Basic rather than open on the LAN
- images are CalVer vYYYYMMDD-N, built with Podman, run under Docker Compose
- registry coordinates live in .env, so no infrastructure detail is
  committed and the MIT publication option stays open

mockups/ holds standalone HTML design studies. log-fi.html is the current
one; the others are superseded exploration kept for reference.
2026-09-05 17:35:37 +03:00