Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
faedb5ba4b |
No files matched your search
Generated
+24
-16
@@ -46,6 +46,7 @@
|
|||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@asteasolutions/zod-to-openapi": "^8.5.0",
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
"@types/compression": "1.8.1",
|
"@types/compression": "1.8.1",
|
||||||
"@types/cookie-parser": "1.4.10",
|
"@types/cookie-parser": "1.4.10",
|
||||||
@@ -126,6 +127,19 @@
|
|||||||
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
|
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@aws-crypto/sha256-browser": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
|
||||||
@@ -851,7 +865,6 @@
|
|||||||
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
|
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.28.6",
|
"@babel/code-frame": "^7.28.6",
|
||||||
"@babel/generator": "^7.28.6",
|
"@babel/generator": "^7.28.6",
|
||||||
@@ -1471,7 +1484,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
@@ -1516,7 +1528,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@@ -4230,7 +4241,6 @@
|
|||||||
"integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
|
"integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
"@types/express-serve-static-core": "^5.0.0",
|
"@types/express-serve-static-core": "^5.0.0",
|
||||||
@@ -4391,7 +4401,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
|
||||||
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
|
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
@@ -4629,7 +4638,6 @@
|
|||||||
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
|
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.56.1",
|
"@typescript-eslint/scope-manager": "8.56.1",
|
||||||
"@typescript-eslint/types": "8.56.1",
|
"@typescript-eslint/types": "8.56.1",
|
||||||
@@ -5243,7 +5251,6 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -5822,7 +5829,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.19",
|
"baseline-browser-mapping": "^2.8.19",
|
||||||
"caniuse-lite": "^1.0.30001751",
|
"caniuse-lite": "^1.0.30001751",
|
||||||
@@ -6716,7 +6722,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.1.tgz",
|
||||||
"integrity": "sha512-fm4D8ti0dQmFPeF8DXSAA//btEmqCOgAc/9Oa3C1LW94h5usNrJEfrON7b4FkPZgnDEn6OUs5NdxiJZmAtGOpQ==",
|
"integrity": "sha512-fm4D8ti0dQmFPeF8DXSAA//btEmqCOgAc/9Oa3C1LW94h5usNrJEfrON7b4FkPZgnDEn6OUs5NdxiJZmAtGOpQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssnano-preset-default": "^7.0.9",
|
"cssnano-preset-default": "^7.0.9",
|
||||||
"lilconfig": "^3.1.3"
|
"lilconfig": "^3.1.3"
|
||||||
@@ -7481,7 +7486,6 @@
|
|||||||
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -7843,7 +7847,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
||||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
@@ -9512,7 +9515,6 @@
|
|||||||
"integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==",
|
"integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/core": "30.2.0",
|
"@jest/core": "30.2.0",
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
@@ -11956,6 +11958,16 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -12347,7 +12359,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -14370,7 +14381,6 @@
|
|||||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -14539,7 +14549,6 @@
|
|||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
@@ -14703,7 +14712,6 @@
|
|||||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@asteasolutions/zod-to-openapi": "^8.5.0",
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
"@types/compression": "1.8.1",
|
"@types/compression": "1.8.1",
|
||||||
"@types/cookie-parser": "1.4.10",
|
"@types/cookie-parser": "1.4.10",
|
||||||
|
|||||||
+2
-2
@@ -3,9 +3,9 @@ import { initializeControllers } from "./config/controllers.js";
|
|||||||
import { createApp } from "./app.js";
|
import { createApp } from "./app.js";
|
||||||
import { initShutdownListener } from "@/shutdown.js";
|
import { initShutdownListener } from "@/shutdown.js";
|
||||||
import { validateEnv } from "@/validation/envValidation.js";
|
import { validateEnv } from "@/validation/envValidation.js";
|
||||||
|
import { getOpenApiSpec } from "@/openapi/index.js";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
|
||||||
|
|
||||||
import Logger, { ILogger } from "@/utils/logger.js";
|
import Logger, { ILogger } from "@/utils/logger.js";
|
||||||
import { SettingsService } from "@/service/index.js";
|
import { SettingsService } from "@/service/index.js";
|
||||||
@@ -20,7 +20,7 @@ const startApp = async () => {
|
|||||||
// FE path
|
// FE path
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
const openApiSpec = JSON.parse(fs.readFileSync(path.join(__dirname, "../openapi.json"), "utf8"));
|
const openApiSpec = getOpenApiSpec();
|
||||||
const frontendPath = path.join(__dirname, "..", "public");
|
const frontendPath = path.join(__dirname, "..", "public");
|
||||||
|
|
||||||
// Create settings service (env only — DB repository injected after connect)
|
// Create settings service (env only — DB repository injected after connect)
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import "./registry.js";
|
||||||
|
import "./routes/monitor.js";
|
||||||
|
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { OpenApiGeneratorV3 } from "@asteasolutions/zod-to-openapi";
|
||||||
|
import type { JsonObject } from "swagger-ui-express";
|
||||||
|
import { registry } from "./registry.js";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
let cached: JsonObject | null = null;
|
||||||
|
|
||||||
|
export function getOpenApiSpec(): JsonObject {
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
const staticSpec = JSON.parse(fs.readFileSync(path.join(__dirname, "../../openapi.json"), "utf8")) as Record<string, unknown>;
|
||||||
|
|
||||||
|
const generator = new OpenApiGeneratorV3(registry.definitions);
|
||||||
|
const generated = generator.generateDocument({
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "generated", version: "0.0.0" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const generatedPaths = (generated.paths ?? {}) as Record<string, unknown>;
|
||||||
|
const generatedSchemas = (generated.components?.schemas ?? {}) as Record<string, unknown>;
|
||||||
|
const generatedSecuritySchemes = (generated.components?.securitySchemes ?? {}) as Record<string, unknown>;
|
||||||
|
|
||||||
|
const staticPaths = (staticSpec.paths ?? {}) as Record<string, unknown>;
|
||||||
|
const staticComponents = (staticSpec.components ?? {}) as Record<string, unknown>;
|
||||||
|
const staticSchemas = (staticComponents.schemas ?? {}) as Record<string, unknown>;
|
||||||
|
const staticSecuritySchemes = (staticComponents.securitySchemes ?? {}) as Record<string, unknown>;
|
||||||
|
|
||||||
|
// Drop static paths that the registry now owns, plus stale entries
|
||||||
|
// for routes that no longer exist in the codebase.
|
||||||
|
const owned = new Set(Object.keys(generatedPaths));
|
||||||
|
const stale = new Set(["/monitors/bulk", "/monitors/test-email"]);
|
||||||
|
const filteredStaticPaths = Object.fromEntries(Object.entries(staticPaths).filter(([key]) => !owned.has(key) && !stale.has(key)));
|
||||||
|
|
||||||
|
cached = {
|
||||||
|
...staticSpec,
|
||||||
|
paths: { ...filteredStaticPaths, ...generatedPaths },
|
||||||
|
components: {
|
||||||
|
...staticComponents,
|
||||||
|
schemas: { ...staticSchemas, ...generatedSchemas },
|
||||||
|
securitySchemes: { ...staticSecuritySchemes, ...generatedSecuritySchemes },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { OpenAPIRegistry, OpenApiGeneratorV3, 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",
|
||||||
|
});
|
||||||
|
|
||||||
|
export function generateMonitorPaths() {
|
||||||
|
const generator = new OpenApiGeneratorV3(registry.definitions);
|
||||||
|
return generator.generateDocument({
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "Checkmate generated paths", version: "0.0.0" },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
import "../registry.js";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { registry } from "../registry.js";
|
||||||
|
import { successEnvelope, successEnvelopeNoData, errorEnvelope } from "../schemas/common.js";
|
||||||
|
import {
|
||||||
|
MonitorSchema,
|
||||||
|
MonitorWithChecksListSchema,
|
||||||
|
CertificateInfoSchema,
|
||||||
|
NotificationsUpdateResultSchema,
|
||||||
|
ImportResultSchema,
|
||||||
|
} from "../schemas/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 bearer = [{ bearerAuth: [] }];
|
||||||
|
const tags = ["monitors"];
|
||||||
|
|
||||||
|
const json = <T extends z.ZodTypeAny>(schema: T) => ({
|
||||||
|
"application/json": { schema },
|
||||||
|
});
|
||||||
|
|
||||||
|
const standardErrors = {
|
||||||
|
"401": { description: "Unauthorized", content: json(errorEnvelope) },
|
||||||
|
"403": { description: "Forbidden", content: json(errorEnvelope) },
|
||||||
|
"500": { description: "Internal server error", content: json(errorEnvelope) },
|
||||||
|
};
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "get",
|
||||||
|
path: "/monitors/team",
|
||||||
|
tags,
|
||||||
|
summary: "List monitors for the caller's team",
|
||||||
|
security: bearer,
|
||||||
|
request: { query: getMonitorsByTeamIdQueryValidation },
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(z.array(MonitorSchema))) },
|
||||||
|
...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": { description: "OK", content: json(successEnvelope(MonitorWithChecksListSchema)) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "get",
|
||||||
|
path: "/monitors/team/groups",
|
||||||
|
tags,
|
||||||
|
summary: "List monitor groups for the caller's team",
|
||||||
|
security: bearer,
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(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": { description: "OK", content: json(successEnvelope(z.unknown())) },
|
||||||
|
"400": { description: "Validation error", content: json(errorEnvelope) },
|
||||||
|
...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": { description: "OK", content: json(successEnvelope(z.unknown())) },
|
||||||
|
...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": { description: "OK", content: json(successEnvelopeNoData) },
|
||||||
|
...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": { description: "OK", content: json(successEnvelopeNoData) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/monitors/pause/{monitorId}",
|
||||||
|
tags,
|
||||||
|
summary: "Toggle pause state for a monitor",
|
||||||
|
security: bearer,
|
||||||
|
request: { params: pauseMonitorParamValidation },
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(MonitorSchema)) },
|
||||||
|
...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": { description: "OK", content: json(successEnvelope(CertificateInfoSchema)) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "patch",
|
||||||
|
path: "/monitors/notifications",
|
||||||
|
tags,
|
||||||
|
summary: "Bulk update notifications across monitors",
|
||||||
|
security: bearer,
|
||||||
|
request: { body: { content: json(updateNotificationsValidation) } },
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(NotificationsUpdateResultSchema)) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/monitors",
|
||||||
|
tags,
|
||||||
|
summary: "Create a new monitor",
|
||||||
|
security: bearer,
|
||||||
|
request: { body: { content: json(createMonitorBodyValidation) } },
|
||||||
|
responses: {
|
||||||
|
"200": { description: "Monitor created", content: json(successEnvelope(MonitorSchema)) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "delete",
|
||||||
|
path: "/monitors",
|
||||||
|
tags,
|
||||||
|
summary: "Delete every monitor (superadmin only)",
|
||||||
|
security: bearer,
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelopeNoData) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/monitors/demo",
|
||||||
|
tags,
|
||||||
|
summary: "Insert preconfigured demo monitors",
|
||||||
|
security: bearer,
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(z.number())) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "get",
|
||||||
|
path: "/monitors/export/json",
|
||||||
|
tags,
|
||||||
|
summary: "Export all team monitors as JSON",
|
||||||
|
security: bearer,
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(z.unknown())) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/monitors/import/json",
|
||||||
|
tags,
|
||||||
|
summary: "Import monitors from JSON",
|
||||||
|
security: bearer,
|
||||||
|
request: { body: { content: json(importMonitorsBodyValidation) } },
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(ImportResultSchema)) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "get",
|
||||||
|
path: "/monitors/games",
|
||||||
|
tags,
|
||||||
|
summary: "List supported game-server types",
|
||||||
|
security: bearer,
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(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": { description: "OK", content: json(successEnvelope(MonitorSchema)) },
|
||||||
|
"404": { description: "Monitor not found", content: json(errorEnvelope) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "patch",
|
||||||
|
path: "/monitors/{monitorId}",
|
||||||
|
tags,
|
||||||
|
summary: "Edit a monitor",
|
||||||
|
security: bearer,
|
||||||
|
request: {
|
||||||
|
params: getMonitorByIdParamValidation,
|
||||||
|
body: { content: json(editMonitorBodyValidation) },
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(MonitorSchema)) },
|
||||||
|
"404": { description: "Monitor not found", content: json(errorEnvelope) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "delete",
|
||||||
|
path: "/monitors/{monitorId}",
|
||||||
|
tags,
|
||||||
|
summary: "Delete a monitor",
|
||||||
|
security: bearer,
|
||||||
|
request: { params: getMonitorByIdParamValidation },
|
||||||
|
responses: {
|
||||||
|
"200": { description: "OK", content: json(successEnvelope(MonitorSchema)) },
|
||||||
|
"404": { description: "Monitor not found", content: json(errorEnvelope) },
|
||||||
|
...standardErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const successEnvelope = <T extends z.ZodTypeAny>(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(),
|
||||||
|
});
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import "../registry.js";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { GeoContinents } from "@/types/geoCheck.js";
|
||||||
|
import { MonitorMatchMethods, MonitorTypes } from "@/types/monitor.js";
|
||||||
|
|
||||||
|
export const MonitorSchema = z
|
||||||
|
.object({
|
||||||
|
_id: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
type: z.enum(MonitorTypes),
|
||||||
|
url: z.string(),
|
||||||
|
port: z.number().optional(),
|
||||||
|
isActive: z.boolean(),
|
||||||
|
interval: z.number(),
|
||||||
|
status: z.enum(["up", "down", "paused", "initializing", "maintenance", "breached"]),
|
||||||
|
statusWindowSize: z.number(),
|
||||||
|
statusWindowThreshold: z.number(),
|
||||||
|
ignoreTlsErrors: z.boolean(),
|
||||||
|
useAdvancedMatching: z.boolean(),
|
||||||
|
jsonPath: z.string().optional(),
|
||||||
|
expectedValue: z.string().optional(),
|
||||||
|
matchMethod: z.enum(MonitorMatchMethods).optional(),
|
||||||
|
notifications: z.array(z.string()),
|
||||||
|
secret: z.string().optional(),
|
||||||
|
cpuAlertThreshold: z.number(),
|
||||||
|
memoryAlertThreshold: z.number(),
|
||||||
|
diskAlertThreshold: z.number(),
|
||||||
|
tempAlertThreshold: z.number(),
|
||||||
|
selectedDisks: z.array(z.string()),
|
||||||
|
gameId: z.string().optional(),
|
||||||
|
grpcServiceName: z.string().optional(),
|
||||||
|
group: z.string().nullable().optional(),
|
||||||
|
geoCheckEnabled: z.boolean(),
|
||||||
|
geoCheckLocations: z.array(z.enum(GeoContinents)),
|
||||||
|
geoCheckInterval: z.number(),
|
||||||
|
teamId: z.string(),
|
||||||
|
userId: z.string(),
|
||||||
|
createdAt: z.string(),
|
||||||
|
updatedAt: z.string(),
|
||||||
|
})
|
||||||
|
.passthrough()
|
||||||
|
.openapi("Monitor");
|
||||||
|
|
||||||
|
export const MonitorWithChecksListSchema = z
|
||||||
|
.object({
|
||||||
|
monitors: z.array(MonitorSchema),
|
||||||
|
monitorCount: z.number(),
|
||||||
|
filteredCount: z.number(),
|
||||||
|
})
|
||||||
|
.passthrough()
|
||||||
|
.openapi("MonitorWithChecksList");
|
||||||
|
|
||||||
|
export const CertificateInfoSchema = z
|
||||||
|
.object({
|
||||||
|
certificateDate: z.string(),
|
||||||
|
})
|
||||||
|
.openapi("CertificateInfo");
|
||||||
|
|
||||||
|
export const NotificationsUpdateResultSchema = z
|
||||||
|
.object({
|
||||||
|
modifiedCount: z.number(),
|
||||||
|
})
|
||||||
|
.openapi("NotificationsUpdateResult");
|
||||||
|
|
||||||
|
export const ImportResultSchema = z
|
||||||
|
.object({
|
||||||
|
imported: z.number(),
|
||||||
|
})
|
||||||
|
.passthrough()
|
||||||
|
.openapi("ImportResult");
|
||||||
Reference in new issue
Block a user