diff --git a/shop/components/forgot-password-form.tsx b/shop/components/forgot-password-form.tsx index f25bc7a..e92aa58 100644 --- a/shop/components/forgot-password-form.tsx +++ b/shop/components/forgot-password-form.tsx @@ -30,8 +30,12 @@ export function ForgotPasswordForm({ setError(null); try { - await resetPassword(email); - setSuccess(true); + const res = await resetPassword(email); + if (res.success) { + setSuccess(true); + } else { + setError(res.error || "Ein Fehler ist aufgetreten."); + } } catch (error: unknown) { setError(error instanceof Error ? error.message : "Ein Fehler ist aufgetreten."); } finally { diff --git a/shop/components/update-password-form.tsx b/shop/components/update-password-form.tsx index a295007..f7a2a8b 100644 --- a/shop/components/update-password-form.tsx +++ b/shop/components/update-password-form.tsx @@ -43,8 +43,12 @@ export function UpdatePasswordForm({ } try { - await updatePassword(password); - router.push("/"); + const res = await updatePassword(password); + if (res.success) { + router.push("/"); + } else { + setError(res.error || "Ein Fehler ist aufgetreten"); + } } catch (error: unknown) { setError(error instanceof Error ? error.message : "Ein Fehler ist aufgetreten"); } finally { diff --git a/shop/lib/actions/auth.ts b/shop/lib/actions/auth.ts index 8b45ce3..bdb7674 100644 --- a/shop/lib/actions/auth.ts +++ b/shop/lib/actions/auth.ts @@ -4,6 +4,7 @@ import { createClient } from '@/lib/supabase/server' import { createAdminClient } from '@/lib/supabase/admin' import { headers } from 'next/headers' import { sendLockoutEmail } from '@/lib/utils/email' +import { sendMail } from '@/utils/mail' export async function signIn(email: string, password: string) { try { @@ -108,25 +109,92 @@ export async function signOut() { return { success: true } } +function getBeautifulEmailHtml(title: string, messageHtml: string, buttonText?: string, buttonUrl?: string) { + return ` +
+
+ +
+

${title}

+
+ + +
+ ${messageHtml} + + ${buttonText && buttonUrl ? ` +
+ + ${buttonText} + +
+

+ Falls der Button nicht funktioniert, kopieren Sie bitte diesen Link in Ihren Browser:
+ ${buttonUrl} +

+ ` : ''} + +
+

+ Dies ist eine automatisch generierte E-Mail.
+ © ${new Date().getFullYear()} CASPOS Shop. Alle Rechte vorbehalten. +

+
+
+
+ `; +} + export async function resetPassword(email: string) { - const supabase = await createClient() - const origin = (await headers()).get('origin') || '' - const { error } = await supabase.auth.resetPasswordForEmail(email, { - redirectTo: `${origin}/auth/update-password`, - }) - if (error) { - throw new Error(error.message) + try { + const admin = createAdminClient() + const { data, error } = await admin.auth.admin.generateLink({ + type: 'recovery', + email, + }) + if (error) { + return { success: false, error: error.message } + } + const tokenHash = (data as any)?.properties?.hashed_token; + if (!tokenHash) { + return { success: false, error: 'Wiederherstellungs-Token konnte nicht generiert werden.' } + } + + const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de' + const resetLink = `${siteUrl}/auth/verify-link?token_hash=${tokenHash}&type=recovery&next=/auth/update-password` + + const messageHtml = ` +

Hallo,

+

Sie haben das Zurücksetzen Ihres Passworts angefordert.

+

Bitte klicken Sie auf den untenstehenden Button, um ein neues Passwort festzulegen:

+ `; + + await sendMail({ + to: email, + subject: 'Passwort zurücksetzen – CASPOS Shop', + text: `Hallo,\n\nSie haben das Zurücksetzen Ihres Passworts angefordert.\n\nBitte klicken Sie auf den folgenden Link, um ein neues Passwort festzulegen:\n${resetLink}\n\nViele Grüße,\nIhr CASPOS Shop-Team`, + html: getBeautifulEmailHtml('Passwort zurücksetzen', messageHtml, 'Passwort zurücksetzen', resetLink) + }) + + return { success: true } + } catch (err: any) { + console.error('Password reset error:', err) + return { success: false, error: err.message || 'Fehler beim Senden der Passwort-Zurücksetzen-E-Mail.' } } - return { success: true } } export async function updatePassword(password: string) { - const supabase = await createClient() - const { error } = await supabase.auth.updateUser({ password }) - if (error) { - throw new Error(error.message) + try { + const supabase = await createClient() + const { error } = await supabase.auth.updateUser({ password }) + if (error) { + return { success: false, error: error.message } + } + return { success: true } + } catch (err: any) { + console.error('Update password error:', err) + return { success: false, error: err.message || 'Fehler beim Aktualisieren des Passworts.' } } - return { success: true } } export async function verifyAdmin() { diff --git a/shop/lib/actions/users.ts b/shop/lib/actions/users.ts index 7170808..1bf432f 100644 --- a/shop/lib/actions/users.ts +++ b/shop/lib/actions/users.ts @@ -88,22 +88,17 @@ export async function createUser(data: { email: string; role?: 'admin' | 'partne await sendMail({ to: data.email, subject: 'Willkommen bei CASPOS Shop – Ihr Benutzerkonto wurde erstellt', - text: `Hallo,\n\nein neues Benutzerkonto wurde für Sie auf CASPOS Shop erstellt.\n\nBitte klicken Sie auf den folgenden Link, um Ihr persönliches Passwort festzulegen und Ihr Konto zu aktivieren:\n${setupLink}\n\nDieser Link ist zeitlich begrenzt gültig.\n\nViele Grüße,\nIhr CASPOS Shop-Team`, - html: ` -
-

Willkommen bei CASPOS Shop!

-

Hallo,

-

für Sie wurde erfolgreich ein neues Benutzerkonto auf unserer Plattform eingerichtet.

-

Bitte klicken Sie auf den untenstehenden Button, um Ihr Passwort festzulegen und Ihr Konto zu aktivieren:

-
- Passwort jetzt festlegen -
-

Falls der Button nicht funktioniert, können Sie auch folgenden Link in Ihren Browser kopieren:
- ${setupLink}

-
-

Diese E-Mail wurde automatisch generiert. Bitte antworten Sie nicht darauf.

-
- `, + text: `Hallo,\n\nein neues Benutzerkonto wurde für Sie auf CASPOS Shop erstellt.\n\nBitte klicken Sie auf den folgenden Link, um Ihr persönliches Passwort festzulegen und Ihr Konto zu aktivieren:\n${setupLink}\n\nViele Grüße,\nIhr CASPOS Shop-Team`, + html: getBeautifulEmailHtml( + 'Willkommen bei CASPOS Shop', + ` +

Hallo,

+

für Sie wurde erfolgreich ein neues Benutzerkonto auf unserer Plattform eingerichtet.

+

Bitte klicken Sie auf den untenstehenden Button, um Ihr Passwort festzulegen und Ihr Konto zu aktivieren:

+ `, + 'Passwort festlegen', + setupLink + ), }); } } @@ -184,11 +179,16 @@ export async function resetPassword(email: string) { // Build link pointing to our own /auth/confirm route const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'; const resetLink = `${siteUrl}/auth/verify-link?token_hash=${tokenHash}&type=recovery&next=/auth/update-password`; + const messageHtml = ` +

Hallo,

+

der Administrator hat das Zurücksetzen Ihres Passworts angefordert.

+

Bitte klicken Sie auf den untenstehenden Button, um ein neues Passwort für Ihr Konto festzulegen:

+ `; await sendMail({ to: email, - subject: 'Passwort zurücksetzen', - text: `Klicken Sie auf den folgenden Link, um Ihr Passwort zurückzusetzen: ${resetLink}`, - html: `

Klicken Sie hier, um Ihr Passwort zurückzusetzen.

`, + subject: 'Passwort zurücksetzen – CASPOS Shop', + text: `Hallo,\n\nder Administrator hat das Zurücksetzen Ihres Passworts angefordert.\n\nBitte klicken Sie auf den folgenden Link, um ein neues Passwort festzulegen:\n${resetLink}\n\nViele Grüße,\nIhr CASPOS Shop-Team`, + html: getBeautifulEmailHtml('Passwort zurücksetzen', messageHtml, 'Passwort zurücksetzen', resetLink), }); } @@ -232,3 +232,39 @@ export async function updateUserProfile(id: string, data: { first_name: string | revalidatePath('/admin/users'); } +function getBeautifulEmailHtml(title: string, messageHtml: string, buttonText?: string, buttonUrl?: string) { + return ` +
+
+ +
+

${title}

+
+ + +
+ ${messageHtml} + + ${buttonText && buttonUrl ? ` +
+ + ${buttonText} + +
+

+ Falls der Button nicht funktioniert, kopieren Sie bitte diesen Link in Ihren Browser:
+ ${buttonUrl} +

+ ` : ''} + +
+

+ Dies ist eine automatisch generierte E-Mail.
+ © ${new Date().getFullYear()} CASPOS Shop. Alle Rechte vorbehalten. +

+
+
+
+ `; +} +