@lzm04521/ssh-mcp-server 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +157 -0
- package/admin-web/dist/assets/index-DXbr5DVR.js +494 -0
- package/admin-web/dist/index.html +6 -0
- package/admin-web/dist/logo.png +0 -0
- package/build/cli/command-line-parser.js +715 -0
- package/build/cli/command-line-parser.js.map +1 -0
- package/build/cli/run-mode.js +44 -0
- package/build/cli/run-mode.js.map +1 -0
- package/build/cli/stdio-proxy.js +304 -0
- package/build/cli/stdio-proxy.js.map +1 -0
- package/build/config/index.js +5 -0
- package/build/config/index.js.map +1 -0
- package/build/config/server.js +16 -0
- package/build/config/server.js.map +1 -0
- package/build/core/mcp-http-server.js +14 -0
- package/build/core/mcp-http-server.js.map +1 -0
- package/build/core/mcp-server.js +95 -0
- package/build/core/mcp-server.js.map +1 -0
- package/build/index.js +98 -0
- package/build/index.js.map +1 -0
- package/build/models/admin-types.js +135 -0
- package/build/models/admin-types.js.map +1 -0
- package/build/models/types.js +2 -0
- package/build/models/types.js.map +1 -0
- package/build/server/index.js +128 -0
- package/build/server/index.js.map +1 -0
- package/build/server/routes/admin.js +929 -0
- package/build/server/routes/admin.js.map +1 -0
- package/build/server/routes/audit.js +9 -0
- package/build/server/routes/audit.js.map +1 -0
- package/build/server/routes/backups.js +11 -0
- package/build/server/routes/backups.js.map +1 -0
- package/build/server/routes/mcp.js +52 -0
- package/build/server/routes/mcp.js.map +1 -0
- package/build/server/routes/settings.js +49 -0
- package/build/server/routes/settings.js.map +1 -0
- package/build/server/routes/system.js +147 -0
- package/build/server/routes/system.js.map +1 -0
- package/build/services/audit-store.js +123 -0
- package/build/services/audit-store.js.map +1 -0
- package/build/services/autostart-service.js +55 -0
- package/build/services/autostart-service.js.map +1 -0
- package/build/services/backup-scheduler.js +90 -0
- package/build/services/backup-scheduler.js.map +1 -0
- package/build/services/backup-service.js +113 -0
- package/build/services/backup-service.js.map +1 -0
- package/build/services/config-store.js +163 -0
- package/build/services/config-store.js.map +1 -0
- package/build/services/defaults.js +35 -0
- package/build/services/defaults.js.map +1 -0
- package/build/services/restart-helper.js +43 -0
- package/build/services/restart-helper.js.map +1 -0
- package/build/services/ssh-connection-manager.js +1775 -0
- package/build/services/ssh-connection-manager.js.map +1 -0
- package/build/services/update-service.js +93 -0
- package/build/services/update-service.js.map +1 -0
- package/build/tools/download.js +41 -0
- package/build/tools/download.js.map +1 -0
- package/build/tools/execute-command.js +53 -0
- package/build/tools/execute-command.js.map +1 -0
- package/build/tools/index.js +17 -0
- package/build/tools/index.js.map +1 -0
- package/build/tools/list-directory.js +57 -0
- package/build/tools/list-directory.js.map +1 -0
- package/build/tools/list-servers.js +51 -0
- package/build/tools/list-servers.js.map +1 -0
- package/build/tools/upload.js +41 -0
- package/build/tools/upload.js.map +1 -0
- package/build/utils/logger.js +28 -0
- package/build/utils/logger.js.map +1 -0
- package/build/utils/ssh-config-parser.js +195 -0
- package/build/utils/ssh-config-parser.js.map +1 -0
- package/build/utils/status-collector.js +226 -0
- package/build/utils/status-collector.js.map +1 -0
- package/build/utils/tool-error.js +20 -0
- package/build/utils/tool-error.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
let timer = null;
|
|
2
|
+
let stopped = false;
|
|
3
|
+
/** 上次启动调度器时的备份配置签名,用于去重避免重复重置 timer */
|
|
4
|
+
let lastSig = null;
|
|
5
|
+
/** 上次备份时间戳(ms),用于判断是否到达间隔 */
|
|
6
|
+
let lastBackupTs = 0;
|
|
7
|
+
/** 防止并发备份 */
|
|
8
|
+
let running = false;
|
|
9
|
+
/** 检查间隔:每 10 分钟检查一次是否该备份,避免 setTimeout 32-bit 溢出 */
|
|
10
|
+
const CHECK_INTERVAL_MS = 10 * 60 * 1000;
|
|
11
|
+
export function stopBackupScheduler() {
|
|
12
|
+
stopped = true;
|
|
13
|
+
if (timer) {
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
timer = null;
|
|
16
|
+
}
|
|
17
|
+
lastSig = null;
|
|
18
|
+
}
|
|
19
|
+
export function startBackupScheduler(store, svc, cfg) {
|
|
20
|
+
const b = cfg?.backups;
|
|
21
|
+
if (!b?.autoEnabled) {
|
|
22
|
+
if (lastSig !== null)
|
|
23
|
+
stopBackupScheduler();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const hours = b.intervalHours ?? 24;
|
|
27
|
+
if (!hours || hours < 1) {
|
|
28
|
+
if (lastSig !== null)
|
|
29
|
+
stopBackupScheduler();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// 配置未变则跳过,避免 chokidar onChange 重复触发导致 timer 重置
|
|
33
|
+
const sig = `${b.autoEnabled}|${hours}|${b.retentionDays ?? ""}|${b.maxCount ?? ""}`;
|
|
34
|
+
if (sig === lastSig)
|
|
35
|
+
return;
|
|
36
|
+
stopBackupScheduler();
|
|
37
|
+
stopped = false;
|
|
38
|
+
lastSig = sig;
|
|
39
|
+
const intervalMs = hours * 3600 * 1000;
|
|
40
|
+
console.error("[backup-scheduler] starting auto backup every", hours, "hour(s)");
|
|
41
|
+
const doBackup = async () => {
|
|
42
|
+
if (stopped || running)
|
|
43
|
+
return;
|
|
44
|
+
running = true;
|
|
45
|
+
try {
|
|
46
|
+
console.error("[backup-scheduler] auto snapshot start");
|
|
47
|
+
await svc.snapshot();
|
|
48
|
+
const latest = await store.load();
|
|
49
|
+
await svc.prune(latest?.backups?.retentionDays, latest?.backups?.maxCount);
|
|
50
|
+
console.error("[backup-scheduler] auto snapshot done");
|
|
51
|
+
lastBackupTs = Date.now();
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
console.error("[backup-scheduler] auto snapshot failed:", e);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
running = false;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const tick = () => {
|
|
61
|
+
if (stopped)
|
|
62
|
+
return;
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
if (now - lastBackupTs >= intervalMs) {
|
|
65
|
+
// 时间到了,执行备份(不 await,避免阻塞 tick 调度)
|
|
66
|
+
doBackup();
|
|
67
|
+
}
|
|
68
|
+
timer = setTimeout(tick, CHECK_INTERVAL_MS);
|
|
69
|
+
timer.unref?.();
|
|
70
|
+
};
|
|
71
|
+
// 首次启动时,如果从未备份过,立即执行一次
|
|
72
|
+
if (lastBackupTs === 0) {
|
|
73
|
+
lastBackupTs = Date.now();
|
|
74
|
+
// 延迟一个 tick 再执行,避免阻塞启动
|
|
75
|
+
timer = setTimeout(async () => {
|
|
76
|
+
if (stopped)
|
|
77
|
+
return;
|
|
78
|
+
await doBackup();
|
|
79
|
+
tick();
|
|
80
|
+
}, CHECK_INTERVAL_MS);
|
|
81
|
+
timer.unref?.();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
tick();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export function rescheduleBackupScheduler(store, svc, cfg) {
|
|
88
|
+
startBackupScheduler(store, svc, cfg);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=backup-scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup-scheduler.js","sourceRoot":"","sources":["../../src/services/backup-scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/** 从备份文件名解析创建时间(config-YYYY-MM-DDTHH-mm-ss-sssZ.json,UTC);解析失败返回 null。
|
|
4
|
+
* 不直接用 mtime:Windows 上 copyFile 保留源文件的 mtime,会让所有备份展示同一时间 */
|
|
5
|
+
export function tsFromName(name) {
|
|
6
|
+
const m = /^config-(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z\.json$/.exec(name);
|
|
7
|
+
if (!m)
|
|
8
|
+
return null;
|
|
9
|
+
const [, y, mo, d, h, mi, s, ms] = m;
|
|
10
|
+
return Date.UTC(+y, +mo - 1, +d, +h, +mi, +s, +ms);
|
|
11
|
+
}
|
|
12
|
+
export class BackupService {
|
|
13
|
+
store;
|
|
14
|
+
constructor(store) {
|
|
15
|
+
this.store = store;
|
|
16
|
+
}
|
|
17
|
+
get dir() { return path.join(path.dirname(this.store.path), "backups"); }
|
|
18
|
+
async prune(retentionDays, maxCount) {
|
|
19
|
+
const items = await this.list();
|
|
20
|
+
const deleted = [];
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
// 按 retentionDays 清理过期
|
|
23
|
+
if (retentionDays && retentionDays > 0) {
|
|
24
|
+
const cutoff = now - retentionDays * 24 * 3600 * 1000;
|
|
25
|
+
for (const it of items) {
|
|
26
|
+
if (it.ts && it.ts < cutoff) {
|
|
27
|
+
try {
|
|
28
|
+
await fs.unlink(it.path);
|
|
29
|
+
deleted.push(it.id);
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// 重新列出剩余,按 maxCount 截断
|
|
36
|
+
const remaining = (await this.list()).filter((i) => !deleted.includes(i.id));
|
|
37
|
+
if (maxCount && maxCount > 0 && remaining.length > maxCount) {
|
|
38
|
+
const toRemove = remaining.slice(maxCount);
|
|
39
|
+
for (const it of toRemove) {
|
|
40
|
+
try {
|
|
41
|
+
await fs.unlink(it.path);
|
|
42
|
+
deleted.push(it.id);
|
|
43
|
+
}
|
|
44
|
+
catch { }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { deleted };
|
|
48
|
+
}
|
|
49
|
+
async snapshot() {
|
|
50
|
+
await fs.mkdir(this.dir, { recursive: true });
|
|
51
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
52
|
+
const name = `config-${ts}.json`;
|
|
53
|
+
const dst = path.join(this.dir, name);
|
|
54
|
+
try {
|
|
55
|
+
await fs.copyFile(this.store.path, dst);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
// 若源不存在,创建一个空快照
|
|
59
|
+
if (e.code === "ENOENT") {
|
|
60
|
+
const cfg = await this.store.load();
|
|
61
|
+
await fs.writeFile(dst, JSON.stringify(cfg, null, 2), "utf-8");
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
// Windows copyFile 保留源 mtime,重置为当前时间,让文件系统时间与备份创建时间一致
|
|
67
|
+
const now = new Date();
|
|
68
|
+
try {
|
|
69
|
+
await fs.utimes(dst, now, now);
|
|
70
|
+
}
|
|
71
|
+
catch { }
|
|
72
|
+
// 自动按保留策略清理
|
|
73
|
+
try {
|
|
74
|
+
const cfg = await this.store.load();
|
|
75
|
+
await this.prune(cfg?.backups?.retentionDays, cfg?.backups?.maxCount);
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
78
|
+
return { id: name, name, path: dst, ts: Date.now() };
|
|
79
|
+
}
|
|
80
|
+
async list() {
|
|
81
|
+
try {
|
|
82
|
+
const files = await fs.readdir(this.dir);
|
|
83
|
+
const items = [];
|
|
84
|
+
for (const f of files.filter(f => f.endsWith(".json")).sort().reverse()) {
|
|
85
|
+
const p = path.join(this.dir, f);
|
|
86
|
+
// 优先用文件名内嵌时间戳;非标准命名回退到 mtime
|
|
87
|
+
const fromName = tsFromName(f);
|
|
88
|
+
if (fromName !== null) {
|
|
89
|
+
items.push({ id: f, name: f, path: p, ts: fromName });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const stat = await fs.stat(p);
|
|
94
|
+
items.push({ id: f, name: f, path: p, ts: stat.mtimeMs });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
items.push({ id: f, name: f, path: p, ts: 0 });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return items;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async restore(id) {
|
|
107
|
+
// 先快照当前
|
|
108
|
+
await this.snapshot();
|
|
109
|
+
const src = path.join(this.dir, id);
|
|
110
|
+
await fs.copyFile(src, this.store.path);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=backup-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup-service.js","sourceRoot":"","sources":["../../src/services/backup-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import chokidar from "chokidar";
|
|
5
|
+
import { DEFAULT_ADMIN_PORT } from "../models/admin-types.js";
|
|
6
|
+
import { GlobalConfigSchema } from "../models/admin-types.js";
|
|
7
|
+
import { DEFAULT_AUDIT_SETTINGS, DEFAULT_BACKUP_SETTINGS, DEFAULT_SECURITY, } from "./defaults.js";
|
|
8
|
+
export function getGlobalConfigPath() {
|
|
9
|
+
if (process.env.SSH_MCP_CONFIG)
|
|
10
|
+
return path.resolve(process.env.SSH_MCP_CONFIG);
|
|
11
|
+
if (process.platform === "win32") {
|
|
12
|
+
const base = process.env.ProgramData || "C:\\ProgramData";
|
|
13
|
+
return path.join(base, "SshMcpServer", "config.json");
|
|
14
|
+
}
|
|
15
|
+
const xdg = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
16
|
+
return path.join(xdg, "ssh-mcp-server", "config.json");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Rust 桌面版(serde Option)会把未设置的可选字段序列化为显式 null,
|
|
20
|
+
* 而 Zod 的 .optional() 不接受 null。本项目语义中两者均表示"未设置",
|
|
21
|
+
* 读取时统一归一为 undefined,保证两版配置文件互通(Rust 写 → Node 读)。
|
|
22
|
+
*/
|
|
23
|
+
function nullToJsonReviver(_key, value) {
|
|
24
|
+
return value === null ? undefined : value;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 主机名读兜底:hosts map 的 key 是唯一标识,Rust 桌面版历史导入的数据
|
|
28
|
+
* 对象内 name 可能为空串(显示为空、编辑变新增)。必须在 Zod parse 之前
|
|
29
|
+
* 回填——ConnectionSchema.name 有 min(1) 约束,空 name 会让整个 load 抛错。
|
|
30
|
+
*/
|
|
31
|
+
function normalizeHostNames(raw) {
|
|
32
|
+
for (const proj of Object.values(raw?.projects || {})) {
|
|
33
|
+
for (const env of Object.values(proj?.environments || {})) {
|
|
34
|
+
const hosts = env?.hosts;
|
|
35
|
+
if (!hosts)
|
|
36
|
+
continue;
|
|
37
|
+
for (const [hName, hCfg] of Object.entries(hosts)) {
|
|
38
|
+
if (hCfg && typeof hCfg === "object" &&
|
|
39
|
+
typeof hCfg.name === "string" && hCfg.name.trim() === "" &&
|
|
40
|
+
hName.trim() !== "") {
|
|
41
|
+
hCfg.name = hName;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return raw;
|
|
47
|
+
}
|
|
48
|
+
export function getFlatHosts(cfg) {
|
|
49
|
+
const m = new Map();
|
|
50
|
+
for (const [pName, proj] of Object.entries(cfg.projects || {})) {
|
|
51
|
+
for (const [eName, env] of Object.entries(proj.environments || {})) {
|
|
52
|
+
for (const [hName, hCfg] of Object.entries(env.hosts || {})) {
|
|
53
|
+
m.set(`${pName}/${eName}/${hName}`, { project: pName, environment: eName, host: hName, config: hCfg });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return m;
|
|
58
|
+
}
|
|
59
|
+
export function getFlatHostByName(name, cfg) {
|
|
60
|
+
const flat = getFlatHosts(cfg);
|
|
61
|
+
if (flat.has(name))
|
|
62
|
+
return { flatName: name, config: flat.get(name).config };
|
|
63
|
+
const candidates = [];
|
|
64
|
+
for (const [k, v] of flat)
|
|
65
|
+
if (v.host === name)
|
|
66
|
+
candidates.push(k);
|
|
67
|
+
if (candidates.length === 1)
|
|
68
|
+
return { flatName: candidates[0], config: flat.get(candidates[0]).config };
|
|
69
|
+
if (candidates.length > 1)
|
|
70
|
+
return { ambiguous: true, candidates };
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
export class ConfigStore {
|
|
74
|
+
opts;
|
|
75
|
+
watchers = [];
|
|
76
|
+
constructor(opts = {}) {
|
|
77
|
+
this.opts = opts;
|
|
78
|
+
}
|
|
79
|
+
get path() {
|
|
80
|
+
return this.opts.configPath || getGlobalConfigPath();
|
|
81
|
+
}
|
|
82
|
+
async getFlatHosts() {
|
|
83
|
+
const cfg = await this.load();
|
|
84
|
+
return getFlatHosts(cfg);
|
|
85
|
+
}
|
|
86
|
+
async getFlatHostByName(name) {
|
|
87
|
+
const cfg = await this.load();
|
|
88
|
+
return getFlatHostByName(name, cfg);
|
|
89
|
+
}
|
|
90
|
+
async load() {
|
|
91
|
+
try {
|
|
92
|
+
const raw = await fs.readFile(this.path, "utf-8");
|
|
93
|
+
const parsed = JSON.parse(raw, nullToJsonReviver);
|
|
94
|
+
const normalized = normalizeHostNames(parsed);
|
|
95
|
+
if (!normalized.audit)
|
|
96
|
+
normalized.audit = { ...DEFAULT_AUDIT_SETTINGS };
|
|
97
|
+
if (!normalized.backups)
|
|
98
|
+
normalized.backups = { ...DEFAULT_BACKUP_SETTINGS };
|
|
99
|
+
if (!normalized.security) {
|
|
100
|
+
normalized.security = {
|
|
101
|
+
commandWhitelist: [...DEFAULT_SECURITY.commandWhitelist],
|
|
102
|
+
commandBlacklist: [...DEFAULT_SECURITY.commandBlacklist],
|
|
103
|
+
allowedLocalPaths: [...DEFAULT_SECURITY.allowedLocalPaths],
|
|
104
|
+
allowedRemotePaths: [...DEFAULT_SECURITY.allowedRemotePaths],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return GlobalConfigSchema.parse(normalized);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
if (e.code === "ENOENT") {
|
|
111
|
+
return {
|
|
112
|
+
port: DEFAULT_ADMIN_PORT,
|
|
113
|
+
projects: {},
|
|
114
|
+
audit: { ...DEFAULT_AUDIT_SETTINGS },
|
|
115
|
+
backups: { ...DEFAULT_BACKUP_SETTINGS },
|
|
116
|
+
security: {
|
|
117
|
+
commandWhitelist: [...DEFAULT_SECURITY.commandWhitelist],
|
|
118
|
+
commandBlacklist: [...DEFAULT_SECURITY.commandBlacklist],
|
|
119
|
+
allowedLocalPaths: [...DEFAULT_SECURITY.allowedLocalPaths],
|
|
120
|
+
allowedRemotePaths: [...DEFAULT_SECURITY.allowedRemotePaths],
|
|
121
|
+
},
|
|
122
|
+
preConnect: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
throw e;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async save(patch) {
|
|
129
|
+
const current = await this.load();
|
|
130
|
+
// 深合并 audit/backups/security,防止浅合并丢字段(R1-4)
|
|
131
|
+
const next = { ...current, ...patch };
|
|
132
|
+
if (patch.audit && current.audit)
|
|
133
|
+
next.audit = { ...current.audit, ...patch.audit };
|
|
134
|
+
if (patch.backups && current.backups)
|
|
135
|
+
next.backups = { ...current.backups, ...patch.backups };
|
|
136
|
+
if (patch.security && current.security)
|
|
137
|
+
next.security = { ...current.security, ...patch.security };
|
|
138
|
+
GlobalConfigSchema.parse(next);
|
|
139
|
+
const dir = path.dirname(this.path);
|
|
140
|
+
await fs.mkdir(dir, { recursive: true });
|
|
141
|
+
try {
|
|
142
|
+
await fs.copyFile(this.path, this.path + ".bak");
|
|
143
|
+
}
|
|
144
|
+
catch { }
|
|
145
|
+
const tmp = this.path + ".tmp";
|
|
146
|
+
await fs.writeFile(tmp, JSON.stringify(next, null, 2), "utf-8");
|
|
147
|
+
JSON.parse(await fs.readFile(tmp, "utf-8"));
|
|
148
|
+
await fs.rename(tmp, this.path);
|
|
149
|
+
}
|
|
150
|
+
onChange(cb) {
|
|
151
|
+
const watcher = chokidar.watch(this.path, { ignoreInitial: true });
|
|
152
|
+
let t = null;
|
|
153
|
+
watcher.on("all", () => {
|
|
154
|
+
if (t)
|
|
155
|
+
clearTimeout(t);
|
|
156
|
+
t = setTimeout(async () => {
|
|
157
|
+
cb(await this.load());
|
|
158
|
+
}, 200);
|
|
159
|
+
});
|
|
160
|
+
return () => watcher.close();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=config-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/services/config-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// 全局默认值的唯一来源:后端路由与前端(经 /admin/api/defaults)共用,
|
|
2
|
+
// 防止多处写死后漂移(安全黑名单漂移尤其危险)。
|
|
3
|
+
// 新建项目时自动创建的默认环境
|
|
4
|
+
export const DEFAULT_ENVIRONMENTS = ["开发环境", "测试环境", "生产环境", "UAT环境"];
|
|
5
|
+
// 未配置安全策略时的默认命令黑名单(行首锚定正则)
|
|
6
|
+
export const DEFAULT_COMMAND_BLACKLIST = [
|
|
7
|
+
"^rm\\s+.*",
|
|
8
|
+
"^shutdown.*",
|
|
9
|
+
"^reboot.*",
|
|
10
|
+
"^halt.*",
|
|
11
|
+
"^poweroff.*",
|
|
12
|
+
"^mkfs.*",
|
|
13
|
+
"^dd\\s+.*",
|
|
14
|
+
];
|
|
15
|
+
// 默认审计策略:开启审计、开启成功执行记录、保留 30 天
|
|
16
|
+
export const DEFAULT_AUDIT_SETTINGS = {
|
|
17
|
+
enabled: true,
|
|
18
|
+
retentionDays: 30,
|
|
19
|
+
logResults: true,
|
|
20
|
+
};
|
|
21
|
+
// 默认备份策略:保留 30 天、最多 20 份、默认不开启定时自动备份、默认间隔 24 小时
|
|
22
|
+
export const DEFAULT_BACKUP_SETTINGS = {
|
|
23
|
+
retentionDays: 30,
|
|
24
|
+
maxCount: 20,
|
|
25
|
+
autoEnabled: false,
|
|
26
|
+
intervalHours: 24,
|
|
27
|
+
};
|
|
28
|
+
// 默认安全策略:默认预设高危命令黑名单,白名单与目录留空
|
|
29
|
+
export const DEFAULT_SECURITY = {
|
|
30
|
+
commandWhitelist: [],
|
|
31
|
+
commandBlacklist: [...DEFAULT_COMMAND_BLACKLIST],
|
|
32
|
+
allowedLocalPaths: [],
|
|
33
|
+
allowedRemotePaths: [],
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/services/defaults.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,0BAA0B;AAE1B,iBAAiB;AACjB,MAAM,CAAC,MAAM,oBAAoB,GAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhF,2BAA2B;AAC3B,MAAM,CAAC,MAAM,yBAAyB,GAAa;IACjD,WAAW;IACX,aAAa;IACb,WAAW;IACX,SAAS;IACT,aAAa;IACb,SAAS;IACT,WAAW;CACZ,CAAC;AAEF,+BAA+B;AAC/B,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,EAAE;IACjB,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF,gDAAgD;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,aAAa,EAAE,EAAE;IACjB,QAAQ,EAAE,EAAE;IACZ,WAAW,EAAE,KAAK;IAClB,aAAa,EAAE,EAAE;CAClB,CAAC;AAEF,8BAA8B;AAC9B,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gBAAgB,EAAE,EAAc;IAChC,gBAAgB,EAAE,CAAC,GAAG,yBAAyB,CAAC;IAChD,iBAAiB,EAAE,EAAc;IACjC,kBAAkB,EAAE,EAAc;CACnC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
// 应用控制(重启):移植自 MCP-DB-Tools 的 RestartHelper ——
|
|
6
|
+
// 起一个分离的隐藏 PowerShell 守候当前 PID 退出后再拉起新实例(避免端口占用冲突),
|
|
7
|
+
// 随后优雅关闭当前进程。新实例不带 --admin-port,从 config.json 读端口(端口修改由此生效)。
|
|
8
|
+
// 实现为临时 .ps1 文件执行:避免 -Command 的嵌套引号在含空格路径下被错误解析;
|
|
9
|
+
// 新实例 stdout/stderr 落盘到系统临时目录,启动失败时可查。
|
|
10
|
+
export function scheduleRestartAndExit(close, timeoutMs = 15000) {
|
|
11
|
+
const exe = process.execPath;
|
|
12
|
+
const script = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
13
|
+
if (process.platform === "win32" && script) {
|
|
14
|
+
try {
|
|
15
|
+
const dir = os.tmpdir();
|
|
16
|
+
const ps1 = path.join(dir, `ssh-mcp-restart-${process.pid}.ps1`);
|
|
17
|
+
const outLog = path.join(dir, "ssh-mcp-restart-out.log");
|
|
18
|
+
const errLog = path.join(dir, "ssh-mcp-restart-err.log");
|
|
19
|
+
const ps = [
|
|
20
|
+
"$ErrorActionPreference='SilentlyContinue'",
|
|
21
|
+
`if (Get-Process -Id ${process.pid}) { Wait-Process -Id ${process.pid} -Timeout 30 }`,
|
|
22
|
+
"Start-Sleep -Milliseconds 600",
|
|
23
|
+
`Start-Process -FilePath '${exe.replace(/'/g, "''")}' -ArgumentList '"${script.replace(/'/g, "''")}"','--admin' -WorkingDirectory '${path.dirname(script).replace(/'/g, "''")}' -WindowStyle Hidden -RedirectStandardOutput '${outLog}' -RedirectStandardError '${errLog}'`,
|
|
24
|
+
].join("; ");
|
|
25
|
+
fs.writeFileSync(ps1, ps, "utf-8");
|
|
26
|
+
spawn("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-File", ps1], {
|
|
27
|
+
detached: true,
|
|
28
|
+
stdio: "ignore",
|
|
29
|
+
windowsHide: true,
|
|
30
|
+
}).unref();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// 守候脚本启动失败则仅退出(由外部进程管理器拉起)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// 非 Windows:无注册表/PowerShell 机制,仅优雅退出,依赖 systemd/pm2 等外部拉起
|
|
37
|
+
const timer = setTimeout(() => process.exit(0), timeoutMs);
|
|
38
|
+
timer.unref();
|
|
39
|
+
Promise.resolve(close())
|
|
40
|
+
.catch(() => { })
|
|
41
|
+
.finally(() => process.exit(0));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=restart-helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-helper.js","sourceRoot":"","sources":["../../src/services/restart-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,+CAA+C;AAC/C,oDAAoD;AACpD,6DAA6D;AAC7D,iDAAiD;AACjD,uCAAuC;AACvC,MAAM,UAAU,sBAAsB,CAAC,KAA6B,EAAE,SAAS,GAAG,KAAK;IACrF,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEpE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YACzD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YACzD,MAAM,EAAE,GAAG;gBACT,2CAA2C;gBAC3C,uBAAuB,OAAO,CAAC,GAAG,wBAAwB,OAAO,CAAC,GAAG,gBAAgB;gBACrF,+BAA+B;gBAC/B,4BAA4B,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,mCAAmC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,kDAAkD,MAAM,6BAA6B,MAAM,GAAG;aAC5Q,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;YACnC,KAAK,CAAC,gBAAgB,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE;gBAC5G,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,QAAQ;gBACf,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC;IACD,0DAA0D;IAE1D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC3D,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;SACrB,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;SACf,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"}
|