diff --git a/server/openapi.json b/server/openapi.json index cfb1c5aa4..ec3a7f925 100644 --- a/server/openapi.json +++ b/server/openapi.json @@ -1,125 +1,1125 @@ { - "openapi": "3.1.0", + "openapi": "3.0.0", "info": { "title": "Checkmate API", - "summary": "Checkmate OpenAPI Specifications", - "description": "Checkmate is an open source monitoring tool used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.", - "contact": { - "name": "API Support", - "url": "mailto:support@bluewavelabs.ca", - "email": "support@bluewavelabs.ca" - }, - "license": { - "name": "AGPLv3", - "url": "https://github.com/bluewave-labs/checkmate/tree/HEAD/LICENSE" - }, - "version": "2.3" + "version": "1.0.0", + "description": "Generated from Zod validation schemas. Source of truth: server/src/validation/*.ts" }, "servers": [ { - "url": "http://localhost:{PORT}/{API_PATH}", - "description": "Local Development Server", - "variables": { - "PORT": { - "default": "52345" - }, - "API_PATH": { - "default": "api/v1" - } - } - }, - { - "url": "https://checkmate-demo.bluewavelabs.ca/{API_PATH}", - "description": "Checkmate Demo Server", - "variables": { - "API_PATH": { - "default": "api/v1" - } - } + "url": "/api/v1", + "description": "Current server" } ], "tags": [ { "name": "auth", - "description": "Authentication and user management" - }, - { - "name": "invite", - "description": "Team invitation management" + "description": "Sign up, log in, password reset, and the authenticated user's profile." }, { "name": "monitors", - "description": "Monitor management (uptime, page speed, hardware)" + "description": "Configure and inspect monitors (HTTP, port, ping, hardware, docker, game, gRPC) for the caller's team." }, { "name": "checks", - "description": "Monitor check results and history" + "description": "Individual monitoring check results, including history, summaries, and bulk deletes." }, { - "name": "maintenance-window", - "description": "Scheduled maintenance windows" - }, - { - "name": "queue", - "description": "Job queue management" - }, - { - "name": "status-page", - "description": "Public status page management" - }, - { - "name": "settings", - "description": "Application configuration settings" - }, - { - "name": "logs", - "description": "Application logs and diagnostics" - }, - { - "name": "notifications", - "description": "Notification integrations (email, slack, discord, etc.)" - }, - { - "name": "diagnostic", - "description": "System health and performance diagnostics" + "name": "geo-checks", + "description": "Geographic check results for monitors with multi-region probing enabled." }, { "name": "incidents", - "description": "Incident management and resolution" + "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", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + }, + "required": ["user"] + }, + "User": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "65f1c2a4d8b9e0123456789a" + }, + "firstName": { + "type": "string", + "example": "Ada" + }, + "lastName": { + "type": "string", + "example": "Lovelace" + }, + "email": { + "type": "string", + "example": "ada@example.com" + }, + "role": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["admin"] + }, + "teamId": { + "type": "string", + "example": "65f1c2a4d8b9e01234567890" + }, + "profileImage": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "example": "2026-04-01T10:00:00.000Z" + }, + "updatedAt": { + "type": "string", + "example": "2026-04-15T14:30:00.000Z" + } + }, + "required": ["_id", "firstName", "lastName", "email", "role", "createdAt", "updatedAt"], + "additionalProperties": { + "nullable": true + } + }, + "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", + "example": "65f1c2a4d8b9e0123456789a" + }, + "name": { + "type": "string", + "example": "Marketing site" + }, + "description": { + "type": "string", + "example": "Production marketing site monitored from the EU region" + }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "unknown"], + "example": "http" + }, + "url": { + "type": "string", + "example": "https://www.example.com" + }, + "port": { + "type": "number", + "example": 443 + }, + "isActive": { + "type": "boolean", + "example": true + }, + "interval": { + "type": "number", + "example": 60000 + }, + "status": { + "type": "string", + "enum": ["up", "down", "paused", "initializing", "maintenance", "breached"], + "example": "up" + }, + "statusWindowSize": { + "type": "number", + "example": 5 + }, + "statusWindowThreshold": { + "type": "number", + "example": 3 + }, + "ignoreTlsErrors": { + "type": "boolean", + "example": false + }, + "useAdvancedMatching": { + "type": "boolean", + "example": false + }, + "jsonPath": { + "type": "string" + }, + "expectedValue": { + "type": "string" + }, + "matchMethod": { + "type": "string", + "enum": ["equal", "include", "regex"] + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["65f1c2a4d8b9e0123456789b"] + }, + "secret": { + "type": "string" + }, + "cpuAlertThreshold": { + "type": "number", + "example": 90 + }, + "memoryAlertThreshold": { + "type": "number", + "example": 90 + }, + "diskAlertThreshold": { + "type": "number", + "example": 90 + }, + "tempAlertThreshold": { + "type": "number", + "example": 80 + }, + "selectedDisks": { + "type": "array", + "items": { + "type": "string" + }, + "example": [] + }, + "gameId": { + "type": "string" + }, + "grpcServiceName": { + "type": "string" + }, + "group": { + "type": "string", + "nullable": true + }, + "geoCheckEnabled": { + "type": "boolean", + "example": false + }, + "geoCheckLocations": { + "type": "array", + "items": { + "type": "string", + "enum": ["EU", "NA", "AS", "SA", "AF", "OC"] + }, + "example": [] + }, + "geoCheckInterval": { + "type": "number", + "example": 300000 + }, + "teamId": { + "type": "string", + "example": "65f1c2a4d8b9e01234567890" + }, + "userId": { + "type": "string", + "example": "65f1c2a4d8b9e01234567891" + }, + "createdAt": { + "type": "string", + "example": "2026-04-01T10:00:00.000Z" + }, + "updatedAt": { + "type": "string", + "example": "2026-04-15T14:30:00.000Z" + } + }, + "required": [ + "_id", + "name", + "type", + "url", + "isActive", + "interval", + "status", + "statusWindowSize", + "statusWindowThreshold", + "ignoreTlsErrors", + "useAdvancedMatching", + "notifications", + "cpuAlertThreshold", + "memoryAlertThreshold", + "diskAlertThreshold", + "tempAlertThreshold", + "selectedDisks", + "geoCheckEnabled", + "geoCheckLocations", + "geoCheckInterval", + "teamId", + "userId", + "createdAt", + "updatedAt" + ], + "additionalProperties": { + "nullable": true + } + }, + "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" + } + ], + "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" + } + } + }, + "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" + } + } + }, + "parameters": {} + }, "paths": { "/auth/register": { "post": { "tags": ["auth"], - "summary": "Register new user", - "description": "Register a new user account with profile information", + "summary": "Register a new user (first user becomes superadmin)", "requestBody": { - "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/RegisterRequest" + "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": "User registered successfully", + "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AuthResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/AuthPayload" + } + }, + "required": ["success", "msg", "data"] } } } }, - "422": { - "$ref": "#/components/responses/ValidationError" + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } } } @@ -127,195 +1127,114 @@ "/auth/login": { "post": { "tags": ["auth"], - "summary": "User login", - "description": "Authenticate user with email and password", + "summary": "Log in", "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LoginRequest" + "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": "Login successful", + "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AuthResponse" + "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": { - "$ref": "#/components/responses/Unauthorized" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - } - } - }, - "/auth/user/{userId}": { - "put": { - "tags": ["auth"], - "summary": "Update user profile", - "description": "Update user information including profile image", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/UserUpdateRequest" - } - } - } - }, - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["auth"], - "summary": "Delete user account", - "description": "Permanently delete user account and associated data", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/auth/users": { - "get": { - "tags": ["auth"], - "summary": "Get all users", - "description": "Retrieve all users (admin/superadmin only)", - "responses": { - "200": { - "description": "Users retrieved successfully", + "description": "Invalid credentials", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - } - } + "msg": { + "type": "string" } - ] - } - } - } - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/auth/users/superadmin": { - "get": { - "tags": ["auth"], - "summary": "Check superadmin exists", - "description": "Check if a superadmin account exists in the system", - "responses": { - "200": { - "description": "Check completed", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "exists": { - "type": "boolean" - } - } - } - } - } - ] + }, + "required": ["success", "msg"] } } } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } } } @@ -323,35 +1242,73 @@ "/auth/recovery/request": { "post": { "tags": ["auth"], - "summary": "Request password reset", - "description": "Send password reset email to user", + "summary": "Request a password recovery email", "requestBody": { - "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["email"], "properties": { "email": { "type": "string", - "format": "email", - "description": "User email address" + "format": "email" } - } + }, + "required": ["email"] + }, + "example": { + "email": "ada@example.com" } } } }, "responses": { "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "description": "User not found" + "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": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } } } @@ -359,34 +1316,70 @@ "/auth/recovery/validate": { "post": { "tags": ["auth"], - "summary": "Validate recovery token", - "description": "Validate password reset token", + "summary": "Validate a password recovery token", "requestBody": { - "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["recoveryToken"], "properties": { "recoveryToken": { "type": "string", - "description": "Password reset token" + "minLength": 1 } - } + }, + "required": ["recoveryToken"] } } } }, "responses": { "200": { - "$ref": "#/components/responses/Success" - }, - "400": { - "description": "Invalid or expired token" + "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": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } } } @@ -394,308 +1387,89 @@ "/auth/recovery/reset": { "post": { "tags": ["auth"], - "summary": "Reset password", - "description": "Reset user password with recovery token", + "summary": "Reset password using a recovery token", "requestBody": { - "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["recoveryToken", "password"], "properties": { "recoveryToken": { "type": "string", - "description": "Password reset token" + "minLength": 1 }, "password": { "type": "string", - "format": "password", - "description": "New password" + "minLength": 8, + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" + }, + "confirm": { + "type": "string" } - } + }, + "required": ["recoveryToken", "password"] } } } }, "responses": { "200": { - "$ref": "#/components/responses/Success" - }, - "400": { - "description": "Invalid token or password requirements not met" + "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": { - "$ref": "#/components/responses/InternalServerError" + "description": "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 monitor", - "description": "Create a new monitoring endpoint", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMonitorRequest" - } - } - } - }, - "responses": { - "201": { - "description": "Monitor created successfully", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/Monitor" - } - } - } - ] - } - } - } - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["monitors"], - "summary": "Delete all monitors", - "description": "Delete all monitors (superadmin only)", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/team": { + "/auth/users/superadmin": { "get": { - "tags": ["monitors"], - "summary": "Get monitors by team", - "description": "Get monitors filtered by team with optional parameters", - "parameters": [ - { - "name": "type", - "in": "query", - "description": "Filter by monitor type", - "schema": { - "oneOf": [ - { - "type": "string", - "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] - } - } - ] - } - }, - { - "name": "filter", - "in": "query", - "description": "Search filter value", - "schema": { - "type": "string" - } - } - ], + "tags": ["auth"], + "summary": "Check whether a superadmin user exists", "responses": { "200": { - "description": "Team monitors retrieved successfully", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Monitor" - } - }, - "totalCount": { - "type": "integer" - } - } - } - } - } - ] - } - } - } - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/team/with-checks": { - "get": { - "tags": ["monitors"], - "summary": "Get monitors with recent checks", - "description": "Get team monitors with their most recent check results", - "parameters": [ - { - "name": "limit", - "in": "query", - "description": "Number of recent checks to include per monitor", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - } - }, - { - "name": "page", - "in": "query", - "description": "Zero-based page index for pagination", - "schema": { - "type": "integer", - "minimum": 0 - } - }, - { - "name": "rowsPerPage", - "in": "query", - "description": "Number of monitors per page", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - } - }, - { - "name": "filter", - "in": "query", - "description": "Search filter value", - "schema": { - "type": "string" - } - }, - { - "name": "field", - "in": "query", - "description": "Field to sort by", - "schema": { - "type": "string" - } - }, - { - "name": "order", - "in": "query", - "description": "Sort order", - "schema": { - "type": "string", - "enum": ["asc", "desc"] - } - }, - { - "name": "type", - "in": "query", - "description": "Filter by monitor type", - "schema": { - "oneOf": [ - { - "type": "string", - "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] - } - } - ] - } - }, - { - "name": "explain", - "in": "query", - "description": "Include MongoDB query explain output (debug)", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "Monitors with checks retrieved successfully" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/team/groups": { - "get": { - "tags": ["monitors"], - "summary": "Get monitor groups by team", - "description": "Get all monitor groups for the authenticated user's team", - "responses": { - "200": { - "description": "Monitor groups retrieved successfully", + "description": "OK", "content": { "application/json": { "schema": { @@ -703,316 +1477,90 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [true] }, "msg": { - "type": "string", - "example": "Monitor groups retrieved successfully" + "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": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "monitors": { - "type": "array", - "items": { - "type": "string" - } - } - } + "$ref": "#/components/schemas/User" } } - } + }, + "required": ["success", "msg", "data"] } } } }, "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get monitor by ID", - "description": "Retrieve a specific monitor by its ID", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "description": "Monitor ID", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Monitor retrieved successfully", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/Monitor" - } - } - } - ] - } - } - } - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "patch": { - "tags": ["monitors"], - "summary": "Update monitor", - "description": "Update an existing monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateMonitorRequest" - } - } - } - }, - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["monitors"], - "summary": "Delete monitor", - "description": "Delete a specific monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/uptime/details/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get uptime details", - "description": "Get detailed uptime statistics for a monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Uptime details retrieved successfully" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/hardware/details/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get hardware monitoring details", - "description": "Get hardware performance metrics for a monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Hardware details retrieved successfully" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/pause/{monitorId}": { - "post": { - "tags": ["monitors"], - "summary": "Pause/unpause monitor", - "description": "Toggle monitor active status", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/pagespeed/details/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get PageSpeed monitor details", - "description": "Get detailed PageSpeed monitoring data including performance metrics and Core Web Vitals", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "description": "The PageSpeed monitor ID", - "schema": { - "type": "string" - } - }, - { - "name": "sortOrder", - "in": "query", - "description": "Sort order for results", - "schema": { - "type": "string", - "enum": ["asc", "desc"], - "default": "desc" - } - }, - { - "name": "limit", - "in": "query", - "description": "Number of results to return", - "schema": { - "type": "integer", - "default": 50 - } - } - ], - "responses": { - "200": { - "description": "PageSpeed details retrieved successfully", + "description": "Unauthorized", "content": { "application/json": { "schema": { @@ -1020,839 +1568,2577 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [false] }, "msg": { - "type": "string", - "example": "PageSpeed details retrieved successfully" + "type": "string" }, "data": { - "type": "object", - "properties": { - "monitor": { - "$ref": "#/components/schemas/Monitor" - }, - "checks": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Check" - } - } - } + "nullable": true } - } + }, + "required": ["success", "msg"] } } } }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/certificate/{monitorId}": { - "get": { - "tags": ["monitors"], - "summary": "Get SSL certificate info", - "description": "Get SSL certificate information for a monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Certificate information retrieved successfully" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/demo": { - "post": { - "tags": ["monitors"], - "summary": "Add demo monitors", - "description": "Add preconfigured demo monitors for testing", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/export/json": { - "get": { - "tags": ["monitors"], - "summary": "Export monitors to JSON", - "description": "Export all monitors to JSON format (admin/superadmin only)", - "responses": { - "200": { - "description": "JSON file generated successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "msg": { - "type": "string", - "example": "Monitors exported successfully" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Monitor" - } - } - } - } - } - } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, "403": { - "$ref": "#/components/responses/Forbidden" + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "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": [] } - ] - } - }, - "/monitors/bulk": { - "post": { - "tags": ["monitors"], - "summary": "Bulk import monitors", - "description": "Import monitors from CSV file", + ], "requestBody": { - "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { - "csvFile": { + "firstName": { "type": "string", - "format": "binary", - "description": "CSV file containing monitor data" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Monitors imported successfully" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/test-email": { - "post": { - "tags": ["monitors"], - "summary": "Send test email", - "description": "Send a test email notification", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/settings": { - "get": { - "tags": ["settings"], - "summary": "Get application settings", - "description": "Retrieve current application configuration", - "responses": { - "200": { - "description": "Settings retrieved successfully", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" + "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"] }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/AppSettings" - } - } - } - ] - } - } - } - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "put": { - "tags": ["settings"], - "summary": "Update application settings", - "description": "Update application configuration (admin/superadmin only)", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AppSettings" - } - } - } - }, - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/settings/test-email": { - "post": { - "tags": ["settings"], - "summary": "Send test email", - "description": "Send test email to verify email configuration", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/notifications": { - "post": { - "tags": ["notifications"], - "summary": "Create notification", - "description": "Create a new notification integration", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateNotificationRequest" + "minItems": 1 + }, + "profileImage": { + "type": "string", + "format": "binary" + } + }, + "required": ["firstName", "lastName", "email", "password", "role", "profileImage"] } } } }, "responses": { "201": { - "description": "Notification created successfully" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/notifications/team": { - "get": { - "tags": ["notifications"], - "summary": "Get team notifications", - "description": "Get all notification configurations for the team", - "responses": { - "200": { - "description": "Notifications retrieved successfully", + "description": "User created", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Notification" - } - } - } + "msg": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/User" } - ] + }, + "required": ["success", "msg", "data"] } } } }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/notifications/{id}": { - "get": { - "tags": ["notifications"], - "summary": "Get notification by ID", - "description": "Retrieve a specific notification configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Notification retrieved successfully" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "put": { - "tags": ["notifications"], - "summary": "Update notification", - "description": "Update an existing notification configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateNotificationRequest" - } - } - } - }, - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["notifications"], - "summary": "Delete notification", - "description": "Delete a notification configuration", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/notifications/test": { - "post": { - "tags": ["notifications"], - "summary": "Test notification", - "description": "Send a test notification to verify configuration", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/notifications/test/all": { - "post": { - "tags": ["notifications"], - "summary": "Test all notifications", - "description": "Send test notifications to all configured integrations", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/logs": { - "get": { - "tags": ["logs"], - "summary": "Get application logs", - "description": "Retrieve application logs (admin/superadmin only)", - "parameters": [ - { - "name": "level", - "in": "query", - "description": "Log level filter", - "schema": { - "type": "string", - "enum": ["error", "warn", "info", "debug"] - } - }, - { - "name": "limit", - "in": "query", - "description": "Number of log entries to return", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1000, - "default": 100 - } - } - ], - "responses": { - "200": { - "description": "Logs retrieved successfully" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/diagnostic/system": { - "get": { - "tags": ["diagnostic"], - "summary": "Get system diagnostics", - "description": "Get system health and performance metrics (admin/superadmin only)", - "responses": { - "200": { - "description": "System diagnostics retrieved successfully", + "401": { + "description": "Unauthorized", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/SystemDiagnostics" - } - } + "msg": { + "type": "string" + }, + "data": { + "nullable": true } - ] + }, + "required": ["success", "msg"] } } } }, "403": { - "$ref": "#/components/responses/Forbidden" + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/auth/user": { - "put": { - "tags": ["auth"], - "summary": "Update current user profile", - "description": "Update authenticated user's profile information", - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/UserUpdateRequest" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } } } } - }, - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["auth"], - "summary": "Delete current user account", - "description": "Delete authenticated user's account and associated data", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] + } } }, "/auth/users/{userId}": { "get": { "tags": ["auth"], - "summary": "Get user by ID", - "description": "Get a specific user by ID (superadmin only)", + "summary": "Get a user by id", + "security": [ + { + "bearerAuth": [] + } + ], "parameters": [ { - "name": "userId", - "in": "path", - "required": true, "schema": { - "type": "string" - } + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" } ], "responses": { "200": { - "description": "User retrieved successfully", + "description": "OK", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/User" - } - } + "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": { - "$ref": "#/components/responses/NotFound" - }, - "403": { - "$ref": "#/components/responses/Forbidden" + "description": "User not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "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": [] } - ] - }, - "put": { - "tags": ["auth"], - "summary": "Update user by ID", - "description": "Update a specific user by ID (superadmin only)", + ], "parameters": [ { - "name": "userId", - "in": "path", - "required": true, "schema": { - "type": "string" - } + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserUpdateRequest" + "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": { - "$ref": "#/components/responses/Success" + "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" + } + } + } }, - "404": { - "$ref": "#/components/responses/NotFound" + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "403": { - "$ref": "#/components/responses/Forbidden" - }, - "422": { - "$ref": "#/components/responses/ValidationError" + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "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": { - "put": { + "patch": { "tags": ["auth"], - "summary": "Update user password by ID", - "description": "Update a specific user's password by ID (superadmin only)", + "summary": "Change a user's password (superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], "parameters": [ { - "name": "userId", - "in": "path", - "required": true, "schema": { - "type": "string" - } + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "userId", + "in": "path" } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["password"], "properties": { "password": { "type": "string", - "format": "password", "minLength": 8, - "description": "New password for the user" + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$" } - } + }, + "required": ["password"] } } } }, "responses": { "200": { - "$ref": "#/components/responses/Success" + "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" + } + } + } }, - "404": { - "$ref": "#/components/responses/NotFound" + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "403": { - "$ref": "#/components/responses/Forbidden" - }, - "422": { - "$ref": "#/components/responses/ValidationError" + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "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" + }, + "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"] + } + } + } + } + } } }, - "/invite": { + "/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"] + }, + "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"] + }, + "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": "Create invite token", - "description": "Create a new invitation token", + "summary": "Send an invite email (admin/superadmin)", + "security": [ + { + "bearerAuth": [] + } + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["email", "role"], "properties": { "email": { "type": "string", @@ -1862,292 +4148,210 @@ "type": "array", "items": { "type": "string", - "enum": ["user", "admin"] - } - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Invite token created successfully" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/invite/send": { - "post": { - "tags": ["invite"], - "summary": "Send invitation email", - "description": "Send invitation email to user", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["email"], - "properties": { - "email": { + "enum": ["user", "admin", "superadmin", "demo"] + }, + "minItems": 1 + }, + "teamId": { "type": "string", - "format": "email" + "minLength": 1 } - } + }, + "required": ["email", "role", "teamId"] } } } }, "responses": { "200": { - "$ref": "#/components/responses/Success" + "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": { - "$ref": "#/components/responses/Forbidden" + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } - }, - "security": [ - { - "bearerAuth": [] - } - ] + } } }, "/invite/verify": { "post": { "tags": ["invite"], - "summary": "Verify invitation token", - "description": "Verify an invitation token", + "summary": "Verify an invite token", "requestBody": { - "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["token"], "properties": { "token": { - "type": "string" + "type": "string", + "minLength": 1 } - } + }, + "required": ["token"] } } } }, "responses": { "200": { - "$ref": "#/components/responses/Success" - }, - "400": { - "description": "Invalid or expired token" + "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": { - "$ref": "#/components/responses/InternalServerError" + "description": "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": "Get checks by monitor", - "description": "Get all checks for a specific monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Checks retrieved successfully" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, + "/invite": { + "post": { + "tags": ["invite"], + "summary": "Create an invite token (admin/superadmin)", "security": [ { "bearerAuth": [] } - ] - }, - "delete": { - "tags": ["checks"], - "summary": "Delete checks by monitor", - "description": "Delete all checks for a specific monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/team": { - "get": { - "tags": ["checks"], - "summary": "Get checks by team", - "description": "Get all checks for team monitors", - "responses": { - "200": { - "description": "Team checks retrieved successfully" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "delete": { - "tags": ["checks"], - "summary": "Delete team checks", - "description": "Delete all checks for team (admin/superadmin only)", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/team/summary": { - "get": { - "tags": ["checks"], - "summary": "Get team checks summary", - "description": "Get summary statistics for team checks", - "responses": { - "200": { - "description": "Team checks summary retrieved successfully" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/team/ttl": { - "put": { - "tags": ["checks"], - "summary": "Update checks TTL", - "description": "Update time-to-live for checks (admin/superadmin only)", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["ttl"], - "properties": { - "ttl": { - "type": "integer", - "minimum": 1, - "description": "Time to live in days" - } - } - } - } - } - }, - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "422": { - "$ref": "#/components/responses/ValidationError" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/check/{checkId}": { - "put": { - "tags": ["checks"], - "summary": "Acknowledge check", - "description": "Acknowledge a specific check", - "parameters": [ - { - "name": "checkId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } ], "requestBody": { "content": { @@ -2155,70 +4359,31 @@ "schema": { "type": "object", "properties": { - "ack": { - "type": "boolean", - "description": "Acknowledgement status" + "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": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/{action}": { - "put": { - "tags": ["checks"], - "summary": "Acknowledge all checks for all monitors", - "description": "Acknowledge all checks across all monitors for the team", - "parameters": [ - { - "name": "action", - "in": "path", - "required": true, - "description": "Action to perform (e.g., 'ack')", - "schema": { - "type": "string", - "enum": ["ack"] - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["ack"], - "properties": { - "ack": { - "type": "boolean", - "description": "Acknowledgement status to set" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Checks acknowledged successfully", + "description": "OK", "content": { "application/json": { "schema": { @@ -2226,81 +4391,31 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [true] }, "msg": { - "type": "string", - "example": "All checks acknowledged successfully" + "type": "string" }, "data": { "type": "object", - "description": "Updated checks information" + "properties": { + "token": { + "type": "string" + } + }, + "required": ["token"], + "additionalProperties": { + "nullable": true + } } - } + }, + "required": ["success", "msg", "data"] } } } }, "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/checks/{action}/{monitorId}": { - "put": { - "tags": ["checks"], - "summary": "Acknowledge all checks for a specific monitor", - "description": "Acknowledge all checks for a specific monitor", - "parameters": [ - { - "name": "action", - "in": "path", - "required": true, - "description": "Action to perform (e.g., 'ack')", - "schema": { - "type": "string", - "enum": ["ack"] - } - }, - { - "name": "monitorId", - "in": "path", - "required": true, - "description": "Monitor ID to acknowledge checks for", - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["ack"], - "properties": { - "ack": { - "type": "boolean", - "description": "Acknowledgement status to set" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Checks acknowledged successfully", + "description": "Unauthorized", "content": { "application/json": { "schema": { @@ -2308,216 +4423,760 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [false] }, "msg": { - "type": "string", - "example": "All checks acknowledged successfully" + "type": "string" }, "data": { - "type": "object", - "description": "Updated checks information" + "nullable": true } - } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "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": { - "$ref": "#/components/responses/Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, - "404": { - "$ref": "#/components/responses/NotFound" + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } - }, - "security": [ - { - "bearerAuth": [] - } - ] + } } }, "/maintenance-window": { "post": { "tags": ["maintenance-window"], - "summary": "Create maintenance window", - "description": "Create a new maintenance window", + "summary": "Create one or more maintenance windows", + "security": [ + { + "bearerAuth": [] + } + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": ["name", "startTime", "endTime"], "properties": { - "name": { - "type": "string", - "maxLength": 100 - }, - "description": { - "type": "string", - "maxLength": 500 - }, - "startTime": { - "type": "string", - "format": "date-time" - }, - "endTime": { - "type": "string", - "format": "date-time" - }, "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"] } } } }, "responses": { - "201": { - "description": "Maintenance window created successfully" + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, - "422": { - "$ref": "#/components/responses/ValidationError" + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } - }, - "security": [ - { - "bearerAuth": [] - } - ] + } } }, "/maintenance-window/team": { "get": { "tags": ["maintenance-window"], - "summary": "Get team maintenance windows", - "description": "Get all maintenance windows for the team", - "responses": { - "200": { - "description": "Maintenance windows retrieved successfully" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, + "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": "Get maintenance windows by monitor", - "description": "Get all maintenance windows for a specific monitor", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Monitor maintenance windows retrieved successfully" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, + "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 maintenance window by ID", - "description": "Get a specific maintenance window", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Maintenance window retrieved successfully" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, + "summary": "Get a maintenance window by id", "security": [ { "bearerAuth": [] } - ] - }, - "put": { - "tags": ["maintenance-window"], - "summary": "Update maintenance window", - "description": "Update an existing maintenance window", + ], "parameters": [ { - "name": "id", - "in": "path", - "required": true, "schema": { - "type": "string" + "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": { - "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { + "active": { + "type": "boolean" + }, "name": { - "type": "string", - "maxLength": 100 + "type": "string" }, - "description": { - "type": "string", - "maxLength": 500 + "repeat": { + "type": "number" }, - "startTime": { + "start": { "type": "string", + "nullable": true, "format": "date-time" }, - "endTime": { + "end": { "type": "string", + "nullable": true, + "format": "date-time" + }, + "expiry": { + "type": "string", + "nullable": true, "format": "date-time" }, "monitors": { @@ -2525,6 +5184,13 @@ "items": { "type": "string" } + }, + "duration": { + "type": "number" + }, + "durationUnit": { + "type": "string", + "enum": ["seconds", "minutes", "hours", "days"] } } } @@ -2533,64 +5199,121 @@ }, "responses": { "200": { - "$ref": "#/components/responses/Success" + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [true] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, - "404": { - "$ref": "#/components/responses/NotFound" + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, - "422": { - "$ref": "#/components/responses/ValidationError" + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } - }, - "security": [ - { - "bearerAuth": [] - } - ] + } }, "delete": { "tags": ["maintenance-window"], - "summary": "Delete maintenance window", - "description": "Delete a specific maintenance window", + "summary": "Delete a maintenance window", + "security": [ + { + "bearerAuth": [] + } + ], "parameters": [ { - "name": "id", - "in": "path", - "required": true, "schema": { - "type": "string" - } + "type": "string", + "minLength": 1 + }, + "required": true, + "name": "id", + "in": "path" } ], "responses": { "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/queue/jobs": { - "get": { - "tags": ["queue"], - "summary": "Get queue jobs", - "description": "Retrieve all jobs in the queue (admin/superadmin only)", - "responses": { - "200": { - "description": "Queue jobs retrieved successfully", + "description": "OK", "content": { "application/json": { "schema": { @@ -2598,46 +5321,46 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [true] }, "msg": { - "type": "string", - "example": "Queue jobs fetched successfully" + "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": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QueueJob" - } + "nullable": true } - } + }, + "required": ["success", "msg"] } } } }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "post": { - "tags": ["queue"], - "summary": "Add job to queue", - "description": "Add a new job to the queue (admin/superadmin only)", - "responses": { - "200": { - "description": "Job added successfully", + "description": "Forbidden", "content": { "application/json": { "schema": { @@ -2645,474 +5368,96 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [false] }, "msg": { - "type": "string", - "example": "Job added to queue successfully" - } - } - } - } - } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/queue/metrics": { - "get": { - "tags": ["queue"], - "summary": "Get queue metrics", - "description": "Retrieve queue metrics (admin/superadmin only)", - "responses": { - "200": { - "description": "Queue metrics retrieved successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "msg": { - "type": "string", - "example": "Queue metrics fetched successfully" + "type": "string" }, "data": { - "$ref": "#/components/schemas/QueueMetrics" + "nullable": true } - } + }, + "required": ["success", "msg"] } } } }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } - }, - "security": [ - { - "bearerAuth": [] - } - ] + } } }, - "/queue/health": { - "get": { - "tags": ["queue"], - "summary": "Check queue health", - "description": "Check the health status of the job queue (admin/superadmin only)", - "responses": { - "200": { - "description": "Queue health status retrieved successfully" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/queue/all-metrics": { - "get": { - "tags": ["queue"], - "summary": "Get all queue metrics", - "description": "Get comprehensive queue metrics (admin/superadmin only)", - "responses": { - "200": { - "description": "All queue metrics retrieved successfully" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/queue/flush": { - "post": { - "tags": ["queue"], - "summary": "Flush queue", - "description": "Clear all jobs from the queue (admin/superadmin only)", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/monitors/games": { + "/monitors/team": { "get": { "tags": ["monitors"], - "summary": "Get game server list", - "description": "Get available game servers for monitoring", - "responses": { - "200": { - "description": "Game servers retrieved successfully", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - } - } - } - } - ] - } - } - } - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, + "summary": "List monitors for the caller's team", "security": [ { "bearerAuth": [] } - ] - } - }, - "/status-page": { - "get": { - "tags": ["status-page"], - "summary": "Get status page", - "description": "Get default status page information", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - } - }, - "post": { - "tags": ["status-page"], - "summary": "Create status page", - "description": "Create a new status page with optional logo upload", - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "logo": { + ], + "parameters": [ + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] + }, + { + "type": "array", + "items": { "type": "string", - "format": "binary", - "description": "Logo file for the status page" - }, - "title": { - "type": "string", - "description": "Status page title" - }, - "description": { - "type": "string", - "description": "Status page description" - }, - "url": { - "type": "string", - "description": "Custom URL for the status page" - }, - "theme": { - "$ref": "#/components/schemas/StatusPageTheme" - }, - "themeMode": { - "$ref": "#/components/schemas/StatusPageThemeMode" + "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] } } - } - } - } - }, - "responses": { - "201": { - "$ref": "#/components/responses/Success" + ] + }, + "required": false, + "name": "type", + "in": "query" }, - "400": { - "$ref": "#/components/responses/BadRequest" - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ { - "bearerAuth": [] - } - ] - }, - "put": { - "tags": ["status-page"], - "summary": "Update status page", - "description": "Update an existing status page with optional logo upload", - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "logo": { - "type": "string", - "format": "binary", - "description": "Logo file for the status page" - }, - "title": { - "type": "string", - "description": "Status page title" - }, - "description": { - "type": "string", - "description": "Status page description" - }, - "url": { - "type": "string", - "description": "Custom URL for the status page" - }, - "theme": { - "$ref": "#/components/schemas/StatusPageTheme" - }, - "themeMode": { - "$ref": "#/components/schemas/StatusPageThemeMode" - } + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [""] } - } - } - } - }, - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "400": { - "$ref": "#/components/responses/BadRequest" - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/status-page/team": { - "get": { - "tags": ["status-page"], - "summary": "Get status pages by team", - "description": "Get all status pages for the authenticated user's team", - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/status-page/{url}": { - "get": { - "tags": ["status-page"], - "summary": "Get status page by URL", - "description": "Get a specific status page by its custom URL", - "parameters": [ - { - "name": "url", - "in": "path", - "required": true, - "schema": { - "type": "string" + ] }, - "description": "Custom URL of the status page" + "required": false, + "name": "filter", + "in": "query" } ], "responses": { "200": { - "$ref": "#/components/responses/Success" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - } - }, - "delete": { - "tags": ["status-page"], - "summary": "Delete status page", - "description": "Delete a status page by its custom URL", - "parameters": [ - { - "name": "url", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "Custom URL of the status page (supports wildcards)" - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/Success" - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "500": { - "$ref": "#/components/responses/InternalServerError" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/incidents/team": { - "get": { - "tags": ["incidents"], - "summary": "Get incidents by team", - "description": "Retrieve all incidents for the authenticated user's team with optional filtering", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Filter by incident status (true = ongoing, false = resolved)", - "schema": { - "type": "boolean" - } - }, - { - "name": "monitorId", - "in": "query", - "description": "Filter by monitor ID", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "in": "query", - "description": "Page number for pagination", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "limit", - "in": "query", - "description": "Number of items per page", - "schema": { - "type": "integer", - "default": 10 - } - }, - { - "name": "sortOrder", - "in": "query", - "description": "Sort order for results", - "schema": { - "type": "string", - "enum": ["asc", "desc"], - "default": "desc" - } - } - ], - "responses": { - "200": { - "description": "Incidents retrieved successfully", + "description": "OK", "content": { "application/json": { "schema": { @@ -3120,57 +5465,200 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [true] }, "msg": { - "type": "string", - "example": "Incidents retrieved successfully" + "type": "string" }, "data": { "type": "array", "items": { - "$ref": "#/components/schemas/Incident" + "$ref": "#/components/schemas/Monitor" } } - } + }, + "required": ["success", "msg", "data"] } } } }, "401": { - "$ref": "#/components/responses/Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "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": [] } - ] - } - }, - "/incidents/team/summary": { - "get": { - "tags": ["incidents"], - "summary": "Get incident summary", - "description": "Retrieve a summary of incidents for the authenticated user's team", + ], "parameters": [ { - "name": "dateRange", - "in": "query", - "description": "Date range for summary", + "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": ["day", "week", "month"], - "default": "week" - } + "enum": ["asc", "desc"] + }, + "required": false, + "name": "order", + "in": "query" + }, + { + "schema": { + "anyOf": [ + { + "type": "string", + "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc", "websocket"] + } + } + ] + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "explain", + "in": "query" } ], "responses": { "200": { - "description": "Incident summary retrieved successfully", + "description": "OK", "content": { "application/json": { "schema": { @@ -3178,53 +5666,104 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [true] }, "msg": { - "type": "string", - "example": "Incident summary retrieved successfully" + "type": "string" }, "data": { - "$ref": "#/components/schemas/IncidentSummary" + "nullable": true } - } + }, + "required": ["success", "msg"] } } } }, "401": { - "$ref": "#/components/responses/Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } - }, - "security": [ - { - "bearerAuth": [] - } - ] + } } }, - "/incidents/{incidentId}": { + "/monitors/team/groups": { "get": { - "tags": ["incidents"], - "summary": "Get incident by ID", - "description": "Retrieve a specific incident by its ID", - "parameters": [ + "tags": ["monitors"], + "summary": "List monitor groups for the caller's team", + "security": [ { - "name": "incidentId", - "in": "path", - "required": true, - "description": "The incident ID", - "schema": { - "type": "string" - } + "bearerAuth": [] } ], "responses": { "200": { - "description": "Incident retrieved successfully", + "description": "OK", "content": { "application/json": { "schema": { @@ -3232,51 +5771,911 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [true] }, "msg": { - "type": "string", - "example": "Incident retrieved successfully" + "type": "string" }, "data": { - "$ref": "#/components/schemas/Incident" + "type": "array", + "items": { + "type": "string" + } } - } + }, + "required": ["success", "msg", "data"] } } } }, "401": { - "$ref": "#/components/responses/Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, - "404": { - "$ref": "#/components/responses/NotFound" + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "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": [] } - ] - } - }, - "/incidents/{incidentId}/resolve": { - "put": { - "tags": ["incidents"], - "summary": "Resolve incident manually", - "description": "Manually resolve an ongoing incident. Requires admin or superadmin role.", + ], "parameters": [ { - "name": "incidentId", - "in": "path", - "required": true, - "description": "The incident ID to resolve", "schema": { - "type": "string" + "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": { @@ -3285,10 +6684,1493 @@ "schema": { "type": "object", "properties": { - "comment": { + "monitorIds": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 100 + }, + "notificationIds": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 100 + }, + "action": { "type": "string", - "description": "Optional comment about the resolution", - "example": "Issue resolved by restarting the service" + "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", "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" + } + }, + "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": "" + }, + "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 + } + }, + "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", "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": [] + }, + "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": "" + }, + "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 + }, + "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", "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" + } + }, + "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": [""] + } + ] + }, + "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 } } } @@ -3297,7 +8179,7 @@ }, "responses": { "200": { - "description": "Incident resolved successfully", + "description": "OK", "content": { "application/json": { "schema": { @@ -3305,1433 +8187,2642 @@ "properties": { "success": { "type": "boolean", - "example": true + "enum": [true] }, "msg": { - "type": "string", - "example": "Incident resolved successfully" + "type": "string" }, "data": { - "$ref": "#/components/schemas/Incident" + "$ref": "#/components/schemas/Monitor" } - } + }, + "required": ["success", "msg", "data"] } } } }, "401": { - "$ref": "#/components/responses/Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "403": { - "$ref": "#/components/responses/Forbidden" - }, - "404": { - "$ref": "#/components/responses/NotFound" + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, "500": { - "$ref": "#/components/responses/InternalServerError" + "description": "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": [] } - ] - } - } - }, - "components": { - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT", - "description": "JWT token obtained from login endpoint" - } - }, - "responses": { - "Success": { - "description": "Operation successful", - "content": { - "application/json": { + ], + "parameters": [ + { "schema": { - "$ref": "#/components/schemas/SuccessResponse" - } - } - } - }, - "NotFound": { - "description": "Resource not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "ValidationError": { - "description": "Validation failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "Unauthorized": { - "description": "Authentication required or token invalid", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "Forbidden": { - "description": "Insufficient permissions", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "BadRequest": { - "description": "Invalid request parameters", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "InternalServerError": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - }, - "schemas": { - "StatusPageTheme": { - "type": "string", - "enum": ["refined", "modern", "bold", "editorial"], - "default": "refined", - "description": "Visual theme for the public status page. Defaults to 'refined'." - }, - "StatusPageThemeMode": { - "type": "string", - "enum": ["auto", "light", "dark"], - "default": "auto", - "description": "Forces a light/dark mode on the public page. 'auto' follows the visitor's OS preference. Defaults to 'auto'." - }, - "SuccessResponse": { - "type": "object", - "required": ["success", "msg"], - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "msg": { - "type": "string", - "example": "Operation completed successfully" - }, - "data": { - "type": "object", - "description": "Response payload (varies by endpoint)" - } - } - }, - "ErrorResponse": { - "type": "object", - "required": ["success", "msg"], - "properties": { - "success": { - "type": "boolean", - "example": false - }, - "msg": { - "type": "string", - "example": "An error occurred" - }, - "details": { - "type": "object", - "description": "Additional error details" - } - } - }, - "RegisterRequest": { - "type": "object", - "required": ["firstName", "lastName", "email", "password"], - "properties": { - "firstName": { - "type": "string", - "minLength": 1, - "maxLength": 50, - "example": "John" - }, - "lastName": { - "type": "string", - "minLength": 1, - "maxLength": 50, - "example": "Doe" - }, - "email": { - "type": "string", - "format": "email", - "example": "john@example.com" - }, - "password": { - "type": "string", - "minLength": 8, - "format": "password", - "example": "SecurePass123!" - }, - "profileImage": { - "type": "string", - "format": "binary", - "description": "Optional profile image file" - }, - "role": { - "type": "array", - "items": { "type": "string", - "enum": ["user", "admin", "superadmin", "Demo"] + "minLength": 1 }, - "default": ["user"] + "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"] + } + } + } } } - }, - "LoginRequest": { - "type": "object", - "required": ["email", "password"], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "john@example.com" + } + }, + "/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"] + } + } + } }, - "password": { - "type": "string", - "format": "password", - "example": "SecurePass123!" + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } } - }, - "AuthResponse": { - "allOf": [ + } + }, + "/notifications/test/all": { + "post": { + "tags": ["notifications"], + "summary": "Send a test alert through every notification channel for the team", + "security": [ { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { "type": "object", "properties": { - "token": { + "monitorId": { "type": "string", - "description": "JWT access token" + "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 + } }, - "user": { - "$ref": "#/components/schemas/User" + "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 + } + } } } } } } - ] - }, - "UserUpdateRequest": { - "type": "object", - "properties": { - "firstName": { - "type": "string", - "minLength": 1, - "maxLength": 50 - }, - "lastName": { - "type": "string", - "minLength": 1, - "maxLength": 50 - }, - "password": { - "type": "string", - "format": "password", - "description": "Current password for verification" - }, - "newPassword": { - "type": "string", - "minLength": 8, - "format": "password", - "description": "New password (if changing)" - }, - "profileImage": { - "type": "string", - "format": "binary", - "description": "New profile image file" - }, - "deleteProfileImage": { - "type": "boolean", - "description": "Flag to delete current profile image" - } - } - }, - "User": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "64f123a456b789012c345def" - }, - "firstName": { - "type": "string", - "example": "John" - }, - "lastName": { - "type": "string", - "example": "Doe" - }, - "email": { - "type": "string", - "format": "email", - "example": "john@example.com" - }, - "role": { - "type": "array", - "items": { - "type": "string", - "enum": ["user", "admin", "superadmin", "Demo"] - } - }, - "profileImage": { - "type": "string", - "description": "URL or path to profile image" - }, - "isActive": { - "type": "boolean" - }, - "teamId": { - "type": "string" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - } - } - }, - "CreateMonitorRequest": { - "type": "object", - "required": ["name", "type", "url"], - "properties": { - "name": { - "type": "string", - "example": "My Website Monitor" - }, - "description": { - "type": "string", - "example": "Monitors the main website homepage" - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket"], - "example": "http" - }, - "url": { - "type": "string", - "format": "uri", - "example": "https://example.com" - }, - "interval": { - "type": "integer", - "example": 300, - "description": "Check interval in seconds" - }, - "statusWindowSize": { - "type": "integer", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "isActive": { - "type": "boolean" - }, - "ignoreTlsErrors": { - "type": "boolean", - "default": false - }, - "useAdvancedMatching": { - "type": "boolean", - "default": false - }, - "port": { - "type": "integer" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Array of notification IDs to associate with this monitor" - }, - "secret": { - "type": "string" - }, - "jsonPath": { - "type": "string" - }, - "expectedValue": { - "type": "string" - }, - "matchMethod": { - "type": "string", - "enum": ["equal", "include", "regex", ""] - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "gameId": { - "type": "string" - }, - "grpcServiceName": { - "type": "string", - "default": "" - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "type": "string", - "nullable": true, - "maxLength": 50 - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": ["northAmerica", "southAmerica", "europe", "asia", "africa", "oceania"] - } - }, - "geoCheckInterval": { - "type": "integer", - "minimum": 300000 - } - } - }, - "UpdateMonitorRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "unknown"] - }, - "url": { - "type": "string" - }, - "statusWindowSize": { - "type": "integer", - "minimum": 1, - "maximum": 20, - "default": 5 - }, - "statusWindowThreshold": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 60 - }, - "description": { - "type": "string" - }, - "interval": { - "type": "integer" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "secret": { - "type": "string" - }, - "ignoreTlsErrors": { - "type": "boolean" - }, - "useAdvancedMatching": { - "type": "boolean" - }, - "jsonPath": { - "type": "string" - }, - "expectedValue": { - "type": "string" - }, - "matchMethod": { - "type": "string", - "enum": ["equal", "include", "regex", ""] - }, - "port": { - "type": "integer", - "minimum": 1, - "maximum": 65535 - }, - "cpuAlertThreshold": { - "type": "number" - }, - "memoryAlertThreshold": { - "type": "number" - }, - "diskAlertThreshold": { - "type": "number" - }, - "tempAlertThreshold": { - "type": "number" - }, - "gameId": { - "type": "string" - }, - "grpcServiceName": { - "type": "string" - }, - "selectedDisks": { - "type": "array", - "items": { - "type": "string" - } - }, - "group": { - "type": "string", - "nullable": true, - "maxLength": 50 - }, - "geoCheckEnabled": { - "type": "boolean" - }, - "geoCheckLocations": { - "type": "array", - "items": { - "type": "string", - "enum": ["northAmerica", "southAmerica", "europe", "asia", "africa", "oceania"] - } - }, - "geoCheckInterval": { - "type": "integer", - "minimum": 300000 - } - } - }, - "Monitor": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "64f123a456b789012c345def" - }, - "userId": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "name": { - "type": "string", - "example": "My Website Monitor" - }, - "description": { - "type": "string", - "example": "Monitors the main website homepage" - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port"] - }, - "url": { - "type": "string", - "format": "uri", - "example": "https://example.com" - }, - "interval": { - "type": "integer", - "example": 300 - }, - "isActive": { - "type": "boolean", - "example": true - }, - "status": { - "type": "boolean", - "description": "Current monitor status (up/down)" - }, - "lastChecked": { - "type": "string", - "format": "date-time", - "description": "Timestamp of last check" - }, - "notifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "httpOptions": { - "type": "object", - "properties": { - "method": { - "type": "string" - }, - "headers": { - "type": "object" - }, - "body": { - "type": "string" - }, - "timeout": { - "type": "integer" + }, + "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"] + } } } }, - "assertions": { - "type": "array", - "items": { - "type": "object" + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } } }, - "createdAt": { - "type": "string", - "format": "date-time" + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } }, - "updatedAt": { - "type": "string", - "format": "date-time" + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } } } - }, - "CreateNotificationRequest": { - "type": "object", - "required": ["type", "name"], - "properties": { - "type": { - "type": "string", - "enum": ["email", "webhook", "slack", "discord", "telegram", "zapier"], - "example": "email" - }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "example": "Admin Email Notifications" - }, - "config": { - "type": "object", - "description": "Configuration specific to notification type", - "oneOf": [ - { - "title": "Email Configuration", + } + }, + "/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", - "format": "email" + "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"] } - }, - { - "title": "Webhook Configuration", + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "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", - "format": "uri" + "pattern": "^[a-zA-Z0-9_-]+$" }, - "headers": { - "type": "object", - "additionalProperties": { + "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" } - } - } - }, - { - "title": "Slack Configuration", - "properties": { - "webhookUrl": { - "type": "string", - "format": "uri" }, - "channel": { - "type": "string" - } + "required": ["success", "msg"] + }, + "example": { + "success": true, + "msg": "OK" } - }, - { - "title": "Discord Configuration", - "properties": { - "webhookUrl": { + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } + } + } + }, + "500": { + "description": "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", - "format": "uri" + "enum": ["uptime", "infrastructure"] } } - } - ] - }, - "isActive": { - "type": "boolean", - "default": true - } - } - }, - "Notification": { - "type": "object", - "properties": { - "_id": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["email", "webhook", "slack", "discord", "telegram", "zapier"] - }, - "name": { - "type": "string" - }, - "config": { - "type": "object", - "description": "Type-specific configuration" - }, - "isActive": { - "type": "boolean" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - } - } - }, - "AppSettings": { - "type": "object", - "properties": { - "appName": { - "type": "string", - "example": "Checkmate" - }, - "appUrl": { - "type": "string", - "format": "uri", - "example": "https://checkmate.example.com" - }, - "logLevel": { - "type": "string", - "enum": ["error", "warn", "info", "debug"], - "default": "info" - }, - "emailConfig": { - "type": "object", - "properties": { - "host": { - "type": "string" - }, - "port": { - "type": "integer" - }, - "secure": { - "type": "boolean" - }, - "user": { - "type": "string" - }, - "pass": { - "type": "string", - "format": "password" - }, - "from": { - "type": "string", - "format": "email" - } - } - }, - "webhookRetries": { - "type": "integer", - "minimum": 0, - "maximum": 10, - "default": 3 - }, - "checksRetention": { - "type": "integer", - "minimum": 1, - "maximum": 365, - "default": 90, - "description": "Days to retain check results" - } - } - }, - "SystemDiagnostics": { - "type": "object", - "properties": { - "uptime": { - "type": "number", - "description": "System uptime in seconds" - }, - "memory": { - "type": "object", - "properties": { - "total": { - "type": "number", - "description": "Total memory in bytes" - }, - "used": { - "type": "number", - "description": "Used memory in bytes" - }, - "free": { - "type": "number", - "description": "Free memory in bytes" - } - } - }, - "cpu": { - "type": "object", - "properties": { - "usage": { - "type": "number", - "description": "CPU usage percentage" - }, - "cores": { - "type": "integer", - "description": "Number of CPU cores" - } - } - }, - "database": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": ["connected", "disconnected", "error"] - }, - "responseTime": { - "type": "number", - "description": "Database response time in milliseconds" - } - } - }, - "queue": { - "type": "object", - "properties": { - "active": { - "type": "integer" - }, - "waiting": { - "type": "integer" - }, - "completed": { - "type": "integer" - }, - "failed": { - "type": "integer" - } - } - } - } - }, - "Incident": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "description": "Unique incident identifier" - }, - "monitorId": { - "type": "string", - "description": "ID of the associated monitor" - }, - "teamId": { - "type": "string", - "description": "ID of the team" - }, - "startTime": { - "type": "string", - "format": "date-time", - "description": "When the incident started" - }, - "endTime": { - "type": ["string", "null"], - "format": "date-time", - "description": "When the incident was resolved (null if ongoing)" - }, - "status": { - "type": "boolean", - "description": "Incident status (true = ongoing, false = resolved)" - }, - "message": { - "type": ["string", "null"], - "description": "Error message or description" - }, - "statusCode": { - "type": ["integer", "null"], - "description": "HTTP status code if applicable" - }, - "resolutionType": { - "type": ["string", "null"], - "enum": ["automatic", "manual", null], - "description": "How the incident was resolved" - }, - "resolvedBy": { - "type": ["string", "null"], - "description": "User ID who resolved the incident (for manual resolution)" - }, - "comment": { - "type": ["string", "null"], - "description": "Resolution comment" - }, - "checks": { - "type": "array", - "items": { - "type": "string" + ] }, - "description": "Array of check IDs associated with this incident" + "required": true, + "name": "type", + "in": "query" }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" + { + "schema": { + "type": "number", + "nullable": true + }, + "required": false, + "name": "timeFrame", + "in": "query" } - } - }, - "IncidentSummary": { - "type": "object", - "properties": { - "totalIncidents": { - "type": "integer", - "description": "Total number of incidents" - }, - "activeIncidents": { - "type": "integer", - "description": "Number of ongoing incidents" - }, - "resolvedIncidents": { - "type": "integer", - "description": "Number of resolved incidents" - }, - "averageResolutionTime": { - "type": "number", - "description": "Average time to resolve incidents in milliseconds" - }, - "incidentsByMonitor": { - "type": "array", - "items": { - "type": "object", - "properties": { - "monitorId": { - "type": "string" - }, - "monitorName": { - "type": "string" - }, - "count": { - "type": "integer" + ], + "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"] } } } - } - } - }, - "QueueJob": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Job ID" }, - "name": { - "type": "string", - "description": "Job name" + "404": { + "description": "Status page not found" }, - "data": { - "type": "object", - "description": "Job payload data" - }, - "status": { - "type": "string", - "enum": ["waiting", "active", "completed", "failed", "delayed"], - "description": "Current job status" - }, - "progress": { - "type": "number", - "description": "Job progress percentage" - }, - "attemptsMade": { - "type": "integer", - "description": "Number of attempts made" - }, - "timestamp": { - "type": "string", - "format": "date-time", - "description": "When the job was created" - }, - "processedOn": { - "type": ["string", "null"], - "format": "date-time", - "description": "When the job started processing" - }, - "finishedOn": { - "type": ["string", "null"], - "format": "date-time", - "description": "When the job finished" - } - } - }, - "QueueMetrics": { - "type": "object", - "properties": { - "waiting": { - "type": "integer", - "description": "Number of jobs waiting" - }, - "active": { - "type": "integer", - "description": "Number of jobs currently processing" - }, - "completed": { - "type": "integer", - "description": "Number of completed jobs" - }, - "failed": { - "type": "integer", - "description": "Number of failed jobs" - }, - "delayed": { - "type": "integer", - "description": "Number of delayed jobs" - } - } - }, - "Check": { - "type": "object", - "description": "A monitoring check result", - "properties": { - "_id": { - "type": "string", - "description": "Check ID" - }, - "metadata": { - "type": "object", - "properties": { - "monitorId": { - "type": "string", - "description": "Associated monitor ID" - }, - "teamId": { - "type": "string", - "description": "Associated team ID" - }, - "type": { - "type": "string", - "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port"], - "description": "Monitor type" - } - }, - "required": ["monitorId", "teamId", "type"] - }, - "status": { - "type": "boolean", - "description": "Check status (true = up, false = down)" - }, - "responseTime": { - "type": "number", - "description": "Response time in milliseconds" - }, - "statusCode": { - "type": "integer", - "description": "HTTP status code (for HTTP monitors)" - }, - "message": { - "type": "string", - "description": "Status message or error description" - }, - "timings": { - "$ref": "#/components/schemas/CheckTimings" - }, - "cpu": { - "$ref": "#/components/schemas/CheckCpuInfo" - }, - "memory": { - "$ref": "#/components/schemas/CheckMemoryInfo" - }, - "disk": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CheckDiskInfo" - } - }, - "host": { - "$ref": "#/components/schemas/CheckHostInfo" - }, - "errors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CheckErrorInfo" - } - }, - "capture": { - "$ref": "#/components/schemas/CheckCaptureInfo" - }, - "net": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CheckNetworkInfo" - } - }, - "accessibility": { - "type": "number", - "description": "PageSpeed accessibility score (0-100)" - }, - "bestPractices": { - "type": "number", - "description": "PageSpeed best practices score (0-100)" - }, - "seo": { - "type": "number", - "description": "PageSpeed SEO score (0-100)" - }, - "performance": { - "type": "number", - "description": "PageSpeed performance score (0-100)" - }, - "audits": { - "$ref": "#/components/schemas/CheckAudits" - }, - "ack": { - "type": "boolean", - "description": "Whether the check has been acknowledged" - }, - "ackAt": { - "type": ["string", "null"], - "format": "date-time", - "description": "When the check was acknowledged" - }, - "expiry": { - "type": "string", - "format": "date-time", - "description": "When the check expires" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - } - } - }, - "CheckTimings": { - "type": "object", - "description": "Detailed timing information for HTTP checks", - "properties": { - "start": { - "type": "number" - }, - "socket": { - "type": "number" - }, - "lookup": { - "type": "number" - }, - "connect": { - "type": "number" - }, - "secureConnect": { - "type": "number" - }, - "upload": { - "type": "number" - }, - "response": { - "type": "number" - }, - "end": { - "type": "number" - }, - "phases": { - "type": "object", - "properties": { - "wait": { - "type": "number" - }, - "dns": { - "type": "number" - }, - "tcp": { - "type": "number" - }, - "tls": { - "type": "number" - }, - "request": { - "type": "number" - }, - "firstByte": { - "type": "number" - }, - "download": { - "type": "number" - }, - "total": { - "type": "number" + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [false] + }, + "msg": { + "type": "string" + }, + "data": { + "nullable": true + } + }, + "required": ["success", "msg"] + } } } } } - }, - "CheckCpuInfo": { - "type": "object", - "description": "CPU information for infrastructure checks", - "properties": { - "physical_core": { - "type": "integer" - }, - "logical_core": { - "type": "integer" - }, - "frequency": { - "type": "number" - }, - "temperature": { - "type": "array", - "items": { - "type": "number" - } - }, - "free_percent": { - "type": "number" - }, - "usage_percent": { - "type": "number" - } - } - }, - "CheckMemoryInfo": { - "type": "object", - "description": "Memory information for infrastructure checks", - "properties": { - "total_bytes": { - "type": "number" - }, - "available_bytes": { - "type": "number" - }, - "used_bytes": { - "type": "number" - }, - "usage_percent": { - "type": "number" - } - } - }, - "CheckDiskInfo": { - "type": "object", - "description": "Disk information for infrastructure checks", - "properties": { - "device": { - "type": "string" - }, - "mountpoint": { - "type": "string" - }, - "read_speed_bytes": { - "type": "number" - }, - "write_speed_bytes": { - "type": "number" - }, - "total_bytes": { - "type": "number" - }, - "free_bytes": { - "type": "number" - }, - "usage_percent": { - "type": "number" - } - } - }, - "CheckHostInfo": { - "type": "object", - "description": "Host information for infrastructure checks", - "properties": { - "os": { - "type": "string" - }, - "platform": { - "type": "string" - }, - "kernel_version": { - "type": "string" - } - } - }, - "CheckErrorInfo": { - "type": "object", - "description": "Error information from checks", - "properties": { - "metric": { - "type": "array", - "items": { - "type": "string" - } - }, - "err": { - "type": "string" - } - } - }, - "CheckCaptureInfo": { - "type": "object", - "description": "Capture agent information", - "properties": { - "version": { - "type": "string" - }, - "mode": { - "type": "string" - } - } - }, - "CheckNetworkInfo": { - "type": "object", - "description": "Network interface information", - "properties": { - "name": { - "type": "string" - }, - "bytes_sent": { - "type": "number" - }, - "bytes_recv": { - "type": "number" - }, - "packets_sent": { - "type": "number" - }, - "packets_recv": { - "type": "number" - }, - "err_in": { - "type": "number" - }, - "err_out": { - "type": "number" - }, - "drop_in": { - "type": "number" - }, - "drop_out": { - "type": "number" - }, - "fifo_in": { - "type": "number" - }, - "fifo_out": { - "type": "number" - } - } - }, - "CheckAudits": { - "type": "object", - "description": "PageSpeed audit results", - "properties": { - "cls": { - "$ref": "#/components/schemas/LighthouseAudit" - }, - "si": { - "$ref": "#/components/schemas/LighthouseAudit" - }, - "fcp": { - "$ref": "#/components/schemas/LighthouseAudit" - }, - "lcp": { - "$ref": "#/components/schemas/LighthouseAudit" - }, - "tbt": { - "$ref": "#/components/schemas/LighthouseAudit" - } - } - }, - "LighthouseAudit": { - "type": "object", - "description": "Individual Lighthouse audit result", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "score": { - "type": "number" - }, - "displayValue": { - "type": "string" - }, - "numericValue": { - "type": "number" - }, - "numericUnit": { - "type": "string" - } - } } } } diff --git a/server/openapi/helpers.ts b/server/openapi/helpers.ts new file mode 100644 index 000000000..fcbd87840 --- /dev/null +++ b/server/openapi/helpers.ts @@ -0,0 +1,54 @@ +import "./registry.js"; +import { z } from "zod"; + +export const successEnvelope = (data: T) => + z.object({ + success: z.literal(true), + msg: z.string(), + data, + }); + +export const successEnvelopeNoData = z.object({ + success: z.literal(true), + msg: z.string(), +}); + +export const errorEnvelope = z.object({ + success: z.literal(false), + msg: z.string(), + data: z.unknown().optional(), +}); + +export const bearer = [{ bearerAuth: [] }]; + +export const json = (schema: T, example?: unknown) => ({ + "application/json": example === undefined ? { schema } : { schema, example }, +}); + +export const standardErrors = { + "401": { description: "Unauthorized", content: json(errorEnvelope) }, + "403": { description: "Forbidden", content: json(errorEnvelope) }, + "500": { description: "Internal server error", content: json(errorEnvelope) }, +}; + +export const okJson = (data: T, description = "OK", example?: unknown) => ({ + description, + content: json(successEnvelope(data), example === undefined ? undefined : { success: true, msg: "OK", data: example }), +}); + +export const okJsonNoData = (description = "OK") => ({ + description, + content: json(successEnvelopeNoData, { success: true, msg: "OK" }), +}); + +export const okUnknown = okJson(z.unknown()); + +export const multipart = (fields: Record, fileField?: string) => { + const shape: Record = { ...fields }; + if (fileField) { + shape[fileField] = z.string().openapi({ type: "string", format: "binary" }); + } + return { + "multipart/form-data": { schema: z.object(shape) }, + }; +}; diff --git a/server/openapi/index.ts b/server/openapi/index.ts new file mode 100644 index 000000000..9758c9d54 --- /dev/null +++ b/server/openapi/index.ts @@ -0,0 +1,56 @@ +import "./registry.js"; +import "./routes/auth.js"; +import "./routes/check.js"; +import "./routes/diagnostic.js"; +import "./routes/geoCheck.js"; +import "./routes/incident.js"; +import "./routes/invite.js"; +import "./routes/log.js"; +import "./routes/maintenanceWindow.js"; +import "./routes/monitor.js"; +import "./routes/notification.js"; +import "./routes/queue.js"; +import "./routes/settings.js"; +import "./routes/statusPage.js"; + +import { OpenApiGeneratorV3 } from "@asteasolutions/zod-to-openapi"; +import type { JsonObject } from "swagger-ui-express"; +import { registry } from "./registry.js"; + +let cached: JsonObject | null = null; + +export function getOpenApiSpec(): JsonObject { + if (cached) return cached; + + const generator = new OpenApiGeneratorV3(registry.definitions); + const document = generator.generateDocument({ + 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)." }, + ], + }); + + cached = document as unknown as JsonObject; + return cached; +} diff --git a/server/openapi/registry.ts b/server/openapi/registry.ts new file mode 100644 index 000000000..a733e61b2 --- /dev/null +++ b/server/openapi/registry.ts @@ -0,0 +1,12 @@ +import { OpenAPIRegistry, extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi"; +import { z } from "zod"; + +extendZodWithOpenApi(z); + +export const registry = new OpenAPIRegistry(); + +registry.registerComponent("securitySchemes", "bearerAuth", { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", +}); diff --git a/server/openapi/routes/auth.ts b/server/openapi/routes/auth.ts new file mode 100644 index 000000000..91254edf3 --- /dev/null +++ b/server/openapi/routes/auth.ts @@ -0,0 +1,203 @@ +import { z } from "zod"; +import { registry } from "../registry.js"; +import { bearer, json, multipart, okJson, okJsonNoData, standardErrors } from "../helpers.js"; +import { + loginValidation, + registrationBodyValidation, + recoveryValidation, + recoveryTokenBodyValidation, + newPasswordValidation, +} from "@/validation/authValidation.js"; +import { + getUserByIdParamValidation, + editUserByIdParamValidation, + editUserByIdBodyValidation, + editUserPasswordByIdBodyValidation, + createUserBodyValidation, + editUserBodyValidation, +} from "@/validation/userValidation.js"; + +const tags = ["auth"]; + +const userObject = z + .object({ + _id: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }), + firstName: z.string().openapi({ example: "Ada" }), + lastName: z.string().openapi({ example: "Lovelace" }), + email: z.string().openapi({ example: "ada@example.com" }), + role: z.array(z.string()).openapi({ example: ["admin"] }), + teamId: z.string().optional().openapi({ example: "65f1c2a4d8b9e01234567890" }), + profileImage: z.string().nullable().optional(), + createdAt: z.string().openapi({ example: "2026-04-01T10:00:00.000Z" }), + updatedAt: z.string().openapi({ example: "2026-04-15T14:30:00.000Z" }), + }) + .passthrough() + .openapi("User"); + +const authPayload = z + .object({ + user: userObject, + token: z.string().optional().openapi({ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }), + }) + .openapi("AuthPayload"); + +registry.registerPath({ + method: "post", + path: "/auth/register", + tags, + summary: "Register a new user (first user becomes superadmin)", + request: { body: { content: multipart(registrationBodyValidation.shape, "profileImage") } }, + responses: { "200": okJson(authPayload), ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/auth/login", + tags, + summary: "Log in", + request: { + body: { + content: json(loginValidation, { email: "ada@example.com", password: "S3cure!Passw0rd" }), + }, + }, + responses: { + "200": okJson(authPayload, "OK", { + 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: json(z.object({ success: z.literal(false), msg: z.string() })) }, + "500": standardErrors["500"], + }, +}); + +registry.registerPath({ + method: "post", + path: "/auth/recovery/request", + tags, + summary: "Request a password recovery email", + request: { body: { content: json(recoveryValidation, { email: "ada@example.com" }) } }, + responses: { "200": okJsonNoData(), "500": standardErrors["500"] }, +}); + +registry.registerPath({ + method: "post", + path: "/auth/recovery/validate", + tags, + summary: "Validate a password recovery token", + request: { body: { content: json(recoveryTokenBodyValidation) } }, + responses: { "200": okJsonNoData(), "500": standardErrors["500"] }, +}); + +registry.registerPath({ + method: "post", + path: "/auth/recovery/reset", + tags, + summary: "Reset password using a recovery token", + request: { body: { content: json(newPasswordValidation) } }, + responses: { "200": okJsonNoData(), "500": standardErrors["500"] }, +}); + +registry.registerPath({ + method: "get", + path: "/auth/users/superadmin", + tags, + summary: "Check whether a superadmin user exists", + responses: { "200": okJson(z.object({ superAdminExists: z.boolean() })), "500": standardErrors["500"] }, +}); + +registry.registerPath({ + method: "get", + path: "/auth/users", + tags, + summary: "List all users (admin/superadmin)", + security: bearer, + responses: { "200": okJson(z.array(userObject)), ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/auth/users", + tags, + summary: "Create a new user (superadmin)", + security: bearer, + request: { body: { content: multipart(createUserBodyValidation.shape, "profileImage") } }, + responses: { "201": okJson(userObject, "User created"), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/auth/users/{userId}", + tags, + summary: "Get a user by id", + security: bearer, + request: { params: getUserByIdParamValidation }, + responses: { + "200": okJson(userObject), + "404": { description: "User not found", content: json(z.object({ success: z.literal(false), msg: z.string() })) }, + ...standardErrors, + }, +}); + +registry.registerPath({ + method: "patch", + path: "/auth/users/{userId}", + tags, + summary: "Edit a user (superadmin)", + security: bearer, + request: { + params: editUserByIdParamValidation, + body: { content: json(editUserByIdBodyValidation) }, + }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); + +registry.registerPath({ + method: "patch", + path: "/auth/users/{userId}/password", + tags, + summary: "Change a user's password (superadmin)", + security: bearer, + request: { + params: editUserByIdParamValidation, + body: { content: json(editUserPasswordByIdBodyValidation) }, + }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/auth/users/{userId}", + tags, + summary: "Delete a user (admin/superadmin)", + security: bearer, + request: { params: getUserByIdParamValidation }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); + +registry.registerPath({ + method: "patch", + path: "/auth/user", + tags, + summary: "Edit the currently authenticated user", + security: bearer, + request: { body: { content: multipart(editUserBodyValidation.shape, "profileImage") } }, + responses: { "200": okJson(userObject), ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/auth/user", + tags, + summary: "Delete the currently authenticated user", + security: bearer, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); diff --git a/server/openapi/routes/check.ts b/server/openapi/routes/check.ts new file mode 100644 index 000000000..a9b6b3804 --- /dev/null +++ b/server/openapi/routes/check.ts @@ -0,0 +1,61 @@ +import { z } from "zod"; +import { registry } from "../registry.js"; +import { bearer, okJson, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; +import { + getChecksParamValidation, + getChecksQueryValidation, + getTeamChecksQueryValidation, + deleteChecksParamValidation, + getChecksSummaryByTeamIdQueryValidation, +} from "@/validation/checkValidation.js"; + +const tags = ["checks"]; + +registry.registerPath({ + method: "get", + path: "/checks/team/summary", + tags, + summary: "Aggregate check summary for the caller's team", + security: bearer, + request: { query: getChecksSummaryByTeamIdQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/checks/team", + tags, + summary: "List checks across the team", + security: bearer, + request: { query: getTeamChecksQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/checks/team", + tags, + summary: "Delete all checks for the team (admin/superadmin)", + security: bearer, + responses: { "200": okJson(z.object({ deletedCount: z.number() })), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/checks/{monitorId}", + tags, + summary: "List checks for a monitor", + security: bearer, + request: { params: getChecksParamValidation, query: getChecksQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/checks/{monitorId}", + tags, + summary: "Delete checks for a monitor", + security: bearer, + request: { params: deleteChecksParamValidation }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); diff --git a/server/openapi/routes/diagnostic.ts b/server/openapi/routes/diagnostic.ts new file mode 100644 index 000000000..49e384876 --- /dev/null +++ b/server/openapi/routes/diagnostic.ts @@ -0,0 +1,11 @@ +import { registry } from "../registry.js"; +import { bearer, okUnknown, standardErrors } from "../helpers.js"; + +registry.registerPath({ + method: "get", + path: "/diagnostic/system", + tags: ["diagnostic"], + summary: "Get system diagnostics (admin/superadmin)", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); diff --git a/server/openapi/routes/geoCheck.ts b/server/openapi/routes/geoCheck.ts new file mode 100644 index 000000000..7b90d0a0d --- /dev/null +++ b/server/openapi/routes/geoCheck.ts @@ -0,0 +1,13 @@ +import { registry } from "../registry.js"; +import { bearer, okUnknown, standardErrors } from "../helpers.js"; +import { getChecksParamValidation, getChecksQueryValidation } from "@/validation/checkValidation.js"; + +registry.registerPath({ + method: "get", + path: "/geo-checks/{monitorId}", + tags: ["geo-checks"], + summary: "Get geo check results for a monitor", + security: bearer, + request: { params: getChecksParamValidation, query: getChecksQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); diff --git a/server/openapi/routes/incident.ts b/server/openapi/routes/incident.ts new file mode 100644 index 000000000..6497a5bd0 --- /dev/null +++ b/server/openapi/routes/incident.ts @@ -0,0 +1,104 @@ +import { z } from "zod"; +import { registry } from "../registry.js"; +import { bearer, okJson, okJsonNoData, standardErrors } from "../helpers.js"; +import { getIncidentsByTeamQueryValidation, getIncidentSummaryQueryValidation } from "@/validation/incidentValidation.js"; + +const tags = ["incidents"]; + +const incidentIdParam = z.object({ incidentId: z.string() }); + +const incidentObject = z + .object({ + id: z.string().openapi({ example: "65f1c2a4d8b9e0123456789c" }), + monitorId: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }), + teamId: z.string().openapi({ example: "65f1c2a4d8b9e01234567890" }), + startTime: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }), + endTime: z.string().nullable().openapi({ example: "2026-04-15T03:34:00.000Z" }), + status: z.boolean().openapi({ example: true, description: "true = resolved, false = ongoing" }), + message: z.string().nullable().optional().openapi({ example: "HTTP 503 from origin" }), + statusCode: z.number().nullable().optional().openapi({ example: 503 }), + resolutionType: z.enum(["automatic", "manual"]).nullable().openapi({ example: "automatic" }), + resolvedBy: z.string().nullable().optional(), + resolvedByEmail: z.string().nullable().optional(), + comment: z.string().nullable().optional(), + createdAt: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }), + updatedAt: z.string().openapi({ example: "2026-04-15T03:34:00.000Z" }), + }) + .passthrough() + .openapi("Incident"); + +const incidentSummaryItem = z + .object({ + id: z.string().openapi({ example: "65f1c2a4d8b9e0123456789c" }), + monitorId: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }), + monitorName: z.string().nullable().openapi({ example: "Marketing site" }), + status: z.boolean().openapi({ example: true }), + startTime: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }), + endTime: z.string().nullable().openapi({ example: "2026-04-15T03:34:00.000Z" }), + resolutionType: z.enum(["automatic", "manual"]).nullable().openapi({ example: "automatic" }), + message: z.string().nullable().openapi({ example: "HTTP 503 from origin" }), + statusCode: z.number().nullable().openapi({ example: 503 }), + createdAt: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }), + }) + .openapi("IncidentSummaryItem"); + +const incidentSummary = z + .object({ + total: z.number().openapi({ example: 12 }), + totalActive: z.number().openapi({ example: 1 }), + totalManualResolutions: z.number().openapi({ example: 2 }), + totalAutomaticResolutions: z.number().openapi({ example: 9 }), + avgResolutionTimeHours: z.number().openapi({ example: 0.5 }), + topMonitor: z + .object({ + monitorId: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }), + monitorName: z.string().nullable().openapi({ example: "Marketing site" }), + incidentCount: z.number().openapi({ example: 4 }), + }) + .nullable(), + latestIncidents: z.array(incidentSummaryItem), + }) + .openapi("IncidentSummary"); + +registry.registerPath({ + method: "get", + path: "/incidents/team", + tags, + summary: "List incidents for the caller's team", + security: bearer, + request: { query: getIncidentsByTeamQueryValidation }, + responses: { "200": okJson(z.array(incidentObject)), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/incidents/team/summary", + tags, + summary: "Incident summary for the caller's team", + security: bearer, + request: { query: getIncidentSummaryQueryValidation }, + responses: { "200": okJson(incidentSummary), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/incidents/{incidentId}", + tags, + summary: "Get an incident by id", + security: bearer, + request: { params: incidentIdParam }, + responses: { + "200": okJson(z.object({ incident: incidentObject, monitor: z.unknown(), user: z.unknown().nullable() })), + ...standardErrors, + }, +}); + +registry.registerPath({ + method: "put", + path: "/incidents/{incidentId}/resolve", + tags, + summary: "Manually resolve an incident (admin/superadmin)", + security: bearer, + request: { params: incidentIdParam }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); diff --git a/server/openapi/routes/invite.ts b/server/openapi/routes/invite.ts new file mode 100644 index 000000000..bf9b568b3 --- /dev/null +++ b/server/openapi/routes/invite.ts @@ -0,0 +1,35 @@ +import { registry } from "../registry.js"; +import { bearer, json, okJson, okJsonNoData, standardErrors } from "../helpers.js"; +import { z } from "zod"; +import { inviteBodyValidation, inviteVerificationBodyValidation } from "@/validation/authValidation.js"; + +const tags = ["invite"]; + +registry.registerPath({ + method: "post", + path: "/invite/send", + tags, + summary: "Send an invite email (admin/superadmin)", + security: bearer, + request: { body: { content: json(inviteBodyValidation) } }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/invite/verify", + tags, + summary: "Verify an invite token", + request: { body: { content: json(inviteVerificationBodyValidation) } }, + responses: { "200": okJson(z.object({ email: z.string(), role: z.array(z.string()).optional() }).passthrough()), "500": standardErrors["500"] }, +}); + +registry.registerPath({ + method: "post", + path: "/invite", + tags, + summary: "Create an invite token (admin/superadmin)", + security: bearer, + request: { body: { content: json(inviteBodyValidation) } }, + responses: { "200": okJson(z.object({ token: z.string() }).passthrough()), ...standardErrors }, +}); diff --git a/server/openapi/routes/log.ts b/server/openapi/routes/log.ts new file mode 100644 index 000000000..8fc043ff7 --- /dev/null +++ b/server/openapi/routes/log.ts @@ -0,0 +1,11 @@ +import { registry } from "../registry.js"; +import { bearer, okUnknown, standardErrors } from "../helpers.js"; + +registry.registerPath({ + method: "get", + path: "/logs", + tags: ["logs"], + summary: "Get application logs (admin/superadmin)", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); diff --git a/server/openapi/routes/maintenanceWindow.ts b/server/openapi/routes/maintenanceWindow.ts new file mode 100644 index 000000000..6afc57031 --- /dev/null +++ b/server/openapi/routes/maintenanceWindow.ts @@ -0,0 +1,73 @@ +import { registry } from "../registry.js"; +import { bearer, json, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; +import { + createMaintenanceWindowBodyValidation, + getMaintenanceWindowByIdParamValidation, + getMaintenanceWindowsByTeamIdQueryValidation, + getMaintenanceWindowsByMonitorIdParamValidation, + deleteMaintenanceWindowByIdParamValidation, + editMaintenanceWindowByIdParamValidation, + editMaintenanceByIdWindowBodyValidation, +} from "@/validation/maintenanceWindowValidation.js"; + +const tags = ["maintenance-window"]; + +registry.registerPath({ + method: "post", + path: "/maintenance-window", + tags, + summary: "Create one or more maintenance windows", + security: bearer, + request: { body: { content: json(createMaintenanceWindowBodyValidation) } }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/maintenance-window/team", + tags, + summary: "List maintenance windows for the caller's team", + security: bearer, + request: { query: getMaintenanceWindowsByTeamIdQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/maintenance-window/monitor/{monitorId}", + tags, + summary: "List maintenance windows for a monitor", + security: bearer, + request: { params: getMaintenanceWindowsByMonitorIdParamValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/maintenance-window/{id}", + tags, + summary: "Get a maintenance window by id", + security: bearer, + request: { params: getMaintenanceWindowByIdParamValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "patch", + path: "/maintenance-window/{id}", + tags, + summary: "Edit a maintenance window", + security: bearer, + request: { params: editMaintenanceWindowByIdParamValidation, body: { content: json(editMaintenanceByIdWindowBodyValidation) } }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/maintenance-window/{id}", + tags, + summary: "Delete a maintenance window", + security: bearer, + request: { params: deleteMaintenanceWindowByIdParamValidation }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); diff --git a/server/openapi/routes/monitor.ts b/server/openapi/routes/monitor.ts new file mode 100644 index 000000000..0e6187318 --- /dev/null +++ b/server/openapi/routes/monitor.ts @@ -0,0 +1,247 @@ +import { z } from "zod"; +import { registry } from "../registry.js"; +import { bearer, json, okJson, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; +import { GeoContinents } from "@/types/geoCheck.js"; +import { MonitorMatchMethods, MonitorTypes } from "@/types/monitor.js"; +import { + getMonitorByIdParamValidation, + getMonitorByIdQueryValidation, + getMonitorsByTeamIdQueryValidation, + getMonitorsWithChecksQueryValidation, + getCertificateParamValidation, + createMonitorBodyValidation, + editMonitorBodyValidation, + pauseMonitorParamValidation, + getUptimeDetailsByIdParamValidation, + getUptimeDetailsByIdQueryValidation, + importMonitorsBodyValidation, + getHardwareDetailsByIdParamValidation, + getHardwareDetailsByIdQueryValidation, +} from "@/validation/monitorValidation.js"; +import { updateNotificationsValidation } from "@/validation/notificationValidation.js"; + +const tags = ["monitors"]; + +const monitorObject = z + .object({ + _id: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }), + name: z.string().openapi({ example: "Marketing site" }), + description: z.string().optional().openapi({ example: "Production marketing site monitored from the EU region" }), + type: z.enum(MonitorTypes).openapi({ example: "http" }), + url: z.string().openapi({ example: "https://www.example.com" }), + port: z.number().optional().openapi({ example: 443 }), + isActive: z.boolean().openapi({ example: true }), + interval: z.number().openapi({ example: 60000 }), + status: z.enum(["up", "down", "paused", "initializing", "maintenance", "breached"]).openapi({ example: "up" }), + statusWindowSize: z.number().openapi({ example: 5 }), + statusWindowThreshold: z.number().openapi({ example: 3 }), + ignoreTlsErrors: z.boolean().openapi({ example: false }), + useAdvancedMatching: z.boolean().openapi({ example: false }), + jsonPath: z.string().optional(), + expectedValue: z.string().optional(), + matchMethod: z.enum(MonitorMatchMethods).optional(), + notifications: z.array(z.string()).openapi({ example: ["65f1c2a4d8b9e0123456789b"] }), + secret: z.string().optional(), + cpuAlertThreshold: z.number().openapi({ example: 90 }), + memoryAlertThreshold: z.number().openapi({ example: 90 }), + diskAlertThreshold: z.number().openapi({ example: 90 }), + tempAlertThreshold: z.number().openapi({ example: 80 }), + selectedDisks: z.array(z.string()).openapi({ example: [] }), + gameId: z.string().optional(), + grpcServiceName: z.string().optional(), + group: z.string().nullable().optional(), + geoCheckEnabled: z.boolean().openapi({ example: false }), + geoCheckLocations: z.array(z.enum(GeoContinents)).openapi({ example: [] }), + geoCheckInterval: z.number().openapi({ example: 300000 }), + teamId: z.string().openapi({ example: "65f1c2a4d8b9e01234567890" }), + userId: z.string().openapi({ example: "65f1c2a4d8b9e01234567891" }), + createdAt: z.string().openapi({ example: "2026-04-01T10:00:00.000Z" }), + updatedAt: z.string().openapi({ example: "2026-04-15T14:30:00.000Z" }), + }) + .passthrough() + .openapi("Monitor"); + +registry.registerPath({ + method: "get", + path: "/monitors/team", + tags, + summary: "List monitors for the caller's team", + security: bearer, + request: { query: getMonitorsByTeamIdQueryValidation }, + responses: { "200": okJson(z.array(monitorObject)), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/team/with-checks", + tags, + summary: "List team monitors with their most recent checks (paginated)", + security: bearer, + request: { query: getMonitorsWithChecksQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/team/groups", + tags, + summary: "List monitor groups for the caller's team", + security: bearer, + responses: { "200": okJson(z.array(z.string())), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/uptime/details/{monitorId}", + tags, + summary: "Get uptime details for a monitor", + security: bearer, + request: { params: getUptimeDetailsByIdParamValidation, query: getUptimeDetailsByIdQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/hardware/details/{monitorId}", + tags, + summary: "Get hardware metrics detail for a monitor", + security: bearer, + request: { params: getHardwareDetailsByIdParamValidation, query: getHardwareDetailsByIdQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/pagespeed/details/{monitorId}", + tags, + summary: "Get PageSpeed detail for a monitor", + security: bearer, + request: { params: getHardwareDetailsByIdParamValidation, query: getHardwareDetailsByIdQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/{monitorId}/geo-checks", + tags, + summary: "Get geo check results for a monitor", + security: bearer, + request: { params: getMonitorByIdParamValidation, query: getMonitorByIdQueryValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/monitors/pause/{monitorId}", + tags, + summary: "Toggle pause state for a monitor", + security: bearer, + request: { params: pauseMonitorParamValidation }, + responses: { "200": okJson(monitorObject), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/certificate/{monitorId}", + tags, + summary: "Get SSL certificate info for a monitor", + security: bearer, + request: { params: getCertificateParamValidation }, + responses: { "200": okJson(z.object({ certificateDate: z.string() })), ...standardErrors }, +}); + +registry.registerPath({ + method: "patch", + path: "/monitors/notifications", + tags, + summary: "Bulk update notifications across monitors", + security: bearer, + request: { body: { content: json(updateNotificationsValidation) } }, + responses: { "200": okJson(z.object({ modifiedCount: z.number() })), ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/monitors", + tags, + summary: "Create a new monitor", + security: bearer, + request: { body: { content: json(createMonitorBodyValidation) } }, + responses: { "200": okJson(monitorObject, "Monitor created"), ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/monitors", + tags, + summary: "Delete every monitor (superadmin only)", + security: bearer, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/monitors/demo", + tags, + summary: "Insert preconfigured demo monitors", + security: bearer, + responses: { "200": okJson(z.number()), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/export/json", + tags, + summary: "Export all team monitors as JSON", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/monitors/import/json", + tags, + summary: "Import monitors from JSON", + security: bearer, + request: { body: { content: json(importMonitorsBodyValidation) } }, + responses: { "200": okJson(z.object({ imported: z.number() }).passthrough()), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/games", + tags, + summary: "List supported game-server types", + security: bearer, + responses: { "200": okJson(z.array(z.string())), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/monitors/{monitorId}", + tags, + summary: "Get a monitor by id", + security: bearer, + request: { params: getMonitorByIdParamValidation }, + responses: { "200": okJson(monitorObject), ...standardErrors }, +}); + +registry.registerPath({ + method: "patch", + path: "/monitors/{monitorId}", + tags, + summary: "Edit a monitor", + security: bearer, + request: { params: getMonitorByIdParamValidation, body: { content: json(editMonitorBodyValidation) } }, + responses: { "200": okJson(monitorObject), ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/monitors/{monitorId}", + tags, + summary: "Delete a monitor", + security: bearer, + request: { params: getMonitorByIdParamValidation }, + responses: { "200": okJson(monitorObject), ...standardErrors }, +}); diff --git a/server/openapi/routes/notification.ts b/server/openapi/routes/notification.ts new file mode 100644 index 000000000..b34c542e7 --- /dev/null +++ b/server/openapi/routes/notification.ts @@ -0,0 +1,155 @@ +import { z } from "zod"; +import { registry } from "../registry.js"; +import { bearer, json, okJson, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; +import { + createNotificationBodyValidation, + deleteNotificationParamValidation, + getNotificationByIdParamValidation, + editNotificationParamValidation, + testAllNotificationsBodyValidation, +} from "@/validation/notificationValidation.js"; + +const tags = ["notifications"]; + +// OpenAPI metadata for each notification variant. Keyed by the discriminator +// value in createNotificationBodyValidation; build fails loudly if a variant +// in the validator has no entry here, which doubles as drift detection. +const notificationVariantMeta: Record }> = { + email: { + component: "EmailNotification", + example: { notificationName: "Ops on-call email", type: "email", address: "alerts@example.com" }, + }, + webhook: { + component: "WebhookNotification", + example: { notificationName: "Custom webhook", type: "webhook", address: "https://example.com/hooks/checkmate" }, + }, + slack: { + component: "SlackNotification", + example: { notificationName: "#alerts", type: "slack", address: "https://hooks.slack.com/services/T000/B000/XXXX" }, + }, + discord: { + component: "DiscordNotification", + example: { notificationName: "#status", type: "discord", address: "https://discord.com/api/webhooks/123/abc" }, + }, + pager_duty: { + component: "PagerDutyNotification", + example: { notificationName: "PagerDuty primary", type: "pager_duty", address: "R01XXXXXXXXXXXXXXXXXXXXXXX" }, + }, + matrix: { + component: "MatrixNotification", + example: { + notificationName: "Matrix room", + type: "matrix", + homeserverUrl: "https://matrix.example.com", + roomId: "!abc123:example.com", + accessToken: "syt_xxx", + }, + }, + teams: { + component: "TeamsNotification", + example: { notificationName: "Teams ops channel", type: "teams", address: "https://outlook.office.com/webhook/..." }, + }, + telegram: { + component: "TelegramNotification", + example: { notificationName: "Telegram bot", type: "telegram", address: "-1001234567890", accessToken: "123456:ABC-DEF" }, + }, + pushover: { + component: "PushoverNotification", + example: { notificationName: "Pushover personal", type: "pushover", address: "u1234567890abcdef", accessToken: "a1234567890abcdef" }, + }, + twilio: { + component: "TwilioNotification", + example: { + notificationName: "Twilio SMS", + type: "twilio", + accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + accessToken: "your-auth-token", + phone: "+15551234567", + twilioPhoneNumber: "+15557654321", + }, + }, +}; + +const decoratedVariants = createNotificationBodyValidation.options.map((variant) => { + const typeField = variant.shape.type as z.ZodLiteral; + const type = typeField.value; + const meta = notificationVariantMeta[type]; + if (!meta) { + throw new Error(`Missing OpenAPI metadata for notification variant "${type}". Add an entry in notificationVariantMeta.`); + } + return variant.openapi(meta.component, { example: meta.example }); +}); + +const notificationBody = z + .discriminatedUnion("type", decoratedVariants as typeof createNotificationBodyValidation.options) + .openapi("NotificationChannelBody"); + +registry.registerPath({ + method: "post", + path: "/notifications", + tags, + summary: "Create a notification channel", + description: "Create a notification channel of any supported type. The `type` field discriminates the body shape.", + security: bearer, + request: { body: { content: json(notificationBody) } }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/notifications/test/all", + tags, + summary: "Send a test alert through every notification channel for the team", + security: bearer, + request: { body: { content: json(testAllNotificationsBodyValidation) } }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/notifications/test", + tags, + summary: "Send a test alert through a single notification channel", + security: bearer, + request: { body: { content: json(notificationBody) } }, + responses: { "200": okJson(z.object({ success: z.boolean() }).passthrough()), ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/notifications/team", + tags, + summary: "List notification channels for the caller's team", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/notifications/{id}", + tags, + summary: "Get a notification channel by id", + security: bearer, + request: { params: getNotificationByIdParamValidation }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "delete", + path: "/notifications/{id}", + tags, + summary: "Delete a notification channel", + security: bearer, + request: { params: deleteNotificationParamValidation }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); + +registry.registerPath({ + method: "patch", + path: "/notifications/{id}", + tags, + summary: "Edit a notification channel", + security: bearer, + request: { params: editNotificationParamValidation, body: { content: json(notificationBody) } }, + responses: { "200": okUnknown, ...standardErrors }, +}); diff --git a/server/openapi/routes/queue.ts b/server/openapi/routes/queue.ts new file mode 100644 index 000000000..f2970c22c --- /dev/null +++ b/server/openapi/routes/queue.ts @@ -0,0 +1,40 @@ +import { registry } from "../registry.js"; +import { bearer, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; + +const tags = ["queue"]; + +registry.registerPath({ + method: "get", + path: "/queue/jobs", + tags, + summary: "List queued monitor jobs (admin/superadmin)", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/queue/metrics", + tags, + summary: "Get queue runtime metrics (admin/superadmin)", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/queue/all-metrics", + tags, + summary: "Get queue metrics across all monitors (admin/superadmin)", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/queue/flush", + tags, + summary: "Flush the monitor job queue (admin/superadmin)", + security: bearer, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); diff --git a/server/openapi/routes/settings.ts b/server/openapi/routes/settings.ts new file mode 100644 index 000000000..1297c3e85 --- /dev/null +++ b/server/openapi/routes/settings.ts @@ -0,0 +1,35 @@ +import { registry } from "../registry.js"; +import { bearer, json, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; +import { updateAppSettingsBodyValidation } from "@/validation/settingsValidation.js"; +import { sendTestEmailBodyValidation } from "@/validation/notificationValidation.js"; + +const tags = ["settings"]; + +registry.registerPath({ + method: "get", + path: "/settings", + tags, + summary: "Get application settings", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "patch", + path: "/settings", + tags, + summary: "Update application settings (admin/superadmin)", + security: bearer, + request: { body: { content: json(updateAppSettingsBodyValidation) } }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/settings/test-email", + tags, + summary: "Send a test email using current SMTP settings (admin/superadmin)", + security: bearer, + request: { body: { content: json(sendTestEmailBodyValidation) } }, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); diff --git a/server/openapi/routes/statusPage.ts b/server/openapi/routes/statusPage.ts new file mode 100644 index 000000000..ac315b31c --- /dev/null +++ b/server/openapi/routes/statusPage.ts @@ -0,0 +1,52 @@ +import { registry } from "../registry.js"; +import { bearer, multipart, okJsonNoData, okUnknown, standardErrors } from "../helpers.js"; +import { createStatusPageBodyValidation, getStatusPageParamValidation, getStatusPageQueryValidation } from "@/validation/statusPageValidation.js"; + +const tags = ["status-page"]; + +registry.registerPath({ + method: "get", + path: "/status-page/team", + tags, + summary: "List status pages for the caller's team", + security: bearer, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "post", + path: "/status-page", + tags, + summary: "Create a status page (logo upload optional)", + security: bearer, + request: { body: { content: multipart(createStatusPageBodyValidation.shape, "logo") } }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "put", + path: "/status-page/{id}", + tags, + summary: "Update a status page (logo upload optional)", + security: bearer, + request: { body: { content: multipart(createStatusPageBodyValidation.shape, "logo") } }, + responses: { "200": okUnknown, ...standardErrors }, +}); + +registry.registerPath({ + method: "get", + path: "/status-page/{url}", + tags, + summary: "Get a public status page by its URL slug", + request: { params: getStatusPageParamValidation, query: getStatusPageQueryValidation }, + responses: { "200": okUnknown, "404": { description: "Status page not found" }, "500": standardErrors["500"] }, +}); + +registry.registerPath({ + method: "delete", + path: "/status-page/{id}", + tags, + summary: "Delete a status page", + security: bearer, + responses: { "200": okJsonNoData(), ...standardErrors }, +}); diff --git a/server/package-lock.json b/server/package-lock.json index cf06ec454..52f37fde8 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -46,6 +46,7 @@ "zod": "^4.3.6" }, "devDependencies": { + "@asteasolutions/zod-to-openapi": "8.5.0", "@eslint/js": "^9.17.0", "@types/compression": "1.8.1", "@types/cookie-parser": "1.4.10", @@ -126,6 +127,19 @@ "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "license": "MIT" }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-8.5.0.tgz", + "integrity": "sha512-SABbKiObg5dLRiTFnqiW1WWwGcg1BJfmHtT2asIBnBHg6Smy/Ms2KHc650+JI4Hw7lSkdiNebEGXpwoxfben8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", @@ -851,6 +865,7 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -1470,6 +1485,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -1514,6 +1530,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -4227,6 +4244,7 @@ "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -4387,6 +4405,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -4624,6 +4643,7 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -5237,6 +5257,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5815,6 +5836,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -6708,6 +6730,7 @@ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.1.tgz", "integrity": "sha512-fm4D8ti0dQmFPeF8DXSAA//btEmqCOgAc/9Oa3C1LW94h5usNrJEfrON7b4FkPZgnDEn6OUs5NdxiJZmAtGOpQ==", "license": "MIT", + "peer": true, "dependencies": { "cssnano-preset-default": "^7.0.9", "lilconfig": "^3.1.3" @@ -7472,6 +7495,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7833,6 +7857,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -9501,6 +9526,7 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -11944,6 +11970,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi3-ts": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.5.0.tgz", + "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -12335,6 +12371,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -14357,6 +14394,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14525,6 +14563,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -14688,6 +14727,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15522,6 +15562,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/server/package.json b/server/package.json index 245ae50e7..ea581be1a 100755 --- a/server/package.json +++ b/server/package.json @@ -11,7 +11,9 @@ "test:integration": "NODE_OPTIONS=--experimental-vm-modules jest --no-coverage test/integration", "dev": "nodemon --exec tsx src/index.js", "start": "node --watch ./dist/index.js", - "build": "tsc && tsc-alias && cp -r src/templates dist/templates", + "clean": "rm -rf dist", + "build": "npm run clean && npm run build:openapi && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && cp -r src/templates dist/templates", + "build:openapi": "tsc --noEmit && tsx scripts/buildOpenApi.ts", "lint": "eslint .", "lint-fix": "eslint --fix .", "format": "prettier --write .", @@ -64,6 +66,7 @@ "zod": "^4.3.6" }, "devDependencies": { + "@asteasolutions/zod-to-openapi": "8.5.0", "@eslint/js": "^9.17.0", "@types/compression": "1.8.1", "@types/cookie-parser": "1.4.10", diff --git a/server/scripts/buildOpenApi.ts b/server/scripts/buildOpenApi.ts new file mode 100644 index 000000000..4f02004b7 --- /dev/null +++ b/server/scripts/buildOpenApi.ts @@ -0,0 +1,10 @@ +import { writeFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { getOpenApiSpec } from "../openapi/index.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const outPath = path.resolve(__dirname, "..", "openapi.json"); + +writeFileSync(outPath, JSON.stringify(getOpenApiSpec(), null, 2) + "\n"); +console.log(`OpenAPI spec written to ${outPath}`); diff --git a/server/tsconfig.build.json b/server/tsconfig.build.json new file mode 100644 index 000000000..b4d63e55e --- /dev/null +++ b/server/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "noEmit": false + }, + "include": ["src"] +} diff --git a/server/tsconfig.json b/server/tsconfig.json index fe8233c0c..d4544d9bb 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "rootDir": "./src", "allowJs": true, "checkJs": false, "paths": { @@ -15,8 +14,9 @@ "skipLibCheck": true, "noUncheckedIndexedAccess": true, "isolatedModules": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "noEmit": true }, - "include": ["src"], + "include": ["src", "openapi", "scripts"], "exclude": ["tests", "dist", "node_modules"] }