VPS Monitor 动态化改造:从硬编码到自动发现服务
实现
1. 扫描逻辑
新建 src/lib/services-discovery.ts:
// Docker 容器
async function scanDocker(): Promise<DetectedService[]> {
const raw = await sh(`docker ps --format '{{.Names}}|{{.Ports}}' 2>/dev/null`);
const services: DetectedService[] = [];
for (const line of raw.split("\n").filter(Boolean)) {
const [name, ports] = line.split("|");
if (!ports) continue;
const matches = ports.matchAll(/(?:0\.0\.0\.0|127\.0\.0\.1|\[::\]):(\d+)->/g);
const seen = new Set<number>();
for (const m of matches) {
const port = parseInt(m[1], 10);
if (seen.has(port)) continue;
seen.add(port);
services.push({ port, source: "docker", rawName: name, host: "docker" });
}
}
return services;
}
// 监听端口
async function scanPorts(): Promise<DetectedService[]> {
const raw = await sh(`ss -tlnp 2>/dev/null`);
const services: DetectedService[] = [];
for (const line of raw.split("\n").slice(1)) {
const parts = line.split(/\s+/);
if (parts.length < 4) continue;
const localAddr = parts[3];
const m = localAddr.match(/:(\d+)$/);
if (!m) continue;
const port = parseInt(m[1], 10);
let host = localAddr.replace(/:\d+$/, "").replace(/^\[|\]$/g, "");
if (host === "*") host = "0.0.0.0";
const procMatch = line.match(/users:\(\("([^"]+)",pid=(\d+)/);
const rawName = procMatch ? procMatch[1] : `port-${port}`;
services.push({ port, source: "port", rawName, host });
}
return services;
}
// K8s NodePort(ss 抓不到)
async function scanK8sNodePorts(): Promise<Set<number>> {
const raw = await sh(`kubectl get svc -A --no-headers 2>/dev/null | awk '{print $6}'`);
const ports = new Set<number>();
for (const line of raw.split("\n")) {
for (const m of line.matchAll(/:(\d+)\//g)) {
ports.add(parseInt(m[1], 10));
}
}
return ports;
}
2. 系统服务过滤
const BLACKLIST_PORTS = new Set([
22, 53, 80, 111, 443, 631, // 系统
3080, 6443, 10250, 10257, 10259, // k3s
36961, 40353, // Tailscale
21115, 21116, 21117, 21118, 21119, // RustDesk 内部
]);
const BLACKLIST_PROCS = [
"sshd", "systemd-resolve", "rpcbind",
"k3s-server", "k3s-agent", "tailscaled",
"hbbs", "hbbr", "containerd", "dockerd",
"systemd", "cron", "dbus",
];
3. 显示配置
新建 config/services.json:
{
"services": [
{ "port": 3003, "name": "VPS Monitor", "desc": "このダッシュボード", "category": "monitoring", "public": true, "path": "/" },
{ "port": 3000, "name": "one-api", "desc": "LLM ゲートウェイ", "category": "llm", "public": true, "path": "/" },
{ "port": 5678, "name": "n8n", "desc": "ワークフロー自動化", "category": "automation", "public": false, "path": "/" },
{ "port": 5001, "name": "RAG Video Service", "desc": "動画処理", "category": "ai", "public": false, "path": "/" },
{ "port": 5002, "name": "RAG Stock Service", "desc": "株式分析", "category": "ai", "public": false, "path": "/" }
]
}
关键:这份配置是「显示美化」,不是「白名单」。扫描到的服务如果没在这里,**仍会显示**(用进程名或 port-XXXX)。
