Merge remote-tracking branch 'origin/develop' into feat/pagespeed-mobile-strat
This commit is contained in:
commit
78fdc548bb
395 files changed
+60597
-29063
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { passwordPattern, nameValidation, lowercaseEmailValidation } from "./shared.js";
|
||||
import { UserRoles } from "@/types/user.js";
|
||||
|
||||
//****************************************
|
||||
// Auth Validations
|
||||
@@ -41,7 +42,7 @@ export const newPasswordValidation = z.object({
|
||||
|
||||
export const inviteBodyValidation = z.object({
|
||||
email: z.email("Must be a valid email address"),
|
||||
role: z.array(z.string()).min(1, "At least one role is required"),
|
||||
role: z.array(z.enum(UserRoles)).min(1, "At least one role is required"),
|
||||
teamId: z.string().min(1, "Team ID is required"),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { booleanCoercion } from "./shared.js";
|
||||
import { GeoContinents } from "@/types/geoCheck.js";
|
||||
import { MonitorTypes } from "@/types/monitor.js";
|
||||
|
||||
//****************************************
|
||||
// Check Validations
|
||||
@@ -11,7 +12,7 @@ export const getChecksParamValidation = z.object({
|
||||
});
|
||||
|
||||
export const getChecksQueryValidation = z.object({
|
||||
type: z.enum(["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket"]).optional(),
|
||||
type: z.enum(MonitorTypes).optional(),
|
||||
sortOrder: z.enum(["asc", "desc"]),
|
||||
limit: z.coerce.number().optional(),
|
||||
dateRange: z.enum(["recent", "hour", "day", "week", "month", "all"]),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { DbTypes } from "@/types/settings.js";
|
||||
import { booleanCoercion } from "./shared.js";
|
||||
|
||||
const envSchema = z.object({
|
||||
// Server Configuration
|
||||
@@ -20,6 +21,9 @@ const envSchema = z.object({
|
||||
|
||||
// Optional
|
||||
ORIGIN: z.string().optional(),
|
||||
|
||||
// Feature flags
|
||||
STATUS_PAGE_THEMES_ENABLED: booleanCoercion.default(true),
|
||||
});
|
||||
|
||||
export type ValidatedEnv = z.infer<typeof envSchema>;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { booleanCoercion } from "./shared.js";
|
||||
import { DurationUnits } from "@/types/maintenanceWindow.js";
|
||||
|
||||
const dateToString = z.coerce.date().transform((d) => d.toISOString());
|
||||
|
||||
@@ -7,17 +8,37 @@ const dateToString = z.coerce.date().transform((d) => d.toISOString());
|
||||
// Maintenance Window Validations
|
||||
//****************************************
|
||||
|
||||
export const createMaintenanceWindowBodyValidation = z.object({
|
||||
monitors: z.array(z.string()).min(1, "At least one monitor is required"),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
active: z.boolean().optional(),
|
||||
duration: z.number().min(1, "Duration is required"),
|
||||
durationUnit: z.enum(["seconds", "minutes", "hours", "days"]),
|
||||
start: dateToString,
|
||||
end: dateToString,
|
||||
repeat: z.number().min(0, "Repeat must be a non-negative number"),
|
||||
expiry: dateToString.optional(),
|
||||
});
|
||||
export const createMaintenanceWindowBodyValidation = z
|
||||
.object({
|
||||
monitors: z.array(z.string()).min(1, "At least one monitor is required"),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
active: z.boolean().optional(),
|
||||
duration: z.number().min(1, "Duration is required"),
|
||||
durationUnit: z.enum(DurationUnits),
|
||||
start: dateToString,
|
||||
end: dateToString,
|
||||
repeat: z.number().min(0, "Repeat must be a non-negative number"),
|
||||
expiry: dateToString.optional(),
|
||||
})
|
||||
.strict()
|
||||
.superRefine((data, ctx) => {
|
||||
const start = new Date(data.start).getTime();
|
||||
const end = new Date(data.end).getTime();
|
||||
if (end <= start) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "End must be after start",
|
||||
path: ["end"],
|
||||
});
|
||||
}
|
||||
if (data.repeat === 0 && end <= Date.now()) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "End must be in the future for one-time maintenance windows",
|
||||
path: ["end"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const getMaintenanceWindowByIdParamValidation = z.object({
|
||||
id: z.string().min(1, "ID is required"),
|
||||
@@ -43,14 +64,29 @@ export const editMaintenanceWindowByIdParamValidation = z.object({
|
||||
id: z.string().min(1, "ID is required"),
|
||||
});
|
||||
|
||||
export const editMaintenanceByIdWindowBodyValidation = z.object({
|
||||
active: z.boolean().optional(),
|
||||
name: z.string().optional(),
|
||||
repeat: z.number().optional(),
|
||||
start: dateToString.optional(),
|
||||
end: dateToString.optional(),
|
||||
expiry: dateToString.optional(),
|
||||
monitors: z.array(z.string()).optional(),
|
||||
duration: z.number().optional(),
|
||||
durationUnit: z.enum(["seconds", "minutes", "hours", "days"]).optional(),
|
||||
});
|
||||
export const editMaintenanceByIdWindowBodyValidation = z
|
||||
.object({
|
||||
active: z.boolean().optional(),
|
||||
name: z.string().optional(),
|
||||
repeat: z.number().optional(),
|
||||
start: dateToString.optional(),
|
||||
end: dateToString.optional(),
|
||||
expiry: dateToString.optional(),
|
||||
monitors: z.array(z.string()).optional(),
|
||||
duration: z.number().optional(),
|
||||
durationUnit: z.enum(DurationUnits).optional(),
|
||||
})
|
||||
.strict()
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.start && data.end) {
|
||||
const start = new Date(data.start).getTime();
|
||||
const end = new Date(data.end).getTime();
|
||||
if (end <= start) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "End must be after start",
|
||||
path: ["end"],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { booleanCoercion } from "./shared.js";
|
||||
import { booleanCoercion, dnsHostnameRegex, dnsServerValidation } from "./shared.js";
|
||||
import { GeoContinents } from "@/types/geoCheck.js";
|
||||
import { MonitorMatchMethods, MonitorTypes, PageSpeedStrategies, DefaultPageSpeedStrategy } from "@/types/monitor.js";
|
||||
import { DefaultPageSpeedStrategy, DnsRecordTypes, MonitorMatchMethods, MonitorStatuses, MonitorTypes, PageSpeedStrategies } from "@/types/monitor.js";
|
||||
|
||||
export const getMonitorByIdParamValidation = z.object({
|
||||
monitorId: z.string().min(1, "Monitor ID is required"),
|
||||
@@ -20,12 +20,7 @@ export const getMonitorByIdQueryValidation = z.object({
|
||||
export const getMonitorsByTeamIdParamValidation = z.object({});
|
||||
|
||||
export const getMonitorsByTeamIdQueryValidation = z.object({
|
||||
type: z
|
||||
.union([
|
||||
z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"]),
|
||||
z.array(z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"])),
|
||||
])
|
||||
.optional(),
|
||||
type: z.union([z.enum(MonitorTypes), z.array(z.enum(MonitorTypes))]).optional(),
|
||||
filter: z.union([z.string(), z.literal("")]).optional(),
|
||||
});
|
||||
|
||||
@@ -36,12 +31,7 @@ export const getMonitorsWithChecksQueryValidation = z.object({
|
||||
filter: z.union([z.string(), z.literal("")]).optional(),
|
||||
field: z.string().optional(),
|
||||
order: z.enum(["asc", "desc"]).optional(),
|
||||
type: z
|
||||
.union([
|
||||
z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"]),
|
||||
z.array(z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"])),
|
||||
])
|
||||
.optional(),
|
||||
type: z.union([z.enum(MonitorTypes), z.array(z.enum(MonitorTypes))]).optional(),
|
||||
explain: booleanCoercion.optional(),
|
||||
});
|
||||
|
||||
@@ -49,72 +39,98 @@ export const getCertificateParamValidation = z.object({
|
||||
monitorId: z.string().min(1, "Monitor ID is required"),
|
||||
});
|
||||
|
||||
export const createMonitorBodyValidation = z.object({
|
||||
_id: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.union([z.string(), z.literal("")]).optional(),
|
||||
type: z.enum(MonitorTypes, "Invalid monitor type"),
|
||||
statusWindowSize: z.number().min(1).max(20).default(5),
|
||||
statusWindowThreshold: z.number().min(1).max(100).default(60),
|
||||
url: z.string().min(1, "URL is required"),
|
||||
ignoreTlsErrors: z.boolean().default(false),
|
||||
useAdvancedMatching: z.boolean().default(false),
|
||||
port: z.number().optional(),
|
||||
isActive: z.boolean().optional(),
|
||||
interval: z.number().optional(),
|
||||
cpuAlertThreshold: z.number().optional(),
|
||||
memoryAlertThreshold: z.number().optional(),
|
||||
diskAlertThreshold: z.number().optional(),
|
||||
tempAlertThreshold: z.number().optional(),
|
||||
notifications: z.array(z.string()).optional(),
|
||||
secret: z.string().optional(),
|
||||
jsonPath: z.union([z.string(), z.literal("")]).optional(),
|
||||
expectedValue: z.union([z.string(), z.literal("")]).optional(),
|
||||
matchMethod: z.union([z.enum(MonitorMatchMethods), z.literal("")]).optional(),
|
||||
gameId: z.union([z.string(), z.literal("")]).optional(),
|
||||
grpcServiceName: z.union([z.string(), z.literal("")]).default(""),
|
||||
strategy: z.enum(PageSpeedStrategies).optional(),
|
||||
selectedDisks: z.array(z.string()).optional(),
|
||||
group: z.union([z.string().max(50).trim(), z.null(), z.literal("")]).optional(),
|
||||
geoCheckEnabled: z.boolean().optional(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).optional(),
|
||||
geoCheckInterval: z.number().min(300000).optional(),
|
||||
});
|
||||
const refineDnsHostname = (body: { type?: string; url?: string }, ctx: z.RefinementCtx) => {
|
||||
if (body.type === "dns" && body.url && !dnsHostnameRegex.test(body.url)) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
path: ["url"],
|
||||
message: "Enter a valid domain (e.g. www.example.com)",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const editMonitorBodyValidation = z.object({
|
||||
name: z.string().optional(),
|
||||
type: z.enum(MonitorTypes).optional(),
|
||||
url: z.string().optional(),
|
||||
statusWindowSize: z.number().min(1).max(20).default(5),
|
||||
statusWindowThreshold: z.number().min(1).max(100).default(60),
|
||||
description: z.union([z.string(), z.literal("")]).optional(),
|
||||
interval: z.number().optional(),
|
||||
notifications: z.array(z.string()).optional(),
|
||||
secret: z.string().optional(),
|
||||
ignoreTlsErrors: z.boolean().optional(),
|
||||
useAdvancedMatching: z.boolean().optional(),
|
||||
jsonPath: z.union([z.string(), z.literal("")]).optional(),
|
||||
expectedValue: z.union([z.string(), z.literal("")]).optional(),
|
||||
matchMethod: z.union([z.enum(MonitorMatchMethods), z.literal("")]).optional(),
|
||||
port: z.number().min(1).max(65535).optional(),
|
||||
cpuAlertThreshold: z.number().optional(),
|
||||
memoryAlertThreshold: z.number().optional(),
|
||||
diskAlertThreshold: z.number().optional(),
|
||||
tempAlertThreshold: z.number().optional(),
|
||||
gameId: z.union([z.string(), z.literal("")]).optional(),
|
||||
grpcServiceName: z.union([z.string(), z.literal("")]).optional(),
|
||||
strategy: z.enum(PageSpeedStrategies).optional(),
|
||||
selectedDisks: z.array(z.string()).optional(),
|
||||
group: z.union([z.string().max(50).trim(), z.null(), z.literal("")]).optional(),
|
||||
geoCheckEnabled: z.boolean().optional(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).optional(),
|
||||
geoCheckInterval: z.number().min(300000).optional(),
|
||||
});
|
||||
export const createMonitorBodyValidation = z
|
||||
.object({
|
||||
_id: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.union([z.string(), z.literal("")]).optional(),
|
||||
type: z.enum(MonitorTypes, "Invalid monitor type"),
|
||||
statusWindowSize: z.number().min(1).max(20).default(5),
|
||||
statusWindowThreshold: z.number().min(1).max(100).default(60),
|
||||
url: z.string().min(1, "URL is required"),
|
||||
ignoreTlsErrors: z.boolean().default(false),
|
||||
useAdvancedMatching: z.boolean().default(false),
|
||||
port: z.number().optional(),
|
||||
isActive: z.boolean().optional(),
|
||||
interval: z.number().optional(),
|
||||
cpuAlertThreshold: z.number().optional(),
|
||||
memoryAlertThreshold: z.number().optional(),
|
||||
diskAlertThreshold: z.number().optional(),
|
||||
tempAlertThreshold: z.number().optional(),
|
||||
notifications: z.array(z.string()).optional(),
|
||||
secret: z.string().optional(),
|
||||
jsonPath: z.union([z.string(), z.literal("")]).optional(),
|
||||
expectedValue: z.union([z.string(), z.literal("")]).optional(),
|
||||
matchMethod: z.union([z.enum(MonitorMatchMethods), z.literal("")]).optional(),
|
||||
gameId: z.union([z.string(), z.literal("")]).optional(),
|
||||
grpcServiceName: z.union([z.string(), z.literal("")]).default(""),
|
||||
strategy: z.enum(PageSpeedStrategies).optional(),
|
||||
selectedDisks: z.array(z.string()).optional(),
|
||||
group: z.union([z.string().max(50).trim(), z.null(), z.literal("")]).optional(),
|
||||
geoCheckEnabled: z.boolean().optional(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).optional(),
|
||||
geoCheckInterval: z.number().min(300000).optional(),
|
||||
dnsServer: dnsServerValidation.optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
})
|
||||
.superRefine(refineDnsHostname);
|
||||
|
||||
export const editMonitorBodyValidation = z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
type: z.enum(MonitorTypes).optional(),
|
||||
url: z.string().optional(),
|
||||
statusWindowSize: z.number().min(1).max(20).default(5),
|
||||
statusWindowThreshold: z.number().min(1).max(100).default(60),
|
||||
description: z.union([z.string(), z.literal("")]).optional(),
|
||||
interval: z.number().optional(),
|
||||
notifications: z.array(z.string()).optional(),
|
||||
secret: z.string().optional(),
|
||||
ignoreTlsErrors: z.boolean().optional(),
|
||||
useAdvancedMatching: z.boolean().optional(),
|
||||
jsonPath: z.union([z.string(), z.literal("")]).optional(),
|
||||
expectedValue: z.union([z.string(), z.literal("")]).optional(),
|
||||
matchMethod: z.union([z.enum(MonitorMatchMethods), z.literal("")]).optional(),
|
||||
port: z.number().min(1).max(65535).optional(),
|
||||
cpuAlertThreshold: z.number().optional(),
|
||||
memoryAlertThreshold: z.number().optional(),
|
||||
diskAlertThreshold: z.number().optional(),
|
||||
tempAlertThreshold: z.number().optional(),
|
||||
gameId: z.union([z.string(), z.literal("")]).optional(),
|
||||
grpcServiceName: z.union([z.string(), z.literal("")]).optional(),
|
||||
strategy: z.enum(PageSpeedStrategies).optional(),
|
||||
selectedDisks: z.array(z.string()).optional(),
|
||||
group: z.union([z.string().max(50).trim(), z.null(), z.literal("")]).optional(),
|
||||
geoCheckEnabled: z.boolean().optional(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).optional(),
|
||||
geoCheckInterval: z.number().min(300000).optional(),
|
||||
dnsServer: dnsServerValidation.optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
})
|
||||
.superRefine(refineDnsHostname);
|
||||
|
||||
export const pauseMonitorParamValidation = z.object({
|
||||
monitorId: z.string().min(1, "Monitor ID is required"),
|
||||
});
|
||||
|
||||
export const bulkPauseMonitorBodyValidation = z.object({
|
||||
monitorIds: z
|
||||
.array(z.string().min(1, "Monitor ID must not be empty"))
|
||||
.min(1, "At least one monitor ID is required")
|
||||
.max(100, "Cannot bulk update more than 100 monitors at once"),
|
||||
pause: z.boolean(),
|
||||
});
|
||||
|
||||
export const getUptimeDetailsByIdParamValidation = z.object({
|
||||
monitorId: z.string().min(1, "Monitor ID is required"),
|
||||
});
|
||||
@@ -124,48 +140,52 @@ export const getUptimeDetailsByIdQueryValidation = z.object({
|
||||
normalize: booleanCoercion.optional(),
|
||||
});
|
||||
|
||||
const importedMonitorSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
userId: z.string().optional(),
|
||||
teamId: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.union([z.string(), z.literal("")]).optional(),
|
||||
status: z.enum(["up", "down", "paused", "initializing", "maintenance", "breached"]).default("initializing"),
|
||||
statusWindow: z.array(z.boolean()).default([]),
|
||||
statusWindowSize: z.number().min(1).max(20).default(5),
|
||||
statusWindowThreshold: z.number().min(1).max(100).default(60),
|
||||
type: z.enum(MonitorTypes, "Invalid monitor type"),
|
||||
ignoreTlsErrors: z.boolean().default(false),
|
||||
useAdvancedMatching: z.boolean().default(false),
|
||||
jsonPath: z.union([z.string(), z.literal("")]).optional(),
|
||||
expectedValue: z.union([z.string(), z.literal("")]).optional(),
|
||||
matchMethod: z.union([z.enum(MonitorMatchMethods), z.literal("")]).optional(),
|
||||
url: z.string().min(1, "URL is required"),
|
||||
port: z.number().optional(),
|
||||
isActive: z.boolean().default(true),
|
||||
interval: z.number().default(60000),
|
||||
uptimePercentage: z.number().optional(),
|
||||
notifications: z.array(z.string()).default([]),
|
||||
secret: z.string().optional(),
|
||||
cpuAlertThreshold: z.number().default(100),
|
||||
cpuAlertCounter: z.number().default(5),
|
||||
memoryAlertThreshold: z.number().default(100),
|
||||
memoryAlertCounter: z.number().default(5),
|
||||
diskAlertThreshold: z.number().default(100),
|
||||
diskAlertCounter: z.number().default(5),
|
||||
tempAlertThreshold: z.number().default(100),
|
||||
tempAlertCounter: z.number().default(5),
|
||||
selectedDisks: z.array(z.string()).default([]),
|
||||
gameId: z.union([z.string(), z.literal("")]).optional(),
|
||||
grpcServiceName: z.union([z.string(), z.literal("")]).default(""),
|
||||
strategy: z.enum(PageSpeedStrategies).default(DefaultPageSpeedStrategy),
|
||||
group: z.union([z.string().max(50).trim(), z.null()]).default(null),
|
||||
geoCheckEnabled: z.boolean().default(false),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).default([]),
|
||||
geoCheckInterval: z.number().min(300000).default(300000),
|
||||
createdAt: z.string().optional(),
|
||||
updatedAt: z.string().optional(),
|
||||
});
|
||||
const importedMonitorSchema = z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
userId: z.string().optional(),
|
||||
teamId: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.union([z.string(), z.literal("")]).optional(),
|
||||
status: z.enum(["up", "down", "paused", "initializing", "maintenance", "breached"]).default("initializing"),
|
||||
statusWindow: z.array(z.boolean()).default([]),
|
||||
statusWindowSize: z.number().min(1).max(20).default(5),
|
||||
statusWindowThreshold: z.number().min(1).max(100).default(60),
|
||||
type: z.enum(MonitorTypes, "Invalid monitor type"),
|
||||
ignoreTlsErrors: z.boolean().default(false),
|
||||
useAdvancedMatching: z.boolean().default(false),
|
||||
jsonPath: z.union([z.string(), z.literal("")]).optional(),
|
||||
expectedValue: z.union([z.string(), z.literal("")]).optional(),
|
||||
matchMethod: z.union([z.enum(MonitorMatchMethods), z.literal("")]).optional(),
|
||||
url: z.string().min(1, "URL is required"),
|
||||
port: z.number().optional(),
|
||||
isActive: z.boolean().default(true),
|
||||
interval: z.number().default(60000),
|
||||
uptimePercentage: z.number().optional(),
|
||||
notifications: z.array(z.string()).default([]),
|
||||
secret: z.string().optional(),
|
||||
cpuAlertThreshold: z.number().default(100),
|
||||
cpuAlertCounter: z.number().default(5),
|
||||
memoryAlertThreshold: z.number().default(100),
|
||||
memoryAlertCounter: z.number().default(5),
|
||||
diskAlertThreshold: z.number().default(100),
|
||||
diskAlertCounter: z.number().default(5),
|
||||
tempAlertThreshold: z.number().default(100),
|
||||
tempAlertCounter: z.number().default(5),
|
||||
selectedDisks: z.array(z.string()).default([]),
|
||||
gameId: z.union([z.string(), z.literal("")]).optional(),
|
||||
grpcServiceName: z.union([z.string(), z.literal("")]).default(""),
|
||||
strategy: z.enum(PageSpeedStrategies).default(DefaultPageSpeedStrategy),
|
||||
group: z.union([z.string().max(50).trim(), z.null()]).default(null),
|
||||
geoCheckEnabled: z.boolean().default(false),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).default([]),
|
||||
geoCheckInterval: z.number().min(300000).default(300000),
|
||||
dnsServer: dnsServerValidation.optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
createdAt: z.string().optional(),
|
||||
updatedAt: z.string().optional(),
|
||||
})
|
||||
.superRefine(refineDnsHostname);
|
||||
|
||||
export const importMonitorsBodyValidation = z.object({
|
||||
monitors: z.array(importedMonitorSchema).min(1, "At least one monitor is required"),
|
||||
@@ -180,3 +200,45 @@ export const getHardwareDetailsByIdParamValidation = z.object({
|
||||
export const getHardwareDetailsByIdQueryValidation = z.object({
|
||||
dateRange: z.enum(["recent", "hour", "day", "week", "month", "all"]).optional(),
|
||||
});
|
||||
|
||||
// Canonical monitor shape returned by /monitors endpoints. Keep aligned with
|
||||
// what the controllers actually serialize.
|
||||
export const monitorResponseSchema = z
|
||||
.object({
|
||||
_id: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
type: z.enum(MonitorTypes),
|
||||
url: z.string(),
|
||||
port: z.number().optional(),
|
||||
isActive: z.boolean(),
|
||||
interval: z.number(),
|
||||
status: z.enum(MonitorStatuses),
|
||||
statusWindowSize: z.number(),
|
||||
statusWindowThreshold: z.number(),
|
||||
ignoreTlsErrors: z.boolean(),
|
||||
useAdvancedMatching: z.boolean(),
|
||||
jsonPath: z.string().optional(),
|
||||
expectedValue: z.string().optional(),
|
||||
matchMethod: z.enum(MonitorMatchMethods).optional(),
|
||||
notifications: z.array(z.string()),
|
||||
secret: z.string().optional(),
|
||||
cpuAlertThreshold: z.number(),
|
||||
memoryAlertThreshold: z.number(),
|
||||
diskAlertThreshold: z.number(),
|
||||
tempAlertThreshold: z.number(),
|
||||
selectedDisks: z.array(z.string()),
|
||||
gameId: z.string().optional(),
|
||||
grpcServiceName: z.string().optional(),
|
||||
group: z.string().nullable().optional(),
|
||||
geoCheckEnabled: z.boolean(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)),
|
||||
geoCheckInterval: z.number(),
|
||||
dnsServer: z.string().optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
teamId: z.string(),
|
||||
userId: z.string(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
@@ -72,6 +72,22 @@ export const createNotificationBodyValidation = z.discriminatedUnion("type", [
|
||||
address: z.string().min(1, "Chat ID is required"),
|
||||
accessToken: z.string().min(1, "Bot token is required"),
|
||||
}),
|
||||
// Pushover notification
|
||||
z.object({
|
||||
notificationName: z.string().min(1, "Notification name is required"),
|
||||
type: z.literal("pushover"),
|
||||
address: z.string().min(1, "User key is required"),
|
||||
accessToken: z.string().min(1, "App token is required"),
|
||||
}),
|
||||
// Twilio SMS notification
|
||||
z.object({
|
||||
notificationName: z.string().min(1, "Notification name is required"),
|
||||
type: z.literal("twilio"),
|
||||
accountSid: z.string().min(1, "Account SID is required"),
|
||||
accessToken: z.string().min(1, "Auth token is required"),
|
||||
phone: z.string().min(1, "Recipient phone number is required"),
|
||||
twilioPhoneNumber: z.string().min(1, "Twilio phone number is required"),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const testNotificationBodyValidation = createNotificationBodyValidation;
|
||||
@@ -97,6 +113,7 @@ export const sendTestEmailBodyValidation = z.object({
|
||||
systemEmailSecure: z.boolean().optional(),
|
||||
systemEmailPool: z.boolean().optional(),
|
||||
systemEmailAddress: z.string().optional(),
|
||||
systemEmailDisplayName: z.string().optional(),
|
||||
systemEmailPassword: z.string().optional(),
|
||||
systemEmailUser: z.string().optional(),
|
||||
systemEmailConnectionHost: z.union([z.string(), z.literal("")]).optional(),
|
||||
|
||||
@@ -14,6 +14,12 @@ export const updateAppSettingsBodyValidation = z
|
||||
timezone: z.string().optional(),
|
||||
systemEmailHost: z.string().nullable().optional(),
|
||||
systemEmailAddress: z.string().nullable().optional(),
|
||||
systemEmailDisplayName: z
|
||||
.string()
|
||||
.max(100)
|
||||
.transform((val) => (val.trim() === "" ? null : val.trim()))
|
||||
.nullable()
|
||||
.optional(),
|
||||
systemEmailPassword: z.string().nullable().optional(),
|
||||
systemEmailUser: z.string().nullable().optional(),
|
||||
systemEmailConnectionHost: z.string().nullable().optional(),
|
||||
|
||||
@@ -15,6 +15,17 @@ export const nameValidation = z
|
||||
|
||||
export const lowercaseEmailValidation = z.email().transform((val) => val.toLowerCase());
|
||||
|
||||
export const dnsServerValidation = z
|
||||
.string()
|
||||
.min(1, "DNS server is required")
|
||||
.refine((v) => z.ipv4().safeParse(v).success || z.ipv6().safeParse(v).success, "Enter a valid IPv4 or IPv6 address (e.g. 8.8.8.8)");
|
||||
|
||||
// Hostname (FQDN) — labels of 1-63 alphanumerics/hyphens separated by dots,
|
||||
// optionally prefixed with `_` for service labels (e.g. _dmarc, _imaps._tcp).
|
||||
// No scheme, port, path, or whitespace. Total length ≤ 253. Kept in sync with
|
||||
// the client-side regex in client/src/Validation/monitor.ts.
|
||||
export const dnsHostnameRegex = /^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/;
|
||||
|
||||
export const booleanCoercion = z.preprocess((val) => {
|
||||
if (val === "true" || val === true) return true;
|
||||
if (val === "false" || val === false) return false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { booleanCoercion } from "./shared.js";
|
||||
import { StatusPageTypes } from "@/types/statusPage.js";
|
||||
import { StatusPageTypes, StatusPageThemes, StatusPageThemeModes } from "@/types/statusPage.js";
|
||||
|
||||
//****************************************
|
||||
// Status Page Validations
|
||||
@@ -33,6 +33,8 @@ export const createStatusPageBodyValidation = z
|
||||
showAdminLoginLink: booleanCoercion.optional(),
|
||||
showInfrastructure: booleanCoercion.optional(),
|
||||
removeLogo: z.union([z.literal("true"), z.literal("false")]).optional(),
|
||||
theme: z.enum(StatusPageThemes).optional(),
|
||||
themeMode: z.enum(StatusPageThemeModes).optional(),
|
||||
})
|
||||
.strip();
|
||||
|
||||
|
||||
@@ -50,3 +50,19 @@ export const editUserBodyValidation = z.object({
|
||||
email: lowercaseEmailValidation.optional(),
|
||||
profilePicture: z.string().optional(),
|
||||
});
|
||||
|
||||
// Canonical user shape returned by auth/user endpoints. Keep aligned with what
|
||||
// the controllers actually serialize (password is intentionally omitted).
|
||||
export const userResponseSchema = z
|
||||
.object({
|
||||
_id: z.string(),
|
||||
firstName: z.string(),
|
||||
lastName: z.string(),
|
||||
email: z.string(),
|
||||
role: z.array(z.string()),
|
||||
teamId: z.string().optional(),
|
||||
profileImage: z.string().nullable().optional(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
Reference in new issue
Block a user