2026-05-30 12:12:14 +02:00
|
|
|
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
|
|
|
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2"
|
|
|
|
|
import { JWT } from "https://esm.sh/google-auth-library@8.9.0"
|
|
|
|
|
|
|
|
|
|
const corsHeaders = {
|
|
|
|
|
'Access-Control-Allow-Origin': '*',
|
|
|
|
|
'Access-Control-Allow-Headers': 'authorization, x-client-info, apiKey, content-type',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
serve(async (req) => {
|
|
|
|
|
if (req.method === 'OPTIONS') {
|
|
|
|
|
return new Response('ok', { headers: corsHeaders })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2026-05-30 12:26:53 +02:00
|
|
|
// 1. Client Supabase (Service Role)
|
2026-05-30 12:12:14 +02:00
|
|
|
const supabaseClient = createClient(
|
|
|
|
|
Deno.env.get('SUPABASE_URL') ?? '',
|
|
|
|
|
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-30 12:26:53 +02:00
|
|
|
// 2. Setup Firebase (Push)
|
2026-05-30 12:12:14 +02:00
|
|
|
const firebaseSecret = Deno.env.get('FIREBASE_SERVICE_ACCOUNT')
|
|
|
|
|
if (!firebaseSecret) throw new Error('Missing FIREBASE_SERVICE_ACCOUNT secret')
|
|
|
|
|
const credentials = JSON.parse(firebaseSecret)
|
|
|
|
|
const jwtClient = new JWT({
|
|
|
|
|
email: credentials.client_email,
|
|
|
|
|
key: credentials.private_key,
|
|
|
|
|
scopes: ['https://www.googleapis.com/auth/firebase.messaging'],
|
|
|
|
|
})
|
|
|
|
|
const tokens = await jwtClient.getAccessToken()
|
|
|
|
|
const fcmAccessToken = tokens.token
|
|
|
|
|
|
2026-05-30 12:26:53 +02:00
|
|
|
// 3. Setup Resend (Email)
|
|
|
|
|
const resendApiKey = Deno.env.get('RESEND_API_KEY')
|
|
|
|
|
if (!resendApiKey) throw new Error('Missing RESEND_API_KEY secret')
|
|
|
|
|
|
|
|
|
|
// 4. Recupero Promemoria (TUTTI i canali, non ancora inviati)
|
2026-05-30 12:12:14 +02:00
|
|
|
const { data: reminders, error: fetchError } = await supabaseClient
|
|
|
|
|
.from('task_reminders')
|
2026-05-30 12:26:53 +02:00
|
|
|
.select(`
|
|
|
|
|
id, task_id, staff_id, channel,
|
|
|
|
|
tasks(title, description),
|
|
|
|
|
staff_members(email, first_name)
|
|
|
|
|
`)
|
2026-05-30 12:12:14 +02:00
|
|
|
.eq('is_sent', false)
|
|
|
|
|
.lte('trigger_at', new Date().toISOString())
|
|
|
|
|
|
|
|
|
|
if (fetchError) throw fetchError
|
|
|
|
|
|
|
|
|
|
if (!reminders || reminders.length === 0) {
|
2026-05-30 12:26:53 +02:00
|
|
|
return new Response(JSON.stringify({ message: 'Nessun promemoria da inviare.' }), {
|
2026-05-30 12:12:14 +02:00
|
|
|
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
|
|
|
|
status: 200,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-30 12:26:53 +02:00
|
|
|
// 5. Smistamento ed Invio
|
2026-05-30 12:12:14 +02:00
|
|
|
for (const reminder of reminders) {
|
|
|
|
|
const taskTitle = reminder.tasks?.title || 'Nuovo Task!';
|
2026-05-30 12:26:53 +02:00
|
|
|
const taskBody = reminder.tasks?.description || 'Hai un task in scadenza.';
|
|
|
|
|
const staffEmail = reminder.staff_members?.email;
|
|
|
|
|
const staffName = reminder.staff_members?.first_name || 'Staff';
|
2026-05-30 12:12:14 +02:00
|
|
|
|
2026-05-30 12:26:53 +02:00
|
|
|
if (reminder.channel === 'push') {
|
|
|
|
|
// --- LOGICA PUSH ---
|
|
|
|
|
const { data: devices } = await supabaseClient
|
|
|
|
|
.from('staff_devices')
|
|
|
|
|
.select('fcm_token')
|
|
|
|
|
.eq('staff_id', reminder.staff_id)
|
2026-05-30 12:12:14 +02:00
|
|
|
|
2026-05-30 12:26:53 +02:00
|
|
|
if (devices && devices.length > 0) {
|
|
|
|
|
for (const device of devices) {
|
|
|
|
|
try {
|
|
|
|
|
await fetch(
|
|
|
|
|
`https://fcm.googleapis.com/v1/projects/${credentials.project_id}/messages:send`,
|
|
|
|
|
{
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Authorization': `Bearer ${fcmAccessToken}`,
|
|
|
|
|
'Content-Type': 'application/json',
|
2026-05-30 12:12:14 +02:00
|
|
|
},
|
2026-05-30 12:26:53 +02:00
|
|
|
body: JSON.stringify({
|
|
|
|
|
message: {
|
|
|
|
|
token: device.fcm_token,
|
|
|
|
|
notification: { title: taskTitle, body: taskBody },
|
|
|
|
|
data: { click_action: 'FLUTTER_NOTIFICATION_CLICK', taskId: reminder.task_id },
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(`Errore FCM token ${device.fcm_token}:`, err)
|
|
|
|
|
}
|
2026-05-30 12:12:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-05-30 12:26:53 +02:00
|
|
|
} else if (reminder.channel === 'email' && staffEmail) {
|
|
|
|
|
// --- LOGICA EMAIL (RESEND) ---
|
|
|
|
|
try {
|
|
|
|
|
await fetch('https://api.resend.com/emails', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Authorization': `Bearer ${resendApiKey}`,
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
from: 'FLUX Reminders <onboarding@resend.dev>', // Sostituirai con la tua email verificata in futuro
|
|
|
|
|
to: staffEmail,
|
|
|
|
|
subject: `Promemoria: ${taskTitle}`,
|
|
|
|
|
html: `<p>Ciao ${staffName},</p>
|
|
|
|
|
<p>Questo è un promemoria per il task: <strong>${taskTitle}</strong>.</p>
|
|
|
|
|
<p>Dettagli: ${taskBody}</p>
|
|
|
|
|
<p><br>Il team FLUX</p>`,
|
|
|
|
|
}),
|
|
|
|
|
})
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(`Errore invio Email a ${staffEmail}:`, err)
|
|
|
|
|
}
|
2026-05-30 12:12:14 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-30 12:26:53 +02:00
|
|
|
// 6. Segna come inviato
|
2026-05-30 12:12:14 +02:00
|
|
|
await supabaseClient
|
|
|
|
|
.from('task_reminders')
|
|
|
|
|
.update({ is_sent: true, updated_at: new Date().toISOString() })
|
|
|
|
|
.eq('id', reminder.id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Response(JSON.stringify({ success: true, processed: reminders.length }), {
|
|
|
|
|
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
|
|
|
|
status: 200,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return new Response(JSON.stringify({ error: error.message }), {
|
|
|
|
|
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
|
|
|
|
status: 500,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|