From 2f3de9797dfaec8813b3ce688605a3cffd6cec91 Mon Sep 17 00:00:00 2001 From: fabiangigler Date: Sat, 19 Sep 2026 22:51:23 +0200 Subject: [PATCH] Use network types for Capture responses --- server/src/service/network/DockerProvider.ts | 62 +++----------------- server/src/types/network.ts | 28 +++++++++ 2 files changed, 35 insertions(+), 55 deletions(-) diff --git a/server/src/service/network/DockerProvider.ts b/server/src/service/network/DockerProvider.ts index 1084ac4ea..134e4e82f 100644 --- a/server/src/service/network/DockerProvider.ts +++ b/server/src/service/network/DockerProvider.ts @@ -2,7 +2,7 @@ import https from "node:https"; import { IStatusProvider } from "@/service/network/IStatusProvider.js"; import { HttpProvider } from "@/service/network/HttpProvider.js"; -import { CheckContext, DockerStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { CaptureDockerContainer, CaptureDockerStatusPayload, CheckContext, DockerStatusPayload, MonitorStatusResponse } from "@/types/network.js"; import { DOCKER_LOG_TAIL_LINES, @@ -24,7 +24,6 @@ import { Monitor, MonitorType } from "@/domain/monitors/monitor.type.js"; import { ILogger } from "@/utils/logger.js"; import { AppError } from "@/utils/AppError.js"; import Dockerode from "dockerode"; -import { z } from "zod"; import { timeRequest } from "@/service/network/utils.js"; import { NETWORK_ERROR } from "@/types/network.js"; import { IEncryptionService } from "@/service/encryption/encryptionService.js"; @@ -44,53 +43,6 @@ const DOCKER_LOG_TS_REGEX = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,9} const DOCKER_TLS_DEFAULT_PORT = 2376; const CAPTURE_FATAL_ERROR_METRICS = new Set(["docker.client", "docker.container.list"]); -const captureDockerResponseSchema = z.object({ - data: z - .array( - z.object({ - container_id: z.string(), - container_name: z.string(), - status: z.string(), - health: z - .object({ - healthy: z.boolean(), - source: z.string(), - }) - .nullish(), - running: z.boolean(), - base_image: z.string(), - exposed_ports: z - .array( - z.object({ - port: z.string(), - protocol: z.string(), - }) - ) - .optional(), - started_at: z.number().optional(), - stats: z - .object({ - cpu_percent: z.number(), - memory_usage: z.number(), - memory_limit: z.number(), - memory_percent: z.number(), - }) - .nullish(), - }) - ) - .nullable(), - errors: z - .array( - z.object({ - metric: z.array(z.string()), - err: z.string(), - }) - ) - .nullish(), -}); - -type CaptureContainer = NonNullable["data"]>[number]; - export interface DockerError extends Error { statusCode?: number; reason?: string; @@ -197,7 +149,7 @@ export class DockerProvider implements IStatusProvider { }; } - private toCaptureContainer(container: CaptureContainer): DockerContainerInfo { + private toCaptureContainer(container: CaptureDockerContainer): DockerContainerInfo { const startedAt = container.started_at && container.started_at > 0 ? new Date(container.started_at * 1000).toISOString() : undefined; const stats = container.stats ? { @@ -228,11 +180,11 @@ export class DockerProvider implements IStatusProvider { private handleCapture = async (monitor: Monitor, ctx?: CheckContext): Promise> => { const captureUrl = new URL(monitor.url.trim()); captureUrl.searchParams.set("all", "true"); - const response = await this.httpProvider.handle({ ...monitor, url: captureUrl.toString() }, ctx); + const response = await this.httpProvider.handle({ ...monitor, url: captureUrl.toString() }, ctx); if (!response.status) return this.failed(monitor, response.code, response.message, response.responseTime ?? 0); - const parsed = captureDockerResponseSchema.safeParse(response.payload); - if (!parsed.success) { + const payload = response.payload; + if (!payload || typeof payload === "string" || (!Array.isArray(payload.data) && payload.data !== null)) { this.logger.warn({ message: "Capture returned an invalid Docker metrics payload", service: SERVICE_NAME, @@ -241,7 +193,7 @@ export class DockerProvider implements IStatusProvider { return this.failed(monitor, NETWORK_ERROR, "Capture returned an invalid Docker metrics payload", response.responseTime ?? 0); } - const errors = parsed.data.errors ?? []; + const errors = payload.errors ?? []; const fatalError = errors.find((error) => error.metric.some((metric) => CAPTURE_FATAL_ERROR_METRICS.has(metric))); if (fatalError) return this.failed(monitor, NETWORK_ERROR, fatalError.err, response.responseTime ?? 0); if (errors.length > 0) { @@ -252,7 +204,7 @@ export class DockerProvider implements IStatusProvider { }); } - const containers = (parsed.data.data ?? []).map((container) => this.toCaptureContainer(container)); + const containers = (payload.data ?? []).map((container) => this.toCaptureContainer(container)); return { monitorId: monitor.id, teamId: monitor.teamId, diff --git a/server/src/types/network.ts b/server/src/types/network.ts index 1f8afe267..53134e781 100644 --- a/server/src/types/network.ts +++ b/server/src/types/network.ts @@ -99,6 +99,34 @@ export interface HardwareStatusPayload { // Docker host monitoring +export interface CaptureDockerContainer { + container_id: string; + container_name: string; + status: string; + health?: { + healthy: boolean; + source: string; + } | null; + running: boolean; + base_image: string; + exposed_ports?: Array<{ + port: string; + protocol: string; + }>; + started_at?: number; + stats?: { + cpu_percent: number; + memory_usage: number; + memory_limit: number; + memory_percent: number; + } | null; +} + +export interface CaptureDockerStatusPayload { + data: CaptureDockerContainer[] | null; + errors: CheckErrorInfo[] | null; +} + export interface DockerStatusPayload { containers: DockerContainerInfo[]; summary: DockerContainerSummary;