diff --git a/apps/dashboard/src/components/forms/notifications/form-pushover.tsx b/apps/dashboard/src/components/forms/notifications/form-pushover.tsx
index 62aeba7a..b11e10b8 100644
--- a/apps/dashboard/src/components/forms/notifications/form-pushover.tsx
+++ b/apps/dashboard/src/components/forms/notifications/form-pushover.tsx
@@ -35,10 +35,18 @@ import { useFormSheetDirty } from "@/components/forms/form-sheet";
import { CheckboxTree } from "@/components/ui/checkbox-tree";
import { useTRPC } from "@/lib/trpc/client";
+const pushoverKeySchema = z
+ .string()
+ .regex(/^[A-Za-z0-9]{30}$/, "Must be exactly 30 alphanumeric characters");
+
const schema = z.object({
name: z.string(),
provider: z.literal("pushover"),
- data: z.record(z.string(), z.string()),
+ data: z.object({
+ token: pushoverKeySchema,
+ user: pushoverKeySchema,
+ priority: z.string(),
+ }),
monitors: z.array(z.number()),
});
@@ -115,6 +123,8 @@ export function FormPushover({
if (isPending) return;
startTransition(async () => {
+ const valid = await form.trigger(["data.token", "data.user"]);
+ if (!valid) return;
try {
const provider = form.getValues("provider");
const data = form.getValues("data");
@@ -173,6 +183,7 @@ export function FormPushover({
Application API Token
@@ -192,6 +203,7 @@ export function FormPushover({
User / Group Key
diff --git a/packages/db/src/schema/notifications/validation.ts b/packages/db/src/schema/notifications/validation.ts
index 5a3ad883..3aa0ad98 100644
--- a/packages/db/src/schema/notifications/validation.ts
+++ b/packages/db/src/schema/notifications/validation.ts
@@ -92,6 +92,8 @@ export const pushoverDataSchema = z.object({
pushover: z.object({
token: pushoverKeySchema,
user: pushoverKeySchema,
+ // Emergency priority (2) is excluded: it requires extra retry/expire
+ // params and acknowledgement handling not surfaced here.
priority: z.coerce.number().int().min(-2).max(1).prefault(0),
}),
});
diff --git a/packages/notifications/pushover/src/index.ts b/packages/notifications/pushover/src/index.ts
index c64e328a..b66cdf3a 100644
--- a/packages/notifications/pushover/src/index.ts
+++ b/packages/notifications/pushover/src/index.ts
@@ -42,13 +42,16 @@ async function send({
}
}
+const parsePushover = (data: string) =>
+ pushoverDataSchema.parse(JSON.parse(data)).pushover;
+
export const sendAlert = async ({
monitor,
notification,
statusCode,
message,
}: NotificationContext) => {
- const { pushover } = pushoverDataSchema.parse(JSON.parse(notification.data));
+ const pushover = parsePushover(notification.data);
await send({
...pushover,
title: `${monitor.name} is down`,
@@ -63,7 +66,7 @@ export const sendRecovery = async ({
monitor,
notification,
}: NotificationContext) => {
- const { pushover } = pushoverDataSchema.parse(JSON.parse(notification.data));
+ const pushover = parsePushover(notification.data);
// Recovery is never urgent: force normal priority so "up again" can't
// bypass the user's Pushover quiet hours.
await send({
@@ -79,7 +82,7 @@ export const sendDegraded = async ({
monitor,
notification,
}: NotificationContext) => {
- const { pushover } = pushoverDataSchema.parse(JSON.parse(notification.data));
+ const pushover = parsePushover(notification.data);
await send({
...pushover,
title: `${monitor.name} is degraded`,