mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:01:08 +02:00
feat(picture): migrate to mana-core auth and update AI models
Web app: - Replace Supabase auth with @manacore/shared-auth - Add Svelte 5 runes auth store (auth.svelte.ts) - Remove deprecated auth files (LoginForm, SignupForm, authService, supabase.ts) - Update all components to use authStore Backend: - Update AI models: flux-schnell, seedream-3, nano-banana - Remove old models: sdxl, flux-pro - Make models endpoint public (no auth required) - Add unique constraint to model name 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
468b7b785c
commit
5e15f57816
25 changed files with 177 additions and 617 deletions
|
|
@ -2,7 +2,7 @@ import { pgTable, uuid, text, timestamp, boolean, integer, real } from 'drizzle-
|
|||
|
||||
export const models = pgTable('models', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
name: text('name').notNull(),
|
||||
name: text('name').notNull().unique(),
|
||||
displayName: text('display_name').notNull(),
|
||||
description: text('description'),
|
||||
replicateId: text('replicate_id').notNull(),
|
||||
|
|
|
|||
|
|
@ -1,63 +1,85 @@
|
|||
import * as dotenv from 'dotenv';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { getDb, closeConnection } from './connection';
|
||||
import { models } from './schema';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const defaultModels = [
|
||||
{
|
||||
name: 'sdxl',
|
||||
displayName: 'Stable Diffusion XL',
|
||||
description: 'High-quality image generation with excellent prompt adherence',
|
||||
replicateId: 'stability-ai/sdxl',
|
||||
version: '39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b',
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 1024,
|
||||
defaultSteps: 25,
|
||||
defaultGuidanceScale: 7.5,
|
||||
supportsNegativePrompt: true,
|
||||
supportsImg2Img: true,
|
||||
supportsSeed: true,
|
||||
isActive: true,
|
||||
isDefault: true,
|
||||
sortOrder: 0,
|
||||
estimatedTimeSeconds: 15,
|
||||
},
|
||||
{
|
||||
name: 'flux-schnell',
|
||||
displayName: 'FLUX Schnell',
|
||||
description: 'Fast image generation with good quality',
|
||||
description:
|
||||
'Schnellstes Modell von Black Forest Labs - generiert hochwertige Bilder in nur 1-4 Schritten. Ideal für schnelle Iterationen.',
|
||||
replicateId: 'black-forest-labs/flux-schnell',
|
||||
version: 'f2ab8a5bfe79f02f0789a146cf5e73d2a4ff2684a98c2b303d1e1ff3814271db',
|
||||
version: 'c846a69991daf4c0e5d016514849d14ee5b2e6846ce6b9d6f21369e564cfe51e',
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 1024,
|
||||
defaultSteps: 4,
|
||||
defaultGuidanceScale: 0,
|
||||
minWidth: 256,
|
||||
minHeight: 256,
|
||||
maxWidth: 1440,
|
||||
maxHeight: 1440,
|
||||
maxSteps: 4,
|
||||
supportsNegativePrompt: false,
|
||||
supportsImg2Img: false,
|
||||
supportsSeed: true,
|
||||
isActive: true,
|
||||
isDefault: true,
|
||||
sortOrder: 0,
|
||||
costPerGeneration: 0.003,
|
||||
estimatedTimeSeconds: 3,
|
||||
},
|
||||
{
|
||||
name: 'seedream-3',
|
||||
displayName: 'Seedream 3',
|
||||
description:
|
||||
'ByteDances hochauflösendes Modell mit nativer 2K-Ausgabe. Exzellente Text-Darstellung und fotorealistische Portraits.',
|
||||
replicateId: 'bytedance/seedream-3',
|
||||
version: 'ed344813bc9f4996be6de4febd8b9c14c7849ad7b21ab047572e3620ee374ee7',
|
||||
defaultWidth: 2048,
|
||||
defaultHeight: 2048,
|
||||
defaultSteps: 1, // Model handles steps internally
|
||||
defaultGuidanceScale: 2.5,
|
||||
minWidth: 512,
|
||||
minHeight: 512,
|
||||
maxWidth: 2048,
|
||||
maxHeight: 2048,
|
||||
maxSteps: 1,
|
||||
supportsNegativePrompt: false,
|
||||
supportsImg2Img: false,
|
||||
supportsSeed: true,
|
||||
isActive: true,
|
||||
isDefault: false,
|
||||
sortOrder: 1,
|
||||
costPerGeneration: 0.03,
|
||||
estimatedTimeSeconds: 5,
|
||||
},
|
||||
{
|
||||
name: 'flux-pro',
|
||||
displayName: 'FLUX Pro',
|
||||
description: 'Professional quality image generation',
|
||||
replicateId: 'black-forest-labs/flux-pro',
|
||||
version: '7d6fbcd3da3f4e1c1c08d8ab0e7a4c2e0e5e3c9e8f8e8e8e8e8e8e8e8e8e8e8e',
|
||||
name: 'nano-banana',
|
||||
displayName: 'Nano Banana',
|
||||
description:
|
||||
'Googles multimodales Gemini-Modell für Bildgenerierung und -bearbeitung. Beste Textdarstellung in Bildern und Charakter-Konsistenz.',
|
||||
replicateId: 'google/nano-banana',
|
||||
version: 'd05a591283da31be3eea28d5634ef9e26989b351718b6489bd308426ebd0a3e8',
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 1024,
|
||||
defaultSteps: 25,
|
||||
defaultGuidanceScale: 3.5,
|
||||
defaultSteps: 1, // Model handles steps internally
|
||||
defaultGuidanceScale: 0,
|
||||
minWidth: 512,
|
||||
minHeight: 512,
|
||||
maxWidth: 2048,
|
||||
maxHeight: 2048,
|
||||
maxSteps: 1,
|
||||
supportsNegativePrompt: false,
|
||||
supportsImg2Img: false,
|
||||
supportsSeed: true,
|
||||
supportsImg2Img: true, // Supports image editing
|
||||
supportsSeed: false, // No seed parameter exposed
|
||||
isActive: true,
|
||||
isDefault: false,
|
||||
sortOrder: 2,
|
||||
estimatedTimeSeconds: 20,
|
||||
costPerGeneration: 0.039,
|
||||
estimatedTimeSeconds: 3,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -69,12 +91,58 @@ async function seed() {
|
|||
|
||||
const db = getDb(databaseUrl);
|
||||
|
||||
console.log('Clearing old models...');
|
||||
// Remove old models that are not in the new list
|
||||
const oldModelNames = ['sdxl', 'flux-pro'];
|
||||
for (const name of oldModelNames) {
|
||||
try {
|
||||
await db.delete(models).where(eq(models.name, name));
|
||||
console.log(` - Removed: ${name}`);
|
||||
} catch (error) {
|
||||
console.error(` - Error removing ${name}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Seeding models...');
|
||||
|
||||
for (const model of defaultModels) {
|
||||
try {
|
||||
await db.insert(models).values(model).onConflictDoNothing();
|
||||
console.log(` - ${model.displayName}`);
|
||||
// Check if model exists
|
||||
const existing = await db.select().from(models).where(eq(models.name, model.name));
|
||||
if (existing.length > 0) {
|
||||
// Update existing
|
||||
await db
|
||||
.update(models)
|
||||
.set({
|
||||
displayName: model.displayName,
|
||||
description: model.description,
|
||||
replicateId: model.replicateId,
|
||||
version: model.version,
|
||||
defaultWidth: model.defaultWidth,
|
||||
defaultHeight: model.defaultHeight,
|
||||
defaultSteps: model.defaultSteps,
|
||||
defaultGuidanceScale: model.defaultGuidanceScale,
|
||||
minWidth: model.minWidth,
|
||||
minHeight: model.minHeight,
|
||||
maxWidth: model.maxWidth,
|
||||
maxHeight: model.maxHeight,
|
||||
maxSteps: model.maxSteps,
|
||||
supportsNegativePrompt: model.supportsNegativePrompt,
|
||||
supportsImg2Img: model.supportsImg2Img,
|
||||
supportsSeed: model.supportsSeed,
|
||||
isActive: model.isActive,
|
||||
isDefault: model.isDefault,
|
||||
sortOrder: model.sortOrder,
|
||||
costPerGeneration: model.costPerGeneration,
|
||||
estimatedTimeSeconds: model.estimatedTimeSeconds,
|
||||
})
|
||||
.where(eq(models.name, model.name));
|
||||
console.log(` - Updated: ${model.displayName}`);
|
||||
} else {
|
||||
// Insert new
|
||||
await db.insert(models).values(model);
|
||||
console.log(` - Added: ${model.displayName}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(` - Error seeding ${model.displayName}:`, error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
|
||||
import { Controller, Get, Param } from '@nestjs/common';
|
||||
import { ModelService } from './model.service';
|
||||
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
|
||||
|
||||
@Controller('models')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class ModelController {
|
||||
constructor(private readonly modelService: ModelService) {}
|
||||
|
||||
// Models are public - no auth required
|
||||
@Get()
|
||||
async getActiveModels() {
|
||||
return this.modelService.getActiveModels();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"clean": "rm -rf .svelte-kit build node_modules"
|
||||
},
|
||||
"dependencies": {
|
||||
"@manacore/shared-auth": "workspace:*",
|
||||
"@manacore/shared-auth-ui": "workspace:*",
|
||||
"@manacore/shared-branding": "workspace:*",
|
||||
"@manacore/shared-i18n": "workspace:*",
|
||||
|
|
@ -26,7 +27,6 @@
|
|||
"@manacore/shared-ui": "workspace:*",
|
||||
"@picture/design-tokens": "workspace:*",
|
||||
"@picture/shared": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.38.4",
|
||||
"konva": "^10.0.2",
|
||||
"posthog-js": "^1.273.1",
|
||||
"svelte-i18n": "^4.0.1"
|
||||
|
|
|
|||
|
|
@ -1,140 +0,0 @@
|
|||
<script lang="ts">
|
||||
import Button from '../ui/Button.svelte';
|
||||
import Input from '../ui/Input.svelte';
|
||||
import Card from '../ui/Card.svelte';
|
||||
import { supabase } from '$lib/supabase';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let email = $state('');
|
||||
let password = $state('');
|
||||
let loading = $state(false);
|
||||
let error = $state('');
|
||||
|
||||
async function handleLogin() {
|
||||
if (!email || !password) {
|
||||
error = 'Please fill in all fields';
|
||||
return;
|
||||
}
|
||||
|
||||
loading = true;
|
||||
error = '';
|
||||
|
||||
const { data, error: authError } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
loading = false;
|
||||
|
||||
if (authError) {
|
||||
error = authError.message;
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.session) {
|
||||
goto('/app/gallery');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGoogleLogin() {
|
||||
loading = true;
|
||||
const { error: authError } = await supabase.auth.signInWithOAuth({
|
||||
provider: 'google',
|
||||
options: {
|
||||
redirectTo: `${window.location.origin}/app/gallery`,
|
||||
},
|
||||
});
|
||||
|
||||
if (authError) {
|
||||
error = authError.message;
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="w-full max-w-md">
|
||||
<div class="mb-6 text-center">
|
||||
<h2 class="text-2xl font-bold text-gray-900">Welcome back</h2>
|
||||
<p class="mt-2 text-sm text-gray-600">Sign in to your account</p>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="mb-4 rounded-md bg-red-50 p-3">
|
||||
<p class="text-sm text-red-800">{error}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleLogin();
|
||||
}}
|
||||
class="space-y-4"
|
||||
>
|
||||
<div>
|
||||
<Input
|
||||
type="email"
|
||||
label="Email"
|
||||
placeholder="you@example.com"
|
||||
bind:value={email}
|
||||
required
|
||||
autocomplete="email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="password"
|
||||
label="Password"
|
||||
placeholder="••••••••"
|
||||
bind:value={password}
|
||||
required
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<a href="/auth/forgot-password" class="text-sm text-blue-600 hover:text-blue-500">
|
||||
Forgot password?
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<Button type="submit" variant="primary" class="w-full" {loading}>Sign in</Button>
|
||||
</form>
|
||||
|
||||
<div class="relative my-6">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-gray-300"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-sm">
|
||||
<span class="bg-white px-2 text-gray-500">Or continue with</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button variant="outline" class="w-full" onclick={handleGoogleLogin} {loading}>
|
||||
<svg class="mr-2 h-5 w-5" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
/>
|
||||
</svg>
|
||||
Google
|
||||
</Button>
|
||||
|
||||
<p class="mt-6 text-center text-sm text-gray-600">
|
||||
Don't have an account?
|
||||
<a href="/auth/signup" class="font-medium text-blue-600 hover:text-blue-500"> Sign up </a>
|
||||
</p>
|
||||
</Card>
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
<script lang="ts">
|
||||
import Button from '../ui/Button.svelte';
|
||||
import Input from '../ui/Input.svelte';
|
||||
import Card from '../ui/Card.svelte';
|
||||
import { supabase } from '$lib/supabase';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let email = $state('');
|
||||
let password = $state('');
|
||||
let confirmPassword = $state('');
|
||||
let loading = $state(false);
|
||||
let error = $state('');
|
||||
let success = $state(false);
|
||||
|
||||
async function handleSignup() {
|
||||
if (!email || !password || !confirmPassword) {
|
||||
error = 'Please fill in all fields';
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
error = 'Passwords do not match';
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
error = 'Password must be at least 6 characters';
|
||||
return;
|
||||
}
|
||||
|
||||
loading = true;
|
||||
error = '';
|
||||
|
||||
const { data, error: authError } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
emailRedirectTo: `${window.location.origin}/app/gallery`,
|
||||
},
|
||||
});
|
||||
|
||||
loading = false;
|
||||
|
||||
if (authError) {
|
||||
error = authError.message;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if email confirmation is required
|
||||
if (data.user && !data.session) {
|
||||
success = true;
|
||||
} else if (data.session) {
|
||||
goto('/app/gallery');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="w-full max-w-md">
|
||||
<div class="mb-6 text-center">
|
||||
<h2 class="text-2xl font-bold text-gray-900">Create account</h2>
|
||||
<p class="mt-2 text-sm text-gray-600">Start generating AI images today</p>
|
||||
</div>
|
||||
|
||||
{#if success}
|
||||
<div class="mb-4 rounded-md bg-green-50 p-4">
|
||||
<h3 class="text-sm font-medium text-green-800">Check your email</h3>
|
||||
<p class="mt-2 text-sm text-green-700">
|
||||
We've sent you a confirmation link. Please check your email to verify your account.
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
{#if error}
|
||||
<div class="mb-4 rounded-md bg-red-50 p-3">
|
||||
<p class="text-sm text-red-800">{error}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSignup();
|
||||
}}
|
||||
class="space-y-4"
|
||||
>
|
||||
<div>
|
||||
<Input
|
||||
type="email"
|
||||
label="Email"
|
||||
placeholder="you@example.com"
|
||||
bind:value={email}
|
||||
required
|
||||
autocomplete="email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="password"
|
||||
label="Password"
|
||||
placeholder="••••••••"
|
||||
bind:value={password}
|
||||
required
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="password"
|
||||
label="Confirm Password"
|
||||
placeholder="••••••••"
|
||||
bind:value={confirmPassword}
|
||||
required
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button type="submit" variant="primary" class="w-full" {loading}>Create account</Button>
|
||||
</form>
|
||||
|
||||
<p class="mt-6 text-center text-sm text-gray-600">
|
||||
Already have an account?
|
||||
<a href="/auth/login" class="font-medium text-blue-600 hover:text-blue-500"> Sign in </a>
|
||||
</p>
|
||||
{/if}
|
||||
</Card>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { images, isLoading as isLoadingImages } from '$lib/stores/images';
|
||||
import { canvasItems, addCanvasItem } from '$lib/stores/canvas';
|
||||
import { getImages } from '$lib/api/images';
|
||||
|
|
@ -30,18 +30,18 @@
|
|||
|
||||
// Load images when modal opens
|
||||
$effect(() => {
|
||||
if (open && $user) {
|
||||
if (open && authStore.user) {
|
||||
loadImages();
|
||||
}
|
||||
});
|
||||
|
||||
async function loadImages() {
|
||||
if (!$user) return;
|
||||
if (!authStore.user) return;
|
||||
|
||||
isLoadingImages.set(true);
|
||||
try {
|
||||
const data = await getImages({
|
||||
userId: $user.id,
|
||||
userId: authStore.user.id,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
archived: false,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { models, selectedModel, isLoadingModels } from '$lib/stores/models';
|
||||
import { isGenerating, generationProgress, generationError } from '$lib/stores/generate';
|
||||
import { isSidebarCollapsed } from '$lib/stores/sidebar';
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
}
|
||||
|
||||
async function handleQuickGenerate() {
|
||||
if (!$user || !selectedModelId || !prompt.trim()) return;
|
||||
if (!authStore.user || !selectedModelId || !prompt.trim()) return;
|
||||
|
||||
isGenerating.set(true);
|
||||
generationError.set('');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { models, selectedModel, isLoadingModels } from '$lib/stores/models';
|
||||
import { isGenerating, generationProgress, generationError } from '$lib/stores/generate';
|
||||
import { getActiveModels } from '$lib/api/models';
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
}
|
||||
|
||||
async function handleGenerate() {
|
||||
if (!$user || !selectedModelId || !prompt.trim()) return;
|
||||
if (!authStore.user || !selectedModelId || !prompt.trim()) return;
|
||||
|
||||
isGenerating.set(true);
|
||||
generationError.set('');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { supabase } from '$lib/supabase';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
|
|
@ -8,7 +7,7 @@
|
|||
let showMobileMenu = $state(false);
|
||||
|
||||
async function handleLogout() {
|
||||
await supabase.auth.signOut();
|
||||
await authStore.signOut();
|
||||
goto('/auth/login');
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +85,7 @@
|
|||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-full bg-blue-600 text-white dark:bg-blue-500"
|
||||
>
|
||||
{$user?.email?.charAt(0).toUpperCase()}
|
||||
{authStore.user?.email?.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
|
|
@ -103,7 +102,7 @@
|
|||
class="absolute right-0 mt-2 w-48 rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800"
|
||||
>
|
||||
<div class="border-b border-gray-100 px-4 py-2 dark:border-gray-700">
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">{$user?.email}</p>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">{authStore.user?.email}</p>
|
||||
</div>
|
||||
<a
|
||||
href="/app/profile"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { supabase } from '$lib/supabase';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { currentTheme } from '$lib/stores/theme';
|
||||
|
|
@ -26,7 +25,7 @@
|
|||
let searchTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
async function handleLogout() {
|
||||
await supabase.auth.signOut();
|
||||
await authStore.signOut();
|
||||
goto('/auth/login');
|
||||
}
|
||||
|
||||
|
|
@ -469,11 +468,11 @@
|
|||
class="flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full text-sm font-semibold text-white"
|
||||
style="background-color: {$currentTheme.primary.default};"
|
||||
>
|
||||
{$user?.email?.charAt(0).toUpperCase()}
|
||||
{authStore.user?.email?.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden text-left">
|
||||
<p class="truncate text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{$user?.email?.split('@')[0]}
|
||||
{authStore.user?.email?.split('@')[0]}
|
||||
</p>
|
||||
<p class="truncate text-xs text-gray-500 dark:text-gray-400">Account</p>
|
||||
</div>
|
||||
|
|
@ -545,7 +544,7 @@
|
|||
class="flex h-9 w-9 items-center justify-center rounded-full text-sm font-semibold text-white"
|
||||
style="background-color: {$currentTheme.primary.default};"
|
||||
>
|
||||
{$user?.email?.charAt(0).toUpperCase()}
|
||||
{authStore.user?.email?.charAt(0).toUpperCase()}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
hideTagSubmenu,
|
||||
} from '$lib/stores/contextMenu';
|
||||
import { tags } from '$lib/stores/tags';
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { addTagToImage, removeTagFromImage, getImageTags } from '$lib/api/tags';
|
||||
import { archiveImage, unarchiveImage, deleteImage, toggleFavorite } from '$lib/api/images';
|
||||
import { images } from '$lib/stores/images';
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
const isFavorite = $derived($contextMenu.image?.is_favorite === true);
|
||||
|
||||
// Check if current image belongs to current user
|
||||
const isOwnImage = $derived($contextMenu.image?.user_id === $user?.id);
|
||||
const isOwnImage = $derived($contextMenu.image?.user_id === authStore.user?.id);
|
||||
|
||||
interface MenuItem {
|
||||
label: string;
|
||||
|
|
|
|||
|
|
@ -1,221 +0,0 @@
|
|||
/**
|
||||
* Authentication service for Picture Web
|
||||
* Uses Supabase auth with compatible interface for shared-auth-ui
|
||||
*/
|
||||
|
||||
import { supabase } from '$lib/supabase';
|
||||
|
||||
export interface AuthResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
needsVerification?: boolean;
|
||||
}
|
||||
|
||||
export interface UserData {
|
||||
id: string;
|
||||
email: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication service compatible with @manacore/shared-auth-ui
|
||||
*/
|
||||
export const authService = {
|
||||
/**
|
||||
* Sign in with email and password
|
||||
*/
|
||||
async signIn(email: string, password: string): Promise<AuthResult> {
|
||||
try {
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
// Handle specific error cases
|
||||
if (error.message?.includes('Invalid login credentials')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'INVALID_CREDENTIALS',
|
||||
};
|
||||
}
|
||||
|
||||
if (error.message?.includes('Email not confirmed')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'EMAIL_NOT_VERIFIED',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Sign in failed',
|
||||
};
|
||||
}
|
||||
|
||||
if (data.session) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'No session returned',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error signing in:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error during sign in',
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
*/
|
||||
async signUp(email: string, password: string): Promise<AuthResult> {
|
||||
try {
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
emailRedirectTo: `${window.location.origin}/auth/callback`,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
if (error.message?.includes('already registered')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'This email is already in use',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Registration failed',
|
||||
};
|
||||
}
|
||||
|
||||
// Check if email confirmation is required
|
||||
if (data.user && !data.session) {
|
||||
return {
|
||||
success: true,
|
||||
needsVerification: true,
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error signing up:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error during registration',
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign in with Google (OAuth)
|
||||
*/
|
||||
async signInWithGoogle(): Promise<AuthResult> {
|
||||
try {
|
||||
const { error } = await supabase.auth.signInWithOAuth({
|
||||
provider: 'google',
|
||||
options: {
|
||||
redirectTo: `${window.location.origin}/app/gallery`,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Google Sign-In failed',
|
||||
};
|
||||
}
|
||||
|
||||
// OAuth redirects, so if we get here, it's working
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error signing in with Google:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error during Google Sign-In',
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign in with Apple (OAuth)
|
||||
*/
|
||||
async signInWithApple(): Promise<AuthResult> {
|
||||
try {
|
||||
const { error } = await supabase.auth.signInWithOAuth({
|
||||
provider: 'apple',
|
||||
options: {
|
||||
redirectTo: `${window.location.origin}/app/gallery`,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Apple Sign-In failed',
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error signing in with Apple:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error during Apple Sign-In',
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign out
|
||||
*/
|
||||
async signOut(): Promise<void> {
|
||||
try {
|
||||
await supabase.auth.signOut();
|
||||
} catch (error) {
|
||||
console.error('Error signing out:', error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Forgot password
|
||||
*/
|
||||
async forgotPassword(email: string): Promise<AuthResult> {
|
||||
try {
|
||||
const { error } = await supabase.auth.resetPasswordForEmail(email, {
|
||||
redirectTo: `${window.location.origin}/auth/reset-password`,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
if (error.message?.includes('rate limit')) {
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
'Too many password reset attempts. Please wait a few minutes before trying again.',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Password reset failed',
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error sending password reset email:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error during password reset',
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import { writable } from 'svelte/store';
|
||||
import type { User, Session } from '@supabase/supabase-js';
|
||||
|
||||
export const user = writable<User | null>(null);
|
||||
export const session = writable<Session | null>(null);
|
||||
export const loading = writable(true);
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { createClient } from '@supabase/supabase-js';
|
||||
import type { Database } from '@picture/shared/types';
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
|
||||
|
||||
export const supabase = createClient<Database>(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
||||
auth: {
|
||||
autoRefreshToken: true,
|
||||
persistSession: true,
|
||||
detectSessionInUrl: true,
|
||||
},
|
||||
});
|
||||
|
|
@ -1,18 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
const unsubscribe = user.subscribe((currentUser) => {
|
||||
if (currentUser) {
|
||||
goto('/app/gallery');
|
||||
} else {
|
||||
goto('/auth/login');
|
||||
}
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
onMount(async () => {
|
||||
await authStore.initialize();
|
||||
if (authStore.isAuthenticated) {
|
||||
goto('/app/gallery');
|
||||
} else {
|
||||
goto('/auth/login');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import {
|
||||
archivedImages,
|
||||
isLoadingArchive,
|
||||
|
|
@ -47,11 +47,11 @@
|
|||
});
|
||||
|
||||
async function loadInitialImages() {
|
||||
if (!$user) return;
|
||||
if (!authStore.user) return;
|
||||
|
||||
isLoadingArchive.set(true);
|
||||
try {
|
||||
const data = await getImages({ userId: $user.id, page: 1, archived: true });
|
||||
const data = await getImages({ userId: authStore.user.id, page: 1, archived: true });
|
||||
archivedImages.set(data);
|
||||
currentArchivePage.set(1);
|
||||
hasMoreArchive.set(data.length === 20);
|
||||
|
|
@ -63,13 +63,13 @@
|
|||
}
|
||||
|
||||
async function loadMoreImages() {
|
||||
if (!$user || !$hasMoreArchive || $isLoadingArchive || loadingMore) return;
|
||||
if (!authStore.user || !$hasMoreArchive || $isLoadingArchive || loadingMore) return;
|
||||
|
||||
loadingMore = true;
|
||||
const nextPage = $currentArchivePage + 1;
|
||||
|
||||
try {
|
||||
const newImages = await getImages({ userId: $user.id, page: nextPage, archived: true });
|
||||
const newImages = await getImages({ userId: authStore.user.id, page: nextPage, archived: true });
|
||||
if (newImages.length > 0) {
|
||||
archivedImages.update((current) => [...current, ...newImages]);
|
||||
currentArchivePage.set(nextPage);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import {
|
||||
boards,
|
||||
isLoadingBoards,
|
||||
|
|
@ -58,11 +58,11 @@
|
|||
});
|
||||
|
||||
async function loadInitialBoards() {
|
||||
if (!$user) return;
|
||||
if (!authStore.user) return;
|
||||
|
||||
isLoadingBoards.set(true);
|
||||
try {
|
||||
const data = await getBoards({ userId: $user.id, page: 1 });
|
||||
const data = await getBoards({ userId: authStore.user.id, page: 1 });
|
||||
boards.set(data);
|
||||
currentBoardsPage.set(1);
|
||||
hasBoardsMore.set(data.length === 20);
|
||||
|
|
@ -75,13 +75,13 @@
|
|||
}
|
||||
|
||||
async function loadMoreBoards() {
|
||||
if (!$user || !$hasBoardsMore || $isLoadingBoards || loadingMore) return;
|
||||
if (!authStore.user || !$hasBoardsMore || $isLoadingBoards || loadingMore) return;
|
||||
|
||||
loadingMore = true;
|
||||
const nextPage = $currentBoardsPage + 1;
|
||||
|
||||
try {
|
||||
const newBoards = await getBoards({ userId: $user.id, page: nextPage });
|
||||
const newBoards = await getBoards({ userId: authStore.user.id, page: nextPage });
|
||||
if (newBoards.length > 0) {
|
||||
boards.update((current) => [...current, ...newBoards]);
|
||||
currentBoardsPage.set(nextPage);
|
||||
|
|
@ -97,13 +97,13 @@
|
|||
}
|
||||
|
||||
async function handleCreateBoard() {
|
||||
if (!$user || !boardName.trim()) return;
|
||||
if (!authStore.user || !boardName.trim()) return;
|
||||
|
||||
isCreating = true;
|
||||
try {
|
||||
const { createBoard } = await import('$lib/api/boards');
|
||||
const newBoard = await createBoard({
|
||||
user_id: $user.id,
|
||||
user_id: authStore.user.id,
|
||||
name: boardName,
|
||||
description: boardDescription || null,
|
||||
});
|
||||
|
|
@ -136,10 +136,10 @@
|
|||
}
|
||||
|
||||
async function handleDuplicateBoard(boardId: string) {
|
||||
if (!$user) return;
|
||||
if (!authStore.user) return;
|
||||
|
||||
try {
|
||||
const newBoard = await duplicateBoard(boardId, $user.id);
|
||||
const newBoard = await duplicateBoard(boardId, authStore.user.id);
|
||||
addBoard({ ...newBoard, item_count: 0 });
|
||||
showToast('Board dupliziert', 'success');
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { currentBoard } from '$lib/stores/boards';
|
||||
import {
|
||||
canvasItems,
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
});
|
||||
|
||||
async function loadBoard() {
|
||||
if (!$user) return;
|
||||
if (!authStore.user) return;
|
||||
|
||||
isLoading = true;
|
||||
try {
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
const board = await getBoardById(boardId);
|
||||
|
||||
// Check if user has access
|
||||
if (board.user_id !== $user.id && !board.is_public) {
|
||||
if (board.user_id !== authStore.user.id && !board.is_public) {
|
||||
showToast('Zugriff verweigert', 'error');
|
||||
goto('/app/board');
|
||||
return;
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
}
|
||||
|
||||
async function handleAddText() {
|
||||
if (!$user || !boardId) return;
|
||||
if (!authStore.user || !boardId) return;
|
||||
|
||||
try {
|
||||
// Add text to center of visible area
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import {
|
||||
images,
|
||||
isLoading,
|
||||
|
|
@ -67,12 +67,12 @@
|
|||
});
|
||||
|
||||
async function loadInitialImages() {
|
||||
if (!$user) return;
|
||||
if (!authStore.user) return;
|
||||
|
||||
isLoading.set(true);
|
||||
try {
|
||||
const data = await getImages({
|
||||
userId: $user.id,
|
||||
userId: authStore.user.id,
|
||||
page: 1,
|
||||
tagIds: $selectedTags.length > 0 ? $selectedTags : undefined,
|
||||
favoritesOnly: $showFavoritesOnly,
|
||||
|
|
@ -88,14 +88,14 @@
|
|||
}
|
||||
|
||||
async function loadMoreImages() {
|
||||
if (!$user || !$hasMore || $isLoading || loadingMore) return;
|
||||
if (!authStore.user || !$hasMore || $isLoading || loadingMore) return;
|
||||
|
||||
loadingMore = true;
|
||||
const nextPage = $currentPage + 1;
|
||||
|
||||
try {
|
||||
const newImages = await getImages({
|
||||
userId: $user.id,
|
||||
userId: authStore.user.id,
|
||||
page: nextPage,
|
||||
tagIds: $selectedTags.length > 0 ? $selectedTags : undefined,
|
||||
favoritesOnly: $showFavoritesOnly,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { supabase } from '$lib/supabase';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import Card from '$lib/components/ui/Card.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
|
|
@ -11,8 +10,7 @@
|
|||
async function handleLogout() {
|
||||
isLoggingOut = true;
|
||||
try {
|
||||
const { error } = await supabase.auth.signOut();
|
||||
if (error) throw error;
|
||||
await authStore.signOut();
|
||||
goto('/');
|
||||
} catch (error) {
|
||||
console.error('Error logging out:', error);
|
||||
|
|
@ -58,9 +56,9 @@
|
|||
>
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Email</h3>
|
||||
<p class="mt-1 text-gray-900 dark:text-gray-100">{$user?.email || 'Not available'}</p>
|
||||
<p class="mt-1 text-gray-900 dark:text-gray-100">{authStore.user?.email || 'Not available'}</p>
|
||||
</div>
|
||||
{#if $user?.email_confirmed_at}
|
||||
{#if authStore.user?.email}
|
||||
<span class="rounded-full bg-green-100 px-3 py-1 text-xs font-medium text-green-800">
|
||||
Verified
|
||||
</span>
|
||||
|
|
@ -77,7 +75,7 @@
|
|||
<div class="flex items-center justify-between border-b border-gray-200 pb-4">
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-500">User ID</h3>
|
||||
<p class="mt-1 font-mono text-sm text-gray-900">{$user?.id || 'Not available'}</p>
|
||||
<p class="mt-1 font-mono text-sm text-gray-900">{authStore.user?.id || 'Not available'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -85,7 +83,7 @@
|
|||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-500">Member Since</h3>
|
||||
<p class="mt-1 text-gray-900">{formatDate($user?.created_at)}</p>
|
||||
<p class="mt-1 text-gray-900">-</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/auth';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { uploadMultipleImages, type UploadProgress } from '$lib/api/upload';
|
||||
import { showToast } from '$lib/stores/toast';
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
let successCount = $state(0);
|
||||
|
||||
async function handleFilesSelected(files: File[]) {
|
||||
if (!$user) {
|
||||
if (!authStore.user) {
|
||||
showToast('Bitte melde dich an', 'error');
|
||||
return;
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
successCount = 0;
|
||||
|
||||
try {
|
||||
const uploadedImages = await uploadMultipleImages(files, $user.id, (progress) => {
|
||||
const uploadedImages = await uploadMultipleImages(files, authStore.user.id, (progress) => {
|
||||
uploadProgress = progress;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
import { ForgotPasswordPage } from '@manacore/shared-auth-ui';
|
||||
import { getForgotPasswordTranslations } from '@manacore/shared-i18n';
|
||||
import PictureLogo from '$lib/components/branding/PictureLogo.svelte';
|
||||
import { authService } from '$lib/services/authService';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
|
||||
// Default to German
|
||||
const translations = getForgotPasswordTranslations('de');
|
||||
|
||||
async function handleForgotPassword(email: string) {
|
||||
return authService.forgotPassword(email);
|
||||
return authStore.resetPassword(email);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
import PictureLogo from '$lib/components/branding/PictureLogo.svelte';
|
||||
import AppSlider from '$lib/components/AppSlider.svelte';
|
||||
import LanguageSelector from '$lib/components/LanguageSelector.svelte';
|
||||
import { authService } from '$lib/services/authService';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { PUBLIC_GOOGLE_CLIENT_ID, PUBLIC_APPLE_CLIENT_ID } from '$env/static/public';
|
||||
|
||||
|
|
@ -20,15 +20,17 @@
|
|||
});
|
||||
|
||||
async function handleSignIn(email: string, password: string) {
|
||||
return authService.signIn(email, password);
|
||||
return authStore.signIn(email, password);
|
||||
}
|
||||
|
||||
async function handleSignInWithGoogle() {
|
||||
return authService.signInWithGoogle();
|
||||
// TODO: Implement OAuth with Mana Core Auth when ready
|
||||
return { success: false, error: 'Google Sign-In not yet implemented' };
|
||||
}
|
||||
|
||||
async function handleSignInWithApple() {
|
||||
return authService.signInWithApple();
|
||||
// TODO: Implement OAuth with Mana Core Auth when ready
|
||||
return { success: false, error: 'Apple Sign-In not yet implemented' };
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { RegisterPage, setGoogleClientId } from '@manacore/shared-auth-ui';
|
||||
import { getRegisterTranslations } from '@manacore/shared-i18n';
|
||||
import PictureLogo from '$lib/components/branding/PictureLogo.svelte';
|
||||
import { authService } from '$lib/services/authService';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { PUBLIC_GOOGLE_CLIENT_ID, PUBLIC_APPLE_CLIENT_ID } from '$env/static/public';
|
||||
|
||||
|
|
@ -17,15 +17,17 @@
|
|||
});
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authService.signUp(email, password);
|
||||
return authStore.signUp(email, password);
|
||||
}
|
||||
|
||||
async function handleSignUpWithGoogle() {
|
||||
return authService.signInWithGoogle();
|
||||
// TODO: Implement OAuth with Mana Core Auth when ready
|
||||
return { success: false, error: 'Google Sign-Up not yet implemented' };
|
||||
}
|
||||
|
||||
async function handleSignUpWithApple() {
|
||||
return authService.signInWithApple();
|
||||
// TODO: Implement OAuth with Mana Core Auth when ready
|
||||
return { success: false, error: 'Apple Sign-Up not yet implemented' };
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue