@sfmc-bds/bds-tools 0.1.0 → 0.2.0-beta.0
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/dist/bds-manager.d.ts.map +1 -1
- package/dist/bds-manager.js +236 -263
- package/dist/bds-manager.js.map +7 -1
- package/dist/changelog.js +39 -50
- package/dist/changelog.js.map +7 -1
- package/dist/check-update.d.ts +2 -2
- package/dist/check-update.d.ts.map +1 -1
- package/dist/check-update.js +361 -401
- package/dist/check-update.js.map +7 -1
- package/dist/cli-pack-manager.d.ts +10 -6
- package/dist/cli-pack-manager.d.ts.map +1 -1
- package/dist/cli-pack-manager.js +226 -135
- package/dist/cli-pack-manager.js.map +7 -1
- package/dist/fsx.js +86 -100
- package/dist/fsx.js.map +7 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +166 -185
- package/dist/http.js.map +7 -1
- package/dist/is-main.d.ts +17 -0
- package/dist/is-main.d.ts.map +1 -0
- package/dist/is-main.js +32 -0
- package/dist/is-main.js.map +7 -0
- package/dist/log.d.ts +2 -4
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +12 -19
- package/dist/log.js.map +7 -1
- package/dist/logger.js +6 -10
- package/dist/logger.js.map +7 -1
- package/dist/pack-manager.d.ts +88 -24
- package/dist/pack-manager.d.ts.map +1 -1
- package/dist/pack-manager.js +280 -245
- package/dist/pack-manager.js.map +7 -1
- package/dist/paths.d.ts +8 -3
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +53 -39
- package/dist/paths.js.map +7 -1
- package/dist/qqutil.d.ts.map +1 -1
- package/dist/qqutil.js +90 -87
- package/dist/qqutil.js.map +7 -1
- package/dist/recovery.js +26 -37
- package/dist/recovery.js.map +7 -1
- package/dist/rollback.d.ts +1 -1
- package/dist/rollback.js +55 -74
- package/dist/rollback.js.map +7 -1
- package/dist/server-properties.d.ts +12 -0
- package/dist/server-properties.d.ts.map +1 -0
- package/dist/server-properties.js +28 -0
- package/dist/server-properties.js.map +7 -0
- package/dist/taskbar.d.ts +2 -0
- package/dist/taskbar.d.ts.map +1 -1
- package/dist/taskbar.js +51 -82
- package/dist/taskbar.js.map +7 -1
- package/dist/types.js +1 -5
- package/dist/types.js.map +7 -1
- package/dist/update-result.d.ts +12 -0
- package/dist/update-result.d.ts.map +1 -0
- package/dist/update-result.js +15 -0
- package/dist/update-result.js.map +7 -0
- package/dist/upstream.js +94 -108
- package/dist/upstream.js.map +7 -1
- package/dist/version.js +63 -81
- package/dist/version.js.map +7 -1
- package/dist/world-packs.d.ts +112 -0
- package/dist/world-packs.d.ts.map +1 -0
- package/dist/world-packs.js +335 -0
- package/dist/world-packs.js.map +7 -0
- package/dist/zipx.d.ts +10 -0
- package/dist/zipx.d.ts.map +1 -0
- package/dist/zipx.js +48 -0
- package/dist/zipx.js.map +7 -0
- package/package.json +17 -6
package/dist/paths.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* paths.ts — 路径常量 & 加载 bds_updater.json
|
|
3
3
|
*
|
|
4
|
-
* ROOT_DIR =
|
|
5
|
-
*
|
|
4
|
+
* ROOT_DIR = 项目/数据根,通过 SDK 统一解析(env SFMC_ROOT > __dirname 上溯)。
|
|
5
|
+
* STATE_DIR / LOG_PATH 等运行态路径一律走 SDK stateDir / logFile,勿再手写 ".sfmc"。
|
|
6
6
|
*/
|
|
7
7
|
import { type BdsUpdaterConfig } from "@sfmc-bds/sdk/node/config";
|
|
8
|
-
/** 项目根目录:bds-tools 的上一级。统一通过 SDK
|
|
8
|
+
/** 项目根目录:bds-tools 的上一级。统一通过 SDK 解析。 */
|
|
9
9
|
export declare const ROOT_DIR: string;
|
|
10
10
|
export declare const CFG_PATH: string;
|
|
11
|
+
/** 运行态目录(相对数据根,勿落在 bds-tools/dist) */
|
|
12
|
+
export declare const STATE_DIR: string;
|
|
11
13
|
export declare const LOG_PATH: string;
|
|
12
14
|
export declare const VERSION_CACHE: string;
|
|
13
15
|
export declare const PID_FILE: string;
|
|
16
|
+
/** 更新中途失败可恢复的回滚标记 */
|
|
14
17
|
export declare const ROLLBACK_MARKER: string;
|
|
18
|
+
/** 确保 .sfmc 运行态目录存在(PID 写入等不经 writeJson 的路径需要) */
|
|
19
|
+
export declare function ensureStateDir(): void;
|
|
15
20
|
/** 加载并解析 bds_updater.json。
|
|
16
21
|
* 启动时 ensure 文件存在,避免 wizard 没跑时 bds_updater.json 缺失导致 loadConfig 抛错。 */
|
|
17
22
|
export declare function loadConfig(): BdsUpdaterConfig;
|
package/dist/paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAOL,KAAK,gBAAgB,EACtB,MAAM,2BAA2B,CAAC;AAOnC,wCAAwC;AACxC,eAAO,MAAM,QAAQ,EAAE,MAAiE,CAAC;AACzF,eAAO,MAAM,QAAQ,EAAE,MAAiD,CAAC;AAEzE,sCAAsC;AACtC,eAAO,MAAM,SAAS,EAAE,MAA2B,CAAC;AACpD,eAAO,MAAM,QAAQ,EAAE,MAAwC,CAAC;AAChE,eAAO,MAAM,aAAa,EAAE,MAAuD,CAAC;AACpF,eAAO,MAAM,QAAQ,EAAE,MAAwC,CAAC;AAChE,qBAAqB;AACrB,eAAO,MAAM,eAAe,EAAE,MAA0D,CAAC;AAEzF,kDAAkD;AAClD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;yEACyE;AACzE,wBAAgB,UAAU,IAAI,gBAAgB,CAO7C;AAED,yBAAyB;AACzB,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,gBAAgB,CAAC;CACvB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,gBAAgB,GAAG,QAAQ,CAS5D;AAED,gBAAgB;AAChB,wBAAgB,QAAQ,IAAI,IAAI,CAM/B"}
|
package/dist/paths.js
CHANGED
|
@@ -1,47 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import {
|
|
2
|
+
configPath,
|
|
3
|
+
DEFAULT_BDS_UPDATER_CONFIG,
|
|
4
|
+
loadEnsuredConfig,
|
|
5
|
+
logFile,
|
|
6
|
+
resolveRuntimeRoot,
|
|
7
|
+
stateDir
|
|
8
|
+
} from "@sfmc-bds/sdk/node/config";
|
|
8
9
|
import fs from "node:fs";
|
|
9
10
|
import path from "node:path";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
11
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
13
|
const SCRIPT_DIR = path.dirname(__filename);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export function loadConfig() {
|
|
23
|
-
const cfg = ensureJsonConfig(ROOT_DIR, "bds_updater.json", {});
|
|
24
|
-
delete cfg._comment;
|
|
25
|
-
return cfg;
|
|
14
|
+
const ROOT_DIR = resolveRuntimeRoot(path.resolve(SCRIPT_DIR, "..", ".."));
|
|
15
|
+
const CFG_PATH = configPath(ROOT_DIR, "bds_updater.json");
|
|
16
|
+
const STATE_DIR = stateDir(ROOT_DIR);
|
|
17
|
+
const LOG_PATH = logFile(ROOT_DIR, "bds-update");
|
|
18
|
+
const VERSION_CACHE = path.join(STATE_DIR, "bds-version-cache.json");
|
|
19
|
+
const PID_FILE = path.join(STATE_DIR, "bds.pid");
|
|
20
|
+
const ROLLBACK_MARKER = path.join(STATE_DIR, "last_update_rollback.json");
|
|
21
|
+
function ensureStateDir() {
|
|
22
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
26
23
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const preserve = Array.isArray(cfg.preserve) ? cfg.preserve : [];
|
|
35
|
-
return { bds_path, backup_dir, preserve, cfg };
|
|
24
|
+
function loadConfig() {
|
|
25
|
+
return loadEnsuredConfig(
|
|
26
|
+
ROOT_DIR,
|
|
27
|
+
"bds_updater.json",
|
|
28
|
+
"bds_updater",
|
|
29
|
+
{ ...DEFAULT_BDS_UPDATER_CONFIG }
|
|
30
|
+
);
|
|
36
31
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
function resolvePaths(cfg) {
|
|
33
|
+
const bds_path = path.resolve(cfg.bds_path || process.cwd());
|
|
34
|
+
const backup_dir = path.resolve(cfg.backup_dir || path.join(bds_path, "..", "backups"));
|
|
35
|
+
const relativeBackup = path.relative(bds_path, backup_dir);
|
|
36
|
+
if (relativeBackup === "" || !relativeBackup.startsWith("..") && !path.isAbsolute(relativeBackup)) {
|
|
37
|
+
throw new Error("backup_dir must be outside bds_path so deployment cannot delete the backup");
|
|
38
|
+
}
|
|
39
|
+
const preserve = Array.isArray(cfg.preserve) ? cfg.preserve : [];
|
|
40
|
+
return { bds_path, backup_dir, preserve, cfg };
|
|
46
41
|
}
|
|
47
|
-
|
|
42
|
+
function clearPid() {
|
|
43
|
+
try {
|
|
44
|
+
if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
CFG_PATH,
|
|
50
|
+
LOG_PATH,
|
|
51
|
+
PID_FILE,
|
|
52
|
+
ROLLBACK_MARKER,
|
|
53
|
+
ROOT_DIR,
|
|
54
|
+
STATE_DIR,
|
|
55
|
+
VERSION_CACHE,
|
|
56
|
+
clearPid,
|
|
57
|
+
ensureStateDir,
|
|
58
|
+
loadConfig,
|
|
59
|
+
resolvePaths
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=paths.js.map
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/paths.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * paths.ts \u2014 \u8DEF\u5F84\u5E38\u91CF & \u52A0\u8F7D bds_updater.json\n *\n * ROOT_DIR = \u9879\u76EE/\u6570\u636E\u6839,\u901A\u8FC7 SDK \u7EDF\u4E00\u89E3\u6790(env SFMC_ROOT > __dirname \u4E0A\u6EAF)\u3002\n * STATE_DIR / LOG_PATH \u7B49\u8FD0\u884C\u6001\u8DEF\u5F84\u4E00\u5F8B\u8D70 SDK stateDir / logFile,\u52FF\u518D\u624B\u5199 \".sfmc\"\u3002\n */\n\nimport {\n configPath,\n DEFAULT_BDS_UPDATER_CONFIG,\n loadEnsuredConfig,\n logFile,\n resolveRuntimeRoot,\n stateDir,\n type BdsUpdaterConfig,\n} from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst SCRIPT_DIR: string = path.dirname(__filename);\n/** \u9879\u76EE\u6839\u76EE\u5F55:bds-tools \u7684\u4E0A\u4E00\u7EA7\u3002\u7EDF\u4E00\u901A\u8FC7 SDK \u89E3\u6790\u3002 */\nexport const ROOT_DIR: string = resolveRuntimeRoot(path.resolve(SCRIPT_DIR, \"..\", \"..\"));\nexport const CFG_PATH: string = configPath(ROOT_DIR, \"bds_updater.json\");\n\n/** \u8FD0\u884C\u6001\u76EE\u5F55(\u76F8\u5BF9\u6570\u636E\u6839\uFF0C\u52FF\u843D\u5728 bds-tools/dist) */\nexport const STATE_DIR: string = stateDir(ROOT_DIR);\nexport const LOG_PATH: string = logFile(ROOT_DIR, \"bds-update\");\nexport const VERSION_CACHE: string = path.join(STATE_DIR, \"bds-version-cache.json\");\nexport const PID_FILE: string = path.join(STATE_DIR, \"bds.pid\");\n/** \u66F4\u65B0\u4E2D\u9014\u5931\u8D25\u53EF\u6062\u590D\u7684\u56DE\u6EDA\u6807\u8BB0 */\nexport const ROLLBACK_MARKER: string = path.join(STATE_DIR, \"last_update_rollback.json\");\n\n/** \u786E\u4FDD .sfmc \u8FD0\u884C\u6001\u76EE\u5F55\u5B58\u5728(PID \u5199\u5165\u7B49\u4E0D\u7ECF writeJson \u7684\u8DEF\u5F84\u9700\u8981) */\nexport function ensureStateDir(): void {\n fs.mkdirSync(STATE_DIR, { recursive: true });\n}\n\n/** \u52A0\u8F7D\u5E76\u89E3\u6790 bds_updater.json\u3002\n * \u542F\u52A8\u65F6 ensure \u6587\u4EF6\u5B58\u5728,\u907F\u514D wizard \u6CA1\u8DD1\u65F6 bds_updater.json \u7F3A\u5931\u5BFC\u81F4 loadConfig \u629B\u9519\u3002 */\nexport function loadConfig(): BdsUpdaterConfig {\n return loadEnsuredConfig(\n ROOT_DIR,\n \"bds_updater.json\",\n \"bds_updater\",\n { ...DEFAULT_BDS_UPDATER_CONFIG } as Record<string, unknown>\n ) as BdsUpdaterConfig;\n}\n\n/** \u89E3\u6790\u540E\u7684 BDS \u8DEF\u5F84 / \u5907\u4EFD\u76EE\u5F55 */\nexport interface BdsPaths {\n bds_path: string;\n backup_dir: string;\n preserve: string[];\n cfg: BdsUpdaterConfig;\n}\n\nexport function resolvePaths(cfg: BdsUpdaterConfig): BdsPaths {\n const bds_path = path.resolve(cfg.bds_path || process.cwd());\n const backup_dir = path.resolve(cfg.backup_dir || path.join(bds_path, \"..\", \"backups\"));\n const relativeBackup = path.relative(bds_path, backup_dir);\n if (relativeBackup === \"\" || (!relativeBackup.startsWith(\"..\") && !path.isAbsolute(relativeBackup))) {\n throw new Error(\"backup_dir must be outside bds_path so deployment cannot delete the backup\");\n }\n const preserve = Array.isArray(cfg.preserve) ? cfg.preserve : [];\n return { bds_path, backup_dir, preserve, cfg };\n}\n\n/** \u5220\u9664 PID \u6587\u4EF6 */\nexport function clearPid(): void {\n try {\n if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);\n } catch {\n /* ignore */\n }\n}\n"],
|
|
5
|
+
"mappings": "AAOA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,MAAM,aAAa,cAAc,YAAY,GAAG;AAChD,MAAM,aAAqB,KAAK,QAAQ,UAAU;AAE3C,MAAM,WAAmB,mBAAmB,KAAK,QAAQ,YAAY,MAAM,IAAI,CAAC;AAChF,MAAM,WAAmB,WAAW,UAAU,kBAAkB;AAGhE,MAAM,YAAoB,SAAS,QAAQ;AAC3C,MAAM,WAAmB,QAAQ,UAAU,YAAY;AACvD,MAAM,gBAAwB,KAAK,KAAK,WAAW,wBAAwB;AAC3E,MAAM,WAAmB,KAAK,KAAK,WAAW,SAAS;AAEvD,MAAM,kBAA0B,KAAK,KAAK,WAAW,2BAA2B;AAGhF,SAAS,iBAAuB;AACrC,KAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C;AAIO,SAAS,aAA+B;AAC7C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,EAAE,GAAG,2BAA2B;AAAA,EAClC;AACF;AAUO,SAAS,aAAa,KAAiC;AAC5D,QAAM,WAAW,KAAK,QAAQ,IAAI,YAAY,QAAQ,IAAI,CAAC;AAC3D,QAAM,aAAa,KAAK,QAAQ,IAAI,cAAc,KAAK,KAAK,UAAU,MAAM,SAAS,CAAC;AACtF,QAAM,iBAAiB,KAAK,SAAS,UAAU,UAAU;AACzD,MAAI,mBAAmB,MAAO,CAAC,eAAe,WAAW,IAAI,KAAK,CAAC,KAAK,WAAW,cAAc,GAAI;AACnG,UAAM,IAAI,MAAM,4EAA4E;AAAA,EAC9F;AACA,QAAM,WAAW,MAAM,QAAQ,IAAI,QAAQ,IAAI,IAAI,WAAW,CAAC;AAC/D,SAAO,EAAE,UAAU,YAAY,UAAU,IAAI;AAC/C;AAGO,SAAS,WAAiB;AAC/B,MAAI;AACF,QAAI,GAAG,WAAW,QAAQ,EAAG,IAAG,WAAW,QAAQ;AAAA,EACrD,QAAQ;AAAA,EAER;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/qqutil.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qqutil.d.ts","sourceRoot":"","sources":["../src/qqutil.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"qqutil.d.ts","sourceRoot":"","sources":["../src/qqutil.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuBH,0BAA0B;AAC1B,wBAAgB,iBAAiB,IAAI,OAAO,CAO3C;AA6CD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1D;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAYlE;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMlF"}
|
package/dist/qqutil.js
CHANGED
|
@@ -1,101 +1,104 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* - 静默模式 (失败不抛出),保证主流程不被通知干扰
|
|
7
|
-
*/
|
|
1
|
+
import {
|
|
2
|
+
configPath,
|
|
3
|
+
modulePath,
|
|
4
|
+
readJson
|
|
5
|
+
} from "@sfmc-bds/sdk/node/config";
|
|
8
6
|
import http from "node:http";
|
|
9
|
-
import { configPath, modulePath, readJson } from "@sfmc-bds/sdk/node/config";
|
|
10
|
-
import { ROOT_DIR } from "./paths.js";
|
|
11
7
|
import { log } from "./log.js";
|
|
8
|
+
import { ROOT_DIR } from "./paths.js";
|
|
12
9
|
let cachedCfg = null;
|
|
13
10
|
function getConfig() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return cachedCfg;
|
|
11
|
+
if (cachedCfg) return cachedCfg;
|
|
12
|
+
cachedCfg = readJson(configPath(ROOT_DIR, "qq_config.json")) ?? {};
|
|
13
|
+
return cachedCfg;
|
|
18
14
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const mod = catalog.modules?.find((m) => m.id === "qq-bridge" || m.configKey === "qq_bridge");
|
|
26
|
-
return mod ? lock.modules?.[mod.id ?? ""]?.enabled === true : false;
|
|
15
|
+
function isQqBridgeEnabled() {
|
|
16
|
+
const catalog = readJson(modulePath(ROOT_DIR, "catalog.json"));
|
|
17
|
+
const lock = readJson(modulePath(ROOT_DIR, "module-lock.json"));
|
|
18
|
+
if (!catalog || !lock) return true;
|
|
19
|
+
const mod = catalog.modules?.find((m) => m.id === "qq-bridge" || m.configKey === "qq_bridge");
|
|
20
|
+
return mod ? lock.modules?.[mod.id ?? ""]?.enabled === true : false;
|
|
27
21
|
}
|
|
28
|
-
function sendToLLBot(payload, timeoutMs =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
req.on("timeout", () => {
|
|
54
|
-
req.destroy(new Error(`LLBot 超时 ${timeoutMs}ms`));
|
|
22
|
+
function sendToLLBot(payload, timeoutMs = 5e3) {
|
|
23
|
+
const cfg = getConfig();
|
|
24
|
+
const url = new URL(cfg.llbot_http || "http://127.0.0.1:3000");
|
|
25
|
+
const data = JSON.stringify(payload);
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const req = http.request(
|
|
28
|
+
{
|
|
29
|
+
hostname: url.hostname,
|
|
30
|
+
port: url.port || 3e3,
|
|
31
|
+
path: "/send_group_msg",
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
"Content-Length": Buffer.byteLength(data)
|
|
36
|
+
},
|
|
37
|
+
timeout: timeoutMs
|
|
38
|
+
},
|
|
39
|
+
(res) => {
|
|
40
|
+
let body = "";
|
|
41
|
+
res.on("data", (c) => body += c);
|
|
42
|
+
res.on("end", () => {
|
|
43
|
+
if (res.statusCode === 200) resolve();
|
|
44
|
+
else reject(new Error(`LLBot HTTP ${res.statusCode}: ${body.slice(0, 200)}`));
|
|
55
45
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
req.on("timeout", () => {
|
|
49
|
+
req.destroy(new Error(`LLBot \u8D85\u65F6 ${timeoutMs}ms`));
|
|
59
50
|
});
|
|
51
|
+
req.on("error", reject);
|
|
52
|
+
req.write(data);
|
|
53
|
+
req.end();
|
|
54
|
+
});
|
|
60
55
|
}
|
|
61
56
|
async function safeSend(label, fn) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
57
|
+
try {
|
|
58
|
+
await fn();
|
|
59
|
+
} catch (e) {
|
|
60
|
+
log.warn(`[QQ] ${label} \u5931\u8D25: ${e.message}`);
|
|
61
|
+
}
|
|
68
62
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
63
|
+
async function sendText(text) {
|
|
64
|
+
const cfg = getConfig();
|
|
65
|
+
if (!isQqBridgeEnabled() || !cfg.qq_group_id) {
|
|
66
|
+
log.warn("[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await safeSend(
|
|
70
|
+
"sendText",
|
|
71
|
+
() => sendToLLBot({
|
|
72
|
+
group_id: parseInt(cfg.qq_group_id ?? "0", 10),
|
|
73
|
+
message: [{ type: "text", data: { text } }]
|
|
74
|
+
})
|
|
75
|
+
);
|
|
79
76
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
77
|
+
async function sendMixed(segments) {
|
|
78
|
+
const cfg = getConfig();
|
|
79
|
+
if (!isQqBridgeEnabled() || !cfg.qq_group_id) {
|
|
80
|
+
log.warn("[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
await safeSend(
|
|
84
|
+
"sendMixed",
|
|
85
|
+
() => sendToLLBot({
|
|
86
|
+
group_id: parseInt(cfg.qq_group_id ?? "0", 10),
|
|
87
|
+
message: segments
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (segments.length === 0)
|
|
98
|
-
return;
|
|
99
|
-
await sendMixed(segments);
|
|
91
|
+
async function sendWithImage(text, base64Img) {
|
|
92
|
+
const segments = [];
|
|
93
|
+
if (text) segments.push({ type: "text", data: { text } });
|
|
94
|
+
if (base64Img) segments.push({ type: "image", data: { file: `base64://${base64Img}` } });
|
|
95
|
+
if (segments.length === 0) return;
|
|
96
|
+
await sendMixed(segments);
|
|
100
97
|
}
|
|
101
|
-
|
|
98
|
+
export {
|
|
99
|
+
isQqBridgeEnabled,
|
|
100
|
+
sendMixed,
|
|
101
|
+
sendText,
|
|
102
|
+
sendWithImage
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=qqutil.js.map
|
package/dist/qqutil.js.map
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/qqutil.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * qqutil.ts \u2014 QQ \u901A\u77E5\u5DE5\u5177 (\u57FA\u4E8E LLBot HTTP OneBot 11)\n *\n * \u6539\u8FDB:\n * - sendTimeout \u63D0\u4F9B\u603B\u8D85\u65F6\uFF0C\u907F\u514D\u901A\u77E5\u53D1\u9001\u6302\u6B7B\u4E3B\u6D41\u7A0B\n * - \u9759\u9ED8\u6A21\u5F0F (\u5931\u8D25\u4E0D\u629B\u51FA)\uFF0C\u4FDD\u8BC1\u4E3B\u6D41\u7A0B\u4E0D\u88AB\u901A\u77E5\u5E72\u6270\n */\n\nimport {\n configPath,\n modulePath,\n readJson,\n type Catalog,\n type ModuleLock,\n type QQBridgeConfig,\n} from \"@sfmc-bds/sdk/node/config\";\nimport http from \"node:http\";\nimport { log } from \"./log.js\";\nimport { ROOT_DIR } from \"./paths.js\";\n\ntype QqConfig = Pick<QQBridgeConfig, \"llbot_http\" | \"qq_group_id\">;\n\nlet cachedCfg: QqConfig | null = null;\nfunction getConfig(): QqConfig {\n if (cachedCfg) return cachedCfg;\n cachedCfg = (readJson<QQBridgeConfig>(configPath(ROOT_DIR, \"qq_config.json\")) ?? {}) as QqConfig;\n return cachedCfg;\n}\n\n/** \u68C0\u67E5 qq-bridge \u6A21\u5757\u662F\u5426\u542F\u7528 */\nexport function isQqBridgeEnabled(): boolean {\n const catalog = readJson<Catalog>(modulePath(ROOT_DIR, \"catalog.json\"));\n const lock = readJson<ModuleLock>(modulePath(ROOT_DIR, \"module-lock.json\"));\n if (!catalog || !lock) return true; // \u6A21\u5757\u76EE\u5F55\u7F3A\u5931\u5219\u4FDD\u5B88\u89C6\u4E3A\u53EF\u7528\n const mod = catalog.modules?.find((m) => m.id === \"qq-bridge\" || m.configKey === \"qq_bridge\") as\n { id?: string } | undefined;\n return mod ? lock.modules?.[mod.id ?? \"\"]?.enabled === true : false;\n}\n\nfunction sendToLLBot(payload: unknown, timeoutMs = 5_000): Promise<void> {\n const cfg = getConfig();\n const url = new URL(cfg.llbot_http || \"http://127.0.0.1:3000\");\n const data = JSON.stringify(payload);\n return new Promise((resolve, reject) => {\n const req = http.request(\n {\n hostname: url.hostname,\n port: url.port || 3000,\n path: \"/send_group_msg\",\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(data),\n },\n timeout: timeoutMs,\n },\n (res) => {\n let body = \"\";\n res.on(\"data\", (c) => (body += c));\n res.on(\"end\", () => {\n if (res.statusCode === 200) resolve();\n else reject(new Error(`LLBot HTTP ${res.statusCode}: ${body.slice(0, 200)}`));\n });\n }\n );\n req.on(\"timeout\", () => {\n req.destroy(new Error(`LLBot \u8D85\u65F6 ${timeoutMs}ms`));\n });\n req.on(\"error\", reject);\n req.write(data);\n req.end();\n });\n}\n\nasync function safeSend(label: string, fn: () => Promise<void>): Promise<void> {\n try {\n await fn();\n } catch (e) {\n log.warn(`[QQ] ${label} \u5931\u8D25: ${(e as Error).message}`);\n }\n}\n\nexport async function sendText(text: string): Promise<void> {\n const cfg = getConfig();\n if (!isQqBridgeEnabled() || !cfg.qq_group_id) {\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931\");\n return;\n }\n await safeSend(\"sendText\", () =>\n sendToLLBot({\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\n message: [{ type: \"text\", data: { text } }],\n })\n );\n}\n\nexport async function sendMixed(segments: unknown[]): Promise<void> {\n const cfg = getConfig();\n if (!isQqBridgeEnabled() || !cfg.qq_group_id) {\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931\");\n return;\n }\n await safeSend(\"sendMixed\", () =>\n sendToLLBot({\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\n message: segments,\n })\n );\n}\n\nexport async function sendWithImage(text: string, base64Img: string): Promise<void> {\n const segments: unknown[] = [];\n if (text) segments.push({ type: \"text\", data: { text } });\n if (base64Img) segments.push({ type: \"image\", data: { file: `base64://${base64Img}` } });\n if (segments.length === 0) return;\n await sendMixed(segments);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/recovery.js
CHANGED
|
@@ -1,45 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
* recovery.ts — 手动触发回滚
|
|
3
|
-
*
|
|
4
|
-
* 用法: node recovery.js
|
|
5
|
-
* 读取 ROLLBACK_MARKER,然后从备份恢复 preserve 项。
|
|
6
|
-
*/
|
|
7
|
-
import { readRollbackMarker, rollbackFromBackup } from "./rollback.js";
|
|
1
|
+
import { isMainModule } from "./is-main.js";
|
|
8
2
|
import { log, closeLog } from "./log.js";
|
|
9
3
|
import { clearPid } from "./paths.js";
|
|
4
|
+
import { readRollbackMarker, rollbackFromBackup } from "./rollback.js";
|
|
10
5
|
async function run() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
log.info("回滚完成,请手动检查并启动 BDS。");
|
|
6
|
+
const m = readRollbackMarker();
|
|
7
|
+
if (!m) {
|
|
8
|
+
log.warn("\u672A\u53D1\u73B0\u56DE\u6EDA\u6807\u8BB0\uFF0C\u65E0\u9700\u6062\u590D\u3002");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
log.info(`\u53D1\u73B0\u56DE\u6EDA\u6807\u8BB0: \u65F6\u95F4 ${new Date(m.timestamp).toISOString()}`);
|
|
12
|
+
log.info(`\u76EE\u6807: \u4ECE ${m.backup_dir} \u6062\u590D ${m.preserve.length} \u9879\u5230 ${m.bds_path}`);
|
|
13
|
+
const result = rollbackFromBackup(m);
|
|
14
|
+
if (!result.ok) {
|
|
15
|
+
log.error(`\u56DE\u6EDA\u5931\u8D25: ${result.reason}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
clearPid();
|
|
19
|
+
log.info("\u56DE\u6EDA\u5B8C\u6210\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u5E76\u542F\u52A8 BDS\u3002");
|
|
26
20
|
}
|
|
27
21
|
function isMain() {
|
|
28
|
-
|
|
29
|
-
return true;
|
|
30
|
-
const entry = process.argv[1] ?? "";
|
|
31
|
-
return entry.endsWith("recovery.js") || entry.endsWith("recovery.ts");
|
|
22
|
+
return isMainModule(import.meta.url);
|
|
32
23
|
}
|
|
33
24
|
if (isMain()) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
process.exit(1);
|
|
43
|
-
});
|
|
25
|
+
run().then(() => {
|
|
26
|
+
closeLog();
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}).catch((e) => {
|
|
29
|
+
log.error(`\u672A\u6355\u83B7\u9519\u8BEF: ${e.message}`);
|
|
30
|
+
closeLog();
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
44
33
|
}
|
|
45
|
-
//# sourceMappingURL=recovery.js.map
|
|
34
|
+
//# sourceMappingURL=recovery.js.map
|
package/dist/recovery.js.map
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/recovery.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * recovery.ts \u2014 \u624B\u52A8\u89E6\u53D1\u56DE\u6EDA\n *\n * \u7528\u6CD5: node recovery.js\n * \u8BFB\u53D6 ROLLBACK_MARKER\uFF0C\u7136\u540E\u4ECE\u5907\u4EFD\u6062\u590D preserve \u9879\u3002\n */\n\nimport { isMainModule } from \"./is-main.js\";\nimport { log, closeLog } from \"./log.js\";\nimport { clearPid } from \"./paths.js\";\nimport { readRollbackMarker, rollbackFromBackup } from \"./rollback.js\";\n\nasync function run(): Promise<void> {\n const m = readRollbackMarker();\n if (!m) {\n log.warn(\"\u672A\u53D1\u73B0\u56DE\u6EDA\u6807\u8BB0\uFF0C\u65E0\u9700\u6062\u590D\u3002\");\n return;\n }\n log.info(`\u53D1\u73B0\u56DE\u6EDA\u6807\u8BB0: \u65F6\u95F4 ${new Date(m.timestamp).toISOString()}`);\n log.info(`\u76EE\u6807: \u4ECE ${m.backup_dir} \u6062\u590D ${m.preserve.length} \u9879\u5230 ${m.bds_path}`);\n\n const result = rollbackFromBackup(m);\n if (!result.ok) {\n log.error(`\u56DE\u6EDA\u5931\u8D25: ${result.reason}`);\n process.exit(1);\n }\n // \u6E05\u7406 pid \u6587\u4EF6\n clearPid();\n log.info(\"\u56DE\u6EDA\u5B8C\u6210\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u5E76\u542F\u52A8 BDS\u3002\");\n}\n\nfunction isMain(): boolean {\n return isMainModule(import.meta.url);\n}\n\nif (isMain()) {\n run()\n .then(() => {\n closeLog();\n process.exit(0);\n })\n .catch((e) => {\n log.error(`\u672A\u6355\u83B7\u9519\u8BEF: ${(e as Error).message}`);\n closeLog();\n process.exit(1);\n });\n}\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,oBAAoB;AAC7B,SAAS,KAAK,gBAAgB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,oBAAoB,0BAA0B;AAEvD,eAAe,MAAqB;AAClC,QAAM,IAAI,mBAAmB;AAC7B,MAAI,CAAC,GAAG;AACN,QAAI,KAAK,gFAAe;AACxB;AAAA,EACF;AACA,MAAI,KAAK,sDAAc,IAAI,KAAK,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE;AAC5D,MAAI,KAAK,wBAAS,EAAE,UAAU,iBAAO,EAAE,SAAS,MAAM,iBAAO,EAAE,QAAQ,EAAE;AAEzE,QAAM,SAAS,mBAAmB,CAAC;AACnC,MAAI,CAAC,OAAO,IAAI;AACd,QAAI,MAAM,6BAAS,OAAO,MAAM,EAAE;AAClC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,WAAS;AACT,MAAI,KAAK,0FAAoB;AAC/B;AAEA,SAAS,SAAkB;AACzB,SAAO,aAAa,YAAY,GAAG;AACrC;AAEA,IAAI,OAAO,GAAG;AACZ,MAAI,EACD,KAAK,MAAM;AACV,aAAS;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,QAAI,MAAM,mCAAW,EAAY,OAAO,EAAE;AAC1C,aAAS;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACL;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/rollback.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 主流程任意阶段失败时,尝试反向恢复 preserves 到 BDS_PATH,
|
|
6
6
|
* 然后从备份重新放回 preserve 内容,恢复旧配置。
|
|
7
7
|
*
|
|
8
|
-
* 持久化:
|
|
8
|
+
* 持久化: <SFMC_ROOT>/.sfmc/last_update_rollback.json 用于跨进程记录。
|
|
9
9
|
*/
|
|
10
10
|
import { getDirSize } from "./fsx.js";
|
|
11
11
|
export interface RollbackMarker {
|
package/dist/rollback.js
CHANGED
|
@@ -1,87 +1,68 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* rollback.ts — 回滚支持
|
|
3
|
-
*
|
|
4
|
-
* 在更新开始前记录: 当前 BDS 目录 + 备份目录。
|
|
5
|
-
* 主流程任意阶段失败时,尝试反向恢复 preserves 到 BDS_PATH,
|
|
6
|
-
* 然后从备份重新放回 preserve 内容,恢复旧配置。
|
|
7
|
-
*
|
|
8
|
-
* 持久化: ROLLBACK_MARKER (BDSTools/.last_update_rollback.json) 用于跨进程记录。
|
|
9
|
-
*/
|
|
10
1
|
import { readJson, writeJson } from "@sfmc-bds/sdk/node/config";
|
|
11
2
|
import fs from "node:fs";
|
|
12
3
|
import path from "node:path";
|
|
13
4
|
import { ROLLBACK_MARKER } from "./paths.js";
|
|
14
5
|
import { copyDirSync, getDirSize, emptyDirSync } from "./fsx.js";
|
|
15
6
|
import { log } from "./log.js";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
log.warn(`[回滚] 无法写入标记: ${e.message}`);
|
|
24
|
-
}
|
|
7
|
+
function writeRollbackMarker(marker) {
|
|
8
|
+
try {
|
|
9
|
+
writeJson(ROLLBACK_MARKER, marker);
|
|
10
|
+
log.info(`[\u56DE\u6EDA] \u5DF2\u8BB0\u5F55\u56DE\u6EDA\u6807\u8BB0 -> ${ROLLBACK_MARKER}`);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
log.warn(`[\u56DE\u6EDA] \u65E0\u6CD5\u5199\u5165\u6807\u8BB0: ${e.message}`);
|
|
13
|
+
}
|
|
25
14
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
catch {
|
|
32
|
-
/* ignore */
|
|
33
|
-
}
|
|
15
|
+
function clearRollbackMarker() {
|
|
16
|
+
try {
|
|
17
|
+
if (fs.existsSync(ROLLBACK_MARKER)) fs.unlinkSync(ROLLBACK_MARKER);
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
34
20
|
}
|
|
35
|
-
|
|
36
|
-
|
|
21
|
+
function readRollbackMarker() {
|
|
22
|
+
return readJson(ROLLBACK_MARKER) ?? null;
|
|
37
23
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
24
|
+
function rollbackFromBackup(marker) {
|
|
25
|
+
const { bds_path, backup_dir, preserve } = marker;
|
|
26
|
+
if (!fs.existsSync(backup_dir)) {
|
|
27
|
+
return { ok: false, reason: `\u5907\u4EFD\u76EE\u5F55\u4E0D\u5B58\u5728: ${backup_dir}` };
|
|
28
|
+
}
|
|
29
|
+
log.warn(`[\u56DE\u6EDA] \u5F00\u59CB\u6062\u590D ${preserve.length} \u9879\u5230 ${bds_path}`);
|
|
30
|
+
for (const item of preserve) {
|
|
31
|
+
const src = path.join(backup_dir, item);
|
|
32
|
+
const dest = path.join(bds_path, item);
|
|
33
|
+
if (!fs.existsSync(src)) {
|
|
34
|
+
log.warn(`[\u56DE\u6EDA] \u8DF3\u8FC7 (\u5907\u4EFD\u4E2D\u4E0D\u5B58\u5728): ${item}`);
|
|
35
|
+
continue;
|
|
46
36
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
60
|
-
if (fs.statSync(src).isDirectory()) {
|
|
61
|
-
// 先删除目标,再复制目录
|
|
62
|
-
if (fs.existsSync(dest))
|
|
63
|
-
emptyDirSync(dest);
|
|
64
|
-
copyDirSync(src, dest);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
fs.copyFileSync(src, dest);
|
|
68
|
-
}
|
|
69
|
-
log.info(`[回滚] 已恢复: ${item}`);
|
|
70
|
-
}
|
|
71
|
-
catch (e) {
|
|
72
|
-
log.warn(`[回滚] 恢复失败 ${item}: ${e.message}`);
|
|
73
|
-
}
|
|
37
|
+
try {
|
|
38
|
+
const destDir = path.dirname(dest);
|
|
39
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
40
|
+
if (fs.statSync(src).isDirectory()) {
|
|
41
|
+
if (fs.existsSync(dest)) emptyDirSync(dest);
|
|
42
|
+
copyDirSync(src, dest);
|
|
43
|
+
} else {
|
|
44
|
+
fs.copyFileSync(src, dest);
|
|
45
|
+
}
|
|
46
|
+
log.info(`[\u56DE\u6EDA] \u5DF2\u6062\u590D: ${item}`);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
log.warn(`[\u56DE\u6EDA] \u6062\u590D\u5931\u8D25 ${item}: ${e.message}`);
|
|
74
49
|
}
|
|
75
|
-
|
|
50
|
+
}
|
|
51
|
+
return { ok: true };
|
|
76
52
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
return { ok: missing.length === 0, missing };
|
|
53
|
+
function verifyBdsInstall(bds_path, expected) {
|
|
54
|
+
const missing = [];
|
|
55
|
+
for (const f of expected) {
|
|
56
|
+
if (!fs.existsSync(path.join(bds_path, f))) missing.push(f);
|
|
57
|
+
}
|
|
58
|
+
return { ok: missing.length === 0, missing };
|
|
85
59
|
}
|
|
86
|
-
export {
|
|
87
|
-
|
|
60
|
+
export {
|
|
61
|
+
clearRollbackMarker,
|
|
62
|
+
getDirSize,
|
|
63
|
+
readRollbackMarker,
|
|
64
|
+
rollbackFromBackup,
|
|
65
|
+
verifyBdsInstall,
|
|
66
|
+
writeRollbackMarker
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=rollback.js.map
|