🐛 fix(picture-web): fix Docker build and auth pages

- Remove unused Google/Apple OAuth imports from login page
- Add missing shared-types package to Dockerfile
- Fix signup page to pass name parameter to signUp
- Add confirmResetPassword method to auth store
- Add reset-password page for completing password reset flow
This commit is contained in:
Wuesteon 2025-12-17 18:58:31 +01:00
parent dcdc15f154
commit 71cbca07d5
5 changed files with 184 additions and 25 deletions

View file

@ -35,6 +35,7 @@ COPY packages/shared-theme-ui ./packages/shared-theme-ui
COPY packages/shared-subscription-types ./packages/shared-subscription-types
COPY packages/shared-subscription-ui ./packages/shared-subscription-ui
COPY packages/shared-profile-ui ./packages/shared-profile-ui
COPY packages/shared-types ./packages/shared-types
COPY packages/shared-ui ./packages/shared-ui
COPY packages/shared-utils ./packages/shared-utils

View file

@ -174,6 +174,26 @@ export const authStore = {
}
},
async confirmResetPassword(token: string, newPassword: string): Promise<AuthResult> {
const authService = await getAuthService();
if (!authService) {
return { success: false, error: 'Auth service not available' };
}
try {
const result = await authService.resetPassword(token, newPassword);
return {
success: result.success,
error: result.error,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Password reset failed',
};
}
},
/**
* Get access token for API calls (raw token, no refresh)
* @deprecated Use getValidToken() instead for automatic refresh

View file

@ -1,37 +1,19 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { locale } from 'svelte-i18n';
import { LoginPage, setGoogleClientId } from '@manacore/shared-auth-ui';
import { LoginPage } from '@manacore/shared-auth-ui';
import { getLoginTranslations } from '@manacore/shared-i18n';
import PictureLogo from '$lib/components/branding/PictureLogo.svelte';
import AppSlider from '$lib/components/AppSlider.svelte';
import LanguageSelector from '$lib/components/LanguageSelector.svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { onMount } from 'svelte';
import { PUBLIC_GOOGLE_CLIENT_ID, PUBLIC_APPLE_CLIENT_ID } from '$env/static/public';
// Get translations based on current locale
const translations = $derived(getLoginTranslations($locale || 'de'));
onMount(() => {
if (PUBLIC_GOOGLE_CLIENT_ID) {
setGoogleClientId(PUBLIC_GOOGLE_CLIENT_ID);
}
});
async function handleSignIn(email: string, password: string) {
return authStore.signIn(email, password);
}
async function handleSignInWithGoogle() {
// TODO: Implement OAuth with Mana Core Auth when ready
return { success: false, error: 'Google Sign-In not yet implemented' };
}
async function handleSignInWithApple() {
// TODO: Implement OAuth with Mana Core Auth when ready
return { success: false, error: 'Apple Sign-In not yet implemented' };
}
</script>
<svelte:head>
@ -43,11 +25,9 @@
logo={PictureLogo}
primaryColor="#3b82f6"
onSignIn={handleSignIn}
onSignInWithGoogle={PUBLIC_GOOGLE_CLIENT_ID ? handleSignInWithGoogle : undefined}
onSignInWithApple={PUBLIC_APPLE_CLIENT_ID ? handleSignInWithApple : undefined}
{goto}
enableGoogle={!!PUBLIC_GOOGLE_CLIENT_ID}
enableApple={!!PUBLIC_APPLE_CLIENT_ID}
enableGoogle={false}
enableApple={false}
successRedirect="/app/gallery"
registerPath="/auth/signup"
forgotPasswordPath="/auth/forgot-password"

View file

@ -0,0 +1,158 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { onMount } from 'svelte';
import PictureLogo from '$lib/components/branding/PictureLogo.svelte';
import { authStore } from '$lib/stores/auth.svelte';
let password = $state('');
let confirmPassword = $state('');
let loading = $state(false);
let error = $state('');
let success = $state(false);
let token = $state('');
onMount(() => {
token = $page.url.searchParams.get('token') || '';
if (!token) {
error = 'Kein Reset-Token gefunden. Bitte fordern Sie einen neuen Link an.';
}
});
async function handleSubmit(e: Event) {
e.preventDefault();
error = '';
if (password !== confirmPassword) {
error = 'Passworter stimmen nicht uberein';
return;
}
if (password.length < 8) {
error = 'Passwort muss mindestens 8 Zeichen lang sein';
return;
}
loading = true;
const result = await authStore.confirmResetPassword(token, password);
loading = false;
if (result.success) {
success = true;
setTimeout(() => {
goto('/auth/login');
}, 3000);
} else {
error = result.error || 'Passwort konnte nicht zuruckgesetzt werden';
}
}
</script>
<svelte:head>
<title>Passwort zurucksetzen - Picture</title>
</svelte:head>
<div
class="flex min-h-screen items-center justify-center bg-gradient-to-br from-[#f0f9ff] to-[#e0f2fe] px-4 py-12 dark:from-[#0c1929] dark:to-[#1e3a5f]"
>
<div class="w-full max-w-md">
<div class="mb-8 flex justify-center">
<PictureLogo class="h-12 w-auto" />
</div>
<div class="rounded-2xl bg-white p-8 shadow-xl dark:bg-gray-800">
<h1 class="mb-6 text-center text-2xl font-bold text-gray-900 dark:text-white">
Neues Passwort festlegen
</h1>
{#if success}
<div class="rounded-lg bg-green-50 p-4 text-center dark:bg-green-900/20">
<p class="text-green-800 dark:text-green-200">
Passwort erfolgreich zuruckgesetzt! Sie werden zur Anmeldung weitergeleitet...
</p>
</div>
{:else if !token}
<div class="rounded-lg bg-red-50 p-4 text-center dark:bg-red-900/20">
<p class="text-red-800 dark:text-red-200">{error}</p>
<button
onclick={() => goto('/auth/forgot-password')}
class="mt-4 text-blue-600 hover:underline dark:text-blue-400"
>
Neuen Link anfordern
</button>
</div>
{:else}
<form onsubmit={handleSubmit} class="space-y-6">
{#if error}
<div
class="rounded-lg bg-red-50 p-3 text-sm text-red-800 dark:bg-red-900/20 dark:text-red-200"
>
{error}
</div>
{/if}
<div>
<label
for="password"
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
>
Neues Passwort
</label>
<input
id="password"
type="password"
bind:value={password}
required
minlength="8"
class="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-3 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
placeholder="Mindestens 8 Zeichen"
/>
</div>
<div>
<label
for="confirmPassword"
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
>
Passwort bestatigen
</label>
<input
id="confirmPassword"
type="password"
bind:value={confirmPassword}
required
minlength="8"
class="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-3 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
placeholder="Passwort wiederholen"
/>
</div>
<button
type="submit"
disabled={loading}
class="w-full rounded-lg bg-blue-600 px-4 py-3 font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
>
{#if loading}
<span class="flex items-center justify-center gap-2">
<span
class="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent"
></span>
Wird zuruckgesetzt...
</span>
{:else}
Passwort zurucksetzen
{/if}
</button>
</form>
<p class="mt-6 text-center text-sm text-gray-600 dark:text-gray-400">
<a href="/auth/login" class="text-blue-600 hover:underline dark:text-blue-400">
Zuruck zur Anmeldung
</a>
</p>
{/if}
</div>
</div>
</div>

View file

@ -8,8 +8,8 @@
// Default to German
const translations = getRegisterTranslations('de');
async function handleSignUp(email: string, password: string) {
return authStore.signUp(email, password);
async function handleSignUp(email: string, password: string, name: string) {
return authStore.signUp(email, password, name);
}
</script>