From 67430bb670d00b95d6dae47023c11c9c15474ea2 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Fri, 19 Jun 2026 14:01:13 -0400 Subject: [PATCH] docs: regenerate research SUMMARY for v1.2 --- .planning/research/SUMMARY.md | 148 ++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 69 deletions(-) diff --git a/.planning/research/SUMMARY.md b/.planning/research/SUMMARY.md index c590b83..68e41e7 100644 --- a/.planning/research/SUMMARY.md +++ b/.planning/research/SUMMARY.md @@ -1,105 +1,115 @@ # Project Research Summary -**Project:** FamilySync — v1.1 "Operability & Polish" -**Domain:** Self-hosted family calendar + lists PWA (operability/admin milestone on a shipped v1.0) -**Researched:** 2026-06-10 -**Confidence:** HIGH (all findings grounded in direct v1.0 source inspection + v1.0 retrospective) +**Project:** FamilySync — v1.2 "Multi-Provider, Theming & Zero-Setup" +**Domain:** Self-hosted family calendar + lists PWA (opening beyond Fastmail to a second provider, on a shipped v1.1) +**Researched:** 2026-06-19 +**Confidence:** HIGH for architecture/pitfalls (direct codebase inspection + shipped v1.0/v1.1 lessons); MEDIUM for Google Calendar API specifics (official docs via Context7); LOW for third-party UX patterns (cross-checked websearch) ## Executive Summary -v1.1 adds six operability/polish features to the proven v1.0 stack (Node 22 + Hono + Drizzle/MariaDB + tsdav/ical.js + web-push, React 19 + Vite + Schedule-X PWA, on Unraid/Docker behind Authelia OIDC + Pangolin/Newt, self-hosted Gitea with an Actions runner). The core stack is unchanged. Every feature reuses existing capabilities; **only one new dependency is warranted — `@playwright/test` (dev, `apps/pwa` scope)** for the authenticated mobile test harness. No new runtime packages: the setup wizard's validations are all covered by `zod` + `mysql2` + native `fetch` + `Buffer`/`web-push`. +v1.2 opens FamilySync beyond Fastmail. The keystone is a hand-rolled `CalendarProvider` TypeScript interface: the existing tsdav/ical.js CalDAV broker is refactored behind it **as a thin delegation wrapper with zero internal rewrite**, then Google Calendar (Google Calendar API v3 + OAuth2, not CalDAV) plugs in as a second implementation, and an in-memory mock provider plugs in as a third (dev-bypass + hermetic CI). On top of that seam: multiple reminders per event (1→N VALARMs / Google `overrides`), PWA dark mode (Light/Dark/System), and zero-manual-setup DB bootstrap (programmatic `migrate()` at boot). The milestone closes by applying CI-surfaced dependency updates. -The six features: (1) **per-event reminders** — VALARM authoring on the event form + a scheduler that honors each event's lead instead of the hardcoded 15-min; (2) **event-driven outbox drain** — cut perceived write-back latency from ~15s to ~1s; (3) **admin Settings** — role-gated UI to manage encrypted app passwords and designate the shared calendar; (4) **initial setup wizard** — first-run validated bootstrap of env/VAPID/DB/app-password; (5) **Gitea CI** — regression gate on PR + Docker image publish; (6) **mobile-emulated authed Playwright harness**. +The core stack is unchanged. **Two new runtime packages only** — `google-auth-library@10.7.0` and `@googleapis/calendar@15.0.0` (scoped, NOT the 50 MB `googleapis` monolith), both in `apps/api`. Dark mode, multiple reminders, migrate-on-boot, and dependency updates add **zero new dependencies** (Zustand `persist`, `ical.js` multi-VALARM, `drizzle-orm/mysql2/migrator`, and existing `pnpm` tooling already cover them). -Three preservation rules are non-negotiable and drive the design: VALARM authoring must **preserve native-client alarms on edit** (never rebuild-from-scratch and silently strip); the outbox durability guarantees (optimistic-202, create-before-delete ordering, drain concurrency guard, fresh-etag-before-PUT, per-uid exactly-once dedup) must be **unchanged** when the drain goes event-driven; and wizard-collected secrets (VAPID private key, `APP_PASSWORD_ENCRYPTION_KEY`) must **stay in env — never touch the DB or any response body**. +Three non-negotiable preservation rules drive the design: (1) the provider refactor must **not regress the live Fastmail path** — all v1.1 outbox/poller/scheduler integration tests must pass unchanged against the new interface; (2) the `reminder_lead_minutes`→`reminder_leads` JSON migration must **migrate existing data** before dropping the old column; (3) Google OAuth tokens are **per-member secrets**, AES-256-GCM encrypted, never in `app_config`, with the `member_credentials` unique constraint widened to `UNIQUE(user_id, provider_type)`. ## Key Findings ### Recommended Stack -No stack change. One new dev dependency; everything else reuses v1.0. +No stack change beyond two scoped Google packages (both `apps/api`): -**Core additions:** - -- **`@playwright/test`** (dev, `apps/pwa`): the global `playwright-cli` binary is interactive tooling and exposes no `storageState`/`devices` presets — `@playwright/test` is required for CI spec files doing authenticated, device-emulated runs. The two coexist. Auth via the existing `DEV_AUTH_BYPASS` avoids mocking Authelia. -- **Gitea Actions workflows** (`.gitea/workflows/*.yml`, no npm packages): GitHub-Actions-compatible syntax but `runs-on: self-hosted`; job image `catthehacker/ubuntu:act-latest`; MariaDB service container `mariadb:11` with `healthcheck.sh --connect --innodb_initialized` (NOT `mysqladmin ping` — removed in MariaDB 11); Docker push via `docker/login-action@v3` + `docker/build-push-action@v5` needs a Gitea PAT with `write:package` scope (no built-in token has registry push rights). -- **Setup wizard validation — zero new deps:** env presence via `zod.safeParse`, DB via `mysql2` connect, VAPID via `Buffer.from(key,'base64url').length === 32`, OIDC via native `fetch('/.well-known/openid-configuration')`. +- **`google-auth-library@10.7.0`** — OAuth2 authorization-code flow + offline refresh-token management; ships its own types; `OAuth2Client` auto-refreshes expired access tokens via the `tokens` event. +- **`@googleapis/calendar@15.0.0`** — Google Calendar API v3 typed client (depends only on `googleapis-common`). **Reject the monolithic `googleapis`** (170+ clients, ~50 MB). +- **Provider abstraction** — hand-rolled TS interface in `apps/api/src/broker/`; no normalization library exists worth a dependency. +- **Dark mode** — pure CSS `[data-theme="dark"]` + Zustand `persist` (built into 5.0.14) + an inline `