From 30e8cfc9ec0e53607b0ac3cb4997f828d90d5a0d Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Sun, 7 Jun 2026 22:37:24 -0400 Subject: [PATCH 01/17] Add custom domain support for published status pages. Fixes #3647 --- client/src/Hooks/useStatusPageForm.ts | 1 + client/src/Pages/StatusPage/Create/index.tsx | 26 + .../Components/HeaderStatusPageControls.tsx | 5 +- client/src/Pages/StatusPage/Status/index.tsx | 16 +- .../Components/StatusPagesTable.tsx | 11 +- client/src/Routes/index.tsx | 16 + client/src/Types/StatusPage.ts | 1 + client/src/Utils/statusPageUrl.ts | 72 + client/src/Validation/statusPage.ts | 23 + client/src/locales/en.json | 5 + server/openapi.json | 24437 +++++++++------- server/openapi/routes/statusPage.ts | 16 +- server/src/app.ts | 3 +- .../src/controllers/statusPageController.ts | 100 +- server/src/db/models/StatusPage.ts | 8 + server/src/middleware/statusPageCorsOrigin.ts | 39 + .../status-pages/IStatusPagesRepository.ts | 1 + .../MongoStatusPagesRepository.ts | 25 +- server/src/routes/statusPageRoute.ts | 1 + .../src/service/business/statusPageService.ts | 38 +- server/src/types/statusPage.ts | 1 + server/src/utils/statusPageDomain.ts | 29 + server/src/validation/statusPageValidation.ts | 18 +- .../unit/services/statusPageService.test.ts | 43 +- .../test/unit/utils/statusPageDomain.test.ts | 28 + 25 files changed, 13919 insertions(+), 11044 deletions(-) create mode 100644 client/src/Utils/statusPageUrl.ts create mode 100644 server/src/middleware/statusPageCorsOrigin.ts create mode 100644 server/src/utils/statusPageDomain.ts create mode 100644 server/test/unit/utils/statusPageDomain.test.ts diff --git a/client/src/Hooks/useStatusPageForm.ts b/client/src/Hooks/useStatusPageForm.ts index 681a3da6c..609db4792 100644 --- a/client/src/Hooks/useStatusPageForm.ts +++ b/client/src/Hooks/useStatusPageForm.ts @@ -31,6 +31,7 @@ export const useStatusPageForm = ({ const defaults: StatusPageFormData = { companyName: data?.companyName || "", url: data?.url || generateDefaultUrl(), + customDomain: data?.customDomain ?? null, timezone: resolveTimezone(data?.timezone), type: data?.type || ["uptime"], color: data?.color || "#13715B", diff --git a/client/src/Pages/StatusPage/Create/index.tsx b/client/src/Pages/StatusPage/Create/index.tsx index c9bc04838..8a7b48010 100644 --- a/client/src/Pages/StatusPage/Create/index.tsx +++ b/client/src/Pages/StatusPage/Create/index.tsx @@ -151,6 +151,11 @@ const CreateStatusPage = () => { fd.append("isPublished", String(data.isPublished)); if (data.companyName) fd.append("companyName", data.companyName); if (data.url) fd.append("url", data.url); + if (data.customDomain !== undefined && data.customDomain !== null) { + fd.append("customDomain", data.customDomain); + } else { + fd.append("customDomain", ""); + } if (data.timezone) fd.append("timezone", data.timezone); if (data.color) fd.append("color", data.color); fd.append("showCharts", String(data.showCharts)); @@ -284,6 +289,27 @@ const CreateStatusPage = () => { /> )} /> + ( + + )} + /> } /> diff --git a/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx b/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx index ba3a5940d..93c29d188 100644 --- a/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx +++ b/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx @@ -8,7 +8,8 @@ import { Settings, ExternalLink } from "lucide-react"; import { useTheme } from "@mui/material"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { PUBLIC_STATUS_PAGE_PREFIX, type StatusPage } from "@/Types/StatusPage"; +import { type StatusPage } from "@/Types/StatusPage"; +import { getStatusPagePublicUrl } from "@/Utils/statusPageUrl"; interface HeaderStatusPageControlsProps { isAdmin: boolean; @@ -50,7 +51,7 @@ export const HeaderStatusPageControls = ({ { window.open( - `${PUBLIC_STATUS_PAGE_PREFIX}/${statusPage.url}`, + getStatusPagePublicUrl(statusPage), "_blank", "noopener,noreferrer" ); diff --git a/client/src/Pages/StatusPage/Status/index.tsx b/client/src/Pages/StatusPage/Status/index.tsx index 9c4001fae..5ccc5f44c 100644 --- a/client/src/Pages/StatusPage/Status/index.tsx +++ b/client/src/Pages/StatusPage/Status/index.tsx @@ -14,6 +14,11 @@ import { type StatusPageResponse, type StatusPageTheme, } from "@/Types/StatusPage"; +import { + buildStatusPageApiPath, + getStatusPagePublicUrl, + isCustomDomainHost, +} from "@/Utils/statusPageUrl"; import { HeaderStatusPageControls } from "@/Pages/StatusPage/Status/Components/HeaderStatusPageControls"; import { StatusPageThemeProvider } from "@/Pages/StatusPage/Status/themes/StatusPageThemeProvider"; import { @@ -67,9 +72,14 @@ const StatusPageView = () => { const isAdmin = useIsAdmin(); const location = useLocation(); - const isPublic = location.pathname.startsWith(PUBLIC_STATUS_PAGE_PREFIX); + const onCustomDomainHost = isCustomDomainHost(); + const isPublic = + onCustomDomainHost || location.pathname.startsWith(PUBLIC_STATUS_PAGE_PREFIX); - const apiUrl = url ? `/status-page/${url}?type=uptime&type=infrastructure` : null; + const apiUrl = buildStatusPageApiPath({ + url, + useCustomDomain: onCustomDomainHost, + }); const { data, isLoading, error } = useGet( apiUrl, @@ -134,7 +144,7 @@ const StatusPageView = () => { ); } - const publicUrl = `${window.location.origin}${PUBLIC_STATUS_PAGE_PREFIX}/${statusPage.url}`; + const publicUrl = getStatusPagePublicUrl(statusPage); return ( { if (row.isPublished) { e.stopPropagation(); - const url = `${PUBLIC_STATUS_PAGE_PREFIX}/${row.url}`; - window.open(url, "_blank", "noopener,noreferrer"); + window.open(getStatusPagePublicUrl(row), "_blank", "noopener,noreferrer"); } }; @@ -70,7 +71,9 @@ export const StatusPagesTable = ({ content: t("pages.statusPages.table.headers.url"), render: (row) => { const content = row.isPublished - ? `/${row.url}` + ? row.customDomain + ? getStatusPagePublicPath(row) + : `/${row.url}` : t("pages.statusPages.table.unpublished"); return ( { + if (isCustomDomainHost()) { + return ( + + } + /> + } + /> + + ); + } + return ( { + const source = + CLIENT_HOST || (typeof window !== "undefined" ? window.location.origin : undefined); + if (!source) { + return null; + } + + try { + return new URL(source).hostname.toLowerCase(); + } catch { + return null; + } +}; + +export const isCustomDomainHost = (): boolean => { + if (typeof window === "undefined") { + return false; + } + + const appHostname = getAppHostname(); + if (!appHostname) { + return false; + } + + return window.location.hostname.toLowerCase() !== appHostname; +}; + +export const getStatusPagePublicPath = ( + statusPage: Pick +): string => { + if (statusPage.customDomain) { + return `https://${statusPage.customDomain}`; + } + + return `${PUBLIC_STATUS_PAGE_PREFIX}/${statusPage.url}`; +}; + +export const getStatusPagePublicUrl = ( + statusPage: Pick +): string => { + if (statusPage.customDomain) { + return `https://${statusPage.customDomain}`; + } + + if (typeof window !== "undefined") { + return `${window.location.origin}${PUBLIC_STATUS_PAGE_PREFIX}/${statusPage.url}`; + } + + return `${PUBLIC_STATUS_PAGE_PREFIX}/${statusPage.url}`; +}; + +export const buildStatusPageApiPath = (options: { + url?: string; + useCustomDomain?: boolean; +}): string | null => { + const query = "type=uptime&type=infrastructure"; + + if (options.useCustomDomain && typeof window !== "undefined") { + const domain = encodeURIComponent(window.location.hostname); + return `/status-page/resolve?domain=${domain}&${query}`; + } + + if (options.url) { + return `/status-page/${options.url}?${query}`; + } + + return null; +}; diff --git a/client/src/Validation/statusPage.ts b/client/src/Validation/statusPage.ts index 542f6b2ae..271699765 100644 --- a/client/src/Validation/statusPage.ts +++ b/client/src/Validation/statusPage.ts @@ -9,6 +9,23 @@ import { STATUS_PAGE_THEMES, STATUS_PAGE_THEME_MODES } from "@/Types/StatusPage" // field definitions. export const statusPageStepRegistry = z.registry<{ step: number }>(); +const statusPageHostnameRegex = + /^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/; + +const normalizeCustomDomainInput = (raw: string | null): string | null => { + if (raw === null) { + return null; + } + + const trimmed = raw.trim().toLowerCase(); + if (!trimmed) { + return null; + } + + const withoutScheme = trimmed.replace(/^https?:\/\//, ""); + return withoutScheme.split("/")[0]?.split(":")[0] ?? null; +}; + export const statusPageSchema = z.object({ companyName: z .string() @@ -22,6 +39,12 @@ export const statusPageSchema = z.object({ /^[a-z0-9-]+$/, "URL can only contain lowercase letters, numbers, and hyphens" ), + customDomain: z + .union([z.string(), z.null()]) + .transform(normalizeCustomDomainInput) + .refine((domain) => domain === null || statusPageHostnameRegex.test(domain), { + message: "Enter a valid domain name (e.g. status.example.com)", + }), timezone: z.string().optional(), type: z .array(z.enum(["uptime", "infrastructure"])) diff --git a/client/src/locales/en.json b/client/src/locales/en.json index e5f7073b6..b209bf414 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -1509,6 +1509,11 @@ "url": { "label": "Your status page address", "placeholder": "my-status-page" + }, + "customDomain": { + "label": "Custom domain", + "placeholder": "status.example.com", + "helper": "Optional. Point a DNS record to this Checkmate instance to serve the status page at your own domain." } } }, diff --git a/server/openapi.json b/server/openapi.json index 2a5abd6e0..d1900f322 100644 --- a/server/openapi.json +++ b/server/openapi.json @@ -1,10995 +1,13446 @@ { - "openapi": "3.0.0", - "info": { - "title": "Checkmate API", - "version": "1.0.0", - "description": "Generated from Zod validation schemas. Source of truth: server/src/validation/*.ts" - }, - "servers": [ - { - "url": "/api/v1", - "description": "Current server" - } - ], - "tags": [ - { - "name": "auth", - "description": "Sign up, log in, password reset, and the authenticated user's profile." - }, - { - "name": "monitors", - "description": "Configure and inspect monitors (HTTP, port, ping, hardware, docker, game, gRPC) for the caller's team." - }, - { - "name": "checks", - "description": "Individual monitoring check results, including history, summaries, and bulk deletes." - }, - { - "name": "geo-checks", - "description": "Geographic check results for monitors with multi-region probing enabled." - }, - { - "name": "incidents", - "description": "Downtime incidents derived from monitor state changes, including filtering and CSV export." - }, - { - "name": "notifications", - "description": "Notification channels (email, webhook, Slack, Discord, PagerDuty, Matrix, Teams, Telegram, Pushover, Twilio) and test alerts." - }, - { - "name": "maintenance-window", - "description": "Scheduled maintenance windows during which monitors do not generate incidents or alerts." - }, - { - "name": "status-page", - "description": "Public status pages, their configuration, and the monitors they expose." - }, - { - "name": "settings", - "description": "Global application settings (admin/superadmin)." - }, - { - "name": "invite", - "description": "Team invitations and accepting them." - }, - { - "name": "queue", - "description": "Background job queue introspection and admin actions (admin/superadmin)." - }, - { - "name": "diagnostic", - "description": "System diagnostics for the running server (admin/superadmin)." - }, - { - "name": "logs", - "description": "Application logs for the running server (admin/superadmin)." - } - ], - "components": { - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT" - } - }, - "schemas": { - "AuthPayload": { - "type": "object", - "properties": { - "user": { - "$ref": "#/components/schemas/User" - }, - "token": { - "type": "string" - } - }, - "required": ["user"], - "example": { - "user": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": ["admin"], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - }, - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - }, - "User": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "email": { - "type": "string" - }, - "role": { - "type": "array", - "items": { - "type": "string" - } - }, - "teamId": { - "type": "string" - }, - "profileImage": { - "type": "string", - "nullable": true - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": ["_id", "firstName", "lastName", "email", "role", "createdAt", "updatedAt"], - "additionalProperties": { - "nullable": true - }, - "example": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": ["admin"], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - } - }, - "Incident": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789c" - }, - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "teamId": { - "type": "string", - "example": "65f1c2a4d8b9e01234567890" - }, - "startTime": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "endTime": { - "type": "string", - "nullable": true, - "example": "2026-04-15T03:34:00.000Z" - }, - "status": { - "type": "boolean", - "example": true, - "description": "true = resolved, false = ongoing" - }, - "message": { - "type": "string", - "nullable": true, - "example": "HTTP 503 from origin" - }, - "statusCode": { - "type": "number", - "nullable": true, - "example": 503 - }, - "resolutionType": { - "type": "string", - "nullable": true, - "enum": ["automatic", "manual", null], - "example": "automatic" - }, - "resolvedBy": { - "type": "string", - "nullable": true - }, - "resolvedByEmail": { - "type": "string", - "nullable": true - }, - "comment": { - "type": "string", - "nullable": true - }, - "createdAt": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "updatedAt": { - "type": "string", - "example": "2026-04-15T03:34:00.000Z" - } - }, - "required": ["id", "monitorId", "teamId", "startTime", "endTime", "status", "resolutionType", "createdAt", "updatedAt"], - "additionalProperties": { - "nullable": true - } - }, - "IncidentSummary": { - "type": "object", - "properties": { - "total": { - "type": "number", - "example": 12 - }, - "totalActive": { - "type": "number", - "example": 1 - }, - "totalManualResolutions": { - "type": "number", - "example": 2 - }, - "totalAutomaticResolutions": { - "type": "number", - "example": 9 - }, - "avgResolutionTimeHours": { - "type": "number", - "example": 0.5 - }, - "topMonitor": { - "type": "object", - "nullable": true, - "properties": { - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "monitorName": { - "type": "string", - "nullable": true, - "example": "Marketing site" - }, - "incidentCount": { - "type": "number", - "example": 4 - } - }, - "required": ["monitorId", "monitorName", "incidentCount"] - }, - "latestIncidents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IncidentSummaryItem" - } - } - }, - "required": [ - "total", - "totalActive", - "totalManualResolutions", - "totalAutomaticResolutions", - "avgResolutionTimeHours", - "topMonitor", - "latestIncidents" - ] - }, - "IncidentSummaryItem": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789c" - }, - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "monitorName": { - "type": "string", - "nullable": true, - "example": "Marketing site" - }, - "status": { - "type": "boolean", - "example": true - }, - "startTime": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "endTime": { - "type": "string", - "nullable": true, - "example": "2026-04-15T03:34:00.000Z" - }, - "resolutionType": { - "type": "string", - "nullable": true, - "enum": ["automatic", "manual", null], - "example": "automatic" - }, - "message": { - "type": "string", - "nullable": true, - "example": "HTTP 503 from origin" - }, - "statusCode": { - "type": "number", - "nullable": true, - "example": 503 - }, - "createdAt": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - } - }, - "required": ["id", "monitorId", "monitorName", "status", "startTime", "endTime", "resolutionType", "message", "statusCode", "createdAt"] - }, - "Monitor": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - "url": { - "type": "string" - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean" - }, - "interval": { - "type": "number" - }, - "status": { - "type": "string", - "enum": ["up", "down", "paused", "initializing", "maintenance", "breached"] - }, - "statusWindowSize": { - "type": "number" - }, - "statusWindowThreshold": { - "type": "number" - }, - "ignoreTlsErrors": { - "type": "boolean" - }, - "useAdvancedMatching": { - "type": "boolean" - }, - "jsonPath": { - "type": "string" - }, - "expectedValue": { - "type": "string" - }, - "matchMethod": { - "type": "string", - "enum": ["equal", "include", "regex"] - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "secret": { - "type": "string" - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "gameId": { - "type": "string" - }, - "grpcServiceName": { - "type": "string" - }, - "group": { - "type": "string", - "nullable": true - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - } - }, - "geoCheckInterval": { - "type": "number" - }, - "dnsServer": { - "type": "string" - }, - "dnsRecordType": { - "type": "string", - "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] - }, - "teamId": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": [ - "_id", - "name", - "type", - "url", - "isActive", - "interval", - "status", - "statusWindowSize", - "statusWindowThreshold", - "ignoreTlsErrors", - "useAdvancedMatching", - "notifications", - "tags", - "cpuAlertThreshold", - "memoryAlertThreshold", - "diskAlertThreshold", - "tempAlertThreshold", - "selectedDisks", - "geoCheckEnabled", - "geoCheckLocations", - "geoCheckInterval", - "teamId", - "userId", - "createdAt", - "updatedAt" - ], - "additionalProperties": { - "nullable": true - }, - "example": { - "_id": "65f1c2a4d8b9e0123456789a", - "name": "Marketing site", - "description": "Production marketing site monitored from the EU region", - "type": "http", - "url": "https://www.example.com", - "port": 443, - "isActive": true, - "interval": 60000, - "status": "up", - "statusWindowSize": 5, - "statusWindowThreshold": 3, - "ignoreTlsErrors": false, - "useAdvancedMatching": false, - "notifications": ["65f1c2a4d8b9e0123456789b"], - "cpuAlertThreshold": 90, - "memoryAlertThreshold": 90, - "diskAlertThreshold": 90, - "tempAlertThreshold": 80, - "selectedDisks": [], - "geoCheckEnabled": false, - "geoCheckLocations": [], - "geoCheckInterval": 300000, - "teamId": "65f1c2a4d8b9e01234567890", - "userId": "65f1c2a4d8b9e01234567891", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - } - }, - "NotificationChannelBody": { - "oneOf": [ - { - "$ref": "#/components/schemas/EmailNotification" - }, - { - "$ref": "#/components/schemas/WebhookNotification" - }, - { - "$ref": "#/components/schemas/SlackNotification" - }, - { - "$ref": "#/components/schemas/DiscordNotification" - }, - { - "$ref": "#/components/schemas/PagerDutyNotification" - }, - { - "$ref": "#/components/schemas/MatrixNotification" - }, - { - "$ref": "#/components/schemas/TeamsNotification" - }, - { - "$ref": "#/components/schemas/TelegramNotification" - }, - { - "$ref": "#/components/schemas/PushoverNotification" - }, - { - "$ref": "#/components/schemas/TwilioNotification" - }, - { - "$ref": "#/components/schemas/NtfyNotification" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "email": "#/components/schemas/EmailNotification", - "webhook": "#/components/schemas/WebhookNotification", - "slack": "#/components/schemas/SlackNotification", - "discord": "#/components/schemas/DiscordNotification", - "pager_duty": "#/components/schemas/PagerDutyNotification", - "matrix": "#/components/schemas/MatrixNotification", - "teams": "#/components/schemas/TeamsNotification", - "telegram": "#/components/schemas/TelegramNotification", - "pushover": "#/components/schemas/PushoverNotification", - "twilio": "#/components/schemas/TwilioNotification", - "ntfy": "#/components/schemas/NtfyNotification" - } - } - }, - "EmailNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["email"] - }, - "address": { - "type": "string", - "format": "email" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - } - }, - "required": ["notificationName", "type", "address"], - "example": { - "notificationName": "Ops on-call email", - "type": "email", - "address": "alerts@example.com" - } - }, - "WebhookNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["webhook"] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - } - }, - "required": ["notificationName", "type", "address"], - "example": { - "notificationName": "Custom webhook", - "type": "webhook", - "address": "https://example.com/hooks/checkmate" - } - }, - "SlackNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["slack"] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - } - }, - "required": ["notificationName", "type", "address"], - "example": { - "notificationName": "#alerts", - "type": "slack", - "address": "https://hooks.slack.com/services/T000/B000/XXXX" - } - }, - "DiscordNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["discord"] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - } - }, - "required": ["notificationName", "type", "address"], - "example": { - "notificationName": "#status", - "type": "discord", - "address": "https://discord.com/api/webhooks/123/abc" - } - }, - "PagerDutyNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["pager_duty"] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - } - }, - "required": ["notificationName", "type", "address"], - "example": { - "notificationName": "PagerDuty primary", - "type": "pager_duty", - "address": "R01XXXXXXXXXXXXXXXXXXXXXXX" - } - }, - "MatrixNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["matrix"] - }, - "address": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "homeserverUrl": { - "type": "string", - "format": "uri" - }, - "roomId": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": ["notificationName", "type", "homeserverUrl", "roomId", "accessToken"], - "example": { - "notificationName": "Matrix room", - "type": "matrix", - "homeserverUrl": "https://matrix.example.com", - "roomId": "!abc123:example.com", - "accessToken": "syt_xxx" - } - }, - "TeamsNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["teams"] - }, - "address": { - "type": "string", - "format": "uri" - } - }, - "required": ["notificationName", "type", "address"], - "example": { - "notificationName": "Teams ops channel", - "type": "teams", - "address": "https://outlook.office.com/webhook/..." - } - }, - "TelegramNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["telegram"] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": ["notificationName", "type", "address", "accessToken"], - "example": { - "notificationName": "Telegram bot", - "type": "telegram", - "address": "-1001234567890", - "accessToken": "123456:ABC-DEF" - } - }, - "PushoverNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["pushover"] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": ["notificationName", "type", "address", "accessToken"], - "example": { - "notificationName": "Pushover personal", - "type": "pushover", - "address": "u1234567890abcdef", - "accessToken": "a1234567890abcdef" - } - }, - "TwilioNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["twilio"] - }, - "accountSid": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - }, - "phone": { - "type": "string", - "minLength": 1 - }, - "twilioPhoneNumber": { - "type": "string", - "minLength": 1 - } - }, - "required": ["notificationName", "type", "accountSid", "accessToken", "phone", "twilioPhoneNumber"], - "example": { - "notificationName": "Twilio SMS", - "type": "twilio", - "accountSid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - "accessToken": "your-auth-token", - "phone": "+15551234567", - "twilioPhoneNumber": "+15557654321" - } - }, - "NtfyNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": ["ntfy"] - }, - "address": { - "type": "string", - "format": "uri" - }, - "topic": { - "type": "string", - "minLength": 1 - } - }, - "required": ["notificationName", "type", "address", "topic"], - "example": { - "notificationName": "ntfy topic", - "type": "ntfy", - "address": "https://ntfy.sh", - "topic": "checkmate-alerts" - } - } - }, - "parameters": {} - }, - "paths": { - "/auth/register": { - "post": { - "tags": ["auth"], - "summary": "Register a new user (first user becomes superadmin)", - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "profileImage": { - "type": "string", - "format": "binary" - }, - "inviteToken": { - "type": "string", - "default": "" - } - }, - "required": ["firstName", "lastName", "email", "password", "profileImage"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/AuthPayload" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/login": { - "post": { - "tags": ["auth"], - "summary": "Log in", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 1 - } - }, - "required": ["email", "password"] - }, - "example": { - "email": "ada@example.com", - "password": "S3cure!Passw0rd" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/AuthPayload" - } - }, - "required": ["success", "msg", "data"] - }, - "example": { - "success": true, - "msg": "OK", - "data": { - "user": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": ["admin"], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - }, - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - } - } - } - }, - "401": { - "description": "Invalid credentials", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/recovery/request": { - "post": { - "tags": ["auth"], - "summary": "Request a password recovery email", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - } - }, - "required": ["email"] - }, - "example": { - "email": "ada@example.com" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/recovery/validate": { - "post": { - "tags": ["auth"], - "summary": "Validate a password recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "recoveryToken": { - "type": "string", - "minLength": 1 - } - }, - "required": ["recoveryToken"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/recovery/reset": { - "post": { - "tags": ["auth"], - "summary": "Reset password using a recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "recoveryToken": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "confirm": { - "type": "string" - } - }, - "required": ["recoveryToken", "password"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/users/superadmin": { - "get": { - "tags": ["auth"], - "summary": "Check whether a superadmin user exists", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "superAdminExists": { - "type": "boolean" - } - }, - "required": ["superAdminExists"] - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/users": { - "get": { - "tags": ["auth"], - "summary": "List all users (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "post": { - "tags": ["auth"], - "summary": "Create a new user (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": ["user", "admin", "superadmin", "demo"] - }, - "minItems": 1 - }, - "profileImage": { - "type": "string", - "format": "binary" - } - }, - "required": ["firstName", "lastName", "email", "password", "role", "profileImage"] - } - } - } - }, - "responses": { - "201": { - "description": "User created", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/users/{userId}": { - "get": { - "tags": ["auth"], - "summary": "Get a user by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "patch": { - "tags": ["auth"], - "summary": "Edit a user (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": ["user", "admin", "superadmin", "demo"] - }, - "minItems": 1 - } - }, - "required": ["firstName", "lastName", "role"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["auth"], - "summary": "Delete a user (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/users/{userId}/password": { - "patch": { - "tags": ["auth"], - "summary": "Change a user's password (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - } - }, - "required": ["password"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/auth/user": { - "patch": { - "tags": ["auth"], - "summary": "Edit the currently authenticated user", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "profilePicture": { - "type": "string" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "newPassword": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "profileImage": { - "type": "string", - "format": "binary" - } - }, - "required": ["profileImage"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["auth"], - "summary": "Delete the currently authenticated user", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/checks/team/summary": { - "get": { - "tags": ["checks"], - "summary": "Aggregate check summary for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/checks/team": { - "get": { - "tags": ["checks"], - "summary": "List checks across the team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": ["asc", "desc"] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["all", "up", "down", "resolve"] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["checks"], - "summary": "Delete all checks for the team (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "deletedCount": { - "type": "number" - } - }, - "required": ["deletedCount"] - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/checks/{monitorId}": { - "get": { - "tags": ["checks"], - "summary": "List checks for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["asc", "desc"] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["all", "up", "down", "resolve"] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["checks"], - "summary": "Delete checks for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/diagnostic/system": { - "get": { - "tags": ["diagnostic"], - "summary": "Get system diagnostics (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/geo-checks/{monitorId}": { - "get": { - "tags": ["geo-checks"], - "summary": "Get geo check results for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["asc", "desc"] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["all", "up", "down", "resolve"] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/incidents/team": { - "get": { - "tags": ["incidents"], - "summary": "List incidents for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": ["asc", "desc"] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"], - "default": "all" - }, - "required": false, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "integer", - "nullable": true, - "minimum": 0 - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": true, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "monitorId", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["manual", "automatic"] - }, - "required": false, - "name": "resolutionType", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Incident" - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/incidents/team/summary": { - "get": { - "tags": ["incidents"], - "summary": "Incident summary for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": false, - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/IncidentSummary" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/incidents/{incidentId}": { - "get": { - "tags": ["incidents"], - "summary": "Get an incident by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "incidentId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "incident": { - "$ref": "#/components/schemas/Incident" - }, - "monitor": { - "nullable": true - }, - "user": { - "nullable": true - } - }, - "required": ["incident"] - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/incidents/{incidentId}/resolve": { - "put": { - "tags": ["incidents"], - "summary": "Manually resolve an incident (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "incidentId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/invite/send": { - "post": { - "tags": ["invite"], - "summary": "Send an invite email (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": ["user", "admin", "superadmin", "demo"] - }, - "minItems": 1 - }, - "teamId": { - "type": "string", - "minLength": 1 - } - }, - "required": ["email", "role", "teamId"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/invite/verify": { - "post": { - "tags": ["invite"], - "summary": "Verify an invite token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "token": { - "type": "string", - "minLength": 1 - } - }, - "required": ["token"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "role": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["email"], - "additionalProperties": { - "nullable": true - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/invite": { - "post": { - "tags": ["invite"], - "summary": "Create an invite token (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": ["user", "admin", "superadmin", "demo"] - }, - "minItems": 1 - }, - "teamId": { - "type": "string", - "minLength": 1 - } - }, - "required": ["email", "role", "teamId"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "token": { - "type": "string" - } - }, - "required": ["token"], - "additionalProperties": { - "nullable": true - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/logs": { - "get": { - "tags": ["logs"], - "summary": "Get application logs (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/maintenance-window": { - "post": { - "tags": ["maintenance-window"], - "summary": "Create one or more maintenance windows", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "name": { - "type": "string", - "minLength": 1 - }, - "active": { - "type": "boolean" - }, - "duration": { - "type": "number", - "minimum": 1 - }, - "durationUnit": { - "type": "string", - "enum": ["seconds", "minutes", "hours", "days"] - }, - "start": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "end": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "repeat": { - "type": "number", - "minimum": 0 - }, - "expiry": { - "type": "string", - "nullable": true, - "format": "date-time" - } - }, - "required": ["monitors", "name", "duration", "durationUnit", "start", "end", "repeat"], - "additionalProperties": false - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/maintenance-window/team": { - "get": { - "tags": ["maintenance-window"], - "summary": "List maintenance windows for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "active", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["asc", "desc"] - }, - "required": false, - "name": "order", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/maintenance-window/monitor/{monitorId}": { - "get": { - "tags": ["maintenance-window"], - "summary": "List maintenance windows for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/maintenance-window/{id}": { - "get": { - "tags": ["maintenance-window"], - "summary": "Get a maintenance window by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "patch": { - "tags": ["maintenance-window"], - "summary": "Edit a maintenance window", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "active": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "repeat": { - "type": "number" - }, - "start": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "end": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "expiry": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "monitors": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "duration": { - "type": "number" - }, - "durationUnit": { - "type": "string", - "enum": ["seconds", "minutes", "hours", "days"] - } - }, - "additionalProperties": false - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["maintenance-window"], - "summary": "Delete a maintenance window", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/team": { - "get": { - "tags": ["monitors"], - "summary": "List monitors for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - } - } - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "required": false, - "name": "tags", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Monitor" - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/team/with-checks": { - "get": { - "tags": ["monitors"], - "summary": "List team monitors with their most recent checks (paginated)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "integer", - "nullable": true, - "minimum": 0 - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["asc", "desc"] - }, - "required": false, - "name": "order", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - } - } - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "required": false, - "name": "tags", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "explain", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/team/groups": { - "get": { - "tags": ["monitors"], - "summary": "List monitor groups for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/uptime/details/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get uptime details for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "normalize", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/hardware/details/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get hardware metrics detail for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/pagespeed/details/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get PageSpeed detail for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/{monitorId}/geo-checks": { - "get": { - "tags": ["monitors"], - "summary": "Get geo check results for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["asc", "desc"] - }, - "required": false, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": ["recent", "hour", "day", "week", "month", "all"] - }, - "required": false, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "numToDisplay", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "normalize", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/pause/{monitorId}": { - "post": { - "tags": ["monitors"], - "summary": "Toggle pause state for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/certificate/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get SSL certificate info for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "certificateDate": { - "type": "string" - } - }, - "required": ["certificateDate"] - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/notifications": { - "patch": { - "tags": ["monitors"], - "summary": "Bulk update notifications across monitors", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitorIds": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "maxItems": 100 - }, - "notificationIds": { - "type": "array", - "items": { - "type": "string" - }, - "maxItems": 100 - }, - "action": { - "type": "string", - "enum": ["add", "remove", "set"] - } - }, - "required": ["monitorIds", "notificationIds", "action"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "modifiedCount": { - "type": "number" - } - }, - "required": ["modifiedCount"] - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors": { - "post": { - "tags": ["monitors"], - "summary": "Create a new monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "url": { - "type": "string", - "minLength": 1 - }, - "ignoreTlsErrors": { - "type": "boolean", - "default": false - }, - "useAdvancedMatching": { - "type": "boolean", - "default": false - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean" - }, - "interval": { - "type": "number" - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "secret": { - "type": "string" - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": ["equal", "include", "regex"] - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ], - "default": "" - }, - "strategy": { - "type": "string", - "enum": ["desktop", "mobile"] - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - } - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] - } - }, - "required": ["name", "type", "url"] - } - } - } - }, - "responses": { - "200": { - "description": "Monitor created", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["monitors"], - "summary": "Delete every monitor (superadmin only)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/demo": { - "post": { - "tags": ["monitors"], - "summary": "Insert preconfigured demo monitors", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "number" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/export/json": { - "get": { - "tags": ["monitors"], - "summary": "Export all team monitors as JSON", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/import/json": { - "post": { - "tags": ["monitors"], - "summary": "Import monitors from JSON", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "status": { - "type": "string", - "enum": ["up", "down", "paused", "initializing", "maintenance", "breached"], - "default": "initializing" - }, - "statusWindow": { - "type": "array", - "items": { - "type": "boolean" - }, - "default": [] - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - "ignoreTlsErrors": { - "type": "boolean", - "default": false - }, - "useAdvancedMatching": { - "type": "boolean", - "default": false - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": ["equal", "include", "regex"] - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "url": { - "type": "string", - "minLength": 1 - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean", - "default": true - }, - "interval": { - "type": "number", - "default": 60000 - }, - "uptimePercentage": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "secret": { - "type": "string" - }, - "cpuAlertThreshold": { - "type": "number", - "default": 100 - }, - "cpuAlertCounter": { - "type": "number", - "default": 5 - }, - "memoryAlertThreshold": { - "type": "number", - "default": 100 - }, - "memoryAlertCounter": { - "type": "number", - "default": 5 - }, - "diskAlertThreshold": { - "type": "number", - "default": 100 - }, - "diskAlertCounter": { - "type": "number", - "default": 5 - }, - "tempAlertThreshold": { - "type": "number", - "default": 100 - }, - "tempAlertCounter": { - "type": "number", - "default": 5 - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ], - "default": "" - }, - "strategy": { - "type": "string", - "enum": ["desktop", "mobile"] - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - } - ], - "default": null - }, - "geoCheckEnabled": { - "type": "boolean", - "default": false - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - }, - "default": [] - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000, - "default": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": ["name", "type", "url"] - }, - "minItems": 1 - } - }, - "required": ["monitors"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "imported": { - "type": "number" - } - }, - "required": ["imported"], - "additionalProperties": { - "nullable": true - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/games": { - "get": { - "tags": ["monitors"], - "summary": "List supported game-server types", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/monitors/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get a monitor by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "patch": { - "tags": ["monitors"], - "summary": "Edit a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] - }, - "url": { - "type": "string" - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "interval": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "secret": { - "type": "string" - }, - "ignoreTlsErrors": { - "type": "boolean" - }, - "useAdvancedMatching": { - "type": "boolean" - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": ["equal", "include", "regex"] - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "port": { - "type": "number", - "minimum": 1, - "maximum": 65535 - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "strategy": { - "type": "string", - "enum": ["desktop", "mobile"] - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] - } - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["monitors"], - "summary": "Delete a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/notifications": { - "post": { - "tags": ["notifications"], - "summary": "Create a notification channel", - "description": "Create a notification channel of any supported type. The `type` field discriminates the body shape.", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/notifications/test/all": { - "post": { - "tags": ["notifications"], - "summary": "Send a test alert through every notification channel for the team", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitorId": { - "type": "string", - "minLength": 1 - } - }, - "required": ["monitorId"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/notifications/test": { - "post": { - "tags": ["notifications"], - "summary": "Send a test alert through a single notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - }, - "required": ["success"], - "additionalProperties": { - "nullable": true - } - } - }, - "required": ["success", "msg", "data"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/notifications/team": { - "get": { - "tags": ["notifications"], - "summary": "List notification channels for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/notifications/{id}": { - "get": { - "tags": ["notifications"], - "summary": "Get a notification channel by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["notifications"], - "summary": "Delete a notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "patch": { - "tags": ["notifications"], - "summary": "Edit a notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/queue/jobs": { - "get": { - "tags": ["queue"], - "summary": "List queued monitor jobs (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/queue/metrics": { - "get": { - "tags": ["queue"], - "summary": "Get queue runtime metrics (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/queue/all-metrics": { - "get": { - "tags": ["queue"], - "summary": "Get queue metrics across all monitors (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/queue/flush": { - "post": { - "tags": ["queue"], - "summary": "Flush the monitor job queue (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/settings": { - "get": { - "tags": ["settings"], - "summary": "Get application settings", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "patch": { - "tags": ["settings"], - "summary": "Update application settings (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "checkTTL": { - "type": "integer", - "minimum": 1, - "maximum": 366 - }, - "systemEmailPort": { - "type": "number", - "nullable": true - }, - "pagespeedApiKey": { - "type": "string", - "nullable": true - }, - "language": { - "type": "string" - }, - "timezone": { - "type": "string" - }, - "systemEmailHost": { - "type": "string", - "nullable": true - }, - "systemEmailAddress": { - "type": "string", - "nullable": true - }, - "systemEmailDisplayName": { - "type": "string", - "nullable": true, - "maxLength": 100 - }, - "systemEmailPassword": { - "type": "string", - "nullable": true - }, - "systemEmailUser": { - "type": "string", - "nullable": true - }, - "systemEmailConnectionHost": { - "type": "string", - "nullable": true - }, - "systemEmailTLSServername": { - "type": "string", - "nullable": true - }, - "showURL": { - "type": "boolean" - }, - "systemEmailSecure": { - "type": "boolean" - }, - "systemEmailPool": { - "type": "boolean" - }, - "systemEmailIgnoreTLS": { - "type": "boolean" - }, - "systemEmailRequireTLS": { - "type": "boolean" - }, - "systemEmailRejectUnauthorized": { - "type": "boolean" - }, - "globalThresholds": { - "type": "object", - "properties": { - "cpu": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "memory": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "disk": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "temperature": { - "type": "number", - "minimum": 1, - "maximum": 150 - } - } - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/settings/test-email": { - "post": { - "tags": ["settings"], - "summary": "Send a test email using current SMTP settings (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "to": { - "type": "string", - "minLength": 1 - }, - "systemEmailHost": { - "type": "string" - }, - "systemEmailPort": { - "type": "number" - }, - "systemEmailSecure": { - "type": "boolean" - }, - "systemEmailPool": { - "type": "boolean" - }, - "systemEmailAddress": { - "type": "string" - }, - "systemEmailDisplayName": { - "type": "string" - }, - "systemEmailPassword": { - "type": "string" - }, - "systemEmailUser": { - "type": "string" - }, - "systemEmailConnectionHost": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - "systemEmailIgnoreTLS": { - "type": "boolean" - }, - "systemEmailRequireTLS": { - "type": "boolean" - }, - "systemEmailRejectUnauthorized": { - "type": "boolean" - }, - "systemEmailTLSServername": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [""] - } - ] - } - }, - "required": ["to"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/status-page/team": { - "get": { - "tags": ["status-page"], - "summary": "List status pages for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/status-page": { - "post": { - "tags": ["status-page"], - "summary": "Create a status page (logo upload optional)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "type": { - "anyOf": [ - { - "type": "string", - "enum": ["uptime", "infrastructure"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["uptime", "infrastructure"] - } - } - ] - }, - "companyName": { - "type": "string", - "minLength": 1 - }, - "url": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$" - }, - "timezone": { - "type": "string" - }, - "color": { - "type": "string" - }, - "monitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - }, - "minItems": 1 - }, - "subMonitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - } - }, - "deleteSubmonitors": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "showCharts": { - "type": "boolean" - }, - "showUptimePercentage": { - "type": "boolean" - }, - "showAdminLoginLink": { - "type": "boolean" - }, - "showInfrastructure": { - "type": "boolean" - }, - "removeLogo": { - "anyOf": [ - { - "type": "string", - "enum": ["true"] - }, - { - "type": "string", - "enum": ["false"] - } - ] - }, - "theme": { - "type": "string", - "enum": ["refined", "modern", "bold", "editorial"] - }, - "themeMode": { - "type": "string", - "enum": ["auto", "light", "dark"] - }, - "logo": { - "type": "string", - "format": "binary" - } - }, - "required": ["type", "companyName", "url", "monitors", "isPublished", "showUptimePercentage", "logo"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/status-page/{id}": { - "put": { - "tags": ["status-page"], - "summary": "Update a status page (logo upload optional)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "type": { - "anyOf": [ - { - "type": "string", - "enum": ["uptime", "infrastructure"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["uptime", "infrastructure"] - } - } - ] - }, - "companyName": { - "type": "string", - "minLength": 1 - }, - "url": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$" - }, - "timezone": { - "type": "string" - }, - "color": { - "type": "string" - }, - "monitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - }, - "minItems": 1 - }, - "subMonitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - } - }, - "deleteSubmonitors": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "showCharts": { - "type": "boolean" - }, - "showUptimePercentage": { - "type": "boolean" - }, - "showAdminLoginLink": { - "type": "boolean" - }, - "showInfrastructure": { - "type": "boolean" - }, - "removeLogo": { - "anyOf": [ - { - "type": "string", - "enum": ["true"] - }, - { - "type": "string", - "enum": ["false"] - } - ] - }, - "theme": { - "type": "string", - "enum": ["refined", "modern", "bold", "editorial"] - }, - "themeMode": { - "type": "string", - "enum": ["auto", "light", "dark"] - }, - "logo": { - "type": "string", - "format": "binary" - } - }, - "required": ["type", "companyName", "url", "monitors", "isPublished", "showUptimePercentage", "logo"] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - }, - "delete": { - "tags": ["status-page"], - "summary": "Delete a status page", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - } - }, - "required": ["success", "msg"] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - }, - "/status-page/{url}": { - "get": { - "tags": ["status-page"], - "summary": "Get a public status page by its URL slug", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "url", - "in": "path" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": ["uptime", "infrastructure"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["uptime", "infrastructure"] - } - } - ] - }, - "required": true, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "timeFrame", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [true] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - }, - "404": { - "description": "Status page not found" - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [false] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": ["success", "msg"] - } - } - } - } - } - } - } - } + "openapi": "3.0.0", + "info": { + "title": "Checkmate API", + "version": "1.0.0", + "description": "Generated from Zod validation schemas. Source of truth: server/src/validation/*.ts" + }, + "servers": [ + { + "url": "/api/v1", + "description": "Current server" + } + ], + "tags": [ + { + "name": "auth", + "description": "Sign up, log in, password reset, and the authenticated user's profile." + }, + { + "name": "monitors", + "description": "Configure and inspect monitors (HTTP, port, ping, hardware, docker, game, gRPC) for the caller's team." + }, + { + "name": "checks", + "description": "Individual monitoring check results, including history, summaries, and bulk deletes." + }, + { + "name": "geo-checks", + "description": "Geographic check results for monitors with multi-region probing enabled." + }, + { + "name": "incidents", + "description": "Downtime incidents derived from monitor state changes, including filtering and CSV export." + }, + { + "name": "notifications", + "description": "Notification channels (email, webhook, Slack, Discord, PagerDuty, Matrix, Teams, Telegram, Pushover, Twilio) and test alerts." + }, + { + "name": "maintenance-window", + "description": "Scheduled maintenance windows during which monitors do not generate incidents or alerts." + }, + { + "name": "status-page", + "description": "Public status pages, their configuration, and the monitors they expose." + }, + { + "name": "settings", + "description": "Global application settings (admin/superadmin)." + }, + { + "name": "invite", + "description": "Team invitations and accepting them." + }, + { + "name": "queue", + "description": "Background job queue introspection and admin actions (admin/superadmin)." + }, + { + "name": "diagnostic", + "description": "System diagnostics for the running server (admin/superadmin)." + }, + { + "name": "logs", + "description": "Application logs for the running server (admin/superadmin)." + } + ], + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "AuthPayload": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/User" + }, + "token": { + "type": "string" + } + }, + "required": [ + "user" + ], + "example": { + "user": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": [ + "admin" + ], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + }, + "User": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "role": { + "type": "array", + "items": { + "type": "string" + } + }, + "teamId": { + "type": "string" + }, + "profileImage": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "_id", + "firstName", + "lastName", + "email", + "role", + "createdAt", + "updatedAt" + ], + "additionalProperties": { + "nullable": true + }, + "example": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": [ + "admin" + ], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + } + }, + "Incident": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789c" + }, + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "teamId": { + "type": "string", + "example": "65f1c2a4d8b9e01234567890" + }, + "startTime": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "endTime": { + "type": "string", + "nullable": true, + "example": "2026-04-15T03:34:00.000Z" + }, + "status": { + "type": "boolean", + "example": true, + "description": "true = resolved, false = ongoing" + }, + "message": { + "type": "string", + "nullable": true, + "example": "HTTP 503 from origin" + }, + "statusCode": { + "type": "number", + "nullable": true, + "example": 503 + }, + "resolutionType": { + "type": "string", + "nullable": true, + "enum": [ + "automatic", + "manual", + null + ], + "example": "automatic" + }, + "resolvedBy": { + "type": "string", + "nullable": true + }, + "resolvedByEmail": { + "type": "string", + "nullable": true + }, + "comment": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "updatedAt": { + "type": "string", + "example": "2026-04-15T03:34:00.000Z" + } + }, + "required": [ + "id", + "monitorId", + "teamId", + "startTime", + "endTime", + "status", + "resolutionType", + "createdAt", + "updatedAt" + ], + "additionalProperties": { + "nullable": true + } + }, + "IncidentSummary": { + "type": "object", + "properties": { + "total": { + "type": "number", + "example": 12 + }, + "totalActive": { + "type": "number", + "example": 1 + }, + "totalManualResolutions": { + "type": "number", + "example": 2 + }, + "totalAutomaticResolutions": { + "type": "number", + "example": 9 + }, + "avgResolutionTimeHours": { + "type": "number", + "example": 0.5 + }, + "topMonitor": { + "type": "object", + "nullable": true, + "properties": { + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "monitorName": { + "type": "string", + "nullable": true, + "example": "Marketing site" + }, + "incidentCount": { + "type": "number", + "example": 4 + } + }, + "required": [ + "monitorId", + "monitorName", + "incidentCount" + ] + }, + "latestIncidents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IncidentSummaryItem" + } + } + }, + "required": [ + "total", + "totalActive", + "totalManualResolutions", + "totalAutomaticResolutions", + "avgResolutionTimeHours", + "topMonitor", + "latestIncidents" + ] + }, + "IncidentSummaryItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789c" + }, + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "monitorName": { + "type": "string", + "nullable": true, + "example": "Marketing site" + }, + "status": { + "type": "boolean", + "example": true + }, + "startTime": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "endTime": { + "type": "string", + "nullable": true, + "example": "2026-04-15T03:34:00.000Z" + }, + "resolutionType": { + "type": "string", + "nullable": true, + "enum": [ + "automatic", + "manual", + null + ], + "example": "automatic" + }, + "message": { + "type": "string", + "nullable": true, + "example": "HTTP 503 from origin" + }, + "statusCode": { + "type": "number", + "nullable": true, + "example": 503 + }, + "createdAt": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + } + }, + "required": [ + "id", + "monitorId", + "monitorName", + "status", + "startTime", + "endTime", + "resolutionType", + "message", + "statusCode", + "createdAt" + ] + }, + "Monitor": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + "url": { + "type": "string" + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "number" + }, + "status": { + "type": "string", + "enum": [ + "up", + "down", + "paused", + "initializing", + "maintenance", + "breached" + ] + }, + "statusWindowSize": { + "type": "number" + }, + "statusWindowThreshold": { + "type": "number" + }, + "ignoreTlsErrors": { + "type": "boolean" + }, + "useAdvancedMatching": { + "type": "boolean" + }, + "jsonPath": { + "type": "string" + }, + "expectedValue": { + "type": "string" + }, + "matchMethod": { + "type": "string", + "enum": [ + "equal", + "include", + "regex" + ] + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "secret": { + "type": "string" + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "gameId": { + "type": "string" + }, + "grpcServiceName": { + "type": "string" + }, + "group": { + "type": "string", + "nullable": true + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + } + }, + "geoCheckInterval": { + "type": "number" + }, + "dnsServer": { + "type": "string" + }, + "dnsRecordType": { + "type": "string", + "enum": [ + "A", + "AAAA", + "CNAME", + "MX", + "TXT", + "NS" + ] + }, + "teamId": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "_id", + "name", + "type", + "url", + "isActive", + "interval", + "status", + "statusWindowSize", + "statusWindowThreshold", + "ignoreTlsErrors", + "useAdvancedMatching", + "notifications", + "tags", + "cpuAlertThreshold", + "memoryAlertThreshold", + "diskAlertThreshold", + "tempAlertThreshold", + "selectedDisks", + "geoCheckEnabled", + "geoCheckLocations", + "geoCheckInterval", + "teamId", + "userId", + "createdAt", + "updatedAt" + ], + "additionalProperties": { + "nullable": true + }, + "example": { + "_id": "65f1c2a4d8b9e0123456789a", + "name": "Marketing site", + "description": "Production marketing site monitored from the EU region", + "type": "http", + "url": "https://www.example.com", + "port": 443, + "isActive": true, + "interval": 60000, + "status": "up", + "statusWindowSize": 5, + "statusWindowThreshold": 3, + "ignoreTlsErrors": false, + "useAdvancedMatching": false, + "notifications": [ + "65f1c2a4d8b9e0123456789b" + ], + "cpuAlertThreshold": 90, + "memoryAlertThreshold": 90, + "diskAlertThreshold": 90, + "tempAlertThreshold": 80, + "selectedDisks": [], + "geoCheckEnabled": false, + "geoCheckLocations": [], + "geoCheckInterval": 300000, + "teamId": "65f1c2a4d8b9e01234567890", + "userId": "65f1c2a4d8b9e01234567891", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + } + }, + "NotificationChannelBody": { + "oneOf": [ + { + "$ref": "#/components/schemas/EmailNotification" + }, + { + "$ref": "#/components/schemas/WebhookNotification" + }, + { + "$ref": "#/components/schemas/SlackNotification" + }, + { + "$ref": "#/components/schemas/DiscordNotification" + }, + { + "$ref": "#/components/schemas/PagerDutyNotification" + }, + { + "$ref": "#/components/schemas/MatrixNotification" + }, + { + "$ref": "#/components/schemas/TeamsNotification" + }, + { + "$ref": "#/components/schemas/TelegramNotification" + }, + { + "$ref": "#/components/schemas/PushoverNotification" + }, + { + "$ref": "#/components/schemas/TwilioNotification" + }, + { + "$ref": "#/components/schemas/NtfyNotification" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "email": "#/components/schemas/EmailNotification", + "webhook": "#/components/schemas/WebhookNotification", + "slack": "#/components/schemas/SlackNotification", + "discord": "#/components/schemas/DiscordNotification", + "pager_duty": "#/components/schemas/PagerDutyNotification", + "matrix": "#/components/schemas/MatrixNotification", + "teams": "#/components/schemas/TeamsNotification", + "telegram": "#/components/schemas/TelegramNotification", + "pushover": "#/components/schemas/PushoverNotification", + "twilio": "#/components/schemas/TwilioNotification", + "ntfy": "#/components/schemas/NtfyNotification" + } + } + }, + "EmailNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "email" + ] + }, + "address": { + "type": "string", + "format": "email" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + } + }, + "required": [ + "notificationName", + "type", + "address" + ], + "example": { + "notificationName": "Ops on-call email", + "type": "email", + "address": "alerts@example.com" + } + }, + "WebhookNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + } + }, + "required": [ + "notificationName", + "type", + "address" + ], + "example": { + "notificationName": "Custom webhook", + "type": "webhook", + "address": "https://example.com/hooks/checkmate" + } + }, + "SlackNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "slack" + ] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + } + }, + "required": [ + "notificationName", + "type", + "address" + ], + "example": { + "notificationName": "#alerts", + "type": "slack", + "address": "https://hooks.slack.com/services/T000/B000/XXXX" + } + }, + "DiscordNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "discord" + ] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + } + }, + "required": [ + "notificationName", + "type", + "address" + ], + "example": { + "notificationName": "#status", + "type": "discord", + "address": "https://discord.com/api/webhooks/123/abc" + } + }, + "PagerDutyNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "pager_duty" + ] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + } + }, + "required": [ + "notificationName", + "type", + "address" + ], + "example": { + "notificationName": "PagerDuty primary", + "type": "pager_duty", + "address": "R01XXXXXXXXXXXXXXXXXXXXXXX" + } + }, + "MatrixNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "matrix" + ] + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "homeserverUrl": { + "type": "string", + "format": "uri" + }, + "roomId": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "notificationName", + "type", + "homeserverUrl", + "roomId", + "accessToken" + ], + "example": { + "notificationName": "Matrix room", + "type": "matrix", + "homeserverUrl": "https://matrix.example.com", + "roomId": "!abc123:example.com", + "accessToken": "syt_xxx" + } + }, + "TeamsNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "teams" + ] + }, + "address": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "notificationName", + "type", + "address" + ], + "example": { + "notificationName": "Teams ops channel", + "type": "teams", + "address": "https://outlook.office.com/webhook/..." + } + }, + "TelegramNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "telegram" + ] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "notificationName", + "type", + "address", + "accessToken" + ], + "example": { + "notificationName": "Telegram bot", + "type": "telegram", + "address": "-1001234567890", + "accessToken": "123456:ABC-DEF" + } + }, + "PushoverNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "pushover" + ] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "notificationName", + "type", + "address", + "accessToken" + ], + "example": { + "notificationName": "Pushover personal", + "type": "pushover", + "address": "u1234567890abcdef", + "accessToken": "a1234567890abcdef" + } + }, + "TwilioNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "twilio" + ] + }, + "accountSid": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + }, + "phone": { + "type": "string", + "minLength": 1 + }, + "twilioPhoneNumber": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "notificationName", + "type", + "accountSid", + "accessToken", + "phone", + "twilioPhoneNumber" + ], + "example": { + "notificationName": "Twilio SMS", + "type": "twilio", + "accountSid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "accessToken": "your-auth-token", + "phone": "+15551234567", + "twilioPhoneNumber": "+15557654321" + } + }, + "NtfyNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": [ + "ntfy" + ] + }, + "address": { + "type": "string", + "format": "uri" + }, + "topic": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "notificationName", + "type", + "address", + "topic" + ], + "example": { + "notificationName": "ntfy topic", + "type": "ntfy", + "address": "https://ntfy.sh", + "topic": "checkmate-alerts" + } + } + }, + "parameters": {} + }, + "paths": { + "/auth/register": { + "post": { + "tags": [ + "auth" + ], + "summary": "Register a new user (first user becomes superadmin)", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "profileImage": { + "type": "string", + "format": "binary" + }, + "inviteToken": { + "type": "string", + "default": "" + } + }, + "required": [ + "firstName", + "lastName", + "email", + "password", + "profileImage" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/AuthPayload" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/login": { + "post": { + "tags": [ + "auth" + ], + "summary": "Log in", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "email", + "password" + ] + }, + "example": { + "email": "ada@example.com", + "password": "S3cure!Passw0rd" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/AuthPayload" + } + }, + "required": [ + "success", + "msg", + "data" + ] + }, + "example": { + "success": true, + "msg": "OK", + "data": { + "user": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": [ + "admin" + ], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + } + } + }, + "401": { + "description": "Invalid credentials", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/recovery/request": { + "post": { + "tags": [ + "auth" + ], + "summary": "Request a password recovery email", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + } + }, + "required": [ + "email" + ] + }, + "example": { + "email": "ada@example.com" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/recovery/validate": { + "post": { + "tags": [ + "auth" + ], + "summary": "Validate a password recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recoveryToken": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "recoveryToken" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/recovery/reset": { + "post": { + "tags": [ + "auth" + ], + "summary": "Reset password using a recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recoveryToken": { + "type": "string", + "minLength": 1 + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "confirm": { + "type": "string" + } + }, + "required": [ + "recoveryToken", + "password" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/users/superadmin": { + "get": { + "tags": [ + "auth" + ], + "summary": "Check whether a superadmin user exists", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "superAdminExists": { + "type": "boolean" + } + }, + "required": [ + "superAdminExists" + ] + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/users": { + "get": { + "tags": [ + "auth" + ], + "summary": "List all users (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "auth" + ], + "summary": "Create a new user (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "user", + "admin", + "superadmin", + "demo" + ] + }, + "minItems": 1 + }, + "profileImage": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "firstName", + "lastName", + "email", + "password", + "role", + "profileImage" + ] + } + } + } + }, + "responses": { + "201": { + "description": "User created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/users/{userId}": { + "get": { + "tags": [ + "auth" + ], + "summary": "Get a user by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "404": { + "description": "User not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "patch": { + "tags": [ + "auth" + ], + "summary": "Edit a user (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "user", + "admin", + "superadmin", + "demo" + ] + }, + "minItems": 1 + } + }, + "required": [ + "firstName", + "lastName", + "role" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "auth" + ], + "summary": "Delete a user (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/users/{userId}/password": { + "patch": { + "tags": [ + "auth" + ], + "summary": "Change a user's password (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + } + }, + "required": [ + "password" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/auth/user": { + "patch": { + "tags": [ + "auth" + ], + "summary": "Edit the currently authenticated user", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "profilePicture": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "newPassword": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "profileImage": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "profileImage" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "auth" + ], + "summary": "Delete the currently authenticated user", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/checks/team/summary": { + "get": { + "tags": [ + "checks" + ], + "summary": "Aggregate check summary for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/checks/team": { + "get": { + "tags": [ + "checks" + ], + "summary": "List checks across the team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "all", + "up", + "down", + "resolve" + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "checks" + ], + "summary": "Delete all checks for the team (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "deletedCount": { + "type": "number" + } + }, + "required": [ + "deletedCount" + ] + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/checks/{monitorId}": { + "get": { + "tags": [ + "checks" + ], + "summary": "List checks for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "all", + "up", + "down", + "resolve" + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "checks" + ], + "summary": "Delete checks for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/diagnostic/system": { + "get": { + "tags": [ + "diagnostic" + ], + "summary": "Get system diagnostics (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/geo-checks/{monitorId}": { + "get": { + "tags": [ + "geo-checks" + ], + "summary": "Get geo check results for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "all", + "up", + "down", + "resolve" + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/incidents/team": { + "get": { + "tags": [ + "incidents" + ], + "summary": "List incidents for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ], + "default": "all" + }, + "required": false, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "monitorId", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "manual", + "automatic" + ] + }, + "required": false, + "name": "resolutionType", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Incident" + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/incidents/team/summary": { + "get": { + "tags": [ + "incidents" + ], + "summary": "Incident summary for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": false, + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/IncidentSummary" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/incidents/{incidentId}": { + "get": { + "tags": [ + "incidents" + ], + "summary": "Get an incident by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "incidentId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "incident": { + "$ref": "#/components/schemas/Incident" + }, + "monitor": { + "nullable": true + }, + "user": { + "nullable": true + } + }, + "required": [ + "incident" + ] + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/incidents/{incidentId}/resolve": { + "put": { + "tags": [ + "incidents" + ], + "summary": "Manually resolve an incident (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "incidentId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/invite/send": { + "post": { + "tags": [ + "invite" + ], + "summary": "Send an invite email (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "user", + "admin", + "superadmin", + "demo" + ] + }, + "minItems": 1 + }, + "teamId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "email", + "role", + "teamId" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/invite/verify": { + "post": { + "tags": [ + "invite" + ], + "summary": "Verify an invite token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "token": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "token" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "role": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "email" + ], + "additionalProperties": { + "nullable": true + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/invite": { + "post": { + "tags": [ + "invite" + ], + "summary": "Create an invite token (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "user", + "admin", + "superadmin", + "demo" + ] + }, + "minItems": 1 + }, + "teamId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "email", + "role", + "teamId" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + }, + "required": [ + "token" + ], + "additionalProperties": { + "nullable": true + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/logs": { + "get": { + "tags": [ + "logs" + ], + "summary": "Get application logs (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/maintenance-window": { + "post": { + "tags": [ + "maintenance-window" + ], + "summary": "Create one or more maintenance windows", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitors": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "active": { + "type": "boolean" + }, + "duration": { + "type": "number", + "minimum": 1 + }, + "durationUnit": { + "type": "string", + "enum": [ + "seconds", + "minutes", + "hours", + "days" + ] + }, + "start": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "end": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "repeat": { + "type": "number", + "minimum": 0 + }, + "expiry": { + "type": "string", + "nullable": true, + "format": "date-time" + } + }, + "required": [ + "monitors", + "name", + "duration", + "durationUnit", + "start", + "end", + "repeat" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/maintenance-window/team": { + "get": { + "tags": [ + "maintenance-window" + ], + "summary": "List maintenance windows for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "active", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "field", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "required": false, + "name": "order", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/maintenance-window/monitor/{monitorId}": { + "get": { + "tags": [ + "maintenance-window" + ], + "summary": "List maintenance windows for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/maintenance-window/{id}": { + "get": { + "tags": [ + "maintenance-window" + ], + "summary": "Get a maintenance window by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "patch": { + "tags": [ + "maintenance-window" + ], + "summary": "Edit a maintenance window", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "repeat": { + "type": "number" + }, + "start": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "end": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "expiry": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "monitors": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "duration": { + "type": "number" + }, + "durationUnit": { + "type": "string", + "enum": [ + "seconds", + "minutes", + "hours", + "days" + ] + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "maintenance-window" + ], + "summary": "Delete a maintenance window", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/team": { + "get": { + "tags": [ + "monitors" + ], + "summary": "List monitors for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + } + } + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "required": false, + "name": "tags", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Monitor" + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/team/with-checks": { + "get": { + "tags": [ + "monitors" + ], + "summary": "List team monitors with their most recent checks (paginated)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "field", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "required": false, + "name": "order", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + } + } + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "required": false, + "name": "tags", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "explain", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/team/groups": { + "get": { + "tags": [ + "monitors" + ], + "summary": "List monitor groups for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/uptime/details/{monitorId}": { + "get": { + "tags": [ + "monitors" + ], + "summary": "Get uptime details for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "normalize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/hardware/details/{monitorId}": { + "get": { + "tags": [ + "monitors" + ], + "summary": "Get hardware metrics detail for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/pagespeed/details/{monitorId}": { + "get": { + "tags": [ + "monitors" + ], + "summary": "Get PageSpeed detail for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/{monitorId}/geo-checks": { + "get": { + "tags": [ + "monitors" + ], + "summary": "Get geo check results for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "required": false, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "recent", + "hour", + "day", + "week", + "month", + "all" + ] + }, + "required": false, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "numToDisplay", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "normalize", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/pause/{monitorId}": { + "post": { + "tags": [ + "monitors" + ], + "summary": "Toggle pause state for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/certificate/{monitorId}": { + "get": { + "tags": [ + "monitors" + ], + "summary": "Get SSL certificate info for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "certificateDate": { + "type": "string" + } + }, + "required": [ + "certificateDate" + ] + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/notifications": { + "patch": { + "tags": [ + "monitors" + ], + "summary": "Bulk update notifications across monitors", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitorIds": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 100 + }, + "notificationIds": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 100 + }, + "action": { + "type": "string", + "enum": [ + "add", + "remove", + "set" + ] + } + }, + "required": [ + "monitorIds", + "notificationIds", + "action" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "modifiedCount": { + "type": "number" + } + }, + "required": [ + "modifiedCount" + ] + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors": { + "post": { + "tags": [ + "monitors" + ], + "summary": "Create a new monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "type": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "url": { + "type": "string", + "minLength": 1 + }, + "ignoreTlsErrors": { + "type": "boolean", + "default": false + }, + "useAdvancedMatching": { + "type": "boolean", + "default": false + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "number" + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "secret": { + "type": "string" + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "equal", + "include", + "regex" + ] + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ], + "default": "" + }, + "strategy": { + "type": "string", + "enum": [ + "desktop", + "mobile" + ] + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + } + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": [ + "A", + "AAAA", + "CNAME", + "MX", + "TXT", + "NS" + ] + } + }, + "required": [ + "name", + "type", + "url" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Monitor created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "monitors" + ], + "summary": "Delete every monitor (superadmin only)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/demo": { + "post": { + "tags": [ + "monitors" + ], + "summary": "Insert preconfigured demo monitors", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "number" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/export/json": { + "get": { + "tags": [ + "monitors" + ], + "summary": "Export all team monitors as JSON", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/import/json": { + "post": { + "tags": [ + "monitors" + ], + "summary": "Import monitors from JSON", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "status": { + "type": "string", + "enum": [ + "up", + "down", + "paused", + "initializing", + "maintenance", + "breached" + ], + "default": "initializing" + }, + "statusWindow": { + "type": "array", + "items": { + "type": "boolean" + }, + "default": [] + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "type": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + "ignoreTlsErrors": { + "type": "boolean", + "default": false + }, + "useAdvancedMatching": { + "type": "boolean", + "default": false + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "equal", + "include", + "regex" + ] + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "url": { + "type": "string", + "minLength": 1 + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean", + "default": true + }, + "interval": { + "type": "number", + "default": 60000 + }, + "uptimePercentage": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "secret": { + "type": "string" + }, + "cpuAlertThreshold": { + "type": "number", + "default": 100 + }, + "cpuAlertCounter": { + "type": "number", + "default": 5 + }, + "memoryAlertThreshold": { + "type": "number", + "default": 100 + }, + "memoryAlertCounter": { + "type": "number", + "default": 5 + }, + "diskAlertThreshold": { + "type": "number", + "default": 100 + }, + "diskAlertCounter": { + "type": "number", + "default": 5 + }, + "tempAlertThreshold": { + "type": "number", + "default": 100 + }, + "tempAlertCounter": { + "type": "number", + "default": 5 + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ], + "default": "" + }, + "strategy": { + "type": "string", + "enum": [ + "desktop", + "mobile" + ] + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + } + ], + "default": null + }, + "geoCheckEnabled": { + "type": "boolean", + "default": false + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + }, + "default": [] + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000, + "default": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": [ + "A", + "AAAA", + "CNAME", + "MX", + "TXT", + "NS" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "name", + "type", + "url" + ] + }, + "minItems": 1 + } + }, + "required": [ + "monitors" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "imported": { + "type": "number" + } + }, + "required": [ + "imported" + ], + "additionalProperties": { + "nullable": true + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/games": { + "get": { + "tags": [ + "monitors" + ], + "summary": "List supported game-server types", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/monitors/{monitorId}": { + "get": { + "tags": [ + "monitors" + ], + "summary": "Get a monitor by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "patch": { + "tags": [ + "monitors" + ], + "summary": "Edit a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "http", + "ping", + "pagespeed", + "hardware", + "docker", + "port", + "game", + "grpc", + "websocket", + "dns", + "unknown" + ] + }, + "url": { + "type": "string" + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "interval": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "secret": { + "type": "string" + }, + "ignoreTlsErrors": { + "type": "boolean" + }, + "useAdvancedMatching": { + "type": "boolean" + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "equal", + "include", + "regex" + ] + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "port": { + "type": "number", + "minimum": 1, + "maximum": 65535 + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "strategy": { + "type": "string", + "enum": [ + "desktop", + "mobile" + ] + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "EU", + "NA", + "AS", + "SA", + "AF", + "OC" + ] + } + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": [ + "A", + "AAAA", + "CNAME", + "MX", + "TXT", + "NS" + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "monitors" + ], + "summary": "Delete a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/notifications": { + "post": { + "tags": [ + "notifications" + ], + "summary": "Create a notification channel", + "description": "Create a notification channel of any supported type. The `type` field discriminates the body shape.", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/notifications/test/all": { + "post": { + "tags": [ + "notifications" + ], + "summary": "Send a test alert through every notification channel for the team", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitorId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "monitorId" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/notifications/test": { + "post": { + "tags": [ + "notifications" + ], + "summary": "Send a test alert through a single notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ], + "additionalProperties": { + "nullable": true + } + } + }, + "required": [ + "success", + "msg", + "data" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/notifications/team": { + "get": { + "tags": [ + "notifications" + ], + "summary": "List notification channels for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/notifications/{id}": { + "get": { + "tags": [ + "notifications" + ], + "summary": "Get a notification channel by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "notifications" + ], + "summary": "Delete a notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "patch": { + "tags": [ + "notifications" + ], + "summary": "Edit a notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/queue/jobs": { + "get": { + "tags": [ + "queue" + ], + "summary": "List queued monitor jobs (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/queue/metrics": { + "get": { + "tags": [ + "queue" + ], + "summary": "Get queue runtime metrics (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/queue/all-metrics": { + "get": { + "tags": [ + "queue" + ], + "summary": "Get queue metrics across all monitors (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/queue/flush": { + "post": { + "tags": [ + "queue" + ], + "summary": "Flush the monitor job queue (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/settings": { + "get": { + "tags": [ + "settings" + ], + "summary": "Get application settings", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "patch": { + "tags": [ + "settings" + ], + "summary": "Update application settings (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "checkTTL": { + "type": "integer", + "minimum": 1, + "maximum": 366 + }, + "systemEmailPort": { + "type": "number", + "nullable": true + }, + "pagespeedApiKey": { + "type": "string", + "nullable": true + }, + "language": { + "type": "string" + }, + "timezone": { + "type": "string" + }, + "systemEmailHost": { + "type": "string", + "nullable": true + }, + "systemEmailAddress": { + "type": "string", + "nullable": true + }, + "systemEmailDisplayName": { + "type": "string", + "nullable": true, + "maxLength": 100 + }, + "systemEmailPassword": { + "type": "string", + "nullable": true + }, + "systemEmailUser": { + "type": "string", + "nullable": true + }, + "systemEmailConnectionHost": { + "type": "string", + "nullable": true + }, + "systemEmailTLSServername": { + "type": "string", + "nullable": true + }, + "showURL": { + "type": "boolean" + }, + "systemEmailSecure": { + "type": "boolean" + }, + "systemEmailPool": { + "type": "boolean" + }, + "systemEmailIgnoreTLS": { + "type": "boolean" + }, + "systemEmailRequireTLS": { + "type": "boolean" + }, + "systemEmailRejectUnauthorized": { + "type": "boolean" + }, + "globalThresholds": { + "type": "object", + "properties": { + "cpu": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "memory": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "disk": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "temperature": { + "type": "number", + "minimum": 1, + "maximum": 150 + } + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/settings/test-email": { + "post": { + "tags": [ + "settings" + ], + "summary": "Send a test email using current SMTP settings (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "minLength": 1 + }, + "systemEmailHost": { + "type": "string" + }, + "systemEmailPort": { + "type": "number" + }, + "systemEmailSecure": { + "type": "boolean" + }, + "systemEmailPool": { + "type": "boolean" + }, + "systemEmailAddress": { + "type": "string" + }, + "systemEmailDisplayName": { + "type": "string" + }, + "systemEmailPassword": { + "type": "string" + }, + "systemEmailUser": { + "type": "string" + }, + "systemEmailConnectionHost": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "systemEmailIgnoreTLS": { + "type": "boolean" + }, + "systemEmailRequireTLS": { + "type": "boolean" + }, + "systemEmailRejectUnauthorized": { + "type": "boolean" + }, + "systemEmailTLSServername": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + } + }, + "required": [ + "to" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/status-page/team": { + "get": { + "tags": [ + "status-page" + ], + "summary": "List status pages for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/status-page": { + "post": { + "tags": [ + "status-page" + ], + "summary": "Create a status page (logo upload optional)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + } + } + ] + }, + "companyName": { + "type": "string", + "minLength": 1 + }, + "url": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "customDomain": { + "anyOf": [ + { + "type": "string", + "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" + }, + { + "nullable": true + } + ] + }, + "timezone": { + "type": "string" + }, + "color": { + "type": "string" + }, + "monitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + }, + "minItems": 1 + }, + "subMonitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + } + }, + "deleteSubmonitors": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "showCharts": { + "type": "boolean" + }, + "showUptimePercentage": { + "type": "boolean" + }, + "showAdminLoginLink": { + "type": "boolean" + }, + "showInfrastructure": { + "type": "boolean" + }, + "removeLogo": { + "anyOf": [ + { + "type": "string", + "enum": [ + "true" + ] + }, + { + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "theme": { + "type": "string", + "enum": [ + "refined", + "modern", + "bold", + "editorial" + ] + }, + "themeMode": { + "type": "string", + "enum": [ + "auto", + "light", + "dark" + ] + }, + "logo": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "type", + "companyName", + "url", + "monitors", + "isPublished", + "showUptimePercentage", + "logo" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/status-page/{id}": { + "put": { + "tags": [ + "status-page" + ], + "summary": "Update a status page (logo upload optional)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + } + } + ] + }, + "companyName": { + "type": "string", + "minLength": 1 + }, + "url": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "customDomain": { + "anyOf": [ + { + "type": "string", + "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" + }, + { + "nullable": true + } + ] + }, + "timezone": { + "type": "string" + }, + "color": { + "type": "string" + }, + "monitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + }, + "minItems": 1 + }, + "subMonitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + } + }, + "deleteSubmonitors": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "showCharts": { + "type": "boolean" + }, + "showUptimePercentage": { + "type": "boolean" + }, + "showAdminLoginLink": { + "type": "boolean" + }, + "showInfrastructure": { + "type": "boolean" + }, + "removeLogo": { + "anyOf": [ + { + "type": "string", + "enum": [ + "true" + ] + }, + { + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "theme": { + "type": "string", + "enum": [ + "refined", + "modern", + "bold", + "editorial" + ] + }, + "themeMode": { + "type": "string", + "enum": [ + "auto", + "light", + "dark" + ] + }, + "logo": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "type", + "companyName", + "url", + "monitors", + "isPublished", + "showUptimePercentage", + "logo" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "status-page" + ], + "summary": "Delete a status page", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + } + }, + "required": [ + "success", + "msg" + ] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/status-page/resolve": { + "get": { + "tags": [ + "status-page" + ], + "summary": "Resolve a published status page by custom domain", + "parameters": [ + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + } + } + ] + }, + "required": true, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "timeFrame", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "domain", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "404": { + "description": "Status page not found" + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + }, + "/status-page/{url}": { + "get": { + "tags": [ + "status-page" + ], + "summary": "Get a public status page by its URL slug", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "url", + "in": "path" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "uptime", + "infrastructure" + ] + } + } + ] + }, + "required": true, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "timeFrame", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + }, + "404": { + "description": "Status page not found" + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + false + ] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": [ + "success", + "msg" + ] + } + } + } + } + } + } + } + } } diff --git a/server/openapi/routes/statusPage.ts b/server/openapi/routes/statusPage.ts index ac315b31c..d17ab88f5 100644 --- a/server/openapi/routes/statusPage.ts +++ b/server/openapi/routes/statusPage.ts @@ -1,6 +1,11 @@ import { registry } from "../registry.js"; import { bearer, multipart, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; -import { createStatusPageBodyValidation, getStatusPageParamValidation, getStatusPageQueryValidation } from "@/validation/statusPageValidation.js"; +import { + createStatusPageBodyValidation, + getStatusPageParamValidation, + getStatusPageQueryValidation, + resolveStatusPageQueryValidation, +} from "@/validation/statusPageValidation.js"; const tags = ["status-page"]; @@ -33,6 +38,15 @@ registry.registerPath({ responses: { "200": okUnknown, ...standardErrors }, }); +registry.registerPath({ + method: "get", + path: "/status-page/resolve", + tags, + summary: "Resolve a published status page by custom domain", + request: { query: resolveStatusPageQueryValidation }, + responses: { "200": okUnknown, "404": { description: "Status page not found" }, "500": standardErrors["500"] }, +}); + registry.registerPath({ method: "get", path: "/status-page/{url}", diff --git a/server/src/app.ts b/server/src/app.ts index da1cd8ad9..02802fc83 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -12,6 +12,7 @@ import { setupRoutes } from "@/config/routes.js"; import { InitializedServices } from "@/config/services.js"; import { InitializedControllers } from "@/config/controllers.js"; import { EnvConfig } from "@/service/system/settingsService.js"; +import { createStatusPageCorsOrigin } from "@/middleware/statusPageCorsOrigin.js"; export const createApp = ({ services, @@ -33,7 +34,7 @@ export const createApp = ({ app.use( cors({ - origin: allowedOrigin, + origin: createStatusPageCorsOrigin(allowedOrigin, services.statusPagesRepository), methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS", allowedHeaders: ["Content-Type", "Authorization", "Accept-Language"], credentials: true, diff --git a/server/src/controllers/statusPageController.ts b/server/src/controllers/statusPageController.ts index 190c3a9fd..1a7ed2347 100644 --- a/server/src/controllers/statusPageController.ts +++ b/server/src/controllers/statusPageController.ts @@ -5,6 +5,7 @@ import { getStatusPageParamValidation, getStatusPageQueryValidation, imageValidation, + resolveStatusPageQueryValidation, } from "@/validation/statusPageValidation.js"; import { AppError } from "@/utils/AppError.js"; import { requireTeamId, requireUserId } from "@/controllers/controllerUtils.js"; @@ -12,6 +13,8 @@ import { IStatusPageService } from "@/service/business/statusPageService.js"; import { IMonitorsRepository } from "@/repositories/index.js"; import { ISettingsService } from "@/service/system/settingsService.js"; import { NormalizeData } from "@/utils/dataUtils.js"; +import { resolveStatusPageDomainFromRequest } from "@/utils/statusPageDomain.js"; +import type { StatusPage } from "@/types/statusPage.js"; const SERVICE_NAME = "statusPageController"; @@ -20,6 +23,7 @@ export interface IStatusPageController { createStatusPage(req: Request, res: Response, next: NextFunction): Promise; updateStatusPage(req: Request, res: Response, next: NextFunction): Promise; getStatusPageByUrl(req: Request, res: Response, next: NextFunction): Promise; + resolveStatusPageByDomain(req: Request, res: Response, next: NextFunction): Promise; getStatusPagesByTeamId(req: Request, res: Response, next: NextFunction): Promise; deleteStatusPage(req: Request, res: Response, next: NextFunction): Promise; } @@ -39,6 +43,41 @@ class StatusPageController implements IStatusPageController { return StatusPageController.SERVICE_NAME; } + private buildStatusPagePayload = async (statusPage: StatusPage, req: Request) => { + if (!statusPage.isPublished) { + const teamId = requireTeamId(req?.user?.teamId); + if (statusPage.teamId !== teamId) { + throw new AppError({ message: "Forbidden", status: 403 }); + } + } + + const settings = await this.settingsService.getDBSettings(); + const showURL = settings.showURL; + + const monitors = await this.monitorsRepository.findByIds(statusPage.monitors); + const monitorOrder = new Map(statusPage.monitors.map((id, index) => [id, index])); + const sortedMonitors = [...monitors].sort((a, b) => { + const orderA = monitorOrder.get(a.id) ?? Number.MAX_SAFE_INTEGER; + const orderB = monitorOrder.get(b.id) ?? Number.MAX_SAFE_INTEGER; + return orderA - orderB; + }); + + const normalizedMonitors = sortedMonitors.map((monitor) => { + const normalizedChecks = NormalizeData(monitor.recentChecks, 10, 100); + if (!showURL) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { url, port, secret, notifications, ...rest } = monitor; + return { ...rest, checks: normalizedChecks }; + } + return { ...monitor, checks: normalizedChecks }; + }); + + return { + statusPage, + monitors: normalizedMonitors, + }; + }; + createStatusPage = async (req: Request, res: Response, next: NextFunction) => { try { createStatusPageBodyValidation.parse(req.body); @@ -96,47 +135,44 @@ class StatusPageController implements IStatusPageController { } const statusPage = await this.statusPageService.getStatusPageByUrl(req.params.url as string); + const data = await this.buildStatusPagePayload(statusPage, req); - if (!statusPage.isPublished) { - const teamId = requireTeamId(req?.user?.teamId); - if (statusPage.teamId !== teamId) { - throw new AppError({ message: "Forbidden", status: 403 }); - } - } - - const settings = await this.settingsService.getDBSettings(); - const showURL = settings.showURL; - - const monitors = await this.monitorsRepository.findByIds(statusPage.monitors); - // Sort monitors according to the order in statusPage.monitors - const monitorOrder = new Map(statusPage.monitors.map((id, index) => [id, index])); - const sortedMonitors = [...monitors].sort((a, b) => { - const orderA = monitorOrder.get(a.id) ?? Number.MAX_SAFE_INTEGER; - const orderB = monitorOrder.get(b.id) ?? Number.MAX_SAFE_INTEGER; - return orderA - orderB; - }); - - const normalizedMonitors = sortedMonitors.map((monitor) => { - const normalizedChecks = NormalizeData(monitor.recentChecks, 10, 100); - if (!showURL) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { url, port, secret, notifications, ...rest } = monitor; - return { ...rest, checks: normalizedChecks }; - } - return { ...monitor, checks: normalizedChecks }; - }); return res.status(200).json({ success: true, msg: "Status page retrieved successfully", - data: { - statusPage, - monitors: normalizedMonitors, - }, + data, }); } catch (error) { next(error); } }; + + resolveStatusPageByDomain = async (req: Request, res: Response, next: NextFunction) => { + try { + resolveStatusPageQueryValidation.parse(req.query); + + const domain = resolveStatusPageDomainFromRequest(req.hostname, req.query.domain as string | undefined); + if (!domain) { + throw new AppError({ message: "Domain is required", status: 400 }); + } + + const statusPage = await this.statusPageService.getStatusPageByCustomDomain(domain); + if (!statusPage.isPublished) { + throw new AppError({ message: "Status page not found", status: 404 }); + } + + const data = await this.buildStatusPagePayload(statusPage, req); + + return res.status(200).json({ + success: true, + msg: "Status page retrieved successfully", + data, + }); + } catch (error) { + next(error); + } + }; + getStatusPagesByTeamId = async (req: Request, res: Response, next: NextFunction) => { try { const teamId = requireTeamId(req.user?.teamId); diff --git a/server/src/db/models/StatusPage.ts b/server/src/db/models/StatusPage.ts index 0b6451492..358b0dde5 100644 --- a/server/src/db/models/StatusPage.ts +++ b/server/src/db/models/StatusPage.ts @@ -65,6 +65,14 @@ const StatusPageSchema = new Schema( required: true, default: "", }, + customDomain: { + type: String, + default: null, + lowercase: true, + trim: true, + sparse: true, + unique: true, + }, timezone: { type: String, }, diff --git a/server/src/middleware/statusPageCorsOrigin.ts b/server/src/middleware/statusPageCorsOrigin.ts new file mode 100644 index 000000000..0ec25c1c0 --- /dev/null +++ b/server/src/middleware/statusPageCorsOrigin.ts @@ -0,0 +1,39 @@ +import type { CorsOptions } from "cors"; +import type { IStatusPagesRepository } from "@/repositories/index.js"; +import { normalizeStatusPageDomain } from "@/utils/statusPageDomain.js"; + +export const createStatusPageCorsOrigin = (clientHost: string, statusPagesRepository: IStatusPagesRepository): CorsOptions["origin"] => { + return (origin, callback) => { + if (!origin) { + callback(null, true); + return; + } + + if (origin === clientHost) { + callback(null, true); + return; + } + + let customDomain: string | null = null; + try { + customDomain = normalizeStatusPageDomain(new URL(origin).hostname); + } catch { + callback(null, false); + return; + } + + if (!customDomain) { + callback(null, false); + return; + } + + statusPagesRepository + .findByCustomDomain(customDomain) + .then((statusPage) => { + callback(null, statusPage.isPublished ? origin : false); + }) + .catch(() => { + callback(null, false); + }); + }; +}; diff --git a/server/src/repositories/status-pages/IStatusPagesRepository.ts b/server/src/repositories/status-pages/IStatusPagesRepository.ts index c901b3c6d..d4ca85f78 100644 --- a/server/src/repositories/status-pages/IStatusPagesRepository.ts +++ b/server/src/repositories/status-pages/IStatusPagesRepository.ts @@ -5,6 +5,7 @@ export interface IStatusPagesRepository { create(userId: string, teamId: string, image: Express.Multer.File | undefined, data: Partial): Promise; // single fetch findByUrl(url: string): Promise; + findByCustomDomain(customDomain: string): Promise; findByTeamId(teamId: string): Promise; // collection fetch // update diff --git a/server/src/repositories/status-pages/MongoStatusPagesRepository.ts b/server/src/repositories/status-pages/MongoStatusPagesRepository.ts index 3f4630eb1..785cfd139 100644 --- a/server/src/repositories/status-pages/MongoStatusPagesRepository.ts +++ b/server/src/repositories/status-pages/MongoStatusPagesRepository.ts @@ -3,6 +3,7 @@ import { type StatusPageDocument, StatusPageModel } from "@/db/models/StatusPage import type { StatusPage, StatusPageLogo, StatusPageLogoDocument } from "@/types/statusPage.js"; import mongoose from "mongoose"; import { AppError } from "@/utils/AppError.js"; +import { normalizeStatusPageDomain } from "@/utils/statusPageDomain.js"; // Type for update data that can include document-level fields (Buffer for logo) type StatusPageUpdateData = Partial> & { @@ -47,6 +48,7 @@ class MongoStatusPagesRepository implements IStatusPagesRepository { type: doc.type, companyName: doc.companyName, url: doc.url, + customDomain: doc.customDomain ?? null, timezone: doc.timezone ?? undefined, color: doc.color, monitors: this.mapIdArray(doc.monitors), @@ -74,10 +76,11 @@ class MongoStatusPagesRepository implements IStatusPagesRepository { }; create = async (userId: string, teamId: string, image: Express.Multer.File | undefined, data: Partial): Promise => { - const { logo, ...restData } = data; + const { logo, customDomain, ...restData } = data; void logo; const statusPage = new StatusPageModel({ ...restData, + customDomain: normalizeStatusPageDomain(customDomain), userId, teamId, }); @@ -102,6 +105,21 @@ class MongoStatusPagesRepository implements IStatusPagesRepository { // Get status page }; + findByCustomDomain = async (customDomain: string): Promise => { + const normalizedDomain = normalizeStatusPageDomain(customDomain); + if (!normalizedDomain) { + throw new AppError({ message: "Status page not found", status: 404 }); + } + + const statusPage = await StatusPageModel.findOne({ + customDomain: normalizedDomain, + }); + if (!statusPage) { + throw new AppError({ message: "Status page not found", status: 404 }); + } + return this.toEntity(statusPage); + }; + findByTeamId = async (teamId: string): Promise => { const statusPages = await StatusPageModel.find({ teamId }); return this.mapDocuments(statusPages); @@ -113,9 +131,12 @@ class MongoStatusPagesRepository implements IStatusPagesRepository { image: Express.Multer.File | undefined, patch: Partial & { removeLogo?: string } ): Promise => { - const { logo, removeLogo, ...restPatch } = patch; + const { logo, removeLogo, customDomain, ...restPatch } = patch; void logo; const updateData: StatusPageUpdateData = { ...restPatch }; + if (customDomain !== undefined) { + updateData.customDomain = normalizeStatusPageDomain(customDomain); + } if (image) { updateData.logo = { data: image.buffer as Buffer, diff --git a/server/src/routes/statusPageRoute.ts b/server/src/routes/statusPageRoute.ts index 871a67300..29cdab609 100755 --- a/server/src/routes/statusPageRoute.ts +++ b/server/src/routes/statusPageRoute.ts @@ -19,6 +19,7 @@ class StatusPageRoutes { this.router.post("/", upload.single("logo"), verifyJWT, this.statusPageController.createStatusPage); this.router.put("/:id", upload.single("logo"), verifyJWT, this.statusPageController.updateStatusPage); + this.router.get("/resolve", this.statusPageController.resolveStatusPageByDomain); this.router.get("/:url", verifyStatusPageAccess, this.statusPageController.getStatusPageByUrl); this.router.delete("/:id", verifyJWT, this.statusPageController.deleteStatusPage); } diff --git a/server/src/service/business/statusPageService.ts b/server/src/service/business/statusPageService.ts index 179461f25..1b77991f3 100644 --- a/server/src/service/business/statusPageService.ts +++ b/server/src/service/business/statusPageService.ts @@ -1,10 +1,13 @@ import { type IStatusPagesRepository } from "@/repositories/index.js"; import { ISettingsService } from "@/service/system/settingsService.js"; import { DEFAULT_STATUS_PAGE_THEME, DEFAULT_STATUS_PAGE_THEME_MODE, StatusPage } from "@/types/index.js"; +import { AppError } from "@/utils/AppError.js"; +import { normalizeStatusPageDomain } from "@/utils/statusPageDomain.js"; export interface IStatusPageService { createStatusPage(userId: string, teamId: string, image: Express.Multer.File | undefined, data: Partial): Promise; getStatusPageByUrl(url: string): Promise; + getStatusPageByCustomDomain(customDomain: string): Promise; getStatusPagesByTeamId(teamId: string): Promise; updateStatusPage(id: string, teamId: string, image: Express.Multer.File | undefined, data: Partial): Promise; @@ -19,6 +22,30 @@ export class StatusPageService implements IStatusPageService { this.settingsService = settingsService; } + private assertCustomDomainAllowed = (customDomain: string | null | undefined) => { + if (!customDomain) { + return; + } + + const clientHost = normalizeStatusPageDomain(this.settingsService.getSettings().clientHost); + if (clientHost && customDomain === clientHost) { + throw new AppError({ + message: "Custom domain cannot match the Checkmate instance host", + status: 400, + }); + } + }; + + private normalizeCustomDomainInput = (data: Partial): Partial => { + if (!("customDomain" in data)) { + return data; + } + + const customDomain = normalizeStatusPageDomain(data.customDomain); + this.assertCustomDomainAllowed(customDomain); + return { ...data, customDomain }; + }; + private withoutThemeFields = (data: Partial): Partial => { const { theme: _theme, themeMode: _themeMode, ...rest } = data; return rest; @@ -42,7 +69,8 @@ export class StatusPageService implements IStatusPageService { image: Express.Multer.File | undefined, data: Partial ): Promise => { - const created = await this.statusPagesRepository.create(userId, teamId, image, this.normalizeInput(data)); + const normalizedData = this.normalizeCustomDomainInput(this.normalizeInput(data)); + const created = await this.statusPagesRepository.create(userId, teamId, image, normalizedData); return this.normalizeTheme(created); }; @@ -51,13 +79,19 @@ export class StatusPageService implements IStatusPageService { return this.normalizeTheme(statusPage); }; + getStatusPageByCustomDomain = async (customDomain: string): Promise => { + const statusPage = await this.statusPagesRepository.findByCustomDomain(customDomain); + return this.normalizeTheme(statusPage); + }; + getStatusPagesByTeamId = async (teamId: string): Promise => { const statusPages = await this.statusPagesRepository.findByTeamId(teamId); return statusPages.map((sp) => this.normalizeTheme(sp)); }; updateStatusPage = async (id: string, teamId: string, image: Express.Multer.File | undefined, data: Partial): Promise => { - const updated = await this.statusPagesRepository.updateById(id, teamId, image, this.normalizeInput(data)); + const normalizedData = this.normalizeCustomDomainInput(this.normalizeInput(data)); + const updated = await this.statusPagesRepository.updateById(id, teamId, image, normalizedData); return this.normalizeTheme(updated); }; diff --git a/server/src/types/statusPage.ts b/server/src/types/statusPage.ts index 0ef4d72a9..db08b7398 100644 --- a/server/src/types/statusPage.ts +++ b/server/src/types/statusPage.ts @@ -26,6 +26,7 @@ export interface StatusPage { type: StatusPageType[]; companyName: string; url: string; + customDomain?: string | null; timezone?: string; color: string; monitors: string[]; diff --git a/server/src/utils/statusPageDomain.ts b/server/src/utils/statusPageDomain.ts new file mode 100644 index 000000000..ec730a806 --- /dev/null +++ b/server/src/utils/statusPageDomain.ts @@ -0,0 +1,29 @@ +export const normalizeStatusPageDomain = (value?: string | null): string | null => { + if (typeof value !== "string") { + return null; + } + + const trimmed = value.trim().toLowerCase(); + if (!trimmed) { + return null; + } + + // Strip scheme, path, and port if a full URL was pasted. + const withoutScheme = trimmed.replace(/^https?:\/\//, ""); + const hostname = withoutScheme.split("/")[0]?.split(":")[0] ?? ""; + + return hostname || null; +}; + +export const resolveStatusPageDomainFromRequest = (hostHeader?: string | null, queryDomain?: string | null): string | null => { + const fromQuery = normalizeStatusPageDomain(queryDomain); + if (fromQuery) { + return fromQuery; + } + + if (!hostHeader) { + return null; + } + + return normalizeStatusPageDomain(hostHeader.split(":")[0]); +}; diff --git a/server/src/validation/statusPageValidation.ts b/server/src/validation/statusPageValidation.ts index 6a42d340c..8d7e8edf9 100644 --- a/server/src/validation/statusPageValidation.ts +++ b/server/src/validation/statusPageValidation.ts @@ -1,6 +1,7 @@ import { z } from "zod"; -import { booleanCoercion } from "./shared.js"; +import { booleanCoercion, dnsHostnameRegex } from "./shared.js"; import { StatusPageTypes, StatusPageThemes, StatusPageThemeModes } from "@/types/statusPage.js"; +import { normalizeStatusPageDomain } from "@/utils/statusPageDomain.js"; //**************************************** // Status Page Validations @@ -15,6 +16,20 @@ export const getStatusPageQueryValidation = z.object({ timeFrame: z.coerce.number().optional(), }); +export const resolveStatusPageQueryValidation = getStatusPageQueryValidation.extend({ + domain: z.string().optional(), +}); + +const customDomainValidation = z.preprocess( + (val) => { + if (val === undefined || val === null) { + return undefined; + } + return normalizeStatusPageDomain(String(val)); + }, + z.union([z.string().regex(dnsHostnameRegex, "Enter a valid domain name (e.g. status.example.com)"), z.null()]).optional() +); + export const createStatusPageBodyValidation = z .object({ type: z.union([z.enum(StatusPageTypes), z.array(z.enum(StatusPageTypes))]).transform((val) => (Array.isArray(val) ? val : [val])), @@ -22,6 +37,7 @@ export const createStatusPageBodyValidation = z url: z.string().regex(/^[a-zA-Z0-9_-]+$/, { message: "URL can only contain letters, numbers, underscores, and hyphens", }), + customDomain: customDomainValidation, timezone: z.string().optional(), color: z.string().optional(), monitors: z.array(z.string().regex(/^[0-9a-fA-F]{24}$/, "Must be a valid monitor ID")).min(1, "At least one monitor is required"), diff --git a/server/test/unit/services/statusPageService.test.ts b/server/test/unit/services/statusPageService.test.ts index 2deac9897..49879cc94 100644 --- a/server/test/unit/services/statusPageService.test.ts +++ b/server/test/unit/services/statusPageService.test.ts @@ -26,20 +26,22 @@ const createRepo = () => ({ create: jest.fn().mockResolvedValue(makeStatusPage()), findByUrl: jest.fn().mockResolvedValue(makeStatusPage()), + findByCustomDomain: jest.fn().mockResolvedValue(makeStatusPage()), findByTeamId: jest.fn().mockResolvedValue([makeStatusPage()]), updateById: jest.fn().mockResolvedValue(makeStatusPage()), deleteById: jest.fn().mockResolvedValue(makeStatusPage()), removeMonitorFromStatusPages: jest.fn().mockResolvedValue(1), }) as unknown as jest.Mocked; -const createSettingsService = (themesEnabled: boolean) => +const createSettingsService = (themesEnabled: boolean, clientHost = "http://localhost:5173") => ({ areStatusPageThemesEnabled: jest.fn().mockReturnValue(themesEnabled), + getSettings: jest.fn().mockReturnValue({ clientHost }), }) as unknown as jest.Mocked; -const createService = (themesEnabled = true) => { +const createService = (themesEnabled = true, clientHost = "http://localhost:5173") => { const repo = createRepo(); - const settingsService = createSettingsService(themesEnabled); + const settingsService = createSettingsService(themesEnabled, clientHost); const service = new StatusPageService(repo, settingsService); return { service, repo, settingsService }; }; @@ -99,6 +101,41 @@ describe("StatusPageService", () => { }); }); + describe("getStatusPageByCustomDomain", () => { + it("delegates to repository", async () => { + const { service, repo } = createService(true); + + const result = await service.getStatusPageByCustomDomain("status.example.com"); + + expect(repo.findByCustomDomain).toHaveBeenCalledWith("status.example.com"); + expect(result).toEqual(makeStatusPage()); + }); + }); + + describe("createStatusPage custom domain validation", () => { + it("rejects custom domains that match the Checkmate instance host", async () => { + const { service } = createService(true, "https://checkmate.example.com"); + + await expect( + service.createStatusPage("user-1", "team-1", undefined, { + customDomain: "checkmate.example.com", + }) + ).rejects.toMatchObject({ + status: 400, + }); + }); + + it("normalizes custom domains before create", async () => { + const { service, repo } = createService(true); + + await service.createStatusPage("user-1", "team-1", undefined, { + customDomain: "https://Status.Example.COM", + }); + + expect(repo.create).toHaveBeenCalledWith("user-1", "team-1", undefined, expect.objectContaining({ customDomain: "status.example.com" })); + }); + }); + describe("getStatusPagesByTeamId", () => { it("delegates to repository", async () => { const { service, repo } = createService(true); diff --git a/server/test/unit/utils/statusPageDomain.test.ts b/server/test/unit/utils/statusPageDomain.test.ts new file mode 100644 index 000000000..37cd03633 --- /dev/null +++ b/server/test/unit/utils/statusPageDomain.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "@jest/globals"; +import { normalizeStatusPageDomain, resolveStatusPageDomainFromRequest } from "../../../src/utils/statusPageDomain.ts"; + +describe("statusPageDomain utils", () => { + describe("normalizeStatusPageDomain", () => { + it("returns null for empty values", () => { + expect(normalizeStatusPageDomain("")).toBeNull(); + expect(normalizeStatusPageDomain(" ")).toBeNull(); + expect(normalizeStatusPageDomain(null)).toBeNull(); + }); + + it("normalizes hostnames", () => { + expect(normalizeStatusPageDomain("Status.Example.COM")).toBe("status.example.com"); + expect(normalizeStatusPageDomain("https://status.example.com/path")).toBe("status.example.com"); + expect(normalizeStatusPageDomain("status.example.com:443")).toBe("status.example.com"); + }); + }); + + describe("resolveStatusPageDomainFromRequest", () => { + it("prefers the query domain over the host header", () => { + expect(resolveStatusPageDomainFromRequest("checkmate.example.com", "status.example.com")).toBe("status.example.com"); + }); + + it("falls back to the host header", () => { + expect(resolveStatusPageDomainFromRequest("status.example.com:8443", undefined)).toBe("status.example.com"); + }); + }); +}); From e390bd764f9106134001ee5f195ff38f3fd5e9af Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Sun, 7 Jun 2026 22:42:58 -0400 Subject: [PATCH 02/17] Format openapi.json for CI format-check. --- server/openapi.json | 24555 +++++++++++++++++++----------------------- 1 file changed, 11111 insertions(+), 13444 deletions(-) diff --git a/server/openapi.json b/server/openapi.json index d1900f322..60d2d3026 100644 --- a/server/openapi.json +++ b/server/openapi.json @@ -1,13446 +1,11113 @@ { - "openapi": "3.0.0", - "info": { - "title": "Checkmate API", - "version": "1.0.0", - "description": "Generated from Zod validation schemas. Source of truth: server/src/validation/*.ts" - }, - "servers": [ - { - "url": "/api/v1", - "description": "Current server" - } - ], - "tags": [ - { - "name": "auth", - "description": "Sign up, log in, password reset, and the authenticated user's profile." - }, - { - "name": "monitors", - "description": "Configure and inspect monitors (HTTP, port, ping, hardware, docker, game, gRPC) for the caller's team." - }, - { - "name": "checks", - "description": "Individual monitoring check results, including history, summaries, and bulk deletes." - }, - { - "name": "geo-checks", - "description": "Geographic check results for monitors with multi-region probing enabled." - }, - { - "name": "incidents", - "description": "Downtime incidents derived from monitor state changes, including filtering and CSV export." - }, - { - "name": "notifications", - "description": "Notification channels (email, webhook, Slack, Discord, PagerDuty, Matrix, Teams, Telegram, Pushover, Twilio) and test alerts." - }, - { - "name": "maintenance-window", - "description": "Scheduled maintenance windows during which monitors do not generate incidents or alerts." - }, - { - "name": "status-page", - "description": "Public status pages, their configuration, and the monitors they expose." - }, - { - "name": "settings", - "description": "Global application settings (admin/superadmin)." - }, - { - "name": "invite", - "description": "Team invitations and accepting them." - }, - { - "name": "queue", - "description": "Background job queue introspection and admin actions (admin/superadmin)." - }, - { - "name": "diagnostic", - "description": "System diagnostics for the running server (admin/superadmin)." - }, - { - "name": "logs", - "description": "Application logs for the running server (admin/superadmin)." - } - ], - "components": { - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT" - } - }, - "schemas": { - "AuthPayload": { - "type": "object", - "properties": { - "user": { - "$ref": "#/components/schemas/User" - }, - "token": { - "type": "string" - } - }, - "required": [ - "user" - ], - "example": { - "user": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": [ - "admin" - ], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - }, - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - }, - "User": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "email": { - "type": "string" - }, - "role": { - "type": "array", - "items": { - "type": "string" - } - }, - "teamId": { - "type": "string" - }, - "profileImage": { - "type": "string", - "nullable": true - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": [ - "_id", - "firstName", - "lastName", - "email", - "role", - "createdAt", - "updatedAt" - ], - "additionalProperties": { - "nullable": true - }, - "example": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": [ - "admin" - ], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - } - }, - "Incident": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789c" - }, - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "teamId": { - "type": "string", - "example": "65f1c2a4d8b9e01234567890" - }, - "startTime": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "endTime": { - "type": "string", - "nullable": true, - "example": "2026-04-15T03:34:00.000Z" - }, - "status": { - "type": "boolean", - "example": true, - "description": "true = resolved, false = ongoing" - }, - "message": { - "type": "string", - "nullable": true, - "example": "HTTP 503 from origin" - }, - "statusCode": { - "type": "number", - "nullable": true, - "example": 503 - }, - "resolutionType": { - "type": "string", - "nullable": true, - "enum": [ - "automatic", - "manual", - null - ], - "example": "automatic" - }, - "resolvedBy": { - "type": "string", - "nullable": true - }, - "resolvedByEmail": { - "type": "string", - "nullable": true - }, - "comment": { - "type": "string", - "nullable": true - }, - "createdAt": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "updatedAt": { - "type": "string", - "example": "2026-04-15T03:34:00.000Z" - } - }, - "required": [ - "id", - "monitorId", - "teamId", - "startTime", - "endTime", - "status", - "resolutionType", - "createdAt", - "updatedAt" - ], - "additionalProperties": { - "nullable": true - } - }, - "IncidentSummary": { - "type": "object", - "properties": { - "total": { - "type": "number", - "example": 12 - }, - "totalActive": { - "type": "number", - "example": 1 - }, - "totalManualResolutions": { - "type": "number", - "example": 2 - }, - "totalAutomaticResolutions": { - "type": "number", - "example": 9 - }, - "avgResolutionTimeHours": { - "type": "number", - "example": 0.5 - }, - "topMonitor": { - "type": "object", - "nullable": true, - "properties": { - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "monitorName": { - "type": "string", - "nullable": true, - "example": "Marketing site" - }, - "incidentCount": { - "type": "number", - "example": 4 - } - }, - "required": [ - "monitorId", - "monitorName", - "incidentCount" - ] - }, - "latestIncidents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IncidentSummaryItem" - } - } - }, - "required": [ - "total", - "totalActive", - "totalManualResolutions", - "totalAutomaticResolutions", - "avgResolutionTimeHours", - "topMonitor", - "latestIncidents" - ] - }, - "IncidentSummaryItem": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789c" - }, - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "monitorName": { - "type": "string", - "nullable": true, - "example": "Marketing site" - }, - "status": { - "type": "boolean", - "example": true - }, - "startTime": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "endTime": { - "type": "string", - "nullable": true, - "example": "2026-04-15T03:34:00.000Z" - }, - "resolutionType": { - "type": "string", - "nullable": true, - "enum": [ - "automatic", - "manual", - null - ], - "example": "automatic" - }, - "message": { - "type": "string", - "nullable": true, - "example": "HTTP 503 from origin" - }, - "statusCode": { - "type": "number", - "nullable": true, - "example": 503 - }, - "createdAt": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - } - }, - "required": [ - "id", - "monitorId", - "monitorName", - "status", - "startTime", - "endTime", - "resolutionType", - "message", - "statusCode", - "createdAt" - ] - }, - "Monitor": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "url": { - "type": "string" - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean" - }, - "interval": { - "type": "number" - }, - "status": { - "type": "string", - "enum": [ - "up", - "down", - "paused", - "initializing", - "maintenance", - "breached" - ] - }, - "statusWindowSize": { - "type": "number" - }, - "statusWindowThreshold": { - "type": "number" - }, - "ignoreTlsErrors": { - "type": "boolean" - }, - "useAdvancedMatching": { - "type": "boolean" - }, - "jsonPath": { - "type": "string" - }, - "expectedValue": { - "type": "string" - }, - "matchMethod": { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "secret": { - "type": "string" - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "gameId": { - "type": "string" - }, - "grpcServiceName": { - "type": "string" - }, - "group": { - "type": "string", - "nullable": true - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - }, - "geoCheckInterval": { - "type": "number" - }, - "dnsServer": { - "type": "string" - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - }, - "teamId": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": [ - "_id", - "name", - "type", - "url", - "isActive", - "interval", - "status", - "statusWindowSize", - "statusWindowThreshold", - "ignoreTlsErrors", - "useAdvancedMatching", - "notifications", - "tags", - "cpuAlertThreshold", - "memoryAlertThreshold", - "diskAlertThreshold", - "tempAlertThreshold", - "selectedDisks", - "geoCheckEnabled", - "geoCheckLocations", - "geoCheckInterval", - "teamId", - "userId", - "createdAt", - "updatedAt" - ], - "additionalProperties": { - "nullable": true - }, - "example": { - "_id": "65f1c2a4d8b9e0123456789a", - "name": "Marketing site", - "description": "Production marketing site monitored from the EU region", - "type": "http", - "url": "https://www.example.com", - "port": 443, - "isActive": true, - "interval": 60000, - "status": "up", - "statusWindowSize": 5, - "statusWindowThreshold": 3, - "ignoreTlsErrors": false, - "useAdvancedMatching": false, - "notifications": [ - "65f1c2a4d8b9e0123456789b" - ], - "cpuAlertThreshold": 90, - "memoryAlertThreshold": 90, - "diskAlertThreshold": 90, - "tempAlertThreshold": 80, - "selectedDisks": [], - "geoCheckEnabled": false, - "geoCheckLocations": [], - "geoCheckInterval": 300000, - "teamId": "65f1c2a4d8b9e01234567890", - "userId": "65f1c2a4d8b9e01234567891", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - } - }, - "NotificationChannelBody": { - "oneOf": [ - { - "$ref": "#/components/schemas/EmailNotification" - }, - { - "$ref": "#/components/schemas/WebhookNotification" - }, - { - "$ref": "#/components/schemas/SlackNotification" - }, - { - "$ref": "#/components/schemas/DiscordNotification" - }, - { - "$ref": "#/components/schemas/PagerDutyNotification" - }, - { - "$ref": "#/components/schemas/MatrixNotification" - }, - { - "$ref": "#/components/schemas/TeamsNotification" - }, - { - "$ref": "#/components/schemas/TelegramNotification" - }, - { - "$ref": "#/components/schemas/PushoverNotification" - }, - { - "$ref": "#/components/schemas/TwilioNotification" - }, - { - "$ref": "#/components/schemas/NtfyNotification" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "email": "#/components/schemas/EmailNotification", - "webhook": "#/components/schemas/WebhookNotification", - "slack": "#/components/schemas/SlackNotification", - "discord": "#/components/schemas/DiscordNotification", - "pager_duty": "#/components/schemas/PagerDutyNotification", - "matrix": "#/components/schemas/MatrixNotification", - "teams": "#/components/schemas/TeamsNotification", - "telegram": "#/components/schemas/TelegramNotification", - "pushover": "#/components/schemas/PushoverNotification", - "twilio": "#/components/schemas/TwilioNotification", - "ntfy": "#/components/schemas/NtfyNotification" - } - } - }, - "EmailNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "email" - ] - }, - "address": { - "type": "string", - "format": "email" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "Ops on-call email", - "type": "email", - "address": "alerts@example.com" - } - }, - "WebhookNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "webhook" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "Custom webhook", - "type": "webhook", - "address": "https://example.com/hooks/checkmate" - } - }, - "SlackNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "slack" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "#alerts", - "type": "slack", - "address": "https://hooks.slack.com/services/T000/B000/XXXX" - } - }, - "DiscordNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "discord" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "#status", - "type": "discord", - "address": "https://discord.com/api/webhooks/123/abc" - } - }, - "PagerDutyNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "pager_duty" - ] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "PagerDuty primary", - "type": "pager_duty", - "address": "R01XXXXXXXXXXXXXXXXXXXXXXX" - } - }, - "MatrixNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "matrix" - ] - }, - "address": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "homeserverUrl": { - "type": "string", - "format": "uri" - }, - "roomId": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "homeserverUrl", - "roomId", - "accessToken" - ], - "example": { - "notificationName": "Matrix room", - "type": "matrix", - "homeserverUrl": "https://matrix.example.com", - "roomId": "!abc123:example.com", - "accessToken": "syt_xxx" - } - }, - "TeamsNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "teams" - ] - }, - "address": { - "type": "string", - "format": "uri" - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "Teams ops channel", - "type": "teams", - "address": "https://outlook.office.com/webhook/..." - } - }, - "TelegramNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "telegram" - ] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "address", - "accessToken" - ], - "example": { - "notificationName": "Telegram bot", - "type": "telegram", - "address": "-1001234567890", - "accessToken": "123456:ABC-DEF" - } - }, - "PushoverNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "pushover" - ] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "address", - "accessToken" - ], - "example": { - "notificationName": "Pushover personal", - "type": "pushover", - "address": "u1234567890abcdef", - "accessToken": "a1234567890abcdef" - } - }, - "TwilioNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "twilio" - ] - }, - "accountSid": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - }, - "phone": { - "type": "string", - "minLength": 1 - }, - "twilioPhoneNumber": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "accountSid", - "accessToken", - "phone", - "twilioPhoneNumber" - ], - "example": { - "notificationName": "Twilio SMS", - "type": "twilio", - "accountSid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - "accessToken": "your-auth-token", - "phone": "+15551234567", - "twilioPhoneNumber": "+15557654321" - } - }, - "NtfyNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "ntfy" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "topic": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "address", - "topic" - ], - "example": { - "notificationName": "ntfy topic", - "type": "ntfy", - "address": "https://ntfy.sh", - "topic": "checkmate-alerts" - } - } - }, - "parameters": {} - }, - "paths": { - "/auth/register": { - "post": { - "tags": [ - "auth" - ], - "summary": "Register a new user (first user becomes superadmin)", - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "profileImage": { - "type": "string", - "format": "binary" - }, - "inviteToken": { - "type": "string", - "default": "" - } - }, - "required": [ - "firstName", - "lastName", - "email", - "password", - "profileImage" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/AuthPayload" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/login": { - "post": { - "tags": [ - "auth" - ], - "summary": "Log in", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "email", - "password" - ] - }, - "example": { - "email": "ada@example.com", - "password": "S3cure!Passw0rd" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/AuthPayload" - } - }, - "required": [ - "success", - "msg", - "data" - ] - }, - "example": { - "success": true, - "msg": "OK", - "data": { - "user": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": [ - "admin" - ], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - }, - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - } - } - } - }, - "401": { - "description": "Invalid credentials", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/recovery/request": { - "post": { - "tags": [ - "auth" - ], - "summary": "Request a password recovery email", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - } - }, - "required": [ - "email" - ] - }, - "example": { - "email": "ada@example.com" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/recovery/validate": { - "post": { - "tags": [ - "auth" - ], - "summary": "Validate a password recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "recoveryToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "recoveryToken" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/recovery/reset": { - "post": { - "tags": [ - "auth" - ], - "summary": "Reset password using a recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "recoveryToken": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "confirm": { - "type": "string" - } - }, - "required": [ - "recoveryToken", - "password" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users/superadmin": { - "get": { - "tags": [ - "auth" - ], - "summary": "Check whether a superadmin user exists", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "superAdminExists": { - "type": "boolean" - } - }, - "required": [ - "superAdminExists" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users": { - "get": { - "tags": [ - "auth" - ], - "summary": "List all users (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "post": { - "tags": [ - "auth" - ], - "summary": "Create a new user (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - }, - "profileImage": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "firstName", - "lastName", - "email", - "password", - "role", - "profileImage" - ] - } - } - } - }, - "responses": { - "201": { - "description": "User created", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users/{userId}": { - "get": { - "tags": [ - "auth" - ], - "summary": "Get a user by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "auth" - ], - "summary": "Edit a user (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - } - }, - "required": [ - "firstName", - "lastName", - "role" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "auth" - ], - "summary": "Delete a user (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users/{userId}/password": { - "patch": { - "tags": [ - "auth" - ], - "summary": "Change a user's password (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - } - }, - "required": [ - "password" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/user": { - "patch": { - "tags": [ - "auth" - ], - "summary": "Edit the currently authenticated user", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "profilePicture": { - "type": "string" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "newPassword": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "profileImage": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "profileImage" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "auth" - ], - "summary": "Delete the currently authenticated user", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/checks/team/summary": { - "get": { - "tags": [ - "checks" - ], - "summary": "Aggregate check summary for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/checks/team": { - "get": { - "tags": [ - "checks" - ], - "summary": "List checks across the team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "all", - "up", - "down", - "resolve" - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "checks" - ], - "summary": "Delete all checks for the team (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "deletedCount": { - "type": "number" - } - }, - "required": [ - "deletedCount" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/checks/{monitorId}": { - "get": { - "tags": [ - "checks" - ], - "summary": "List checks for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "all", - "up", - "down", - "resolve" - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "checks" - ], - "summary": "Delete checks for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/diagnostic/system": { - "get": { - "tags": [ - "diagnostic" - ], - "summary": "Get system diagnostics (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/geo-checks/{monitorId}": { - "get": { - "tags": [ - "geo-checks" - ], - "summary": "Get geo check results for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "all", - "up", - "down", - "resolve" - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/team": { - "get": { - "tags": [ - "incidents" - ], - "summary": "List incidents for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ], - "default": "all" - }, - "required": false, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "integer", - "nullable": true, - "minimum": 0 - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": true, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "monitorId", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "manual", - "automatic" - ] - }, - "required": false, - "name": "resolutionType", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Incident" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/team/summary": { - "get": { - "tags": [ - "incidents" - ], - "summary": "Incident summary for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": false, - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/IncidentSummary" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/{incidentId}": { - "get": { - "tags": [ - "incidents" - ], - "summary": "Get an incident by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "incidentId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "incident": { - "$ref": "#/components/schemas/Incident" - }, - "monitor": { - "nullable": true - }, - "user": { - "nullable": true - } - }, - "required": [ - "incident" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/{incidentId}/resolve": { - "put": { - "tags": [ - "incidents" - ], - "summary": "Manually resolve an incident (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "incidentId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/invite/send": { - "post": { - "tags": [ - "invite" - ], - "summary": "Send an invite email (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - }, - "teamId": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "email", - "role", - "teamId" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/invite/verify": { - "post": { - "tags": [ - "invite" - ], - "summary": "Verify an invite token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "token": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "token" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "role": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "email" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/invite": { - "post": { - "tags": [ - "invite" - ], - "summary": "Create an invite token (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - }, - "teamId": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "email", - "role", - "teamId" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "token": { - "type": "string" - } - }, - "required": [ - "token" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/logs": { - "get": { - "tags": [ - "logs" - ], - "summary": "Get application logs (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window": { - "post": { - "tags": [ - "maintenance-window" - ], - "summary": "Create one or more maintenance windows", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "name": { - "type": "string", - "minLength": 1 - }, - "active": { - "type": "boolean" - }, - "duration": { - "type": "number", - "minimum": 1 - }, - "durationUnit": { - "type": "string", - "enum": [ - "seconds", - "minutes", - "hours", - "days" - ] - }, - "start": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "end": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "repeat": { - "type": "number", - "minimum": 0 - }, - "expiry": { - "type": "string", - "nullable": true, - "format": "date-time" - } - }, - "required": [ - "monitors", - "name", - "duration", - "durationUnit", - "start", - "end", - "repeat" - ], - "additionalProperties": false - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window/team": { - "get": { - "tags": [ - "maintenance-window" - ], - "summary": "List maintenance windows for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "active", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": false, - "name": "order", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window/monitor/{monitorId}": { - "get": { - "tags": [ - "maintenance-window" - ], - "summary": "List maintenance windows for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window/{id}": { - "get": { - "tags": [ - "maintenance-window" - ], - "summary": "Get a maintenance window by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "maintenance-window" - ], - "summary": "Edit a maintenance window", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "active": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "repeat": { - "type": "number" - }, - "start": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "end": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "expiry": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "monitors": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "duration": { - "type": "number" - }, - "durationUnit": { - "type": "string", - "enum": [ - "seconds", - "minutes", - "hours", - "days" - ] - } - }, - "additionalProperties": false - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "maintenance-window" - ], - "summary": "Delete a maintenance window", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/team": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List monitors for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - } - } - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "required": false, - "name": "tags", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Monitor" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/team/with-checks": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List team monitors with their most recent checks (paginated)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "integer", - "nullable": true, - "minimum": 0 - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": false, - "name": "order", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - } - } - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "required": false, - "name": "tags", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "explain", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/team/groups": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List monitor groups for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/uptime/details/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get uptime details for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "normalize", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/hardware/details/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get hardware metrics detail for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/pagespeed/details/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get PageSpeed detail for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/{monitorId}/geo-checks": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get geo check results for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": false, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "numToDisplay", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "normalize", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/pause/{monitorId}": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Toggle pause state for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/certificate/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get SSL certificate info for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "certificateDate": { - "type": "string" - } - }, - "required": [ - "certificateDate" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/notifications": { - "patch": { - "tags": [ - "monitors" - ], - "summary": "Bulk update notifications across monitors", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitorIds": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "maxItems": 100 - }, - "notificationIds": { - "type": "array", - "items": { - "type": "string" - }, - "maxItems": 100 - }, - "action": { - "type": "string", - "enum": [ - "add", - "remove", - "set" - ] - } - }, - "required": [ - "monitorIds", - "notificationIds", - "action" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "modifiedCount": { - "type": "number" - } - }, - "required": [ - "modifiedCount" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Create a new monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "url": { - "type": "string", - "minLength": 1 - }, - "ignoreTlsErrors": { - "type": "boolean", - "default": false - }, - "useAdvancedMatching": { - "type": "boolean", - "default": false - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean" - }, - "interval": { - "type": "number" - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "secret": { - "type": "string" - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ], - "default": "" - }, - "strategy": { - "type": "string", - "enum": [ - "desktop", - "mobile" - ] - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - } - }, - "required": [ - "name", - "type", - "url" - ] - } - } - } - }, - "responses": { - "200": { - "description": "Monitor created", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "monitors" - ], - "summary": "Delete every monitor (superadmin only)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/demo": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Insert preconfigured demo monitors", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "number" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/export/json": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Export all team monitors as JSON", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/import/json": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Import monitors from JSON", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "status": { - "type": "string", - "enum": [ - "up", - "down", - "paused", - "initializing", - "maintenance", - "breached" - ], - "default": "initializing" - }, - "statusWindow": { - "type": "array", - "items": { - "type": "boolean" - }, - "default": [] - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "ignoreTlsErrors": { - "type": "boolean", - "default": false - }, - "useAdvancedMatching": { - "type": "boolean", - "default": false - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "url": { - "type": "string", - "minLength": 1 - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean", - "default": true - }, - "interval": { - "type": "number", - "default": 60000 - }, - "uptimePercentage": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "secret": { - "type": "string" - }, - "cpuAlertThreshold": { - "type": "number", - "default": 100 - }, - "cpuAlertCounter": { - "type": "number", - "default": 5 - }, - "memoryAlertThreshold": { - "type": "number", - "default": 100 - }, - "memoryAlertCounter": { - "type": "number", - "default": 5 - }, - "diskAlertThreshold": { - "type": "number", - "default": 100 - }, - "diskAlertCounter": { - "type": "number", - "default": 5 - }, - "tempAlertThreshold": { - "type": "number", - "default": 100 - }, - "tempAlertCounter": { - "type": "number", - "default": 5 - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ], - "default": "" - }, - "strategy": { - "type": "string", - "enum": [ - "desktop", - "mobile" - ] - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - } - ], - "default": null - }, - "geoCheckEnabled": { - "type": "boolean", - "default": false - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - "default": [] - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000, - "default": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": [ - "name", - "type", - "url" - ] - }, - "minItems": 1 - } - }, - "required": [ - "monitors" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "imported": { - "type": "number" - } - }, - "required": [ - "imported" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/games": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List supported game-server types", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get a monitor by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "monitors" - ], - "summary": "Edit a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "url": { - "type": "string" - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "interval": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "secret": { - "type": "string" - }, - "ignoreTlsErrors": { - "type": "boolean" - }, - "useAdvancedMatching": { - "type": "boolean" - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "port": { - "type": "number", - "minimum": 1, - "maximum": 65535 - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "strategy": { - "type": "string", - "enum": [ - "desktop", - "mobile" - ] - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "monitors" - ], - "summary": "Delete a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications": { - "post": { - "tags": [ - "notifications" - ], - "summary": "Create a notification channel", - "description": "Create a notification channel of any supported type. The `type` field discriminates the body shape.", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/test/all": { - "post": { - "tags": [ - "notifications" - ], - "summary": "Send a test alert through every notification channel for the team", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitorId": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "monitorId" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/test": { - "post": { - "tags": [ - "notifications" - ], - "summary": "Send a test alert through a single notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - }, - "required": [ - "success" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/team": { - "get": { - "tags": [ - "notifications" - ], - "summary": "List notification channels for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/{id}": { - "get": { - "tags": [ - "notifications" - ], - "summary": "Get a notification channel by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "notifications" - ], - "summary": "Delete a notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "notifications" - ], - "summary": "Edit a notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/jobs": { - "get": { - "tags": [ - "queue" - ], - "summary": "List queued monitor jobs (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/metrics": { - "get": { - "tags": [ - "queue" - ], - "summary": "Get queue runtime metrics (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/all-metrics": { - "get": { - "tags": [ - "queue" - ], - "summary": "Get queue metrics across all monitors (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/flush": { - "post": { - "tags": [ - "queue" - ], - "summary": "Flush the monitor job queue (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/settings": { - "get": { - "tags": [ - "settings" - ], - "summary": "Get application settings", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "settings" - ], - "summary": "Update application settings (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "checkTTL": { - "type": "integer", - "minimum": 1, - "maximum": 366 - }, - "systemEmailPort": { - "type": "number", - "nullable": true - }, - "pagespeedApiKey": { - "type": "string", - "nullable": true - }, - "language": { - "type": "string" - }, - "timezone": { - "type": "string" - }, - "systemEmailHost": { - "type": "string", - "nullable": true - }, - "systemEmailAddress": { - "type": "string", - "nullable": true - }, - "systemEmailDisplayName": { - "type": "string", - "nullable": true, - "maxLength": 100 - }, - "systemEmailPassword": { - "type": "string", - "nullable": true - }, - "systemEmailUser": { - "type": "string", - "nullable": true - }, - "systemEmailConnectionHost": { - "type": "string", - "nullable": true - }, - "systemEmailTLSServername": { - "type": "string", - "nullable": true - }, - "showURL": { - "type": "boolean" - }, - "systemEmailSecure": { - "type": "boolean" - }, - "systemEmailPool": { - "type": "boolean" - }, - "systemEmailIgnoreTLS": { - "type": "boolean" - }, - "systemEmailRequireTLS": { - "type": "boolean" - }, - "systemEmailRejectUnauthorized": { - "type": "boolean" - }, - "globalThresholds": { - "type": "object", - "properties": { - "cpu": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "memory": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "disk": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "temperature": { - "type": "number", - "minimum": 1, - "maximum": 150 - } - } - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/settings/test-email": { - "post": { - "tags": [ - "settings" - ], - "summary": "Send a test email using current SMTP settings (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "to": { - "type": "string", - "minLength": 1 - }, - "systemEmailHost": { - "type": "string" - }, - "systemEmailPort": { - "type": "number" - }, - "systemEmailSecure": { - "type": "boolean" - }, - "systemEmailPool": { - "type": "boolean" - }, - "systemEmailAddress": { - "type": "string" - }, - "systemEmailDisplayName": { - "type": "string" - }, - "systemEmailPassword": { - "type": "string" - }, - "systemEmailUser": { - "type": "string" - }, - "systemEmailConnectionHost": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "systemEmailIgnoreTLS": { - "type": "boolean" - }, - "systemEmailRequireTLS": { - "type": "boolean" - }, - "systemEmailRejectUnauthorized": { - "type": "boolean" - }, - "systemEmailTLSServername": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "to" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/team": { - "get": { - "tags": [ - "status-page" - ], - "summary": "List status pages for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page": { - "post": { - "tags": [ - "status-page" - ], - "summary": "Create a status page (logo upload optional)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "type": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "companyName": { - "type": "string", - "minLength": 1 - }, - "url": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$" - }, - "customDomain": { - "anyOf": [ - { - "type": "string", - "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" - }, - { - "nullable": true - } - ] - }, - "timezone": { - "type": "string" - }, - "color": { - "type": "string" - }, - "monitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - }, - "minItems": 1 - }, - "subMonitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - } - }, - "deleteSubmonitors": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "showCharts": { - "type": "boolean" - }, - "showUptimePercentage": { - "type": "boolean" - }, - "showAdminLoginLink": { - "type": "boolean" - }, - "showInfrastructure": { - "type": "boolean" - }, - "removeLogo": { - "anyOf": [ - { - "type": "string", - "enum": [ - "true" - ] - }, - { - "type": "string", - "enum": [ - "false" - ] - } - ] - }, - "theme": { - "type": "string", - "enum": [ - "refined", - "modern", - "bold", - "editorial" - ] - }, - "themeMode": { - "type": "string", - "enum": [ - "auto", - "light", - "dark" - ] - }, - "logo": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "type", - "companyName", - "url", - "monitors", - "isPublished", - "showUptimePercentage", - "logo" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/{id}": { - "put": { - "tags": [ - "status-page" - ], - "summary": "Update a status page (logo upload optional)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "type": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "companyName": { - "type": "string", - "minLength": 1 - }, - "url": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$" - }, - "customDomain": { - "anyOf": [ - { - "type": "string", - "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" - }, - { - "nullable": true - } - ] - }, - "timezone": { - "type": "string" - }, - "color": { - "type": "string" - }, - "monitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - }, - "minItems": 1 - }, - "subMonitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - } - }, - "deleteSubmonitors": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "showCharts": { - "type": "boolean" - }, - "showUptimePercentage": { - "type": "boolean" - }, - "showAdminLoginLink": { - "type": "boolean" - }, - "showInfrastructure": { - "type": "boolean" - }, - "removeLogo": { - "anyOf": [ - { - "type": "string", - "enum": [ - "true" - ] - }, - { - "type": "string", - "enum": [ - "false" - ] - } - ] - }, - "theme": { - "type": "string", - "enum": [ - "refined", - "modern", - "bold", - "editorial" - ] - }, - "themeMode": { - "type": "string", - "enum": [ - "auto", - "light", - "dark" - ] - }, - "logo": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "type", - "companyName", - "url", - "monitors", - "isPublished", - "showUptimePercentage", - "logo" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "status-page" - ], - "summary": "Delete a status page", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/resolve": { - "get": { - "tags": [ - "status-page" - ], - "summary": "Resolve a published status page by custom domain", - "parameters": [ - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "required": true, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "timeFrame", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "domain", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "404": { - "description": "Status page not found" - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/{url}": { - "get": { - "tags": [ - "status-page" - ], - "summary": "Get a public status page by its URL slug", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "url", - "in": "path" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "required": true, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "timeFrame", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "404": { - "description": "Status page not found" - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - } - } + "openapi": "3.0.0", + "info": { + "title": "Checkmate API", + "version": "1.0.0", + "description": "Generated from Zod validation schemas. Source of truth: server/src/validation/*.ts" + }, + "servers": [ + { + "url": "/api/v1", + "description": "Current server" + } + ], + "tags": [ + { + "name": "auth", + "description": "Sign up, log in, password reset, and the authenticated user's profile." + }, + { + "name": "monitors", + "description": "Configure and inspect monitors (HTTP, port, ping, hardware, docker, game, gRPC) for the caller's team." + }, + { + "name": "checks", + "description": "Individual monitoring check results, including history, summaries, and bulk deletes." + }, + { + "name": "geo-checks", + "description": "Geographic check results for monitors with multi-region probing enabled." + }, + { + "name": "incidents", + "description": "Downtime incidents derived from monitor state changes, including filtering and CSV export." + }, + { + "name": "notifications", + "description": "Notification channels (email, webhook, Slack, Discord, PagerDuty, Matrix, Teams, Telegram, Pushover, Twilio) and test alerts." + }, + { + "name": "maintenance-window", + "description": "Scheduled maintenance windows during which monitors do not generate incidents or alerts." + }, + { + "name": "status-page", + "description": "Public status pages, their configuration, and the monitors they expose." + }, + { + "name": "settings", + "description": "Global application settings (admin/superadmin)." + }, + { + "name": "invite", + "description": "Team invitations and accepting them." + }, + { + "name": "queue", + "description": "Background job queue introspection and admin actions (admin/superadmin)." + }, + { + "name": "diagnostic", + "description": "System diagnostics for the running server (admin/superadmin)." + }, + { + "name": "logs", + "description": "Application logs for the running server (admin/superadmin)." + } + ], + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "AuthPayload": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/User" + }, + "token": { + "type": "string" + } + }, + "required": ["user"], + "example": { + "user": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": ["admin"], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + }, + "User": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "role": { + "type": "array", + "items": { + "type": "string" + } + }, + "teamId": { + "type": "string" + }, + "profileImage": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": ["_id", "firstName", "lastName", "email", "role", "createdAt", "updatedAt"], + "additionalProperties": { + "nullable": true + }, + "example": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": ["admin"], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + } + }, + "Incident": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789c" + }, + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "teamId": { + "type": "string", + "example": "65f1c2a4d8b9e01234567890" + }, + "startTime": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "endTime": { + "type": "string", + "nullable": true, + "example": "2026-04-15T03:34:00.000Z" + }, + "status": { + "type": "boolean", + "example": true, + "description": "true = resolved, false = ongoing" + }, + "message": { + "type": "string", + "nullable": true, + "example": "HTTP 503 from origin" + }, + "statusCode": { + "type": "number", + "nullable": true, + "example": 503 + }, + "resolutionType": { + "type": "string", + "nullable": true, + "enum": ["automatic", "manual", null], + "example": "automatic" + }, + "resolvedBy": { + "type": "string", + "nullable": true + }, + "resolvedByEmail": { + "type": "string", + "nullable": true + }, + "comment": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "updatedAt": { + "type": "string", + "example": "2026-04-15T03:34:00.000Z" + } + }, + "required": ["id", "monitorId", "teamId", "startTime", "endTime", "status", "resolutionType", "createdAt", "updatedAt"], + "additionalProperties": { + "nullable": true + } + }, + "IncidentSummary": { + "type": "object", + "properties": { + "total": { + "type": "number", + "example": 12 + }, + "totalActive": { + "type": "number", + "example": 1 + }, + "totalManualResolutions": { + "type": "number", + "example": 2 + }, + "totalAutomaticResolutions": { + "type": "number", + "example": 9 + }, + "avgResolutionTimeHours": { + "type": "number", + "example": 0.5 + }, + "topMonitor": { + "type": "object", + "nullable": true, + "properties": { + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "monitorName": { + "type": "string", + "nullable": true, + "example": "Marketing site" + }, + "incidentCount": { + "type": "number", + "example": 4 + } + }, + "required": ["monitorId", "monitorName", "incidentCount"] + }, + "latestIncidents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IncidentSummaryItem" + } + } + }, + "required": [ + "total", + "totalActive", + "totalManualResolutions", + "totalAutomaticResolutions", + "avgResolutionTimeHours", + "topMonitor", + "latestIncidents" + ] + }, + "IncidentSummaryItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789c" + }, + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "monitorName": { + "type": "string", + "nullable": true, + "example": "Marketing site" + }, + "status": { + "type": "boolean", + "example": true + }, + "startTime": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "endTime": { + "type": "string", + "nullable": true, + "example": "2026-04-15T03:34:00.000Z" + }, + "resolutionType": { + "type": "string", + "nullable": true, + "enum": ["automatic", "manual", null], + "example": "automatic" + }, + "message": { + "type": "string", + "nullable": true, + "example": "HTTP 503 from origin" + }, + "statusCode": { + "type": "number", + "nullable": true, + "example": 503 + }, + "createdAt": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + } + }, + "required": ["id", "monitorId", "monitorName", "status", "startTime", "endTime", "resolutionType", "message", "statusCode", "createdAt"] + }, + "Monitor": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "url": { + "type": "string" + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "number" + }, + "status": { + "type": "string", + "enum": ["up", "down", "paused", "initializing", "maintenance", "breached"] + }, + "statusWindowSize": { + "type": "number" + }, + "statusWindowThreshold": { + "type": "number" + }, + "ignoreTlsErrors": { + "type": "boolean" + }, + "useAdvancedMatching": { + "type": "boolean" + }, + "jsonPath": { + "type": "string" + }, + "expectedValue": { + "type": "string" + }, + "matchMethod": { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "secret": { + "type": "string" + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "gameId": { + "type": "string" + }, + "grpcServiceName": { + "type": "string" + }, + "group": { + "type": "string", + "nullable": true + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + }, + "geoCheckInterval": { + "type": "number" + }, + "dnsServer": { + "type": "string" + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + }, + "teamId": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "_id", + "name", + "type", + "url", + "isActive", + "interval", + "status", + "statusWindowSize", + "statusWindowThreshold", + "ignoreTlsErrors", + "useAdvancedMatching", + "notifications", + "tags", + "cpuAlertThreshold", + "memoryAlertThreshold", + "diskAlertThreshold", + "tempAlertThreshold", + "selectedDisks", + "geoCheckEnabled", + "geoCheckLocations", + "geoCheckInterval", + "teamId", + "userId", + "createdAt", + "updatedAt" + ], + "additionalProperties": { + "nullable": true + }, + "example": { + "_id": "65f1c2a4d8b9e0123456789a", + "name": "Marketing site", + "description": "Production marketing site monitored from the EU region", + "type": "http", + "url": "https://www.example.com", + "port": 443, + "isActive": true, + "interval": 60000, + "status": "up", + "statusWindowSize": 5, + "statusWindowThreshold": 3, + "ignoreTlsErrors": false, + "useAdvancedMatching": false, + "notifications": ["65f1c2a4d8b9e0123456789b"], + "cpuAlertThreshold": 90, + "memoryAlertThreshold": 90, + "diskAlertThreshold": 90, + "tempAlertThreshold": 80, + "selectedDisks": [], + "geoCheckEnabled": false, + "geoCheckLocations": [], + "geoCheckInterval": 300000, + "teamId": "65f1c2a4d8b9e01234567890", + "userId": "65f1c2a4d8b9e01234567891", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + } + }, + "NotificationChannelBody": { + "oneOf": [ + { + "$ref": "#/components/schemas/EmailNotification" + }, + { + "$ref": "#/components/schemas/WebhookNotification" + }, + { + "$ref": "#/components/schemas/SlackNotification" + }, + { + "$ref": "#/components/schemas/DiscordNotification" + }, + { + "$ref": "#/components/schemas/PagerDutyNotification" + }, + { + "$ref": "#/components/schemas/MatrixNotification" + }, + { + "$ref": "#/components/schemas/TeamsNotification" + }, + { + "$ref": "#/components/schemas/TelegramNotification" + }, + { + "$ref": "#/components/schemas/PushoverNotification" + }, + { + "$ref": "#/components/schemas/TwilioNotification" + }, + { + "$ref": "#/components/schemas/NtfyNotification" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "email": "#/components/schemas/EmailNotification", + "webhook": "#/components/schemas/WebhookNotification", + "slack": "#/components/schemas/SlackNotification", + "discord": "#/components/schemas/DiscordNotification", + "pager_duty": "#/components/schemas/PagerDutyNotification", + "matrix": "#/components/schemas/MatrixNotification", + "teams": "#/components/schemas/TeamsNotification", + "telegram": "#/components/schemas/TelegramNotification", + "pushover": "#/components/schemas/PushoverNotification", + "twilio": "#/components/schemas/TwilioNotification", + "ntfy": "#/components/schemas/NtfyNotification" + } + } + }, + "EmailNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["email"] + }, + "address": { + "type": "string", + "format": "email" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "Ops on-call email", + "type": "email", + "address": "alerts@example.com" + } + }, + "WebhookNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["webhook"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "Custom webhook", + "type": "webhook", + "address": "https://example.com/hooks/checkmate" + } + }, + "SlackNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["slack"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "#alerts", + "type": "slack", + "address": "https://hooks.slack.com/services/T000/B000/XXXX" + } + }, + "DiscordNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["discord"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "#status", + "type": "discord", + "address": "https://discord.com/api/webhooks/123/abc" + } + }, + "PagerDutyNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["pager_duty"] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "PagerDuty primary", + "type": "pager_duty", + "address": "R01XXXXXXXXXXXXXXXXXXXXXXX" + } + }, + "MatrixNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["matrix"] + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "homeserverUrl": { + "type": "string", + "format": "uri" + }, + "roomId": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "homeserverUrl", "roomId", "accessToken"], + "example": { + "notificationName": "Matrix room", + "type": "matrix", + "homeserverUrl": "https://matrix.example.com", + "roomId": "!abc123:example.com", + "accessToken": "syt_xxx" + } + }, + "TeamsNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["teams"] + }, + "address": { + "type": "string", + "format": "uri" + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "Teams ops channel", + "type": "teams", + "address": "https://outlook.office.com/webhook/..." + } + }, + "TelegramNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["telegram"] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "address", "accessToken"], + "example": { + "notificationName": "Telegram bot", + "type": "telegram", + "address": "-1001234567890", + "accessToken": "123456:ABC-DEF" + } + }, + "PushoverNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["pushover"] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "address", "accessToken"], + "example": { + "notificationName": "Pushover personal", + "type": "pushover", + "address": "u1234567890abcdef", + "accessToken": "a1234567890abcdef" + } + }, + "TwilioNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["twilio"] + }, + "accountSid": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + }, + "phone": { + "type": "string", + "minLength": 1 + }, + "twilioPhoneNumber": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "accountSid", "accessToken", "phone", "twilioPhoneNumber"], + "example": { + "notificationName": "Twilio SMS", + "type": "twilio", + "accountSid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "accessToken": "your-auth-token", + "phone": "+15551234567", + "twilioPhoneNumber": "+15557654321" + } + }, + "NtfyNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["ntfy"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "topic": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "address", "topic"], + "example": { + "notificationName": "ntfy topic", + "type": "ntfy", + "address": "https://ntfy.sh", + "topic": "checkmate-alerts" + } + } + }, + "parameters": {} + }, + "paths": { + "/auth/register": { + "post": { + "tags": ["auth"], + "summary": "Register a new user (first user becomes superadmin)", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "profileImage": { + "type": "string", + "format": "binary" + }, + "inviteToken": { + "type": "string", + "default": "" + } + }, + "required": ["firstName", "lastName", "email", "password", "profileImage"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/AuthPayload" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/login": { + "post": { + "tags": ["auth"], + "summary": "Log in", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 1 + } + }, + "required": ["email", "password"] + }, + "example": { + "email": "ada@example.com", + "password": "S3cure!Passw0rd" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/AuthPayload" + } + }, + "required": ["success", "msg", "data"] + }, + "example": { + "success": true, + "msg": "OK", + "data": { + "user": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": ["admin"], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + } + } + }, + "401": { + "description": "Invalid credentials", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/recovery/request": { + "post": { + "tags": ["auth"], + "summary": "Request a password recovery email", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["email"] + }, + "example": { + "email": "ada@example.com" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/recovery/validate": { + "post": { + "tags": ["auth"], + "summary": "Validate a password recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recoveryToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["recoveryToken"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/recovery/reset": { + "post": { + "tags": ["auth"], + "summary": "Reset password using a recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recoveryToken": { + "type": "string", + "minLength": 1 + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "confirm": { + "type": "string" + } + }, + "required": ["recoveryToken", "password"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users/superadmin": { + "get": { + "tags": ["auth"], + "summary": "Check whether a superadmin user exists", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "superAdminExists": { + "type": "boolean" + } + }, + "required": ["superAdminExists"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users": { + "get": { + "tags": ["auth"], + "summary": "List all users (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "post": { + "tags": ["auth"], + "summary": "Create a new user (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + }, + "profileImage": { + "type": "string", + "format": "binary" + } + }, + "required": ["firstName", "lastName", "email", "password", "role", "profileImage"] + } + } + } + }, + "responses": { + "201": { + "description": "User created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users/{userId}": { + "get": { + "tags": ["auth"], + "summary": "Get a user by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "404": { + "description": "User not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["auth"], + "summary": "Edit a user (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + } + }, + "required": ["firstName", "lastName", "role"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["auth"], + "summary": "Delete a user (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users/{userId}/password": { + "patch": { + "tags": ["auth"], + "summary": "Change a user's password (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + } + }, + "required": ["password"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/user": { + "patch": { + "tags": ["auth"], + "summary": "Edit the currently authenticated user", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "profilePicture": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "newPassword": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "profileImage": { + "type": "string", + "format": "binary" + } + }, + "required": ["profileImage"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["auth"], + "summary": "Delete the currently authenticated user", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/checks/team/summary": { + "get": { + "tags": ["checks"], + "summary": "Aggregate check summary for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/checks/team": { + "get": { + "tags": ["checks"], + "summary": "List checks across the team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["all", "up", "down", "resolve"] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["checks"], + "summary": "Delete all checks for the team (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "deletedCount": { + "type": "number" + } + }, + "required": ["deletedCount"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/checks/{monitorId}": { + "get": { + "tags": ["checks"], + "summary": "List checks for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["all", "up", "down", "resolve"] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["checks"], + "summary": "Delete checks for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/diagnostic/system": { + "get": { + "tags": ["diagnostic"], + "summary": "Get system diagnostics (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/geo-checks/{monitorId}": { + "get": { + "tags": ["geo-checks"], + "summary": "Get geo check results for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["all", "up", "down", "resolve"] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/team": { + "get": { + "tags": ["incidents"], + "summary": "List incidents for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"], + "default": "all" + }, + "required": false, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "monitorId", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["manual", "automatic"] + }, + "required": false, + "name": "resolutionType", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Incident" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/team/summary": { + "get": { + "tags": ["incidents"], + "summary": "Incident summary for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": false, + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/IncidentSummary" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/{incidentId}": { + "get": { + "tags": ["incidents"], + "summary": "Get an incident by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "incidentId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "incident": { + "$ref": "#/components/schemas/Incident" + }, + "monitor": { + "nullable": true + }, + "user": { + "nullable": true + } + }, + "required": ["incident"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/{incidentId}/resolve": { + "put": { + "tags": ["incidents"], + "summary": "Manually resolve an incident (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "incidentId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/invite/send": { + "post": { + "tags": ["invite"], + "summary": "Send an invite email (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + }, + "teamId": { + "type": "string", + "minLength": 1 + } + }, + "required": ["email", "role", "teamId"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/invite/verify": { + "post": { + "tags": ["invite"], + "summary": "Verify an invite token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "token": { + "type": "string", + "minLength": 1 + } + }, + "required": ["token"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "role": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["email"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/invite": { + "post": { + "tags": ["invite"], + "summary": "Create an invite token (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + }, + "teamId": { + "type": "string", + "minLength": 1 + } + }, + "required": ["email", "role", "teamId"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + }, + "required": ["token"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/logs": { + "get": { + "tags": ["logs"], + "summary": "Get application logs (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window": { + "post": { + "tags": ["maintenance-window"], + "summary": "Create one or more maintenance windows", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitors": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "active": { + "type": "boolean" + }, + "duration": { + "type": "number", + "minimum": 1 + }, + "durationUnit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days"] + }, + "start": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "end": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "repeat": { + "type": "number", + "minimum": 0 + }, + "expiry": { + "type": "string", + "nullable": true, + "format": "date-time" + } + }, + "required": ["monitors", "name", "duration", "durationUnit", "start", "end", "repeat"], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window/team": { + "get": { + "tags": ["maintenance-window"], + "summary": "List maintenance windows for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "active", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "field", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": false, + "name": "order", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window/monitor/{monitorId}": { + "get": { + "tags": ["maintenance-window"], + "summary": "List maintenance windows for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window/{id}": { + "get": { + "tags": ["maintenance-window"], + "summary": "Get a maintenance window by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["maintenance-window"], + "summary": "Edit a maintenance window", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "repeat": { + "type": "number" + }, + "start": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "end": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "expiry": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "monitors": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "duration": { + "type": "number" + }, + "durationUnit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days"] + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["maintenance-window"], + "summary": "Delete a maintenance window", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/team": { + "get": { + "tags": ["monitors"], + "summary": "List monitors for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + } + } + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "required": false, + "name": "tags", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Monitor" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/team/with-checks": { + "get": { + "tags": ["monitors"], + "summary": "List team monitors with their most recent checks (paginated)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "field", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": false, + "name": "order", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + } + } + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "required": false, + "name": "tags", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "explain", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/team/groups": { + "get": { + "tags": ["monitors"], + "summary": "List monitor groups for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/uptime/details/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get uptime details for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "normalize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/hardware/details/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get hardware metrics detail for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/pagespeed/details/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get PageSpeed detail for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/{monitorId}/geo-checks": { + "get": { + "tags": ["monitors"], + "summary": "Get geo check results for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": false, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "numToDisplay", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "normalize", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/pause/{monitorId}": { + "post": { + "tags": ["monitors"], + "summary": "Toggle pause state for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/certificate/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get SSL certificate info for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "certificateDate": { + "type": "string" + } + }, + "required": ["certificateDate"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/notifications": { + "patch": { + "tags": ["monitors"], + "summary": "Bulk update notifications across monitors", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitorIds": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 100 + }, + "notificationIds": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 100 + }, + "action": { + "type": "string", + "enum": ["add", "remove", "set"] + } + }, + "required": ["monitorIds", "notificationIds", "action"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "modifiedCount": { + "type": "number" + } + }, + "required": ["modifiedCount"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors": { + "post": { + "tags": ["monitors"], + "summary": "Create a new monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "url": { + "type": "string", + "minLength": 1 + }, + "ignoreTlsErrors": { + "type": "boolean", + "default": false + }, + "useAdvancedMatching": { + "type": "boolean", + "default": false + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "number" + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "secret": { + "type": "string" + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ], + "default": "" + }, + "strategy": { + "type": "string", + "enum": ["desktop", "mobile"] + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + } + }, + "required": ["name", "type", "url"] + } + } + } + }, + "responses": { + "200": { + "description": "Monitor created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["monitors"], + "summary": "Delete every monitor (superadmin only)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/demo": { + "post": { + "tags": ["monitors"], + "summary": "Insert preconfigured demo monitors", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "number" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/export/json": { + "get": { + "tags": ["monitors"], + "summary": "Export all team monitors as JSON", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/import/json": { + "post": { + "tags": ["monitors"], + "summary": "Import monitors from JSON", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "status": { + "type": "string", + "enum": ["up", "down", "paused", "initializing", "maintenance", "breached"], + "default": "initializing" + }, + "statusWindow": { + "type": "array", + "items": { + "type": "boolean" + }, + "default": [] + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "ignoreTlsErrors": { + "type": "boolean", + "default": false + }, + "useAdvancedMatching": { + "type": "boolean", + "default": false + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "url": { + "type": "string", + "minLength": 1 + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean", + "default": true + }, + "interval": { + "type": "number", + "default": 60000 + }, + "uptimePercentage": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "secret": { + "type": "string" + }, + "cpuAlertThreshold": { + "type": "number", + "default": 100 + }, + "cpuAlertCounter": { + "type": "number", + "default": 5 + }, + "memoryAlertThreshold": { + "type": "number", + "default": 100 + }, + "memoryAlertCounter": { + "type": "number", + "default": 5 + }, + "diskAlertThreshold": { + "type": "number", + "default": 100 + }, + "diskAlertCounter": { + "type": "number", + "default": 5 + }, + "tempAlertThreshold": { + "type": "number", + "default": 100 + }, + "tempAlertCounter": { + "type": "number", + "default": 5 + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ], + "default": "" + }, + "strategy": { + "type": "string", + "enum": ["desktop", "mobile"] + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + } + ], + "default": null + }, + "geoCheckEnabled": { + "type": "boolean", + "default": false + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + "default": [] + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000, + "default": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": ["name", "type", "url"] + }, + "minItems": 1 + } + }, + "required": ["monitors"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "imported": { + "type": "number" + } + }, + "required": ["imported"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/games": { + "get": { + "tags": ["monitors"], + "summary": "List supported game-server types", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get a monitor by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["monitors"], + "summary": "Edit a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "url": { + "type": "string" + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "interval": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "secret": { + "type": "string" + }, + "ignoreTlsErrors": { + "type": "boolean" + }, + "useAdvancedMatching": { + "type": "boolean" + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "port": { + "type": "number", + "minimum": 1, + "maximum": 65535 + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "strategy": { + "type": "string", + "enum": ["desktop", "mobile"] + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["monitors"], + "summary": "Delete a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications": { + "post": { + "tags": ["notifications"], + "summary": "Create a notification channel", + "description": "Create a notification channel of any supported type. The `type` field discriminates the body shape.", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/test/all": { + "post": { + "tags": ["notifications"], + "summary": "Send a test alert through every notification channel for the team", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitorId": { + "type": "string", + "minLength": 1 + } + }, + "required": ["monitorId"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/test": { + "post": { + "tags": ["notifications"], + "summary": "Send a test alert through a single notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": ["success"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/team": { + "get": { + "tags": ["notifications"], + "summary": "List notification channels for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/{id}": { + "get": { + "tags": ["notifications"], + "summary": "Get a notification channel by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["notifications"], + "summary": "Delete a notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["notifications"], + "summary": "Edit a notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/jobs": { + "get": { + "tags": ["queue"], + "summary": "List queued monitor jobs (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/metrics": { + "get": { + "tags": ["queue"], + "summary": "Get queue runtime metrics (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/all-metrics": { + "get": { + "tags": ["queue"], + "summary": "Get queue metrics across all monitors (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/flush": { + "post": { + "tags": ["queue"], + "summary": "Flush the monitor job queue (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/settings": { + "get": { + "tags": ["settings"], + "summary": "Get application settings", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["settings"], + "summary": "Update application settings (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "checkTTL": { + "type": "integer", + "minimum": 1, + "maximum": 366 + }, + "systemEmailPort": { + "type": "number", + "nullable": true + }, + "pagespeedApiKey": { + "type": "string", + "nullable": true + }, + "language": { + "type": "string" + }, + "timezone": { + "type": "string" + }, + "systemEmailHost": { + "type": "string", + "nullable": true + }, + "systemEmailAddress": { + "type": "string", + "nullable": true + }, + "systemEmailDisplayName": { + "type": "string", + "nullable": true, + "maxLength": 100 + }, + "systemEmailPassword": { + "type": "string", + "nullable": true + }, + "systemEmailUser": { + "type": "string", + "nullable": true + }, + "systemEmailConnectionHost": { + "type": "string", + "nullable": true + }, + "systemEmailTLSServername": { + "type": "string", + "nullable": true + }, + "showURL": { + "type": "boolean" + }, + "systemEmailSecure": { + "type": "boolean" + }, + "systemEmailPool": { + "type": "boolean" + }, + "systemEmailIgnoreTLS": { + "type": "boolean" + }, + "systemEmailRequireTLS": { + "type": "boolean" + }, + "systemEmailRejectUnauthorized": { + "type": "boolean" + }, + "globalThresholds": { + "type": "object", + "properties": { + "cpu": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "memory": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "disk": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "temperature": { + "type": "number", + "minimum": 1, + "maximum": 150 + } + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/settings/test-email": { + "post": { + "tags": ["settings"], + "summary": "Send a test email using current SMTP settings (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "minLength": 1 + }, + "systemEmailHost": { + "type": "string" + }, + "systemEmailPort": { + "type": "number" + }, + "systemEmailSecure": { + "type": "boolean" + }, + "systemEmailPool": { + "type": "boolean" + }, + "systemEmailAddress": { + "type": "string" + }, + "systemEmailDisplayName": { + "type": "string" + }, + "systemEmailPassword": { + "type": "string" + }, + "systemEmailUser": { + "type": "string" + }, + "systemEmailConnectionHost": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "systemEmailIgnoreTLS": { + "type": "boolean" + }, + "systemEmailRequireTLS": { + "type": "boolean" + }, + "systemEmailRejectUnauthorized": { + "type": "boolean" + }, + "systemEmailTLSServername": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["to"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/team": { + "get": { + "tags": ["status-page"], + "summary": "List status pages for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page": { + "post": { + "tags": ["status-page"], + "summary": "Create a status page (logo upload optional)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "companyName": { + "type": "string", + "minLength": 1 + }, + "url": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "customDomain": { + "anyOf": [ + { + "type": "string", + "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" + }, + { + "nullable": true + } + ] + }, + "timezone": { + "type": "string" + }, + "color": { + "type": "string" + }, + "monitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + }, + "minItems": 1 + }, + "subMonitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + } + }, + "deleteSubmonitors": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "showCharts": { + "type": "boolean" + }, + "showUptimePercentage": { + "type": "boolean" + }, + "showAdminLoginLink": { + "type": "boolean" + }, + "showInfrastructure": { + "type": "boolean" + }, + "removeLogo": { + "anyOf": [ + { + "type": "string", + "enum": ["true"] + }, + { + "type": "string", + "enum": ["false"] + } + ] + }, + "theme": { + "type": "string", + "enum": ["refined", "modern", "bold", "editorial"] + }, + "themeMode": { + "type": "string", + "enum": ["auto", "light", "dark"] + }, + "logo": { + "type": "string", + "format": "binary" + } + }, + "required": ["type", "companyName", "url", "monitors", "isPublished", "showUptimePercentage", "logo"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/{id}": { + "put": { + "tags": ["status-page"], + "summary": "Update a status page (logo upload optional)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "companyName": { + "type": "string", + "minLength": 1 + }, + "url": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "customDomain": { + "anyOf": [ + { + "type": "string", + "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" + }, + { + "nullable": true + } + ] + }, + "timezone": { + "type": "string" + }, + "color": { + "type": "string" + }, + "monitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + }, + "minItems": 1 + }, + "subMonitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + } + }, + "deleteSubmonitors": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "showCharts": { + "type": "boolean" + }, + "showUptimePercentage": { + "type": "boolean" + }, + "showAdminLoginLink": { + "type": "boolean" + }, + "showInfrastructure": { + "type": "boolean" + }, + "removeLogo": { + "anyOf": [ + { + "type": "string", + "enum": ["true"] + }, + { + "type": "string", + "enum": ["false"] + } + ] + }, + "theme": { + "type": "string", + "enum": ["refined", "modern", "bold", "editorial"] + }, + "themeMode": { + "type": "string", + "enum": ["auto", "light", "dark"] + }, + "logo": { + "type": "string", + "format": "binary" + } + }, + "required": ["type", "companyName", "url", "monitors", "isPublished", "showUptimePercentage", "logo"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["status-page"], + "summary": "Delete a status page", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/resolve": { + "get": { + "tags": ["status-page"], + "summary": "Resolve a published status page by custom domain", + "parameters": [ + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "required": true, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "timeFrame", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "domain", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "404": { + "description": "Status page not found" + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/{url}": { + "get": { + "tags": ["status-page"], + "summary": "Get a public status page by its URL slug", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "url", + "in": "path" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "required": true, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "timeFrame", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "404": { + "description": "Status page not found" + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + } + } } From 187c8eaba1af4985d9ad3035c936266d31c11f62 Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Wed, 10 Jun 2026 11:03:34 -0400 Subject: [PATCH 03/17] chore: format openapi.json with prettier --- server/openapi.json | 24607 +++++++++++++++++++----------------------- 1 file changed, 11137 insertions(+), 13470 deletions(-) diff --git a/server/openapi.json b/server/openapi.json index 2681d7e9a..f3a6a2505 100644 --- a/server/openapi.json +++ b/server/openapi.json @@ -1,13472 +1,11139 @@ { - "openapi": "3.0.0", - "info": { - "title": "Checkmate API", - "version": "1.0.0", - "description": "Generated from Zod validation schemas. Source of truth: server/src/api/validation/*.ts" - }, - "servers": [ - { - "url": "/api/v1", - "description": "Current server" - } - ], - "tags": [ - { - "name": "auth", - "description": "Sign up, log in, password reset, and the authenticated user's profile." - }, - { - "name": "monitors", - "description": "Configure and inspect monitors (HTTP, port, ping, hardware, docker, game, gRPC) for the caller's team." - }, - { - "name": "checks", - "description": "Individual monitoring check results, including history, summaries, and bulk deletes." - }, - { - "name": "geo-checks", - "description": "Geographic check results for monitors with multi-region probing enabled." - }, - { - "name": "incidents", - "description": "Downtime incidents derived from monitor state changes, including filtering and CSV export." - }, - { - "name": "notifications", - "description": "Notification channels (email, webhook, Slack, Discord, PagerDuty, Matrix, Teams, Telegram, Pushover, Twilio) and test alerts." - }, - { - "name": "maintenance-window", - "description": "Scheduled maintenance windows during which monitors do not generate incidents or alerts." - }, - { - "name": "status-page", - "description": "Public status pages, their configuration, and the monitors they expose." - }, - { - "name": "settings", - "description": "Global application settings (admin/superadmin)." - }, - { - "name": "invite", - "description": "Team invitations and accepting them." - }, - { - "name": "queue", - "description": "Background job queue introspection and admin actions (admin/superadmin)." - }, - { - "name": "diagnostic", - "description": "System diagnostics for the running server (admin/superadmin)." - }, - { - "name": "logs", - "description": "Application logs for the running server (admin/superadmin)." - } - ], - "components": { - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT" - } - }, - "schemas": { - "AuthPayload": { - "type": "object", - "properties": { - "user": { - "$ref": "#/components/schemas/User" - }, - "token": { - "type": "string" - } - }, - "required": [ - "user" - ], - "example": { - "user": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": [ - "admin" - ], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - }, - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - }, - "User": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "email": { - "type": "string" - }, - "role": { - "type": "array", - "items": { - "type": "string" - } - }, - "teamId": { - "type": "string" - }, - "profileImage": { - "type": "string", - "nullable": true - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": [ - "_id", - "firstName", - "lastName", - "email", - "role", - "createdAt", - "updatedAt" - ], - "additionalProperties": { - "nullable": true - }, - "example": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": [ - "admin" - ], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - } - }, - "Incident": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789c" - }, - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "teamId": { - "type": "string", - "example": "65f1c2a4d8b9e01234567890" - }, - "startTime": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "endTime": { - "type": "string", - "nullable": true, - "example": "2026-04-15T03:34:00.000Z" - }, - "status": { - "type": "boolean", - "example": true, - "description": "true = resolved, false = ongoing" - }, - "message": { - "type": "string", - "nullable": true, - "example": "HTTP 503 from origin" - }, - "statusCode": { - "type": "number", - "nullable": true, - "example": 503 - }, - "resolutionType": { - "type": "string", - "nullable": true, - "enum": [ - "automatic", - "manual", - null - ], - "example": "automatic" - }, - "resolvedBy": { - "type": "string", - "nullable": true - }, - "resolvedByEmail": { - "type": "string", - "nullable": true - }, - "comment": { - "type": "string", - "nullable": true - }, - "createdAt": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "updatedAt": { - "type": "string", - "example": "2026-04-15T03:34:00.000Z" - } - }, - "required": [ - "id", - "monitorId", - "teamId", - "startTime", - "endTime", - "status", - "resolutionType", - "createdAt", - "updatedAt" - ], - "additionalProperties": { - "nullable": true - } - }, - "IncidentSummary": { - "type": "object", - "properties": { - "total": { - "type": "number", - "example": 12 - }, - "totalActive": { - "type": "number", - "example": 1 - }, - "totalManualResolutions": { - "type": "number", - "example": 2 - }, - "totalAutomaticResolutions": { - "type": "number", - "example": 9 - }, - "avgResolutionTimeHours": { - "type": "number", - "example": 0.5 - }, - "topMonitor": { - "type": "object", - "nullable": true, - "properties": { - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "monitorName": { - "type": "string", - "nullable": true, - "example": "Marketing site" - }, - "incidentCount": { - "type": "number", - "example": 4 - } - }, - "required": [ - "monitorId", - "monitorName", - "incidentCount" - ] - }, - "latestIncidents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IncidentSummaryItem" - } - } - }, - "required": [ - "total", - "totalActive", - "totalManualResolutions", - "totalAutomaticResolutions", - "avgResolutionTimeHours", - "topMonitor", - "latestIncidents" - ] - }, - "IncidentSummaryItem": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789c" - }, - "monitorId": { - "type": "string", - "example": "65f1c2a4d8b9e0123456789a" - }, - "monitorName": { - "type": "string", - "nullable": true, - "example": "Marketing site" - }, - "status": { - "type": "boolean", - "example": true - }, - "startTime": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - }, - "endTime": { - "type": "string", - "nullable": true, - "example": "2026-04-15T03:34:00.000Z" - }, - "resolutionType": { - "type": "string", - "nullable": true, - "enum": [ - "automatic", - "manual", - null - ], - "example": "automatic" - }, - "message": { - "type": "string", - "nullable": true, - "example": "HTTP 503 from origin" - }, - "statusCode": { - "type": "number", - "nullable": true, - "example": 503 - }, - "createdAt": { - "type": "string", - "example": "2026-04-15T03:21:00.000Z" - } - }, - "required": [ - "id", - "monitorId", - "monitorName", - "status", - "startTime", - "endTime", - "resolutionType", - "message", - "statusCode", - "createdAt" - ] - }, - "Monitor": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "url": { - "type": "string" - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean" - }, - "interval": { - "type": "number" - }, - "status": { - "type": "string", - "enum": [ - "up", - "down", - "paused", - "initializing", - "maintenance", - "breached" - ] - }, - "statusWindowSize": { - "type": "number" - }, - "statusWindowThreshold": { - "type": "number" - }, - "ignoreTlsErrors": { - "type": "boolean" - }, - "useAdvancedMatching": { - "type": "boolean" - }, - "jsonPath": { - "type": "string" - }, - "expectedValue": { - "type": "string" - }, - "matchMethod": { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "customUpCodes": { - "type": "array", - "items": { - "type": "number" - } - }, - "secret": { - "type": "string" - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "gameId": { - "type": "string" - }, - "grpcServiceName": { - "type": "string" - }, - "group": { - "type": "string", - "nullable": true - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - }, - "geoCheckInterval": { - "type": "number" - }, - "dnsServer": { - "type": "string" - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - }, - "teamId": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": [ - "_id", - "name", - "type", - "url", - "isActive", - "interval", - "status", - "statusWindowSize", - "statusWindowThreshold", - "ignoreTlsErrors", - "useAdvancedMatching", - "notifications", - "tags", - "cpuAlertThreshold", - "memoryAlertThreshold", - "diskAlertThreshold", - "tempAlertThreshold", - "selectedDisks", - "geoCheckEnabled", - "geoCheckLocations", - "geoCheckInterval", - "teamId", - "userId", - "createdAt", - "updatedAt" - ], - "additionalProperties": { - "nullable": true - }, - "example": { - "_id": "65f1c2a4d8b9e0123456789a", - "name": "Marketing site", - "description": "Production marketing site monitored from the EU region", - "type": "http", - "url": "https://www.example.com", - "port": 443, - "isActive": true, - "interval": 60000, - "status": "up", - "statusWindowSize": 5, - "statusWindowThreshold": 3, - "ignoreTlsErrors": false, - "useAdvancedMatching": false, - "notifications": [ - "65f1c2a4d8b9e0123456789b" - ], - "cpuAlertThreshold": 90, - "memoryAlertThreshold": 90, - "diskAlertThreshold": 90, - "tempAlertThreshold": 80, - "selectedDisks": [], - "geoCheckEnabled": false, - "geoCheckLocations": [], - "geoCheckInterval": 300000, - "teamId": "65f1c2a4d8b9e01234567890", - "userId": "65f1c2a4d8b9e01234567891", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - } - }, - "NotificationChannelBody": { - "oneOf": [ - { - "$ref": "#/components/schemas/EmailNotification" - }, - { - "$ref": "#/components/schemas/WebhookNotification" - }, - { - "$ref": "#/components/schemas/SlackNotification" - }, - { - "$ref": "#/components/schemas/DiscordNotification" - }, - { - "$ref": "#/components/schemas/PagerDutyNotification" - }, - { - "$ref": "#/components/schemas/MatrixNotification" - }, - { - "$ref": "#/components/schemas/TeamsNotification" - }, - { - "$ref": "#/components/schemas/TelegramNotification" - }, - { - "$ref": "#/components/schemas/PushoverNotification" - }, - { - "$ref": "#/components/schemas/TwilioNotification" - }, - { - "$ref": "#/components/schemas/NtfyNotification" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "email": "#/components/schemas/EmailNotification", - "webhook": "#/components/schemas/WebhookNotification", - "slack": "#/components/schemas/SlackNotification", - "discord": "#/components/schemas/DiscordNotification", - "pager_duty": "#/components/schemas/PagerDutyNotification", - "matrix": "#/components/schemas/MatrixNotification", - "teams": "#/components/schemas/TeamsNotification", - "telegram": "#/components/schemas/TelegramNotification", - "pushover": "#/components/schemas/PushoverNotification", - "twilio": "#/components/schemas/TwilioNotification", - "ntfy": "#/components/schemas/NtfyNotification" - } - } - }, - "EmailNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "email" - ] - }, - "address": { - "type": "string", - "format": "email" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "Ops on-call email", - "type": "email", - "address": "alerts@example.com" - } - }, - "WebhookNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "webhook" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "Custom webhook", - "type": "webhook", - "address": "https://example.com/hooks/checkmate" - } - }, - "SlackNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "slack" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "#alerts", - "type": "slack", - "address": "https://hooks.slack.com/services/T000/B000/XXXX" - } - }, - "DiscordNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "discord" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "#status", - "type": "discord", - "address": "https://discord.com/api/webhooks/123/abc" - } - }, - "PagerDutyNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "pager_duty" - ] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "homeserverUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "roomId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "accessToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "PagerDuty primary", - "type": "pager_duty", - "address": "R01XXXXXXXXXXXXXXXXXXXXXXX" - } - }, - "MatrixNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "matrix" - ] - }, - "address": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "homeserverUrl": { - "type": "string", - "format": "uri" - }, - "roomId": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "homeserverUrl", - "roomId", - "accessToken" - ], - "example": { - "notificationName": "Matrix room", - "type": "matrix", - "homeserverUrl": "https://matrix.example.com", - "roomId": "!abc123:example.com", - "accessToken": "syt_xxx" - } - }, - "TeamsNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "teams" - ] - }, - "address": { - "type": "string", - "format": "uri" - } - }, - "required": [ - "notificationName", - "type", - "address" - ], - "example": { - "notificationName": "Teams ops channel", - "type": "teams", - "address": "https://outlook.office.com/webhook/..." - } - }, - "TelegramNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "telegram" - ] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "address", - "accessToken" - ], - "example": { - "notificationName": "Telegram bot", - "type": "telegram", - "address": "-1001234567890", - "accessToken": "123456:ABC-DEF" - } - }, - "PushoverNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "pushover" - ] - }, - "address": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "address", - "accessToken" - ], - "example": { - "notificationName": "Pushover personal", - "type": "pushover", - "address": "u1234567890abcdef", - "accessToken": "a1234567890abcdef" - } - }, - "TwilioNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "twilio" - ] - }, - "accountSid": { - "type": "string", - "minLength": 1 - }, - "accessToken": { - "type": "string", - "minLength": 1 - }, - "phone": { - "type": "string", - "minLength": 1 - }, - "twilioPhoneNumber": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "accountSid", - "accessToken", - "phone", - "twilioPhoneNumber" - ], - "example": { - "notificationName": "Twilio SMS", - "type": "twilio", - "accountSid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - "accessToken": "your-auth-token", - "phone": "+15551234567", - "twilioPhoneNumber": "+15557654321" - } - }, - "NtfyNotification": { - "type": "object", - "properties": { - "notificationName": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "enum": [ - "ntfy" - ] - }, - "address": { - "type": "string", - "format": "uri" - }, - "topic": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "notificationName", - "type", - "address", - "topic" - ], - "example": { - "notificationName": "ntfy topic", - "type": "ntfy", - "address": "https://ntfy.sh", - "topic": "checkmate-alerts" - } - } - }, - "parameters": {} - }, - "paths": { - "/auth/register": { - "post": { - "tags": [ - "auth" - ], - "summary": "Register a new user (first user becomes superadmin)", - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "profileImage": { - "type": "string", - "format": "binary" - }, - "inviteToken": { - "type": "string", - "default": "" - } - }, - "required": [ - "firstName", - "lastName", - "email", - "password", - "profileImage" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/AuthPayload" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/login": { - "post": { - "tags": [ - "auth" - ], - "summary": "Log in", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "email", - "password" - ] - }, - "example": { - "email": "ada@example.com", - "password": "S3cure!Passw0rd" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/AuthPayload" - } - }, - "required": [ - "success", - "msg", - "data" - ] - }, - "example": { - "success": true, - "msg": "OK", - "data": { - "user": { - "_id": "65f1c2a4d8b9e0123456789a", - "firstName": "Ada", - "lastName": "Lovelace", - "email": "ada@example.com", - "role": [ - "admin" - ], - "teamId": "65f1c2a4d8b9e01234567890", - "createdAt": "2026-04-01T10:00:00.000Z", - "updatedAt": "2026-04-15T14:30:00.000Z" - }, - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - } - } - } - }, - "401": { - "description": "Invalid credentials", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/recovery/request": { - "post": { - "tags": [ - "auth" - ], - "summary": "Request a password recovery email", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - } - }, - "required": [ - "email" - ] - }, - "example": { - "email": "ada@example.com" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/recovery/validate": { - "post": { - "tags": [ - "auth" - ], - "summary": "Validate a password recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "recoveryToken": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "recoveryToken" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/recovery/reset": { - "post": { - "tags": [ - "auth" - ], - "summary": "Reset password using a recovery token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "recoveryToken": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "confirm": { - "type": "string" - } - }, - "required": [ - "recoveryToken", - "password" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users/superadmin": { - "get": { - "tags": [ - "auth" - ], - "summary": "Check whether a superadmin user exists", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "superAdminExists": { - "type": "boolean" - } - }, - "required": [ - "superAdminExists" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users": { - "get": { - "tags": [ - "auth" - ], - "summary": "List all users (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "post": { - "tags": [ - "auth" - ], - "summary": "Create a new user (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - }, - "profileImage": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "firstName", - "lastName", - "email", - "password", - "role", - "profileImage" - ] - } - } - } - }, - "responses": { - "201": { - "description": "User created", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users/{userId}": { - "get": { - "tags": [ - "auth" - ], - "summary": "Get a user by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "auth" - ], - "summary": "Edit a user (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - } - }, - "required": [ - "firstName", - "lastName", - "role" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "auth" - ], - "summary": "Delete a user (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/users/{userId}/password": { - "patch": { - "tags": [ - "auth" - ], - "summary": "Change a user's password (superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "userId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - } - }, - "required": [ - "password" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/auth/user": { - "patch": { - "tags": [ - "auth" - ], - "summary": "Edit the currently authenticated user", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "lastName": { - "type": "string", - "maxLength": 50, - "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" - }, - "email": { - "type": "string", - "format": "email" - }, - "profilePicture": { - "type": "string" - }, - "password": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "newPassword": { - "type": "string", - "minLength": 8, - "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" - }, - "profileImage": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "profileImage" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/User" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "auth" - ], - "summary": "Delete the currently authenticated user", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/checks/team/summary": { - "get": { - "tags": [ - "checks" - ], - "summary": "Aggregate check summary for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/checks/team": { - "get": { - "tags": [ - "checks" - ], - "summary": "List checks across the team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "all", - "up", - "down", - "resolve" - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "checks" - ], - "summary": "Delete all checks for the team (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "deletedCount": { - "type": "number" - } - }, - "required": [ - "deletedCount" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/checks/{monitorId}": { - "get": { - "tags": [ - "checks" - ], - "summary": "List checks for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "all", - "up", - "down", - "resolve" - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "checks" - ], - "summary": "Delete checks for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/diagnostic/system": { - "get": { - "tags": [ - "diagnostic" - ], - "summary": "Get system diagnostics (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/geo-checks/{monitorId}": { - "get": { - "tags": [ - "geo-checks" - ], - "summary": "Get geo check results for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "all", - "up", - "down", - "resolve" - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "ack", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/team": { - "get": { - "tags": [ - "incidents" - ], - "summary": "List incidents for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": true, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ], - "default": "all" - }, - "required": false, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "integer", - "nullable": true, - "minimum": 0 - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": true, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "monitorId", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "manual", - "automatic" - ] - }, - "required": false, - "name": "resolutionType", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Incident" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/team/summary": { - "get": { - "tags": [ - "incidents" - ], - "summary": "Incident summary for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": false, - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/IncidentSummary" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/{incidentId}": { - "get": { - "tags": [ - "incidents" - ], - "summary": "Get an incident by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "incidentId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "incident": { - "$ref": "#/components/schemas/Incident" - }, - "monitor": { - "nullable": true - }, - "user": { - "nullable": true - } - }, - "required": [ - "incident" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/incidents/{incidentId}/resolve": { - "put": { - "tags": [ - "incidents" - ], - "summary": "Manually resolve an incident (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "incidentId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/invite/send": { - "post": { - "tags": [ - "invite" - ], - "summary": "Send an invite email (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - }, - "teamId": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "email", - "role", - "teamId" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/invite/verify": { - "post": { - "tags": [ - "invite" - ], - "summary": "Verify an invite token", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "token": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "token" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "role": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "email" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/invite": { - "post": { - "tags": [ - "invite" - ], - "summary": "Create an invite token (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "user", - "admin", - "superadmin", - "demo" - ] - }, - "minItems": 1 - }, - "teamId": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "email", - "role", - "teamId" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "token": { - "type": "string" - } - }, - "required": [ - "token" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/logs": { - "get": { - "tags": [ - "logs" - ], - "summary": "Get application logs (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window": { - "post": { - "tags": [ - "maintenance-window" - ], - "summary": "Create one or more maintenance windows", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "name": { - "type": "string", - "minLength": 1 - }, - "active": { - "type": "boolean" - }, - "duration": { - "type": "number", - "minimum": 1 - }, - "durationUnit": { - "type": "string", - "enum": [ - "seconds", - "minutes", - "hours", - "days" - ] - }, - "start": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "end": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "repeat": { - "type": "number", - "minimum": 0 - }, - "expiry": { - "type": "string", - "nullable": true, - "format": "date-time" - } - }, - "required": [ - "monitors", - "name", - "duration", - "durationUnit", - "start", - "end", - "repeat" - ], - "additionalProperties": false - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window/team": { - "get": { - "tags": [ - "maintenance-window" - ], - "summary": "List maintenance windows for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "active", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": false, - "name": "order", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window/monitor/{monitorId}": { - "get": { - "tags": [ - "maintenance-window" - ], - "summary": "List maintenance windows for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/maintenance-window/{id}": { - "get": { - "tags": [ - "maintenance-window" - ], - "summary": "Get a maintenance window by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "maintenance-window" - ], - "summary": "Edit a maintenance window", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "active": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "repeat": { - "type": "number" - }, - "start": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "end": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "expiry": { - "type": "string", - "nullable": true, - "format": "date-time" - }, - "monitors": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "duration": { - "type": "number" - }, - "durationUnit": { - "type": "string", - "enum": [ - "seconds", - "minutes", - "hours", - "days" - ] - } - }, - "additionalProperties": false - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "maintenance-window" - ], - "summary": "Delete a maintenance window", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/team": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List monitors for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - } - } - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "required": false, - "name": "tags", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Monitor" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/team/with-checks": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List team monitors with their most recent checks (paginated)", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "integer", - "nullable": true, - "minimum": 0 - }, - "required": false, - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "rowsPerPage", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "required": false, - "name": "filter", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": false, - "name": "order", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - } - } - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "required": false, - "name": "tags", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "explain", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/team/groups": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List monitor groups for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/uptime/details/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get uptime details for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": true, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "normalize", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/hardware/details/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get hardware metrics detail for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/pagespeed/details/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get PageSpeed detail for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/{monitorId}/geo-checks": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get geo check results for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "required": false, - "name": "sortOrder", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "recent", - "hour", - "day", - "week", - "month", - "all" - ] - }, - "required": false, - "name": "dateRange", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "numToDisplay", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "normalize", - "in": "query" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - } - ] - }, - "required": false, - "name": "continent", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/pause/{monitorId}": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Toggle pause state for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/certificate/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get SSL certificate info for a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "certificateDate": { - "type": "string" - } - }, - "required": [ - "certificateDate" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/notifications": { - "patch": { - "tags": [ - "monitors" - ], - "summary": "Bulk update notifications across monitors", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitorIds": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "maxItems": 100 - }, - "notificationIds": { - "type": "array", - "items": { - "type": "string" - }, - "maxItems": 100 - }, - "action": { - "type": "string", - "enum": [ - "add", - "remove", - "set" - ] - } - }, - "required": [ - "monitorIds", - "notificationIds", - "action" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "modifiedCount": { - "type": "number" - } - }, - "required": [ - "modifiedCount" - ] - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Create a new monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "url": { - "type": "string", - "minLength": 1 - }, - "ignoreTlsErrors": { - "type": "boolean", - "default": false - }, - "useAdvancedMatching": { - "type": "boolean", - "default": false - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean" - }, - "interval": { - "type": "number" - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "customUpCodes": { - "type": "array", - "items": { - "type": "number" - }, - "default": [] - }, - "secret": { - "type": "string" - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ], - "default": "" - }, - "strategy": { - "type": "string", - "enum": [ - "desktop", - "mobile" - ] - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - } - }, - "required": [ - "name", - "type", - "url" - ] - } - } - } - }, - "responses": { - "200": { - "description": "Monitor created", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "monitors" - ], - "summary": "Delete every monitor (superadmin only)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/demo": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Insert preconfigured demo monitors", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "number" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/export/json": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Export all team monitors as JSON", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/import/json": { - "post": { - "tags": [ - "monitors" - ], - "summary": "Import monitors from JSON", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "status": { - "type": "string", - "enum": [ - "up", - "down", - "paused", - "initializing", - "maintenance", - "breached" - ], - "default": "initializing" - }, - "statusWindow": { - "type": "array", - "items": { - "type": "boolean" - }, - "default": [] - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "ignoreTlsErrors": { - "type": "boolean", - "default": false - }, - "useAdvancedMatching": { - "type": "boolean", - "default": false - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "url": { - "type": "string", - "minLength": 1 - }, - "port": { - "type": "number" - }, - "isActive": { - "type": "boolean", - "default": true - }, - "interval": { - "type": "number", - "default": 60000 - }, - "uptimePercentage": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "customUpCodes": { - "type": "array", - "items": { - "type": "number" - }, - "default": [] - }, - "secret": { - "type": "string" - }, - "cpuAlertThreshold": { - "type": "number", - "default": 100 - }, - "cpuAlertCounter": { - "type": "number", - "default": 5 - }, - "memoryAlertThreshold": { - "type": "number", - "default": 100 - }, - "memoryAlertCounter": { - "type": "number", - "default": 5 - }, - "diskAlertThreshold": { - "type": "number", - "default": 100 - }, - "diskAlertCounter": { - "type": "number", - "default": 5 - }, - "tempAlertThreshold": { - "type": "number", - "default": 100 - }, - "tempAlertCounter": { - "type": "number", - "default": 5 - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ], - "default": "" - }, - "strategy": { - "type": "string", - "enum": [ - "desktop", - "mobile" - ] - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - } - ], - "default": null - }, - "geoCheckEnabled": { - "type": "boolean", - "default": false - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - }, - "default": [] - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000, - "default": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - }, - "required": [ - "name", - "type", - "url" - ] - }, - "minItems": 1 - } - }, - "required": [ - "monitors" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "imported": { - "type": "number" - } - }, - "required": [ - "imported" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/games": { - "get": { - "tags": [ - "monitors" - ], - "summary": "List supported game-server types", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/monitors/{monitorId}": { - "get": { - "tags": [ - "monitors" - ], - "summary": "Get a monitor by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "monitors" - ], - "summary": "Edit a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "http", - "ping", - "pagespeed", - "hardware", - "docker", - "port", - "game", - "grpc", - "websocket", - "dns", - "unknown" - ] - }, - "url": { - "type": "string" - }, - "statusWindowSize": { - "type": "number", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "number", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "interval": { - "type": "number" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "customUpCodes": { - "type": "array", - "items": { - "type": "number" - } - }, - "secret": { - "type": "string" - }, - "ignoreTlsErrors": { - "type": "boolean" - }, - "useAdvancedMatching": { - "type": "boolean" - }, - "jsonPath": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "expectedValue": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "matchMethod": { - "anyOf": [ - { - "type": "string", - "enum": [ - "equal", - "include", - "regex" - ] - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "port": { - "type": "number", - "minimum": 1, - "maximum": 65535 - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "gameId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "grpcServiceName": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "strategy": { - "type": "string", - "enum": [ - "desktop", - "mobile" - ] - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "anyOf": [ - { - "type": "string", - "maxLength": 50 - }, - { - "nullable": true - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "EU", - "NA", - "AS", - "SA", - "AF", - "OC" - ] - } - }, - "geoCheckInterval": { - "type": "number", - "minimum": 300000 - }, - "dnsServer": { - "type": "string", - "minLength": 1 - }, - "dnsRecordType": { - "type": "string", - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "TXT", - "NS" - ] - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "monitors" - ], - "summary": "Delete a monitor", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "monitorId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/Monitor" - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications": { - "post": { - "tags": [ - "notifications" - ], - "summary": "Create a notification channel", - "description": "Create a notification channel of any supported type. The `type` field discriminates the body shape.", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/test/all": { - "post": { - "tags": [ - "notifications" - ], - "summary": "Send a test alert through every notification channel for the team", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "monitorId": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "monitorId" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/test": { - "post": { - "tags": [ - "notifications" - ], - "summary": "Send a test alert through a single notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - }, - "required": [ - "success" - ], - "additionalProperties": { - "nullable": true - } - } - }, - "required": [ - "success", - "msg", - "data" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/team": { - "get": { - "tags": [ - "notifications" - ], - "summary": "List notification channels for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/notifications/{id}": { - "get": { - "tags": [ - "notifications" - ], - "summary": "Get a notification channel by id", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "notifications" - ], - "summary": "Delete a notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "notifications" - ], - "summary": "Edit a notification channel", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationChannelBody" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/jobs": { - "get": { - "tags": [ - "queue" - ], - "summary": "List queued monitor jobs (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/metrics": { - "get": { - "tags": [ - "queue" - ], - "summary": "Get queue runtime metrics (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/all-metrics": { - "get": { - "tags": [ - "queue" - ], - "summary": "Get queue metrics across all monitors (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/queue/flush": { - "post": { - "tags": [ - "queue" - ], - "summary": "Flush the monitor job queue (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/settings": { - "get": { - "tags": [ - "settings" - ], - "summary": "Get application settings", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "patch": { - "tags": [ - "settings" - ], - "summary": "Update application settings (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "checkTTL": { - "type": "integer", - "minimum": 1, - "maximum": 366 - }, - "systemEmailPort": { - "type": "number", - "nullable": true - }, - "pagespeedApiKey": { - "type": "string", - "nullable": true - }, - "language": { - "type": "string" - }, - "timezone": { - "type": "string" - }, - "systemEmailHost": { - "type": "string", - "nullable": true - }, - "systemEmailAddress": { - "type": "string", - "nullable": true - }, - "systemEmailDisplayName": { - "type": "string", - "nullable": true, - "maxLength": 100 - }, - "systemEmailPassword": { - "type": "string", - "nullable": true - }, - "systemEmailUser": { - "type": "string", - "nullable": true - }, - "systemEmailConnectionHost": { - "type": "string", - "nullable": true - }, - "systemEmailTLSServername": { - "type": "string", - "nullable": true - }, - "showURL": { - "type": "boolean" - }, - "systemEmailSecure": { - "type": "boolean" - }, - "systemEmailPool": { - "type": "boolean" - }, - "systemEmailIgnoreTLS": { - "type": "boolean" - }, - "systemEmailRequireTLS": { - "type": "boolean" - }, - "systemEmailRejectUnauthorized": { - "type": "boolean" - }, - "globalThresholds": { - "type": "object", - "properties": { - "cpu": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "memory": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "disk": { - "type": "number", - "minimum": 1, - "maximum": 100 - }, - "temperature": { - "type": "number", - "minimum": 1, - "maximum": 150 - } - } - } - } - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/settings/test-email": { - "post": { - "tags": [ - "settings" - ], - "summary": "Send a test email using current SMTP settings (admin/superadmin)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "to": { - "type": "string", - "minLength": 1 - }, - "systemEmailHost": { - "type": "string" - }, - "systemEmailPort": { - "type": "number" - }, - "systemEmailSecure": { - "type": "boolean" - }, - "systemEmailPool": { - "type": "boolean" - }, - "systemEmailAddress": { - "type": "string" - }, - "systemEmailDisplayName": { - "type": "string" - }, - "systemEmailPassword": { - "type": "string" - }, - "systemEmailUser": { - "type": "string" - }, - "systemEmailConnectionHost": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - }, - "systemEmailIgnoreTLS": { - "type": "boolean" - }, - "systemEmailRequireTLS": { - "type": "boolean" - }, - "systemEmailRejectUnauthorized": { - "type": "boolean" - }, - "systemEmailTLSServername": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "enum": [ - "" - ] - } - ] - } - }, - "required": [ - "to" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/team": { - "get": { - "tags": [ - "status-page" - ], - "summary": "List status pages for the caller's team", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page": { - "post": { - "tags": [ - "status-page" - ], - "summary": "Create a status page (logo upload optional)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "type": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "companyName": { - "type": "string", - "minLength": 1 - }, - "url": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$" - }, - "customDomain": { - "anyOf": [ - { - "type": "string", - "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" - }, - { - "nullable": true - } - ] - }, - "timezone": { - "type": "string" - }, - "color": { - "type": "string" - }, - "monitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - }, - "minItems": 1 - }, - "subMonitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - } - }, - "deleteSubmonitors": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "showCharts": { - "type": "boolean" - }, - "showUptimePercentage": { - "type": "boolean" - }, - "showAdminLoginLink": { - "type": "boolean" - }, - "showInfrastructure": { - "type": "boolean" - }, - "removeLogo": { - "anyOf": [ - { - "type": "string", - "enum": [ - "true" - ] - }, - { - "type": "string", - "enum": [ - "false" - ] - } - ] - }, - "theme": { - "type": "string", - "enum": [ - "refined", - "modern", - "bold", - "editorial" - ] - }, - "themeMode": { - "type": "string", - "enum": [ - "auto", - "light", - "dark" - ] - }, - "logo": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "type", - "companyName", - "url", - "monitors", - "isPublished", - "showUptimePercentage", - "logo" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/{id}": { - "put": { - "tags": [ - "status-page" - ], - "summary": "Update a status page (logo upload optional)", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "type": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "companyName": { - "type": "string", - "minLength": 1 - }, - "url": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$" - }, - "customDomain": { - "anyOf": [ - { - "type": "string", - "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" - }, - { - "nullable": true - } - ] - }, - "timezone": { - "type": "string" - }, - "color": { - "type": "string" - }, - "monitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - }, - "minItems": 1 - }, - "subMonitors": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$" - } - }, - "deleteSubmonitors": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "showCharts": { - "type": "boolean" - }, - "showUptimePercentage": { - "type": "boolean" - }, - "showAdminLoginLink": { - "type": "boolean" - }, - "showInfrastructure": { - "type": "boolean" - }, - "removeLogo": { - "anyOf": [ - { - "type": "string", - "enum": [ - "true" - ] - }, - { - "type": "string", - "enum": [ - "false" - ] - } - ] - }, - "theme": { - "type": "string", - "enum": [ - "refined", - "modern", - "bold", - "editorial" - ] - }, - "themeMode": { - "type": "string", - "enum": [ - "auto", - "light", - "dark" - ] - }, - "logo": { - "type": "string", - "format": "binary" - } - }, - "required": [ - "type", - "companyName", - "url", - "monitors", - "isPublished", - "showUptimePercentage", - "logo" - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - }, - "delete": { - "tags": [ - "status-page" - ], - "summary": "Delete a status page", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - } - }, - "required": [ - "success", - "msg" - ] - }, - "example": { - "success": true, - "msg": "OK" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/resolve": { - "get": { - "tags": [ - "status-page" - ], - "summary": "Resolve a published status page by custom domain", - "parameters": [ - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "required": true, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "timeFrame", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "domain", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "404": { - "description": "Status page not found" - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - }, - "/status-page/{url}": { - "get": { - "tags": [ - "status-page" - ], - "summary": "Get a public status page by its URL slug", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "name": "url", - "in": "path" - }, - { - "schema": { - "anyOf": [ - { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "uptime", - "infrastructure" - ] - } - } - ] - }, - "required": true, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "number", - "nullable": true - }, - "required": false, - "name": "timeFrame", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - }, - "404": { - "description": "Status page not found" - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "msg": { - "type": "string" - }, - "data": { - "nullable": true - } - }, - "required": [ - "success", - "msg" - ] - } - } - } - } - } - } - } - } + "openapi": "3.0.0", + "info": { + "title": "Checkmate API", + "version": "1.0.0", + "description": "Generated from Zod validation schemas. Source of truth: server/src/api/validation/*.ts" + }, + "servers": [ + { + "url": "/api/v1", + "description": "Current server" + } + ], + "tags": [ + { + "name": "auth", + "description": "Sign up, log in, password reset, and the authenticated user's profile." + }, + { + "name": "monitors", + "description": "Configure and inspect monitors (HTTP, port, ping, hardware, docker, game, gRPC) for the caller's team." + }, + { + "name": "checks", + "description": "Individual monitoring check results, including history, summaries, and bulk deletes." + }, + { + "name": "geo-checks", + "description": "Geographic check results for monitors with multi-region probing enabled." + }, + { + "name": "incidents", + "description": "Downtime incidents derived from monitor state changes, including filtering and CSV export." + }, + { + "name": "notifications", + "description": "Notification channels (email, webhook, Slack, Discord, PagerDuty, Matrix, Teams, Telegram, Pushover, Twilio) and test alerts." + }, + { + "name": "maintenance-window", + "description": "Scheduled maintenance windows during which monitors do not generate incidents or alerts." + }, + { + "name": "status-page", + "description": "Public status pages, their configuration, and the monitors they expose." + }, + { + "name": "settings", + "description": "Global application settings (admin/superadmin)." + }, + { + "name": "invite", + "description": "Team invitations and accepting them." + }, + { + "name": "queue", + "description": "Background job queue introspection and admin actions (admin/superadmin)." + }, + { + "name": "diagnostic", + "description": "System diagnostics for the running server (admin/superadmin)." + }, + { + "name": "logs", + "description": "Application logs for the running server (admin/superadmin)." + } + ], + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "AuthPayload": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/User" + }, + "token": { + "type": "string" + } + }, + "required": ["user"], + "example": { + "user": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": ["admin"], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + }, + "User": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "role": { + "type": "array", + "items": { + "type": "string" + } + }, + "teamId": { + "type": "string" + }, + "profileImage": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": ["_id", "firstName", "lastName", "email", "role", "createdAt", "updatedAt"], + "additionalProperties": { + "nullable": true + }, + "example": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": ["admin"], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + } + }, + "Incident": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789c" + }, + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "teamId": { + "type": "string", + "example": "65f1c2a4d8b9e01234567890" + }, + "startTime": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "endTime": { + "type": "string", + "nullable": true, + "example": "2026-04-15T03:34:00.000Z" + }, + "status": { + "type": "boolean", + "example": true, + "description": "true = resolved, false = ongoing" + }, + "message": { + "type": "string", + "nullable": true, + "example": "HTTP 503 from origin" + }, + "statusCode": { + "type": "number", + "nullable": true, + "example": 503 + }, + "resolutionType": { + "type": "string", + "nullable": true, + "enum": ["automatic", "manual", null], + "example": "automatic" + }, + "resolvedBy": { + "type": "string", + "nullable": true + }, + "resolvedByEmail": { + "type": "string", + "nullable": true + }, + "comment": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "updatedAt": { + "type": "string", + "example": "2026-04-15T03:34:00.000Z" + } + }, + "required": ["id", "monitorId", "teamId", "startTime", "endTime", "status", "resolutionType", "createdAt", "updatedAt"], + "additionalProperties": { + "nullable": true + } + }, + "IncidentSummary": { + "type": "object", + "properties": { + "total": { + "type": "number", + "example": 12 + }, + "totalActive": { + "type": "number", + "example": 1 + }, + "totalManualResolutions": { + "type": "number", + "example": 2 + }, + "totalAutomaticResolutions": { + "type": "number", + "example": 9 + }, + "avgResolutionTimeHours": { + "type": "number", + "example": 0.5 + }, + "topMonitor": { + "type": "object", + "nullable": true, + "properties": { + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "monitorName": { + "type": "string", + "nullable": true, + "example": "Marketing site" + }, + "incidentCount": { + "type": "number", + "example": 4 + } + }, + "required": ["monitorId", "monitorName", "incidentCount"] + }, + "latestIncidents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IncidentSummaryItem" + } + } + }, + "required": [ + "total", + "totalActive", + "totalManualResolutions", + "totalAutomaticResolutions", + "avgResolutionTimeHours", + "topMonitor", + "latestIncidents" + ] + }, + "IncidentSummaryItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789c" + }, + "monitorId": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "monitorName": { + "type": "string", + "nullable": true, + "example": "Marketing site" + }, + "status": { + "type": "boolean", + "example": true + }, + "startTime": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + }, + "endTime": { + "type": "string", + "nullable": true, + "example": "2026-04-15T03:34:00.000Z" + }, + "resolutionType": { + "type": "string", + "nullable": true, + "enum": ["automatic", "manual", null], + "example": "automatic" + }, + "message": { + "type": "string", + "nullable": true, + "example": "HTTP 503 from origin" + }, + "statusCode": { + "type": "number", + "nullable": true, + "example": 503 + }, + "createdAt": { + "type": "string", + "example": "2026-04-15T03:21:00.000Z" + } + }, + "required": ["id", "monitorId", "monitorName", "status", "startTime", "endTime", "resolutionType", "message", "statusCode", "createdAt"] + }, + "Monitor": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "url": { + "type": "string" + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "number" + }, + "status": { + "type": "string", + "enum": ["up", "down", "paused", "initializing", "maintenance", "breached"] + }, + "statusWindowSize": { + "type": "number" + }, + "statusWindowThreshold": { + "type": "number" + }, + "ignoreTlsErrors": { + "type": "boolean" + }, + "useAdvancedMatching": { + "type": "boolean" + }, + "jsonPath": { + "type": "string" + }, + "expectedValue": { + "type": "string" + }, + "matchMethod": { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "customUpCodes": { + "type": "array", + "items": { + "type": "number" + } + }, + "secret": { + "type": "string" + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "gameId": { + "type": "string" + }, + "grpcServiceName": { + "type": "string" + }, + "group": { + "type": "string", + "nullable": true + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + }, + "geoCheckInterval": { + "type": "number" + }, + "dnsServer": { + "type": "string" + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + }, + "teamId": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "_id", + "name", + "type", + "url", + "isActive", + "interval", + "status", + "statusWindowSize", + "statusWindowThreshold", + "ignoreTlsErrors", + "useAdvancedMatching", + "notifications", + "tags", + "cpuAlertThreshold", + "memoryAlertThreshold", + "diskAlertThreshold", + "tempAlertThreshold", + "selectedDisks", + "geoCheckEnabled", + "geoCheckLocations", + "geoCheckInterval", + "teamId", + "userId", + "createdAt", + "updatedAt" + ], + "additionalProperties": { + "nullable": true + }, + "example": { + "_id": "65f1c2a4d8b9e0123456789a", + "name": "Marketing site", + "description": "Production marketing site monitored from the EU region", + "type": "http", + "url": "https://www.example.com", + "port": 443, + "isActive": true, + "interval": 60000, + "status": "up", + "statusWindowSize": 5, + "statusWindowThreshold": 3, + "ignoreTlsErrors": false, + "useAdvancedMatching": false, + "notifications": ["65f1c2a4d8b9e0123456789b"], + "cpuAlertThreshold": 90, + "memoryAlertThreshold": 90, + "diskAlertThreshold": 90, + "tempAlertThreshold": 80, + "selectedDisks": [], + "geoCheckEnabled": false, + "geoCheckLocations": [], + "geoCheckInterval": 300000, + "teamId": "65f1c2a4d8b9e01234567890", + "userId": "65f1c2a4d8b9e01234567891", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + } + }, + "NotificationChannelBody": { + "oneOf": [ + { + "$ref": "#/components/schemas/EmailNotification" + }, + { + "$ref": "#/components/schemas/WebhookNotification" + }, + { + "$ref": "#/components/schemas/SlackNotification" + }, + { + "$ref": "#/components/schemas/DiscordNotification" + }, + { + "$ref": "#/components/schemas/PagerDutyNotification" + }, + { + "$ref": "#/components/schemas/MatrixNotification" + }, + { + "$ref": "#/components/schemas/TeamsNotification" + }, + { + "$ref": "#/components/schemas/TelegramNotification" + }, + { + "$ref": "#/components/schemas/PushoverNotification" + }, + { + "$ref": "#/components/schemas/TwilioNotification" + }, + { + "$ref": "#/components/schemas/NtfyNotification" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "email": "#/components/schemas/EmailNotification", + "webhook": "#/components/schemas/WebhookNotification", + "slack": "#/components/schemas/SlackNotification", + "discord": "#/components/schemas/DiscordNotification", + "pager_duty": "#/components/schemas/PagerDutyNotification", + "matrix": "#/components/schemas/MatrixNotification", + "teams": "#/components/schemas/TeamsNotification", + "telegram": "#/components/schemas/TelegramNotification", + "pushover": "#/components/schemas/PushoverNotification", + "twilio": "#/components/schemas/TwilioNotification", + "ntfy": "#/components/schemas/NtfyNotification" + } + } + }, + "EmailNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["email"] + }, + "address": { + "type": "string", + "format": "email" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "Ops on-call email", + "type": "email", + "address": "alerts@example.com" + } + }, + "WebhookNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["webhook"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "Custom webhook", + "type": "webhook", + "address": "https://example.com/hooks/checkmate" + } + }, + "SlackNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["slack"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "#alerts", + "type": "slack", + "address": "https://hooks.slack.com/services/T000/B000/XXXX" + } + }, + "DiscordNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["discord"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "#status", + "type": "discord", + "address": "https://discord.com/api/webhooks/123/abc" + } + }, + "PagerDutyNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["pager_duty"] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "homeserverUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "roomId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "PagerDuty primary", + "type": "pager_duty", + "address": "R01XXXXXXXXXXXXXXXXXXXXXXX" + } + }, + "MatrixNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["matrix"] + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "homeserverUrl": { + "type": "string", + "format": "uri" + }, + "roomId": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "homeserverUrl", "roomId", "accessToken"], + "example": { + "notificationName": "Matrix room", + "type": "matrix", + "homeserverUrl": "https://matrix.example.com", + "roomId": "!abc123:example.com", + "accessToken": "syt_xxx" + } + }, + "TeamsNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["teams"] + }, + "address": { + "type": "string", + "format": "uri" + } + }, + "required": ["notificationName", "type", "address"], + "example": { + "notificationName": "Teams ops channel", + "type": "teams", + "address": "https://outlook.office.com/webhook/..." + } + }, + "TelegramNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["telegram"] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "address", "accessToken"], + "example": { + "notificationName": "Telegram bot", + "type": "telegram", + "address": "-1001234567890", + "accessToken": "123456:ABC-DEF" + } + }, + "PushoverNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["pushover"] + }, + "address": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "address", "accessToken"], + "example": { + "notificationName": "Pushover personal", + "type": "pushover", + "address": "u1234567890abcdef", + "accessToken": "a1234567890abcdef" + } + }, + "TwilioNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["twilio"] + }, + "accountSid": { + "type": "string", + "minLength": 1 + }, + "accessToken": { + "type": "string", + "minLength": 1 + }, + "phone": { + "type": "string", + "minLength": 1 + }, + "twilioPhoneNumber": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "accountSid", "accessToken", "phone", "twilioPhoneNumber"], + "example": { + "notificationName": "Twilio SMS", + "type": "twilio", + "accountSid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "accessToken": "your-auth-token", + "phone": "+15551234567", + "twilioPhoneNumber": "+15557654321" + } + }, + "NtfyNotification": { + "type": "object", + "properties": { + "notificationName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "enum": ["ntfy"] + }, + "address": { + "type": "string", + "format": "uri" + }, + "topic": { + "type": "string", + "minLength": 1 + } + }, + "required": ["notificationName", "type", "address", "topic"], + "example": { + "notificationName": "ntfy topic", + "type": "ntfy", + "address": "https://ntfy.sh", + "topic": "checkmate-alerts" + } + } + }, + "parameters": {} + }, + "paths": { + "/auth/register": { + "post": { + "tags": ["auth"], + "summary": "Register a new user (first user becomes superadmin)", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "profileImage": { + "type": "string", + "format": "binary" + }, + "inviteToken": { + "type": "string", + "default": "" + } + }, + "required": ["firstName", "lastName", "email", "password", "profileImage"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/AuthPayload" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/login": { + "post": { + "tags": ["auth"], + "summary": "Log in", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 1 + } + }, + "required": ["email", "password"] + }, + "example": { + "email": "ada@example.com", + "password": "S3cure!Passw0rd" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/AuthPayload" + } + }, + "required": ["success", "msg", "data"] + }, + "example": { + "success": true, + "msg": "OK", + "data": { + "user": { + "_id": "65f1c2a4d8b9e0123456789a", + "firstName": "Ada", + "lastName": "Lovelace", + "email": "ada@example.com", + "role": ["admin"], + "teamId": "65f1c2a4d8b9e01234567890", + "createdAt": "2026-04-01T10:00:00.000Z", + "updatedAt": "2026-04-15T14:30:00.000Z" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + } + } + }, + "401": { + "description": "Invalid credentials", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/recovery/request": { + "post": { + "tags": ["auth"], + "summary": "Request a password recovery email", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["email"] + }, + "example": { + "email": "ada@example.com" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/recovery/validate": { + "post": { + "tags": ["auth"], + "summary": "Validate a password recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recoveryToken": { + "type": "string", + "minLength": 1 + } + }, + "required": ["recoveryToken"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/recovery/reset": { + "post": { + "tags": ["auth"], + "summary": "Reset password using a recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recoveryToken": { + "type": "string", + "minLength": 1 + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "confirm": { + "type": "string" + } + }, + "required": ["recoveryToken", "password"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users/superadmin": { + "get": { + "tags": ["auth"], + "summary": "Check whether a superadmin user exists", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "superAdminExists": { + "type": "boolean" + } + }, + "required": ["superAdminExists"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users": { + "get": { + "tags": ["auth"], + "summary": "List all users (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "post": { + "tags": ["auth"], + "summary": "Create a new user (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + }, + "profileImage": { + "type": "string", + "format": "binary" + } + }, + "required": ["firstName", "lastName", "email", "password", "role", "profileImage"] + } + } + } + }, + "responses": { + "201": { + "description": "User created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users/{userId}": { + "get": { + "tags": ["auth"], + "summary": "Get a user by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "404": { + "description": "User not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["auth"], + "summary": "Edit a user (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + } + }, + "required": ["firstName", "lastName", "role"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["auth"], + "summary": "Delete a user (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/users/{userId}/password": { + "patch": { + "tags": ["auth"], + "summary": "Change a user's password (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + } + }, + "required": ["password"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/auth/user": { + "patch": { + "tags": ["auth"], + "summary": "Edit the currently authenticated user", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "lastName": { + "type": "string", + "maxLength": 50, + "pattern": "^(?=.*[\\p{L}\\p{Sc}])[\\p{L}\\p{Sc}\\s'\\-().]+$/u" + }, + "email": { + "type": "string", + "format": "email" + }, + "profilePicture": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "newPassword": { + "type": "string", + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "profileImage": { + "type": "string", + "format": "binary" + } + }, + "required": ["profileImage"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["auth"], + "summary": "Delete the currently authenticated user", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/checks/team/summary": { + "get": { + "tags": ["checks"], + "summary": "Aggregate check summary for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/checks/team": { + "get": { + "tags": ["checks"], + "summary": "List checks across the team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["all", "up", "down", "resolve"] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["checks"], + "summary": "Delete all checks for the team (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "deletedCount": { + "type": "number" + } + }, + "required": ["deletedCount"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/checks/{monitorId}": { + "get": { + "tags": ["checks"], + "summary": "List checks for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["all", "up", "down", "resolve"] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["checks"], + "summary": "Delete checks for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/diagnostic/system": { + "get": { + "tags": ["diagnostic"], + "summary": "Get system diagnostics (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/geo-checks/{monitorId}": { + "get": { + "tags": ["geo-checks"], + "summary": "Get geo check results for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["all", "up", "down", "resolve"] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "ack", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/team": { + "get": { + "tags": ["incidents"], + "summary": "List incidents for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": true, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"], + "default": "all" + }, + "required": false, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "monitorId", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["manual", "automatic"] + }, + "required": false, + "name": "resolutionType", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Incident" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/team/summary": { + "get": { + "tags": ["incidents"], + "summary": "Incident summary for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": false, + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/IncidentSummary" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/{incidentId}": { + "get": { + "tags": ["incidents"], + "summary": "Get an incident by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "incidentId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "incident": { + "$ref": "#/components/schemas/Incident" + }, + "monitor": { + "nullable": true + }, + "user": { + "nullable": true + } + }, + "required": ["incident"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/incidents/{incidentId}/resolve": { + "put": { + "tags": ["incidents"], + "summary": "Manually resolve an incident (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "incidentId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/invite/send": { + "post": { + "tags": ["invite"], + "summary": "Send an invite email (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + }, + "teamId": { + "type": "string", + "minLength": 1 + } + }, + "required": ["email", "role", "teamId"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/invite/verify": { + "post": { + "tags": ["invite"], + "summary": "Verify an invite token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "token": { + "type": "string", + "minLength": 1 + } + }, + "required": ["token"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "role": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["email"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/invite": { + "post": { + "tags": ["invite"], + "summary": "Create an invite token (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "role": { + "type": "array", + "items": { + "type": "string", + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + }, + "teamId": { + "type": "string", + "minLength": 1 + } + }, + "required": ["email", "role", "teamId"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + }, + "required": ["token"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/logs": { + "get": { + "tags": ["logs"], + "summary": "Get application logs (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window": { + "post": { + "tags": ["maintenance-window"], + "summary": "Create one or more maintenance windows", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitors": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "active": { + "type": "boolean" + }, + "duration": { + "type": "number", + "minimum": 1 + }, + "durationUnit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days"] + }, + "start": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "end": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "repeat": { + "type": "number", + "minimum": 0 + }, + "expiry": { + "type": "string", + "nullable": true, + "format": "date-time" + } + }, + "required": ["monitors", "name", "duration", "durationUnit", "start", "end", "repeat"], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window/team": { + "get": { + "tags": ["maintenance-window"], + "summary": "List maintenance windows for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "active", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "field", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": false, + "name": "order", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window/monitor/{monitorId}": { + "get": { + "tags": ["maintenance-window"], + "summary": "List maintenance windows for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/maintenance-window/{id}": { + "get": { + "tags": ["maintenance-window"], + "summary": "Get a maintenance window by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["maintenance-window"], + "summary": "Edit a maintenance window", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "repeat": { + "type": "number" + }, + "start": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "end": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "expiry": { + "type": "string", + "nullable": true, + "format": "date-time" + }, + "monitors": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "duration": { + "type": "number" + }, + "durationUnit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days"] + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["maintenance-window"], + "summary": "Delete a maintenance window", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/team": { + "get": { + "tags": ["monitors"], + "summary": "List monitors for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + } + } + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "required": false, + "name": "tags", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Monitor" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/team/with-checks": { + "get": { + "tags": ["monitors"], + "summary": "List team monitors with their most recent checks (paginated)", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "rowsPerPage", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "required": false, + "name": "filter", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "field", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": false, + "name": "order", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + } + } + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "required": false, + "name": "tags", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "explain", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/team/groups": { + "get": { + "tags": ["monitors"], + "summary": "List monitor groups for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/uptime/details/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get uptime details for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": true, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "normalize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/hardware/details/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get hardware metrics detail for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/pagespeed/details/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get PageSpeed detail for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/{monitorId}/geo-checks": { + "get": { + "tags": ["monitors"], + "summary": "Get geo check results for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["asc", "desc"] + }, + "required": false, + "name": "sortOrder", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": ["recent", "hour", "day", "week", "month", "all"] + }, + "required": false, + "name": "dateRange", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "numToDisplay", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "normalize", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + } + ] + }, + "required": false, + "name": "continent", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/pause/{monitorId}": { + "post": { + "tags": ["monitors"], + "summary": "Toggle pause state for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/certificate/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get SSL certificate info for a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "certificateDate": { + "type": "string" + } + }, + "required": ["certificateDate"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/notifications": { + "patch": { + "tags": ["monitors"], + "summary": "Bulk update notifications across monitors", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitorIds": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 100 + }, + "notificationIds": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 100 + }, + "action": { + "type": "string", + "enum": ["add", "remove", "set"] + } + }, + "required": ["monitorIds", "notificationIds", "action"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "modifiedCount": { + "type": "number" + } + }, + "required": ["modifiedCount"] + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors": { + "post": { + "tags": ["monitors"], + "summary": "Create a new monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "url": { + "type": "string", + "minLength": 1 + }, + "ignoreTlsErrors": { + "type": "boolean", + "default": false + }, + "useAdvancedMatching": { + "type": "boolean", + "default": false + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "number" + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "customUpCodes": { + "type": "array", + "items": { + "type": "number" + }, + "default": [] + }, + "secret": { + "type": "string" + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ], + "default": "" + }, + "strategy": { + "type": "string", + "enum": ["desktop", "mobile"] + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + } + }, + "required": ["name", "type", "url"] + } + } + } + }, + "responses": { + "200": { + "description": "Monitor created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["monitors"], + "summary": "Delete every monitor (superadmin only)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/demo": { + "post": { + "tags": ["monitors"], + "summary": "Insert preconfigured demo monitors", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "number" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/export/json": { + "get": { + "tags": ["monitors"], + "summary": "Export all team monitors as JSON", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/import/json": { + "post": { + "tags": ["monitors"], + "summary": "Import monitors from JSON", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "status": { + "type": "string", + "enum": ["up", "down", "paused", "initializing", "maintenance", "breached"], + "default": "initializing" + }, + "statusWindow": { + "type": "array", + "items": { + "type": "boolean" + }, + "default": [] + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "ignoreTlsErrors": { + "type": "boolean", + "default": false + }, + "useAdvancedMatching": { + "type": "boolean", + "default": false + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "url": { + "type": "string", + "minLength": 1 + }, + "port": { + "type": "number" + }, + "isActive": { + "type": "boolean", + "default": true + }, + "interval": { + "type": "number", + "default": 60000 + }, + "uptimePercentage": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "customUpCodes": { + "type": "array", + "items": { + "type": "number" + }, + "default": [] + }, + "secret": { + "type": "string" + }, + "cpuAlertThreshold": { + "type": "number", + "default": 100 + }, + "cpuAlertCounter": { + "type": "number", + "default": 5 + }, + "memoryAlertThreshold": { + "type": "number", + "default": 100 + }, + "memoryAlertCounter": { + "type": "number", + "default": 5 + }, + "diskAlertThreshold": { + "type": "number", + "default": 100 + }, + "diskAlertCounter": { + "type": "number", + "default": 5 + }, + "tempAlertThreshold": { + "type": "number", + "default": 100 + }, + "tempAlertCounter": { + "type": "number", + "default": 5 + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ], + "default": "" + }, + "strategy": { + "type": "string", + "enum": ["desktop", "mobile"] + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + } + ], + "default": null + }, + "geoCheckEnabled": { + "type": "boolean", + "default": false + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + "default": [] + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000, + "default": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": ["name", "type", "url"] + }, + "minItems": 1 + } + }, + "required": ["monitors"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "imported": { + "type": "number" + } + }, + "required": ["imported"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/games": { + "get": { + "tags": ["monitors"], + "summary": "List supported game-server types", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/monitors/{monitorId}": { + "get": { + "tags": ["monitors"], + "summary": "Get a monitor by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["monitors"], + "summary": "Edit a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] + }, + "url": { + "type": "string" + }, + "statusWindowSize": { + "type": "number", + "minimum": 1, + "maximum": 20, + "default": 5 + }, + "statusWindowThreshold": { + "type": "number", + "minimum": 1, + "maximum": 100, + "default": 60 + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "interval": { + "type": "number" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "customUpCodes": { + "type": "array", + "items": { + "type": "number" + } + }, + "secret": { + "type": "string" + }, + "ignoreTlsErrors": { + "type": "boolean" + }, + "useAdvancedMatching": { + "type": "boolean" + }, + "jsonPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "expectedValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "matchMethod": { + "anyOf": [ + { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "port": { + "type": "number", + "minimum": 1, + "maximum": 65535 + }, + "cpuAlertThreshold": { + "type": "number" + }, + "memoryAlertThreshold": { + "type": "number" + }, + "diskAlertThreshold": { + "type": "number" + }, + "tempAlertThreshold": { + "type": "number" + }, + "gameId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "grpcServiceName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "strategy": { + "type": "string", + "enum": ["desktop", "mobile"] + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + } + }, + "group": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "nullable": true + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "geoCheckEnabled": { + "type": "boolean" + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + } + }, + "geoCheckInterval": { + "type": "number", + "minimum": 300000 + }, + "dnsServer": { + "type": "string", + "minLength": 1 + }, + "dnsRecordType": { + "type": "string", + "enum": ["A", "AAAA", "CNAME", "MX", "TXT", "NS"] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["monitors"], + "summary": "Delete a monitor", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "monitorId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Monitor" + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications": { + "post": { + "tags": ["notifications"], + "summary": "Create a notification channel", + "description": "Create a notification channel of any supported type. The `type` field discriminates the body shape.", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/test/all": { + "post": { + "tags": ["notifications"], + "summary": "Send a test alert through every notification channel for the team", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "monitorId": { + "type": "string", + "minLength": 1 + } + }, + "required": ["monitorId"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/test": { + "post": { + "tags": ["notifications"], + "summary": "Send a test alert through a single notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": ["success"], + "additionalProperties": { + "nullable": true + } + } + }, + "required": ["success", "msg", "data"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/team": { + "get": { + "tags": ["notifications"], + "summary": "List notification channels for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/notifications/{id}": { + "get": { + "tags": ["notifications"], + "summary": "Get a notification channel by id", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["notifications"], + "summary": "Delete a notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["notifications"], + "summary": "Edit a notification channel", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationChannelBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/jobs": { + "get": { + "tags": ["queue"], + "summary": "List queued monitor jobs (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/metrics": { + "get": { + "tags": ["queue"], + "summary": "Get queue runtime metrics (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/all-metrics": { + "get": { + "tags": ["queue"], + "summary": "Get queue metrics across all monitors (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/queue/flush": { + "post": { + "tags": ["queue"], + "summary": "Flush the monitor job queue (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/settings": { + "get": { + "tags": ["settings"], + "summary": "Get application settings", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "patch": { + "tags": ["settings"], + "summary": "Update application settings (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "checkTTL": { + "type": "integer", + "minimum": 1, + "maximum": 366 + }, + "systemEmailPort": { + "type": "number", + "nullable": true + }, + "pagespeedApiKey": { + "type": "string", + "nullable": true + }, + "language": { + "type": "string" + }, + "timezone": { + "type": "string" + }, + "systemEmailHost": { + "type": "string", + "nullable": true + }, + "systemEmailAddress": { + "type": "string", + "nullable": true + }, + "systemEmailDisplayName": { + "type": "string", + "nullable": true, + "maxLength": 100 + }, + "systemEmailPassword": { + "type": "string", + "nullable": true + }, + "systemEmailUser": { + "type": "string", + "nullable": true + }, + "systemEmailConnectionHost": { + "type": "string", + "nullable": true + }, + "systemEmailTLSServername": { + "type": "string", + "nullable": true + }, + "showURL": { + "type": "boolean" + }, + "systemEmailSecure": { + "type": "boolean" + }, + "systemEmailPool": { + "type": "boolean" + }, + "systemEmailIgnoreTLS": { + "type": "boolean" + }, + "systemEmailRequireTLS": { + "type": "boolean" + }, + "systemEmailRejectUnauthorized": { + "type": "boolean" + }, + "globalThresholds": { + "type": "object", + "properties": { + "cpu": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "memory": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "disk": { + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "temperature": { + "type": "number", + "minimum": 1, + "maximum": 150 + } + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/settings/test-email": { + "post": { + "tags": ["settings"], + "summary": "Send a test email using current SMTP settings (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "minLength": 1 + }, + "systemEmailHost": { + "type": "string" + }, + "systemEmailPort": { + "type": "number" + }, + "systemEmailSecure": { + "type": "boolean" + }, + "systemEmailPool": { + "type": "boolean" + }, + "systemEmailAddress": { + "type": "string" + }, + "systemEmailDisplayName": { + "type": "string" + }, + "systemEmailPassword": { + "type": "string" + }, + "systemEmailUser": { + "type": "string" + }, + "systemEmailConnectionHost": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + "systemEmailIgnoreTLS": { + "type": "boolean" + }, + "systemEmailRequireTLS": { + "type": "boolean" + }, + "systemEmailRejectUnauthorized": { + "type": "boolean" + }, + "systemEmailTLSServername": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] + } + ] + } + }, + "required": ["to"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/team": { + "get": { + "tags": ["status-page"], + "summary": "List status pages for the caller's team", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page": { + "post": { + "tags": ["status-page"], + "summary": "Create a status page (logo upload optional)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "companyName": { + "type": "string", + "minLength": 1 + }, + "url": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "customDomain": { + "anyOf": [ + { + "type": "string", + "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" + }, + { + "nullable": true + } + ] + }, + "timezone": { + "type": "string" + }, + "color": { + "type": "string" + }, + "monitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + }, + "minItems": 1 + }, + "subMonitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + } + }, + "deleteSubmonitors": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "showCharts": { + "type": "boolean" + }, + "showUptimePercentage": { + "type": "boolean" + }, + "showAdminLoginLink": { + "type": "boolean" + }, + "showInfrastructure": { + "type": "boolean" + }, + "removeLogo": { + "anyOf": [ + { + "type": "string", + "enum": ["true"] + }, + { + "type": "string", + "enum": ["false"] + } + ] + }, + "theme": { + "type": "string", + "enum": ["refined", "modern", "bold", "editorial"] + }, + "themeMode": { + "type": "string", + "enum": ["auto", "light", "dark"] + }, + "logo": { + "type": "string", + "format": "binary" + } + }, + "required": ["type", "companyName", "url", "monitors", "isPublished", "showUptimePercentage", "logo"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/{id}": { + "put": { + "tags": ["status-page"], + "summary": "Update a status page (logo upload optional)", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "companyName": { + "type": "string", + "minLength": 1 + }, + "url": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "customDomain": { + "anyOf": [ + { + "type": "string", + "pattern": "^(?=.{1,253}$)([a-zA-Z0-9_](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}$" + }, + { + "nullable": true + } + ] + }, + "timezone": { + "type": "string" + }, + "color": { + "type": "string" + }, + "monitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + }, + "minItems": 1 + }, + "subMonitors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-fA-F]{24}$" + } + }, + "deleteSubmonitors": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "showCharts": { + "type": "boolean" + }, + "showUptimePercentage": { + "type": "boolean" + }, + "showAdminLoginLink": { + "type": "boolean" + }, + "showInfrastructure": { + "type": "boolean" + }, + "removeLogo": { + "anyOf": [ + { + "type": "string", + "enum": ["true"] + }, + { + "type": "string", + "enum": ["false"] + } + ] + }, + "theme": { + "type": "string", + "enum": ["refined", "modern", "bold", "editorial"] + }, + "themeMode": { + "type": "string", + "enum": ["auto", "light", "dark"] + }, + "logo": { + "type": "string", + "format": "binary" + } + }, + "required": ["type", "companyName", "url", "monitors", "isPublished", "showUptimePercentage", "logo"] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + }, + "delete": { + "tags": ["status-page"], + "summary": "Delete a status page", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/resolve": { + "get": { + "tags": ["status-page"], + "summary": "Resolve a published status page by custom domain", + "parameters": [ + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "required": true, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "timeFrame", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "domain", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "404": { + "description": "Status page not found" + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + }, + "/status-page/{url}": { + "get": { + "tags": ["status-page"], + "summary": "Get a public status page by its URL slug", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "url", + "in": "path" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["uptime", "infrastructure"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["uptime", "infrastructure"] + } + } + ] + }, + "required": true, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "timeFrame", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "404": { + "description": "Status page not found" + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + } + } + } + } + } } From 2bf3c08f3ce64e8bf9840be87942445e4c11396e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 10 Jun 2026 09:30:14 -0700 Subject: [PATCH 04/17] reorg jobqueue to worker --- server/src/api/controllers/queueController.ts | 18 +++++------ server/src/config/controllers.ts | 2 +- server/src/config/services.ts | 22 ++++++------- .../src/domain/incidents/incident.service.ts | 2 +- server/src/domain/monitors/monitor.service.ts | 32 +++++++++---------- .../notification.message-builder.ts | 2 +- .../notifications/notification.service.ts | 2 +- server/src/domain/users/user.service.ts | 12 +++---- server/src/service/emailService.ts | 2 +- server/src/shutdown.ts | 2 +- .../worker.helper.ts} | 4 +-- .../worker.interface.ts} | 20 ++++++------ .../worker.less-simple.ts} | 20 ++++++------ .../worker.super-simple.ts} | 14 ++++---- 14 files changed, 77 insertions(+), 77 deletions(-) rename server/src/{service/job-queues/job-queue.helper.ts => worker/worker.helper.ts} (99%) rename server/src/{service/job-queues/job-queue.interface.ts => worker/worker.interface.ts} (78%) rename server/src/{service/job-queues/job-queue.less-simple.ts => worker/worker.less-simple.ts} (95%) rename server/src/{service/job-queues/job-queue.sumper-simple.ts => worker/worker.super-simple.ts} (95%) diff --git a/server/src/api/controllers/queueController.ts b/server/src/api/controllers/queueController.ts index 0561ba6f0..0d3e2d1d0 100755 --- a/server/src/api/controllers/queueController.ts +++ b/server/src/api/controllers/queueController.ts @@ -1,4 +1,4 @@ -import { IJobQueue } from "@/service/job-queues/job-queue.interface.js"; +import { IWorker } from "@/worker/worker.interface.js"; import { getQueueJobsQueryValidation } from "@/api/validation/queueValidation.js"; import { Request, Response, NextFunction } from "express"; @@ -13,9 +13,9 @@ export interface IJobQueueController { class JobQueueController implements IJobQueueController { static SERVICE_NAME = SERVICE_NAME; - private jobQueue: IJobQueue; - constructor(jobQueue: IJobQueue) { - this.jobQueue = jobQueue; + private worker: IWorker; + constructor(worker: IWorker) { + this.worker = worker; } get serviceName() { @@ -24,7 +24,7 @@ class JobQueueController implements IJobQueueController { getMetrics = async (req: Request, res: Response, next: NextFunction) => { try { - const metrics = await this.jobQueue.getMetrics(); + const metrics = await this.worker.getMetrics(); res.status(200).json({ success: true, msg: "Queue metrics fetched successfully", @@ -38,7 +38,7 @@ class JobQueueController implements IJobQueueController { getJobs = async (req: Request, res: Response, next: NextFunction) => { try { const pagination = getQueueJobsQueryValidation.parse(req.query); - const { jobs, count } = await this.jobQueue.getJobs(pagination); + const { jobs, count } = await this.worker.getJobs(pagination); return res.status(200).json({ success: true, msg: "Queue jobs fetched successfully", @@ -52,8 +52,8 @@ class JobQueueController implements IJobQueueController { getAllMetrics = async (req: Request, res: Response, next: NextFunction) => { try { const pagination = getQueueJobsQueryValidation.parse(req.query); - const { jobs, count } = await this.jobQueue.getJobs(pagination); - const metrics = await this.jobQueue.getMetrics(); + const { jobs, count } = await this.worker.getJobs(pagination); + const metrics = await this.worker.getMetrics(); return res.status(200).json({ success: true, msg: "Queue metrics fetched successfully", @@ -66,7 +66,7 @@ class JobQueueController implements IJobQueueController { flushQueue = async (req: Request, res: Response, next: NextFunction) => { try { - const result = await this.jobQueue.flushQueues(); + const result = await this.worker.flushQueues(); return res.status(200).json({ success: true, msg: "Queue flushed successfully", diff --git a/server/src/config/controllers.ts b/server/src/config/controllers.ts index 09dc6651b..cdbb876b8 100644 --- a/server/src/config/controllers.ts +++ b/server/src/config/controllers.ts @@ -39,7 +39,7 @@ export const initializeControllers = (services: InitializedServices): Initialize geoCheckController: new GeoCheckController(services.geoChecksService), inviteController: new InviteController(services.inviteService), maintenanceWindowController: new MaintenanceWindowController(services.maintenanceWindowService), - queueController: new QueueController(services.jobQueue), + queueController: new QueueController(services.worker), logController: new LogController(services.logger), statusPageController: new StatusPageController(services.statusPageService, services.monitorsRepository, services.settingsService), notificationController: new NotificationController(services.notificationsService, services.monitorsRepository), diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 9aed9c968..dcd6cbb98 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -28,10 +28,10 @@ import { IUserService, UserService } from "@/domain/users/user.service.js"; import { BufferService, IBufferService } from "@/service/bufferService.js"; import { EmailService, IEmailService } from "@/service/emailService.js"; import { GlobalPingService } from "@/service/globalPingService.js"; -import { QueueHelper } from "@/service/job-queues/job-queue.helper.js"; -import { IJobQueue } from "@/service/job-queues/job-queue.interface.js"; -import { LessSimpleQueue } from "@/service/job-queues/job-queue.less-simple.js"; -import { SuperSimpleQueue } from "@/service/job-queues/job-queue.sumper-simple.js"; +import { QueueHelper } from "@/worker/worker.helper.js"; +import { IWorker } from "@/worker/worker.interface.js"; +import { LessSimpleWorker } from "@/worker/worker.less-simple.js"; +import { SuperSimpleQueue } from "@/worker/worker.super-simple.js"; import { INetworkService, NetworkService } from "@/service/networkService.js"; import { IStatusService, StatusService } from "@/service/statusService.js"; @@ -110,7 +110,7 @@ export type InitializedServices = { emailService: IEmailService; bufferService: IBufferService; statusService: IStatusService; - jobQueue: IJobQueue; + worker: IWorker; userService: IUserService; checkService: ICheckService; geoChecksService: IGeoChecksService; @@ -337,13 +337,13 @@ export const initializeServices = async ({ }); } - let jobQueue: IJobQueue; + let worker: IWorker; switch (envSettings.queueType) { case "lessSimpleQueue": - jobQueue = await LessSimpleQueue.create(logger, queueHelper, monitorsRepository, queueWorkersRepository, envSettings, queueMode); + worker = await LessSimpleWorker.create(logger, queueHelper, monitorsRepository, queueWorkersRepository, envSettings, queueMode); break; default: - jobQueue = await SuperSimpleQueue.create(logger, queueHelper, monitorsRepository); + worker = await SuperSimpleQueue.create(logger, queueHelper, monitorsRepository); } // Business services @@ -353,7 +353,7 @@ export const initializeServices = async ({ settingsService, logger, jwt, - jobQueue, + worker, monitorsRepository, usersRepository, invitesRepository, @@ -373,7 +373,7 @@ export const initializeServices = async ({ maintenanceWindowsRepository, }); const monitorService = new MonitorService({ - jobQueue, + worker, logger, games, monitorsRepository, @@ -393,7 +393,7 @@ export const initializeServices = async ({ emailService, bufferService, statusService, - jobQueue, + worker, userService, checkService, geoChecksService, diff --git a/server/src/domain/incidents/incident.service.ts b/server/src/domain/incidents/incident.service.ts index d588086b2..311fad567 100644 --- a/server/src/domain/incidents/incident.service.ts +++ b/server/src/domain/incidents/incident.service.ts @@ -8,7 +8,7 @@ import type { IMonitorsRepository } from "@/domain/monitors/monitor.repository.i import type { IUsersRepository } from "@/domain/users/user.repository.interface.js"; import type { Incident, IncidentSummary } from "@/domain/incidents/incident.type.js"; import type { User } from "@/domain/users/user.type.js"; -import type { MonitorActionDecision } from "@/service/job-queues/job-queue.helper.js"; +import type { MonitorActionDecision } from "@/worker/worker.helper.js"; import type { INotificationMessageBuilder } from "@/domain/notifications/notification.message-builder.js"; import type { ILogger } from "@/utils/logger.js"; diff --git a/server/src/domain/monitors/monitor.service.ts b/server/src/domain/monitors/monitor.service.ts index eaa40dcfe..a72d5a50d 100644 --- a/server/src/domain/monitors/monitor.service.ts +++ b/server/src/domain/monitors/monitor.service.ts @@ -21,7 +21,7 @@ import type { IStatusPagesRepository } from "@/domain/status-pages/status-page-r import demoMonitorsData from "@/utils/demoMonitors.json" with { type: "json" }; import { AppError } from "@/utils/AppError.js"; import type { ImportedMonitor } from "@/api/validation/monitorValidation.js"; -import { IJobQueue } from "@/service/job-queues/job-queue.interface.js"; +import { IWorker } from "@/worker/worker.interface.js"; import { ILogger } from "@/utils/logger.js"; const SERVICE_NAME = "MonitorService"; @@ -94,7 +94,7 @@ export interface IMonitorService { export class MonitorService implements IMonitorService { static SERVICE_NAME = SERVICE_NAME; - private jobQueue: IJobQueue; + private worker: IWorker; private logger: ILogger; private games: GamesMap; private monitorsRepository: IMonitorsRepository; @@ -105,7 +105,7 @@ export class MonitorService implements IMonitorService { private incidentsRepository: IIncidentsRepository; constructor({ - jobQueue, + worker, logger, games, monitorsRepository, @@ -115,7 +115,7 @@ export class MonitorService implements IMonitorService { statusPagesRepository, incidentsRepository, }: { - jobQueue: IJobQueue; + worker: IWorker; logger: ILogger; games: GamesMap; monitorsRepository: IMonitorsRepository; @@ -125,7 +125,7 @@ export class MonitorService implements IMonitorService { statusPagesRepository: IStatusPagesRepository; incidentsRepository: IIncidentsRepository; }) { - this.jobQueue = jobQueue; + this.worker = worker; this.logger = logger; this.games = games; this.monitorsRepository = monitorsRepository; @@ -171,7 +171,7 @@ export class MonitorService implements IMonitorService { throw new AppError({ message: "Failed to create monitor", status: 500, service: SERVICE_NAME, method: "createMonitor" }); } - this.jobQueue.addJob(monitor.id, monitor); + this.worker.addJob(monitor.id, monitor); }; createMonitors = async (monitors: Array): Promise => { @@ -180,7 +180,7 @@ export class MonitorService implements IMonitorService { throw new AppError({ message: "Failed to create monitors", status: 500, service: SERVICE_NAME, method: "createMonitors" }); } - await Promise.all(createdMonitors.map((monitor) => this.jobQueue.addJob(monitor.id, monitor))); + await Promise.all(createdMonitors.map((monitor) => this.worker.addJob(monitor.id, monitor))); return createdMonitors; }; @@ -196,7 +196,7 @@ export class MonitorService implements IMonitorService { })); const demoMonitors = await this.monitorsRepository.createMonitors(monitors as unknown as Monitor[]); - await Promise.all(demoMonitors.map((monitor) => this.jobQueue.addJob(monitor.id, monitor))); + await Promise.all(demoMonitors.map((monitor) => this.worker.addJob(monitor.id, monitor))); return demoMonitors; }; @@ -428,7 +428,7 @@ export class MonitorService implements IMonitorService { editMonitor = async ({ teamId, monitorId, body }: { teamId: string; monitorId: string; body: Partial }) => { const editedMonitor = await this.monitorsRepository.updateById(monitorId, teamId, body); - await this.jobQueue.updateJob(editedMonitor); + await this.worker.updateJob(editedMonitor); return editedMonitor; }; @@ -448,7 +448,7 @@ export class MonitorService implements IMonitorService { // If notifications were updated, we should update the jobs in the queue if (modifiedCount > 0) { const monitors = await this.monitorsRepository.findByIds(monitorIds); - await Promise.all(monitors.map((monitor) => this.jobQueue.updateJob(monitor))); + await Promise.all(monitors.map((monitor) => this.worker.updateJob(monitor))); } return modifiedCount; @@ -457,9 +457,9 @@ export class MonitorService implements IMonitorService { pauseMonitor = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise => { const monitor = await this.monitorsRepository.togglePauseById(monitorId, teamId); if (monitor.isActive) { - await this.jobQueue.resumeJob(monitor); + await this.worker.resumeJob(monitor); } else { - await this.jobQueue.pauseJob(monitor); + await this.worker.pauseJob(monitor); } return monitor; }; @@ -478,9 +478,9 @@ export class MonitorService implements IMonitorService { const results = await Promise.allSettled( monitors.map(async (monitor) => { if (monitor.isActive) { - await this.jobQueue.resumeJob(monitor); + await this.worker.resumeJob(monitor); } else { - await this.jobQueue.pauseJob(monitor); + await this.worker.pauseJob(monitor); } }) ); @@ -541,7 +541,7 @@ export class MonitorService implements IMonitorService { }); }); - await this.jobQueue.deleteJob(monitor); + await this.worker.deleteJob(monitor); return monitor; }; @@ -550,7 +550,7 @@ export class MonitorService implements IMonitorService { await Promise.all( monitors.map(async (monitor) => { try { - await this.jobQueue.deleteJob(monitor); + await this.worker.deleteJob(monitor); await this.checksRepository.deleteByMonitorId(monitor.id); await this.geoChecksRepository.deleteByMonitorId(monitor.id); await this.statusPagesRepository.removeMonitorFromStatusPages(monitor.id); diff --git a/server/src/domain/notifications/notification.message-builder.ts b/server/src/domain/notifications/notification.message-builder.ts index 9d3714ae7..b11d99a94 100644 --- a/server/src/domain/notifications/notification.message-builder.ts +++ b/server/src/domain/notifications/notification.message-builder.ts @@ -1,6 +1,6 @@ import type { Monitor } from "@/domain/monitors/monitor.types.js"; import type { HardwareStatusPayload, MonitorStatusResponse } from "@/types/network.js"; -import type { MonitorActionDecision } from "@/service/job-queues/job-queue.helper.js"; +import type { MonitorActionDecision } from "@/worker/worker.helper.js"; import type { NotificationMessage, NotificationType, diff --git a/server/src/domain/notifications/notification.service.ts b/server/src/domain/notifications/notification.service.ts index ea6a9265c..7fc6e3543 100644 --- a/server/src/domain/notifications/notification.service.ts +++ b/server/src/domain/notifications/notification.service.ts @@ -5,7 +5,7 @@ import type { NotificationMessage } from "@/domain/notifications/notification.ty import { IMonitorsRepository } from "@/domain/monitors/monitor.repository.interface.js"; import { INotificationsRepository } from "@/domain/notifications/notification.repository.interface.js"; import { INotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js"; -import type { MonitorActionDecision } from "@/service/job-queues/job-queue.helper.js"; +import type { MonitorActionDecision } from "@/worker/worker.helper.js"; import type { ISettingsService } from "@/domain/app-settings/app-settings.service.js"; import { ILogger } from "@/utils/logger.js"; import type { INotificationMessageBuilder } from "@/domain/notifications/notification.message-builder.js"; diff --git a/server/src/domain/users/user.service.ts b/server/src/domain/users/user.service.ts index 4edacfbc5..33d95f166 100644 --- a/server/src/domain/users/user.service.ts +++ b/server/src/domain/users/user.service.ts @@ -8,12 +8,12 @@ import type { User } from "@/domain/users/user.type.js"; import { canManageRole, type UserRole } from "@/domain/users/user.type.js"; import bcrypt from "bcryptjs"; import { AppError } from "@/utils/AppError.js"; -import { IJobQueue } from "@/service/job-queues/job-queue.interface.js"; import { IEmailService } from "@/service/emailService.js"; import { EnvConfig, ISettingsService } from "@/domain/app-settings/app-settings.service.js"; import { ILogger } from "@/utils/logger.js"; import jwt from "jsonwebtoken"; import crypto from "crypto"; +import { IWorker } from "@/worker/worker.interface.js"; type CryptoType = typeof crypto; type JwtType = typeof jwt; const SERVICE_NAME = "userService"; @@ -53,7 +53,7 @@ export class UserService implements IUserService { private settingsService: ISettingsService; private logger: ILogger; private jwt: JwtType; - private jobQueue: IJobQueue; + private worker: IWorker; private crypto: CryptoType; private monitorsRepository: IMonitorsRepository; private usersRepository: IUsersRepository; @@ -68,7 +68,7 @@ export class UserService implements IUserService { settingsService, logger, jwt, - jobQueue, + worker, monitorsRepository, usersRepository, invitesRepository, @@ -81,7 +81,7 @@ export class UserService implements IUserService { settingsService: ISettingsService; logger: ILogger; jwt: JwtType; - jobQueue: IJobQueue; + worker: IWorker; monitorsRepository: IMonitorsRepository; usersRepository: IUsersRepository; invitesRepository: IInvitesRepository; @@ -93,7 +93,7 @@ export class UserService implements IUserService { this.settingsService = settingsService; this.logger = logger; this.jwt = jwt; - this.jobQueue = jobQueue; + this.worker = worker; this.crypto = crypto; this.monitorsRepository = monitorsRepository; this.usersRepository = usersRepository; @@ -330,7 +330,7 @@ export class UserService implements IUserService { if (roles.includes("superadmin")) { const monitors = await this.monitorsRepository.findByTeamId(teamId, {}); if (monitors) { - await Promise.all(monitors.map((monitor) => this.jobQueue.deleteJob(monitor))); + await Promise.all(monitors.map((monitor) => this.worker.deleteJob(monitor))); } } // 6. Delete the user by id diff --git a/server/src/service/emailService.ts b/server/src/service/emailService.ts index ca153392d..c156cc18b 100755 --- a/server/src/service/emailService.ts +++ b/server/src/service/emailService.ts @@ -63,7 +63,7 @@ export class EmailService implements IEmailService { init = () => { const loadTemplate = (templateName: string) => { try { - const templatePath = this.path.join(__dirname, `../../templates/${templateName}.mjml`); + const templatePath = this.path.join(__dirname, `../templates/${templateName}.mjml`); const templateContent = this.fs.readFileSync(templatePath, "utf8"); return this.compile(templateContent); } catch (error: unknown) { diff --git a/server/src/shutdown.ts b/server/src/shutdown.ts index 3c1fc4ee7..38d1d5114 100644 --- a/server/src/shutdown.ts +++ b/server/src/shutdown.ts @@ -16,7 +16,7 @@ export const initShutdownListener = (server: Server | null, services: Initialize try { server?.close(); - await services.jobQueue.shutdown(); + await services.worker.shutdown(); await services.db.disconnect(); logger.info({ message: "Graceful shutdown complete" }); process.exit(0); diff --git a/server/src/service/job-queues/job-queue.helper.ts b/server/src/worker/worker.helper.ts similarity index 99% rename from server/src/service/job-queues/job-queue.helper.ts rename to server/src/worker/worker.helper.ts index 69ecb86c6..6840ff695 100644 --- a/server/src/service/job-queues/job-queue.helper.ts +++ b/server/src/worker/worker.helper.ts @@ -22,7 +22,7 @@ import { ITeamsRepository } from "@/domain/teams/team.repository.interface.js"; import { ILogger } from "@/utils/logger.js"; import { IBufferService } from "@/service/bufferService.js"; -export interface IQueueHelper { +export interface IWorkerHelper { readonly serviceName: string; getHeartbeatJob(): (monitor: Monitor) => Promise; getHeartbeatGeoJob(): (monitor: Monitor) => Promise; @@ -45,7 +45,7 @@ export interface MonitorActionDecision { }; } -export class QueueHelper implements IQueueHelper { +export class QueueHelper implements IWorkerHelper { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; diff --git a/server/src/service/job-queues/job-queue.interface.ts b/server/src/worker/worker.interface.ts similarity index 78% rename from server/src/service/job-queues/job-queue.interface.ts rename to server/src/worker/worker.interface.ts index 3e8267738..45dc94ca1 100644 --- a/server/src/service/job-queues/job-queue.interface.ts +++ b/server/src/worker/worker.interface.ts @@ -1,7 +1,7 @@ import { Monitor } from "@/domain/monitors/monitor.types.js"; import { QueueWorker } from "@/domain/queue-workers/queue-worker.type.js"; -export type QueueJobFailure = { +export type WorkerJobFailure = { monitorId: string | number; monitorUrl: string | null; monitorType: string | null; @@ -10,17 +10,17 @@ export type QueueJobFailure = { failReason: string | null; }; -export type QueueMetrics = { +export type WorkerMetrics = { jobs: number; activeJobs: number; failingJobs: number; - jobsWithFailures: QueueJobFailure[]; + jobsWithFailures: WorkerJobFailure[]; totalRuns: number; totalFailures: number; workers: QueueWorker[]; }; -export type QueueJobSummary = { +export type WorkerJobSummary = { monitorId: string | number; monitorUrl: string | null; monitorType: string | null; @@ -41,17 +41,17 @@ export type QueueJobSummary = { repeat: number | null; }; -export type QueueJobsPagination = { +export type WorkerJobsPagination = { page?: number; rowsPerPage?: number; }; -export type QueueJobsPage = { - jobs: QueueJobSummary[]; +export type WorkerJobsPage = { + jobs: WorkerJobSummary[]; count: number; }; -export interface IJobQueue { +export interface IWorker { readonly serviceName: string; init(): Promise; addJob(monitorId: string, monitor: Monitor): Promise; @@ -60,7 +60,7 @@ export interface IJobQueue { resumeJob(monitor: Monitor): Promise; updateJob(monitor: Monitor): Promise; shutdown(): Promise; - getMetrics(): Promise; - getJobs(pagination: QueueJobsPagination): Promise; + getMetrics(): Promise; + getJobs(pagination: WorkerJobsPagination): Promise; flushQueues(): Promise<{ success: boolean }>; } diff --git a/server/src/service/job-queues/job-queue.less-simple.ts b/server/src/worker/worker.less-simple.ts similarity index 95% rename from server/src/service/job-queues/job-queue.less-simple.ts rename to server/src/worker/worker.less-simple.ts index ad12341c1..bd4752c6e 100644 --- a/server/src/service/job-queues/job-queue.less-simple.ts +++ b/server/src/worker/worker.less-simple.ts @@ -2,9 +2,9 @@ import { IMonitorsRepository } from "@/domain/monitors/monitor.repository.interf import { IQueueWorkersRepository } from "@/domain/queue-workers/queue-worker.repository.interface.js"; import { ILogger } from "@/utils/logger.js"; import { MongoStore, Scheduler, IJob, AddJobInput } from "less-simple-scheduler"; -import { IQueueHelper } from "@/service/job-queues/job-queue.helper.js"; import { Monitor, supportsGeoCheck } from "@/domain/monitors/monitor.types.js"; -import { type QueueMetrics, type QueueJobsPagination, type QueueJobsPage, IJobQueue } from "@/service/job-queues/job-queue.interface.js"; +import { IWorkerHelper } from "@/worker/worker.helper.js"; +import { type WorkerMetrics, type WorkerJobsPagination, type WorkerJobsPage, IWorker } from "@/worker/worker.interface.js"; import { type QueueMode } from "@/domain/app-settings/app-settings.type.js"; import { EnvConfig } from "@/domain/app-settings/app-settings.service.js"; const SERVICE_NAME = "JobQueue"; @@ -17,11 +17,11 @@ type MonitorJob = Omit & { data: Monitor }; const MONITOR_TEMPLATES = new Set(["monitor-job", "geo-check-job"]); const isMonitorJob = (job: IJob): job is MonitorJob => MONITOR_TEMPLATES.has(job.template); -export class LessSimpleQueue implements IJobQueue { +export class LessSimpleWorker implements IWorker { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; - private helper: IQueueHelper; + private helper: IWorkerHelper; private monitorsRepository: IMonitorsRepository; private workersRepository: IQueueWorkersRepository; private readonly scheduler: Scheduler; @@ -30,7 +30,7 @@ export class LessSimpleQueue implements IJobQueue { constructor( logger: ILogger, - helper: IQueueHelper, + helper: IWorkerHelper, monitorsRepository: IMonitorsRepository, workersRepository: IQueueWorkersRepository, scheduler: Scheduler, @@ -46,7 +46,7 @@ export class LessSimpleQueue implements IJobQueue { } get serviceName() { - return LessSimpleQueue.SERVICE_NAME; + return LessSimpleWorker.SERVICE_NAME; } private registerListeners = () => { @@ -126,7 +126,7 @@ export class LessSimpleQueue implements IJobQueue { static async create( logger: ILogger, - helper: IQueueHelper, + helper: IWorkerHelper, monitorsRepository: IMonitorsRepository, workersRepository: IQueueWorkersRepository, envSettings: EnvConfig, @@ -139,7 +139,7 @@ export class LessSimpleQueue implements IJobQueue { concurrency: envSettings.queueMode === "primary" ? 0 : 50, processEvery: 100, }); - const instance = new LessSimpleQueue(logger, helper, monitorsRepository, workersRepository, scheduler, queueMode); + const instance = new LessSimpleWorker(logger, helper, monitorsRepository, workersRepository, scheduler, queueMode); await instance.init(queueMode); return instance; @@ -350,7 +350,7 @@ export class LessSimpleQueue implements IJobQueue { this.scheduler.stop(); }; - getMetrics = async (): Promise => { + getMetrics = async (): Promise => { const stats = await this.scheduler.getStats(); const jobsWithFailures = stats.jobsWithFailures.map((failure) => { @@ -379,7 +379,7 @@ export class LessSimpleQueue implements IJobQueue { }; }; - getJobs = async ({ page = 0, rowsPerPage = 0 }: QueueJobsPagination): Promise => { + getJobs = async ({ page = 0, rowsPerPage = 0 }: WorkerJobsPagination): Promise => { const listOptions = rowsPerPage > 0 ? { skip: Math.max(page, 0) * rowsPerPage, limit: rowsPerPage } : undefined; const [jobs, count] = await Promise.all([this.scheduler.getJobs(listOptions), this.scheduler.countJobs()]); const mapped = jobs.map((job) => { diff --git a/server/src/service/job-queues/job-queue.sumper-simple.ts b/server/src/worker/worker.super-simple.ts similarity index 95% rename from server/src/service/job-queues/job-queue.sumper-simple.ts rename to server/src/worker/worker.super-simple.ts index c53ca5860..86b9f152a 100644 --- a/server/src/service/job-queues/job-queue.sumper-simple.ts +++ b/server/src/worker/worker.super-simple.ts @@ -1,25 +1,25 @@ import { IMonitorsRepository } from "@/domain/monitors/monitor.repository.interface.js"; import { ILogger } from "@/utils/logger.js"; import Scheduler from "super-simple-scheduler"; -import { IQueueHelper } from "@/service/job-queues/job-queue.helper.js"; import { Monitor, MonitorType, supportsGeoCheck } from "@/domain/monitors/monitor.types.js"; import { hostname } from "node:os"; import { randomUUID } from "node:crypto"; import { QueueMode } from "@/domain/app-settings/app-settings.type.js"; -import { IJobQueue, type QueueJobSummary } from "@/service/job-queues/job-queue.interface.js"; +import { IWorker, WorkerJobSummary } from "@/worker/worker.interface.js"; +import { IWorkerHelper } from "@/worker/worker.helper.js"; const SERVICE_NAME = "JobQueue"; -export class SuperSimpleQueue implements IJobQueue { +export class SuperSimpleQueue implements IWorker { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; - private helper: IQueueHelper; + private helper: IWorkerHelper; private workerId: string; private monitorsRepository: IMonitorsRepository; private readonly scheduler: Scheduler; - constructor(logger: ILogger, helper: IQueueHelper, monitorsRepository: IMonitorsRepository, scheduler: Scheduler) { + constructor(logger: ILogger, helper: IWorkerHelper, monitorsRepository: IMonitorsRepository, scheduler: Scheduler) { this.logger = logger; this.helper = helper; this.monitorsRepository = monitorsRepository; @@ -100,7 +100,7 @@ export class SuperSimpleQueue implements IJobQueue { }); }; - static async create(logger: ILogger, helper: IQueueHelper, monitorsRepository: IMonitorsRepository) { + static async create(logger: ILogger, helper: IWorkerHelper, monitorsRepository: IMonitorsRepository) { const scheduler = new Scheduler({ concurrency: 50, // storeType: "mongo", @@ -341,7 +341,7 @@ export class SuperSimpleQueue implements IJobQueue { return metrics; }; - getJobs = async ({ page = 0, rowsPerPage = 0 }: { page?: number; rowsPerPage?: number }): Promise<{ jobs: QueueJobSummary[]; count: number }> => { + getJobs = async ({ page = 0, rowsPerPage = 0 }: { page?: number; rowsPerPage?: number }): Promise<{ jobs: WorkerJobSummary[]; count: number }> => { const jobs = await this.scheduler.getJobs(); const mapped = jobs.map((job) => { return { From c5f57eda62f7374bca2146ebe4286555f626c5c6 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 10 Jun 2026 09:30:24 -0700 Subject: [PATCH 05/17] update tests --- server/test/helpers/heartbeatTestHarness.ts | 2 +- server/test/integration/incidentLifecycle.test.ts | 2 +- server/test/unit/services/incidentService.test.ts | 2 +- server/test/unit/services/lessSimpleQueue.test.ts | 2 +- server/test/unit/services/monitorService.test.ts | 4 ++-- .../unit/services/notificationMessageBuilder.test.ts | 2 +- .../test/unit/services/notificationsService.test.ts | 2 +- server/test/unit/services/superSimpleQueue.test.ts | 2 +- .../unit/services/superSimpleQueueHelper.test.ts | 2 +- server/test/unit/services/userService.test.ts | 12 ++++++------ 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/server/test/helpers/heartbeatTestHarness.ts b/server/test/helpers/heartbeatTestHarness.ts index 46a6fc630..79dae92cd 100644 --- a/server/test/helpers/heartbeatTestHarness.ts +++ b/server/test/helpers/heartbeatTestHarness.ts @@ -1,5 +1,5 @@ import { jest } from "@jest/globals"; -import { QueueHelper } from "../../src/service/job-queues/job-queue.helper.ts"; +import { QueueHelper } from "../../src/worker/worker.helper.ts"; import { StatusService } from "../../src/service/statusService.ts"; import { IncidentService } from "../../src/domain/incidents/incident.service.ts"; import { InMemoryMonitorsRepository } from "./InMemoryMonitorsRepository.ts"; diff --git a/server/test/integration/incidentLifecycle.test.ts b/server/test/integration/incidentLifecycle.test.ts index 3f301113a..73b25d893 100644 --- a/server/test/integration/incidentLifecycle.test.ts +++ b/server/test/integration/incidentLifecycle.test.ts @@ -6,7 +6,7 @@ import type { IMonitorsRepository } from "../../src/domain/monitors/monitor.repo import type { IUsersRepository } from "../../src/domain/users/user.repository.interface.ts"; import type { INotificationMessageBuilder } from "../../src/domain/notifications/notification.message-builder.ts"; import type { Monitor } from "../../src/domain/monitors/monitor.types.ts"; -import type { MonitorActionDecision } from "../../src/service/job-queues/job-queue.helper.ts"; +import type { MonitorActionDecision } from "../../src/worker/worker.helper.ts"; // ── Helpers ────────────────────────────────────────────────────────────────── diff --git a/server/test/unit/services/incidentService.test.ts b/server/test/unit/services/incidentService.test.ts index f62f4ccf4..aa4fffce8 100644 --- a/server/test/unit/services/incidentService.test.ts +++ b/server/test/unit/services/incidentService.test.ts @@ -7,7 +7,7 @@ import type { IUsersRepository } from "../../../src/domain/users/user.repository import type { INotificationMessageBuilder } from "../../../src/domain/notifications/notification.message-builder.ts"; import type { Monitor } from "../../../src/domain/monitors/monitor.types.ts"; import type { Incident } from "../../../src/domain/incidents/incident.type.ts"; -import type { MonitorActionDecision } from "../../../src/service/job-queues/job-queue.helper.ts"; +import type { MonitorActionDecision } from "../../../src/worker/worker.helper.ts"; // ── Helpers ────────────────────────────────────────────────────────────────── diff --git a/server/test/unit/services/lessSimpleQueue.test.ts b/server/test/unit/services/lessSimpleQueue.test.ts index b43383825..8b29df1bb 100644 --- a/server/test/unit/services/lessSimpleQueue.test.ts +++ b/server/test/unit/services/lessSimpleQueue.test.ts @@ -44,7 +44,7 @@ jest.unstable_mockModule("less-simple-scheduler", () => ({ MongoStore: MockMongoStore, })); -const { LessSimpleQueue } = await import("../../../src/service/job-queues/job-queue.less-simple.ts"); +const { LessSimpleWorker: LessSimpleQueue } = await import("../../../src/worker/worker.less-simple.ts"); const createQueueHelper = () => ({ getHeartbeatJob: jest.fn().mockReturnValue(() => Promise.resolve()), diff --git a/server/test/unit/services/monitorService.test.ts b/server/test/unit/services/monitorService.test.ts index 3dff29f26..d64007ebe 100644 --- a/server/test/unit/services/monitorService.test.ts +++ b/server/test/unit/services/monitorService.test.ts @@ -98,7 +98,7 @@ const createService = ( const jobQueue = overrides.jobQueue ?? createJobQueueMock(); const logger = overrides.logger ?? createMockLogger(); const service = new MonitorService({ - jobQueue: jobQueue as any, + worker: jobQueue as any, logger: logger as any, games: (overrides.games ?? { cs2: { name: "Counter-Strike 2" } }) as any, monitorsRepository: overrides.monitorsRepository ?? createMonitorsRepositoryMock(), @@ -1535,7 +1535,7 @@ describe("MonitorService", () => { const logger = createMockLogger(); const service = new MonitorService({ - jobQueue: jobQueue as any, + worker: jobQueue as any, logger: logger as any, games: {} as any, monitorsRepository, diff --git a/server/test/unit/services/notificationMessageBuilder.test.ts b/server/test/unit/services/notificationMessageBuilder.test.ts index 0ae4d8096..6e1c4f4dc 100644 --- a/server/test/unit/services/notificationMessageBuilder.test.ts +++ b/server/test/unit/services/notificationMessageBuilder.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, beforeEach } from "@jest/globals"; import { NotificationMessageBuilder } from "../../../src/domain/notifications/notification.message-builder.ts"; import type { Monitor } from "../../../src/domain/monitors/monitor.types.ts"; import type { MonitorStatusResponse, HardwareStatusPayload } from "../../../src/types/network.ts"; -import type { MonitorActionDecision } from "../../../src/service/job-queues/job-queue.helper.ts"; +import type { MonitorActionDecision } from "../../../src/worker/worker.helper.ts"; // ── Helpers ────────────────────────────────────────────────────────────────── diff --git a/server/test/unit/services/notificationsService.test.ts b/server/test/unit/services/notificationsService.test.ts index eca1f349b..8ba8b34aa 100644 --- a/server/test/unit/services/notificationsService.test.ts +++ b/server/test/unit/services/notificationsService.test.ts @@ -4,7 +4,7 @@ import { createMockLogger } from "../../helpers/createMockLogger.ts"; import type { Monitor } from "../../../src/domain/monitors/monitor.types.ts"; import type { Notification } from "../../../src/domain/notifications/notification.type.ts"; import type { MonitorStatusResponse } from "../../../src/types/network.ts"; -import type { MonitorActionDecision } from "../../../src/service/job-queues/job-queue.helper.ts"; +import type { MonitorActionDecision } from "../../../src/worker/worker.helper.ts"; // ── Helpers ────────────────────────────────────────────────────────────────── diff --git a/server/test/unit/services/superSimpleQueue.test.ts b/server/test/unit/services/superSimpleQueue.test.ts index 8f446b74f..b9e9b8314 100644 --- a/server/test/unit/services/superSimpleQueue.test.ts +++ b/server/test/unit/services/superSimpleQueue.test.ts @@ -35,7 +35,7 @@ jest.unstable_mockModule("super-simple-scheduler", () => ({ default: MockScheduler, })); -const { SuperSimpleQueue } = await import("../../../src/service/job-queues/job-queue.sumper-simple.ts"); +const { SuperSimpleQueue } = await import("../../../src/worker/worker.super-simple.ts"); const createQueueHelper = () => ({ getHeartbeatJob: jest.fn().mockReturnValue(() => Promise.resolve()), diff --git a/server/test/unit/services/superSimpleQueueHelper.test.ts b/server/test/unit/services/superSimpleQueueHelper.test.ts index 7a369f4db..bb4a7c001 100644 --- a/server/test/unit/services/superSimpleQueueHelper.test.ts +++ b/server/test/unit/services/superSimpleQueueHelper.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, jest } from "@jest/globals"; -import { QueueHelper } from "../../../src/service/job-queues/job-queue.helper.ts"; +import { QueueHelper } from "../../../src/worker/worker.helper.ts"; import type { Monitor } from "../../../src/domain/monitors/monitor.types.ts"; import { createMockLogger } from "../../helpers/createMockLogger.ts"; diff --git a/server/test/unit/services/userService.test.ts b/server/test/unit/services/userService.test.ts index e2e11369c..7b2fb380b 100644 --- a/server/test/unit/services/userService.test.ts +++ b/server/test/unit/services/userService.test.ts @@ -65,7 +65,7 @@ const createService = (overrides?: Record) => { const settingsService = { getSettings: jest.fn().mockReturnValue(makeAppSettings()), }; - const jobQueue = { + const worker = { deleteJob: jest.fn().mockResolvedValue(undefined), }; const jwtMock = { @@ -85,7 +85,7 @@ const createService = (overrides?: Record) => { monitorsRepository, emailService, settingsService, - jobQueue, + worker, jwt: jwtMock, crypto: cryptoMock, ...overrides, @@ -475,23 +475,23 @@ describe("UserService", () => { describe("deleteUser", () => { it("deletes user and cleans up monitor jobs for superadmin", async () => { - const { service, usersRepository, jobQueue, monitorsRepository } = createService(); + const { service, usersRepository, worker, monitorsRepository } = createService(); await service.deleteUser({ userId: "user-1", teamId: "team-1", roles: ["superadmin"] }); expect(monitorsRepository.findByTeamId).toHaveBeenCalledWith("team-1", {}); - expect(jobQueue.deleteJob).toHaveBeenCalledTimes(2); + expect(worker.deleteJob).toHaveBeenCalledTimes(2); expect(usersRepository.deleteById).toHaveBeenCalledWith("user-1"); }); it("handles null monitors for superadmin gracefully", async () => { - const { service, usersRepository, jobQueue } = createService({ + const { service, usersRepository, worker } = createService({ monitorsRepository: { findByTeamId: jest.fn().mockResolvedValue(null) }, }); await service.deleteUser({ userId: "user-1", teamId: "team-1", roles: ["superadmin"] }); - expect(jobQueue.deleteJob).not.toHaveBeenCalled(); + expect(worker.deleteJob).not.toHaveBeenCalled(); expect(usersRepository.deleteById).toHaveBeenCalledWith("user-1"); }); From ee9c08b1bb6caef55a55df1e717793991bf01c60 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 10 Jun 2026 13:12:51 -0700 Subject: [PATCH 06/17] extract reactors and pipeline --- server/src/config/services.ts | 43 ++- .../src/worker/reactors/reactor.dispatcher.ts | 39 +++ .../src/worker/reactors/reactor.incident.ts | 13 + .../src/worker/reactors/reactor.interface.ts | 7 + .../worker/reactors/reactor.notification.ts | 13 + server/src/worker/worker.check-pipeline.ts | 187 +++++++++++++ server/src/worker/worker.helper.ts | 255 +++--------------- server/src/worker/worker.interface.ts | 11 + .../worker/worker.monitor-status-policy.ts | 46 ++++ 9 files changed, 380 insertions(+), 234 deletions(-) create mode 100644 server/src/worker/reactors/reactor.dispatcher.ts create mode 100644 server/src/worker/reactors/reactor.incident.ts create mode 100644 server/src/worker/reactors/reactor.interface.ts create mode 100644 server/src/worker/reactors/reactor.notification.ts create mode 100644 server/src/worker/worker.check-pipeline.ts create mode 100644 server/src/worker/worker.monitor-status-policy.ts diff --git a/server/src/config/services.ts b/server/src/config/services.ts index dcd6cbb98..8d872201e 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -28,12 +28,13 @@ import { IUserService, UserService } from "@/domain/users/user.service.js"; import { BufferService, IBufferService } from "@/service/bufferService.js"; import { EmailService, IEmailService } from "@/service/emailService.js"; import { GlobalPingService } from "@/service/globalPingService.js"; -import { QueueHelper } from "@/worker/worker.helper.js"; +import { WorkerHelper } from "@/worker/worker.helper.js"; import { IWorker } from "@/worker/worker.interface.js"; import { LessSimpleWorker } from "@/worker/worker.less-simple.js"; import { SuperSimpleQueue } from "@/worker/worker.super-simple.js"; import { INetworkService, NetworkService } from "@/service/networkService.js"; import { IStatusService, StatusService } from "@/service/statusService.js"; +import { MonitorStatusPolicy } from "@/worker/worker.monitor-status-policy.js"; // Network providers import { PingProvider } from "@/service/network/PingProvider.js"; @@ -102,6 +103,10 @@ import MongoUsersRepository from "@/domain/users/user.repository.mongo.js"; import { ILogger } from "@/utils/logger.js"; import { AppError } from "@/utils/AppError.js"; import { type QueueMode } from "@/domain/app-settings/app-settings.type.js"; +import { NotificationReactor } from "@/worker/reactors/reactor.notification.js"; +import { ReactorDispatcher } from "@/worker/reactors/reactor.dispatcher.js"; +import { IncidentReactor } from "@/worker/reactors/reactor.incident.js"; +import { CheckPipeline, GeoChecksPipeline } from "@/worker/worker.check-pipeline.js"; export type InitializedServices = { settingsService: ISettingsService; @@ -309,23 +314,39 @@ export const initializeServices = async ({ const tagsService = new TagsService(tagsRepository, monitorsRepository); - const queueHelper = new QueueHelper( - logger, + const monitorStatusPolicy = new MonitorStatusPolicy(); + // reactors and dispatcher + const notificationReactor = new NotificationReactor(notificationsService); + const incidentReactor = new IncidentReactor(incidentService); + const reactorDispatcher = new ReactorDispatcher(logger, [notificationReactor, incidentReactor]); + + // pipelines + const checkPipeline = new CheckPipeline( + monitorsRepository, + maintenanceWindowsRepository, + checkService, networkService, + bufferService, + monitorStatusPolicy, statusService, - notificationsService, + logger + ); + + const geoCheckPipeline = new GeoChecksPipeline(maintenanceWindowsRepository, geoChecksService, bufferService, logger); + + const workerHelper = new WorkerHelper( + logger, checkService, settingsService, - bufferService, - incidentService, - maintenanceWindowsRepository, monitorsRepository, teamsRepository, monitorStatsRepository, checksRepository, incidentsRepository, - geoChecksService, - geoChecksRepository + geoChecksRepository, + reactorDispatcher, + checkPipeline, + geoCheckPipeline ); if (queueMode === "worker" && envSettings.queueType !== "lessSimpleQueue") { @@ -340,10 +361,10 @@ export const initializeServices = async ({ let worker: IWorker; switch (envSettings.queueType) { case "lessSimpleQueue": - worker = await LessSimpleWorker.create(logger, queueHelper, monitorsRepository, queueWorkersRepository, envSettings, queueMode); + worker = await LessSimpleWorker.create(logger, workerHelper, monitorsRepository, queueWorkersRepository, envSettings, queueMode); break; default: - worker = await SuperSimpleQueue.create(logger, queueHelper, monitorsRepository); + worker = await SuperSimpleQueue.create(logger, workerHelper, monitorsRepository); } // Business services diff --git a/server/src/worker/reactors/reactor.dispatcher.ts b/server/src/worker/reactors/reactor.dispatcher.ts new file mode 100644 index 000000000..a4ffe757e --- /dev/null +++ b/server/src/worker/reactors/reactor.dispatcher.ts @@ -0,0 +1,39 @@ +import { ILogger } from "@/utils/logger.js"; +import { MonitorEvaluation } from "@/worker/worker.interface.js"; +import { IMonitorReactor } from "@/worker/reactors/reactor.interface.js"; + +const SERVICE_NAME = "ReactorDispatcher"; + +export interface IReactorDispatcher { + dispatch(evaluation: MonitorEvaluation): Promise; +} + +export class ReactorDispatcher implements IReactorDispatcher { + constructor( + private logger: ILogger, + private reactors: IMonitorReactor[] + ) {} + + private logFailure(reactor: IMonitorReactor, evaluation: MonitorEvaluation, error: unknown) { + this.logger.error({ + service: SERVICE_NAME, + method: "dispatch", + message: `Reactor ${reactor.name} failed for monitor ${evaluation.monitor.id}: ${error instanceof Error ? error.message : String(error)}`, + stack: error instanceof Error ? error.stack : undefined, + }); + } + + dispatch = async (evaluation: MonitorEvaluation) => { + for (const reactor of this.reactors) { + if (reactor.blocking) { + try { + await reactor.react(evaluation); + } catch (error: unknown) { + this.logFailure(reactor, evaluation, error); + } + } else { + reactor.react(evaluation).catch((error: unknown) => this.logFailure(reactor, evaluation, error)); + } + } + }; +} diff --git a/server/src/worker/reactors/reactor.incident.ts b/server/src/worker/reactors/reactor.incident.ts new file mode 100644 index 000000000..3679a30cf --- /dev/null +++ b/server/src/worker/reactors/reactor.incident.ts @@ -0,0 +1,13 @@ +import { IMonitorReactor } from "@/worker/reactors/reactor.interface.js"; +import { MonitorEvaluation } from "@/worker/worker.interface.js"; +import { IIncidentService } from "@/domain/incidents/incident.service.js"; + +export class IncidentReactor implements IMonitorReactor { + readonly name = "incident"; + readonly blocking = true; + constructor(private incidentService: IIncidentService) {} + + react = async (evaluation: MonitorEvaluation) => { + await this.incidentService.handleIncident(evaluation.monitor, evaluation.statusChange.code, evaluation.decision, evaluation.status); + }; +} diff --git a/server/src/worker/reactors/reactor.interface.ts b/server/src/worker/reactors/reactor.interface.ts new file mode 100644 index 000000000..f9ed6e436 --- /dev/null +++ b/server/src/worker/reactors/reactor.interface.ts @@ -0,0 +1,7 @@ +import { MonitorEvaluation } from "@/worker/worker.interface.js"; + +export interface IMonitorReactor { + readonly name: string; + readonly blocking: boolean; + react(evaluation: MonitorEvaluation): Promise; +} diff --git a/server/src/worker/reactors/reactor.notification.ts b/server/src/worker/reactors/reactor.notification.ts new file mode 100644 index 000000000..1daf528f4 --- /dev/null +++ b/server/src/worker/reactors/reactor.notification.ts @@ -0,0 +1,13 @@ +import { INotificationsService } from "@/domain/notifications/notification.service.js"; +import { IMonitorReactor } from "@/worker/reactors/reactor.interface.js"; +import { MonitorEvaluation } from "@/worker/worker.interface.js"; +export class NotificationReactor implements IMonitorReactor { + readonly name = "notification"; + readonly blocking = false; + constructor(private notificationService: INotificationsService) {} + + react = async (evaluation: MonitorEvaluation) => { + if (!evaluation.decision.shouldSendNotification) return; + await this.notificationService.handleNotifications(evaluation.monitor, evaluation.status, evaluation.decision); + }; +} diff --git a/server/src/worker/worker.check-pipeline.ts b/server/src/worker/worker.check-pipeline.ts new file mode 100644 index 000000000..f0958fa20 --- /dev/null +++ b/server/src/worker/worker.check-pipeline.ts @@ -0,0 +1,187 @@ +import { Monitor } from "@/domain/monitors/monitor.types.js"; +import { MonitorEvaluation } from "@/worker/worker.interface.js"; +import { IMaintenanceWindowsRepository } from "@/domain/maintenance-windows/maintenance-window.repository.interface.js"; +import { IMonitorsRepository } from "@/domain/monitors/monitor.repository.interface.js"; +import { isWindowActive } from "@/utils/maintenanceWindow.js"; +import { ILogger } from "@/utils/logger.js"; +import { INetworkService } from "@/service/networkService.js"; +import { ICheckService } from "@/domain/checks/check.service.js"; +import { IBufferService } from "@/service/bufferService.js"; +import { IMonitorStatusPolicy } from "@/worker/worker.monitor-status-policy.js"; +import { IStatusService } from "@/service/statusService.js"; +import { AppError } from "@/utils/AppError.js"; +import { supportsGeoCheck } from "@/domain/monitors/monitor.types.js"; +import { IGeoChecksService } from "@/domain/geo-checks/geo-check.service.js"; + +const SERVICE_NAME = "CheckPipeline"; + +export interface ICheckPipeline { + run(monitor: Monitor): Promise; // null = skipped +} + +export class CheckPipeline implements ICheckPipeline { + constructor( + private monitorsRepository: IMonitorsRepository, + private maintenanceWindowsRepository: IMaintenanceWindowsRepository, + private checkService: ICheckService, + private networkService: INetworkService, + private bufferService: IBufferService, + private monitorStatusPolicy: IMonitorStatusPolicy, + private statusService: IStatusService, + private logger: ILogger + ) {} + + private async isInMaintenanceWindow(monitorId: string, teamId: string) { + const maintenanceWindows = await this.maintenanceWindowsRepository.findByMonitorId(monitorId, teamId); + const now = new Date(); + return maintenanceWindows.some((window) => isWindowActive(window, now)); + } + + run = async (monitor: Monitor): Promise => { + const monitorId = monitor.id; + if (!monitorId) { + throw new AppError({ message: "No monitor id", service: SERVICE_NAME, method: "getMonitorJob" }); + } + // **************************** + // Step 1: Acquire + // **************************** + + // Step 1a: Maintenance window gate - skip if in maintenance + const maintenanceWindowActive = await this.isInMaintenanceWindow(monitor.id, monitor.teamId); + if (maintenanceWindowActive) { + this.logger.debug({ + message: `Monitor ${monitor.id} is in maintenance window`, + service: SERVICE_NAME, + method: "runCheckPipeline", + }); + if (monitor.status !== "maintenance") { + await this.monitorsRepository.updateById(monitor.id, monitor.teamId, { status: "maintenance" }); + } + return null; + } + + // Step 1b: Acquire status + const status = await this.networkService.requestStatus(monitor); + if (!status) { + throw new Error("No network response"); + } + + // **************************** + // Step 2: Record + // **************************** + + // Step 2a: Create & record a check, return null if fail + const check = this.checkService.buildCheck(status); + if (!check) { + this.logger.warn({ + message: `No check could be built for monitor ${monitor.id}`, + service: SERVICE_NAME, + method: "runCHeckPipeline", + details: { code: status.code, message: status.message }, + }); + return null; + } + // Step 2b: Add to buffer + this.bufferService.addToBuffer(check); + + // **************************** + // Step 3: Evaluate and return result to reactors + // **************************** + const statusChangeResult = await this.statusService.updateMonitorStatus(status, check); + + // Step 5. Get decisions and create an evaluation obj + const decision = this.monitorStatusPolicy.evaluate(statusChangeResult); + const evaluation: MonitorEvaluation = { + monitor: statusChangeResult.monitor, + status, + check, + statusChange: statusChangeResult, + decision, + }; + return evaluation; + }; +} + +export class GeoChecksPipeline implements ICheckPipeline { + constructor( + private maintenanceWindowsRepository: IMaintenanceWindowsRepository, + private geoChecksService: IGeoChecksService, + private bufferService: IBufferService, + private logger: ILogger + ) {} + + private async isInMaintenanceWindow(monitorId: string, teamId: string) { + const maintenanceWindows = await this.maintenanceWindowsRepository.findByMonitorId(monitorId, teamId); + const now = new Date(); + return maintenanceWindows.some((window) => isWindowActive(window, now)); + } + + run = async (monitor: Monitor): Promise => { + // **************************** + // Step 1: Acquire + // **************************** + + // Step 1a: Guards - skip if unsupported or not enabled + + if (!monitor.id) { + throw new AppError({ message: "No monitor id", service: SERVICE_NAME, method: "getHeartbeatGeoJob" }); + } + + if (!monitor.geoCheckEnabled) { + return null; + } + if (!supportsGeoCheck(monitor.type)) { + this.logger.debug({ + message: `Monitor ${monitor.id} type does not support geo checks, skipping`, + service: SERVICE_NAME, + method: "runGeoChecksPipeline", + }); + return null; + } + + if (!monitor.geoCheckLocations || monitor.geoCheckLocations.length === 0) { + this.logger.warn({ + message: `No geo check locations configured for monitor ${monitor.id}`, + service: SERVICE_NAME, + method: "runGeoChecksPipeline", + }); + return null; + } + + // Step 1b: Maintenance window check + const maintenanceWindowActive = await this.isInMaintenanceWindow(monitor.id, monitor.teamId); + if (maintenanceWindowActive) { + this.logger.debug({ + message: `Monitor ${monitor.id} is in maintenance window, skipping geo check`, + service: SERVICE_NAME, + method: "runGeoChecksPipeline", + }); + return null; + } + + // **************************** + // Step 2: Record + // **************************** + + // Step 2a: Build geo check, return null if fail + const geoCheck = await this.geoChecksService.buildGeoCheck(monitor); + if (!geoCheck) { + this.logger.warn({ + message: `No geo check could be built for monitor ${monitor.id}`, + service: SERVICE_NAME, + method: "runGeoChecksPipeline", + }); + return null; + } + + // Step 2b: Add to buffer + this.bufferService.addGeoCheckToBuffer(geoCheck); + + this.logger.debug({ + message: `Geo check job executed for monitor ${monitor.id}`, + service: SERVICE_NAME, + method: "runGeoChecksPipeline", + }); + return null; + }; +} diff --git a/server/src/worker/worker.helper.ts b/server/src/worker/worker.helper.ts index 6840ff695..29de9f2a3 100644 --- a/server/src/worker/worker.helper.ts +++ b/server/src/worker/worker.helper.ts @@ -1,26 +1,17 @@ const SERVICE_NAME = "JobQueueHelper"; import type { Monitor } from "@/domain/monitors/monitor.types.js"; -import { supportsGeoCheck } from "@/domain/monitors/monitor.types.js"; -import { AppError } from "@/utils/AppError.js"; import { ISettingsService } from "@/domain/app-settings/app-settings.service.js"; import { ICheckService } from "@/domain/checks/check.service.js"; -import { type IGeoChecksService } from "@/domain/geo-checks/geo-check.service.js"; -import { IncidentService } from "@/domain/incidents/incident.service.js"; -import { INotificationsService } from "@/domain/notifications/notification.service.js"; -import { INetworkService } from "@/service/networkService.js"; -import { IStatusService } from "@/service/statusService.js"; import { CHECK_TTL_SENTINEL } from "@/domain/checks/check.type.js"; -import { type StatusChangeResult } from "@/types/network.js"; -import { isWindowActive } from "@/utils/maintenanceWindow.js"; import { IChecksRepository } from "@/domain/checks/check.repository.interface.js"; import { IGeoChecksRepository } from "@/domain/geo-checks/geo-check.repository.interface.js"; import { IIncidentsRepository } from "@/domain/incidents/incident.repository.interface.js"; -import { IMaintenanceWindowsRepository } from "@/domain/maintenance-windows/maintenance-window.repository.interface.js"; import { IMonitorStatsRepository } from "@/domain/monitor-stats/monitor-stats.repository.interface.js"; import { IMonitorsRepository } from "@/domain/monitors/monitor.repository.interface.js"; import { ITeamsRepository } from "@/domain/teams/team.repository.interface.js"; import { ILogger } from "@/utils/logger.js"; -import { IBufferService } from "@/service/bufferService.js"; +import { IReactorDispatcher } from "@/worker/reactors/reactor.dispatcher.js"; +import { ICheckPipeline } from "@/worker/worker.check-pipeline.js"; export interface IWorkerHelper { readonly serviceName: string; @@ -28,7 +19,6 @@ export interface IWorkerHelper { getHeartbeatGeoJob(): (monitor: Monitor) => Promise; getCleanupOrphanedJob(): () => Promise; getCleanupRetentionJob(): () => Promise; - isInMaintenanceWindow(monitorId: string, teamId: string): Promise; } export interface MonitorActionDecision { @@ -45,138 +35,59 @@ export interface MonitorActionDecision { }; } -export class QueueHelper implements IWorkerHelper { +export class WorkerHelper implements IWorkerHelper { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; - private networkService: INetworkService; - private statusService: IStatusService; - private notificationsService: INotificationsService; private checkService: ICheckService; private settingsService: ISettingsService; - private buffer: IBufferService; - private incidentService: IncidentService; - private maintenanceWindowsRepository: IMaintenanceWindowsRepository; private monitorsRepository: IMonitorsRepository; private teamsRepository: ITeamsRepository; private monitorStatsRepository: IMonitorStatsRepository; private checksRepository: IChecksRepository; private incidentsRepository: IIncidentsRepository; - private geoChecksService: IGeoChecksService; private geoChecksRepository: IGeoChecksRepository; + private reactorDispatcher: IReactorDispatcher; + private checkPipeline: ICheckPipeline; + private geoCheckPipeline: ICheckPipeline; constructor( logger: ILogger, - networkService: INetworkService, - statusService: IStatusService, - notificationsService: INotificationsService, checkService: ICheckService, settingsService: ISettingsService, - buffer: IBufferService, - incidentService: IncidentService, - maintenanceWindowsRepository: IMaintenanceWindowsRepository, monitorsRepository: IMonitorsRepository, teamsRepository: ITeamsRepository, monitorStatsRepository: IMonitorStatsRepository, checksRepository: IChecksRepository, incidentsRepository: IIncidentsRepository, - geoChecksService: IGeoChecksService, - geoChecksRepository: IGeoChecksRepository + geoChecksRepository: IGeoChecksRepository, + reactorDispatcher: IReactorDispatcher, + checkPipeline: ICheckPipeline, + geoCheckPipeline: ICheckPipeline ) { this.logger = logger; - this.networkService = networkService; - this.statusService = statusService; this.checkService = checkService; this.settingsService = settingsService; - this.buffer = buffer; - this.notificationsService = notificationsService; - this.incidentService = incidentService; - this.maintenanceWindowsRepository = maintenanceWindowsRepository; this.monitorsRepository = monitorsRepository; this.teamsRepository = teamsRepository; this.monitorStatsRepository = monitorStatsRepository; this.checksRepository = checksRepository; this.incidentsRepository = incidentsRepository; - this.geoChecksService = geoChecksService; this.geoChecksRepository = geoChecksRepository; + this.reactorDispatcher = reactorDispatcher; + this.checkPipeline = checkPipeline; + this.geoCheckPipeline = geoCheckPipeline; } get serviceName() { - return QueueHelper.SERVICE_NAME; + return WorkerHelper.SERVICE_NAME; } getHeartbeatJob = () => { return async (monitor: Monitor) => { try { - const monitorId = monitor.id; - const teamId = monitor.teamId; - if (!monitorId) { - throw new AppError({ message: "No monitor id", service: SERVICE_NAME, method: "getMonitorJob" }); - } - - // Step 1. Check for maintenance window, if found, skip the check - - const maintenanceWindowActive = await this.isInMaintenanceWindow(monitorId, teamId); - if (maintenanceWindowActive) { - this.logger.debug({ - message: `Monitor ${monitorId} is in maintenance window`, - service: SERVICE_NAME, - method: "getMonitorJob", - }); - if (monitor.status !== "maintenance") { - await this.monitorsRepository.updateById(monitorId, teamId, { status: "maintenance" }); - } - return; - } - - // Step 2. Request monitor status - const status = await this.networkService.requestStatus(monitor); - if (!status) { - throw new Error("No network response"); - } - - // Step 3. Build check - const check = this.checkService.buildCheck(status); - if (!check) { - this.logger.warn({ - message: `No check could be built for monitor ${monitorId}`, - service: SERVICE_NAME, - method: "getMonitorJob", - details: { code: status.code, message: status.message }, - }); - return; - } - // Step 4 Add check to buffer - this.buffer.addToBuffer(check); - // Step 4. Update monitor status - const statusChangeResult = await this.statusService.updateMonitorStatus(status, check); - - // Step 5. Get decisions - const decision = this.evaluateMonitorAction(statusChangeResult); - - // Step 6. Handle notifications (best effort, continue even in event of failure, don't wait) - if (decision.shouldSendNotification) { - this.notificationsService.handleNotifications(statusChangeResult.monitor, status, decision).catch((error: unknown) => { - this.logger.error({ - message: `Error sending notifications for job ${statusChangeResult.monitor.id}: ${error instanceof Error ? error.message : "Unknown error"}`, - service: SERVICE_NAME, - method: "getMonitorJob", - stack: error instanceof Error ? error.stack : undefined, - }); - }); - } - - // Step 7. Handle incidents - try { - await this.incidentService.handleIncident(statusChangeResult.monitor, statusChangeResult.code, decision, status); - } catch (error: unknown) { - this.logger.warn({ - message: `Error handling incident for job ${monitor.id}: ${error instanceof Error ? error.message : "Unknown error"}`, - service: SERVICE_NAME, - method: "getMonitorJob", - stack: error instanceof Error ? error.stack : undefined, - }); - } + const evaluation = await this.checkPipeline.run(monitor); + if (evaluation) await this.reactorDispatcher.dispatch(evaluation); } catch (error: unknown) { this.logger.warn({ message: error instanceof Error ? error.message : "Unknown error", @@ -189,6 +100,22 @@ export class QueueHelper implements IWorkerHelper { }; }; + getHeartbeatGeoJob = () => { + return async (monitor: Monitor) => { + try { + await this.geoCheckPipeline.run(monitor); + } catch (error: unknown) { + this.logger.error({ + message: error instanceof Error ? error.message : "Unknown error", + service: SERVICE_NAME, + method: "getHeartbeatGeoJob", + stack: error instanceof Error ? error.stack : undefined, + }); + // Don't throw - geo check failures shouldn't crash the job scheduler + } + }; + }; + getCleanupOrphanedJob = () => { return async () => { try { @@ -280,86 +207,6 @@ export class QueueHelper implements IWorkerHelper { }; }; - getHeartbeatGeoJob = () => { - return async (monitor: Monitor) => { - try { - const monitorId = monitor.id; - const teamId = monitor.teamId; - - // Step 1: Validate monitor eligibility - if (!monitorId) { - throw new AppError({ message: "No monitor id", service: SERVICE_NAME, method: "getHeartbeatGeoJob" }); - } - - if (!monitor.geoCheckEnabled) { - return; - } - if (!supportsGeoCheck(monitor.type)) { - this.logger.debug({ - message: `Monitor ${monitorId} type does not support geo checks, skipping`, - service: SERVICE_NAME, - method: "getHeartbeatGeoJob", - }); - return; - } - - if (!monitor.geoCheckLocations || monitor.geoCheckLocations.length === 0) { - this.logger.warn({ - message: `No geo check locations configured for monitor ${monitorId}`, - service: SERVICE_NAME, - method: "getHeartbeatGeoJob", - }); - return; - } - - // Step 2: Check for maintenance window - const maintenanceWindowActive = await this.isInMaintenanceWindow(monitorId, teamId); - if (maintenanceWindowActive) { - this.logger.debug({ - message: `Monitor ${monitorId} is in maintenance window, skipping geo check`, - service: SERVICE_NAME, - method: "getHeartbeatGeoJob", - }); - return; - } - - // Step 3: Build geo check (handles API calls and polling) - const geoCheck = await this.geoChecksService.buildGeoCheck(monitor); - if (!geoCheck) { - this.logger.warn({ - message: `No geo check could be built for monitor ${monitorId}`, - service: SERVICE_NAME, - method: "getHeartbeatGeoJob", - }); - return; - } - - // Step 4: Add geo check to buffer - this.buffer.addGeoCheckToBuffer(geoCheck); - - this.logger.debug({ - message: `Geo check job executed for monitor ${monitorId}`, - service: SERVICE_NAME, - method: "getHeartbeatGeoJob", - }); - } catch (error: unknown) { - this.logger.error({ - message: error instanceof Error ? error.message : "Unknown error", - service: SERVICE_NAME, - method: "getHeartbeatGeoJob", - stack: error instanceof Error ? error.stack : undefined, - }); - // Don't throw - geo check failures shouldn't crash the job scheduler - } - }; - }; - - async isInMaintenanceWindow(monitorId: string, teamId: string) { - const maintenanceWindows = await this.maintenanceWindowsRepository.findByMonitorId(monitorId, teamId); - const now = new Date(); - return maintenanceWindows.some((window) => isWindowActive(window, now)); - } - getCleanupRetentionJob = () => { return async () => { try { @@ -393,42 +240,4 @@ export class QueueHelper implements IWorkerHelper { } }; }; - - private evaluateMonitorAction(statusChangeResult: StatusChangeResult): MonitorActionDecision { - const { monitor, statusChanged, prevStatus } = statusChangeResult; - - // Initialize result - const decision: MonitorActionDecision = { - shouldCreateIncident: false, - shouldResolveIncident: false, - shouldSendNotification: false, - incidentReason: null, - notificationReason: null, - }; - - if (!statusChanged) { - return decision; - } - - if (monitor.status === "down") { - // Monitor went down (unreachable) - decision.shouldCreateIncident = true; - decision.shouldSendNotification = true; - decision.incidentReason = "status_down"; - decision.notificationReason = "status_change"; - } else if (monitor.status === "breached") { - // Hardware monitor exceeded thresholds - decision.shouldCreateIncident = true; - decision.shouldSendNotification = true; - decision.incidentReason = "threshold_breach"; - decision.notificationReason = "threshold_breach"; - } else if (monitor.status === "up" && (prevStatus === "down" || prevStatus === "breached")) { - // Monitor recovered from down or breached state - decision.shouldResolveIncident = true; - decision.shouldSendNotification = true; - decision.notificationReason = "status_change"; - } - - return decision; - } } diff --git a/server/src/worker/worker.interface.ts b/server/src/worker/worker.interface.ts index 45dc94ca1..5d1ccf97f 100644 --- a/server/src/worker/worker.interface.ts +++ b/server/src/worker/worker.interface.ts @@ -1,5 +1,16 @@ import { Monitor } from "@/domain/monitors/monitor.types.js"; import { QueueWorker } from "@/domain/queue-workers/queue-worker.type.js"; +import { Check } from "@/domain/checks/check.type.js"; +import { MonitorPayloadMap, MonitorStatusResponse, StatusChangeResult } from "@/types/network.js"; +import { MonitorActionDecision } from "@/worker/worker.helper.js"; + +export type MonitorEvaluation = { + monitor: Monitor; + status: MonitorStatusResponse; // raw result from networkService.requestStatus + check: Check; + statusChange: StatusChangeResult; // from statusService.updateMonitorStatus + decision: MonitorActionDecision; // from MonitorStatusPolicy +}; export type WorkerJobFailure = { monitorId: string | number; diff --git a/server/src/worker/worker.monitor-status-policy.ts b/server/src/worker/worker.monitor-status-policy.ts new file mode 100644 index 000000000..4ef36b2a9 --- /dev/null +++ b/server/src/worker/worker.monitor-status-policy.ts @@ -0,0 +1,46 @@ +import { StatusChangeResult } from "@/types/network.js"; +import { MonitorActionDecision } from "@/worker/worker.helper.js"; + +export interface IMonitorStatusPolicy { + evaluate(statusChange: StatusChangeResult): MonitorActionDecision; +} + +export class MonitorStatusPolicy implements IMonitorStatusPolicy { + evaluate = (statusChangeResult: StatusChangeResult): MonitorActionDecision => { + const { monitor, statusChanged, prevStatus } = statusChangeResult; + + // Initialize result + const decision: MonitorActionDecision = { + shouldCreateIncident: false, + shouldResolveIncident: false, + shouldSendNotification: false, + incidentReason: null, + notificationReason: null, + }; + + if (!statusChanged) { + return decision; + } + + if (monitor.status === "down") { + // Monitor went down (unreachable) + decision.shouldCreateIncident = true; + decision.shouldSendNotification = true; + decision.incidentReason = "status_down"; + decision.notificationReason = "status_change"; + } else if (monitor.status === "breached") { + // Hardware monitor exceeded thresholds + decision.shouldCreateIncident = true; + decision.shouldSendNotification = true; + decision.incidentReason = "threshold_breach"; + decision.notificationReason = "threshold_breach"; + } else if (monitor.status === "up" && (prevStatus === "down" || prevStatus === "breached")) { + // Monitor recovered from down or breached state + decision.shouldResolveIncident = true; + decision.shouldSendNotification = true; + decision.notificationReason = "status_change"; + } + + return decision; + }; +} From fa7eaa4788f96a4878e6d48ea7cefa8f9c03aee1 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 10 Jun 2026 13:13:01 -0700 Subject: [PATCH 07/17] update tests --- server/test/helpers/heartbeatTestHarness.ts | 36 +- .../test/unit/services/checkPipeline.test.ts | 217 +++++++ .../unit/services/monitorStatusPolicy.test.ts | 74 +++ .../unit/services/reactorDispatcher.test.ts | 87 +++ .../services/superSimpleQueueHelper.test.ts | 565 ++---------------- 5 files changed, 461 insertions(+), 518 deletions(-) create mode 100644 server/test/unit/services/checkPipeline.test.ts create mode 100644 server/test/unit/services/monitorStatusPolicy.test.ts create mode 100644 server/test/unit/services/reactorDispatcher.test.ts diff --git a/server/test/helpers/heartbeatTestHarness.ts b/server/test/helpers/heartbeatTestHarness.ts index 79dae92cd..ea78d5a5d 100644 --- a/server/test/helpers/heartbeatTestHarness.ts +++ b/server/test/helpers/heartbeatTestHarness.ts @@ -1,5 +1,10 @@ import { jest } from "@jest/globals"; -import { QueueHelper } from "../../src/worker/worker.helper.ts"; +import { WorkerHelper } from "../../src/worker/worker.helper.ts"; +import { MonitorStatusPolicy } from "../../src/worker/worker.monitor-status-policy.ts"; +import { CheckPipeline, GeoChecksPipeline } from "../../src/worker/worker.check-pipeline.ts"; +import { NotificationReactor } from "../../src/worker/reactors/reactor.notification.ts"; +import { IncidentReactor } from "../../src/worker/reactors/reactor.incident.ts"; +import { ReactorDispatcher } from "../../src/worker/reactors/reactor.dispatcher.ts"; import { StatusService } from "../../src/service/statusService.ts"; import { IncidentService } from "../../src/domain/incidents/incident.service.ts"; import { InMemoryMonitorsRepository } from "./InMemoryMonitorsRepository.ts"; @@ -142,24 +147,37 @@ export function createHeartbeatTestHarness(): HeartbeatTestHarness { }; const maintenanceWindowsRepo = { findByMonitorId: jest.fn().mockResolvedValue([]) }; + const geoChecksService = { buildGeoCheck: jest.fn() }; - const helper = new QueueHelper( - logger, + const checkPipeline = new CheckPipeline( + monitorsRepo as any, + maintenanceWindowsRepo as any, + checkService as any, networkService as any, + bufferStub as any, + new MonitorStatusPolicy(), statusService as any, - notificationsService as any, + logger + ); + const geoCheckPipeline = new GeoChecksPipeline(maintenanceWindowsRepo as any, geoChecksService as any, bufferStub as any, logger); + + const notificationReactor = new NotificationReactor(notificationsService as any); + const incidentReactor = new IncidentReactor(incidentService as any); + const reactorDispatcher = new ReactorDispatcher(logger, [notificationReactor, incidentReactor]); + + const helper = new WorkerHelper( + logger, checkService as any, { getDBSettings: jest.fn() } as any, - bufferStub as any, - incidentService as any, - maintenanceWindowsRepo as any, monitorsRepo as any, { findAllTeamIds: jest.fn() } as any, createStubMonitorStatsRepo() as any, createStubChecksRepo() as any, incidentsRepo as any, - { buildGeoCheck: jest.fn() } as any, - { deleteByMonitorIdsNotIn: jest.fn() } as any + { deleteByMonitorIdsNotIn: jest.fn() } as any, + reactorDispatcher, + checkPipeline, + geoCheckPipeline ); return { diff --git a/server/test/unit/services/checkPipeline.test.ts b/server/test/unit/services/checkPipeline.test.ts new file mode 100644 index 000000000..20ab65950 --- /dev/null +++ b/server/test/unit/services/checkPipeline.test.ts @@ -0,0 +1,217 @@ +import { describe, expect, it, jest } from "@jest/globals"; +import { CheckPipeline, GeoChecksPipeline } from "../../../src/worker/worker.check-pipeline.ts"; +import { MonitorStatusPolicy } from "../../../src/worker/worker.monitor-status-policy.ts"; +import type { Monitor } from "../../../src/domain/monitors/monitor.types.ts"; +import { createMockLogger } from "../../helpers/createMockLogger.ts"; + +const makeMonitor = (overrides?: Partial): Monitor => + ({ + id: "m1", + teamId: "team", + type: "http", + interval: 60000, + status: "up", + geoCheckEnabled: false, + geoCheckLocations: [], + ...overrides, + }) as Monitor; + +const activeWindow = () => { + const now = Date.now(); + return { active: true, start: new Date(now - 1000).toISOString(), end: new Date(now + 1000).toISOString(), repeat: 0 }; +}; + +// ── CheckPipeline ──────────────────────────────────────────────────────────── + +const createCheckPipeline = (overrides?: Record) => { + const defaults = { + logger: createMockLogger(), + networkService: { requestStatus: jest.fn().mockResolvedValue({ monitorId: "m1", status: true, code: 200, message: "OK" }) }, + statusService: { + updateMonitorStatus: jest.fn().mockResolvedValue({ monitor: { id: "m1", status: "up" }, statusChanged: false, prevStatus: "up", code: 200 }), + }, + checkService: { buildCheck: jest.fn().mockReturnValue({ id: "check-1" }) }, + buffer: { addToBuffer: jest.fn() }, + monitorsRepository: { updateById: jest.fn().mockResolvedValue({}) }, + maintenanceWindowsRepository: { findByMonitorId: jest.fn().mockResolvedValue([]) }, + monitorStatusPolicy: new MonitorStatusPolicy(), + ...overrides, + }; + const pipeline = new CheckPipeline( + defaults.monitorsRepository as any, + defaults.maintenanceWindowsRepository as any, + defaults.checkService as any, + defaults.networkService as any, + defaults.buffer as any, + defaults.monitorStatusPolicy as any, + defaults.statusService as any, + defaults.logger as any + ); + return { pipeline, defaults }; +}; + +describe("CheckPipeline", () => { + it("throws when monitor id is missing", async () => { + const { pipeline } = createCheckPipeline(); + await expect(pipeline.run({} as Monitor)).rejects.toThrow("No monitor id"); + }); + + it("skips and sets maintenance status when in maintenance window", async () => { + const { pipeline, defaults } = createCheckPipeline({ + maintenanceWindowsRepository: { findByMonitorId: jest.fn().mockResolvedValue([activeWindow()]) }, + }); + + const result = await pipeline.run(makeMonitor({ status: "up" })); + + expect(result).toBeNull(); + expect(defaults.networkService.requestStatus).not.toHaveBeenCalled(); + expect(defaults.monitorsRepository.updateById).toHaveBeenCalledWith("m1", "team", { status: "maintenance" }); + }); + + it("does not re-write status when already in maintenance", async () => { + const { pipeline, defaults } = createCheckPipeline({ + maintenanceWindowsRepository: { findByMonitorId: jest.fn().mockResolvedValue([activeWindow()]) }, + }); + + await pipeline.run(makeMonitor({ status: "maintenance" })); + + expect(defaults.monitorsRepository.updateById).not.toHaveBeenCalled(); + }); + + it("throws when network response is null", async () => { + const { pipeline } = createCheckPipeline({ + networkService: { requestStatus: jest.fn().mockResolvedValue(null) }, + }); + await expect(pipeline.run(makeMonitor())).rejects.toThrow("No network response"); + }); + + it("returns null and warns when buildCheck returns null", async () => { + const { pipeline, defaults } = createCheckPipeline({ + checkService: { buildCheck: jest.fn().mockReturnValue(null) }, + }); + + const result = await pipeline.run(makeMonitor()); + + expect(result).toBeNull(); + expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No check could be built") })); + expect(defaults.statusService.updateMonitorStatus).not.toHaveBeenCalled(); + }); + + it("buffers the check, updates status, and returns an evaluation", async () => { + const status = { monitorId: "m1", status: false, code: 500, message: "Error" }; + const statusChange = { monitor: { id: "m1", status: "down" }, statusChanged: true, prevStatus: "up", code: 500 }; + const { pipeline, defaults } = createCheckPipeline({ + networkService: { requestStatus: jest.fn().mockResolvedValue(status) }, + statusService: { updateMonitorStatus: jest.fn().mockResolvedValue(statusChange) }, + }); + + const result = await pipeline.run(makeMonitor()); + + expect(defaults.buffer.addToBuffer).toHaveBeenCalledWith({ id: "check-1" }); + expect(defaults.statusService.updateMonitorStatus).toHaveBeenCalled(); + expect(result).toMatchObject({ + monitor: statusChange.monitor, + status, + check: { id: "check-1" }, + statusChange, + decision: expect.objectContaining({ shouldCreateIncident: true, shouldSendNotification: true, incidentReason: "status_down" }), + }); + }); + + it("proceeds with the check when no maintenance window is active", async () => { + const { pipeline, defaults } = createCheckPipeline({ + maintenanceWindowsRepository: { findByMonitorId: jest.fn().mockResolvedValue([{ ...activeWindow(), active: false }]) }, + }); + + await pipeline.run(makeMonitor()); + + expect(defaults.networkService.requestStatus).toHaveBeenCalled(); + }); +}); + +// ── GeoChecksPipeline ────────────────────────────────────────────────────────── + +const createGeoPipeline = (overrides?: Record) => { + const defaults = { + logger: createMockLogger(), + geoChecksService: { buildGeoCheck: jest.fn().mockResolvedValue(null) }, + buffer: { addGeoCheckToBuffer: jest.fn() }, + maintenanceWindowsRepository: { findByMonitorId: jest.fn().mockResolvedValue([]) }, + ...overrides, + }; + const pipeline = new GeoChecksPipeline( + defaults.maintenanceWindowsRepository as any, + defaults.geoChecksService as any, + defaults.buffer as any, + defaults.logger as any + ); + return { pipeline, defaults }; +}; + +const geoMonitor = (overrides?: Partial) => + makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: ["us-east"], ...overrides }); + +describe("GeoChecksPipeline", () => { + it("throws when monitor id is missing", async () => { + const { pipeline } = createGeoPipeline(); + await expect(pipeline.run({} as Monitor)).rejects.toThrow("No monitor id"); + }); + + it("returns null when geoCheckEnabled is false", async () => { + const { pipeline, defaults } = createGeoPipeline(); + const result = await pipeline.run(makeMonitor({ geoCheckEnabled: false })); + expect(result).toBeNull(); + expect(defaults.geoChecksService.buildGeoCheck).not.toHaveBeenCalled(); + }); + + it("returns null when monitor type does not support geo checks", async () => { + const { pipeline, defaults } = createGeoPipeline(); + await pipeline.run(geoMonitor({ type: "hardware" })); + expect(defaults.logger.debug).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("does not support geo checks") })); + expect(defaults.geoChecksService.buildGeoCheck).not.toHaveBeenCalled(); + }); + + it("warns when geoCheckLocations is empty", async () => { + const { pipeline, defaults } = createGeoPipeline(); + await pipeline.run(geoMonitor({ geoCheckLocations: [] })); + expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No geo check locations") })); + }); + + it("warns when geoCheckLocations is undefined", async () => { + const { pipeline, defaults } = createGeoPipeline(); + await pipeline.run(geoMonitor({ geoCheckLocations: undefined as any })); + expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No geo check locations") })); + }); + + it("skips when in maintenance window", async () => { + const { pipeline, defaults } = createGeoPipeline({ + maintenanceWindowsRepository: { findByMonitorId: jest.fn().mockResolvedValue([activeWindow()]) }, + }); + await pipeline.run(geoMonitor()); + expect(defaults.logger.debug).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("maintenance window") })); + expect(defaults.geoChecksService.buildGeoCheck).not.toHaveBeenCalled(); + }); + + it("warns when buildGeoCheck returns null", async () => { + const { pipeline, defaults } = createGeoPipeline(); + await pipeline.run(geoMonitor()); + expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No geo check could be built") })); + }); + + it("adds geo check to buffer on success", async () => { + const geoCheck = { id: "gc-1", monitorId: "m1" }; + const { pipeline, defaults } = createGeoPipeline({ + geoChecksService: { buildGeoCheck: jest.fn().mockResolvedValue(geoCheck) }, + }); + await pipeline.run(geoMonitor()); + expect(defaults.buffer.addGeoCheckToBuffer).toHaveBeenCalledWith(geoCheck); + expect(defaults.logger.debug).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("Geo check job executed") })); + }); + + it("propagates errors (the job glue handles isolation)", async () => { + const { pipeline } = createGeoPipeline({ + geoChecksService: { buildGeoCheck: jest.fn().mockRejectedValue(new Error("api timeout")) }, + }); + await expect(pipeline.run(geoMonitor())).rejects.toThrow("api timeout"); + }); +}); diff --git a/server/test/unit/services/monitorStatusPolicy.test.ts b/server/test/unit/services/monitorStatusPolicy.test.ts new file mode 100644 index 000000000..7b340a3e9 --- /dev/null +++ b/server/test/unit/services/monitorStatusPolicy.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from "@jest/globals"; +import { MonitorStatusPolicy } from "../../../src/worker/worker.monitor-status-policy.ts"; +import type { StatusChangeResult } from "../../../src/types/network.ts"; + +// ── Helpers ────────────────────────────────────────────────────────────────── + +const makeStatusChange = (overrides: Partial & { status?: string }): StatusChangeResult => { + const { status = "up", ...rest } = overrides; + return { + monitor: { id: "m1", status } as any, + statusChanged: false, + prevStatus: "up", + code: 200, + timestamp: 0, + ...rest, + } as StatusChangeResult; +}; + +// ── Tests ──────────────────────────────────────────────────────────────────── + +describe("MonitorStatusPolicy", () => { + const policy = new MonitorStatusPolicy(); + + it("does nothing when statusChanged is false", () => { + const decision = policy.evaluate(makeStatusChange({ status: "down", statusChanged: false, prevStatus: "up" })); + expect(decision).toEqual({ + shouldCreateIncident: false, + shouldResolveIncident: false, + shouldSendNotification: false, + incidentReason: null, + notificationReason: null, + }); + }); + + it("creates incident and notifies when monitor goes down", () => { + const decision = policy.evaluate(makeStatusChange({ status: "down", statusChanged: true, prevStatus: "up", code: 500 })); + expect(decision).toMatchObject({ + shouldCreateIncident: true, + shouldSendNotification: true, + incidentReason: "status_down", + notificationReason: "status_change", + }); + }); + + it("creates incident and notifies when monitor is breached", () => { + const decision = policy.evaluate(makeStatusChange({ status: "breached", statusChanged: true, prevStatus: "up" })); + expect(decision).toMatchObject({ + shouldCreateIncident: true, + shouldSendNotification: true, + incidentReason: "threshold_breach", + notificationReason: "threshold_breach", + }); + }); + + it("resolves incident when monitor recovers from down", () => { + const decision = policy.evaluate(makeStatusChange({ status: "up", statusChanged: true, prevStatus: "down" })); + expect(decision).toMatchObject({ + shouldResolveIncident: true, + shouldSendNotification: true, + shouldCreateIncident: false, + notificationReason: "status_change", + }); + }); + + it("resolves incident when monitor recovers from breached", () => { + const decision = policy.evaluate(makeStatusChange({ status: "up", statusChanged: true, prevStatus: "breached" })); + expect(decision).toMatchObject({ shouldResolveIncident: true, shouldSendNotification: true }); + }); + + it("does not create or resolve for unhandled status transitions", () => { + const decision = policy.evaluate(makeStatusChange({ status: "paused", statusChanged: true, prevStatus: "up" })); + expect(decision).toMatchObject({ shouldCreateIncident: false, shouldResolveIncident: false, shouldSendNotification: false }); + }); +}); diff --git a/server/test/unit/services/reactorDispatcher.test.ts b/server/test/unit/services/reactorDispatcher.test.ts new file mode 100644 index 000000000..0bcc90526 --- /dev/null +++ b/server/test/unit/services/reactorDispatcher.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, it, jest } from "@jest/globals"; +import { ReactorDispatcher } from "../../../src/worker/reactors/reactor.dispatcher.ts"; +import { createMockLogger } from "../../helpers/createMockLogger.ts"; + +const evaluation = { monitor: { id: "m1" } } as any; + +const makeReactor = (name: string, blocking: boolean, impl?: () => Promise) => ({ + name, + blocking, + react: jest.fn(impl ?? (() => Promise.resolve())), +}); + +describe("ReactorDispatcher", () => { + it("runs every reactor with the evaluation", async () => { + const a = makeReactor("a", true); + const b = makeReactor("b", false); + const dispatcher = new ReactorDispatcher(createMockLogger() as any, [a, b] as any); + + await dispatcher.dispatch(evaluation); + + expect(a.react).toHaveBeenCalledWith(evaluation); + expect(b.react).toHaveBeenCalledWith(evaluation); + }); + + it("awaits blocking reactors before resolving", async () => { + let done = false; + const blocking = makeReactor( + "incident", + true, + () => + new Promise((r) => + setTimeout(() => { + done = true; + r(); + }, 10) + ) + ); + const dispatcher = new ReactorDispatcher(createMockLogger() as any, [blocking] as any); + + await dispatcher.dispatch(evaluation); + + expect(done).toBe(true); + }); + + it("does not await non-blocking reactors", async () => { + let done = false; + const nonBlocking = makeReactor( + "notification", + false, + () => + new Promise((r) => + setTimeout(() => { + done = true; + r(); + }, 20) + ) + ); + const dispatcher = new ReactorDispatcher(createMockLogger() as any, [nonBlocking] as any); + + await dispatcher.dispatch(evaluation); + + expect(done).toBe(false); // resolved before the slow fire-and-forget reactor finished + }); + + it("isolates a throwing blocking reactor and still runs later reactors", async () => { + const logger = createMockLogger(); + const bad = makeReactor("incident", true, () => Promise.reject(new Error("db error"))); + const good = makeReactor("after", true); + const dispatcher = new ReactorDispatcher(logger as any, [bad, good] as any); + + await expect(dispatcher.dispatch(evaluation)).resolves.toBeUndefined(); + + expect(good.react).toHaveBeenCalled(); + expect(logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("db error") })); + }); + + it("isolates a rejecting non-blocking reactor (fire-and-forget)", async () => { + const logger = createMockLogger(); + const bad = makeReactor("notification", false, () => Promise.reject(new Error("smtp down"))); + const dispatcher = new ReactorDispatcher(logger as any, [bad] as any); + + await dispatcher.dispatch(evaluation); + await new Promise((r) => setTimeout(r, 10)); // let the detached catch run + + expect(logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("smtp down") })); + }); +}); diff --git a/server/test/unit/services/superSimpleQueueHelper.test.ts b/server/test/unit/services/superSimpleQueueHelper.test.ts index bb4a7c001..1c6f28861 100644 --- a/server/test/unit/services/superSimpleQueueHelper.test.ts +++ b/server/test/unit/services/superSimpleQueueHelper.test.ts @@ -1,14 +1,17 @@ import { describe, expect, it, jest } from "@jest/globals"; -import { QueueHelper } from "../../../src/worker/worker.helper.ts"; +import { WorkerHelper } from "../../../src/worker/worker.helper.ts"; import type { Monitor } from "../../../src/domain/monitors/monitor.types.ts"; import { createMockLogger } from "../../helpers/createMockLogger.ts"; // ── Helpers ────────────────────────────────────────────────────────────────── +// +// WorkerHelper is now thin glue: it wires the scheduler's job functions to the +// CheckPipeline / GeoChecksPipeline (acquire→record→decide) and the +// ReactorDispatcher (react), and owns the cleanup jobs. The pipeline and +// dispatcher internals are tested in checkPipeline.test.ts / reactorDispatcher.test.ts; +// here we test only the glue + cleanup jobs. const createHelper = (overrides?: Record) => { - const maintenanceWindowsRepository = { - findByMonitorId: jest.fn().mockResolvedValue([]), - }; const monitorsRepository = { updateById: jest.fn().mockResolvedValue({}), findAllMonitorIds: jest.fn().mockResolvedValue(["m1"]), @@ -29,55 +32,48 @@ const createHelper = (overrides?: Record) => { const geoChecksRepository = { deleteByMonitorIdsNotIn: jest.fn().mockResolvedValue(0), }; - const statusServiceMock = { - updateMonitorStatus: jest.fn().mockResolvedValue({ monitor: { id: "m1", status: "up" }, statusChanged: false, prevStatus: "up", code: 200 }), - }; - const settingsServiceMock = { + const settingsService = { getDBSettings: jest.fn().mockResolvedValue({ checkTTL: 30 }), }; - const geoChecksServiceMock = { - buildGeoCheck: jest.fn().mockResolvedValue(null), + const checkService = { + deleteOlderThan: jest.fn().mockResolvedValue(0), }; + const checkPipeline = { run: jest.fn().mockResolvedValue(null) }; + const geoCheckPipeline = { run: jest.fn().mockResolvedValue(null) }; + const reactorDispatcher = { dispatch: jest.fn().mockResolvedValue(undefined) }; + const defaults = { logger: createMockLogger(), - networkService: { requestStatus: jest.fn() }, - statusService: statusServiceMock, - notificationsService: { handleNotifications: jest.fn().mockResolvedValue(undefined) }, - checkService: { buildCheck: jest.fn().mockReturnValue({ id: "check-1" }), deleteOlderThan: jest.fn().mockResolvedValue(0) }, - settingsService: settingsServiceMock, - buffer: { addToBuffer: jest.fn(), addGeoCheckToBuffer: jest.fn() }, - incidentService: { handleIncident: jest.fn().mockResolvedValue(undefined) }, - maintenanceWindowsRepository, + checkService, + settingsService, monitorsRepository, teamsRepository, monitorStatsRepository, checksRepository, incidentsRepository, - geoChecksService: geoChecksServiceMock, geoChecksRepository, + reactorDispatcher, + checkPipeline, + geoCheckPipeline, ...overrides, }; - const helper = new QueueHelper( + const helper = new WorkerHelper( defaults.logger as any, - defaults.networkService as any, - defaults.statusService as any, - defaults.notificationsService as any, defaults.checkService as any, defaults.settingsService as any, - defaults.buffer as any, - defaults.incidentService as any, - defaults.maintenanceWindowsRepository as any, defaults.monitorsRepository as any, defaults.teamsRepository as any, defaults.monitorStatsRepository as any, defaults.checksRepository as any, defaults.incidentsRepository as any, - defaults.geoChecksService as any, - defaults.geoChecksRepository as any + defaults.geoChecksRepository as any, + defaults.reactorDispatcher as any, + defaults.checkPipeline as any, + defaults.geoCheckPipeline as any ); - return { helper, defaults, maintenanceWindowsRepository }; + return { helper, defaults }; }; const makeMonitor = (overrides?: Partial): Monitor => @@ -94,7 +90,7 @@ const makeMonitor = (overrides?: Partial): Monitor => // ── Tests ──────────────────────────────────────────────────────────────────── -describe("QueueHelper", () => { +describe("WorkerHelper", () => { describe("serviceName", () => { it("returns JobQueueHelper", () => { const { helper } = createHelper(); @@ -102,278 +98,63 @@ describe("QueueHelper", () => { }); }); - // ── getHeartbeatJob ────────────────────────────────────────────────────── + // ── getHeartbeatJob (glue) ──────────────────────────────────────────────── describe("getHeartbeatJob", () => { - it("throws when monitor id is missing", async () => { - const { helper } = createHelper(); - const job = helper.getHeartbeatJob(); - await expect(job({} as Monitor)).rejects.toThrow("No monitor id"); + it("runs the pipeline and dispatches the evaluation to reactors", async () => { + const evaluation = { monitor: { id: "m1" }, decision: {} }; + const { helper, defaults } = createHelper({ checkPipeline: { run: jest.fn().mockResolvedValue(evaluation) } }); + + await helper.getHeartbeatJob()(makeMonitor()); + + expect(defaults.checkPipeline.run).toHaveBeenCalled(); + expect(defaults.reactorDispatcher.dispatch).toHaveBeenCalledWith(evaluation); }); - it("skips execution and sets maintenance status when in maintenance window", async () => { - const { helper, defaults } = createHelper(); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(true); - const job = helper.getHeartbeatJob(); + it("does not dispatch when the pipeline returns null (skipped)", async () => { + const { helper, defaults } = createHelper({ checkPipeline: { run: jest.fn().mockResolvedValue(null) } }); - await job(makeMonitor({ status: "up" })); + await helper.getHeartbeatJob()(makeMonitor()); - expect(defaults.networkService.requestStatus).not.toHaveBeenCalled(); - expect(defaults.monitorsRepository.updateById).toHaveBeenCalledWith("m1", "team", { status: "maintenance" }); + expect(defaults.reactorDispatcher.dispatch).not.toHaveBeenCalled(); }); - it("skips monitor status update when already in maintenance", async () => { - const { helper, defaults } = createHelper(); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(true); - const job = helper.getHeartbeatJob(); + it("logs and rethrows when the pipeline throws", async () => { + const { helper, defaults } = createHelper({ checkPipeline: { run: jest.fn().mockRejectedValue(new Error("boom")) } }); - await job(makeMonitor({ status: "maintenance" })); - - expect(defaults.monitorsRepository.updateById).not.toHaveBeenCalled(); + await expect(helper.getHeartbeatJob()(makeMonitor())).rejects.toThrow("boom"); + expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: "boom" })); }); - it("throws when network response is null", async () => { - const { helper } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(null) }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); + it("rethrows non-Error failures", async () => { + const { helper } = createHelper({ checkPipeline: { run: jest.fn().mockRejectedValue("unexpected") } }); - await expect(job(makeMonitor())).rejects.toThrow("No network response"); - }); - - it("returns early and warns when buildCheck returns null", async () => { - const networkResponse = { monitorId: "m1", status: true, code: 200, message: "OK" }; - const { helper, defaults } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - checkService: { buildCheck: jest.fn().mockReturnValue(null), deleteOlderThan: jest.fn() }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await job(makeMonitor()); - - expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No check could be built") })); - expect(defaults.statusService.updateMonitorStatus).not.toHaveBeenCalled(); - }); - - it("processes full pipeline: network → check → status → incident", async () => { - const networkResponse = { monitorId: "m1", status: true, code: 200, message: "OK" }; - const { helper, defaults } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await job(makeMonitor()); - - expect(defaults.buffer.addToBuffer).toHaveBeenCalled(); - expect(defaults.statusService.updateMonitorStatus).toHaveBeenCalled(); - expect(defaults.incidentService.handleIncident).toHaveBeenCalled(); - }); - - it("sends notifications when decision says shouldSendNotification", async () => { - const networkResponse = { monitorId: "m1", status: false, code: 500, message: "Error" }; - const statusResult = { monitor: { id: "m1", status: "down" }, statusChanged: true, prevStatus: "up", code: 500 }; - const { helper, defaults } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - statusService: { updateMonitorStatus: jest.fn().mockResolvedValue(statusResult) }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await job(makeMonitor()); - - expect(defaults.notificationsService.handleNotifications).toHaveBeenCalledWith( - statusResult.monitor, - networkResponse, - expect.objectContaining({ shouldSendNotification: true, shouldCreateIncident: true }) - ); - }); - - it("logs error when notifications fail (fire-and-forget)", async () => { - const networkResponse = { monitorId: "m1", status: false, code: 500, message: "Error" }; - const statusResult = { monitor: { id: "m1", status: "down" }, statusChanged: true, prevStatus: "up", code: 500 }; - const notificationsService = { handleNotifications: jest.fn().mockRejectedValue(new Error("smtp down")) }; - const { helper, defaults } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - statusService: { updateMonitorStatus: jest.fn().mockResolvedValue(statusResult) }, - notificationsService, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await job(makeMonitor()); - - // Wait for the fire-and-forget catch to execute - await new Promise((r) => setTimeout(r, 10)); - expect(defaults.logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("smtp down") })); - }); - - it("logs error when notifications fail with non-Error (fire-and-forget)", async () => { - const networkResponse = { monitorId: "m1", status: false, code: 500, message: "Error" }; - const statusResult = { monitor: { id: "m1", status: "down" }, statusChanged: true, prevStatus: "up", code: 500 }; - const notificationsService = { handleNotifications: jest.fn().mockRejectedValue("string error") }; - const { helper, defaults } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - statusService: { updateMonitorStatus: jest.fn().mockResolvedValue(statusResult) }, - notificationsService, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await job(makeMonitor()); - await new Promise((r) => setTimeout(r, 10)); - expect(defaults.logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("Unknown error") })); - }); - - it("logs warning when incident handling fails (fire-and-forget)", async () => { - const networkResponse = { monitorId: "m1", status: true, code: 200, message: "OK" }; - const incidentService = { handleIncident: jest.fn().mockRejectedValue(new Error("db error")) }; - const { helper, defaults } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - incidentService, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await job(makeMonitor()); - await new Promise((r) => setTimeout(r, 10)); - expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("db error") })); - }); - - it("logs warning when incident handling fails with non-Error", async () => { - const networkResponse = { monitorId: "m1", status: true, code: 200, message: "OK" }; - const incidentService = { handleIncident: jest.fn().mockRejectedValue(42) }; - const { helper, defaults } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - incidentService, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await job(makeMonitor()); - await new Promise((r) => setTimeout(r, 10)); - expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("Unknown error") })); - }); - - it("logs and rethrows on unexpected error with non-Error", async () => { - const { helper } = createHelper({ - networkService: { requestStatus: jest.fn().mockRejectedValue("unexpected") }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - - await expect(job(makeMonitor())).rejects.toBe("unexpected"); + await expect(helper.getHeartbeatJob()(makeMonitor())).rejects.toBe("unexpected"); }); }); - // ── getHeartbeatGeoJob ─────────────────────────────────────────────────── + // ── getHeartbeatGeoJob (glue) ───────────────────────────────────────────── describe("getHeartbeatGeoJob", () => { - it("throws nothing when monitor id is missing (logs error)", async () => { + it("runs the geo pipeline", async () => { const { helper, defaults } = createHelper(); - const job = helper.getHeartbeatGeoJob(); - await job({} as Monitor); + await helper.getHeartbeatGeoJob()(makeMonitor()); - expect(defaults.logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: "No monitor id" })); + expect(defaults.geoCheckPipeline.run).toHaveBeenCalled(); }); - it("returns early when geoCheckEnabled is false", async () => { - const { helper, defaults } = createHelper(); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: false })); - - expect(defaults.geoChecksService.buildGeoCheck).not.toHaveBeenCalled(); - }); - - it("returns early when monitor type does not support geo checks", async () => { - const { helper, defaults } = createHelper(); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: true, type: "hardware", geoCheckLocations: ["us-east"] })); - - expect(defaults.logger.debug).toHaveBeenCalledWith( - expect.objectContaining({ message: expect.stringContaining("does not support geo checks") }) - ); - expect(defaults.geoChecksService.buildGeoCheck).not.toHaveBeenCalled(); - }); - - it("returns early when geoCheckLocations is empty", async () => { - const { helper, defaults } = createHelper(); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: [] })); - - expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No geo check locations") })); - }); - - it("returns early when geoCheckLocations is undefined", async () => { - const { helper, defaults } = createHelper(); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: undefined as any })); - - expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No geo check locations") })); - }); - - it("skips when in maintenance window", async () => { - const { helper, defaults } = createHelper(); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(true); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: ["us-east"] })); - - expect(defaults.logger.debug).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("maintenance window") })); - expect(defaults.geoChecksService.buildGeoCheck).not.toHaveBeenCalled(); - }); - - it("warns when buildGeoCheck returns null", async () => { - const { helper, defaults } = createHelper(); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: ["us-east"] })); - - expect(defaults.logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("No geo check could be built") })); - }); - - it("adds geo check to buffer on success", async () => { - const geoCheck = { id: "gc-1", monitorId: "m1" }; - const { helper, defaults } = createHelper({ - geoChecksService: { buildGeoCheck: jest.fn().mockResolvedValue(geoCheck) }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: ["us-east"] })); - - expect(defaults.buffer.addGeoCheckToBuffer).toHaveBeenCalledWith(geoCheck); - expect(defaults.logger.debug).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("Geo check job executed") })); - }); - - it("logs error on unexpected failure without rethrowing", async () => { - const { helper, defaults } = createHelper({ - geoChecksService: { buildGeoCheck: jest.fn().mockRejectedValue(new Error("api timeout")) }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatGeoJob(); - - await job(makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: ["us-east"] })); + it("catches and logs a pipeline error without rethrowing", async () => { + const { helper, defaults } = createHelper({ geoCheckPipeline: { run: jest.fn().mockRejectedValue(new Error("api timeout")) } }); + await expect(helper.getHeartbeatGeoJob()(makeMonitor())).resolves.toBeUndefined(); expect(defaults.logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: "api timeout" })); }); - it("logs 'Unknown error' for non-Error exceptions", async () => { - const { helper, defaults } = createHelper({ - geoChecksService: { buildGeoCheck: jest.fn().mockRejectedValue("string error") }, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatGeoJob(); + it("logs 'Unknown error' for non-Error failures", async () => { + const { helper, defaults } = createHelper({ geoCheckPipeline: { run: jest.fn().mockRejectedValue("string error") } }); - await job(makeMonitor({ geoCheckEnabled: true, type: "http", geoCheckLocations: ["us-east"] })); + await helper.getHeartbeatGeoJob()(makeMonitor()); expect(defaults.logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: "Unknown error", stack: undefined })); }); @@ -400,7 +181,6 @@ describe("QueueHelper", () => { expect(defaults.checksRepository.deleteByMonitorIdsNotIn).toHaveBeenCalledWith(["m1"]); expect(defaults.incidentsRepository.deleteByMonitorIdsNotIn).toHaveBeenCalledWith(["m1"]); expect(defaults.geoChecksRepository.deleteByMonitorIdsNotIn).toHaveBeenCalledWith(["m1"]); - // Logs info for each deleted count > 0 expect(defaults.logger.info).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("2 orphaned monitors") })); expect(defaults.logger.info).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("3 orphaned monitor stats") })); expect(defaults.logger.info).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining("4 orphaned checks") })); @@ -413,7 +193,6 @@ describe("QueueHelper", () => { const job = helper.getCleanupOrphanedJob(); await job(); - // Only start and completion logs, no deletion logs const infoMessages = (defaults.logger.info as jest.Mock).mock.calls.map((c: any) => c[0].message); expect(infoMessages).toContain("Starting cleanup of orphaned data"); expect(infoMessages).toContain("Cleanup of orphaned data completed"); @@ -483,236 +262,4 @@ describe("QueueHelper", () => { expect(defaults.logger.error).toHaveBeenCalledWith(expect.objectContaining({ message: "Unknown error", stack: undefined })); }); }); - - // ── isInMaintenanceWindow ──────────────────────────────────────────────── - - describe("isInMaintenanceWindow", () => { - it("returns true when an active window spans now", async () => { - const now = new Date(); - const { helper, maintenanceWindowsRepository } = createHelper(); - maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ - { - active: true, - start: new Date(now.getTime() - 1000).toISOString(), - end: new Date(now.getTime() + 1000).toISOString(), - repeat: 0, - }, - ]); - await expect(helper.isInMaintenanceWindow("m1", "team")).resolves.toBe(true); - }); - - it("returns true when repeat interval advances window into current time", async () => { - const now = Date.now(); - const { helper, maintenanceWindowsRepository } = createHelper(); - maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ - { - active: true, - start: new Date(now - 7200000).toISOString(), - end: new Date(now - 6600000).toISOString(), - repeat: 3600000, - }, - ]); - await expect(helper.isInMaintenanceWindow("m1", "team")).resolves.toBe(true); - }); - - it("returns false when no active windows exist", async () => { - const { helper } = createHelper(); - await expect(helper.isInMaintenanceWindow("m1", "team")).resolves.toBe(false); - }); - - it("returns false when window is inactive", async () => { - const now = new Date(); - const { helper, maintenanceWindowsRepository } = createHelper(); - maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ - { - active: false, - start: new Date(now.getTime() - 1000).toISOString(), - end: new Date(now.getTime() + 1000).toISOString(), - repeat: 0, - }, - ]); - await expect(helper.isInMaintenanceWindow("m1", "team")).resolves.toBe(false); - }); - - it("returns false when active window is in the past without repeat", async () => { - const now = Date.now(); - const { helper, maintenanceWindowsRepository } = createHelper(); - maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ - { - active: true, - start: new Date(now - 5000).toISOString(), - end: new Date(now - 1000).toISOString(), - repeat: 0, - }, - ]); - await expect(helper.isInMaintenanceWindow("m1", "team")).resolves.toBe(false); - }); - - it("returns false when repeat advances past current time without match", async () => { - const now = Date.now(); - const { helper, maintenanceWindowsRepository } = createHelper(); - // Window was 2 hours ago, lasts 10 minutes, repeats every hour - // First repeat: -1h to -50min (past), second repeat: now to +10min... actually this would match - // Use a repeat that skips over now - maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ - { - active: true, - start: new Date(now - 3600000 * 3 - 300000).toISOString(), // 3h5min ago - end: new Date(now - 3600000 * 3).toISOString(), // 3h ago, 5min window - repeat: 3600000, // 1 hour repeat - }, - ]); - // Advances: -2h5m to -2h, -1h5m to -1h, -5m to 0 — that last one might match if now is within 5min - // Let's use a different setup that clearly doesn't match - maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ - { - active: true, - start: new Date(now - 1800000 - 60000).toISOString(), // 31 min ago - end: new Date(now - 1800000).toISOString(), // 30 min ago, 1 min window - repeat: 3600000, // 1 hour repeat — next window would be in 29 min - }, - ]); - await expect(helper.isInMaintenanceWindow("m1", "team")).resolves.toBe(false); - }); - - it("preserves previous true result via accumulator", async () => { - const now = new Date(); - const { helper, maintenanceWindowsRepository } = createHelper(); - maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ - { - active: true, - start: new Date(now.getTime() - 1000).toISOString(), - end: new Date(now.getTime() + 1000).toISOString(), - repeat: 0, - }, - { - active: false, - start: new Date(now.getTime() - 1000).toISOString(), - end: new Date(now.getTime() + 1000).toISOString(), - repeat: 0, - }, - ]); - // Second window is inactive but first already matched — accumulator should preserve true - await expect(helper.isInMaintenanceWindow("m1", "team")).resolves.toBe(true); - }); - }); - - // ── evaluateMonitorAction (tested indirectly via getHeartbeatJob) ───────── - - describe("evaluateMonitorAction", () => { - const runJobAndGetDecision = async (statusResult: Record) => { - const networkResponse = { monitorId: "m1", status: true, code: 200, message: "OK" }; - const incidentService = { handleIncident: jest.fn().mockResolvedValue(undefined) }; - const notificationsService = { handleNotifications: jest.fn().mockResolvedValue(undefined) }; - const { helper } = createHelper({ - networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, - statusService: { updateMonitorStatus: jest.fn().mockResolvedValue(statusResult) }, - incidentService, - notificationsService, - }); - jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); - const job = helper.getHeartbeatJob(); - await job(makeMonitor()); - return { incidentService, notificationsService }; - }; - - it("does nothing when statusChanged is false", async () => { - const { incidentService, notificationsService } = await runJobAndGetDecision({ - monitor: { id: "m1", status: "up" }, - statusChanged: false, - prevStatus: "up", - code: 200, - }); - expect(incidentService.handleIncident).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - expect.objectContaining({ shouldCreateIncident: false, shouldResolveIncident: false, shouldSendNotification: false }), - expect.anything() - ); - }); - - it("creates incident and notifies when monitor goes down", async () => { - const { incidentService } = await runJobAndGetDecision({ - monitor: { id: "m1", status: "down" }, - statusChanged: true, - prevStatus: "up", - code: 500, - }); - expect(incidentService.handleIncident).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - expect.objectContaining({ - shouldCreateIncident: true, - shouldSendNotification: true, - incidentReason: "status_down", - notificationReason: "status_change", - }), - expect.anything() - ); - }); - - it("creates incident and notifies when monitor is breached", async () => { - const { incidentService } = await runJobAndGetDecision({ - monitor: { id: "m1", status: "breached" }, - statusChanged: true, - prevStatus: "up", - code: 200, - }); - expect(incidentService.handleIncident).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - expect.objectContaining({ - shouldCreateIncident: true, - incidentReason: "threshold_breach", - notificationReason: "threshold_breach", - }), - expect.anything() - ); - }); - - it("resolves incident when monitor recovers from down", async () => { - const { incidentService } = await runJobAndGetDecision({ - monitor: { id: "m1", status: "up" }, - statusChanged: true, - prevStatus: "down", - code: 200, - }); - expect(incidentService.handleIncident).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - expect.objectContaining({ shouldResolveIncident: true, shouldSendNotification: true }), - expect.anything() - ); - }); - - it("resolves incident when monitor recovers from breached", async () => { - const { incidentService } = await runJobAndGetDecision({ - monitor: { id: "m1", status: "up" }, - statusChanged: true, - prevStatus: "breached", - code: 200, - }); - expect(incidentService.handleIncident).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - expect.objectContaining({ shouldResolveIncident: true }), - expect.anything() - ); - }); - - it("does not create or resolve for unhandled status transitions", async () => { - const { incidentService } = await runJobAndGetDecision({ - monitor: { id: "m1", status: "paused" }, - statusChanged: true, - prevStatus: "up", - code: 200, - }); - expect(incidentService.handleIncident).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - expect.objectContaining({ shouldCreateIncident: false, shouldResolveIncident: false, shouldSendNotification: false }), - expect.anything() - ); - }); - }); }); From 0aef8b652f53c21c6127c527d3e51a4616bab213 Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:58:45 -0400 Subject: [PATCH 08/17] fix: use partial index for status page customDomain Replace the sparse unique index with a partial index so multiple status pages without a custom domain no longer collide on null values. --- .../status-page-repository.mongo.ts | 28 +++++++++++++++---- .../domain/status-pages/status-page.model.ts | 11 ++++++-- .../unit/services/statusPageService.test.ts | 9 ++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/server/src/domain/status-pages/status-page-repository.mongo.ts b/server/src/domain/status-pages/status-page-repository.mongo.ts index 78e43a3bd..2dedca4ef 100644 --- a/server/src/domain/status-pages/status-page-repository.mongo.ts +++ b/server/src/domain/status-pages/status-page-repository.mongo.ts @@ -78,9 +78,10 @@ class MongoStatusPagesRepository implements IStatusPagesRepository { create = async (userId: string, teamId: string, image: Express.Multer.File | undefined, data: Partial): Promise => { const { logo, customDomain, ...restData } = data; void logo; + const normalizedCustomDomain = normalizeStatusPageDomain(customDomain); const statusPage = new StatusPageModel({ ...restData, - customDomain: normalizeStatusPageDomain(customDomain), + ...(normalizedCustomDomain ? { customDomain: normalizedCustomDomain } : {}), userId, teamId, }); @@ -133,20 +134,35 @@ class MongoStatusPagesRepository implements IStatusPagesRepository { ): Promise => { const { logo, removeLogo, customDomain, ...restPatch } = patch; void logo; - const updateData: StatusPageUpdateData = { ...restPatch }; + const setData: StatusPageUpdateData = { ...restPatch }; + const unsetData: Record = {}; + if (customDomain !== undefined) { - updateData.customDomain = normalizeStatusPageDomain(customDomain); + const normalizedCustomDomain = normalizeStatusPageDomain(customDomain); + if (normalizedCustomDomain) { + setData.customDomain = normalizedCustomDomain; + } else { + unsetData.customDomain = 1; + } } if (image) { - updateData.logo = { + setData.logo = { data: image.buffer as Buffer, contentType: image.mimetype, }; } else if (removeLogo === "true") { - updateData.logo = null; + setData.logo = null; } - const statusPage = await StatusPageModel.findOneAndUpdate({ teamId, _id: id }, updateData, { + const updateQuery = + unsetData.customDomain !== undefined + ? { + $set: setData, + $unset: unsetData, + } + : setData; + + const statusPage = await StatusPageModel.findOneAndUpdate({ teamId, _id: id }, updateQuery, { new: true, }); diff --git a/server/src/domain/status-pages/status-page.model.ts b/server/src/domain/status-pages/status-page.model.ts index bd6801318..3337fe998 100644 --- a/server/src/domain/status-pages/status-page.model.ts +++ b/server/src/domain/status-pages/status-page.model.ts @@ -67,11 +67,8 @@ const StatusPageSchema = new Schema( }, customDomain: { type: String, - default: null, lowercase: true, trim: true, - sparse: true, - unique: true, }, timezone: { type: String, @@ -136,6 +133,14 @@ const StatusPageSchema = new Schema( { timestamps: true } ); +StatusPageSchema.index( + { customDomain: 1 }, + { + unique: true, + partialFilterExpression: { customDomain: { $type: "string" } }, + } +); + const StatusPageModel = model("StatusPage", StatusPageSchema); export type { StatusPageDocument }; diff --git a/server/test/unit/services/statusPageService.test.ts b/server/test/unit/services/statusPageService.test.ts index 7359b015a..b2c2cdcee 100644 --- a/server/test/unit/services/statusPageService.test.ts +++ b/server/test/unit/services/statusPageService.test.ts @@ -134,6 +134,15 @@ describe("StatusPageService", () => { expect(repo.create).toHaveBeenCalledWith("user-1", "team-1", undefined, expect.objectContaining({ customDomain: "status.example.com" })); }); + + it("allows multiple status pages without a custom domain", async () => { + const { service, repo } = createService(true); + + await service.createStatusPage("user-1", "team-1", undefined, { companyName: "First" }); + await service.createStatusPage("user-1", "team-1", undefined, { companyName: "Second" }); + + expect(repo.create).toHaveBeenCalledTimes(2); + }); }); describe("getStatusPagesByTeamId", () => { From 6a1ab622b3b947994499e8746c1ebd4272fd135c Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:58:50 -0400 Subject: [PATCH 09/17] fix: scope custom domain CORS to public status page routes Only allow registered custom domain origins on public GET/OPTIONS status page API routes instead of the entire application API surface. --- .../api/middleware/statusPagePublicApiPath.ts | 24 +++++++++++++++++++ server/src/app.ts | 23 +++++++++++------- .../statusPagePublicApiPath.test.ts | 19 +++++++++++++++ 3 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 server/src/api/middleware/statusPagePublicApiPath.ts create mode 100644 server/test/unit/middleware/statusPagePublicApiPath.test.ts diff --git a/server/src/api/middleware/statusPagePublicApiPath.ts b/server/src/api/middleware/statusPagePublicApiPath.ts new file mode 100644 index 000000000..a4b5e42d1 --- /dev/null +++ b/server/src/api/middleware/statusPagePublicApiPath.ts @@ -0,0 +1,24 @@ +const PUBLIC_STATUS_PAGE_API_PREFIX = "/api/v1/status-page"; + +export const isPublicStatusPageApiPath = (method: string, path: string): boolean => { + if (!path.startsWith(PUBLIC_STATUS_PAGE_API_PREFIX)) { + return false; + } + + if (method !== "GET" && method !== "OPTIONS") { + return false; + } + + const subPath = path.slice(PUBLIC_STATUS_PAGE_API_PREFIX.length).split("?")[0] ?? ""; + const slug = subPath.replace(/^\//, ""); + + if (slug === "resolve") { + return true; + } + + if (!slug || slug === "team" || slug.includes("/")) { + return false; + } + + return true; +}; diff --git a/server/src/app.ts b/server/src/app.ts index 5e61a0ab0..3bafd4aec 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -13,6 +13,7 @@ import { InitializedServices } from "@/config/services.js"; import { InitializedControllers } from "@/config/controllers.js"; import { EnvConfig } from "@/domain/app-settings/app-settings.service.js"; import { createStatusPageCorsOrigin } from "@/api/middleware/statusPageCorsOrigin.js"; +import { isPublicStatusPageApiPath } from "@/api/middleware/statusPagePublicApiPath.js"; export const createApp = ({ services, @@ -29,17 +30,23 @@ export const createApp = ({ }) => { const allowedOrigin = envSettings.clientHost; const app = express(); + const defaultCorsOptions = { + origin: allowedOrigin, + methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS", + allowedHeaders: ["Content-Type", "Authorization", "Accept-Language"], + credentials: true, + } as const; + const publicStatusPageCorsOrigin = createStatusPageCorsOrigin(allowedOrigin, services.statusPagesRepository); app.use(generalApiLimiter); - app.use( - cors({ - origin: createStatusPageCorsOrigin(allowedOrigin, services.statusPagesRepository), - methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS", - allowedHeaders: ["Content-Type", "Authorization", "Accept-Language"], - credentials: true, - }) - ); + app.use((req, res, next) => { + const corsOptions = isPublicStatusPageApiPath(req.method, req.path) + ? { ...defaultCorsOptions, origin: publicStatusPageCorsOrigin } + : defaultCorsOptions; + + return cors(corsOptions)(req, res, next); + }); app.use(express.static(frontendPath)); diff --git a/server/test/unit/middleware/statusPagePublicApiPath.test.ts b/server/test/unit/middleware/statusPagePublicApiPath.test.ts new file mode 100644 index 000000000..95a799593 --- /dev/null +++ b/server/test/unit/middleware/statusPagePublicApiPath.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "@jest/globals"; +import { isPublicStatusPageApiPath } from "../../../src/api/middleware/statusPagePublicApiPath.ts"; + +describe("isPublicStatusPageApiPath", () => { + it("allows public resolve and slug routes", () => { + expect(isPublicStatusPageApiPath("GET", "/api/v1/status-page/resolve")).toBe(true); + expect(isPublicStatusPageApiPath("OPTIONS", "/api/v1/status-page/resolve")).toBe(true); + expect(isPublicStatusPageApiPath("GET", "/api/v1/status-page/my-status-page")).toBe(true); + expect(isPublicStatusPageApiPath("GET", "/api/v1/status-page/my-status-page?type=uptime")).toBe(true); + }); + + it("rejects authenticated and mutating status page routes", () => { + expect(isPublicStatusPageApiPath("GET", "/api/v1/status-page/team")).toBe(false); + expect(isPublicStatusPageApiPath("POST", "/api/v1/status-page")).toBe(false); + expect(isPublicStatusPageApiPath("PUT", "/api/v1/status-page/64b1f2a3c4d5e6f7a8b9c0d1")).toBe(false); + expect(isPublicStatusPageApiPath("DELETE", "/api/v1/status-page/64b1f2a3c4d5e6f7a8b9c0d1")).toBe(false); + expect(isPublicStatusPageApiPath("GET", "/api/v1/monitors")).toBe(false); + }); +}); From 120708ed47c56027175f08cb4d9efc98b595d809 Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:58:59 -0400 Subject: [PATCH 10/17] perf: cache custom domain CORS origin lookups Avoid hitting the database on every cross-origin request by caching published custom domain checks for five minutes. --- .../api/middleware/statusPageCorsOrigin.ts | 22 ++++++- .../middleware/statusPageCorsOrigin.test.ts | 60 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 server/test/unit/middleware/statusPageCorsOrigin.test.ts diff --git a/server/src/api/middleware/statusPageCorsOrigin.ts b/server/src/api/middleware/statusPageCorsOrigin.ts index 7a48ca45f..acc515bf9 100644 --- a/server/src/api/middleware/statusPageCorsOrigin.ts +++ b/server/src/api/middleware/statusPageCorsOrigin.ts @@ -2,7 +2,16 @@ import type { CorsOptions } from "cors"; import type { IStatusPagesRepository } from "@/domain/status-pages/status-page-repository.interface.js"; import { normalizeStatusPageDomain } from "@/utils/statusPageDomain.js"; +const CUSTOM_DOMAIN_CORS_CACHE_TTL_MS = 5 * 60 * 1000; + +type CustomDomainCorsCacheEntry = { + allowed: boolean; + expiresAt: number; +}; + export const createStatusPageCorsOrigin = (clientHost: string, statusPagesRepository: IStatusPagesRepository): CorsOptions["origin"] => { + const cache = new Map(); + return (origin, callback) => { if (!origin) { callback(null, true); @@ -27,10 +36,21 @@ export const createStatusPageCorsOrigin = (clientHost: string, statusPagesReposi return; } + const cached = cache.get(customDomain); + if (cached && cached.expiresAt > Date.now()) { + callback(null, cached.allowed ? origin : false); + return; + } + statusPagesRepository .findByCustomDomain(customDomain) .then((statusPage) => { - callback(null, statusPage.isPublished ? origin : false); + const allowed = statusPage.isPublished; + cache.set(customDomain, { + allowed, + expiresAt: Date.now() + CUSTOM_DOMAIN_CORS_CACHE_TTL_MS, + }); + callback(null, allowed ? origin : false); }) .catch(() => { callback(null, false); diff --git a/server/test/unit/middleware/statusPageCorsOrigin.test.ts b/server/test/unit/middleware/statusPageCorsOrigin.test.ts new file mode 100644 index 000000000..b0f92cac4 --- /dev/null +++ b/server/test/unit/middleware/statusPageCorsOrigin.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it, jest, beforeEach } from "@jest/globals"; +import { createStatusPageCorsOrigin } from "../../../src/api/middleware/statusPageCorsOrigin.ts"; +import type { IStatusPagesRepository } from "../../../src/domain/status-pages/status-page-repository.interface.ts"; +import type { StatusPage } from "../../../src/domain/status-pages/status-page.type.ts"; + +const makeStatusPage = (overrides?: Partial): StatusPage => + ({ + id: "sp-1", + isPublished: true, + customDomain: "status.example.com", + ...overrides, + }) as StatusPage; + +const createRepo = () => + ({ + findByCustomDomain: jest.fn().mockResolvedValue(makeStatusPage()), + }) as unknown as jest.Mocked>; + +const invokeOrigin = (originFn: ReturnType, origin: string): Promise => + new Promise((resolve, reject) => { + originFn?.(origin, (error, result) => { + if (error) { + reject(error); + return; + } + resolve(result ?? false); + }); + }); + +describe("createStatusPageCorsOrigin", () => { + let repo: jest.Mocked>; + + beforeEach(() => { + repo = createRepo(); + }); + + it("allows the configured client host without a repository lookup", async () => { + const originFn = createStatusPageCorsOrigin("https://checkmate.example.com", repo as IStatusPagesRepository); + + await expect(invokeOrigin(originFn, "https://checkmate.example.com")).resolves.toBe(true); + expect(repo.findByCustomDomain).not.toHaveBeenCalled(); + }); + + it("caches published custom domain lookups", async () => { + const originFn = createStatusPageCorsOrigin("https://checkmate.example.com", repo as IStatusPagesRepository); + + await expect(invokeOrigin(originFn, "https://status.example.com")).resolves.toBe("https://status.example.com"); + await expect(invokeOrigin(originFn, "https://status.example.com")).resolves.toBe("https://status.example.com"); + + expect(repo.findByCustomDomain).toHaveBeenCalledTimes(1); + expect(repo.findByCustomDomain).toHaveBeenCalledWith("status.example.com"); + }); + + it("rejects unpublished custom domains", async () => { + repo.findByCustomDomain.mockResolvedValue(makeStatusPage({ isPublished: false })); + const originFn = createStatusPageCorsOrigin("https://checkmate.example.com", repo as IStatusPagesRepository); + + await expect(invokeOrigin(originFn, "https://status.example.com")).resolves.toBe(false); + }); +}); From 091c36cbf338b673110b0806d76df26d52516468 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 11 Jun 2026 10:40:59 -0700 Subject: [PATCH 11/17] create upload middleware --- server/src/api/middleware/handleErrors.ts | 9 ++++++-- server/src/api/middleware/upload.ts | 27 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 server/src/api/middleware/upload.ts diff --git a/server/src/api/middleware/handleErrors.ts b/server/src/api/middleware/handleErrors.ts index ff591484f..2f817b2af 100755 --- a/server/src/api/middleware/handleErrors.ts +++ b/server/src/api/middleware/handleErrors.ts @@ -1,11 +1,16 @@ import type { NextFunction, Request, Response } from "express"; +import { MulterError } from "multer"; import { logger } from "@/utils/logger.js"; import { AppError } from "@/utils/AppError.js"; +// Multer throws MulterError, send 413 Payload Too Large / 400 +const multerErrorStatus = (error: MulterError): number => (error.code === "LIMIT_FILE_SIZE" ? 413 : 400); + const handleErrors = (error: unknown, req: Request, res: Response, _next: NextFunction) => { - const status = error instanceof AppError ? error.status || 500 : 500; + const status = + error instanceof MulterError ? multerErrorStatus(error) : error instanceof AppError ? error.status || 500 : 500; const message = error instanceof AppError ? error.message : error instanceof Error ? error.message : "Server error"; - const service = error instanceof AppError ? error.service : "unknownService"; + const service = error instanceof AppError ? error.service : error instanceof MulterError ? "uploadMiddleware" : "unknownService"; const method = error instanceof AppError ? error.method : "unknownMethod"; logger.error({ message: message, diff --git a/server/src/api/middleware/upload.ts b/server/src/api/middleware/upload.ts new file mode 100644 index 000000000..c3f257884 --- /dev/null +++ b/server/src/api/middleware/upload.ts @@ -0,0 +1,27 @@ +import multer from "multer"; +import { AppError } from "@/utils/AppError.js"; +import { ImageMimeTypes, MAX_IMAGE_SIZE_BYTES } from "@/types/upload.js"; + +// Reusable multer instance for handling image uploads +const imageUpload = multer({ + limits: { + fileSize: MAX_IMAGE_SIZE_BYTES, + files: 1, + }, + fileFilter: (_req, file, cb) => { + if (!(ImageMimeTypes as readonly string[]).includes(file.mimetype)) { + cb( + new AppError({ + status: 415, + message: "File must be a valid image (jpeg, jpg, or png)", + service: "uploadMiddleware", + method: "fileFilter", + }) + ); + return; + } + cb(null, true); + }, +}); + +export { imageUpload }; From 0ca738b080937a5df1f0f5e96bdfc06cff14a2c1 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 11 Jun 2026 10:41:53 -0700 Subject: [PATCH 12/17] add an upload type with size constraints and acceptable file types --- server/src/types/upload.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 server/src/types/upload.ts diff --git a/server/src/types/upload.ts b/server/src/types/upload.ts new file mode 100644 index 000000000..355688839 --- /dev/null +++ b/server/src/types/upload.ts @@ -0,0 +1,5 @@ +// Shared constraints for user supplied image uploads +export const ImageMimeTypes = ["image/jpeg", "image/png", "image/jpg"] as const; +export type ImageMimeType = (typeof ImageMimeTypes)[number]; + +export const MAX_IMAGE_SIZE_BYTES = 3 * 1024 * 1024; // 3MB From a43363ebfcb232b267b7415e2530e8d4356ce45b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 11 Jun 2026 10:42:05 -0700 Subject: [PATCH 13/17] apply middleware --- server/src/api/routes/authRoute.ts | 12 +++++------- server/src/api/routes/statusPageRoute.ts | 7 +++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/server/src/api/routes/authRoute.ts b/server/src/api/routes/authRoute.ts index 22055ae40..e8ed31f2e 100644 --- a/server/src/api/routes/authRoute.ts +++ b/server/src/api/routes/authRoute.ts @@ -1,10 +1,8 @@ import { Router, RequestHandler } from "express"; -import { isAllowed } from "../middleware/isAllowed.js"; -import multer from "multer"; +import { isAllowed } from "@/api/middleware/isAllowed.js"; +import { imageUpload } from "@/api/middleware/upload.js"; import { IAuthController } from "@/api/controllers/authController.js"; -const upload = multer(); - class AuthRoutes { private router: Router; private authController: IAuthController; @@ -16,7 +14,7 @@ class AuthRoutes { } initRoutes(verifyJWT: RequestHandler) { - this.router.post("/register", upload.single("profileImage"), this.authController.registerUser); + this.router.post("/register", imageUpload.single("profileImage"), this.authController.registerUser); this.router.post("/login", this.authController.loginUser); this.router.post("/recovery/request", this.authController.requestRecovery); @@ -26,13 +24,13 @@ class AuthRoutes { this.router.get("/users/superadmin", this.authController.checkSuperadminExists); this.router.get("/users", verifyJWT, isAllowed(["admin", "superadmin"]), this.authController.getAllUsers); - this.router.post("/users", verifyJWT, isAllowed(["superadmin"]), upload.single("profileImage"), this.authController.createUser); + this.router.post("/users", verifyJWT, isAllowed(["superadmin"]), imageUpload.single("profileImage"), this.authController.createUser); this.router.get("/users/:userId", verifyJWT, isAllowed(["admin", "superadmin"]), this.authController.getUserById); this.router.patch("/users/:userId", verifyJWT, isAllowed(["superadmin"]), this.authController.editUserById); this.router.patch("/users/:userId/password", verifyJWT, isAllowed(["superadmin"]), this.authController.editUserPasswordById); this.router.delete("/users/:userId", verifyJWT, isAllowed(["admin", "superadmin"]), this.authController.deleteUserById); - this.router.patch("/user", verifyJWT, upload.single("profileImage"), isAllowed(["admin", "superadmin", "user"]), this.authController.editUser); + this.router.patch("/user", verifyJWT, imageUpload.single("profileImage"), isAllowed(["admin", "superadmin", "user"]), this.authController.editUser); this.router.delete("/user", verifyJWT, isAllowed(["admin", "superadmin", "user"]), this.authController.deleteUser); } diff --git a/server/src/api/routes/statusPageRoute.ts b/server/src/api/routes/statusPageRoute.ts index 807970e4b..90b759048 100644 --- a/server/src/api/routes/statusPageRoute.ts +++ b/server/src/api/routes/statusPageRoute.ts @@ -1,7 +1,6 @@ import { IStatusPageController } from "@/api/controllers/statusPageController.js"; import { RequestHandler, Router } from "express"; -import multer from "multer"; -const upload = multer(); +import { imageUpload } from "@/api/middleware/upload.js"; class StatusPageRoutes { private router: Router; @@ -16,8 +15,8 @@ class StatusPageRoutes { initRoutes(verifyJWT: RequestHandler, verifyStatusPageAccess: RequestHandler) { this.router.get("/team", verifyJWT, this.statusPageController.getStatusPagesByTeamId); - this.router.post("/", upload.single("logo"), verifyJWT, this.statusPageController.createStatusPage); - this.router.put("/:id", upload.single("logo"), verifyJWT, this.statusPageController.updateStatusPage); + this.router.post("/", imageUpload.single("logo"), verifyJWT, this.statusPageController.createStatusPage); + this.router.put("/:id", imageUpload.single("logo"), verifyJWT, this.statusPageController.updateStatusPage); this.router.get("/:url", verifyStatusPageAccess, this.statusPageController.getStatusPageByUrl); this.router.delete("/:id", verifyJWT, this.statusPageController.deleteStatusPage); From ac77216f0717b73a274aa6bbce37f0b33c4194c1 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 11 Jun 2026 10:42:15 -0700 Subject: [PATCH 14/17] add middleware tests --- .../test/unit/middleware/handleErrors.test.ts | 38 +++++++++++++++++ server/test/unit/middleware/upload.test.ts | 42 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 server/test/unit/middleware/handleErrors.test.ts create mode 100644 server/test/unit/middleware/upload.test.ts diff --git a/server/test/unit/middleware/handleErrors.test.ts b/server/test/unit/middleware/handleErrors.test.ts new file mode 100644 index 000000000..5994cf7d3 --- /dev/null +++ b/server/test/unit/middleware/handleErrors.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it, jest } from "@jest/globals"; +import type { NextFunction, Request, Response } from "express"; +import { MulterError } from "multer"; +import { handleErrors } from "../../../src/api/middleware/handleErrors.ts"; +import { AppError } from "../../../src/utils/AppError.ts"; + +const makeRes = (): Response => { + const res = {} as Response; + res.status = jest.fn().mockReturnValue(res) as unknown as Response["status"]; + res.json = jest.fn().mockReturnValue(res) as unknown as Response["json"]; + return res; +}; + +describe("handleErrors", () => { + it("maps a multer file-size violation to 413 Payload Too Large", () => { + const res = makeRes(); + handleErrors(new MulterError("LIMIT_FILE_SIZE", "profileImage"), {} as Request, res, (() => {}) as NextFunction); + expect(res.status).toHaveBeenCalledWith(413); + }); + + it("maps other multer errors to 400", () => { + const res = makeRes(); + handleErrors(new MulterError("LIMIT_UNEXPECTED_FILE", "profileImage"), {} as Request, res, (() => {}) as NextFunction); + expect(res.status).toHaveBeenCalledWith(400); + }); + + it("preserves AppError status codes", () => { + const res = makeRes(); + handleErrors(new AppError({ status: 415, message: "bad image" }), {} as Request, res, (() => {}) as NextFunction); + expect(res.status).toHaveBeenCalledWith(415); + }); + + it("falls back to 500 for unknown errors", () => { + const res = makeRes(); + handleErrors(new Error("boom"), {} as Request, res, (() => {}) as NextFunction); + expect(res.status).toHaveBeenCalledWith(500); + }); +}); diff --git a/server/test/unit/middleware/upload.test.ts b/server/test/unit/middleware/upload.test.ts new file mode 100644 index 000000000..bbdfb205c --- /dev/null +++ b/server/test/unit/middleware/upload.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from "@jest/globals"; +import type { Request } from "express"; +import { imageUpload } from "../../../src/api/middleware/upload.ts"; +import { MAX_IMAGE_SIZE_BYTES } from "../../../src/types/upload.ts"; +import { AppError } from "../../../src/utils/AppError.ts"; + +// Reaches into the multer instance config to exercise the fileFilter/limits in isolation. +// Guards against regressing GHSA-9xvg-x28f-m78m (pre-auth DoS via unbounded uploads). +const config = (imageUpload as unknown as { fileFilter: Function; limits: { fileSize: number; files: number } }); + +const makeFile = (mimetype: string): Express.Multer.File => + ({ fieldname: "profileImage", originalname: "x", mimetype }) as Express.Multer.File; + +const runFilter = (mimetype: string): Promise<{ error: unknown; accepted: unknown }> => + new Promise((resolve) => { + config.fileFilter({} as Request, makeFile(mimetype), (error: unknown, accepted: unknown) => resolve({ error, accepted })); + }); + +describe("imageUpload middleware", () => { + it("enforces a file size limit so large uploads cannot exhaust memory", () => { + expect(config.limits.fileSize).toBe(MAX_IMAGE_SIZE_BYTES); + expect(config.limits.fileSize).toBeLessThanOrEqual(3 * 1024 * 1024); + }); + + it("limits a request to a single file", () => { + expect(config.limits.files).toBe(1); + }); + + it("accepts allowed image mime types", async () => { + for (const mimetype of ["image/jpeg", "image/png", "image/jpg"]) { + const { error, accepted } = await runFilter(mimetype); + expect(error).toBeNull(); + expect(accepted).toBe(true); + } + }); + + it("rejects non-image uploads with a 415 AppError", async () => { + const { error } = await runFilter("application/octet-stream"); + expect(error).toBeInstanceOf(AppError); + expect((error as AppError).status).toBe(415); + }); +}); From 8af13b04ecb4305ea11b37e42db649b262a664e5 Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:59:01 -0400 Subject: [PATCH 15/17] fix: derive custom domain URL protocol from client host Use the configured client host protocol instead of hardcoding https for custom domain status page links. --- client/src/Utils/statusPageUrl.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/client/src/Utils/statusPageUrl.ts b/client/src/Utils/statusPageUrl.ts index 9c898ee34..ff19f87c6 100644 --- a/client/src/Utils/statusPageUrl.ts +++ b/client/src/Utils/statusPageUrl.ts @@ -2,6 +2,23 @@ import { PUBLIC_STATUS_PAGE_PREFIX, type StatusPage } from "@/Types/StatusPage"; const CLIENT_HOST = import.meta.env.VITE_APP_CLIENT_HOST as string | undefined; +const getClientOriginProtocol = (): string => { + const source = + CLIENT_HOST || (typeof window !== "undefined" ? window.location.origin : undefined); + if (!source) { + return "https:"; + } + + try { + return new URL(source).protocol; + } catch { + return "https:"; + } +}; + +const buildCustomDomainOrigin = (customDomain: string): string => + `${getClientOriginProtocol()}//${customDomain}`; + export const getAppHostname = (): string | null => { const source = CLIENT_HOST || (typeof window !== "undefined" ? window.location.origin : undefined); @@ -33,7 +50,7 @@ export const getStatusPagePublicPath = ( statusPage: Pick ): string => { if (statusPage.customDomain) { - return `https://${statusPage.customDomain}`; + return buildCustomDomainOrigin(statusPage.customDomain); } return `${PUBLIC_STATUS_PAGE_PREFIX}/${statusPage.url}`; @@ -43,7 +60,7 @@ export const getStatusPagePublicUrl = ( statusPage: Pick ): string => { if (statusPage.customDomain) { - return `https://${statusPage.customDomain}`; + return buildCustomDomainOrigin(statusPage.customDomain); } if (typeof window !== "undefined") { From ef049b652155cf5920151166bf27d03ae2910870 Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:55:10 -0400 Subject: [PATCH 16/17] fix: resolve CORS TypeScript error and format server files Type defaultCorsOptions as CorsOptions so allowedHeaders is compatible with the cors middleware. Format files from develop merge for CI. --- server/src/api/middleware/handleErrors.ts | 3 +-- server/src/api/routes/authRoute.ts | 8 +++++++- server/src/app.ts | 6 +++--- server/test/unit/middleware/upload.test.ts | 5 ++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/server/src/api/middleware/handleErrors.ts b/server/src/api/middleware/handleErrors.ts index 2f817b2af..239349b90 100755 --- a/server/src/api/middleware/handleErrors.ts +++ b/server/src/api/middleware/handleErrors.ts @@ -7,8 +7,7 @@ import { AppError } from "@/utils/AppError.js"; const multerErrorStatus = (error: MulterError): number => (error.code === "LIMIT_FILE_SIZE" ? 413 : 400); const handleErrors = (error: unknown, req: Request, res: Response, _next: NextFunction) => { - const status = - error instanceof MulterError ? multerErrorStatus(error) : error instanceof AppError ? error.status || 500 : 500; + const status = error instanceof MulterError ? multerErrorStatus(error) : error instanceof AppError ? error.status || 500 : 500; const message = error instanceof AppError ? error.message : error instanceof Error ? error.message : "Server error"; const service = error instanceof AppError ? error.service : error instanceof MulterError ? "uploadMiddleware" : "unknownService"; const method = error instanceof AppError ? error.method : "unknownMethod"; diff --git a/server/src/api/routes/authRoute.ts b/server/src/api/routes/authRoute.ts index e8ed31f2e..3b202a502 100644 --- a/server/src/api/routes/authRoute.ts +++ b/server/src/api/routes/authRoute.ts @@ -30,7 +30,13 @@ class AuthRoutes { this.router.patch("/users/:userId/password", verifyJWT, isAllowed(["superadmin"]), this.authController.editUserPasswordById); this.router.delete("/users/:userId", verifyJWT, isAllowed(["admin", "superadmin"]), this.authController.deleteUserById); - this.router.patch("/user", verifyJWT, imageUpload.single("profileImage"), isAllowed(["admin", "superadmin", "user"]), this.authController.editUser); + this.router.patch( + "/user", + verifyJWT, + imageUpload.single("profileImage"), + isAllowed(["admin", "superadmin", "user"]), + this.authController.editUser + ); this.router.delete("/user", verifyJWT, isAllowed(["admin", "superadmin", "user"]), this.authController.deleteUser); } diff --git a/server/src/app.ts b/server/src/app.ts index 3bafd4aec..907c0f77d 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -1,6 +1,6 @@ import express from "express"; import path from "path"; -import cors from "cors"; +import cors, { type CorsOptions } from "cors"; import helmet from "helmet"; import compression from "compression"; import cookieParser from "cookie-parser"; @@ -30,12 +30,12 @@ export const createApp = ({ }) => { const allowedOrigin = envSettings.clientHost; const app = express(); - const defaultCorsOptions = { + const defaultCorsOptions: CorsOptions = { origin: allowedOrigin, methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS", allowedHeaders: ["Content-Type", "Authorization", "Accept-Language"], credentials: true, - } as const; + }; const publicStatusPageCorsOrigin = createStatusPageCorsOrigin(allowedOrigin, services.statusPagesRepository); app.use(generalApiLimiter); diff --git a/server/test/unit/middleware/upload.test.ts b/server/test/unit/middleware/upload.test.ts index bbdfb205c..dc172d3da 100644 --- a/server/test/unit/middleware/upload.test.ts +++ b/server/test/unit/middleware/upload.test.ts @@ -6,10 +6,9 @@ import { AppError } from "../../../src/utils/AppError.ts"; // Reaches into the multer instance config to exercise the fileFilter/limits in isolation. // Guards against regressing GHSA-9xvg-x28f-m78m (pre-auth DoS via unbounded uploads). -const config = (imageUpload as unknown as { fileFilter: Function; limits: { fileSize: number; files: number } }); +const config = imageUpload as unknown as { fileFilter: Function; limits: { fileSize: number; files: number } }; -const makeFile = (mimetype: string): Express.Multer.File => - ({ fieldname: "profileImage", originalname: "x", mimetype }) as Express.Multer.File; +const makeFile = (mimetype: string): Express.Multer.File => ({ fieldname: "profileImage", originalname: "x", mimetype }) as Express.Multer.File; const runFilter = (mimetype: string): Promise<{ error: unknown; accepted: unknown }> => new Promise((resolve) => { From 517139cbf48283af1c9573ca7acf2cfc62bcfcc4 Mon Sep 17 00:00:00 2001 From: Ian Troisi <56521951+Cincinnatus101010@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:48:31 -0400 Subject: [PATCH 17/17] fix: cache negative custom domain CORS lookups Use a shorter TTL for rejected origins so unknown domains do not hit the database on every cross-origin request. --- .../api/middleware/statusPageCorsOrigin.ts | 19 ++++++++++++++----- .../middleware/statusPageCorsOrigin.test.ts | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/server/src/api/middleware/statusPageCorsOrigin.ts b/server/src/api/middleware/statusPageCorsOrigin.ts index acc515bf9..338c43472 100644 --- a/server/src/api/middleware/statusPageCorsOrigin.ts +++ b/server/src/api/middleware/statusPageCorsOrigin.ts @@ -2,16 +2,27 @@ import type { CorsOptions } from "cors"; import type { IStatusPagesRepository } from "@/domain/status-pages/status-page-repository.interface.js"; import { normalizeStatusPageDomain } from "@/utils/statusPageDomain.js"; -const CUSTOM_DOMAIN_CORS_CACHE_TTL_MS = 5 * 60 * 1000; +const CUSTOM_DOMAIN_CORS_POSITIVE_CACHE_TTL_MS = 5 * 60 * 1000; +const CUSTOM_DOMAIN_CORS_NEGATIVE_CACHE_TTL_MS = 60 * 1000; type CustomDomainCorsCacheEntry = { allowed: boolean; expiresAt: number; }; +const getCustomDomainCorsCacheTtlMs = (allowed: boolean): number => + allowed ? CUSTOM_DOMAIN_CORS_POSITIVE_CACHE_TTL_MS : CUSTOM_DOMAIN_CORS_NEGATIVE_CACHE_TTL_MS; + export const createStatusPageCorsOrigin = (clientHost: string, statusPagesRepository: IStatusPagesRepository): CorsOptions["origin"] => { const cache = new Map(); + const cacheOriginResult = (customDomain: string, allowed: boolean) => { + cache.set(customDomain, { + allowed, + expiresAt: Date.now() + getCustomDomainCorsCacheTtlMs(allowed), + }); + }; + return (origin, callback) => { if (!origin) { callback(null, true); @@ -46,13 +57,11 @@ export const createStatusPageCorsOrigin = (clientHost: string, statusPagesReposi .findByCustomDomain(customDomain) .then((statusPage) => { const allowed = statusPage.isPublished; - cache.set(customDomain, { - allowed, - expiresAt: Date.now() + CUSTOM_DOMAIN_CORS_CACHE_TTL_MS, - }); + cacheOriginResult(customDomain, allowed); callback(null, allowed ? origin : false); }) .catch(() => { + cacheOriginResult(customDomain, false); callback(null, false); }); }; diff --git a/server/test/unit/middleware/statusPageCorsOrigin.test.ts b/server/test/unit/middleware/statusPageCorsOrigin.test.ts index b0f92cac4..88a854845 100644 --- a/server/test/unit/middleware/statusPageCorsOrigin.test.ts +++ b/server/test/unit/middleware/statusPageCorsOrigin.test.ts @@ -56,5 +56,19 @@ describe("createStatusPageCorsOrigin", () => { const originFn = createStatusPageCorsOrigin("https://checkmate.example.com", repo as IStatusPagesRepository); await expect(invokeOrigin(originFn, "https://status.example.com")).resolves.toBe(false); + await expect(invokeOrigin(originFn, "https://status.example.com")).resolves.toBe(false); + + expect(repo.findByCustomDomain).toHaveBeenCalledTimes(1); + }); + + it("caches unknown custom domain lookups", async () => { + repo.findByCustomDomain.mockRejectedValue(new Error("Status page not found")); + const originFn = createStatusPageCorsOrigin("https://checkmate.example.com", repo as IStatusPagesRepository); + + await expect(invokeOrigin(originFn, "https://unknown.example.com")).resolves.toBe(false); + await expect(invokeOrigin(originFn, "https://unknown.example.com")).resolves.toBe(false); + + expect(repo.findByCustomDomain).toHaveBeenCalledTimes(1); + expect(repo.findByCustomDomain).toHaveBeenCalledWith("unknown.example.com"); }); });