diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 7c299809b..51c10232b 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -5,6 +5,7 @@ import { render } from "@react-email/components"; import { format } from "date-fns"; import { and, eq } from "drizzle-orm"; import { + formatTelegramErrorMessage, sendCustomNotification, sendDiscordNotification, sendEmailNotification, @@ -201,7 +202,7 @@ export const sendBuildErrorNotifications = async ({ await sendTelegramNotification( telegram, - `⚠️ Build Failed\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}\n\nError:\n
${errorMessage}`,
+ `⚠️ Build Failed\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}\n\nError:\n${formatTelegramErrorMessage(errorMessage)}`,
inlineButton,
);
}
diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts
index 7a838ff46..70c111947 100644
--- a/packages/server/src/utils/notifications/database-backup.ts
+++ b/packages/server/src/utils/notifications/database-backup.ts
@@ -5,6 +5,7 @@ import { render } from "@react-email/components";
import { format } from "date-fns";
import { and, eq } from "drizzle-orm";
import {
+ formatTelegramErrorMessage,
sendCustomNotification,
sendDiscordNotification,
sendEmailNotification,
@@ -208,7 +209,7 @@ export const sendDatabaseBackupNotifications = async ({
const statusEmoji = type === "success" ? "✅" : "❌";
const typeStatus = type === "success" ? "Successful" : "Failed";
const errorMsg = isError
- ? `\n\nError:\n${errorMessage}`
+ ? `\n\nError:\n${formatTelegramErrorMessage(errorMessage)}`
: "";
const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDatabase Name: ${databaseName}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`;
diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts
index 98d4cec53..38a3cc068 100644
--- a/packages/server/src/utils/notifications/docker-cleanup.ts
+++ b/packages/server/src/utils/notifications/docker-cleanup.ts
@@ -5,6 +5,7 @@ import { render } from "@react-email/components";
import { format } from "date-fns";
import { and, eq } from "drizzle-orm";
import {
+ escapeHtml,
sendCustomNotification,
sendDiscordNotification,
sendEmailNotification,
@@ -143,7 +144,7 @@ export const sendDockerCleanupNotifications = async (
if (telegram) {
await sendTelegramNotification(
telegram,
- `✅ Docker Cleanup\n\nMessage: ${message}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`,
+ `✅ Docker Cleanup\n\nMessage: ${escapeHtml(message)}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`,
);
}
diff --git a/packages/server/src/utils/notifications/dokploy-backup.ts b/packages/server/src/utils/notifications/dokploy-backup.ts
index e838534f2..ab5e8a22e 100644
--- a/packages/server/src/utils/notifications/dokploy-backup.ts
+++ b/packages/server/src/utils/notifications/dokploy-backup.ts
@@ -5,6 +5,7 @@ import { render } from "@react-email/components";
import { format } from "date-fns";
import { eq } from "drizzle-orm";
import {
+ formatTelegramErrorMessage,
sendCustomNotification,
sendDiscordNotification,
sendEmailNotification,
@@ -186,7 +187,7 @@ export const sendDokployBackupNotifications = async ({
const statusEmoji = type === "success" ? "✅" : "❌";
const typeStatus = type === "success" ? "Successful" : "Failed";
const errorMsg = isError
- ? `\n\nError:\n${errorMessage}`
+ ? `\n\nError:\n${formatTelegramErrorMessage(errorMessage)}`
: "";
const sizeInfo = backupSize
? `\nBackup Size: ${backupSize}`
diff --git a/packages/server/src/utils/notifications/server-threshold.ts b/packages/server/src/utils/notifications/server-threshold.ts
index a8749a349..a32217204 100644
--- a/packages/server/src/utils/notifications/server-threshold.ts
+++ b/packages/server/src/utils/notifications/server-threshold.ts
@@ -4,6 +4,7 @@ import { db } from "../../db";
import { notifications } from "../../db/schema";
import ServerThresholdEmail from "../../emails/emails/server-threshold";
import {
+ escapeHtml,
sendCustomNotification,
sendDiscordNotification,
sendEmailNotification,
@@ -193,7 +194,7 @@ export const sendServerThresholdNotifications = async (
Type: ${payload.Type}
Current Value: ${payload.Value.toFixed(2)}%
Threshold: ${payload.Threshold.toFixed(2)}%
- Message: ${payload.Message}
+ Message: ${escapeHtml(payload.Message)}
Time: ${date.toLocaleString()}
`,
);
diff --git a/packages/server/src/utils/notifications/utils.ts b/packages/server/src/utils/notifications/utils.ts
index d65258862..506713352 100644
--- a/packages/server/src/utils/notifications/utils.ts
+++ b/packages/server/src/utils/notifications/utils.ts
@@ -101,6 +101,30 @@ export const sendDiscordNotification = async (
}
};
+export const escapeHtml = (s: string) =>
+ s.replace(/&/g, "&").replace(//g, ">");
+
+export const formatTelegramErrorMessage = (errorMessage: string) => {
+ const escaped = escapeHtml(errorMessage);
+ const limit = 3000;
+ if (escaped.length <= limit) return escaped;
+ let end = limit;
+ const ampIdx = escaped.lastIndexOf("&", end);
+ if (ampIdx !== -1 && escaped.indexOf(";", ampIdx) > end) {
+ end = ampIdx;
+ }
+ if (
+ end > 0 &&
+ end < escaped.length &&
+ isHighSurrogate(escaped.charCodeAt(end - 1))
+ ) {
+ end--;
+ }
+ return `${escaped.substring(0, end)}…`;
+};
+
+const isHighSurrogate = (code: number) => code >= 0xd800 && code <= 0xdbff;
+
export const sendTelegramNotification = async (
connection: typeof telegram.$inferInsert,
messageText: string,
@@ -111,7 +135,7 @@ export const sendTelegramNotification = async (
) => {
try {
const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;
- await fetch(url, {
+ const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
@@ -125,8 +149,17 @@ export const sendTelegramNotification = async (
},
}),
});
+ if (!response.ok) {
+ const body = await response.text();
+ throw new Error(
+ `Failed to send telegram notification: ${response.status} ${body}`,
+ );
+ }
} catch (err) {
console.log(err);
+ throw new Error(
+ `Failed to send telegram notification ${err instanceof Error ? err.message : "Unknown error"}`,
+ );
}
};
diff --git a/packages/server/src/utils/notifications/volume-backup.ts b/packages/server/src/utils/notifications/volume-backup.ts
index cdb1e3a96..2eb9c3c5f 100644
--- a/packages/server/src/utils/notifications/volume-backup.ts
+++ b/packages/server/src/utils/notifications/volume-backup.ts
@@ -5,6 +5,7 @@ import { render } from "@react-email/components";
import { format } from "date-fns";
import { and, eq } from "drizzle-orm";
import {
+ formatTelegramErrorMessage,
sendCustomNotification,
sendDiscordNotification,
sendEmailNotification,
@@ -223,7 +224,7 @@ export const sendVolumeBackupNotifications = async ({
const statusEmoji = type === "success" ? "✅" : "❌";
const typeStatus = type === "success" ? "Successful" : "Failed";
const errorMsg = isError
- ? `\n\nError:\n${errorMessage}`
+ ? `\n\nError:\n${formatTelegramErrorMessage(errorMessage)}`
: "";
const sizeInfo = backupSize
? `\nBackup Size: ${backupSize}`