import { randomUUID } from "crypto"; import "dotenv/config"; import { ConfigService } from "@nestjs/config"; import { DataSource } from "typeorm"; import { buildDataSourceOptions } from "../src/database/database.config"; import { MonitorRunEntity } from "../src/monitoring/monitor-run.entity"; import { MonitorAgentEntity } from "../src/monitoring/monitor-agent.entity"; import { MonitorTargetEntity } from "../src/monitoring/monitor-target.entity"; import { InspectionRunEntity } from "../src/runs/inspection-run.entity"; import { InspectionTaskEntity } from "../src/tasks/inspection-task.entity"; function buildSteps(siteUrl: string, titleKeyword?: string) { const steps = [ { action: "goto", url: siteUrl, timeoutMs: 20000 }, { action: "waitForLoadState", value: "domcontentloaded", timeoutMs: 12000 }, { action: "waitForSelector", selector: "body", timeoutMs: 12000 }, { action: "assertVisible", selector: "body", timeoutMs: 12000 }, ] as Array>; if (titleKeyword) { steps.push({ action: "assertText", selector: "title", contains: titleKeyword, timeoutMs: 4000, }); } steps.push({ action: "screenshot", value: "landing-page" }); return JSON.stringify(steps, null, 2); } async function seedInspectionData(dataSource: DataSource) { const taskRepo = dataSource.getRepository(InspectionTaskEntity); const runRepo = dataSource.getRepository(InspectionRunEntity); const tasks = [ { id: "gov-portal-main-seed", name: "呼和浩特市政府首页巡检", siteUrl: "http://www.huhhot.gov.cn/", cronExpr: "*/15 * * * *", serviceKey: "gov-portal-main", titleKeyword: "呼和浩特", }, { id: "gov-portal-secondary-seed", name: "土默特右旗政府首页巡检", siteUrl: "http://www.tmtyq.gov.cn/", cronExpr: "*/30 * * * *", serviceKey: "gov-portal-secondary", titleKeyword: "土默特右旗", }, ]; for (const item of tasks) { let task = await taskRepo.findOne({ where: { id: item.id } }); if (!task) { task = taskRepo.create({ ...item, stepsJson: buildSteps(item.siteUrl, item.titleKeyword), preferredExecutor: "browser", allowAgentFallback: false, status: "enabled", lastRunAt: null, }); } else { task.name = item.name; task.siteUrl = item.siteUrl; task.cronExpr = item.cronExpr; task.serviceKey = item.serviceKey; task.stepsJson = buildSteps(item.siteUrl, item.titleKeyword); task.preferredExecutor = "browser"; task.allowAgentFallback = false; task.status = "enabled"; } await taskRepo.save(task); const existingRunCount = await runRepo.count({ where: { taskId: task.id } }); if (existingRunCount === 0) { const startedAt = new Date(Date.now() - 10 * 60 * 1000); const endedAt = new Date(Date.now() - 9 * 60 * 1000); const run = runRepo.create({ id: randomUUID(), taskId: task.id, traceId: `seed-${task.serviceKey}`, status: "success", errorTag: null, failureReason: null, screenshotUrls: JSON.stringify([]), stepLogsJson: JSON.stringify([]), pageUrl: task.siteUrl, domSummary: null, startedAt, endedAt, }); await runRepo.save(run); } } } async function seedMonitoringData(dataSource: DataSource) { const agentRepo = dataSource.getRepository(MonitorAgentEntity); const targetRepo = dataSource.getRepository(MonitorTargetEntity); const runRepo = dataSource.getRepository(MonitorRunEntity); const agentSeeds = [ { id: "monitor-agent-local-linux-seed", name: "本地 Linux Agent 模板", agentKey: "linux-demo-agent", host: "192.168.1.50", platform: "linux" as const, status: "disabled" as const, monitoringEnabled: false, logIngestEnabled: false, proactivePushEnabled: false, }, { id: "monitor-agent-local-windows-seed", name: "本地 Windows Agent", agentKey: "local-windows-agent", host: "localhost", platform: "windows" as const, status: "enabled" as const, monitoringEnabled: true, logIngestEnabled: false, proactivePushEnabled: false, }, ]; for (const item of agentSeeds) { let agent = await agentRepo.findOne({ where: { id: item.id } }); if (!agent) { agent = agentRepo.create({ ...item, version: null, lastHeartbeatAt: null, lastHeartbeatMetaJson: null, lastVerifyAt: null, lastVerifyStatus: null, lastVerifyMessage: null, }); } else { agent.name = item.name; agent.agentKey = item.agentKey; agent.host = item.host; agent.platform = item.platform; agent.status = item.status; agent.monitoringEnabled = item.monitoringEnabled; agent.logIngestEnabled = item.logIngestEnabled; agent.proactivePushEnabled = item.proactivePushEnabled; } await agentRepo.save(agent); } const targets = [ { id: "monitor-http-seed", name: "本机 API 健康检查", type: "http" as const, serviceKey: "sentai-api", cronExpr: "*/5 * * * *", endpoint: "http://127.0.0.1:3001/health", configJson: JSON.stringify( { timeoutMs: 8000, expectedStatus: 200, expectedText: "\"ok\":true", }, null, 2, ), secretJson: JSON.stringify({}, null, 2), status: "enabled" as const, }, { id: "monitor-http-blackbox-template-seed", name: "HTTP Blackbox 接入模板", type: "http" as const, serviceKey: "http-blackbox-template", cronExpr: "*/10 * * * *", endpoint: "http://192.168.1.30:8080/health", configJson: JSON.stringify( { collectionMode: "blackbox", blackboxBaseUrl: "http://192.168.1.50:9115", blackboxModule: "http_2xx", blackboxTarget: "http://192.168.1.30:8080/health", }, null, 2, ), secretJson: JSON.stringify({}, null, 2), status: "disabled" as const, }, { id: "monitor-redis-template-seed", name: "Redis 接入模板", type: "redis" as const, serviceKey: "infra-redis-template", cronExpr: "*/10 * * * *", endpoint: "192.168.1.10", configJson: JSON.stringify( { tcpHost: "192.168.1.10", tcpPort: 6379, timeoutMs: 5000, redisSections: ["server", "memory", "stats", "replication"], }, null, 2, ), secretJson: JSON.stringify( { authPassword: "", sshHost: "", sshPort: 22, sshUsername: "", sshPassword: "", logFilePath: "", logTailLines: 120, logKeyword: "ERROR|Exception|Timeout|Refused", }, null, 2, ), status: "disabled" as const, }, { id: "monitor-java-template-seed", name: "Java 应用接入模板", type: "java-app" as const, serviceKey: "java-app-template", cronExpr: "*/10 * * * *", endpoint: "http://192.168.1.20:8080", configJson: JSON.stringify( { javaCollectionMode: "hybrid", javaHealthPath: "/actuator/health", javaMetricsPath: "/actuator/metrics", jmxExporterUrl: "http://192.168.1.20:9404/metrics", jmxMetricsPath: "", javaRequireHealthCheck: true, }, null, 2, ), secretJson: JSON.stringify( { sshHost: "", sshPort: 22, sshUsername: "", sshPassword: "", logFilePath: "/data/logs/app/application.log", logTailLines: 150, logKeyword: "ERROR|Exception|Timeout|Refused", }, null, 2, ), status: "disabled" as const, }, ]; for (const item of targets) { let target = await targetRepo.findOne({ where: { id: item.id } }); if (!target) { target = targetRepo.create({ ...item, lastRunAt: null, }); } else { target.name = item.name; target.type = item.type; target.serviceKey = item.serviceKey; target.cronExpr = item.cronExpr; target.endpoint = item.endpoint; target.configJson = item.configJson; target.secretJson = item.secretJson; target.status = item.status; } await targetRepo.save(target); } const healthTarget = await targetRepo.findOneByOrFail({ id: "monitor-http-seed" }); const existingRunCount = await runRepo.count({ where: { targetId: healthTarget.id } }); if (existingRunCount === 0) { const startedAt = new Date(Date.now() - 5 * 60 * 1000); const endedAt = new Date(Date.now() - 4 * 60 * 1000); const run = runRepo.create({ id: randomUUID(), targetId: healthTarget.id, traceId: "seed-monitor-http", status: "success", errorTag: null, summary: "API 健康检查通过", failureReason: null, metricsJson: JSON.stringify({ statusCode: 200 }, null, 2), detailsJson: JSON.stringify({ endpoint: healthTarget.endpoint }, null, 2), logExcerpt: null, resultJsonUrl: null, startedAt, endedAt, }); await runRepo.save(run); } } async function main() { const configService = new ConfigService(process.env); const dataSource = new DataSource( buildDataSourceOptions(configService, { synchronize: false }), ); await dataSource.initialize(); await seedInspectionData(dataSource); await seedMonitoringData(dataSource); await dataSource.destroy(); } void main().catch((error) => { console.error(error); process.exit(1); });