With COOKIE_SECURE=true behind a TLS-terminating reverse proxy, Express
only ever sees the proxy's plain-HTTP connection to the container, so
req.secure was always false from its perspective. express-session
silently refused to set a `secure` cookie on a connection it believed was
insecure -- login would succeed (200, parent data returned) but no
Set-Cookie ever reached the browser, so the very next request came back
401 and bounced to the login screen. Looked like "logs in, then
immediately signs back out" -- reproduced on both Mac Chrome and iPhone
once COOKIE_SECURE was turned on for push notifications.
app.set('trust proxy', 1) when COOKIE_SECURE is true makes Express read
X-Forwarded-Proto from the proxy, so it correctly sees the connection as
secure. Tied to COOKIE_SECURE rather than a new flag since it's the same
"yes, I'm behind an HTTPS-terminating proxy" fact either way. Verified: no
Set-Cookie without the header (correct -- a request that didn't actually
come through the proxy shouldn't get one), Set-Cookie with Secure/SameSite
correctly present given X-Forwarded-Proto: https, and a full
login-then-auth-check cycle staying authenticated.
setVapidDetails() throws synchronously on an invalid subject (e.g. missing
the mailto:/https: prefix — an easy mistake). It ran unguarded at module
load, so a bad value took down the entire server, not just push. Wrapped
in try/catch; push now degrades the same way as when it's unconfigured,
and /api/push/vapid-public-key reflects actual readiness (post-validation)
instead of just whether the env vars were present.
Parents get a real push notification when a kid checks off a task
(false->true transitions only, fire-and-forget, degrades gracefully with
no VAPID keys configured). Dashboard is a fully installable iOS/Android
PWA; each child's kiosk link gets its own dynamic per-token manifest so
"Add to Home Screen" opens straight into their board in standalone mode.
Kiosk view is reworked for tablets: safe-area-aware full-bleed layout,
the whole task row is now tappable (previously only the 24px checkbox
was, well under Apple's touch-target minimum), and app icons are
generated by a small dependency-free PNG encoder (no image tooling
available in this environment).
Push requires real HTTPS (iOS Safari won't allow it otherwise) - README
and UNRAID.md cover VAPID setup and the HTTPS prerequisite.
Visiting the bare root path returned Express's default "Cannot GET /"
since there was no route or index.html for it. dashboard.js already
bounces to /login.html when the session check fails, so this one
redirect covers both logged-in and logged-out visitors.
docker-compose.unraid.yml now uses a Git URL as the build context
(https://git.oservr.com/ort/KCal.git#main) instead of requiring a manual
clone onto the array — Docker/BuildKit fetches the source itself at build
time. Requires the repo to allow anonymous HTTP read access in Gitea
(Settings -> Visibility -> Public); this is a read-only clone, nothing
more. Verified with a real docker build/compose run against the public
repo, not just syntax-checked.
UNRAID.md rewritten to match: no deploy key, no NerdTools/git, no source
checkout needed. Kept a token-auth alternative documented for anyone who'd
rather keep the repo private.
docker-compose.unraid.yml bind-mounts data to /mnt/user/appdata/kids-calendar
instead of a named volume, matching Unraid's appdata convention so backup
plugins pick it up. UNRAID.md covers getting the code onto the array,
running via Compose Manager or terminal, reverse-proxy/HTTPS notes, and
updating. Linked from README.
Project overview, current status, architecture summary, and the
commit/push workflow for the Gitea remote (deploy key location,
GIT_SSH_COMMAND usage).
Express + SQLite (node:sqlite, no native build step) family calendar:
parent accounts with household invites, per-child calendars with
save/duplicate/print, a token-gated read-only kiosk view for tablets,
and polling to keep parent and kiosk views in sync. Defaults to port 3007.