style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
+43
-41
@@ -2,13 +2,13 @@
|
||||
|
||||
## Header
|
||||
|
||||
| Field | Value |
|
||||
|--------------|---------------------------------------------------------|
|
||||
| Deploy URL | LIVE via Pangolin/Newt (operator domain) — confirmed reachable; real Authelia OIDC login working 2026-06-07 |
|
||||
| Build SHA | 86069b8 (2026-06-07 live bring-up + write-path fixes) |
|
||||
| Build date | 2026-06-07 |
|
||||
| PWA build | CLEAN — dist/sw.js + workbox generated; 140/140 tests |
|
||||
| API build | CLEAN — tsc passed; 102/102 tests |
|
||||
| Field | Value |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| Deploy URL | LIVE via Pangolin/Newt (operator domain) — confirmed reachable; real Authelia OIDC login working 2026-06-07 |
|
||||
| Build SHA | 86069b8 (2026-06-07 live bring-up + write-path fixes) |
|
||||
| Build date | 2026-06-07 |
|
||||
| PWA build | CLEAN — dist/sw.js + workbox generated; 140/140 tests |
|
||||
| API build | CLEAN — tsc passed; 102/102 tests |
|
||||
|
||||
> **2026-06-07 live verification note.** Gate 2 was executed live against the running
|
||||
> Docker stack through Pangolin/Newt (Mode A). Several blocker bugs were found and fixed
|
||||
@@ -41,13 +41,13 @@ Add to Authelia `configuration.yml` under `identity_providers.oidc.clients`:
|
||||
identity_providers:
|
||||
oidc:
|
||||
clients:
|
||||
- client_id: 'familysync-dev' # Use 'familysync' for Unraid prod (Mode B)
|
||||
- client_id: 'familysync-dev' # Use 'familysync' for Unraid prod (Mode B)
|
||||
client_name: 'FamilySync'
|
||||
client_secret: '$pbkdf2-sha512$...' # The HASH from the command above
|
||||
client_secret: '$pbkdf2-sha512$...' # The HASH from the command above
|
||||
public: false
|
||||
authorization_policy: 'one_factor'
|
||||
redirect_uris:
|
||||
- 'https://familysync-dev.DOMAIN/callback' # Replace DOMAIN; Mode B: familysync.DOMAIN
|
||||
- 'https://familysync-dev.DOMAIN/callback' # Replace DOMAIN; Mode B: familysync.DOMAIN
|
||||
scopes: [openid, profile, email]
|
||||
response_types: [code]
|
||||
grant_types: [authorization_code, refresh_token]
|
||||
@@ -73,6 +73,7 @@ OIDC_REDIRECT_URI=https://familysync-dev.DOMAIN/callback
|
||||
```
|
||||
|
||||
Also ensure:
|
||||
|
||||
- `NODE_ENV=production` is set in the container — this forces `devBypassActive=false` in
|
||||
`apps/api/src/index.ts`, mounting the OIDC guard unconditionally.
|
||||
- `DEV_AUTH_BYPASS` is **absent** (or unset) from the production environment block.
|
||||
@@ -90,6 +91,7 @@ docker run -d --name newt --restart unless-stopped \
|
||||
```
|
||||
|
||||
In Pangolin, create a route:
|
||||
|
||||
- Host: `familysync-dev.DOMAIN`
|
||||
- Upstream: `http://<api-host>:3000`
|
||||
- Pangolin's own auth: **OFF** — FamilySync does Authelia OIDC at the app layer.
|
||||
@@ -125,11 +127,11 @@ Mark each row PASS or FAIL and add notes. On failure, apply the indicated remedy
|
||||
|
||||
### Part A — Auth, Session, Colors (Task 2)
|
||||
|
||||
| # | Ref | Check | Result | Notes |
|
||||
|---|-----|-------|--------|-------|
|
||||
| A1 | AUTH-01 | Open `https://familysync-dev.DOMAIN` → redirects to Authelia → login completes → land on the app with name, color, and at least one cached event | ✅ PASS (2026-06-07) | Real Authelia OIDC login lands on the calendar; name (email claim), assigned color, and cached events render. Name self-heals to full name once Authelia emits name/preferred_username (see backlog/memory). |
|
||||
| A2 | AUTH-02 | Fully close + reopen browser → revisit the URL → no re-login prompted (session persists) | 🟡 PASS (transparent) | Confirmed (desktop + iPhone): cold open bounces through Authelia but its SSO carries the session, so NO credential prompt — user lands straight on the app. Note: the app's own oidc-auth cookie is session-scoped (dropped on browser close), so each cold open does a redirect round-trip. Acceptable for v1; making the app cookie persistent (skip the bounce) is a minor follow-up. |
|
||||
| A3 | AUTH-03 | Second member logs in on a separate device → distinct stable color assigned (different from first member's color) | ✅ PASS (2026-06-07) | Second member (amelia, id=3) logged in on her iPhone. Found + fixed a collision bug (both members were #E8734A — COUNT%palette reused a slot after a deletion); now luc=#E8734A, amelia=#4A90D9 (distinct, stable). Fix: first-unused-palette-color (commit f700182). |
|
||||
| # | Ref | Check | Result | Notes |
|
||||
| --- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| A1 | AUTH-01 | Open `https://familysync-dev.DOMAIN` → redirects to Authelia → login completes → land on the app with name, color, and at least one cached event | ✅ PASS (2026-06-07) | Real Authelia OIDC login lands on the calendar; name (email claim), assigned color, and cached events render. Name self-heals to full name once Authelia emits name/preferred_username (see backlog/memory). |
|
||||
| A2 | AUTH-02 | Fully close + reopen browser → revisit the URL → no re-login prompted (session persists) | 🟡 PASS (transparent) | Confirmed (desktop + iPhone): cold open bounces through Authelia but its SSO carries the session, so NO credential prompt — user lands straight on the app. Note: the app's own oidc-auth cookie is session-scoped (dropped on browser close), so each cold open does a redirect round-trip. Acceptable for v1; making the app cookie persistent (skip the bounce) is a minor follow-up. |
|
||||
| A3 | AUTH-03 | Second member logs in on a separate device → distinct stable color assigned (different from first member's color) | ✅ PASS (2026-06-07) | Second member (amelia, id=3) logged in on her iPhone. Found + fixed a collision bug (both members were #E8734A — COUNT%palette reused a slot after a deletion); now luc=#E8734A, amelia=#4A90D9 (distinct, stable). Fix: first-unused-palette-color (commit f700182). |
|
||||
|
||||
### Part B — iOS PWA Standalone Login (Task 2) — LOAD-BEARING CHECK
|
||||
|
||||
@@ -141,19 +143,19 @@ Mark each row PASS or FAIL and add notes. On failure, apply the indicated remedy
|
||||
> confirm `/callback` is in the service worker denylist (`apps/pwa/src/sw-denylist.ts`) and is not
|
||||
> intercepted by Workbox; redeploy and retest.
|
||||
|
||||
| # | Ref | Check | Result | Notes |
|
||||
|---|-----|-------|--------|-------|
|
||||
| B1 | iOS PWA | Open `https://familysync-dev.DOMAIN` in Safari on iPhone → in-app install walkthrough appears → tap "Add to Home Screen" | ✅ PASS (2026-06-07) | Wife added FamilySync to her iPhone Home Screen and logged in (user id=3 created). |
|
||||
| B2 | iOS PWA | Launch FamilySync from Home Screen → opens full-screen with no Safari browser chrome (standalone mode) | ✅ PASS (2026-06-07) | Confirmed: launches full-screen standalone from Home Screen. |
|
||||
| B3 | iOS PWA (Pitfall 2) | Complete Authelia OIDC login from standalone mode → redirect does NOT break out of standalone (user stays in the app, not dropped to Safari) | ✅ PASS (2026-06-07) | Confirmed working — OIDC login from standalone stays in the app, no drop to Safari. **Load-bearing check cleared.** |
|
||||
| B4 | PWA-01 | Installed PWA on iOS opens full-screen with no browser chrome | ✅ PASS (2026-06-07) | Confirmed (same as B2). |
|
||||
| B5 | PWA-02 | Installed PWA on Android opens full-screen with no browser chrome | [ ] PENDING — device | Android install not yet exercised. |
|
||||
| # | Ref | Check | Result | Notes |
|
||||
| --- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| B1 | iOS PWA | Open `https://familysync-dev.DOMAIN` in Safari on iPhone → in-app install walkthrough appears → tap "Add to Home Screen" | ✅ PASS (2026-06-07) | Wife added FamilySync to her iPhone Home Screen and logged in (user id=3 created). |
|
||||
| B2 | iOS PWA | Launch FamilySync from Home Screen → opens full-screen with no Safari browser chrome (standalone mode) | ✅ PASS (2026-06-07) | Confirmed: launches full-screen standalone from Home Screen. |
|
||||
| B3 | iOS PWA (Pitfall 2) | Complete Authelia OIDC login from standalone mode → redirect does NOT break out of standalone (user stays in the app, not dropped to Safari) | ✅ PASS (2026-06-07) | Confirmed working — OIDC login from standalone stays in the app, no drop to Safari. **Load-bearing check cleared.** |
|
||||
| B4 | PWA-01 | Installed PWA on iOS opens full-screen with no browser chrome | ✅ PASS (2026-06-07) | Confirmed (same as B2). |
|
||||
| B5 | PWA-02 | Installed PWA on Android opens full-screen with no browser chrome | [ ] PENDING — device | Android install not yet exercised. |
|
||||
|
||||
### Part C — SSE Smoke Test (Gate before Phase 4)
|
||||
|
||||
| # | Ref | Check | Result | Notes |
|
||||
|---|-----|-------|--------|-------|
|
||||
| C1 | SSE | Hold stream open 5+ min without it being cut (see curl command below) | ✅ PASS (2026-06-08) | Held GET /api/sse/heartbeat open ~6 min over familysync-dev.bergerhouse.net (Pangolin→Newt→api) with a valid session cookie (01:37:53Z→01:43:54Z); 35 heartbeat events id 0→34 at ~10s cadence; response bytes grew 71→2535 (incremental → Pangolin buffering OFF); no early cut. Phase 4 entry gate (D-14 / issue #1034) CLEARED. Caveat: proves no idle-timeout/buffering over ~6 min, not the absence of a max total connection-duration cap — residual risk covered by Phase 4 design (D-10/D-11/D-12 in 04-CONTEXT.md). |
|
||||
| # | Ref | Check | Result | Notes |
|
||||
| --- | --- | --------------------------------------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| C1 | SSE | Hold stream open 5+ min without it being cut (see curl command below) | ✅ PASS (2026-06-08) | Held GET /api/sse/heartbeat open ~6 min over familysync-dev.bergerhouse.net (Pangolin→Newt→api) with a valid session cookie (01:37:53Z→01:43:54Z); 35 heartbeat events id 0→34 at ~10s cadence; response bytes grew 71→2535 (incremental → Pangolin buffering OFF); no early cut. Phase 4 entry gate (D-14 / issue #1034) CLEARED. Caveat: proves no idle-timeout/buffering over ~6 min, not the absence of a max total connection-duration cap — residual risk covered by Phase 4 design (D-10/D-11/D-12 in 04-CONTEXT.md). |
|
||||
|
||||
```bash
|
||||
# Get the session cookie from browser DevTools → Application → Cookies (oidc-auth=<value>)
|
||||
@@ -164,14 +166,14 @@ curl -N -H "Cookie: oidc-auth=<value>" https://familysync-dev.DOMAIN/api/sse/hea
|
||||
|
||||
### Part D — Create / Edit / Delete Fastmail Round-trips (Task 3)
|
||||
|
||||
| # | Ref | Check | Result | Notes |
|
||||
|---|-----|-------|--------|-------|
|
||||
| D1 | CAL-04 | Create a timed event → "Syncing…" toast → "Saved" toast → event appears in native Fastmail app on next sync | ✅ PASS (2026-06-07) | Timed create round-trips to caldav.fastmail.com (outbox rows reach `done`); appears in the app. Timezone fix applied (was 4h off). |
|
||||
| D2 | CAL-07 | Create an all-day event → same Syncing→Saved flow → appears in Fastmail | ✅ PASS (2026-06-07) | All-day create round-trips to Fastmail (verified VEVENT: DTSTART/DTEND VALUE=DATE, exclusive end). Found + fixed a display off-by-one (single-day showed across 2 days — Schedule-X inclusive vs iCal exclusive end; commit d4d5327). Reload to confirm 1-day rendering. |
|
||||
| D3 | CAL-04 | Create a weekly recurring event → appears in Fastmail | ✅ PASS — write correct; UX gaps backlogged | A weekly event was created and recurred in Fastmail with a valid `RRULE:FREQ=WEEKLY`. Two UX gaps surfaced (NOT write-correctness): no "repeat until/count" bound (series is unbounded → recurs into 2028+) and the end-date is the per-occurrence duration (a 2-month end made each occurrence 63 days → overlapping every day). Backlogged 999.7/999.8. Deleting the recurring series cleared the master + all occurrences from Fastmail in one delete (recurring-series delete verified). |
|
||||
| D4 | CAL-05 | Edit an existing event's title and time → Syncing→Saved → change persists in Fastmail | ✅ PASS (2026-06-07) | Edit/move confirmed working; update outbox rows reach `done`; post-write refetch race fixed so the change shows without manual refresh. |
|
||||
| D5 | CAL-06 | Delete an event via the two-tap confirmation dialog → Syncing→Saved → event disappears from all views on next sync | ✅ PASS (2026-06-07) | Delete confirmed working; delete cache-reconciliation fix means the event leaves the cache/UI (was lingering as a ghost). |
|
||||
| D6 | D-08 | (Optional) Trigger a 412 conflict by editing the same event in Fastmail first → conflict toast appears in the app → calendar re-fetches | ✅ PASS (2026-06-07) | Observed live: a stale-etag update produced `412 conflict` (outbox id=7) and the "This event changed elsewhere" conflict toast; calendar re-syncs. |
|
||||
| # | Ref | Check | Result | Notes |
|
||||
| --- | ------ | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| D1 | CAL-04 | Create a timed event → "Syncing…" toast → "Saved" toast → event appears in native Fastmail app on next sync | ✅ PASS (2026-06-07) | Timed create round-trips to caldav.fastmail.com (outbox rows reach `done`); appears in the app. Timezone fix applied (was 4h off). |
|
||||
| D2 | CAL-07 | Create an all-day event → same Syncing→Saved flow → appears in Fastmail | ✅ PASS (2026-06-07) | All-day create round-trips to Fastmail (verified VEVENT: DTSTART/DTEND VALUE=DATE, exclusive end). Found + fixed a display off-by-one (single-day showed across 2 days — Schedule-X inclusive vs iCal exclusive end; commit d4d5327). Reload to confirm 1-day rendering. |
|
||||
| D3 | CAL-04 | Create a weekly recurring event → appears in Fastmail | ✅ PASS — write correct; UX gaps backlogged | A weekly event was created and recurred in Fastmail with a valid `RRULE:FREQ=WEEKLY`. Two UX gaps surfaced (NOT write-correctness): no "repeat until/count" bound (series is unbounded → recurs into 2028+) and the end-date is the per-occurrence duration (a 2-month end made each occurrence 63 days → overlapping every day). Backlogged 999.7/999.8. Deleting the recurring series cleared the master + all occurrences from Fastmail in one delete (recurring-series delete verified). |
|
||||
| D4 | CAL-05 | Edit an existing event's title and time → Syncing→Saved → change persists in Fastmail | ✅ PASS (2026-06-07) | Edit/move confirmed working; update outbox rows reach `done`; post-write refetch race fixed so the change shows without manual refresh. |
|
||||
| D5 | CAL-06 | Delete an event via the two-tap confirmation dialog → Syncing→Saved → event disappears from all views on next sync | ✅ PASS (2026-06-07) | Delete confirmed working; delete cache-reconciliation fix means the event leaves the cache/UI (was lingering as a ghost). |
|
||||
| D6 | D-08 | (Optional) Trigger a 412 conflict by editing the same event in Fastmail first → conflict toast appears in the app → calendar re-fetches | ✅ PASS (2026-06-07) | Observed live: a stale-etag update produced `412 conflict` (outbox id=7) and the "This event changed elsewhere" conflict toast; calendar re-syncs. |
|
||||
|
||||
---
|
||||
|
||||
@@ -189,14 +191,14 @@ Response body: {"ok":true,"db":"up"}
|
||||
|
||||
## Summary
|
||||
|
||||
| Section | Status |
|
||||
|---------|--------|
|
||||
| Production builds (PWA + API) | ✅ CLEAN (2026-06-07; 102 API + 140 PWA tests) |
|
||||
| Operator infra setup | ✅ DONE (Authelia client + Pangolin/Newt live; OIDC login working) |
|
||||
| A — Auth / session / colors | ✅ A1, A2 (transparent SSO), A3 all PASS |
|
||||
| B — iOS standalone login (load-bearing) | ✅ B1–B4 PASS (install + standalone launch + standalone login); B5 (Android) deferred |
|
||||
| C — SSE smoke test | ✅ PASS (2026-06-08) — Phase 4 ENTRY gate (D-14 / issue #1034) CLEARED; held ~6 min, 35 heartbeats, incremental delivery, no proxy cut |
|
||||
| D — Fastmail write round-trips | ✅ D1–D6 PASS (create/all-day/recurring/edit/delete/conflict); recurring-series delete also verified |
|
||||
| Section | Status |
|
||||
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Production builds (PWA + API) | ✅ CLEAN (2026-06-07; 102 API + 140 PWA tests) |
|
||||
| Operator infra setup | ✅ DONE (Authelia client + Pangolin/Newt live; OIDC login working) |
|
||||
| A — Auth / session / colors | ✅ A1, A2 (transparent SSO), A3 all PASS |
|
||||
| B — iOS standalone login (load-bearing) | ✅ B1–B4 PASS (install + standalone launch + standalone login); B5 (Android) deferred |
|
||||
| C — SSE smoke test | ✅ PASS (2026-06-08) — Phase 4 ENTRY gate (D-14 / issue #1034) CLEARED; held ~6 min, 35 heartbeats, incremental delivery, no proxy cut |
|
||||
| D — Fastmail write round-trips | ✅ D1–D6 PASS (create/all-day/recurring/edit/delete/conflict); recurring-series delete also verified |
|
||||
|
||||
Gate 2 is complete when all rows are PASS. Record final status here:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user