feat(fase-1): auth, collective management, and DB migrations
- SQL migrations: users, collectives, collective_members, collective_invitations,
full RLS policies, is_active_member/is_member/is_admin helpers,
accept_invitation SECURITY DEFINER function, admin auto-promote trigger,
avatars storage bucket
- Auth refactor: replace keycloak-js with Supabase OAuth (GoTrue OIDC proxy,
Option B). signInWithOAuth({ provider: 'keycloak' }) + PKCE flow
- GoTrue config: GOTRUE_EXTERNAL_KEYCLOAK_URL + GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI
added to docker-compose.dev.yml; Keycloak realm updated with GoTrue callback URI
- New routes: /auth/callback, /invitation/[token], /(app)/collective/manage
- Functional onboarding: create collective or join via invite link
- Full settings page: display name (debounced), avatar (initials/emoji/upload),
language switcher, Keycloak account console link
- Components: Avatar.svelte (initials/emoji/upload with fallback),
ImageCropper.svelte (cropperjs, 1:1 crop, lazy-loaded)
- i18n: added all Fase 1 message keys (en + es); renamed 'delete' → 'action_delete'
(JS reserved word); fixed plugin-m-function-matcher major-version URL format
- Database types: manually seeded packages/types/src/database.ts from migrations
- .env.development: committed public dev defaults for SvelteKit type checking
- CLAUDE.md: documented /etc/hosts requirement for keycloak hostname
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
39
apps/web/src/routes/auth/callback/+page.svelte
Normal file
39
apps/web/src/routes/auth/callback/+page.svelte
Normal file
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { getSupabase } from '$lib/supabase';
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
onMount(async () => {
|
||||
const code = $page.url.searchParams.get('code');
|
||||
|
||||
if (!code) {
|
||||
error = 'No authorization code received.';
|
||||
return;
|
||||
}
|
||||
|
||||
const { error: exchangeError } = await getSupabase().auth.exchangeCodeForSession(code);
|
||||
|
||||
if (exchangeError) {
|
||||
error = exchangeError.message;
|
||||
return;
|
||||
}
|
||||
|
||||
// onAuthStateChange in root layout handles post-login routing
|
||||
goto('/');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen items-center justify-center bg-background">
|
||||
{#if error}
|
||||
<div class="max-w-sm text-center">
|
||||
<p class="mb-4 text-sm text-red-600">{error}</p>
|
||||
<a href="/" class="text-sm text-slate-600 underline">{m.auth_back_to_home()}</a>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm text-text-secondary">{m.loading()}</p>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user