fix(auth+pwa): resolve stuck-loading race condition and PWA build error
Auth: `getSession()` in a SvelteKit load function races with Supabase's async localStorage init and can return null for a valid session, triggering a spurious login() redirect. Moved auth redirect to (app)/+layout.svelte via $effect, which correctly waits for onAuthStateChange to fire. Also fixed collective data never loading on page refresh (INITIAL_SESSION event, not SIGNED_IN). PWA: SvelteKit blocks Workbox imports in src/service-worker.ts, preventing @vite-pwa/sveltekit from finding the compiled SW output. Switched to generateSW strategy (plugin auto-generates the SW). Custom SW deferred to Fase 2b using src/sw.ts (a filename SvelteKit does not intercept). Docs: added gotchas 6–8 to CLAUDE.md covering both root causes so they are not reintroduced. Updated plan/fase-2b with the sw.ts workaround note. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,73 @@
|
||||
-- seed.sql — development seed data
|
||||
-- Mirrors Keycloak test users (UUIDs must match realm-export.json)
|
||||
-- Password for all users: test1234
|
||||
-- UUIDs are fixed and match both keycloak/realm-export.json and auth.users below.
|
||||
-- Password for all Keycloak test users: test1234
|
||||
|
||||
-- ── Users ─────────────────────────────────────────────────────────────────────
|
||||
-- Inserted into the public.users table (synced from Keycloak via trigger in Fase 1)
|
||||
-- For Fase 0 we insert directly since the trigger isn't written yet.
|
||||
-- ── GoTrue auth.users — pre-seed so auth.uid() returns the same UUIDs ────────
|
||||
-- GoTrue normally generates its own UUIDs on first OIDC login. By pre-seeding
|
||||
-- auth.users + auth.identities with the known Keycloak sub UUIDs, GoTrue finds
|
||||
-- the existing identity on login and reuses the same UUID. This keeps auth.uid()
|
||||
-- consistent with all foreign keys in public.* tables.
|
||||
INSERT INTO auth.users (
|
||||
instance_id, id, aud, role, email,
|
||||
email_confirmed_at,
|
||||
-- Token fields must be '' not NULL; GoTrue's Go struct uses string (not *string)
|
||||
confirmation_token, recovery_token, email_change_token_new, email_change,
|
||||
raw_app_meta_data, raw_user_meta_data,
|
||||
is_super_admin, created_at, updated_at
|
||||
)
|
||||
VALUES
|
||||
('00000000-0000-0000-0000-000000000000', '11111111-1111-1111-1111-111111111111', 'authenticated', 'authenticated', 'ana@dev.local',
|
||||
now(), '', '', '', '',
|
||||
'{"provider":"keycloak","providers":["keycloak"]}',
|
||||
'{"full_name":"Ana García","email":"ana@dev.local","email_verified":true}',
|
||||
false, now(), now()),
|
||||
('00000000-0000-0000-0000-000000000000', '22222222-2222-2222-2222-222222222222', 'authenticated', 'authenticated', 'borja@dev.local',
|
||||
now(), '', '', '', '',
|
||||
'{"provider":"keycloak","providers":["keycloak"]}',
|
||||
'{"full_name":"Borja López","email":"borja@dev.local","email_verified":true}',
|
||||
false, now(), now()),
|
||||
('00000000-0000-0000-0000-000000000000', '33333333-3333-3333-3333-333333333333', 'authenticated', 'authenticated', 'carmen@dev.local',
|
||||
now(), '', '', '', '',
|
||||
'{"provider":"keycloak","providers":["keycloak"]}',
|
||||
'{"full_name":"Carmen Martínez","email":"carmen@dev.local","email_verified":true}',
|
||||
false, now(), now()),
|
||||
('00000000-0000-0000-0000-000000000000', '44444444-4444-4444-4444-444444444444', 'authenticated', 'authenticated', 'david@dev.local',
|
||||
now(), '', '', '', '',
|
||||
'{"provider":"keycloak","providers":["keycloak"]}',
|
||||
'{"full_name":"David Fernández","email":"david@dev.local","email_verified":true}',
|
||||
false, now(), now()),
|
||||
('00000000-0000-0000-0000-000000000000', '55555555-5555-5555-5555-555555555555', 'authenticated', 'authenticated', 'eva@dev.local',
|
||||
now(), '', '', '', '',
|
||||
'{"provider":"keycloak","providers":["keycloak"]}',
|
||||
'{"full_name":"Eva Ruiz","email":"eva@dev.local","email_verified":true}',
|
||||
false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- ── GoTrue auth.identities — link Keycloak provider_id to the pre-seeded UUIDs ─
|
||||
INSERT INTO auth.identities (
|
||||
provider_id, user_id, identity_data, provider,
|
||||
created_at, updated_at
|
||||
)
|
||||
VALUES
|
||||
('11111111-1111-1111-1111-111111111111', '11111111-1111-1111-1111-111111111111',
|
||||
'{"sub":"11111111-1111-1111-1111-111111111111","iss":"http://keycloak:8080/realms/colectivo","email":"ana@dev.local","full_name":"Ana García","email_verified":true}',
|
||||
'keycloak', now(), now()),
|
||||
('22222222-2222-2222-2222-222222222222', '22222222-2222-2222-2222-222222222222',
|
||||
'{"sub":"22222222-2222-2222-2222-222222222222","iss":"http://keycloak:8080/realms/colectivo","email":"borja@dev.local","full_name":"Borja López","email_verified":true}',
|
||||
'keycloak', now(), now()),
|
||||
('33333333-3333-3333-3333-333333333333', '33333333-3333-3333-3333-333333333333',
|
||||
'{"sub":"33333333-3333-3333-3333-333333333333","iss":"http://keycloak:8080/realms/colectivo","email":"carmen@dev.local","full_name":"Carmen Martínez","email_verified":true}',
|
||||
'keycloak', now(), now()),
|
||||
('44444444-4444-4444-4444-444444444444', '44444444-4444-4444-4444-444444444444',
|
||||
'{"sub":"44444444-4444-4444-4444-444444444444","iss":"http://keycloak:8080/realms/colectivo","email":"david@dev.local","full_name":"David Fernández","email_verified":true}',
|
||||
'keycloak', now(), now()),
|
||||
('55555555-5555-5555-5555-555555555555', '55555555-5555-5555-5555-555555555555',
|
||||
'{"sub":"55555555-5555-5555-5555-555555555555","iss":"http://keycloak:8080/realms/colectivo","email":"eva@dev.local","full_name":"Eva Ruiz","email_verified":true}',
|
||||
'keycloak', now(), now())
|
||||
ON CONFLICT (provider_id, provider) DO NOTHING;
|
||||
|
||||
-- ── public.users — synced from auth.users via trigger on login ────────────────
|
||||
-- Pre-seeded here too so the data is available before first login.
|
||||
INSERT INTO public.users (id, email, display_name, language, avatar_type)
|
||||
VALUES
|
||||
('11111111-1111-1111-1111-111111111111', 'ana@dev.local', 'Ana García', 'es', 'initials'),
|
||||
@@ -31,30 +93,39 @@ VALUES
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '44444444-4444-4444-4444-444444444444', 'guest')
|
||||
ON CONFLICT (collective_id, user_id) DO NOTHING;
|
||||
|
||||
-- ── Sample shopping list ───────────────────────────────────────────────────────
|
||||
INSERT INTO public.shopping_lists (id, collective_id, name, status, created_by)
|
||||
VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Weekly shop', 'active', '11111111-1111-1111-1111-111111111111')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
-- ── Phase 2a seed data (shopping lists, items, frequency) ─────────────────────
|
||||
-- Guarded: only inserted when the tables exist (Phase 2a migrations applied).
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = 'shopping_lists') THEN
|
||||
INSERT INTO public.shopping_lists (id, collective_id, name, status, created_by)
|
||||
VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Weekly shop', 'active', '11111111-1111-1111-1111-111111111111')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO public.shopping_items (list_id, name, quantity, unit, sort_order, created_by)
|
||||
VALUES
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Milk', 2, 'L', 1, '11111111-1111-1111-1111-111111111111'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Bread', 1, NULL, 2, '22222222-2222-2222-2222-222222222222'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Eggs', 1, 'doz', 3, '11111111-1111-1111-1111-111111111111'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Coffee', 1, NULL, 4, '33333333-3333-3333-3333-333333333333'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Apples', 6, NULL, 5, '22222222-2222-2222-2222-222222222222')
|
||||
ON CONFLICT DO NOTHING;
|
||||
INSERT INTO public.shopping_items (list_id, name, quantity, unit, sort_order, created_by)
|
||||
VALUES
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Milk', 2, 'L', 1, '11111111-1111-1111-1111-111111111111'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Bread', 1, NULL, 2, '22222222-2222-2222-2222-222222222222'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Eggs', 1, 'doz', 3, '11111111-1111-1111-1111-111111111111'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Coffee', 1, NULL, 4, '33333333-3333-3333-3333-333333333333'),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Apples', 6, NULL, 5, '22222222-2222-2222-2222-222222222222')
|
||||
ON CONFLICT DO NOTHING;
|
||||
END IF;
|
||||
|
||||
-- ── Item frequency baseline (from pre-existing items above) ────────────────────
|
||||
INSERT INTO public.item_frequency (collective_id, name, use_count, last_used_at)
|
||||
VALUES
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'milk', 5, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'bread', 4, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'eggs', 3, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'coffee', 3, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'apples', 2, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'butter', 2, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'yogurt', 1, now())
|
||||
ON CONFLICT (collective_id, name) DO UPDATE SET
|
||||
use_count = EXCLUDED.use_count,
|
||||
last_used_at = EXCLUDED.last_used_at;
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = 'item_frequency') THEN
|
||||
INSERT INTO public.item_frequency (collective_id, name, use_count, last_used_at)
|
||||
VALUES
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'milk', 5, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'bread', 4, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'eggs', 3, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'coffee', 3, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'apples', 2, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'butter', 2, now()),
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'yogurt', 1, now())
|
||||
ON CONFLICT (collective_id, name) DO UPDATE SET
|
||||
use_count = EXCLUDED.use_count,
|
||||
last_used_at = EXCLUDED.last_used_at;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
Reference in New Issue
Block a user