Spec updates

This commit is contained in:
jableader 2025-11-01 17:31:03 +11:00
parent b0c749b17d
commit 52649e812a

View file

@ -104,39 +104,27 @@ This plan is adapted to the existing codebase, focusing on refactoring rather th
---
## 0. Current State (Nov 2025) and Gap Analysis
## 0. Current State (Nov 1, 2025)
Current implementation is single-tenant with username-based login and no household context.
All core features are migrated to multi-tenancy with path-scoped endpoints and token-based auth. The codebase no longer uses the `X-Household-Slug` header. Tests and type checks are fully green.
What exists today
What exists now
- Auth
- `src/api/auth.ts`: username login (`login(username: string)`), `currentUser()` via cookie `user_id`.
- `src/composables/useAuth.ts`: stores `Person | null`, exposes `login(username)` and `loadUser()`.
- `src/components/LoginPage.vue`: lists persons and logs in by selected person name.
- `src/api/auth.ts`: email/password login and register; token-only refresh in `currentUser()` which then loads `/api/v1/users/me/households`.
- `src/composables/useAuth.ts`: manages `user`, `households`, and `activeHousehold`; exposes login/logout/createAccount and `fetchHouseholds()`.
- Routing
- `src/router/index.ts`: flat routes (`/recipes`, `/shopping`, `/mealplan`); no `/:householdSlug` nesting or redirects.
- Guard checks `requiresAuth` only; no household awareness.
- `src/router/index.ts`: feature-flagged nesting under `/:householdSlug/...`; public routes include `/create-account`, `/welcome`, and `/invitations/accept`.
- Guard fetches households, redirects root `/` to the first household's `mealplan`, and uses memory history in tests (hash in browser).
- SDK/API
- `src/api/sdk.ts`: calls `/api/v1/...` without household context.
- Generated OpenAPI types do not include household slug in paths; shapes are single-tenant.
- `src/api/sdk.ts`: recipes, meals, and shopping are migrated to `/api/v1/households/{householdSlug}/...` typed endpoints. Persons and parse use temporary raw fetch endpoints where OpenAPI lacks coverage.
- `src/api/client.ts`: Authorization header provider only; household header injection removed.
- Domain & UI
- Identity revolves around `Person`; no household model, switcher, or settings views.
- Member arrays (`chefs`, `consumers`, `cleanup`) normalized to MemberRef `{ id, displayName }` with decoders handling legacy shapes gracefully.
- Invitation Accept flow implemented; Household Settings supports sending invitations and listing members (members via temporary raw fetch path-scoped endpoint until typed spec lands).
Gaps vs requirements
- Authentication & onboarding
- Implemented: JWT `loginWithPassword`, `logout`, account creation via `createAccount` with token provider wiring.
- UI: `LoginPage.vue` shows email/password form under feature flag; `CreateAccount.vue` view implemented; `Welcome.vue` creates household; Invitation Accept implemented; Google OAuth pending.
- Routing & URL-based tenancy
- Partial: Feature-flagged nesting implemented; still need guard logic for fetching households and redirects.
- `useHousehold.ts` and `HouseholdSwitcher.vue` added; further wiring to fetch households pending.
- API boundary
- No household scoping passed to backend. Need a typed strategy (prefer header parameter) without breaking OpenAPI typing.
- Cleanup
- `persons` used across login and tests; must be deprecated in favor of authenticated `user` and their households.
Assumptions and constraints
- Preserve strict TS (no `any`/`unknown` in app code) and keep OpenAPI as source of truth.
- Do not rewrite typed API paths in code; prefer header or OpenAPI param for household scoping.
Status of tests and typing
- All tests pass: 27 files, 48 tests.
- `tsc` and `vue-tsc` pass with no errors.
---
@ -144,19 +132,11 @@ Assumptions and constraints
The backend OpenAPI has been updated and now exposes `householdSlug` as a path parameter for scoped endpoints (recipes, meals, shopping, invitations, whoami). Auth endpoints are fully typed (login/register/refresh/logout). Invitation acceptance remains a global endpoint (`/api/v1/invitations/accept`) with a typed operation.
Implications and actions:
- Replace the temporary `X-Household-Slug` header injection with typed path parameters.
- Migrate all API calls to pass `{ params: { path: { householdSlug } } }` where required.
- Remove `setHouseholdSlugProvider` usage once the migration is complete.
- Auth refresh now returns only `{ accessToken, tokenType }` (no `user` object). The `currentUser()` flow must:
1) Call refresh to obtain a token and set the Authorization provider.
2) Load user/household context via typed endpoints (e.g., `GET /api/v1/users/me/households`).
3) Optional: Use `GET /api/v1/households/{householdSlug}/whoami` to validate membership for the active route.
- Invitations:
- Create invitation is now typed at `POST /api/v1/households/{householdSlug}/invitations`.
- Accept invitation is typed at `POST /api/v1/invitations/accept`.
- Remove temporary raw fetch usage for invitations and switch to the generated client.
- Household members listing endpoint is still not present in OpenAPI; continue using the temporary fetch wrapper until the backend exposes it.
Implications and actions (completed):
- Removed `X-Household-Slug` and migrated to typed path parameters across recipes, meals, and shopping.
- `currentUser()` updated to token-only refresh and household loading.
- Invitations: sending is typed under household scope; accept is typed globally.
- Members listing is temporarily fetched via a path-scoped raw endpoint until OpenAPI includes it.
---
@ -166,7 +146,7 @@ Progress Log (Nov 1, 2025)
- Implemented `loginWithPassword`, `logout`, and stubs in `src/api/auth.ts` to satisfy tests.
- Refactored `useAuth` to add households and activeHousehold state, plus `loginWithPassword` and `logout`. Tests added and passing.
- Added router tests and implemented feature-flagged nested routes and new public routes. Placeholders for onboarding/invitations added.
- Implemented `useHousehold.ts`, header + auth providers in API client, minimal `HouseholdSwitcher.vue`, and mounted it. Added a header injection test.
- Implemented `useHousehold.ts`, Authorization provider in API client, minimal `HouseholdSwitcher.vue`, and mounted it. Replaced header injection test with path-scoped assertion.
- Implemented JWT login/register in `auth.ts` and wired token to client provider. `useAuth` updated with households fetching. `currentUser` refactored to token-only refresh plus households load.
- Router guard updated to handle public/multitenant routing and redirects.
- Added `useAuth.createAccount` with state update and tests for it; implemented `CreateAccount.vue` with form and navigation.
@ -174,11 +154,11 @@ Progress Log (Nov 1, 2025)
- Next: Implement Household Settings (invite members form), then remove legacy Person UI.
- Added a Settings link to `HouseholdSwitcher.vue` to surface the `household-settings` route for easier discovery.
- Implemented Household Settings invite form and members list UI. `src/views/HouseholdSettings.vue` now loads members via a temporary `listMembers()` in `src/api/households.ts` using the raw fetch helper. When the backend exposes a typed endpoint, we will swap to the generated client.
- Fixed router tests by using memory history in non-browser environments to avoid relying on `window.location` during unit tests.
- Router uses memory history in tests to avoid relying on `window.location`.
- Backend updated OpenAPI and codegen has been run:
- Many endpoints are now path-scoped with `{householdSlug}` (recipes, meals, shopping, invitations (create), whoami).
- Auth endpoints (login/register/refresh/logout) are fully typed; `refresh` returns only `{ accessToken, tokenType }`.
- Action: begin migrating API usage to typed path parameters and remove temporary header injection and fetch helpers.
- Completed migration to typed path parameters; header injection removed; only small raw fetch helpers remain for endpoints not yet in OpenAPI (persons, parse, members list).
---
@ -208,13 +188,12 @@ Router
API client
- `src/api/client.ts`:
- Previous: header injection (`X-Household-Slug`) from a configurable getter to scope requests.
- Now: migrate calls to use typed `{ params: { path: { householdSlug } } }` and remove the `X-Household-Slug` header provider.
- Authorization provider remains as-is, fed by the JWT token from login/refresh.
- Uses typed `{ params: { path: { householdSlug } } }` across SDK; `X-Household-Slug` header provider removed.
- Authorization provider remains as-is, fed by JWT token from login/refresh.
SDK
- `src/api/sdk.ts`:
- No path changes; ensure all calls work with new auth and household header.
- Recipes, meals, shopping fully path-scoped to households. Persons/parse remain via raw fetch until typed coverage.
UI
- Login Page: Refactored to show email/password form when multitenant flag is enabled; legacy person list retained otherwise. Link to Create Account added.
@ -224,10 +203,9 @@ UI
- Adjust pages that call SDK/API to pass `{ householdSlug }` path params once client services are migrated.
Tests
- Update MSW handlers/tests to assume JWT auth and household header.
- Add tests for router guards, invitation acceptance, and household switching.
- Add API tests for invitations (accept/send) and members listing header behavior. Router tests run under memory history in tests.
- Update tests to assert that calls pass `householdSlug` via typed params instead of relying on an injected header.
- MSW handlers updated for path-scoped endpoints; Authorization header assertions retained where relevant.
- Router tests run under memory history.
- Replaced header injection test with path-scoped recipe list test.
---
@ -245,24 +223,25 @@ Tests
## Migration Plan & Feature Flag
- Optional `MULTITENANT_ENABLED` flag for staged rollout of routes and UI.
- Keep legacy login until backend endpoints are ready; hide persons UI once households exist for a user.
- `VUE_APP_MULTITENANT_ENABLED` flag controls nested household routes. With the migration complete, keep this flag for rollout control; default can be enabled once backend is stable across environments.
---
## Acceptance Criteria (Summary)
- Users can create accounts, login (email/password, Google), logout, and refresh sessions.
- Users can create accounts, login (email/password), logout, and refresh sessions (token-only).
- All routes operate under `/:householdSlug` with correct redirects and deep link support.
- Active household is selectable and visible; API calls are correctly scoped.
- Invitation token acceptance adds membership and navigates to the household dashboard.
- Legacy persons login removed from UI; tests updated and passing.
- Active household is selectable and visible; API calls are correctly scoped via path params.
- Invitation token acceptance adds membership and navigates appropriately.
- Legacy persons identity is no longer used in auth; MemberRef used in meal UIs; tests updated and passing.
Google OAuth is planned next.
---
## Open Questions
## Open Questions / Next Steps
- Backend: path vs header vs cookie for `householdSlug`? Confirm to finalize client strategy.
- Exact OpenAPI shapes for `User`, `Household`, `Invitation` endpoints.
- Google OAuth flow pattern (token exchange vs redirect).
- JWT storage medium per security guidance (cookie vs localStorage).
- Add a user profile endpoint and load it post-refresh to populate `currentUser()` with real data instead of a placeholder.
- Replace temporary raw fetch calls (persons, parse, members listing) with typed endpoints when available.
- Implement Google OAuth login and account creation flows.
- Review `MyShoppingPage` usage and remove legacy `getMyShoppingList/saveMyShoppingList` stubs when UI is refactored or removed.