import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; import { startRequest, endRequest } from "../_shared/logger.ts"; const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-request-id', }; interface AnnouncementPayload { title: string; message: string; severity: 'info' | 'warning' | 'critical'; actionUrl?: string; } serve(async (req) => { if (req.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } const tracking = startRequest('notify-system-announcement'); try { const supabaseUrl = Deno.env.get('SUPABASE_URL')!; const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; const supabase = createClient(supabaseUrl, supabaseServiceKey); // Get authorization header const authHeader = req.headers.get('authorization'); if (!authHeader) { throw new Error('Authorization header required'); } // Verify user is admin or superuser const token = authHeader.replace('Bearer ', ''); const { data: { user }, error: authError } = await supabase.auth.getUser(token); if (authError || !user) { throw new Error('Unauthorized: Invalid token'); } // Check user role const { data: roles, error: roleError } = await supabase .from('user_roles') .select('role') .eq('user_id', user.id) .in('role', ['admin', 'superuser']); if (roleError || !roles || roles.length === 0) { throw new Error('Unauthorized: Admin or superuser role required'); } // Get user profile for logging const { data: profile } = await supabase .from('profiles') .select('username, display_name') .eq('user_id', user.id) .single(); const payload: AnnouncementPayload = await req.json(); // Validate required fields if (!payload.title || !payload.message || !payload.severity) { throw new Error('Missing required fields: title, message, or severity'); } if (!['info', 'warning', 'critical'].includes(payload.severity)) { throw new Error('Invalid severity level. Must be: info, warning, or critical'); } console.log('Processing system announcement:', { title: payload.title, severity: payload.severity, publishedBy: profile?.username || 'unknown', requestId: tracking.requestId }); // Fetch the workflow ID for system announcements const { data: template, error: templateError } = await supabase .from('notification_templates') .select('workflow_id') .eq('workflow_id', 'system-announcement') .eq('is_active', true) .maybeSingle(); if (templateError) { console.error('Error fetching workflow:', templateError); throw new Error(`Failed to fetch workflow: ${templateError.message}`); } if (!template) { console.warn('No active system-announcement workflow found'); return new Response( JSON.stringify({ success: false, error: 'No active system-announcement workflow configured', }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 400, } ); } const announcementId = crypto.randomUUID(); const publishedAt = new Date().toISOString(); const publishedBy = profile?.display_name || profile?.username || 'System Admin'; // Build notification payload for all users const notificationPayload = { baseUrl: 'https://www.thrillwiki.com', announcementId, title: payload.title, message: payload.message, severity: payload.severity, actionUrl: payload.actionUrl || '', publishedAt, publishedBy, }; console.log('Triggering announcement to all users via "users" topic'); // Invoke the trigger-notification function with users topic const { data: result, error: notifyError } = await supabase.functions.invoke( 'trigger-notification', { body: { workflowId: template.workflow_id, topicKey: 'users', payload: notificationPayload, }, } ); if (notifyError) { console.error('Error triggering notification:', notifyError); throw notifyError; } console.log('System announcement triggered successfully:', result); endRequest(tracking, 200); return new Response( JSON.stringify({ success: true, transactionId: result?.transactionId, announcementId, payload: notificationPayload, requestId: tracking.requestId }), { headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }, status: 200, } ); } catch (error: any) { console.error('Error in notify-system-announcement:', error); endRequest(tracking, error.message.includes('Unauthorized') ? 403 : 500, error.message); return new Response( JSON.stringify({ success: false, error: error.message, requestId: tracking.requestId }), { headers: { ...corsHeaders, 'Content-Type': 'application/json', 'X-Request-ID': tracking.requestId }, status: error.message.includes('Unauthorized') ? 403 : 500, } ); } });