Skip to content

v1.2.0 — Onboarding completeness + setup/self-host hardening

Latest

Choose a tag to compare

@svenger87 svenger87 released this 01 Jun 09:30

Added

  • Plugin widgets that have nothing to show no longer render as a blank cell. When the Vehicles, Stonks, or Pocket Money widget is enabled in /settings/widgets but the plugin itself is disabled — or enabled with no data yet — the dashboard now shows a short "discover" card explaining what the widget does and linking straight to /settings/plugins (to enable it) or the feature page (to add the first vehicle / ticker / account). Dismissible per-device so kiosks stay clean. Previously the widget silently returned nothing, so a user who'd turned it on just saw an empty gap.
  • /settings/notifications now surfaces a "push server not configured" hint when the server has no VAPID keys (e.g. Node.js wasn't installed when setup.sh ran). Independent of browser support, so it explains the real reason push is dead on a fresh install and links to the setup guide.
  • The weather widget now shows a "set up weather" call-to-action linking to /settings/weather when OPENWEATHERMAP_API_KEY is unset, instead of a cryptic "No data" error.
  • The calendar page now shows an "add a calendar" banner linking to /settings/calendar when no calendars exist yet, instead of leaving the grid empty with no guidance.
  • The first-run setup wizard now has a Calendar step (between People and Home Assistant): connect Google for two-way sync, paste an iCal (.ics) feed with a built-in "test feed" check, or skip. A new family no longer finishes onboarding with an empty calendar and no nudge. Setup state tracks has_calendar so the step and checklist know when it's done.
  • The dashboard's one-shot "Finish setting up" banner is replaced by a live, collapsible getting-started checklist — family members, a calendar, weather location, Home Assistant — that ticks items off as you configure them (in the wizard or directly in settings) and stays re-openable (collapse, not dismiss-forever) until you complete setup.
  • The shopping list shows a "Connect Bring!" button in the header when Bring isn't linked, so the optional sync is discoverable without hunting through settings (the badge still shows when connected).
  • /settings/google shows a "Reconnect" banner when Google rejects the saved authorization (invalid_grant — revoked or expired) during a sync. Previously sync just died silently; now the cron/manual sync flags needs_reauth and the banner prompts a one-click reconnect (cleared automatically on the next successful sync).
  • /settings/homeassistant shows the same "Reconnect" treatment when Home Assistant rejects the saved access token (a 401 from a live connection probe): a banner explains it and the connection form is revealed with the URL prefilled, so you can paste a fresh long-lived token without disconnecting first. The status badge no longer misleadingly shows "Connected" while the token is dead.

Changed

  • setup.sh no longer silently falls back to http://localhost:8100 when run without an interactive terminal (e.g. piped over SSH) — that default broke access from every other device with no error. It now accepts --url <URL> (or the KINBOARD_URL env var) for non-interactive runs, and errors out with the fix when neither is given. --non-interactive still accepts the localhost default but now prints a clear warning that it's local-only.
  • setup.sh's end-of-run summary now flags when push notifications are off because no VAPID keys were generated, with the one-line fix (install Node.js, re-run ./setup.sh --force), so it doesn't get lost in the scrollback.
  • Pocket Money manual deposit + withdrawal on /settings/pocket-money (parent) and the kid-facing spend request on /pocket-money now use a proper shadcn Dialog modal instead of window.prompt(). The kid-side flow previously had to fire two prompts back-to-back (amount, then reason) which was hostile UX on a touchscreen kiosk; now amount + reason live in one modal with an Enter-to-submit shortcut and an autofocused amount input. New shared components/pocket-money/amount-dialog.tsx powers all three call-sites.
  • /pocket-money (kid view) now shows a pending-approval banner above the goal area when the kid has one or more outstanding withdrawal requests waiting on parent sign-off. Single-request form shows the amount; multi-request form shows the count. Reuses the existing useWithdrawalRequests(accountId, "pending") query so it stays live as parents approve/deny without a page refresh.

Fixed

  • On a plain-HTTP LAN deployment, the first-run setup wizard's People step crashed with crypto.randomUUID is not a function, blocking onboarding entirely — that browser API only exists in a secure context (HTTPS, or http://localhost). Several other spots (offline shopping queue, Home Assistant rooms/dashboards, camera ids) had the same latent crash. Added a safeRandomUUID() helper that uses the native API when available and falls back to crypto.getRandomValues (which is available in insecure contexts), so the app works over plain HTTP on the LAN. Affected every HTTP-only self-host; HTTPS / localhost installs were unaffected.
  • The storage container reported unhealthy forever even though it served fine. Its healthcheck hit http://localhost:5000/status, but localhost resolves to IPv6 [::1] in that image while the storage server binds IPv4 (0.0.0.0:5000) — so every probe got connection-refused. Switched the healthcheck to 127.0.0.1. Affected every self-hoster; anything gating on storage's health (monitoring, depends_on: service_healthy) was misled by the false negative.
  • A bare docker compose up (without going through webapp/docker/start.sh) used to leave rest/auth/storage/realtime crash-looping with password authentication failed for user authenticator, because the Supabase service-role password alignment only ran from start.sh. The stack now does this itself: a one-shot db-init service aligns the authenticator / supabase_auth_admin / supabase_storage_admin passwords with POSTGRES_PASSWORD and creates the _realtime schema before those services start (they wait on it via service_completed_successfully). start.sh up no longer needs its own alignment step. Plain docker compose up -d now works out of the box.
  • Pocket Money allowance cron now re-anchors when the parent changes allowance_day_of_week mid-cycle. Previously, if the last pay landed on (say) Sunday and the parent then switched to Friday, the (interval − 1) day dedup window kept rejecting the new schedule's first Friday — the kid effectively had to wait one full cycle past the schedule change before the new day took effect. Cron now compares the last pay's UTC day-of-week to today's: if they differ, the schedule was retuned, so it pays now and lets the new day become the future anchor. Same-DOW reruns still respect the full interval window (no double-pay).
  • Push notifications for calendar events showed times offset by the container's UTC clock — e.g. an event at 08:00 CEST rendered as "Beginnt um 06:00" because the webapp container's TZ defaulted to UTC and the toLocaleTimeString calls in cron/schedule-event-reminders and cron/process-notifications had no explicit timeZone option. Two-pronged fix: a shared lib/notifications/format.ts:formatEventTime helper now passes timeZone: process.env.TZ ?? "Europe/Berlin" explicitly to the formatter (defense-in-depth), and the webapp container in docker-compose.yml now receives TZ=${TZ:-Europe/Berlin} so all server-side date logic uses the right zone. .env.example default changed from TZ=UTC to TZ=Europe/Berlin; self-hosters outside Europe/Berlin set TZ in .env to their household's IANA zone (e.g. America/New_York).

Security

  • Bumped next 16.2.4 → 16.2.6, which clears all 15 Dependabot advisories on the webapp (7 high, 6 moderate, 2 low) — the vulnerable transitive qs / ws came in through Next and resolve to patched versions on the bump. npm audit now reports 0 vulnerabilities for webapp/. Also bumped ws → 8.21.0 in the docs/wiki/screenshots/mocks/ha screenshot-tooling lockfile (1 moderate). Lockfiles regenerated on Linux (npm 10) so npm ci stays reproducible on CI.