Files
flux/supabase/functions/reset-password/index.ts
2026-06-02 10:55:26 +02:00

129 lines
5.3 KiB
TypeScript

import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
serve(async (req) => {
// 1. GESTIONE CORS
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
}
try {
// 2. ESTRAZIONE EMAIL
const { email } = await req.json()
if (!email) {
throw new Error("Devi fornire un indirizzo email.")
}
// 3. INIZIALIZZAZIONE CLIENT ADMIN
const supabaseAdmin = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
)
// 4. RECUPERO NOME UTENTE (Opzionale, ma fa molto pro per l'email)
const { data: staffData } = await supabaseAdmin
.from('staff_members')
.select('name')
.eq('email', email)
.single()
const userName = staffData?.name || "Socio"
// 5. GENERAZIONE DEL LINK DI RECOVERY
// Usiamo lo stesso identico URL di redirect che abbiamo blindato prima!
const { data: linkData, error: linkError } = await supabaseAdmin.auth.admin.generateLink({
type: 'recovery',
email: email,
options: {
redirectTo: 'https://flux.catelli.it/set-password'
}
})
if (linkError) {
// Se l'utente non esiste su Auth, Supabase lancia un errore.
// Per sicurezza (evitare l'enumerazione delle email), restituiamo comunque un 200 finto
// oppure intercettiamo l'errore per il debug.
console.error("Errore generazione link Supabase:", linkError)
throw new Error("Se l'email è registrata, riceverai a breve le istruzioni.")
}
const secureLink = linkData.properties.action_link
// 6. IL POSTINO RESEND
const RESEND_API_KEY = Deno.env.get('RESEND_API_KEY')
if (!RESEND_API_KEY) throw new Error("Chiave API di Resend non configurata.")
const emailHtml = `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; max-width: 550px; margin: 40px auto; padding: 32px; border: 1px solid #e2e8f0; border-radius: 16px; background-color: #ffffff; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);">
<div style="margin-bottom: 32px; text-align: left;">
<img src="https://flux.catelli.it/assets/flux_logo_dark.svg" width="150" style="display: block; height: auto; border: 0;" alt="FLUX Logo" />
</div>
<h2 style="color: #0f172a; font-size: 22px; font-weight: 700; margin-bottom: 16px; margin-top: 0; letter-spacing: -0.3px;">
Ripristino Password
</h2>
<p style="color: #334155; font-size: 15px; line-height: 1.6; margin-bottom: 20px; margin-top: 0;">
Ciao <strong>${userName}</strong>,
</p>
<p style="color: #334155; font-size: 15px; line-height: 1.6; margin-bottom: 24px;">
Abbiamo ricevuto una richiesta per reimpostare la password del tuo account su FLUX. Clicca sul pulsante qui sotto per sceglierne una nuova.
</p>
<div style="margin: 36px 0; text-align: center;">
<a href="${secureLink}" style="background-color: #0f172a; color: #ffffff; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-size: 15px; font-weight: 600; display: inline-block; box-shadow: 0 4px 6px -1px rgba(15, 23, 42, 0.2);">
Reimposta la tua Password
</a>
</div>
<div style="border-top: 1px solid #f1f5f9; padding-top: 20px; margin-top: 32px;">
<p style="color: #94a3b8; font-size: 12px; line-height: 1.5; margin-bottom: 0;">
Se non hai richiesto tu il ripristino, puoi ignorare tranquillamente questa email. Il link è strettamente personale e valido per un solo utilizzo.<br><br>
<a href="${secureLink}" style="color: #2563eb; text-decoration: none; word-break: break-all;">${secureLink}</a>
</p>
</div>
</div>
`
// Qui puoi cambiare l'indirizzo mittente se vuoi separare gli inviti dal supporto tecnico
const resendResponse = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${RESEND_API_KEY}`,
},
body: JSON.stringify({
from: 'FLUX Support <support@flux.catelli.it>', // Modificato per sembrare più istituzionale
to: [email],
subject: 'Recupero Password FLUX',
html: emailHtml,
}),
})
if (!resendResponse.ok) {
const resendError = await resendResponse.text()
console.error("Resend ha fallito:", resendError)
throw new Error("Errore durante l'invio dell'email.")
}
// 7. TUTTO OK
return new Response(
JSON.stringify({ success: true, message: "Email inviata con successo." }),
{ status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" } }
)
} catch (error: any) {
console.error("CRASH FUNZIONE RESET:", error.message || error);
return new Response(
JSON.stringify({ error: error.message || "Errore generico del server" }),
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
})