12 Commits
Author SHA1 Message Date
ginnoir ec2f3930ad feat: proper logo, favicon, and pwa icons
- replace placeholder house svg with clean geometric house mark
  on indigo-700 (#4338CA) — roof triangle, body, door cutout
- add favicon.svg with prefers-color-scheme dark variant
- add icon-16/32 sizes; regenerate all pngs from svg via sharp
- update BrandMark component to use inline svg house instead of 'f'
- wire favicon + sized pngs into layout metadata
- fix middleware to allow icon/manifest/favicon paths without auth
  (android launcher fetches icons in a cookieless system context)
- fix getCurrentSession() to redirect to /login instead of throwing
  when session cookie is stale/invalid
2026-06-03 15:59:34 -05:00
ginnoir 4ec1f34150 chore: gitignore maintainer deploy scripts, scrub personal domain from dev.mjs 2026-06-02 19:30:27 -05:00
ginnoir cce3bac5b2 chore: repo cleanup and prod sync tooling
- Remove stale deploy/docker-entrypoint.sh (Dockerfile copies root version)
- Remove scripts/seed.ts (superseded by seed.mjs)
- Add src/app/error.tsx and global-error.tsx (untracked error boundaries)
- Gitignore deploy/.prod/ for synced production configs
- Add scripts/sync-prod.ps1 to pull compose/env/Caddyfile from valhalla
- Add scripts/apply-compose.ps1 to push compose changes and restart services
2026-06-02 18:51:59 -05:00
ginnoir 29795e0d51 chore: format pre-existing files missed by lint-staged 2026-06-01 18:46:53 -05:00
ginnoir 19308be768 feat: add dev startup script, fix push notifications, and scope test sends to device
- pnpm dev:local/dev:reset: orchestrate DB container, migrations, seed, and Next.js dev server in one command; Caddy snippet + docs for HTTPS via dev.ginnoir.com
- Fix dev login on HTTPS: set both authjs.session-token and __Secure-authjs.session-token so Auth.js finds the session regardless of cookie name resolution
- Suppress hydration mismatch on <html> caused by pre-paint script changing data-nav before React hydrates
- VAPID startup warning if keys not configured; remove dead NEXT_PUBLIC_VAPID_PUBLIC_KEY var
- PushOptIn: hydrate subscription state on mount; reuse existing subscription on iOS to avoid redundant prompts
- sendPushToEndpoint: new function to send to a single device endpoint
- sendTestNotification: scoped to the calling device's endpoint (ownership-verified) instead of all user subscriptions
2026-06-01 17:45:20 -05:00
ginnoirandClaude Sonnet 4.6 c66bf68373 Seed household on container startup via seed.mjs
Without a household row the signIn callback has nothing to attach the
first user to, causing "No household membership" on first load. Add
scripts/seed.mjs (plain ESM, only needs postgres which is already in the
image) and run it from the entrypoint after migrations. Idempotent — skips
if a household already exists. Default calendars/lists are created by the
signIn callback when the first user authenticates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 20:49:20 -05:00
ginnoir c73338e256 Code-side
src/lib/dev-login-config.ts — startup assertion: throws if NODE_ENV=production + ENABLE_DEV_LOGIN=true, scoped to runtime (skipped during next build).
Container

scripts/migrate.mjs — runs Drizzle migrations against DATABASE_URL.
deploy/docker-entrypoint.sh — runs migrations then exec node server.js. Skip with RUN_MIGRATIONS=false.
Dockerfile — copies drizzle/, scripts/migrate.mjs, entrypoint into runner stage; ENTRYPOINT now points at the script.
Compose

deploy/compose.yaml — famapp now image: ${FAMAPP_IMAGE:-ghcr.io/ginnoir/famapp:latest} (build still works locally as fallback). Authentik pinned via AUTHENTIK_IMAGE_TAG (default 2024.12.3). New RUN_MIGRATIONS env passed through.
.env.production.example — documents FAMAPP_IMAGE, AUTHENTIK_IMAGE_TAG, RUN_MIGRATIONS.
CI/CD

.github/workflows/ci.yml — push/PR: typecheck + lint + format:check + build.
.github/workflows/release.yml — v* tag: build + push ghcr.io/ginnoir/famapp:vX.Y.Z, :X.Y, :latest to GHCR.
Docs

deploy/README.md — full deploy/rollback/release runbook.
CHANGELOG.md — release log seeded with an Unreleased entry.
docs/tasks/09-pre-deploy-checklist.md — task 09 reframed from one-shot removal to a recurring pre-deploy checklist.
STATUS.md — updated.
Verified: pnpm typecheck, pnpm format, pnpm build, and docker compose config all clean.
2026-05-06 17:37:37 -05:00
ginnoirandClaude Sonnet 4.6 b6abe052c9 Implement tasks 40, 41, 42: web push, notification bus, reminders engine
Task 40 — Web Push (VAPID):
- Add web-push package + @types/web-push
- pnpm vapid:generate script prints VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, NEXT_PUBLIC_VAPID_PUBLIC_KEY
- push_subscriptions schema + migration 0013
- _core/push.ts: sendPush() iterates subscriptions, prunes 404/410 stale entries
- SW push/notificationclick event handlers added to generated sw.js template
- PushOptIn client component on /settings (opt-in, disable, send test)

Task 42 — Notification bus + ntfy adapter:
- notifications table + notif_push/notif_inapp/notif_ntfy user columns (migration 0013)
- _core/notify.ts: notify() fans out to push, in-app DB, and optional ntfy POST
- NotificationBell server component in AppNav: unread badge, dropdown inbox, mark-read
- NotifyChannelToggles client component in /settings

Task 41 — Reminders engine:
- fired_at + created_by added to reminders; default channel changed to 'auto'
- _core/reminders.ts: scheduleReminder (upsert), cancelReminder, listReminders, tickReminders
- tickReminders uses pg_try_advisory_xact_lock for horizontal-scale safety
- src/instrumentation.ts starts reminder worker (30s tick) on Node.js boot
- Notes actions use scheduleReminder/cancelReminder instead of raw SQL
- Calendar createEvent: optional remindMinutesBefore, deleteEvent: cancelReminder
- Calendar-shell: "Remind me 30 min before" checkbox on new event form

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 16:55:02 -05:00
ginnoirandClaude Sonnet 4.6 8aab25e0ac Implement PWA shell (task 50)
Add web manifest, placeholder icons, app-shell service worker, and
install prompt so famapp is installable on iOS and Android home screens.

- public/manifest.webmanifest — name, short_name, standalone display,
  start_url /, all four icon sizes (192/384/512 + maskable)
- public/icon-{180,192,384,512,512-maskable}.png — indigo placeholder
  squares generated by scripts/generate-icons.mjs (pnpm gen:icons)
- public/icon.svg — house-icon SVG source for future branded export
- public/sw.js — hand-rolled app-shell SW: precaches offline.html,
  cache-first for /_next/static/, network-first navigation with offline
  fallback, network-only for /api/*
- public/offline.html — branded offline fallback page
- src/components/pwa-register.tsx — client SW registration
- src/components/install-prompt.tsx — dismissible install banner:
  beforeinstallprompt on Android, "Add to Home Screen" hint on iOS
- Root layout: viewport themeColor, manifest/appleWebApp metadata,
  apple-touch-icon, mounts PwaRegister + InstallPrompt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 16:00:02 -05:00
ginnoir 7e3ae6eb04 Implement lists module and dev login setup 2026-05-06 03:49:03 -05:00
ginnoir 744c1119a9 Implement calendar module 2026-05-06 03:10:28 -05:00
ginnoirandClaude Sonnet 4.6 6710c8b231 Add household seeding & session (task 07)
- scripts/seed.ts: idempotent pnpm db:seed inserts "Home" household if none exists (powered by tsx)
- signIn callback: first member of household gets owner role, subsequent users get member
- src/lib/session.ts: getCurrentSession() returns { user, household, role }, throws if unauthenticated or unmembered
- /settings/household: member list for all roles; rename form gated to owner only (server action also enforces)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 02:42:48 -05:00