CLAUDE.md shrinks to rules + status + index (175 lines, down from 476). Dev setup, prod deploy, and per-phase build records move to docs/. Gotchas regrouped by domain (auth, frontend, PWA, testing, backend, deploy, platform) and unnumbered so they don't drift as new ones land. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
49 lines
5.1 KiB
Markdown
49 lines
5.1 KiB
Markdown
# Fase 1 — what has been built
|
||
|
||
- `supabase/migrations/001_users.sql` — `users` table, `handle_new_user` trigger
|
||
- `supabase/migrations/002_collectives.sql` — `collectives`, `collective_members`, `collective_invitations`; auto-promote-oldest-admin trigger
|
||
- `supabase/migrations/003_rls.sql` — full RLS policies, `is_active_member`/`is_member`/`is_admin` helpers, `accept_invitation()` function
|
||
- `supabase/migrations/004_storage.sql` — `avatars` bucket (private, signed URLs)
|
||
- `apps/web/src/lib/supabase.ts` — Supabase singleton with PKCE flow
|
||
- `apps/web/src/lib/auth.ts` — `login()` / `logout()` via `supabase.auth.signInWithOAuth`
|
||
- `apps/web/src/lib/stores/auth.ts` — `currentUser`, `authLoading`, `isAuthenticated`, `displayName`
|
||
- `apps/web/src/lib/stores/collective.ts` — `currentCollective`, `userCollectives`, `collectiveMembers`
|
||
- `apps/web/src/lib/components/Avatar.svelte` — initials / emoji / upload with deterministic colour hash
|
||
- `apps/web/src/lib/components/ImageCropper.svelte` — lazy-loaded cropperjs, 1:1, 256×256 WebP output
|
||
- `apps/web/src/routes/+layout.svelte` — auth state listener, collective bootstrap
|
||
- `apps/web/src/routes/(app)/+layout.ts` — SSR disabled (`ssr: false`); no auth check here (see gotcha below)
|
||
- `apps/web/src/routes/(app)/+layout.svelte` — sidebar with collective switcher; auth redirect via `$effect`
|
||
- `apps/web/src/routes/auth/callback/+page.svelte` — PKCE code exchange
|
||
- `apps/web/src/routes/onboarding/+page.svelte` — create collective or join via link
|
||
- `apps/web/src/routes/(app)/collective/manage/+page.svelte` — members, roles, invite link generator
|
||
- `apps/web/src/routes/invitation/[token]/+page.svelte` — accept invitation (auth-aware)
|
||
- `apps/web/src/routes/(app)/settings/+page.svelte` — display name, avatar, language, sign out
|
||
- `apps/web/messages/en.json` + `es.json` — all Fase 1 + 2a strings
|
||
- `packages/types/src/database.ts` — manually seeded from migrations (update with `just db-types` once CLI is wired)
|
||
- `setup.sh` — idempotent local dev setup (prerequisites, .env, Docker, migrations, seed, Paraglide)
|
||
- `infra/kong-start.sh` — Kong container entrypoint; substitutes `${VAR}` placeholders in `kong.yml` before Kong starts (Kong 2.8 does not do this natively)
|
||
|
||
## Test suite — initial scaffolding
|
||
|
||
- `packages/test-utils/` — shared test infrastructure (Vitest integration tests)
|
||
- `package.json` — `@colectivo/test-utils`; depends on `@supabase/supabase-js`, `jose`, `pg`
|
||
- `vitest.config.ts` — loads root `.env` via `loadEnv`; single-fork mode (no parallelism races)
|
||
- `src/seed-constants.ts` — fixed UUIDs for all 5 seed users + collective + seed list
|
||
- `src/supabase-clients.ts` — `createClientAs(userId)` signs HS256 JWT with `SUPABASE_JWT_SECRET`; `createAdminClient()` uses service role key
|
||
- `src/db-helpers.ts` — `sql()` via raw `pg` Pool for direct DB manipulation (bypasses RLS); used to set `deleted_at = 8 days ago` etc.
|
||
- `tests/rls-isolation.test.ts` — F-series: Eva (non-member) sees zero rows, all writes blocked
|
||
- `tests/rls-collective.test.ts` — B-series: collective read/update/invite by role
|
||
- `tests/rls-lists.test.ts` — C-series: list CRUD, soft-delete, trash window, cross-collective isolation
|
||
- `tests/rls-items.test.ts` — D-series: item CRUD by role, cross-collective item isolation
|
||
- `tests/rls-frequency.test.ts` — E-series: frequency read-only + trigger populates on INSERT
|
||
- `supabase/tests/*.sql` each start with `CREATE EXTENSION IF NOT EXISTS pgtap;` so they're idempotent on a fresh DB
|
||
- `001_accept_invitation.sql` — `accept_invitation()` happy path + not_found / expired / already_used; uses `set_config('request.jwt.claim.sub', …)` to set `auth.uid()` for the test session, and looks up by `token` (not `id`)
|
||
- `002_item_frequency_trigger.sql` — trigger normalizes name with `lower(trim(...))` and increments `use_count` on repeat INSERTs. RLS write-blocks are covered by the Vitest suite instead (postgres superuser bypasses RLS here)
|
||
- `003_promote_on_admin_leave.sql` — deletes a synthetic admin from `public.users` to fire the `BEFORE DELETE` trigger and asserts the oldest remaining member is promoted
|
||
- `apps/web/playwright.config.ts` — Playwright config; `globalSetup` is a Keycloak health check (not a session cache), `reuseExistingServer: true`
|
||
- `apps/web/tests/fixtures/users.ts` — seed user constants (Ana, Borja, Carmen, David, Eva)
|
||
- `apps/web/tests/fixtures/login.ts` — `loginAs(page, user)` runs the real OAuth flow (Keycloak → GoTrue PKCE) and waits for the session token to land in localStorage. Each test that needs auth calls this in `beforeEach`; cached `storageState` is NOT used (see the Playwright storageState gotcha in `CLAUDE.md`)
|
||
- `apps/web/tests/e2e/auth.test.ts` — A-series: unauthenticated redirect, full login, collective visible in sidebar, sign-out, guest login
|
||
- `apps/web/tests/e2e/lists.test.ts` — C-series: create (with reload-persistence check), soft-delete → appears in trash drawer, archive, guest read-only
|
||
- `apps/web/tests/e2e/items.test.ts` — D-series: seed list renders, add item (with reload-persistence check), toggle check, desktop-hover delete, frequency suggestions, guest read-only
|