Files
gpt-engineer-app[bot] a1280ddd05 Migrate Novu functions to wrapEdgeFunction
Refactor Phase 3 Batch 2–4 Novu-related functions to use the createEdgeFunction wrapper, replacing explicit HTTP servers with edge wrapper, adding standardized logging, tracing, and error handling across subscriber management, topic/notification, and migration/sync functions.
2025-11-11 03:55:02 +00:00

135 lines
4.4 KiB
TypeScript

import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4";
import { corsHeadersWithTracing as corsHeaders } from '../_shared/cors.ts';
import { edgeLogger } from "../_shared/logger.ts";
import { createEdgeFunction } from '../_shared/edgeFunctionWrapper.ts';
interface AnnouncementPayload {
title: string;
message: string;
severity: 'info' | 'warning' | 'critical';
actionUrl?: string;
}
export default createEdgeFunction(
{
name: 'notify-system-announcement',
requireAuth: true,
corsHeaders: corsHeaders
},
async (req, context) => {
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const supabase = createClient(supabaseUrl, supabaseServiceKey);
context.span.setAttribute('action', 'notify_system_announcement');
// Check user role
const { data: roles, error: roleError } = await supabase
.from('user_roles')
.select('role')
.eq('user_id', context.userId)
.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', context.userId)
.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');
}
edgeLogger.info('Processing system announcement', {
action: 'notify_system_announcement',
title: payload.title,
severity: payload.severity,
publishedBy: profile?.username || 'unknown',
requestId: context.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) {
edgeLogger.error('Error fetching workflow', { action: 'notify_system_announcement', requestId: context.requestId, error: templateError });
throw new Error(`Failed to fetch workflow: ${templateError.message}`);
}
if (!template) {
edgeLogger.warn('No active system-announcement workflow found', { action: 'notify_system_announcement', requestId: context.requestId });
throw new Error('No active system-announcement workflow configured');
}
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,
};
edgeLogger.info('Triggering announcement to all users via "users" topic', { action: 'notify_system_announcement', requestId: context.requestId });
// 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) {
edgeLogger.error('Error triggering notification', { action: 'notify_system_announcement', requestId: context.requestId, error: notifyError });
throw notifyError;
}
edgeLogger.info('System announcement triggered successfully', { action: 'notify_system_announcement', requestId: context.requestId, result });
return new Response(
JSON.stringify({
success: true,
transactionId: result?.transactionId,
announcementId,
payload: notificationPayload,
}),
{
headers: {
'Content-Type': 'application/json',
},
status: 200,
}
);
}
);