mirror of
https://github.com/nicotsx/ironmount.git
synced 2025-12-10 12:10:51 +01:00
Feat/notifications alerts (#52)
* feat: notifications backend & creation * feat: assign notification to backup schedule * refactor: status dot one component * chore(notification-details): remove refetchInterval
This commit is contained in:
5
app/server/modules/notifications/builders/custom.ts
Normal file
5
app/server/modules/notifications/builders/custom.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
|
||||
export function buildCustomShoutrrrUrl(config: Extract<NotificationConfig, { type: "custom" }>): string {
|
||||
return config.shoutrrrUrl;
|
||||
}
|
||||
28
app/server/modules/notifications/builders/discord.ts
Normal file
28
app/server/modules/notifications/builders/discord.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
|
||||
export function buildDiscordShoutrrrUrl(config: Extract<NotificationConfig, { type: "discord" }>): string {
|
||||
const url = new URL(config.webhookUrl);
|
||||
const pathParts = url.pathname.split("/").filter(Boolean);
|
||||
|
||||
if (pathParts.length < 4 || pathParts[0] !== "api" || pathParts[1] !== "webhooks") {
|
||||
throw new Error("Invalid Discord webhook URL format");
|
||||
}
|
||||
|
||||
const [, , webhookId, webhookToken] = pathParts;
|
||||
|
||||
let shoutrrrUrl = `discord://${webhookToken}@${webhookId}`;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (config.username) {
|
||||
params.append("username", config.username);
|
||||
}
|
||||
if (config.avatarUrl) {
|
||||
params.append("avatar_url", config.avatarUrl);
|
||||
}
|
||||
|
||||
if (params.toString()) {
|
||||
shoutrrrUrl += `?${params.toString()}`;
|
||||
}
|
||||
|
||||
return shoutrrrUrl;
|
||||
}
|
||||
10
app/server/modules/notifications/builders/email.ts
Normal file
10
app/server/modules/notifications/builders/email.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
|
||||
export function buildEmailShoutrrrUrl(config: Extract<NotificationConfig, { type: "email" }>): string {
|
||||
const protocol = config.useTLS ? "smtps" : "smtp";
|
||||
const auth = `${encodeURIComponent(config.username)}:${encodeURIComponent(config.password)}`;
|
||||
const host = `${config.smtpHost}:${config.smtpPort}`;
|
||||
const toRecipients = config.to.map((email) => encodeURIComponent(email)).join(",");
|
||||
|
||||
return `${protocol}://${auth}@${host}/?from=${encodeURIComponent(config.from)}&to=${toRecipients}`;
|
||||
}
|
||||
15
app/server/modules/notifications/builders/gotify.ts
Normal file
15
app/server/modules/notifications/builders/gotify.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
|
||||
export function buildGotifyShoutrrrUrl(config: Extract<NotificationConfig, { type: "gotify" }>): string {
|
||||
const url = new URL(config.serverUrl);
|
||||
const hostname = url.hostname;
|
||||
const port = url.port ? `:${url.port}` : "";
|
||||
|
||||
let shoutrrrUrl = `gotify://${hostname}${port}/${config.token}`;
|
||||
|
||||
if (config.priority !== undefined) {
|
||||
shoutrrrUrl += `?priority=${config.priority}`;
|
||||
}
|
||||
|
||||
return shoutrrrUrl;
|
||||
}
|
||||
29
app/server/modules/notifications/builders/index.ts
Normal file
29
app/server/modules/notifications/builders/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
import { buildEmailShoutrrrUrl } from "./email";
|
||||
import { buildSlackShoutrrrUrl } from "./slack";
|
||||
import { buildDiscordShoutrrrUrl } from "./discord";
|
||||
import { buildGotifyShoutrrrUrl } from "./gotify";
|
||||
import { buildNtfyShoutrrrUrl } from "./ntfy";
|
||||
import { buildCustomShoutrrrUrl } from "./custom";
|
||||
|
||||
export function buildShoutrrrUrl(config: NotificationConfig): string {
|
||||
switch (config.type) {
|
||||
case "email":
|
||||
return buildEmailShoutrrrUrl(config);
|
||||
case "slack":
|
||||
return buildSlackShoutrrrUrl(config);
|
||||
case "discord":
|
||||
return buildDiscordShoutrrrUrl(config);
|
||||
case "gotify":
|
||||
return buildGotifyShoutrrrUrl(config);
|
||||
case "ntfy":
|
||||
return buildNtfyShoutrrrUrl(config);
|
||||
case "custom":
|
||||
return buildCustomShoutrrrUrl(config);
|
||||
default: {
|
||||
// TypeScript exhaustiveness check
|
||||
const _exhaustive: never = config;
|
||||
throw new Error(`Unsupported notification type: ${(_exhaustive as NotificationConfig).type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
app/server/modules/notifications/builders/ntfy.ts
Normal file
28
app/server/modules/notifications/builders/ntfy.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
|
||||
export function buildNtfyShoutrrrUrl(config: Extract<NotificationConfig, { type: "ntfy" }>): string {
|
||||
let shoutrrrUrl: string;
|
||||
|
||||
if (config.serverUrl) {
|
||||
const url = new URL(config.serverUrl);
|
||||
const hostname = url.hostname;
|
||||
const port = url.port ? `:${url.port}` : "";
|
||||
shoutrrrUrl = `ntfy://${hostname}${port}/${config.topic}`;
|
||||
} else {
|
||||
shoutrrrUrl = `ntfy://ntfy.sh/${config.topic}`;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (config.token) {
|
||||
params.append("token", config.token);
|
||||
}
|
||||
if (config.priority) {
|
||||
params.append("priority", config.priority);
|
||||
}
|
||||
|
||||
if (params.toString()) {
|
||||
shoutrrrUrl += `?${params.toString()}`;
|
||||
}
|
||||
|
||||
return shoutrrrUrl;
|
||||
}
|
||||
31
app/server/modules/notifications/builders/slack.ts
Normal file
31
app/server/modules/notifications/builders/slack.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
|
||||
export function buildSlackShoutrrrUrl(config: Extract<NotificationConfig, { type: "slack" }>): string {
|
||||
const url = new URL(config.webhookUrl);
|
||||
const pathParts = url.pathname.split("/").filter(Boolean);
|
||||
|
||||
if (pathParts.length < 4 || pathParts[0] !== "services") {
|
||||
throw new Error("Invalid Slack webhook URL format");
|
||||
}
|
||||
|
||||
const [, tokenA, tokenB, tokenC] = pathParts;
|
||||
|
||||
let shoutrrrUrl = `slack://hook:${tokenA}-${tokenB}-${tokenC}@webhook`;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (config.channel) {
|
||||
params.append("channel", config.channel);
|
||||
}
|
||||
if (config.username) {
|
||||
params.append("username", config.username);
|
||||
}
|
||||
if (config.iconEmoji) {
|
||||
params.append("icon", config.iconEmoji);
|
||||
}
|
||||
|
||||
if (params.toString()) {
|
||||
shoutrrrUrl += `?${params.toString()}`;
|
||||
}
|
||||
|
||||
return shoutrrrUrl;
|
||||
}
|
||||
Reference in New Issue
Block a user