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
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:
co-authored by
Claude Opus 4.8
parent
6c2c6f24a9
commit
24bc8d2c32
+98
-97
@@ -25,54 +25,54 @@ No API key or `Authorization` header is used. Credentials are never included in
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
| ------ | ------------------------------------- | --------- | -------------------------------------------------- |
|
||||
| GET | `/health` | None | DB liveness check |
|
||||
| GET | `/callback` | None | OIDC authorization-code exchange |
|
||||
| GET | `/api/setup/status` | None | Setup wizard completion status |
|
||||
| POST | `/api/setup/config` | None | Store OIDC and VAPID config (wizard step 1) |
|
||||
| POST | `/api/setup/validate/db` | None | Validate DB connectivity (wizard step) |
|
||||
| POST | `/api/setup/validate/oidc` | None | Validate OIDC issuer discovery (wizard step) |
|
||||
| POST | `/api/setup/validate/vapid` | None | Validate VAPID key pair (wizard step) |
|
||||
| POST | `/api/setup/credential` | None | Store first admin's Fastmail credential (wizard) |
|
||||
| POST | `/api/setup/complete` | None | Lock the setup wizard |
|
||||
| GET | `/api/auth/mode` | None | Auth mode discovery (local vs OIDC enabled) |
|
||||
| POST | `/api/auth/local/login` | None | Local username+password login |
|
||||
| POST | `/api/auth/local/logout` | None | Clear local session cookie |
|
||||
| GET | `/api/auth/local/logout` | None | Clear local session cookie (browser redirect alias)|
|
||||
| GET | `/api/login` | OIDC | OIDC login entry point, redirects to `/` |
|
||||
| GET | `/api/me` | Session | Current user identity, role, and setup status |
|
||||
| POST | `/api/me/credential` | Session | Member self-service Fastmail credential update |
|
||||
| POST | `/api/me/password` | Session | Member self-change local password |
|
||||
| POST | `/api/me/link-oidc` | Session | Initiate OIDC identity link for a local user |
|
||||
| GET | `/api/events` | Session | Windowed calendar occurrences |
|
||||
| POST | `/api/events/create` | Session | Enqueue a new event write |
|
||||
| PATCH | `/api/events/:uid/edit` | Session | Enqueue an event update |
|
||||
| DELETE | `/api/events/:uid` | Session | Enqueue an event delete |
|
||||
| GET | `/api/events/sync-status` | Session | Outbox status for a UID |
|
||||
| GET | `/api/events/writable-calendars` | Session | Calendars the member can write to |
|
||||
| GET | `/api/lists` | Session | All lists accessible to the member |
|
||||
| POST | `/api/lists` | Session | Create a list |
|
||||
| PATCH | `/api/lists/:id` | Session | Update list name or sharing |
|
||||
| DELETE | `/api/lists/:id` | Session | Delete a list (owner only) |
|
||||
| GET | `/api/lists/:id/items` | Session | All items in a list |
|
||||
| POST | `/api/lists/:id/items` | Session | Add an item to a list |
|
||||
| PATCH | `/api/list-items/:itemId` | Session | Update a single list item field |
|
||||
| DELETE | `/api/list-items/:itemId` | Session | Delete a list item |
|
||||
| GET | `/api/sse/heartbeat` | Session | SSE heartbeat stream |
|
||||
| GET | `/api/sse/lists` | Session | Scoped live-list SSE stream |
|
||||
| GET | `/api/push/vapid-public-key` | Session | VAPID public key for push subscribe |
|
||||
| POST | `/api/push/subscription` | Session | Register a push subscription |
|
||||
| DELETE | `/api/push/subscription` | Session | Remove push subscriptions for caller |
|
||||
| GET | `/api/admin/members` | Admin | List members with credential status |
|
||||
| POST | `/api/admin/members` | Admin | Create a new local member |
|
||||
| POST | `/api/admin/members/:id/password` | Admin | Reset a local member's password |
|
||||
| POST | `/api/admin/credentials` | Admin | Validate and store a member's Fastmail credential |
|
||||
| GET | `/api/admin/calendars` | Admin | List synced calendars |
|
||||
| PUT | `/api/admin/calendars/:id/shared` | Admin | Designate the shared family calendar |
|
||||
| GET | `/api/admin/config/timezone` | Admin | Get household timezone |
|
||||
| PUT | `/api/admin/config/timezone` | Admin | Set household timezone |
|
||||
| POST | `/api/admin/config/timezone/seed` | Admin | Seed household timezone if not yet set |
|
||||
| Method | Path | Auth | Description |
|
||||
| ------ | --------------------------------- | ------- | --------------------------------------------------- |
|
||||
| GET | `/health` | None | DB liveness check |
|
||||
| GET | `/callback` | None | OIDC authorization-code exchange |
|
||||
| GET | `/api/setup/status` | None | Setup wizard completion status |
|
||||
| POST | `/api/setup/config` | None | Store OIDC and VAPID config (wizard step 1) |
|
||||
| POST | `/api/setup/validate/db` | None | Validate DB connectivity (wizard step) |
|
||||
| POST | `/api/setup/validate/oidc` | None | Validate OIDC issuer discovery (wizard step) |
|
||||
| POST | `/api/setup/validate/vapid` | None | Validate VAPID key pair (wizard step) |
|
||||
| POST | `/api/setup/credential` | None | Store first admin's Fastmail credential (wizard) |
|
||||
| POST | `/api/setup/complete` | None | Lock the setup wizard |
|
||||
| GET | `/api/auth/mode` | None | Auth mode discovery (local vs OIDC enabled) |
|
||||
| POST | `/api/auth/local/login` | None | Local username+password login |
|
||||
| POST | `/api/auth/local/logout` | None | Clear local session cookie |
|
||||
| GET | `/api/auth/local/logout` | None | Clear local session cookie (browser redirect alias) |
|
||||
| GET | `/api/login` | OIDC | OIDC login entry point, redirects to `/` |
|
||||
| GET | `/api/me` | Session | Current user identity, role, and setup status |
|
||||
| POST | `/api/me/credential` | Session | Member self-service Fastmail credential update |
|
||||
| POST | `/api/me/password` | Session | Member self-change local password |
|
||||
| POST | `/api/me/link-oidc` | Session | Initiate OIDC identity link for a local user |
|
||||
| GET | `/api/events` | Session | Windowed calendar occurrences |
|
||||
| POST | `/api/events/create` | Session | Enqueue a new event write |
|
||||
| PATCH | `/api/events/:uid/edit` | Session | Enqueue an event update |
|
||||
| DELETE | `/api/events/:uid` | Session | Enqueue an event delete |
|
||||
| GET | `/api/events/sync-status` | Session | Outbox status for a UID |
|
||||
| GET | `/api/events/writable-calendars` | Session | Calendars the member can write to |
|
||||
| GET | `/api/lists` | Session | All lists accessible to the member |
|
||||
| POST | `/api/lists` | Session | Create a list |
|
||||
| PATCH | `/api/lists/:id` | Session | Update list name or sharing |
|
||||
| DELETE | `/api/lists/:id` | Session | Delete a list (owner only) |
|
||||
| GET | `/api/lists/:id/items` | Session | All items in a list |
|
||||
| POST | `/api/lists/:id/items` | Session | Add an item to a list |
|
||||
| PATCH | `/api/list-items/:itemId` | Session | Update a single list item field |
|
||||
| DELETE | `/api/list-items/:itemId` | Session | Delete a list item |
|
||||
| GET | `/api/sse/heartbeat` | Session | SSE heartbeat stream |
|
||||
| GET | `/api/sse/lists` | Session | Scoped live-list SSE stream |
|
||||
| GET | `/api/push/vapid-public-key` | Session | VAPID public key for push subscribe |
|
||||
| POST | `/api/push/subscription` | Session | Register a push subscription |
|
||||
| DELETE | `/api/push/subscription` | Session | Remove push subscriptions for caller |
|
||||
| GET | `/api/admin/members` | Admin | List members with credential status |
|
||||
| POST | `/api/admin/members` | Admin | Create a new local member |
|
||||
| POST | `/api/admin/members/:id/password` | Admin | Reset a local member's password |
|
||||
| POST | `/api/admin/credentials` | Admin | Validate and store a member's Fastmail credential |
|
||||
| GET | `/api/admin/calendars` | Admin | List synced calendars |
|
||||
| PUT | `/api/admin/calendars/:id/shared` | Admin | Designate the shared family calendar |
|
||||
| GET | `/api/admin/config/timezone` | Admin | Get household timezone |
|
||||
| PUT | `/api/admin/config/timezone` | Admin | Set household timezone |
|
||||
| POST | `/api/admin/config/timezone/seed` | Admin | Seed household timezone if not yet set |
|
||||
|
||||
---
|
||||
|
||||
@@ -129,12 +129,12 @@ Stores non-secret OIDC and VAPID configuration into `app_config`. Returns `423`
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| ---------------- | ------ | -------- | ------------------------------ |
|
||||
| `oidcIssuer` | string | Yes | HTTPS URL |
|
||||
| `oidcClientId` | string | Yes | 1–256 characters |
|
||||
| `vapidPublicKey` | string | Yes | 1–512 characters |
|
||||
| `appExternalUrl` | string | Yes | HTTPS URL, max 512 characters |
|
||||
| Field | Type | Required | Constraints |
|
||||
| ---------------- | ------ | -------- | ----------------------------- |
|
||||
| `oidcIssuer` | string | Yes | HTTPS URL |
|
||||
| `oidcClientId` | string | Yes | 1–256 characters |
|
||||
| `vapidPublicKey` | string | Yes | 1–512 characters |
|
||||
| `appExternalUrl` | string | Yes | HTTPS URL, max 512 characters |
|
||||
|
||||
**Response 200**
|
||||
|
||||
@@ -187,10 +187,10 @@ Creates the first admin user (no OIDC identity yet, `claimed: false`) and valida
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| --------------- | ------ | -------- | ---------------- |
|
||||
| Field | Type | Required | Constraints |
|
||||
| --------------- | ------ | -------- | ------------------------------- |
|
||||
| `fastmailEmail` | string | Yes | Valid email, max 256 characters |
|
||||
| `appPassword` | string | Yes | 1–500 characters |
|
||||
| `appPassword` | string | Yes | 1–500 characters |
|
||||
|
||||
Zod validation errors for this route never echo received values (the app password is never included in error responses).
|
||||
|
||||
@@ -233,6 +233,7 @@ Pre-auth endpoint. Returns which authentication methods are currently enabled. U
|
||||
Validates a username + password against `local_credentials` and issues a signed `local-session` JWT cookie. Pre-auth — reachable without a session.
|
||||
|
||||
Rate limiting is per-username (not per-IP):
|
||||
|
||||
- 5 failures within 60 seconds → `429 Too Many Requests`
|
||||
- 10 cumulative failures → `423 Account Locked` (auto-expires after 15 minutes or on admin password reset)
|
||||
|
||||
@@ -244,10 +245,10 @@ Timing-oracle defense: `verifyPassword` (scrypt) is always called, even for unkn
|
||||
{ "username": "alice", "password": "hunter2" }
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| ---------- | ------ | -------- | ---------------- |
|
||||
| Field | Type | Required | Constraints |
|
||||
| ---------- | ------ | -------- | -------------------------- |
|
||||
| `username` | string | Yes | 1–128 characters (trimmed) |
|
||||
| `password` | string | Yes | 1–1000 characters |
|
||||
| `password` | string | Yes | 1–1000 characters |
|
||||
|
||||
Zod validation errors never echo received values.
|
||||
|
||||
@@ -296,14 +297,14 @@ Display name is derived from OIDC claims in priority order: `name` → `preferre
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
| -------------------- | ------- | ---------------------------------------------------------------- |
|
||||
| `id` | integer | Stable member ID |
|
||||
| `displayName` | string | Derived from OIDC claims or set by admin |
|
||||
| `color` | string | Member's assigned color (hex) |
|
||||
| `isAdmin` | boolean | Whether the member has the admin role |
|
||||
| `needsProviderSetup` | boolean | True when no Fastmail credential is stored for this member |
|
||||
| `hasLocalCredential` | boolean | True when a local username/password credential exists |
|
||||
| Field | Type | Description |
|
||||
| -------------------- | ------- | ---------------------------------------------------------- |
|
||||
| `id` | integer | Stable member ID |
|
||||
| `displayName` | string | Derived from OIDC claims or set by admin |
|
||||
| `color` | string | Member's assigned color (hex) |
|
||||
| `isAdmin` | boolean | Whether the member has the admin role |
|
||||
| `needsProviderSetup` | boolean | True when no Fastmail credential is stored for this member |
|
||||
| `hasLocalCredential` | boolean | True when a local username/password credential exists |
|
||||
|
||||
**Error responses:** `401` if the session is invalid.
|
||||
|
||||
@@ -323,11 +324,11 @@ Member self-service endpoint to set or rotate their own Fastmail CalDAV app pass
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| --------------- | ------ | -------- | ---------------- |
|
||||
| `providerType` | string | Yes | Must be `"caldav"` |
|
||||
| Field | Type | Required | Constraints |
|
||||
| --------------- | ------ | -------- | ------------------------------- |
|
||||
| `providerType` | string | Yes | Must be `"caldav"` |
|
||||
| `fastmailEmail` | string | Yes | Valid email, max 256 characters |
|
||||
| `appPassword` | string | Yes | 1–500 characters |
|
||||
| `appPassword` | string | Yes | 1–500 characters |
|
||||
|
||||
**Response 200** — `{ "ok": true }`
|
||||
|
||||
@@ -348,9 +349,9 @@ Self-service password change for local-auth members. Requires the current passwo
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| ----------------- | ------ | -------- | ---------------- |
|
||||
| `currentPassword` | string | Yes | 1+ characters |
|
||||
| Field | Type | Required | Constraints |
|
||||
| ----------------- | ------ | -------- | -------------------- |
|
||||
| `currentPassword` | string | Yes | 1+ characters |
|
||||
| `newPassword` | string | Yes | Minimum 8 characters |
|
||||
|
||||
**Response 200** — `{ "ok": true }`
|
||||
@@ -942,10 +943,10 @@ Creates a new local-auth member: inserts a `users` row and a `local_credentials`
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| ----------------- | ------ | -------- | ---------------- |
|
||||
| `displayName` | string | Yes | 1–256 characters |
|
||||
| `username` | string | Yes | 1–128 characters |
|
||||
| Field | Type | Required | Constraints |
|
||||
| ----------------- | ------ | -------- | -------------------- |
|
||||
| `displayName` | string | Yes | 1–256 characters |
|
||||
| `username` | string | Yes | 1–128 characters |
|
||||
| `initialPassword` | string | Yes | Minimum 8 characters |
|
||||
|
||||
Zod validation errors never echo received values (the initial password is never included in error responses).
|
||||
@@ -997,12 +998,12 @@ Validates and stores a Fastmail CalDAV app password for any household member. Pe
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| --------------- | ------- | -------- | ---------------- |
|
||||
| `userId` | integer | Yes | Positive integer |
|
||||
| `providerType` | string | Yes | Must be `"caldav"` |
|
||||
| Field | Type | Required | Constraints |
|
||||
| --------------- | ------- | -------- | ------------------------------- |
|
||||
| `userId` | integer | Yes | Positive integer |
|
||||
| `providerType` | string | Yes | Must be `"caldav"` |
|
||||
| `fastmailEmail` | string | Yes | Valid email, max 256 characters |
|
||||
| `appPassword` | string | Yes | 1–500 characters |
|
||||
| `appPassword` | string | Yes | 1–500 characters |
|
||||
|
||||
Zod validation errors and CalDAV validation failures return `400` with `{ "error": "Invalid request" }` — the app password is never echoed.
|
||||
|
||||
@@ -1022,7 +1023,7 @@ Lists all synced calendars with their shared-calendar designation.
|
||||
{
|
||||
"calendars": [
|
||||
{ "id": 1, "displayName": "Personal", "isShared": false },
|
||||
{ "id": 2, "displayName": "Family", "isShared": true }
|
||||
{ "id": 2, "displayName": "Family", "isShared": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1065,8 +1066,8 @@ Validates and upserts the household IANA timezone into `app_config`.
|
||||
{ "timezone": "America/Toronto" }
|
||||
```
|
||||
|
||||
| Field | Type | Required | Constraints |
|
||||
| ---------- | ------ | -------- | -------------------------- |
|
||||
| Field | Type | Required | Constraints |
|
||||
| ---------- | ------ | -------- | ------------------------------------ |
|
||||
| `timezone` | string | Yes | Valid IANA timezone, 1–64 characters |
|
||||
|
||||
**Response 200** — `{ "ok": true }`
|
||||
@@ -1105,17 +1106,17 @@ All error responses use a consistent JSON envelope.
|
||||
{ "error": "Human-readable message" }
|
||||
```
|
||||
|
||||
| HTTP Status | Meaning |
|
||||
| ----------- | ------------------------------------------------------------------------------ |
|
||||
| `400` | Invalid request parameters (e.g., malformed date window) |
|
||||
| `401` | Session missing or invalid |
|
||||
| HTTP Status | Meaning |
|
||||
| ----------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `400` | Invalid request parameters (e.g., malformed date window) |
|
||||
| `401` | Session missing or invalid |
|
||||
| `403` | Authenticated but not authorized (wrong owner, sharee attempted owner-only op, non-admin on admin route) |
|
||||
| `404` | Resource not found |
|
||||
| `409` | Conflict (e.g., duplicate username) |
|
||||
| `422` | Valid request but cannot be fulfilled (e.g., user has no calendar configured) |
|
||||
| `423` | Locked (setup already complete, or account locked after too many failed logins)|
|
||||
| `429` | Too many requests (login rate limit exceeded for this username) |
|
||||
| `503` | DB or downstream service unavailable |
|
||||
| `404` | Resource not found |
|
||||
| `409` | Conflict (e.g., duplicate username) |
|
||||
| `422` | Valid request but cannot be fulfilled (e.g., user has no calendar configured) |
|
||||
| `423` | Locked (setup already complete, or account locked after too many failed logins) |
|
||||
| `429` | Too many requests (login rate limit exceeded for this username) |
|
||||
| `503` | DB or downstream service unavailable |
|
||||
|
||||
Zod validation failures return `400` with a structured body from `@hono/zod-validator` rather than the `{ "error": "..." }` envelope. Exception: credential and password routes use a `noEchoHook` that always returns `{ "error": "Invalid request" }` to prevent echoing submitted secrets in error details.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user