style(17): apply prettier formatting to satisfy CI format:check
CI / changes (pull_request) Successful in 6s
CI / api (pull_request) Successful in 2m9s
CI / fast-checks (pull_request) Successful in 2m30s
CI / security (pull_request) Successful in 59s
CI / harness (pull_request) Failing after 12m0s
CI / gate (pull_request) Failing after 2s

Reformats 4 phase-17 files (SettingsSheet.tsx, tokens.css, vite.config.ts,
pwa-assets.config.ts) plus 11 pre-existing non-conformant docs/READMEs that
the repo-wide format:check also flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lucas Berger
2026-06-18 15:23:35 -04:00
co-authored by Claude Opus 4.8
parent 6c2c6f24a9
commit 24bc8d2c32
15 changed files with 290 additions and 276 deletions
+26 -26
View File
@@ -125,27 +125,27 @@ Migration files are written to `src/db/migrations/` and checked into source cont
## Environment variables
| Variable | Required | Description |
| ----------------------------- | ------------------- | ---------------------------------------------------------------------------------- |
| `DB_HOST` | Yes | MariaDB host |
| `DB_USER` | Yes | MariaDB user |
| `DB_PASSWORD` | Yes | MariaDB password |
| `DB_NAME` | Yes | MariaDB database name |
| `DB_PORT` | No (default `3306`) | MariaDB port |
| `OIDC_ISSUER` | Yes (production) | Authelia issuer URL |
| `OIDC_CLIENT_ID` | Yes (production) | OIDC client ID |
| `OIDC_CLIENT_SECRET` | Yes (production) | OIDC client secret |
| `OIDC_AUTH_EXTERNAL_URL` | Yes (production) | External-facing URL for redirect_uri behind Pangolin tunnel |
| `OIDC_REDIRECT_URI` | No | Explicit redirect URI (overrides the `${OIDC_AUTH_EXTERNAL_URL}/callback` default) |
| `VAPID_SUBJECT` | Yes (push) | `mailto:` or `https:` operator identifier |
| `VAPID_PUBLIC_KEY` | Yes (push) | VAPID public key |
| `VAPID_PRIVATE_KEY` | Yes (push) | VAPID private key |
| `APP_PASSWORD_ENCRYPTION_KEY` | Yes | AES-256-GCM key (64-char hex) for stored Fastmail app passwords |
| `LOCAL_SESSION_SECRET` | Yes (local auth) | HS256 signing key for local-session JWT cookies (min 32 chars) |
| `LOCAL_SESSION_EXPIRES` | No (default `86400`)| Local session lifetime in seconds |
| `DEV_AUTH_BYPASS` | No | Set to `true` (non-production only) to skip OIDC and inject a dev user |
| `NODE_ENV` | No | Set to `production` to enforce OIDC unconditionally |
| `TZ` | No | IANA timezone fallback when household_timezone is not set in app_config |
| Variable | Required | Description |
| ----------------------------- | -------------------- | ---------------------------------------------------------------------------------- |
| `DB_HOST` | Yes | MariaDB host |
| `DB_USER` | Yes | MariaDB user |
| `DB_PASSWORD` | Yes | MariaDB password |
| `DB_NAME` | Yes | MariaDB database name |
| `DB_PORT` | No (default `3306`) | MariaDB port |
| `OIDC_ISSUER` | Yes (production) | Authelia issuer URL |
| `OIDC_CLIENT_ID` | Yes (production) | OIDC client ID |
| `OIDC_CLIENT_SECRET` | Yes (production) | OIDC client secret |
| `OIDC_AUTH_EXTERNAL_URL` | Yes (production) | External-facing URL for redirect_uri behind Pangolin tunnel |
| `OIDC_REDIRECT_URI` | No | Explicit redirect URI (overrides the `${OIDC_AUTH_EXTERNAL_URL}/callback` default) |
| `VAPID_SUBJECT` | Yes (push) | `mailto:` or `https:` operator identifier |
| `VAPID_PUBLIC_KEY` | Yes (push) | VAPID public key |
| `VAPID_PRIVATE_KEY` | Yes (push) | VAPID private key |
| `APP_PASSWORD_ENCRYPTION_KEY` | Yes | AES-256-GCM key (64-char hex) for stored Fastmail app passwords |
| `LOCAL_SESSION_SECRET` | Yes (local auth) | HS256 signing key for local-session JWT cookies (min 32 chars) |
| `LOCAL_SESSION_EXPIRES` | No (default `86400`) | Local session lifetime in seconds |
| `DEV_AUTH_BYPASS` | No | Set to `true` (non-production only) to skip OIDC and inject a dev user |
| `NODE_ENV` | No | Set to `production` to enforce OIDC unconditionally |
| `TZ` | No | IANA timezone fallback when household_timezone is not set in app_config |
> **Note:** `CREDENTIAL_ENCRYPTION_KEY` was renamed to `APP_PASSWORD_ENCRYPTION_KEY`. Update any existing `.env` files if upgrading from an earlier phase.
@@ -155,11 +155,11 @@ See [../../docs/CONFIGURATION.md](../../docs/CONFIGURATION.md) for the full refe
The API supports two non-exclusive auth modes, determined at startup:
| Mode | When active | How it works |
| ---- | ----------- | ------------ |
| **Local** | Always (default) | `POST /api/auth/local/login` with username + password; issues an HS256 JWT `local-session` cookie. Requires `LOCAL_SESSION_SECRET`. |
| **OIDC** | When `OIDC_ISSUER` + `OIDC_CLIENT_ID` are set (env or app_config) | `@hono/oidc-auth` authorization-code + PKCE against Authelia. Local users can upgrade to OIDC via `POST /api/me/link-oidc`. |
| **Dev bypass** | `DEV_AUTH_BYPASS=true` in non-production | Skips both guards and injects a synthetic dev user. Blocked in `NODE_ENV=production` by boot guard. |
| Mode | When active | How it works |
| -------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| **Local** | Always (default) | `POST /api/auth/local/login` with username + password; issues an HS256 JWT `local-session` cookie. Requires `LOCAL_SESSION_SECRET`. |
| **OIDC** | When `OIDC_ISSUER` + `OIDC_CLIENT_ID` are set (env or app_config) | `@hono/oidc-auth` authorization-code + PKCE against Authelia. Local users can upgrade to OIDC via `POST /api/me/link-oidc`. |
| **Dev bypass** | `DEV_AUTH_BYPASS=true` in non-production | Skips both guards and injects a synthetic dev user. Blocked in `NODE_ENV=production` by boot guard. |
`GET /api/auth/mode` returns `{ localEnabled, oidcEnabled }` before authentication — the PWA uses this to decide which login form to show.
+11 -11
View File
@@ -32,17 +32,17 @@ The API backend must also be running for most features. See [GETTING-STARTED.md]
## Scripts
| Command | What it does |
| ------------------------------------------------ | ------------------------------------------------------------------------- |
| `pnpm --filter @familysync/pwa dev` | Start Vite dev server (HMR) |
| `pnpm --filter @familysync/pwa build` | Type-check then build production bundle (`tsc && vite build`) |
| `pnpm --filter @familysync/pwa preview` | Serve the production build locally |
| `pnpm --filter @familysync/pwa lint` | Run ESLint over `src/` and `e2e/` with zero warnings allowed |
| `pnpm --filter @familysync/pwa typecheck` | Run `tsc --noEmit` for both `src/` and `e2e/` tsconfigs |
| `pnpm --filter @familysync/pwa test` | Run Vitest unit/integration suite once (`vitest run`) |
| `pnpm --filter @familysync/pwa test:e2e` | Run Playwright end-to-end tests headlessly |
| `pnpm --filter @familysync/pwa test:e2e:ui` | Open the Playwright UI runner |
| `pnpm --filter @familysync/pwa test:e2e:headed` | Run Playwright tests in a headed browser |
| Command | What it does |
| ----------------------------------------------- | ------------------------------------------------------------- |
| `pnpm --filter @familysync/pwa dev` | Start Vite dev server (HMR) |
| `pnpm --filter @familysync/pwa build` | Type-check then build production bundle (`tsc && vite build`) |
| `pnpm --filter @familysync/pwa preview` | Serve the production build locally |
| `pnpm --filter @familysync/pwa lint` | Run ESLint over `src/` and `e2e/` with zero warnings allowed |
| `pnpm --filter @familysync/pwa typecheck` | Run `tsc --noEmit` for both `src/` and `e2e/` tsconfigs |
| `pnpm --filter @familysync/pwa test` | Run Vitest unit/integration suite once (`vitest run`) |
| `pnpm --filter @familysync/pwa test:e2e` | Run Playwright end-to-end tests headlessly |
| `pnpm --filter @familysync/pwa test:e2e:ui` | Open the Playwright UI runner |
| `pnpm --filter @familysync/pwa test:e2e:headed` | Run Playwright tests in a headed browser |
## Source layout
+2 -2
View File
@@ -1,4 +1,4 @@
import { defineConfig, minimal2023Preset } from '@vite-pwa/assets-generator/config'
import { defineConfig, minimal2023Preset } from '@vite-pwa/assets-generator/config';
export default defineConfig({
preset: {
@@ -7,4 +7,4 @@ export default defineConfig({
images: ['public/logo.svg'],
// Do NOT set overrideManifestIcons: true — the manifest is maintained by hand
// in vite.config.ts (plan 17-04); auto-override would stomp the explicit entries.
})
});
+7 -1
View File
@@ -29,7 +29,13 @@ import { usePushSubscription } from '../hooks/usePushSubscription.js';
import { useIsPhone } from '../hooks/useIsPhone.js';
import { useFocusTrap } from '../hooks/useFocusTrap.js';
import { InstructionSheet } from './InstructionSheet.js';
import { fetchMe, fetchAuthMode, fetchChangePassword, fetchLinkOidc, fetchLocalLogout } from '../api/client.js';
import {
fetchMe,
fetchAuthMode,
fetchChangePassword,
fetchLinkOidc,
fetchLocalLogout,
} from '../api/client.js';
// CR-04: fetch VAPID key (from sessionStorage cache if available) for the
// tap-gated subscribe() path. Same logic as PushPermissionPrompt.
+1 -1
View File
@@ -14,7 +14,7 @@
*/
:root,
[data-theme="light"] {
[data-theme='light'] {
/* ─────────────────────────────────────────────────────────────────────────
* BASE SURFACE / BORDER / TEXT PALETTE
* ───────────────────────────────────────────────────────────────────────── */
+6 -1
View File
@@ -42,7 +42,12 @@ export default defineConfig({
icons: [
{ src: '/icon-192.png', sizes: '192x192', type: 'image/png' },
{ src: '/icon-512.png', sizes: '512x512', type: 'image/png' },
{ src: '/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
{
src: '/icon-maskable-512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable',
},
],
},
}),