393 lines · 8.8 KB · TypeScript
RawDownload
import type {	custom,	discord,	email,	gotify,	lark,	mattermost,	ntfy,	pushover,	resend,	slack,	teams,	telegram,} from "@dokploy/server/db/schema";import nodemailer from "nodemailer";import { Resend } from "resend"; export const sendEmailNotification = async (	connection: typeof email.$inferInsert,	subject: string,	htmlContent: string,	attachments?: { filename: string; content: Buffer }[],) => {	try {		const {			smtpServer,			smtpPort,			username,			password,			fromAddress,			toAddresses,		} = connection;		const transporter = nodemailer.createTransport({			host: smtpServer,			port: smtpPort,			auth: { user: username, pass: password },		}); 		await transporter.sendMail({			from: fromAddress,			to: toAddresses.join(", "),			subject,			html: htmlContent,			textEncoding: "base64",			attachments,		});	} catch (err) {		console.log(err);		throw new Error(			`Failed to send email notification ${err instanceof Error ? err.message : "Unknown error"}`,		);	}}; export const sendResendNotification = async (	connection: typeof resend.$inferInsert,	subject: string,	htmlContent: string,) => {	try {		const client = new Resend(connection.apiKey); 		const result = await client.emails.send({			from: connection.fromAddress,			to: connection.toAddresses,			subject,			html: htmlContent,		}); 		if (result.error) {			throw new Error(result.error.message);		}	} catch (err) {		console.log(err);		throw new Error(			`Failed to send Resend notification ${err instanceof Error ? err.message : "Unknown error"}`,		);	}}; export const sendDiscordNotification = async (	connection: typeof discord.$inferInsert,	embed: any,) => {	try {		const response = await fetch(connection.webhookUrl, {			method: "POST",			headers: { "Content-Type": "application/json" },			body: JSON.stringify({ embeds: [embed] }),		});		if (!response.ok) {			throw new Error(				`Failed to send discord notification ${response.statusText}`,			);		}	} catch (err) {		console.log("error", err);		throw new Error(			`Failed to send discord notification ${err instanceof Error ? err.message : "Unknown error"}`,		);	}}; export const sendTelegramNotification = async (	connection: typeof telegram.$inferInsert,	messageText: string,	inlineButton?: {		text: string;		url: string;	}[][],) => {	try {		const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;		await fetch(url, {			method: "POST",			headers: { "Content-Type": "application/json" },			body: JSON.stringify({				chat_id: connection.chatId,				message_thread_id: connection.messageThreadId,				text: messageText,				parse_mode: "HTML",				disable_web_page_preview: true,				reply_markup: {					inline_keyboard: inlineButton,				},			}),		});	} catch (err) {		console.log(err);	}}; export const sendSlackNotification = async (	connection: typeof slack.$inferInsert,	message: any,) => {	try {		const response = await fetch(connection.webhookUrl, {			method: "POST",			headers: { "Content-Type": "application/json" },			body: JSON.stringify(message),		});		if (!response.ok) {			throw new Error(				`Failed to send slack notification ${response.statusText}`,			);		}	} catch (err) {		console.log("error", err);		throw new Error(			`Failed to send slack notification ${err instanceof Error ? err.message : "Unknown error"}`,		);	}}; export const sendGotifyNotification = async (	connection: typeof gotify.$inferInsert,	title: string,	message: string,) => {	const response = await fetch(`${connection.serverUrl}/message`, {		method: "POST",		headers: {			"Content-Type": "application/json",			"X-Gotify-Key": connection.appToken,		},		body: JSON.stringify({			title: title,			message: message,			priority: connection.priority,			extras: {				"client::display": {					contentType: "text/plain",				},			},		}),	}); 	if (!response.ok) {		throw new Error(			`Failed to send Gotify notification: ${response.statusText}`,		);	}}; export const sendNtfyNotification = async (	connection: typeof ntfy.$inferInsert,	title: string,	tags: string,	actions: string,	message: string,) => {	const response = await fetch(`${connection.serverUrl}/${connection.topic}`, {		method: "POST",		headers: {			...(connection.accessToken && {				Authorization: `Bearer ${connection.accessToken}`,			}),			"X-Priority": connection.priority?.toString() || "3",			"X-Title": title,			"X-Tags": tags,			"X-Actions": actions,		},		body: message,	}); 	if (!response.ok) {		throw new Error(`Failed to send ntfy notification: ${response.statusText}`);	}}; export const sendMattermostNotification = async (	connection: typeof mattermost.$inferInsert,	message: any,) => {	const payload = {		...message,		// Only include username if it's provided and not empty		...(message.username?.trim() && { username: message.username }),		// Only include channel if it's provided and not empty		...(message.channel?.trim() && {			channel: `#${message.channel.replace("#", "")}`,		}),	}; 	const response = await fetch(connection.webhookUrl, {		method: "POST",		headers: { "Content-Type": "application/json" },		body: JSON.stringify(payload),	}); 	if (!response.ok) {		throw new Error(			`Failed to send Mattermost notification: ${response.statusText}`,		);	}}; export const sendCustomNotification = async (	connection: typeof custom.$inferInsert,	payload: Record<string, any>,) => {	try {		// Merge default headers with custom headers (now already an object from jsonb)		const headers: Record<string, string> = {			"Content-Type": "application/json",			...(connection.headers || {}),		}; 		// Default body with payload		const body = JSON.stringify(payload); 		const response = await fetch(connection.endpoint, {			method: "POST",			headers,			body,		}); 		if (!response.ok) {			throw new Error(				`Failed to send custom notification: ${response.statusText}`,			);		} 		return response;	} catch (error) {		console.error("Error sending custom notification:", error);		throw error;	}}; export const sendLarkNotification = async (	connection: typeof lark.$inferInsert,	message: any,) => {	try {		await fetch(connection.webhookUrl, {			method: "POST",			headers: { "Content-Type": "application/json" },			body: JSON.stringify(message),		});	} catch (err) {		console.log(err);	}}; export interface TeamsAdaptiveCardMessage {	title: string;	themeColor?: string;	facts?: { name: string; value: string }[];	potentialAction?: { type: "Action.OpenUrl"; title: string; url: string };} export const sendTeamsNotification = async (	connection: typeof teams.$inferInsert,	message: TeamsAdaptiveCardMessage,) => {	try {		const bodyElements: Record<string, unknown>[] = [			{				type: "TextBlock",				text: message.title,				size: "Medium",				weight: "Bolder",				wrap: true,			},		]; 		if (message.facts && message.facts.length > 0) {			bodyElements.push({				type: "FactSet",				facts: message.facts.map((f) => ({					title: f.name,					value: f.value,				})),			});		} 		const cardContent: Record<string, unknown> = {			type: "AdaptiveCard",			$schema: "http://adaptivecards.io/schemas/adaptive-card.json",			version: "1.4",			body: bodyElements,		}; 		if (message.potentialAction) {			cardContent.actions = [				{					type: "Action.OpenUrl",					title: message.potentialAction.title,					url: message.potentialAction.url,				},			];		} 		const payload = {			type: "message",			attachments: [				{					contentType: "application/vnd.microsoft.card.adaptive",					content: cardContent,				},			],		}; 		const response = await fetch(connection.webhookUrl, {			method: "POST",			headers: { "Content-Type": "application/json" },			body: JSON.stringify(payload),		}); 		if (!response.ok) {			throw new Error(				`Failed to send Teams notification: ${response.statusText}`,			);		}	} catch (err) {		console.log(err);		throw new Error(			`Failed to send Teams notification ${err instanceof Error ? err.message : "Unknown error"}`,		);	}}; export const sendPushoverNotification = async (	connection: typeof pushover.$inferInsert,	title: string,	message: string,) => {	const formData = new URLSearchParams();	formData.append("token", connection.apiToken);	formData.append("user", connection.userKey);	formData.append("title", title);	formData.append("message", message);	formData.append("priority", connection.priority?.toString() || "0"); 	// For emergency priority (2), retry and expire are required	if (connection.priority === 2) {		formData.append("retry", connection.retry?.toString() || "30");		formData.append("expire", connection.expire?.toString() || "3600");	} 	const response = await fetch("https://api.pushover.net/1/messages.json", {		method: "POST",		body: formData,	}); 	if (!response.ok) {		throw new Error(			`Failed to send Pushover notification: ${response.statusText}`,		);	}};