wulf-pulse/app/api/notification-channels/[id]/test/route.ts

121 lines
4.2 KiB
TypeScript

/**
* Test Notification Channel — send a test message.
*/
import { NextRequest, NextResponse } from 'next/server';
import { postgresClient } from '@/lib/services/postgres-client';
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await params;
const result = await postgresClient.query(
`SELECT * FROM notification_channels WHERE id = $1`, [id]
);
if (result.rows.length === 0) {
return NextResponse.json({ error: 'Channel not found' }, { status: 404 });
}
const channel = result.rows[0];
const testMessage = `🧪 Test notification from Pulse Pipeline Engine\nChannel: ${channel.name}\nTime: ${new Date().toISOString()}`;
let resp: Response;
switch (channel.channel_type) {
case 'teams': {
if (!channel.config.webhook_url) {
return NextResponse.json({ error: 'Missing webhook_url in channel config' }, { status: 400 });
}
const card = {
type: 'message',
attachments: [{
contentType: 'application/vnd.microsoft.card.adaptive',
content: {
type: 'AdaptiveCard',
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
version: '1.4',
body: [
{ type: 'TextBlock', text: '🧪 Pulse Test Notification', weight: 'bolder', size: 'medium' },
{ type: 'TextBlock', text: `Channel: ${channel.name}`, wrap: true },
{ type: 'TextBlock', text: `Time: ${new Date().toISOString()}`, size: 'small', isSubtle: true },
],
},
}],
};
resp = await fetch(channel.config.webhook_url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(card),
});
break;
}
case 'telegram': {
if (!channel.config.bot_token || !channel.config.chat_id) {
return NextResponse.json({ error: 'Missing bot_token or chat_id in channel config' }, { status: 400 });
}
resp = await fetch(`https://api.telegram.org/bot${channel.config.bot_token}/sendMessage`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
chat_id: channel.config.chat_id,
text: testMessage,
parse_mode: channel.config.parse_mode || 'HTML',
}),
});
break;
}
case 'ntfy': {
if (!channel.config.topic) {
return NextResponse.json({ error: 'Missing topic in channel config' }, { status: 400 });
}
const serverUrl = channel.config.server_url || 'https://ntfy.sh';
const headers: Record<string, string> = {
'Title': 'Pulse Test Notification',
'Priority': 'default',
'Tags': 'test_tube',
};
if (channel.config.auth_token) {
headers['Authorization'] = `Bearer ${channel.config.auth_token}`;
}
resp = await fetch(`${serverUrl}/${channel.config.topic}`, {
method: 'POST',
headers,
body: testMessage,
});
break;
}
case 'webhook': {
if (!channel.config.url) {
return NextResponse.json({ error: 'Missing url in channel config' }, { status: 400 });
}
resp = await fetch(channel.config.url, {
method: channel.config.method || 'POST',
headers: {
'Content-Type': 'application/json',
...(channel.config.headers || {}),
},
body: JSON.stringify({ test: true, message: testMessage, timestamp: new Date().toISOString() }),
});
break;
}
default:
return NextResponse.json({ error: `Unknown channel type: ${channel.channel_type}` }, { status: 400 });
}
const status = resp.status;
const responseText = await resp.text();
return NextResponse.json({
success: resp.ok,
status,
response: responseText.substring(0, 500),
});
} catch (error) {
const msg = error instanceof Error ? error.message : String(error);
return NextResponse.json({ error: msg }, { status: 500 });
}
}