diff --git a/plugins/host-metrics/src/backend/managers/cron.ts b/plugins/host-metrics/src/backend/managers/cron.ts index 393f3e585..48124bbad 100644 --- a/plugins/host-metrics/src/backend/managers/cron.ts +++ b/plugins/host-metrics/src/backend/managers/cron.ts @@ -82,6 +82,23 @@ export function buildApplyCrontabCommand(body: string): string { export function registerCronRoutes(app: Router, deps: ManagerRoutesDeps): void { const { validateHostId } = deps; + /** + * @openapi + * /plugin-api/host-metrics/managers/cron/{id}: + * get: + * summary: List the SSH user's crontab entries + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The parsed crontab entries. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/cron/:id", validateHostId, @@ -91,6 +108,32 @@ export function registerCronRoutes(app: Router, deps: ManagerRoutesDeps): void { }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/cron/{id}: + * post: + * summary: Replace the SSH user's crontab + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [entries] + * properties: + * entries: { type: array, items: { type: object, properties: { schedule: { type: string }, command: { type: string }, enabled: { type: boolean } } } } + * responses: + * 200: { description: The crontab was written. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/cron/:id", validateHostId, diff --git a/plugins/host-metrics/src/backend/managers/firewall.ts b/plugins/host-metrics/src/backend/managers/firewall.ts index 3723b1bf6..f9d368658 100644 --- a/plugins/host-metrics/src/backend/managers/firewall.ts +++ b/plugins/host-metrics/src/backend/managers/firewall.ts @@ -48,6 +48,23 @@ export function registerFirewallRoutes( deps: ManagerRoutesDeps, ): void { const { validateHostId } = deps; + /** + * @openapi + * /plugin-api/host-metrics/managers/firewall/{id}: + * get: + * summary: Read the host firewall (iptables, nftables or ufw) + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The firewall type, status and rules. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/firewall/:id", validateHostId, @@ -56,6 +73,35 @@ export function registerFirewallRoutes( }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/firewall/{id}/rule: + * post: + * summary: Add or delete an input rule + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [op, protocol, port, target] + * properties: + * op: { type: string, enum: [add, delete] } + * protocol: { type: string, enum: [tcp, udp] } + * port: { type: integer } + * target: { type: string, description: ACCEPT, DROP or REJECT } + * responses: + * 200: { description: The command result and firewall backend used. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/firewall/:id/rule", validateHostId, @@ -101,6 +147,23 @@ export function registerFirewallRoutes( ), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/firewall/{id}/persist: + * post: + * summary: Save the current firewall rules so they survive a reboot + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The save result. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/firewall/:id/persist", validateHostId, diff --git a/plugins/host-metrics/src/backend/managers/health.ts b/plugins/host-metrics/src/backend/managers/health.ts index c9eec0dc7..3e6509d83 100644 --- a/plugins/host-metrics/src/backend/managers/health.ts +++ b/plugins/host-metrics/src/backend/managers/health.ts @@ -149,6 +149,23 @@ export function registerHealthRoutes( }); } }; + /** + * @openapi + * /plugin-api/host-metrics/managers/health/{id}: + * get: + * summary: Get the host's health checks and their last results + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The configured checks and results. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/health/:id", validateHostId, @@ -162,6 +179,33 @@ export function registerHealthRoutes( }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/health/{id}/config: + * post: + * summary: Save the host's health checks + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [checks] + * properties: + * checks: { type: array, items: { type: object, properties: { id: { type: string }, name: { type: string }, type: { type: string }, target: { type: string }, port: { type: integer }, path: { type: string } } } } + * intervalSeconds: { type: integer } + * responses: + * 200: { description: The saved checks. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/health/:id/config", validateHostId, @@ -195,6 +239,23 @@ export function registerHealthRoutes( ), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/health/{id}/run: + * post: + * summary: Run the host's health checks now + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The result of each check. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/health/:id/run", validateHostId, diff --git a/plugins/host-metrics/src/backend/managers/logs.ts b/plugins/host-metrics/src/backend/managers/logs.ts index 6f71192db..8448f4e1a 100644 --- a/plugins/host-metrics/src/backend/managers/logs.ts +++ b/plugins/host-metrics/src/backend/managers/logs.ts @@ -43,6 +43,23 @@ export function buildJournalCommand(unit: string, lines: number): string { export function registerLogRoutes(app: Router, deps: ManagerRoutesDeps): void { const { validateHostId } = deps; + /** + * @openapi + * /plugin-api/host-metrics/managers/logs/{id}/files: + * get: + * summary: List readable log files under /var/log + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: Common logs and the files found. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/logs/:id/files", validateHostId, @@ -57,6 +74,34 @@ export function registerLogRoutes(app: Router, deps: ManagerRoutesDeps): void { }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/logs/{id}: + * get: + * summary: Tail a log file or a systemd unit's journal + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * - in: query + * name: path + * schema: { type: string } + * description: A log file under an allowed directory. + * - in: query + * name: unit + * schema: { type: string } + * description: A systemd unit. Used instead of path when given. + * - in: query + * name: lines + * schema: { type: integer } + * responses: + * 200: { description: The last lines of the log. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/logs/:id", validateHostId, diff --git a/plugins/host-metrics/src/backend/managers/packages.ts b/plugins/host-metrics/src/backend/managers/packages.ts index b2cb7e648..169970f41 100644 --- a/plugins/host-metrics/src/backend/managers/packages.ts +++ b/plugins/host-metrics/src/backend/managers/packages.ts @@ -17,6 +17,23 @@ export function registerPackageRoutes( deps: ManagerRoutesDeps, ): void { const { validateHostId } = deps; + /** + * @openapi + * /plugin-api/host-metrics/managers/packages/{id}: + * get: + * summary: List upgradable packages and the package manager + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The package manager and upgradable packages. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/packages/:id", validateHostId, @@ -32,6 +49,33 @@ export function registerPackageRoutes( }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/packages/{id}/action: + * post: + * summary: Install or upgrade a package, or upgrade everything + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [action] + * properties: + * action: { type: string, enum: [install, upgrade, upgrade-all] } + * pkg: { type: string, description: The package name. Not needed for upgrade-all. } + * responses: + * 200: { description: The command output. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/packages/:id/action", validateHostId, diff --git a/plugins/host-metrics/src/backend/managers/simple-reads.ts b/plugins/host-metrics/src/backend/managers/simple-reads.ts index 6c22787a1..09ac68c24 100644 --- a/plugins/host-metrics/src/backend/managers/simple-reads.ts +++ b/plugins/host-metrics/src/backend/managers/simple-reads.ts @@ -110,6 +110,23 @@ export function registerSimpleReadRoutes( deps: ManagerRoutesDeps, ): void { const { validateHostId } = deps; + /** + * @openapi + * /plugin-api/host-metrics/managers/top-memory/{id}: + * get: + * summary: List the processes using the most memory + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The top processes by memory. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/top-memory/:id", validateHostId, @@ -119,6 +136,23 @@ export function registerSimpleReadRoutes( }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/timers/{id}: + * get: + * summary: List systemd timers + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The timers with their next and last runs. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/timers/:id", validateHostId, @@ -128,6 +162,23 @@ export function registerSimpleReadRoutes( }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/disk-breakdown/{id}: + * get: + * summary: List mounted filesystems and their usage + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: Size, used and free space per mount. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/disk-breakdown/:id", validateHostId, diff --git a/plugins/host-metrics/src/backend/managers/ssl.ts b/plugins/host-metrics/src/backend/managers/ssl.ts index b683aeab8..2f3e12307 100644 --- a/plugins/host-metrics/src/backend/managers/ssl.ts +++ b/plugins/host-metrics/src/backend/managers/ssl.ts @@ -151,6 +151,23 @@ export function buildRevokeCommand(client: AcmeClient, name: string): string { export function registerSslRoutes(app: Router, deps: ManagerRoutesDeps): void { const { validateHostId } = deps; + /** + * @openapi + * /plugin-api/host-metrics/managers/ssl/{id}: + * get: + * summary: List certificates from certbot and acme.sh + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The installed ACME clients and certificates. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/ssl/:id", validateHostId, @@ -183,6 +200,36 @@ export function registerSslRoutes(app: Router, deps: ManagerRoutesDeps): void { }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/ssl/{id}/issue: + * post: + * summary: Issue a certificate with certbot or acme.sh + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [client, domains, challenge] + * properties: + * client: { type: string, enum: [certbot, acme.sh] } + * domains: { type: array, items: { type: string } } + * challenge: { type: string, enum: [http-standalone, http-webroot, dns] } + * webroot: { type: string } + * dnsProvider: { type: string } + * responses: + * 200: { description: The issue result. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/ssl/:id/issue", validateHostId, @@ -227,6 +274,33 @@ export function registerSslRoutes(app: Router, deps: ManagerRoutesDeps): void { }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/ssl/{id}/renew: + * post: + * summary: Renew certificates + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [client] + * properties: + * client: { type: string, enum: [certbot, acme.sh] } + * dryRun: { type: boolean } + * responses: + * 200: { description: The renew result. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/ssl/:id/renew", validateHostId, diff --git a/plugins/host-metrics/src/backend/managers/users.ts b/plugins/host-metrics/src/backend/managers/users.ts index 7b599e6b3..176fb0499 100644 --- a/plugins/host-metrics/src/backend/managers/users.ts +++ b/plugins/host-metrics/src/backend/managers/users.ts @@ -73,6 +73,23 @@ export type UserAction = "create" | "delete" | "addToGroup" | "removeFromGroup"; export function registerUserRoutes(app: Router, deps: ManagerRoutesDeps): void { const { validateHostId } = deps; + /** + * @openapi + * /plugin-api/host-metrics/managers/users/{id}: + * get: + * summary: List local users and groups + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: The users and groups. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.get( "/host-metrics/managers/users/:id", validateHostId, @@ -90,6 +107,34 @@ export function registerUserRoutes(app: Router, deps: ManagerRoutesDeps): void { }), ); + /** + * @openapi + * /plugin-api/host-metrics/managers/users/{id}/action: + * post: + * summary: Create or delete a user, or change a user's groups + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [action, username] + * properties: + * action: { type: string, enum: [create, delete, addToGroup, removeFromGroup] } + * username: { type: string } + * group: { type: string, description: Needed for the group actions. } + * responses: + * 200: { description: The command result. } + * 400: { description: Invalid input. } + * 403: { description: No access to the host, or elevation denied. } + * 500: { description: The command failed on the host. } + */ app.post( "/host-metrics/managers/users/:id/action", validateHostId,