41 KiB
Admin Environment Dashboard Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将 Admin /dashboard/analytics 改造成多站点环境状态总览总控面板,覆盖本机开发、NAS 线上、腾讯云、r4se 远程环境,并明确展示未接入证据,第一版只做观测与只读自检,不提供高风险写操作。
Architecture: 后端在 API admin/platform-config 下新增环境面板聚合模块,以 Site -> Node -> Service -> Signal 为统一模型;HTTP dashboard/self-check 负责状态快照和只读兜底,平台级 EnvironmentEventBus 通过 local/mqtt 模式收口 topic 事件并驱动 recent events 与 cache invalidation,API SSE stream 把脱敏后的增量事件推送给 Admin;内部服务信号从现有 Nest 服务读取,远程信号通过只读适配器读取 Jenkins、Kubernetes、Tencent Cloud、Caddy、WireGuard、OpenClash/Mihomo;前端保留 Vben Dashboard 路由,替换为 A 方案布局:顶部状态条、左侧站点栏、中间拓扑、右侧证据/动作抽屉、底部事件流。Admin 不跑轮询或定时刷新。
Tech Stack: NestJS、TypeScript、TypeORM、axios、mqtt、tencentcloud-sdk-nodejs、Vben Admin、Vue 3、antdv-next、Vitest/Jest、Playwright 轻量页面烟测。
Source Spec
D:\MyFiles\KT\Node\kt-template-online-api\docs\superpowers\specs\2026-06-18-admin-environment-dashboard-design.md- 当前确认的页面方向:方案 A
Site Command Center - 第一版能力边界:观测与只读自检;重启、部署、迁移、重建容器、插件启停、任务立即执行、Caddy/OpenClash 改配置等动作必须展示为禁用。
File Structure Map
API Repo
D:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\domain\environment-dashboard.types.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\application\environment-dashboard-status.mapper.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\application\environment-dashboard-action.catalog.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\application\environment-event.materializer.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\application\environment-dashboard.service.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\application\environment-dashboard-self-check.service.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\application\environment-event-stream.service.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\environment-dashboard-config.service.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\environment-dashboard-cache.service.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\environment-dashboard-evidence.mapper.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\event\environment-event-bus.service.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\event\environment-mqtt-topic.catalog.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\event\qqbot-environment-event.bridge.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\adapters\environment-readonly-http.client.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\adapters\jenkins-readonly.adapter.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\adapters\kubernetes-readonly.adapter.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\adapters\tencent-cloud-readonly.adapter.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\adapters\caddy-readonly.adapter.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\adapters\wireguard-readonly.adapter.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\adapters\mihomo-readonly.adapter.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\collectors\local-dev-signal.collector.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\infrastructure\collectors\nas-prod-signal.collector.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\presentation\environment-dashboard.controller.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\environment-dashboard\presentation\dto\environment-dashboard.dto.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\admin\platform-config\admin-platform-config.module.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\core\qqbot-core.module.tsD:\MyFiles\KT\Node\kt-template-online-api\src\modules\qqbot\plugin-platform\qqbot-plugin-platform.module.tsD:\MyFiles\KT\Node\kt-template-online-api\.env.exampleD:\MyFiles\KT\Node\kt-template-online-api\README.mdD:\MyFiles\KT\Node\kt-template-online-api\API.mdD:\MyFiles\KT\Node\kt-template-online-api\test\modules\admin\environment-dashboard\*.spec.ts
Admin Repo
D:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\api\system\environment.tsD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\index.vueD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\components\EnvironmentStatusBar.vueD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\components\EnvironmentSiteRail.vueD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\components\EnvironmentTopology.vueD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\components\EnvironmentEvidencePanel.vueD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\components\EnvironmentEventStream.vueD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\composables\useEnvironmentDashboardStream.tsD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\types.tsD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\dashboard\analytics\environment-dashboard.spec.tsxD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\api\system\environment.spec.tsD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\locales\langs\zh-CN\page.jsonD:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\locales\langs\en-US\page.json
Data Contract
The API response uses stable IDs so Admin can preserve selection across refreshes.
export type EnvironmentHealthStatus =
| 'ok'
| 'degraded'
| 'down'
| 'blocked'
| 'isolated'
| 'unknown'
| 'unwired';
export type EnvironmentSiteStatus = 'online' | 'degraded' | 'isolated' | 'unknown';
export type EnvironmentSignalSourceKind =
| 'live'
| 'cached'
| 'derived'
| 'configured'
| 'external-link'
| 'unwired';
export interface EnvironmentDashboardResponse {
generatedAt: string;
refreshedAt: string;
summary: EnvironmentDashboardSummary;
sites: EnvironmentSite[];
topology: EnvironmentTopology;
actions: EnvironmentAction[];
events: EnvironmentEvent[];
}
export interface EnvironmentEventEnvelope {
eventId: string;
topic: string;
siteId: string;
nodeId?: string;
serviceId?: string;
signalId?: string;
severity: EnvironmentHealthStatus;
sourceKind: 'local' | 'mqtt' | EnvironmentSignalSourceKind;
observedAt: string;
expiresAt?: string;
retained?: boolean;
summary: string;
evidence?: EnvironmentEvidence[];
}
export type EnvironmentStreamEventType =
| 'environment-event'
| 'environment-signal'
| 'snapshot-required'
| 'heartbeat'
| 'error';
Severity aggregation order is fixed:
const severityWeight: Record<EnvironmentHealthStatus, number> = {
ok: 0,
unwired: 1,
unknown: 1,
degraded: 2,
isolated: 3,
down: 4,
blocked: 5,
};
Execution Rules
- Work on development branches in each touched repo; do not create
.worktreedirectories. - No backend
.env.development,.env.production, real token, SSH key, cloud secret, Jenkins token, kube token, or database password may be committed. - Every new or touched function/method/hook/event handler/exported arrow function must include JSDoc explaining purpose, parameter origin, return semantics, and side effects when present.
- High-risk actions are represented by disabled action records with
enabled: falseanddisabledReason; controller must reject execution paths because this plan does not add a generic write-action endpoint. unwiredandunknownare valid product states. A missing integration must be visible evidence, not a green health badge.- MQTT is an event layer, not the only source of truth. Retained messages require
observedAtandexpiresAt; expired retained messages must becomeunknownorunwired. - Admin must not connect to MQTT directly. Broker credentials and internal topics stay inside API runtime.
- Admin must not poll or use timer-based dashboard refresh. After the first snapshot, fresh state arrives through SSE; only user actions and
snapshot-requiredmay trigger another dashboard request. - For interface changes, verify through a real local HTTP request against the Nest app or an already running local service.
Task 0: Baseline Branches And Dependency Check
-
In API repo, confirm clean state:
cd D:\MyFiles\KT\Node\kt-template-online-api git status --short git rev-parse --abbrev-ref HEADExpected output: no unstaged lines; branch is
mainor an existing development branch chosen by the user. -
Create or switch API development branch:
git switch -c codex/admin-environment-dashboardExpected output:
Switched to a new branch 'codex/admin-environment-dashboard'. -
In Admin repo, confirm clean state and create branch:
cd D:\MyFiles\KT\Vue\kt-template-admin git status --short git switch -c codex/admin-environment-dashboardExpected output: no unstaged lines, then new branch message.
-
Confirm package manager and engine gates:
cd D:\MyFiles\KT\Node\kt-template-online-api node -v pnpm -v pnpm pkg get packageManager pnpm pkg get enginesExpected output: current Node and pnpm satisfy
engines; if not, runnvm lsbefore changing version. -
Install Tencent Cloud official SDK in API repo only when it is not already present:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm add tencentcloud-sdk-nodejsExpected output: dependency added to
package.jsonand lockfile. No credential values are written. -
Confirm the existing MQTT client dependency before adding any event-bus code:
cd D:\MyFiles\KT\Node\kt-template-online-api rg -n '"mqtt"' package.json pnpm-lock.yamlExpected output:
mqttis already present inpackage.json; do not add a second broker/client package.
Task 1: API Contract, Status Mapper, And RED Tests
-
Create
environment-dashboard.types.tswith domain interfaces for site/node/service/signal/action/event/topology. Include status unions from the spec and source metadata:export interface EnvironmentSignal { id: string; label: string; status: EnvironmentHealthStatus; sourceKind: EnvironmentSignalSourceKind; summary: string; evidence: EnvironmentEvidence[]; observedAt?: string; staleAfterSeconds?: number; } -
Create
environment-dashboard-status.mapper.tswith documented helpers:pickWorstHealthStatus(statuses)mapSiteStatus(statuses)countSignals(sites)normalizeObservedAt(dateLike)
-
Add RED test
test/modules/admin/environment-dashboard/environment-dashboard-status.spec.ts:import { mapSiteStatus, pickWorstHealthStatus, } from '../../../../src/modules/admin/platform-config/environment-dashboard/application/environment-dashboard-status.mapper'; describe('environment dashboard status mapper', () => { it('uses blocked as the strongest signal', () => { expect(pickWorstHealthStatus(['ok', 'down', 'blocked'])).toBe('blocked'); }); it('keeps unwired integrations visible without marking a site healthy', () => { expect(mapSiteStatus(['ok', 'unwired'])).toBe('unknown'); }); it('marks isolated remote sites separately from ordinary degradation', () => { expect(mapSiteStatus(['ok', 'isolated'])).toBe('isolated'); }); }); -
Run RED:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-dashboard-status.spec.ts --runInBandExpected output: fails before mapper implementation is complete because module or exported functions are missing.
-
Implement mapper and rerun the same command. Expected output: test suite passes.
Task 2: API Action Catalog And Disabled Write Boundaries
-
Create
environment-dashboard-action.catalog.ts. It must return all supported visible actions:- enabled readonly:
refresh-dashboard,run-self-check,open-runtime-logs,open-service-route - disabled high-risk:
restart-api-pod,trigger-jenkins-deploy,run-db-migration,recreate-napcat-container,toggle-plugin,run-plugin-task-now,create-minio-bucket,wordpress-import,reload-caddy,switch-openclash,restart-tencent-cvm,modify-wireguard-peer
- enabled readonly:
-
Add
test/modules/admin/environment-dashboard/environment-dashboard-action.catalog.spec.tsasserting disabled actions are present and all write actions haveenabled: false. -
Run RED, implement, rerun:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-dashboard-action.catalog.spec.ts --runInBandExpected output after implementation: all action catalog tests pass.
Task 3: API Evidence Helpers And Readonly HTTP Client
-
Create
environment-dashboard-evidence.mapper.tswith functions:liveEvidence(source, summary, observedAt, metadata)unwiredEvidence(source, missingConfigKeys, documentationPath)errorEvidence(source, error, observedAt)cachedEvidence(source, summary, observedAt, expiresAt)
-
Create
environment-readonly-http.client.tswrapping axios with:- GET and HEAD only
- timeout from environment dashboard config
- response body truncation before evidence storage
- secret-safe header handling
-
Add tests:
environment-dashboard-evidence.mapper.spec.tsenvironment-readonly-http.client.spec.ts
-
Run targeted tests:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-dashboard-evidence.mapper.spec.ts test/modules/admin/environment-dashboard/environment-readonly-http.client.spec.ts --runInBandExpected output: tests pass; no real network access is required because axios is mocked.
Task 3A: API Platform Event Bus And MQTT Topic Contract
-
Create
infrastructure/event/environment-mqtt-topic.catalog.tswith documented topic builders:signal(siteId, nodeId, serviceId)event(siteId, nodeId, serviceId)selfCheckResult(siteId)qqbotRuntime(selfId)qqbotNapcatLogin(selfId)pluginTaskRun(pluginKey, taskKey)
-
Create
infrastructure/event/environment-event-bus.service.ts:- supports
ENV_DASHBOARD_EVENT_BUS=local|mqtt - uses
ENV_DASHBOARD_MQTT_URL,ENV_DASHBOARD_MQTT_CLIENT_ID,ENV_DASHBOARD_MQTT_USERNAME,ENV_DASHBOARD_MQTT_PASSWORD, andENV_DASHBOARD_MQTT_TOPIC_PREFIX - subscribes only to the environment dashboard topic prefix
- publishes local events through in-process subscribers for tests and dev
- marks broker disconnect as an environment event
- never logs broker credentials or full payloads
- supports
-
Create
application/environment-event.materializer.ts:- accepts
EnvironmentEventEnvelope - rejects stale retained messages when
expiresAtis earlier than current time - appends safe recent events for dashboard response
- invalidates
environment-dashboard-cache.service.tswhen a non-stale signal event arrives - never turns MQTT-only data into green status without fresh
observedAtevidence
- accepts
-
Create
infrastructure/event/qqbot-environment-event.bridge.ts:- maps QQBot/NapCat runtime events into environment event envelopes
- depends on a narrow bus interface, not QQBot topic constants inside dashboard application code
- does not make environment dashboard depend on
QqbotBusServiceimplementation details
-
Add RED/GREEN tests:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-mqtt-topic.catalog.spec.ts test/modules/admin/environment-dashboard/environment-event-bus.service.spec.ts test/modules/admin/environment-dashboard/environment-event.materializer.spec.ts test/modules/admin/environment-dashboard/qqbot-environment-event.bridge.spec.ts --runInBandExpected output after implementation: topic mapping is deterministic, expired retained signal becomes non-green, broker disconnect creates an event without marking every service down, and QQBot bridge tests pass without importing dashboard code from QQBot-specific topic constants.
Task 3B: API SSE Stream For Admin Realtime Updates
-
Create
application/environment-event-stream.service.ts:- exposes an RxJS Observable stream compatible with Nest
@Sse - subscribes to
EnvironmentEventBus/environment-event.materializer.ts - emits
environment-event,environment-signal,snapshot-required,heartbeat, anderror - supports browser
Last-Event-IDorlastEventIdquery fallback for replay - maintains a bounded in-memory replay buffer controlled by
ENV_DASHBOARD_SSE_REPLAY_LIMIT - emits
snapshot-requiredwhen the requested event id is older than the replay buffer - uses
ENV_DASHBOARD_SSE_HEARTBEAT_MSonly for connection keepalive, not for dashboard refresh - never opens MQTT or remote HTTP connections from the SSE method itself
- exposes an RxJS Observable stream compatible with Nest
-
Add RED/GREEN tests:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-event-stream.service.spec.ts --runInBandExpected output after implementation: replay by last event id works, stale replay id emits
snapshot-required, heartbeat does not trigger dashboard service calls, and stream payloads contain no broker credentials.
Task 4: API Remote Adapters For Jenkins And Kubernetes
-
Create
environment-dashboard-config.service.tsthat reads optional dashboard integration env vars. It returns explicit missing-key lists for each integration instead of throwing. -
Create
jenkins-readonly.adapter.ts:- uses Jenkins Remote Access API endpoint from env
- reads latest build/job metadata through GET
- maps configured-but-failing response to
downordegraded - maps missing URL/token/job to
unwired - never triggers builds
-
Create
kubernetes-readonly.adapter.ts:- uses Kubernetes API server, namespace, label selector, deployment name, and bearer token from env
- reads Deployment and Pod metadata through GET
- maps generation mismatch or not-ready pods to
degraded - maps API unreachable to
isolated - maps missing config to
unwired - never sends PATCH/POST/DELETE
-
Add adapter tests:
- missing config produces
unwiredevidence with exact missing keys - success response produces
live - HTTP failure produces non-green status
- missing config produces
-
Run:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/jenkins-readonly.adapter.spec.ts test/modules/admin/environment-dashboard/kubernetes-readonly.adapter.spec.ts --runInBandExpected output: tests pass with mocked HTTP client.
Task 5: API Remote Adapters For Tencent Cloud, Caddy, WireGuard, And Mihomo
-
Create
tencent-cloud-readonly.adapter.ts:- uses
tencentcloud-sdk-nodejs - reads CVM instance status and Cloud Monitor summaries only when credentials and instance IDs are configured
- maps missing credentials to
unwired - maps SDK errors to
isolatedorunknownwith error evidence - stores no secret in evidence or logs
- uses
-
Create
caddy-readonly.adapter.ts:- reads configured public URL with HEAD/GET
- optionally reads Caddy Admin API config only when admin URL is configured
- maps missing admin API to an
unwiredsignal and public URL success tolive - never reloads or writes Caddy config
-
Create
wireguard-readonly.adapter.ts:- reads only configured health endpoints or public route checks for Tencent Cloud and r4se WireGuard
- if no safe endpoint is configured, returns
unwiredwith evidence explaining the absent read source - does not SSH into hosts in the dashboard request path
-
Create
mihomo-readonly.adapter.ts:- reads Mihomo/OpenClash REST API
GET /version,GET /configs, andGET /proxieswhen URL and secret are configured - maps missing URL/secret to
unwired - never changes selector, proxy, rule, or config
- reads Mihomo/OpenClash REST API
-
Add tests for each adapter using mocked clients:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/tencent-cloud-readonly.adapter.spec.ts test/modules/admin/environment-dashboard/caddy-readonly.adapter.spec.ts test/modules/admin/environment-dashboard/wireguard-readonly.adapter.spec.ts test/modules/admin/environment-dashboard/mihomo-readonly.adapter.spec.ts --runInBandExpected output: tests pass; Tencent SDK is mocked.
Task 6: API Internal Signal Collectors
-
Create
local-dev-signal.collector.tsfor local-dev site:- API process/runtime health from
RuntimeHealthService.getRuntimeHealth() - local Admin proxy/config as configured source
- local dependency gaps shown as
unknownorunwired
- API process/runtime health from
-
Create
nas-prod-signal.collector.tsfor NAS production site:- Runtime health from
RuntimeHealthService - QQBot summary from
QqbotDashboardService.summary() - NapCat runtime/login visibility through exported NapCat runtime port or explicitly exported service
- plugin platform/task visibility through
QqbotPluginPlatformServiceand exported task service - MinIO connection through existing
MinioClientService.checkConnection - WordPress integration through
WordpressService.tryLoginWithConfiguredAdmin() - Jenkins/K8s through adapters from Task 4
- Loki/MySQL/Redis as configured or derived signals when no direct safe adapter exists
- Runtime health from
-
Update module exports only where the collector needs existing services:
src/modules/qqbot/core/qqbot-core.module.ts: exportQqbotDashboardServicesrc/modules/qqbot/plugin-platform/qqbot-plugin-platform.module.ts: exportQqbotPluginTaskServiceif task status counts are required- Prefer existing exported NapCat runtime port before exporting concrete NapCat services
-
Add
nas-prod-signal.collector.spec.tswith mocked dependencies proving:- QQBot offline does not mark API down
- one disabled plugin task appears as degraded service evidence
- MinIO failure is contained to MinIO service
- missing Jenkins/K8s config remains
unwired
-
Run:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/nas-prod-signal.collector.spec.ts --runInBandExpected output: tests pass.
Task 7: API Dashboard Aggregator, Cache, And Self-Check
-
Create
environment-dashboard-cache.service.ts:- caches successful aggregate response for
ENV_DASHBOARD_CACHE_TTL_MS - marks cached signals with
sourceKind: 'cached' - does not cache thrown exceptions as green results
- caches successful aggregate response for
-
Create
environment-dashboard.service.ts:- assembles four sites:
local-dev,nas-prod,tencent-cloud,r4se - aggregates summary counters
- merges safe recent events from
environment-event.materializer.ts - builds topology edges:
- local-dev -> API/Admin local services
- nas-prod -> Jenkins/K8s/API/Admin/MySQL/Redis/Loki/MinIO/WordPress/QQBot/NapCat/Plugin Platform/Plugin Tasks
- tencent-cloud -> WireGuard/Caddy
- r4se -> WireGuard/OpenClash
- attaches action catalog
- returns deterministic IDs for UI selection
- assembles four sites:
-
Create
environment-dashboard-self-check.service.ts:- runs the same collectors with
forceRefresh: true - returns
EnvironmentDashboardResponse - records event entries for failed adapters through
EnvironmentEventBus - performs no write operation
- runs the same collectors with
-
Add service tests:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-dashboard.service.spec.ts test/modules/admin/environment-dashboard/environment-dashboard-self-check.service.spec.ts --runInBandExpected output: tests pass and generated topology includes all four sites.
Task 8: API Controller, Module Wiring, Env Docs, And Local HTTP Smoke
-
Create
presentation/dto/environment-dashboard.dto.tswith Swagger DTO classes. Keep DTO shape aligned to the domain response. -
Create
presentation/environment-dashboard.controller.ts:GET /system/environment/dashboardPOST /system/environment/self-checkGET /system/environment/events/stream- guards:
JwtAuthGuard - response wrapper: existing
vbenSuccess events/streamuses Nest@Sseand returnsEnvironmentStreamEvent- JSDoc on controller methods states Admin route origin and no side effects beyond read-only probes/cache refresh/SSE subscription
-
Update
admin-platform-config.module.tsimports/providers/controllers. Include only required modules. -
Update
.env.example,README.md, andAPI.mdwith optional env variables and read-only semantics:ENV_DASHBOARD_CACHE_TTL_MS=15000 ENV_DASHBOARD_SIGNAL_TIMEOUT_MS=5000 ENV_DASHBOARD_EVENT_BUS=local ENV_DASHBOARD_MQTT_URL= ENV_DASHBOARD_MQTT_CLIENT_ID=kt-template-online-api-environment ENV_DASHBOARD_MQTT_USERNAME= ENV_DASHBOARD_MQTT_PASSWORD= ENV_DASHBOARD_MQTT_TOPIC_PREFIX=kt/env ENV_DASHBOARD_SSE_REPLAY_LIMIT=200 ENV_DASHBOARD_SSE_HEARTBEAT_MS=25000 ENV_DASHBOARD_JENKINS_URL= ENV_DASHBOARD_JENKINS_JOB=KT-Template/KT-Template-API/main ENV_DASHBOARD_JENKINS_USERNAME= ENV_DASHBOARD_JENKINS_TOKEN= ENV_DASHBOARD_K8S_API_SERVER= ENV_DASHBOARD_K8S_NAMESPACE=kt-prod ENV_DASHBOARD_K8S_DEPLOYMENT=kt-template-online-api ENV_DASHBOARD_K8S_LABEL_SELECTOR=app=kt-template-online-api ENV_DASHBOARD_K8S_BEARER_TOKEN= ENV_DASHBOARD_TENCENT_CLOUD_ENABLED=false ENV_DASHBOARD_TENCENT_SECRET_ID= ENV_DASHBOARD_TENCENT_SECRET_KEY= ENV_DASHBOARD_TENCENT_REGION= ENV_DASHBOARD_TENCENT_INSTANCE_ID= ENV_DASHBOARD_CADDY_ADMIN_URL= ENV_DASHBOARD_CADDY_PUBLIC_URL= ENV_DASHBOARD_R4SE_MIHOMO_URL= ENV_DASHBOARD_R4SE_MIHOMO_SECRET= ENV_DASHBOARD_TENCENT_WIREGUARD_HEALTH_URL= ENV_DASHBOARD_R4SE_WIREGUARD_HEALTH_URL= -
Run API targeted tests:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-dashboard.controller.spec.ts --runInBandExpected output: controller spec passes.
-
Add controller spec coverage for SSE:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm exec jest --runTestsByPath test/modules/admin/environment-dashboard/environment-dashboard.events.controller.spec.ts --runInBandExpected output: authenticated request can subscribe to SSE,
Last-Event-IDis passed to stream service, and no dashboard polling timer exists in API code. -
Run API typecheck:
cd D:\MyFiles\KT\Node\kt-template-online-api pnpm run typecheckExpected output: no TypeScript errors.
-
Start or reuse local API service, then make real local requests:
curl.exe -H "Authorization: Bearer <local-admin-token>" http://127.0.0.1:<api-port>/system/environment/dashboard curl.exe -X POST -H "Authorization: Bearer <local-admin-token>" http://127.0.0.1:<api-port>/system/environment/self-check curl.exe -N -H "Authorization: Bearer <local-admin-token>" http://127.0.0.1:<api-port>/system/environment/events/streamExpected output: dashboard and self-check return
code: 0or existing success wrapper equivalent, four site records, and at least oneunwiredevidence item when remote env vars are absent; SSE stream returnstext/event-streamand emits a heartbeat or synthetic environment event without closing immediately.
Task 9: Admin API Wrapper And RED Tests
-
Create
apps/web-antdv-next/src/api/system/environment.tswith exported interfaces matching the API response and functions:getEnvironmentDashboard()runEnvironmentSelfCheck()getEnvironmentDashboardEventsUrl(lastEventId?)
-
Add
apps/web-antdv-next/src/api/system/environment.spec.ts:import { requestClient } from '#/api/request'; import { getEnvironmentDashboard, getEnvironmentDashboardEventsUrl, runEnvironmentSelfCheck, } from './environment'; vi.mock('#/api/request', () => ({ requestClient: { get: vi.fn(), post: vi.fn(), }, })); describe('environment dashboard api', () => { it('loads the aggregate dashboard', async () => { await getEnvironmentDashboard(); expect(requestClient.get).toHaveBeenCalledWith('/system/environment/dashboard'); }); it('runs readonly self check', async () => { await runEnvironmentSelfCheck(); expect(requestClient.post).toHaveBeenCalledWith('/system/environment/self-check'); }); it('builds the SSE stream url without exposing MQTT config', () => { expect(getEnvironmentDashboardEventsUrl()).toContain('/system/environment/events/stream'); expect(getEnvironmentDashboardEventsUrl('evt-1')).toContain('lastEventId=evt-1'); }); }); -
Run RED, implement, rerun:
cd D:\MyFiles\KT\Vue\kt-template-admin pnpm exec vitest run apps/web-antdv-next/src/api/system/environment.spec.tsExpected output after implementation: tests pass.
Task 10: Admin Component Shell And Visual States
-
Replace the old sample content in
apps/web-antdv-next/src/views/dashboard/analytics/index.vuewith a single route root element. Do not keep old Vben analysis sample cards/charts. -
Create
types.tsfor view-only types. Keep it aligned with API wrapper types by importing from the API wrapper where practical. -
Create
EnvironmentStatusBar.vue:- global status, generated/refreshed time, signal counters
- refresh and self-check buttons using antdv-next buttons/icons
- loading and error state
-
Create
EnvironmentSiteRail.vue:- site cards/list items for local-dev, nas-prod, tencent-cloud, r4se
- status badge per site
- evidence count and unwired count
-
Create
EnvironmentTopology.vue:- responsive service topology using semantic HTML/CSS grid, not canvas-only rendering
- service nodes show status, source kind, and selected state
- links/edges represented accessibly and remain readable on 1366px desktop
-
Create
EnvironmentEvidencePanel.vue:- selected site/service/signal details
- evidence list grouped by source
- action list with disabled high-risk actions visibly disabled and reason text
- no write endpoint calls
-
Create
EnvironmentEventStream.vue:- latest events sorted newest first
- status/source kind tags
- compact display with stable row height
-
Styling constraints:
- use
antdv-nextcomponents already present in the app - avoid marketing hero, gradient blobs, decorative cards inside cards
- ensure mobile layout stacks as top status -> site rail -> topology -> evidence -> events
- text must not overlap at 390px, 768px, 1366px, and 1920px widths
- use
Task 11: Admin Page Integration, Locales, And Tests
-
Wire
index.vue:- load dashboard once on mount
- start
useEnvironmentDashboardStream.tsEventSource after the first snapshot succeeds - refresh with
getEnvironmentDashboard - self-check with
runEnvironmentSelfCheck - process
environment-eventandenvironment-signalwithout fetching dashboard again - process
snapshot-requiredby running exactly one dashboard snapshot request - ignore
heartbeatfor state updates - preserve selected site/service when IDs still exist after refresh
- select first degraded/down/blocked service by default, else first site
- display explicit empty/error state when API fails
- close EventSource on route leave/unmount
-
Create
composables/useEnvironmentDashboardStream.ts:- wraps browser
EventSource - uses
getEnvironmentDashboardEventsUrl(lastEventId?) - stores last accepted event id in component state only
- exposes connection state for the status bar
- does not use
setInterval,setTimeoutrefresh loops, or background polling - lets native EventSource reconnect; after server sends
snapshot-required, caller performs one snapshot fetch
- wraps browser
-
Update locale title from analytics sample to environment dashboard:
apps/web-antdv-next/src/locales/langs/zh-CN/page.json:环境总览apps/web-antdv-next/src/locales/langs/en-US/page.json:Environment
-
Add page spec
environment-dashboard.spec.tsx:- mocked API returns all four sites
- disabled write actions render disabled
- unwired Jenkins/K8s evidence renders as evidence, not healthy state
- MQTT-origin recent event renders only after API returns it; page does not instantiate an MQTT client
- SSE
environment-signalupdates one node without callinggetEnvironmentDashboard - SSE
snapshot-requiredcallsgetEnvironmentDashboardonce - no dashboard polling or timer-based refresh is registered
- self-check button calls the POST wrapper
-
Run:
cd D:\MyFiles\KT\Vue\kt-template-admin pnpm exec vitest run apps/web-antdv-next/src/views/dashboard/analytics/environment-dashboard.spec.tsxExpected output: tests pass.
-
Run Admin typecheck:
cd D:\MyFiles\KT\Vue\kt-template-admin pnpm -F @vben/web-antdv-next run typecheckExpected output: no TypeScript errors.
Task 12: Local Page Smoke And Interface Smoke
-
Start or reuse local API and Admin dev servers. Record ports and process IDs under
.kt-workspace/test-artifacts/environment-dashboard/. -
In the in-app browser or Playwright, open
/dashboard/analyticsand verify:- top global status bar is visible
- left site rail contains local-dev, nas-prod, tencent-cloud, r4se
- topology contains Jenkins, K8s, QQBot/NapCat, Plugin Tasks, Tencent Caddy/WireGuard, r4se OpenClash/WireGuard
- right panel shows evidence for selected item
- bottom event stream appears
- event stream can display an API-provided MQTT-origin event without exposing broker configuration in frontend state
- Network tab shows one dashboard snapshot and one
events/streamlong connection after page load, with no repeated dashboard polling - closing/reopening the route closes the old EventSource before creating a new one
- Jenkins/K8s missing config appears as
unwiredorunknown, never green - disabled write actions cannot be clicked
-
Capture screenshots:
- desktop 1366x768
- wide desktop 1920x1080
- mobile 390x844
Store under
.kt-workspace/test-artifacts/environment-dashboard/.
-
Make one real local HTTP call from Admin session to both endpoints through the browser network panel or request logs. Evidence must include endpoint, HTTP status, and presence of four site IDs.
-
Make one real local SSE connection from Admin session. Evidence must include endpoint, HTTP status,
text/event-stream, and no repeated dashboard GET requests after the initial snapshot. -
Stop Node/Vite processes started by this task if they were not already running before the task.
Task 13: KT Loops, Review, Commit, And Push Gate
-
Run documentation sync check for changed files:
cd D:\MyFiles\KT pnpm --dir mcp/ktWorkflow run change-doc-sync -- --project Node/kt-template-online-api --changed "src/modules/admin/platform-config/environment-dashboard,.env.example,README.md,API.md" pnpm --dir mcp/ktWorkflow run change-doc-sync -- --project Vue/kt-template-admin --changed "apps/web-antdv-next/src/views/dashboard/analytics,apps/web-antdv-next/src/api/system/environment.ts"Expected output: either required docs are updated or the script records that no additional docs are required.
-
Run cleanup dry-run and execute only stale
.kt-workspacecleanup if reported:cd D:\MyFiles\KT pnpm --dir mcp/ktWorkflow run cleanup-history -- --dry-runExpected output: if
deleted=[], no execute run is needed; if stale files are listed, run with--executeand rerun dry-run. -
Run global review for API and Admin:
cd D:\MyFiles\KT pnpm --dir mcp/ktWorkflow run global-review -- --project Node/kt-template-online-api pnpm --dir mcp/ktWorkflow run global-review -- --project Vue/kt-template-adminExpected output: no Important findings remain. Any finding that is a false positive must be documented with evidence.
-
Update
D:\MyFiles\KT\TASKS.mdwith concise scope, changed files, validation evidence, and remaining online env wiring gaps. -
Commit API repo:
cd D:\MyFiles\KT\Node\kt-template-online-api git status --short git add package.json pnpm-lock.yaml .env.example README.md API.md src/modules/admin/platform-config src/modules/qqbot/core/qqbot-core.module.ts src/modules/qqbot/plugin-platform/qqbot-plugin-platform.module.ts test/modules/admin/environment-dashboard docs/superpowers/plans/2026-06-18-admin-environment-dashboard-implementation-plan.md git commit -m "feat: 新增环境总览总控面板接口"Expected output: one API commit. Do not stage secrets.
-
Commit Admin repo:
cd D:\MyFiles\KT\Vue\kt-template-admin git status --short git add apps/web-antdv-next/src/api/system/environment.ts apps/web-antdv-next/src/api/system/environment.spec.ts apps/web-antdv-next/src/views/dashboard/analytics apps/web-antdv-next/src/locales/langs/zh-CN/page.json apps/web-antdv-next/src/locales/langs/en-US/page.json git commit -m "feat: 重构分析页为环境总览总控面板"Expected output: one Admin commit.
-
Commit KT root if
TASKS.mdchanged:cd D:\MyFiles\KT git status --short git add TASKS.md git commit -m "docs: 同步环境总览面板任务状态"Expected output: one root coordination commit.
-
Push only after the user explicitly asks for push or deployment:
git push origin codex/admin-environment-dashboardExpected output: remote branch pushed. If push triggers deployment, Jenkins/K8s observation and online self-test are required before claiming online completion.
Online Wiring Checklist After Implementation
When the user asks to deploy or test online, wire environment variables in the online secret/config system without committing values:
- Jenkins read-only URL/job/user/token.
- K8s API server/namespace/deployment/selector/bearer token with read-only RBAC.
- Tencent Cloud official SDK credentials scoped to CVM/Cloud Monitor read-only APIs.
- Tencent Cloud Caddy public URL and optional Admin API URL if safely reachable.
- r4se Mihomo/OpenClash read-only API URL and secret.
- WireGuard health endpoints for Tencent Cloud and r4se, or explicit unwired status if no safe read endpoint exists.
Online completion evidence must include:
GET /system/environment/dashboardreturns four sites.POST /system/environment/self-checkreturns fresh evidence.GET /system/environment/events/streamestablishes an SSE stream; a backend environment event reaches Admin without polling.- MQTT event bus is either
localwith explicit configured evidence, ormqttwith broker connection status shown as evidence. - A synthetic non-secret MQTT signal event invalidates dashboard cache and appears in recent events; an expired retained signal does not create a green status.
- Jenkins/K8s nodes show live evidence when configured, or explicit missing-key evidence when not configured.
- Tencent Cloud and r4se nodes do not show green health without a live/configured source.
- Admin page displays the same state without console errors.