diff --git a/server/openapi.json b/server/openapi.json index 4b710b639..28a22f394 100644 --- a/server/openapi.json +++ b/server/openapi.json @@ -536,6 +536,341 @@ "updatedAt": "2026-04-15T14:30:00.000Z" } }, + "UptimeDetails": { + "type": "object", + "properties": { + "monitorData": { + "type": "object", + "properties": { + "monitor": { + "type": "object", + "properties": { + "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"] + }, + "method": { + "type": "string", + "enum": ["GET", "HEAD"] + }, + "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" + }, + "lastEvaluatedAt": { + "type": "number" + }, + "id": { + "type": "string" + } + }, + "required": [ + "name", + "type", + "url", + "isActive", + "interval", + "status", + "statusWindowSize", + "statusWindowThreshold", + "ignoreTlsErrors", + "useAdvancedMatching", + "method", + "notifications", + "tags", + "cpuAlertThreshold", + "memoryAlertThreshold", + "diskAlertThreshold", + "tempAlertThreshold", + "selectedDisks", + "geoCheckEnabled", + "geoCheckLocations", + "geoCheckInterval", + "teamId", + "userId", + "createdAt", + "updatedAt", + "lastEvaluatedAt", + "id" + ], + "additionalProperties": { + "nullable": true + } + }, + "groupedChecks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bucketDate": { + "type": "string" + }, + "avgResponseTime": { + "type": "number" + }, + "totalChecks": { + "type": "number" + }, + "avgDns": { + "type": "number" + }, + "avgTcp": { + "type": "number" + }, + "avgTls": { + "type": "number" + }, + "avgRequest": { + "type": "number" + }, + "avgFirstByte": { + "type": "number" + }, + "avgDownload": { + "type": "number" + } + }, + "required": [ + "bucketDate", + "avgResponseTime", + "totalChecks", + "avgDns", + "avgTcp", + "avgTls", + "avgRequest", + "avgFirstByte", + "avgDownload" + ] + } + }, + "groupedUpChecks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bucketDate": { + "type": "string" + }, + "avgResponseTime": { + "type": "number" + }, + "totalChecks": { + "type": "number" + } + }, + "required": ["bucketDate", "avgResponseTime", "totalChecks"] + } + }, + "groupedDownChecks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bucketDate": { + "type": "string" + }, + "avgResponseTime": { + "type": "number" + }, + "totalChecks": { + "type": "number" + } + }, + "required": ["bucketDate", "avgResponseTime", "totalChecks"] + } + }, + "groupedAvgResponseTime": { + "type": "number" + }, + "groupedUptimePercentage": { + "type": "number" + } + }, + "required": ["monitor", "groupedChecks", "groupedUpChecks", "groupedDownChecks", "groupedAvgResponseTime", "groupedUptimePercentage"] + }, + "monitorStats": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string" + }, + "monitorId": { + "type": "string" + }, + "avgResponseTime": { + "type": "number" + }, + "maxResponseTime": { + "type": "number" + }, + "totalChecks": { + "type": "number" + }, + "totalUpChecks": { + "type": "number" + }, + "totalDownChecks": { + "type": "number" + }, + "uptimePercentage": { + "type": "number" + }, + "lastCheckTimestamp": { + "type": "number" + }, + "lastResponseTime": { + "type": "number" + }, + "timeOfLastFailure": { + "type": "number" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "monitorId", + "avgResponseTime", + "maxResponseTime", + "totalChecks", + "totalUpChecks", + "totalDownChecks", + "uptimePercentage", + "lastCheckTimestamp", + "lastResponseTime", + "createdAt", + "updatedAt" + ] + } + }, + "required": ["monitorData", "monitorStats"] + }, "NotificationChannelBody": { "oneOf": [ { @@ -2426,6 +2761,9 @@ "minLength": 8, "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" }, + "deleteProfileImage": { + "type": "boolean" + }, "profileImage": { "type": "string", "format": "binary" @@ -3747,7 +4085,7 @@ { "schema": { "type": "string", - "enum": ["manual", "automatic"] + "enum": ["automatic", "manual"] }, "required": false, "name": "resolutionType", @@ -6013,14 +6351,6 @@ "required": true, "name": "dateRange", "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "normalize", - "in": "query" } ], "responses": { @@ -6039,10 +6369,10 @@ "type": "string" }, "data": { - "nullable": true + "$ref": "#/components/schemas/UptimeDetails" } }, - "required": ["success", "msg"] + "required": ["success", "msg", "data"] } } } @@ -10538,7 +10868,7 @@ }, "theme": { "type": "string", - "enum": ["refined", "modern", "bold", "editorial"] + "enum": ["refined", "modern", "bold", "editorial", "minimal"] }, "themeMode": { "type": "string", @@ -10756,7 +11086,7 @@ }, "theme": { "type": "string", - "enum": ["refined", "modern", "bold", "editorial"] + "enum": ["refined", "modern", "bold", "editorial", "minimal"] }, "themeMode": { "type": "string", diff --git a/server/openapi/routes/monitor.ts b/server/openapi/routes/monitor.ts index a941e54bf..01b687678 100644 --- a/server/openapi/routes/monitor.ts +++ b/server/openapi/routes/monitor.ts @@ -16,6 +16,7 @@ import { getHardwareDetailsByIdParamValidation, getHardwareDetailsByIdQueryValidation, monitorResponseSchema, + uptimeDetailsResponseSchema, } from "@/api/validation/monitorValidation.js"; import { updateNotificationsValidation } from "@/api/validation/notificationValidation.js"; @@ -81,6 +82,8 @@ registry.registerPath({ responses: { "200": okJson(z.array(z.string())), ...standardErrors }, }); +const uptimeDetailsObject = uptimeDetailsResponseSchema.openapi("UptimeDetails"); + registry.registerPath({ method: "get", path: "/monitors/uptime/details/{monitorId}", @@ -88,7 +91,7 @@ registry.registerPath({ summary: "Get uptime details for a monitor", security: bearer, request: { params: getUptimeDetailsByIdParamValidation, query: getUptimeDetailsByIdQueryValidation }, - responses: { "200": okUnknown, ...standardErrors }, + responses: { "200": okJson(uptimeDetailsObject), ...standardErrors }, }); registry.registerPath({ diff --git a/server/src/api/validation/monitorValidation.ts b/server/src/api/validation/monitorValidation.ts index 0e8b7b0bd..86db942a5 100644 --- a/server/src/api/validation/monitorValidation.ts +++ b/server/src/api/validation/monitorValidation.ts @@ -311,3 +311,52 @@ export const monitorResponseSchema = z lastEvaluatedAt: z.number(), }) .passthrough(); + +// Grouped-check buckets returned by GET /monitors/uptime/details/{monitorId}. Keep +// aligned with GroupedCheck / GroupedUptimeCheck in domain/checks/check.type.ts. +export const groupedCheckResponseSchema = z.object({ + bucketDate: z.string(), + avgResponseTime: z.number(), + totalChecks: z.number(), +}); + +export const groupedUptimeCheckResponseSchema = groupedCheckResponseSchema.extend({ + avgDns: z.number(), + avgTcp: z.number(), + avgTls: z.number(), + avgRequest: z.number(), + avgFirstByte: z.number(), + avgDownload: z.number(), +}); + +// Keep aligned with MonitorStats in domain/monitor-stats/monitor-stats.type.ts. +export const monitorStatsResponseSchema = z.object({ + id: z.string(), + monitorId: z.string(), + avgResponseTime: z.number(), + maxResponseTime: z.number(), + totalChecks: z.number(), + totalUpChecks: z.number(), + totalDownChecks: z.number(), + uptimePercentage: z.number(), + lastCheckTimestamp: z.number(), + lastResponseTime: z.number(), + timeOfLastFailure: z.number().optional(), + createdAt: z.string(), + updatedAt: z.string(), +}); + +// Response of GET /monitors/uptime/details/{monitorId}. Keep aligned with +// UptimeDetailsResult in domain/monitors/monitor.type.ts; the monitor here is the +// repository's domain entity, which serializes `id` rather than `_id`. +export const uptimeDetailsResponseSchema = z.object({ + monitorData: z.object({ + monitor: monitorResponseSchema.omit({ _id: true }).extend({ id: z.string() }), + groupedChecks: z.array(groupedUptimeCheckResponseSchema), + groupedUpChecks: z.array(groupedCheckResponseSchema), + groupedDownChecks: z.array(groupedCheckResponseSchema), + groupedAvgResponseTime: z.number(), + groupedUptimePercentage: z.number(), + }), + monitorStats: monitorStatsResponseSchema.nullable(), +});