@sfmc-bds/cli 0.5.2 → 0.6.1
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/management/deployment.js +1 -1
- package/dist/management/deployment.js.map +2 -2
- package/dist/management/maintenance.d.ts +6 -1
- package/dist/management/maintenance.d.ts.map +1 -1
- package/dist/management/maintenance.js +30 -40
- package/dist/management/maintenance.js.map +2 -2
- package/dist/management/platform-worker.js +5 -6
- package/dist/management/platform-worker.js.map +2 -2
- package/dist/management/server.d.ts.map +1 -1
- package/dist/management/server.js +50 -27
- package/dist/management/server.js.map +2 -2
- package/dist/management/tasks.js +1 -1
- package/dist/management/tasks.js.map +2 -2
- package/dist/services.d.ts +2 -1
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +16 -3
- package/dist/services.js.map +2 -2
- package/package.json +7 -7
|
@@ -12,7 +12,7 @@ async function deploymentPreflight(ports) {
|
|
|
12
12
|
if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error("\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72");
|
|
13
13
|
for (const file of ["BDS/bedrock_server.exe", "BDS/bedrock_server", "data/sfmc_data.db"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error("\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E");
|
|
14
14
|
const space = fs.statfsSync(ROOT);
|
|
15
|
-
if (space.bavail * space.bsize < 1024 ** 3) throw new Error("\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\
|
|
15
|
+
if (space.bavail * space.bsize < 1024 ** 3) throw new Error("\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u548C BDS");
|
|
16
16
|
await new Promise((resolve, reject) => {
|
|
17
17
|
const server = net.createServer();
|
|
18
18
|
server.once("error", (error) => reject(new Error(`DB \u7AEF\u53E3 ${ports.db} \u4E0D\u53EF\u7528: ${error.message}`)));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/deployment.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport net from \"node:net\";\nimport dgram from \"node:dgram\";\nimport { ROOT } from \"../runtime.js\";\nexport function deploymentPort(value: unknown, fallback: number) {\n const port = value === undefined ? fallback : Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(\"\u7AEF\u53E3\u5FC5\u987B\u4E3A 1 \u5230 65535 \u7684\u6574\u6570\");\n return port;\n}\nexport async function deploymentPreflight(ports: { db: number; bds: number; bds6: number }) {\n if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error(\"\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72\");\n for (const file of [\"BDS/bedrock_server.exe\", \"BDS/bedrock_server\", \"data/sfmc_data.db\"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error(\"\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E\");\n const space = fs.statfsSync(ROOT);\n if (space.bavail * space.bsize < 1024 ** 3) throw new Error(\"\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,SAAS,YAAY;AACd,SAAS,eAAe,OAAgB,UAAkB;AAC/D,QAAM,OAAO,UAAU,SAAY,WAAW,OAAO,KAAK;AAC1D,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,MAAO,OAAM,IAAI,MAAM,kEAAqB;AAC9F,SAAO;AACT;AACA,eAAsB,oBAAoB,OAAkD;AAC1F,MAAI,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,EAAE,KAAM,OAAM,IAAI,MAAM,0EAAc;AAChF,aAAW,QAAQ,CAAC,0BAA0B,sBAAsB,mBAAmB,EAAG,KAAI,GAAG,WAAW,KAAK,KAAK,MAAM,IAAI,CAAC,EAAG,OAAM,IAAI,MAAM,+HAA2B;AAC/K,QAAM,QAAQ,GAAG,WAAW,IAAI;AAChC,MAAI,MAAM,SAAS,MAAM,QAAQ,QAAQ,EAAG,OAAM,IAAI,MAAM,
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport net from \"node:net\";\nimport dgram from \"node:dgram\";\nimport { ROOT } from \"../runtime.js\";\nexport function deploymentPort(value: unknown, fallback: number) {\n const port = value === undefined ? fallback : Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(\"\u7AEF\u53E3\u5FC5\u987B\u4E3A 1 \u5230 65535 \u7684\u6574\u6570\");\n return port;\n}\nexport async function deploymentPreflight(ports: { db: number; bds: number; bds6: number }) {\n if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error(\"\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72\");\n for (const file of [\"BDS/bedrock_server.exe\", \"BDS/bedrock_server\", \"data/sfmc_data.db\"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error(\"\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E\");\n const space = fs.statfsSync(ROOT);\n if (space.bavail * space.bsize < 1024 ** 3) throw new Error(\"\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u548C BDS\");\n await new Promise<void>((resolve, reject) => {\n const server = net.createServer(); server.once(\"error\", error => reject(new Error(`DB \u7AEF\u53E3 ${ports.db} \u4E0D\u53EF\u7528: ${error.message}`)));\n server.listen(ports.db, \"0.0.0.0\", () => server.close(error => error ? reject(error) : resolve()));\n });\n for (const [port, type, address] of [[ports.bds, \"udp4\", \"0.0.0.0\"], [ports.bds6, \"udp6\", \"::\"]] as const) {\n await new Promise<void>((resolve, reject) => {\n const socket = dgram.createSocket(type);\n socket.once(\"error\", error => { socket.close(); reject(new Error(`BDS \u7AEF\u53E3 ${port} \u4E0D\u53EF\u7528: ${error.message}`)); });\n socket.bind(port, address, () => socket.close(() => resolve()));\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,SAAS,YAAY;AACd,SAAS,eAAe,OAAgB,UAAkB;AAC/D,QAAM,OAAO,UAAU,SAAY,WAAW,OAAO,KAAK;AAC1D,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,MAAO,OAAM,IAAI,MAAM,kEAAqB;AAC9F,SAAO;AACT;AACA,eAAsB,oBAAoB,OAAkD;AAC1F,MAAI,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,EAAE,KAAM,OAAM,IAAI,MAAM,0EAAc;AAChF,aAAW,QAAQ,CAAC,0BAA0B,sBAAsB,mBAAmB,EAAG,KAAI,GAAG,WAAW,KAAK,KAAK,MAAM,IAAI,CAAC,EAAG,OAAM,IAAI,MAAM,+HAA2B;AAC/K,QAAM,QAAQ,GAAG,WAAW,IAAI;AAChC,MAAI,MAAM,SAAS,MAAM,QAAQ,QAAQ,EAAG,OAAM,IAAI,MAAM,sGAA2B;AACvF,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,SAAS,IAAI,aAAa;AAAG,WAAO,KAAK,SAAS,WAAS,OAAO,IAAI,MAAM,mBAAS,MAAM,EAAE,wBAAS,MAAM,OAAO,EAAE,CAAC,CAAC;AAC7H,WAAO,OAAO,MAAM,IAAI,WAAW,MAAM,OAAO,MAAM,WAAS,QAAQ,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC;AAAA,EACnG,CAAC;AACD,aAAW,CAAC,MAAM,MAAM,OAAO,KAAK,CAAC,CAAC,MAAM,KAAK,QAAQ,SAAS,GAAG,CAAC,MAAM,MAAM,QAAQ,IAAI,CAAC,GAAY;AACzG,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,SAAS,MAAM,aAAa,IAAI;AACtC,aAAO,KAAK,SAAS,WAAS;AAAE,eAAO,MAAM;AAAG,eAAO,IAAI,MAAM,oBAAU,IAAI,wBAAS,MAAM,OAAO,EAAE,CAAC;AAAA,MAAG,CAAC;AAC5G,aAAO,KAAK,MAAM,SAAS,MAAM,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,IAChE,CAAC;AAAA,EACH;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,10 +7,15 @@ export declare function stopForMaintenance(names: ServiceName[], phase: TaskCont
|
|
|
7
7
|
export declare function restoreRunning(names: ServiceName[], phase: TaskContext["phase"]): Promise<void>;
|
|
8
8
|
export declare function verifyRunning(names: ServiceName[]): Promise<void>;
|
|
9
9
|
export declare function createSnapshot(id: string, phase: TaskContext["phase"]): Promise<string>;
|
|
10
|
+
/** 备份只读取实例文件,即使复制失败也恢复原先运行的服务。 */
|
|
11
|
+
export declare function createBackup(context: TaskContext): Promise<{
|
|
12
|
+
backupId: string;
|
|
13
|
+
}>;
|
|
10
14
|
export declare function restoreSnapshot(id: string, phase: TaskContext["phase"]): Promise<{
|
|
11
15
|
restored: string;
|
|
12
16
|
}>;
|
|
13
|
-
|
|
17
|
+
/** 普通维护只负责停服与恢复;全量快照仅由手动备份任务创建。 */
|
|
18
|
+
export declare function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>): Promise<T>;
|
|
14
19
|
export declare function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string): Promise<{
|
|
15
20
|
detached: boolean;
|
|
16
21
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/management/maintenance.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAa3D,wBAAgB,eAAe,IAAI,MAAM,CASxC;AACD,wBAAsB,cAAc,CAAC,mBAAmB,UAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAaxF;AACD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAUlH;AACD,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,iBAOrF;AACD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,iBAWvD;AAMD,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/management/maintenance.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAa3D,wBAAgB,eAAe,IAAI,MAAM,CASxC;AACD,wBAAsB,cAAc,CAAC,mBAAmB,UAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAaxF;AACD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAUlH;AACD,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,iBAOrF;AACD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,iBAWvD;AAMD,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,mBAsB3E;AACD,kCAAkC;AAClC,wBAAsB,YAAY,CAAC,OAAO,EAAE,WAAW;;GAatD;AACD,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC;;GAiB5E;AACD,mCAAmC;AACnC,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAa7H;AACD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM;;GAmBhG;AACD,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,iBASxD"}
|
|
@@ -43,7 +43,7 @@ async function attachmentPlan(includeReleaseNotes = true) {
|
|
|
43
43
|
targetVersion = metadata.version;
|
|
44
44
|
}
|
|
45
45
|
const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : void 0]);
|
|
46
|
-
return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter((row) => row.running && row.ownership === "external").map((row) => row.name), steps: development ? ["\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72"] : ["\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408", "\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA", "\
|
|
46
|
+
return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter((row) => row.running && row.ownership === "external").map((row) => row.name), steps: development ? ["\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72"] : ["\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408", "\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA", "\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1", "\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1"], ...releaseNotes ? { releaseNotes } : {} };
|
|
47
47
|
}
|
|
48
48
|
async function stopForMaintenance(names, phase) {
|
|
49
49
|
const before = await queryServicesRuntimeLocal();
|
|
@@ -82,20 +82,42 @@ function snapshotTargets() {
|
|
|
82
82
|
return { configs: path.join(ROOT, "configs"), modules: path.join(ROOT, "modules"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? "data/sfmc_data.db")), bds };
|
|
83
83
|
}
|
|
84
84
|
async function createSnapshot(id, phase) {
|
|
85
|
+
if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error("\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7");
|
|
85
86
|
phase("backup", "\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD");
|
|
86
87
|
const destination = path.join(ROOT, ".sfmc", "backups", id);
|
|
87
|
-
fs.mkdirSync(destination, { recursive: true });
|
|
88
88
|
const targets = snapshotTargets();
|
|
89
89
|
for (const target of Object.values(targets)) {
|
|
90
90
|
const absolute = path.resolve(target);
|
|
91
91
|
if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error("\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62");
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
94
|
+
fs.mkdirSync(destination);
|
|
95
|
+
try {
|
|
96
|
+
for (const [key, target] of Object.entries(targets)) {
|
|
97
|
+
if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });
|
|
98
|
+
}
|
|
99
|
+
atomicJson(path.join(destination, "manifest.json"), { createdAt: (/* @__PURE__ */ new Date()).toISOString(), targets, complete: true });
|
|
100
|
+
} catch (error) {
|
|
101
|
+
await fs.promises.rm(destination, { recursive: true, force: true });
|
|
102
|
+
throw error;
|
|
95
103
|
}
|
|
96
|
-
atomicJson(path.join(destination, "manifest.json"), { createdAt: (/* @__PURE__ */ new Date()).toISOString(), targets, complete: true });
|
|
97
104
|
return destination;
|
|
98
105
|
}
|
|
106
|
+
async function createBackup(context) {
|
|
107
|
+
const running = await stopForMaintenance([...START_ORDER], context.phase);
|
|
108
|
+
let completed = false;
|
|
109
|
+
try {
|
|
110
|
+
await createSnapshot(context.id, context.phase);
|
|
111
|
+
completed = true;
|
|
112
|
+
} finally {
|
|
113
|
+
try {
|
|
114
|
+
await restoreRunning(running, context.phase);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw Object.assign(new Error(`\u5907\u4EFD${completed ? "\u5DF2\u5B8C\u6210" : "\u672A\u5B8C\u6210"}\uFF0C\u6062\u590D\u670D\u52A1\u8FD0\u884C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`), { code: "io", details: completed ? { backupId: context.id } : void 0 });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { backupId: context.id };
|
|
120
|
+
}
|
|
99
121
|
async function restoreSnapshot(id, phase) {
|
|
100
122
|
if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error("\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7");
|
|
101
123
|
const source = path.join(ROOT, ".sfmc", "backups", id);
|
|
@@ -114,31 +136,9 @@ async function restoreSnapshot(id, phase) {
|
|
|
114
136
|
}
|
|
115
137
|
return { restored: id };
|
|
116
138
|
}
|
|
117
|
-
async function
|
|
118
|
-
const source = path.join(ROOT, ".sfmc", "backups", id);
|
|
119
|
-
const manifest = readJson(path.join(source, "manifest.json"), { complete: false, targets: {} });
|
|
120
|
-
const targets = snapshotTargets();
|
|
121
|
-
if (!manifest.complete || path.resolve(manifest.targets[kind] ?? "") !== path.resolve(targets[kind])) throw new Error("\u7A0B\u5E8F\u5907\u4EFD\u4E0D\u5B8C\u6574\u6216\u76EE\u6807\u4E0D\u5339\u914D");
|
|
122
|
-
const saved = path.join(source, kind);
|
|
123
|
-
if (kind === "modules") {
|
|
124
|
-
if (!fs.existsSync(saved)) return;
|
|
125
|
-
await fs.promises.rm(targets.modules, { recursive: true, force: true });
|
|
126
|
-
await fs.promises.cp(saved, targets.modules, { recursive: true, dereference: false });
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const cfg = readJson(path.join(ROOT, "configs", "bds_updater.json"), {});
|
|
130
|
-
const preserved = /* @__PURE__ */ new Set(["worlds", "config", "server.properties", "allowlist.json", "whitelist.json", "permissions.json", ...(cfg.preserve ?? []).map((name) => name.split(/[\\/]/)[0])]);
|
|
131
|
-
if (!fs.existsSync(saved)) throw new Error("\u7F3A\u5C11\u539F BDS \u7A0B\u5E8F\u5907\u4EFD");
|
|
132
|
-
const target = path.resolve(targets.bds);
|
|
133
|
-
if (target === path.parse(target).root || target === path.resolve(ROOT)) throw new Error("\u4E0D\u5B89\u5168\u7684\u7A0B\u5E8F\u56DE\u9000\u76EE\u6807");
|
|
134
|
-
fs.mkdirSync(target, { recursive: true });
|
|
135
|
-
for (const entry of fs.readdirSync(target)) if (!preserved.has(entry)) await fs.promises.rm(path.join(target, entry), { recursive: true, force: true });
|
|
136
|
-
for (const entry of fs.readdirSync(saved)) if (!preserved.has(entry)) await fs.promises.cp(path.join(saved, entry), path.join(target, entry), { recursive: true, dereference: false });
|
|
137
|
-
}
|
|
138
|
-
async function withStoppedServices(context, names, action, backup = true, rollbackPrograms) {
|
|
139
|
+
async function withStoppedServices(context, names, action) {
|
|
139
140
|
const running = await stopForMaintenance(names, context.phase);
|
|
140
141
|
try {
|
|
141
|
-
if (backup) await createSnapshot(context.id, context.phase);
|
|
142
142
|
context.phase("execute");
|
|
143
143
|
const result = await action();
|
|
144
144
|
context.phase("verify");
|
|
@@ -147,18 +147,7 @@ async function withStoppedServices(context, names, action, backup = true, rollba
|
|
|
147
147
|
} catch (error) {
|
|
148
148
|
for (const name of [...START_ORDER].reverse().filter((name2) => running.includes(name2))) await services[name].stop(false, false).catch(() => {
|
|
149
149
|
});
|
|
150
|
-
|
|
151
|
-
let rollbackError;
|
|
152
|
-
if (rollbackPrograms) {
|
|
153
|
-
context.phase("rollback-program", "\u56DE\u9000\u7A0B\u5E8F\u7248\u672C\uFF0C\u4FDD\u7559\u5F53\u524D\u73A9\u5BB6\u6570\u636E");
|
|
154
|
-
try {
|
|
155
|
-
await restoreProgramSnapshot(context.id, rollbackPrograms);
|
|
156
|
-
programRolledBack = true;
|
|
157
|
-
} catch (failure) {
|
|
158
|
-
rollbackError = String(failure);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62\uFF1B\u5907\u4EFD\u7F16\u53F7 ${context.id}: ${error instanceof Error ? error.message : String(error)}`), { code: "io", details: { backupId: context.id, programRolledBack, dataRestoreRequired: Boolean(rollbackPrograms), ...rollbackError ? { rollbackError } : {}, ...error.details ?? {} } });
|
|
150
|
+
throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62: ${error instanceof Error ? error.message : String(error)}`), { code: "io", details: error.details });
|
|
162
151
|
}
|
|
163
152
|
}
|
|
164
153
|
async function launchPlatformUpdate(store, context, target) {
|
|
@@ -197,6 +186,7 @@ async function runPnpm(args, cwd) {
|
|
|
197
186
|
}
|
|
198
187
|
export {
|
|
199
188
|
attachmentPlan,
|
|
189
|
+
createBackup,
|
|
200
190
|
createSnapshot,
|
|
201
191
|
launchPlatformUpdate,
|
|
202
192
|
platformVersion,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/maintenance.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { ROOT, isMonorepoLayout, isSourceCheckout } from \"../runtime.js\";\nimport { queryServicesRuntimeLocal, services, START_ORDER, type ServiceName } from \"../services.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { atomicJson, type TaskContext, type TaskStore } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport type { AttachmentPlan } from \"@sfmc-bds/management\";\nimport { isDaemonServer } from \"../daemon/role.js\";\nimport { callDaemon } from \"../daemon/client.js\";\nimport { platformReleaseNotes } from \"@sfmc-bds/management/node\";\n\nasync function runtimeRows() {\n if (isDaemonServer()) return queryServicesRuntimeLocal();\n const result = await callDaemon(\"status\");\n if (result.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u8BFB\u53D6\u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u72B6\u6001\");\n return result.rows;\n}\n\nconst require = createRequire(import.meta.url);\nexport function platformVersion(): string {\n if (isMonorepoLayout(ROOT)) return readJson<{ version: string }>(path.join(ROOT, \"packages\", \"meta\", \"package.json\"), { version: \"unknown\" }).version;\n if (process.env.SFMC_PLATFORM_VERSION) return process.env.SFMC_PLATFORM_VERSION;\n const active = readJson<{ version?: string }>(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), {});\n if (active.version) return active.version;\n const deployed = readJson<{ version?: string }>(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"package.json\"), {});\n if (deployed.version) return deployed.version;\n try { return (require(\"@sfmc-bds/sfmc/package.json\") as { version: string }).version; }\n catch { return (readJson<{ version: string }>(path.join(fileURLToPath(new URL(\"../../..\", import.meta.url)), \"package.json\"), { version: \"unknown\" })).version; }\n}\nexport async function attachmentPlan(includeReleaseNotes = true): Promise<AttachmentPlan> {\n const development = isMonorepoLayout(ROOT) || isSourceCheckout();\n const currentVersion = platformVersion();\n let targetVersion = currentVersion;\n if (!development) {\n const response = await fetch(\"https://registry.npmjs.org/@sfmc-bds%2fsfmc/latest\", { signal: AbortSignal.timeout(15_000) });\n if (!response.ok) throw new Error(`\u5E73\u53F0\u7248\u672C\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n const metadata = await response.json() as { version?: string };\n if (!metadata.version || !/^\\d+\\.\\d+\\.\\d+$/.test(metadata.version)) throw new Error(\"latest \u6CA1\u6709\u6307\u5411\u6709\u6548\u7684\u7A33\u5B9A\u5E73\u53F0\u7248\u672C\");\n targetVersion = metadata.version;\n }\n const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : undefined]);\n return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter(row => row.running && row.ownership === \"external\").map(row => row.name), steps: development ? [\"\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72\"] : [\"\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408\", \"\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\", \"\u5907\u4EFD\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u4E0E\u4E16\u754C\", \"\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1\", \"\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\"], ...(releaseNotes ? { releaseNotes } : {}) };\n}\nexport async function stopForMaintenance(names: ServiceName[], phase: TaskContext[\"phase\"]): Promise<ServiceName[]> {\n const before = await queryServicesRuntimeLocal();\n const affected = before.filter(row => names.includes(row.name) && row.running);\n if (affected.some(row => row.ownership === \"external\")) throw Object.assign(new Error(\"\u5B58\u5728\u65E0\u6CD5\u901A\u8FC7\u5F53\u524D\u5B88\u62A4\u8FDB\u7A0B\u4F18\u96C5\u505C\u6B62\u7684\u5916\u90E8\u670D\u52A1\uFF1B\u8BF7\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u505C\u6B62\u540E\u91CD\u65B0\u63A5\u5165\"), { code: \"conflict\" });\n phase(\"stop-services\", \"\u4F18\u96C5\u505C\u6B62\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\");\n const running = affected.map(row => row.name);\n for (const name of [...START_ORDER].reverse().filter(name => names.includes(name))) await services[name].stop(false, false);\n const after = await queryServicesRuntimeLocal();\n if (after.some(row => names.includes(row.name) && row.running)) throw new Error(\"\u670D\u52A1\u672A\u5B8C\u5168\u9000\u51FA\uFF0C\u5DF2\u4E2D\u6B62\u6587\u4EF6\u64CD\u4F5C\");\n return running;\n}\nexport async function restoreRunning(names: ServiceName[], phase: TaskContext[\"phase\"]) {\n phase(\"restore\", \"\u6062\u590D\u7EF4\u62A4\u524D\u8FD0\u884C\u7684\u670D\u52A1\");\n for (const name of START_ORDER.filter(name => names.includes(name))) {\n const result = await services[name].start();\n if (result.status === \"skipped\") throw new Error(`\u6062\u590D ${name} \u5931\u8D25: ${result.reason}`);\n }\n await verifyRunning(names);\n}\nexport async function verifyRunning(names: ServiceName[]) {\n if (!names.length) return;\n await new Promise(resolve => setTimeout(resolve, 2000));\n for (const name of names) {\n if (!services[name].running || !services[name].pid) throw new Error(`${name} \u542F\u52A8\u540E\u5DF2\u9000\u51FA`);\n }\n if (names.includes(\"db\")) {\n const cfg = readJson<{ db_port?: number; http_auth?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const response = await fetch(`http://127.0.0.1:${cfg.db_port ?? 3001}/api/sfmc/status`, { headers: cfg.http_auth ? { authorization: `Bearer ${cfg.http_auth}` } : {}, signal: AbortSignal.timeout(5000) });\n if (!response.ok) throw new Error(`DB \u5065\u5EB7\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n }\n}\nfunction snapshotTargets() {\n const db = readJson<{ dbDir?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const bds = resolveBdsContext().bdsRoot;\n return { configs: path.join(ROOT, \"configs\"), modules: path.join(ROOT, \"modules\"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? \"data/sfmc_data.db\")), bds };\n}\nexport async function createSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n phase(\"backup\", \"\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD\");\n const destination = path.join(ROOT, \".sfmc\", \"backups\", id);\n fs.mkdirSync(destination, { recursive: true });\n const targets = snapshotTargets();\n for (const target of Object.values(targets)) {\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62\");\n }\n for (const [key, target] of Object.entries(targets)) {\n if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });\n }\n atomicJson(path.join(destination, \"manifest.json\"), { createdAt: new Date().toISOString(), targets, complete: true });\n return destination;\n}\nexport async function restoreSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n if (!manifest.complete) throw new Error(\"\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u7981\u6B62\u6062\u590D\");\n const targets = snapshotTargets();\n phase(\"restore-data\", \"\u6309\u7528\u6237\u660E\u786E\u9009\u62E9\u6062\u590D\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u548C\u4E16\u754C\");\n for (const [key, target] of Object.entries(targets)) {\n if (path.resolve(manifest.targets[key] ?? \"\") !== path.resolve(target)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u4E0E\u5F53\u524D\u90E8\u7F72\u4E0D\u4E00\u81F4\");\n const saved = path.join(source, key);\n if (!fs.existsSync(saved)) continue;\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || fs.lstatSync(saved).isSymbolicLink()) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u6062\u590D\u76EE\u6807\");\n await fs.promises.rm(absolute, { recursive: true, force: true });\n await fs.promises.cp(saved, absolute, { recursive: true, dereference: false });\n }\n return { restored: id };\n}\n/** \u53EA\u56DE\u9000\u7A0B\u5E8F\u5185\u5BB9\uFF0C\u4E16\u754C\u3001\u73A9\u5BB6\u6743\u9650\u548C\u6570\u636E\u5E93\u59CB\u7EC8\u4FDD\u7559\u5728\u5F53\u524D\u72B6\u6001\u3002 */\nasync function restoreProgramSnapshot(id: string, kind: \"modules\" | \"bds\") {\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n const targets = snapshotTargets();\n if (!manifest.complete || path.resolve(manifest.targets[kind] ?? \"\") !== path.resolve(targets[kind])) throw new Error(\"\u7A0B\u5E8F\u5907\u4EFD\u4E0D\u5B8C\u6574\u6216\u76EE\u6807\u4E0D\u5339\u914D\");\n const saved = path.join(source, kind);\n if (kind === \"modules\") {\n if (!fs.existsSync(saved)) return;\n await fs.promises.rm(targets.modules, { recursive: true, force: true });\n await fs.promises.cp(saved, targets.modules, { recursive: true, dereference: false });\n return;\n }\n const cfg = readJson<{ preserve?: string[] }>(path.join(ROOT, \"configs\", \"bds_updater.json\"), {});\n const preserved = new Set([\"worlds\", \"config\", \"server.properties\", \"allowlist.json\", \"whitelist.json\", \"permissions.json\", ...(cfg.preserve ?? []).map(name => name.split(/[\\\\/]/)[0]!)]);\n if (!fs.existsSync(saved)) throw new Error(\"\u7F3A\u5C11\u539F BDS \u7A0B\u5E8F\u5907\u4EFD\");\n const target = path.resolve(targets.bds);\n if (target === path.parse(target).root || target === path.resolve(ROOT)) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u7A0B\u5E8F\u56DE\u9000\u76EE\u6807\");\n fs.mkdirSync(target, { recursive: true });\n for (const entry of fs.readdirSync(target)) if (!preserved.has(entry)) await fs.promises.rm(path.join(target, entry), { recursive: true, force: true });\n for (const entry of fs.readdirSync(saved)) if (!preserved.has(entry)) await fs.promises.cp(path.join(saved, entry), path.join(target, entry), { recursive: true, dereference: false });\n}\nexport async function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>, backup = true, rollbackPrograms?: \"modules\" | \"bds\"): Promise<T> {\n const running = await stopForMaintenance(names, context.phase);\n try {\n if (backup) await createSnapshot(context.id, context.phase);\n context.phase(\"execute\");\n const result = await action();\n context.phase(\"verify\");\n await restoreRunning(running, context.phase);\n return result;\n } catch (error) {\n // \u5931\u8D25\u540E\u4FDD\u6301\u505C\u670D\uFF0C\u4E0D\u731C\u6D4B\u672A\u77E5\u6570\u636E\u8FC1\u79FB\u662F\u5426\u53EF\u4EE5\u5B89\u5168\u6062\u590D\u8FD0\u884C\u3002\n for (const name of [...START_ORDER].reverse().filter(name => running.includes(name))) await services[name].stop(false, false).catch(() => {});\n let programRolledBack = false;\n let rollbackError: string | undefined;\n if (rollbackPrograms) {\n context.phase(\"rollback-program\", \"\u56DE\u9000\u7A0B\u5E8F\u7248\u672C\uFF0C\u4FDD\u7559\u5F53\u524D\u73A9\u5BB6\u6570\u636E\");\n try { await restoreProgramSnapshot(context.id, rollbackPrograms); programRolledBack = true; }\n catch (failure) { rollbackError = String(failure); }\n }\n throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62\uFF1B\u5907\u4EFD\u7F16\u53F7 ${context.id}: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: { backupId: context.id, programRolledBack, dataRestoreRequired: Boolean(rollbackPrograms), ...(rollbackError ? { rollbackError } : {}), ...((error as { details?: object }).details ?? {}) } });\n }\n}\nexport async function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string) {\n if (isMonorepoLayout(ROOT)) throw new Error(\"\u6E90\u7801\u90E8\u7F72\u4E0D\u8986\u76D6\u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u6B63\u5E38\u5F00\u53D1\u53D1\u5E03\u6D41\u7A0B\");\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(target)) throw new Error(\"\u9700\u8981\u56FA\u5B9A\u7A33\u5B9A\u7248\u672C\");\n const rows = await runtimeRows();\n if (rows.some(row => row.running && row.ownership === \"external\")) throw new Error(\"\u5916\u90E8\u670D\u52A1\u5FC5\u987B\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u4F18\u96C5\u505C\u6B62\");\n const request = path.join(ROOT, \".sfmc\", \"operations\", `${context.id}.request.json`);\n const previous = readJson(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), { entry: fs.existsSync(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\")) ? path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\") : process.env.SFMC_DAEMON_ENTRY, version: platformVersion() });\n atomicJson(request, { root: ROOT, id: context.id, target, running: rows.filter(row => row.running).map(row => row.name), previous });\n const entry = fileURLToPath(new URL(\"./platform-worker.js\", import.meta.url));\n const log = path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n fs.mkdirSync(path.dirname(log), { recursive: true });\n // \u4E0D\u628A\u7236\u8FDB\u7A0B\u7684\u65E5\u5FD7\u53E5\u67C4\u4EA4\u7ED9\u5B50\u8FDB\u7A0B\u3002Windows \u4E0A\u7EE7\u627F\u53E5\u67C4\u65F6\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u4F1A\u7ED3\u675F\u5C1A\u672A\u8131\u79BB\u7684\u7EF4\u62A4\u8FDB\u7A0B\uFF0C\u5907\u4EFD\u4F1A\u505C\u5728\u534A\u622A\u4E14\u9501\u65E0\u4EBA\u91CA\u653E\u3002\n const child = spawn(process.execPath, [entry, request], { cwd: ROOT, env: { ...process.env, SFMC_ROOT: ROOT, SFMC_MAINTENANCE_LOG: log }, detached: true, windowsHide: true, stdio: \"ignore\" });\n await new Promise<void>((resolve, reject) => { child.once(\"spawn\", resolve); child.once(\"error\", reject); });\n child.unref();\n const record = store.get(context.id) as ReturnType<TaskStore[\"get\"]> & { workerPid?: number };\n record.status = \"running\"; if (child.pid) record.workerPid = child.pid;\n store.save(record);\n return { detached: true };\n}\nexport async function runPnpm(args: string[], cwd: string) {\n const script = process.env.SFMC_PNPM_ENTRY;\n if (script) return runProcess(process.execPath, [script, ...args], cwd);\n const command = process.platform === \"win32\" ? \"pnpm.cmd\" : \"pnpm\";\n const crossSpawn = require(\"cross-spawn\") as typeof spawn;\n await new Promise<void>((resolve, reject) => {\n const child = crossSpawn(command, args, { cwd, env: process.env, windowsHide: true, stdio: \"ignore\" });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`pnpm \u6267\u884C\u5931\u8D25 (${code})`)));\n });\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AACtB,SAAS,qBAAqB;AAC9B,SAAS,MAAM,kBAAkB,wBAAwB;AACzD,SAAS,2BAA2B,UAAU,mBAAqC;AACnF,SAAS,yBAAyB;AAClC,SAAS,kBAAoD;AAC7D,SAAS,UAAU,kBAAkB;AAErC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AAErC,eAAe,cAAc;AAC3B,MAAI,eAAe,EAAG,QAAO,0BAA0B;AACvD,QAAM,SAAS,MAAM,WAAW,QAAQ;AACxC,MAAI,OAAO,SAAS,SAAU,OAAM,IAAI,MAAM,0EAAc;AAC5D,SAAO,OAAO;AAChB;AAEA,MAAMA,WAAU,cAAc,YAAY,GAAG;AACtC,SAAS,kBAA0B;AACxC,MAAI,iBAAiB,IAAI,EAAG,QAAO,SAA8B,KAAK,KAAK,MAAM,YAAY,QAAQ,cAAc,GAAG,EAAE,SAAS,UAAU,CAAC,EAAE;AAC9I,MAAI,QAAQ,IAAI,sBAAuB,QAAO,QAAQ,IAAI;AAC1D,QAAM,SAAS,SAA+B,KAAK,KAAK,MAAM,SAAS,WAAW,aAAa,GAAG,CAAC,CAAC;AACpG,MAAI,OAAO,QAAS,QAAO,OAAO;AAClC,QAAM,WAAW,SAA+B,KAAK,KAAK,MAAM,gBAAgB,aAAa,QAAQ,cAAc,GAAG,CAAC,CAAC;AACxH,MAAI,SAAS,QAAS,QAAO,SAAS;AACtC,MAAI;AAAE,WAAQA,SAAQ,6BAA6B,EAA0B;AAAA,EAAS,QAChF;AAAE,WAAQ,SAA8B,KAAK,KAAK,cAAc,IAAI,IAAI,YAAY,YAAY,GAAG,CAAC,GAAG,cAAc,GAAG,EAAE,SAAS,UAAU,CAAC,EAAG;AAAA,EAAS;AAClK;AACA,eAAsB,eAAe,sBAAsB,MAA+B;AACxF,QAAM,cAAc,iBAAiB,IAAI,KAAK,iBAAiB;AAC/D,QAAM,iBAAiB,gBAAgB;AACvC,MAAI,gBAAgB;AACpB,MAAI,CAAC,aAAa;AAChB,UAAM,WAAW,MAAM,MAAM,sDAAsD,EAAE,QAAQ,YAAY,QAAQ,IAAM,EAAE,CAAC;AAC1H,QAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,qDAAa,SAAS,MAAM,GAAG;AACjE,UAAM,WAAW,MAAM,SAAS,KAAK;AACrC,QAAI,CAAC,SAAS,WAAW,CAAC,kBAAkB,KAAK,SAAS,OAAO,EAAG,OAAM,IAAI,MAAM,uFAAsB;AAC1G,oBAAgB,SAAS;AAAA,EAC3B;AACA,QAAM,CAAC,MAAM,YAAY,IAAI,MAAM,QAAQ,IAAI,CAAC,YAAY,GAAG,uBAAuB,CAAC,cAAc,qBAAqB,aAAa,IAAI,MAAS,CAAC;AACrJ,SAAO,EAAE,gBAAgB,eAAe,aAAa,iBAAiB,CAAC,eAAe,kBAAkB,gBAAgB,kBAAkB,KAAK,OAAO,SAAO,IAAI,WAAW,IAAI,cAAc,UAAU,EAAE,IAAI,SAAO,IAAI,IAAI,GAAG,OAAO,cAAc,CAAC,kGAAkB,IAAI,CAAC,kFAAiB,wFAAkB,
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { ROOT, isMonorepoLayout, isSourceCheckout } from \"../runtime.js\";\nimport { queryServicesRuntimeLocal, services, START_ORDER, type ServiceName } from \"../services.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { atomicJson, type TaskContext, type TaskStore } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport type { AttachmentPlan } from \"@sfmc-bds/management\";\nimport { isDaemonServer } from \"../daemon/role.js\";\nimport { callDaemon } from \"../daemon/client.js\";\nimport { platformReleaseNotes } from \"@sfmc-bds/management/node\";\n\nasync function runtimeRows() {\n if (isDaemonServer()) return queryServicesRuntimeLocal();\n const result = await callDaemon(\"status\");\n if (result.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u8BFB\u53D6\u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u72B6\u6001\");\n return result.rows;\n}\n\nconst require = createRequire(import.meta.url);\nexport function platformVersion(): string {\n if (isMonorepoLayout(ROOT)) return readJson<{ version: string }>(path.join(ROOT, \"packages\", \"meta\", \"package.json\"), { version: \"unknown\" }).version;\n if (process.env.SFMC_PLATFORM_VERSION) return process.env.SFMC_PLATFORM_VERSION;\n const active = readJson<{ version?: string }>(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), {});\n if (active.version) return active.version;\n const deployed = readJson<{ version?: string }>(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"package.json\"), {});\n if (deployed.version) return deployed.version;\n try { return (require(\"@sfmc-bds/sfmc/package.json\") as { version: string }).version; }\n catch { return (readJson<{ version: string }>(path.join(fileURLToPath(new URL(\"../../..\", import.meta.url)), \"package.json\"), { version: \"unknown\" })).version; }\n}\nexport async function attachmentPlan(includeReleaseNotes = true): Promise<AttachmentPlan> {\n const development = isMonorepoLayout(ROOT) || isSourceCheckout();\n const currentVersion = platformVersion();\n let targetVersion = currentVersion;\n if (!development) {\n const response = await fetch(\"https://registry.npmjs.org/@sfmc-bds%2fsfmc/latest\", { signal: AbortSignal.timeout(15_000) });\n if (!response.ok) throw new Error(`\u5E73\u53F0\u7248\u672C\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n const metadata = await response.json() as { version?: string };\n if (!metadata.version || !/^\\d+\\.\\d+\\.\\d+$/.test(metadata.version)) throw new Error(\"latest \u6CA1\u6709\u6307\u5411\u6709\u6548\u7684\u7A33\u5B9A\u5E73\u53F0\u7248\u672C\");\n targetVersion = metadata.version;\n }\n const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : undefined]);\n return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter(row => row.running && row.ownership === \"external\").map(row => row.name), steps: development ? [\"\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72\"] : [\"\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408\", \"\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\", \"\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1\", \"\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\"], ...(releaseNotes ? { releaseNotes } : {}) };\n}\nexport async function stopForMaintenance(names: ServiceName[], phase: TaskContext[\"phase\"]): Promise<ServiceName[]> {\n const before = await queryServicesRuntimeLocal();\n const affected = before.filter(row => names.includes(row.name) && row.running);\n if (affected.some(row => row.ownership === \"external\")) throw Object.assign(new Error(\"\u5B58\u5728\u65E0\u6CD5\u901A\u8FC7\u5F53\u524D\u5B88\u62A4\u8FDB\u7A0B\u4F18\u96C5\u505C\u6B62\u7684\u5916\u90E8\u670D\u52A1\uFF1B\u8BF7\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u505C\u6B62\u540E\u91CD\u65B0\u63A5\u5165\"), { code: \"conflict\" });\n phase(\"stop-services\", \"\u4F18\u96C5\u505C\u6B62\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\");\n const running = affected.map(row => row.name);\n for (const name of [...START_ORDER].reverse().filter(name => names.includes(name))) await services[name].stop(false, false);\n const after = await queryServicesRuntimeLocal();\n if (after.some(row => names.includes(row.name) && row.running)) throw new Error(\"\u670D\u52A1\u672A\u5B8C\u5168\u9000\u51FA\uFF0C\u5DF2\u4E2D\u6B62\u6587\u4EF6\u64CD\u4F5C\");\n return running;\n}\nexport async function restoreRunning(names: ServiceName[], phase: TaskContext[\"phase\"]) {\n phase(\"restore\", \"\u6062\u590D\u7EF4\u62A4\u524D\u8FD0\u884C\u7684\u670D\u52A1\");\n for (const name of START_ORDER.filter(name => names.includes(name))) {\n const result = await services[name].start();\n if (result.status === \"skipped\") throw new Error(`\u6062\u590D ${name} \u5931\u8D25: ${result.reason}`);\n }\n await verifyRunning(names);\n}\nexport async function verifyRunning(names: ServiceName[]) {\n if (!names.length) return;\n await new Promise(resolve => setTimeout(resolve, 2000));\n for (const name of names) {\n if (!services[name].running || !services[name].pid) throw new Error(`${name} \u542F\u52A8\u540E\u5DF2\u9000\u51FA`);\n }\n if (names.includes(\"db\")) {\n const cfg = readJson<{ db_port?: number; http_auth?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const response = await fetch(`http://127.0.0.1:${cfg.db_port ?? 3001}/api/sfmc/status`, { headers: cfg.http_auth ? { authorization: `Bearer ${cfg.http_auth}` } : {}, signal: AbortSignal.timeout(5000) });\n if (!response.ok) throw new Error(`DB \u5065\u5EB7\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n }\n}\nfunction snapshotTargets() {\n const db = readJson<{ dbDir?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const bds = resolveBdsContext().bdsRoot;\n return { configs: path.join(ROOT, \"configs\"), modules: path.join(ROOT, \"modules\"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? \"data/sfmc_data.db\")), bds };\n}\nexport async function createSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n phase(\"backup\", \"\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD\");\n const destination = path.join(ROOT, \".sfmc\", \"backups\", id);\n const targets = snapshotTargets();\n for (const target of Object.values(targets)) {\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62\");\n }\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.mkdirSync(destination);\n try {\n for (const [key, target] of Object.entries(targets)) {\n if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });\n }\n atomicJson(path.join(destination, \"manifest.json\"), { createdAt: new Date().toISOString(), targets, complete: true });\n } catch (error) {\n // \u53EA\u6E05\u7406\u672C\u6B21\u521B\u5EFA\u7684\u4E0D\u5B8C\u6574\u5FEB\u7167\uFF0C\u907F\u514D\u5907\u4EFD\u5931\u8D25\u540E\u7EE7\u7EED\u5360\u7528\u7A7A\u95F4\u3002\n await fs.promises.rm(destination, { recursive: true, force: true });\n throw error;\n }\n return destination;\n}\n/** \u5907\u4EFD\u53EA\u8BFB\u53D6\u5B9E\u4F8B\u6587\u4EF6\uFF0C\u5373\u4F7F\u590D\u5236\u5931\u8D25\u4E5F\u6062\u590D\u539F\u5148\u8FD0\u884C\u7684\u670D\u52A1\u3002 */\nexport async function createBackup(context: TaskContext) {\n const running = await stopForMaintenance([...START_ORDER], context.phase);\n let completed = false;\n try {\n await createSnapshot(context.id, context.phase);\n completed = true;\n } finally {\n try { await restoreRunning(running, context.phase); }\n catch (error) {\n throw Object.assign(new Error(`\u5907\u4EFD${completed ? \"\u5DF2\u5B8C\u6210\" : \"\u672A\u5B8C\u6210\"}\uFF0C\u6062\u590D\u670D\u52A1\u8FD0\u884C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: completed ? { backupId: context.id } : undefined });\n }\n }\n return { backupId: context.id };\n}\nexport async function restoreSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n if (!manifest.complete) throw new Error(\"\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u7981\u6B62\u6062\u590D\");\n const targets = snapshotTargets();\n phase(\"restore-data\", \"\u6309\u7528\u6237\u660E\u786E\u9009\u62E9\u6062\u590D\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u548C\u4E16\u754C\");\n for (const [key, target] of Object.entries(targets)) {\n if (path.resolve(manifest.targets[key] ?? \"\") !== path.resolve(target)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u4E0E\u5F53\u524D\u90E8\u7F72\u4E0D\u4E00\u81F4\");\n const saved = path.join(source, key);\n if (!fs.existsSync(saved)) continue;\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || fs.lstatSync(saved).isSymbolicLink()) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u6062\u590D\u76EE\u6807\");\n await fs.promises.rm(absolute, { recursive: true, force: true });\n await fs.promises.cp(saved, absolute, { recursive: true, dereference: false });\n }\n return { restored: id };\n}\n/** \u666E\u901A\u7EF4\u62A4\u53EA\u8D1F\u8D23\u505C\u670D\u4E0E\u6062\u590D\uFF1B\u5168\u91CF\u5FEB\u7167\u4EC5\u7531\u624B\u52A8\u5907\u4EFD\u4EFB\u52A1\u521B\u5EFA\u3002 */\nexport async function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>): Promise<T> {\n const running = await stopForMaintenance(names, context.phase);\n try {\n context.phase(\"execute\");\n const result = await action();\n context.phase(\"verify\");\n await restoreRunning(running, context.phase);\n return result;\n } catch (error) {\n // \u5931\u8D25\u540E\u4FDD\u6301\u505C\u670D\uFF0C\u4E0D\u731C\u6D4B\u672A\u77E5\u6570\u636E\u8FC1\u79FB\u662F\u5426\u53EF\u4EE5\u5B89\u5168\u6062\u590D\u8FD0\u884C\u3002\n for (const name of [...START_ORDER].reverse().filter(name => running.includes(name))) await services[name].stop(false, false).catch(() => {});\n throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: (error as { details?: object }).details });\n }\n}\nexport async function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string) {\n if (isMonorepoLayout(ROOT)) throw new Error(\"\u6E90\u7801\u90E8\u7F72\u4E0D\u8986\u76D6\u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u6B63\u5E38\u5F00\u53D1\u53D1\u5E03\u6D41\u7A0B\");\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(target)) throw new Error(\"\u9700\u8981\u56FA\u5B9A\u7A33\u5B9A\u7248\u672C\");\n const rows = await runtimeRows();\n if (rows.some(row => row.running && row.ownership === \"external\")) throw new Error(\"\u5916\u90E8\u670D\u52A1\u5FC5\u987B\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u4F18\u96C5\u505C\u6B62\");\n const request = path.join(ROOT, \".sfmc\", \"operations\", `${context.id}.request.json`);\n const previous = readJson(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), { entry: fs.existsSync(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\")) ? path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\") : process.env.SFMC_DAEMON_ENTRY, version: platformVersion() });\n atomicJson(request, { root: ROOT, id: context.id, target, running: rows.filter(row => row.running).map(row => row.name), previous });\n const entry = fileURLToPath(new URL(\"./platform-worker.js\", import.meta.url));\n const log = path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n fs.mkdirSync(path.dirname(log), { recursive: true });\n // \u4E0D\u628A\u7236\u8FDB\u7A0B\u7684\u65E5\u5FD7\u53E5\u67C4\u4EA4\u7ED9\u5B50\u8FDB\u7A0B\uFF0C\u907F\u514D\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u65F6\u4E2D\u65AD\u7EF4\u62A4\u8FDB\u7A0B\u3002\n const child = spawn(process.execPath, [entry, request], { cwd: ROOT, env: { ...process.env, SFMC_ROOT: ROOT, SFMC_MAINTENANCE_LOG: log }, detached: true, windowsHide: true, stdio: \"ignore\" });\n await new Promise<void>((resolve, reject) => { child.once(\"spawn\", resolve); child.once(\"error\", reject); });\n child.unref();\n const record = store.get(context.id) as ReturnType<TaskStore[\"get\"]> & { workerPid?: number };\n record.status = \"running\"; if (child.pid) record.workerPid = child.pid;\n store.save(record);\n return { detached: true };\n}\nexport async function runPnpm(args: string[], cwd: string) {\n const script = process.env.SFMC_PNPM_ENTRY;\n if (script) return runProcess(process.execPath, [script, ...args], cwd);\n const command = process.platform === \"win32\" ? \"pnpm.cmd\" : \"pnpm\";\n const crossSpawn = require(\"cross-spawn\") as typeof spawn;\n await new Promise<void>((resolve, reject) => {\n const child = crossSpawn(command, args, { cwd, env: process.env, windowsHide: true, stdio: \"ignore\" });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`pnpm \u6267\u884C\u5931\u8D25 (${code})`)));\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["require", "name"]
|
|
7
7
|
}
|
|
@@ -6,7 +6,7 @@ import { withMaintenanceLock } from "@sfmc-bds/management/node";
|
|
|
6
6
|
import { toManagementError } from "@sfmc-bds/management";
|
|
7
7
|
import { atomicJson } from "./tasks.js";
|
|
8
8
|
import { readJson, runProcess } from "./domain.js";
|
|
9
|
-
import {
|
|
9
|
+
import { runPnpm } from "./maintenance.js";
|
|
10
10
|
import { callDaemon, disconnectDaemonClient } from "../daemon/client.js";
|
|
11
11
|
import { ROOT } from "../runtime.js";
|
|
12
12
|
import { START_ORDER } from "../services.js";
|
|
@@ -125,7 +125,6 @@ async function main() {
|
|
|
125
125
|
const lock = readJson(path.join(ROOT, ".sfmc", "maintenance.lock"), { nonce: "" });
|
|
126
126
|
process.env.SFMC_MAINTENANCE_TOKEN = lock.nonce;
|
|
127
127
|
const daemon = await stopServices();
|
|
128
|
-
await createSnapshot(request.id, phase);
|
|
129
128
|
await exitDaemon(daemon);
|
|
130
129
|
phase("execute");
|
|
131
130
|
atomicJson(activeFile, { entry, version: request.target, pnpm: process.env.SFMC_PNPM_ENTRY });
|
|
@@ -144,7 +143,7 @@ async function main() {
|
|
|
144
143
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
145
144
|
const status = await callDaemon("status");
|
|
146
145
|
if (status.kind !== "status" || request.running.some((name) => !status.rows.some((row) => row.name === name && row.running))) throw new Error("\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\u672A\u5168\u90E8\u6062\u590D");
|
|
147
|
-
record.result = { version: request.target
|
|
146
|
+
record.result = { version: request.target };
|
|
148
147
|
record.status = "succeeded";
|
|
149
148
|
} catch (error) {
|
|
150
149
|
let rolledBack = false;
|
|
@@ -154,10 +153,10 @@ async function main() {
|
|
|
154
153
|
atomicJson(activeFile, request.previous);
|
|
155
154
|
rolledBack = true;
|
|
156
155
|
} catch (rollbackError) {
|
|
157
|
-
phase("rollback-blocked", `\u65E0\u6CD5\u5B89\u5168\u505C\u6B62\u76EE\u6807\u7248\u672C\uFF1B\u4FDD\u7559\u5165\u53E3\
|
|
156
|
+
phase("rollback-blocked", `\u65E0\u6CD5\u5B89\u5168\u505C\u6B62\u76EE\u6807\u7248\u672C\uFF1B\u4FDD\u7559\u5F53\u524D\u5165\u53E3\uFF0C\u4E0D\u8986\u76D6\u73A9\u5BB6\u6570\u636E: ${String(rollbackError)}`);
|
|
158
157
|
}
|
|
159
158
|
}
|
|
160
|
-
record.result = { programRolledBack: rolledBack,
|
|
159
|
+
record.result = { programRolledBack: rolledBack, dataRestoreRequired: switched, preparedEntry };
|
|
161
160
|
throw error;
|
|
162
161
|
}
|
|
163
162
|
});
|
|
@@ -173,7 +172,7 @@ async function main() {
|
|
|
173
172
|
} catch (error) {
|
|
174
173
|
record.status = "failed";
|
|
175
174
|
record.error = toManagementError(error);
|
|
176
|
-
record.result ??= { programRolledBack: false,
|
|
175
|
+
record.result ??= { programRolledBack: false, dataRestoreRequired: switched, preparedEntry };
|
|
177
176
|
maintenanceLog(`failed ${record.error.message}`);
|
|
178
177
|
} finally {
|
|
179
178
|
record.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/platform-worker.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\nimport { atomicJson } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport { createSnapshot, runPnpm } from \"./maintenance.js\";\nimport { callDaemon, disconnectDaemonClient } from \"../daemon/client.js\";\nimport { ROOT } from \"../runtime.js\";\nimport { START_ORDER } from \"../services.js\";\nimport { daemonLogPath, readDaemonMeta } from \"../daemon/paths.js\";\n\n/**\n * \u7EF4\u62A4\u8FDB\u7A0B\u81EA\u5DF1\u5199\u65E5\u5FD7\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7236\u8FDB\u7A0B\u628A\u6807\u51C6\u8F93\u51FA\u63A5\u5230\u81EA\u5DF1\u7684\u6587\u4EF6\u53E5\u67C4\u65F6\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u4F1A\u5BFC\u81F4\u8FD9\u6BB5\u8F93\u51FA\u4E22\u5931\uFF1B\u8FD9\u91CC\u76F4\u63A5\u8FFD\u52A0\u5230\u65E5\u5FD7\u6587\u4EF6\u3002\n */\nfunction maintenanceLog(message: string) {\n const file = process.env.SFMC_MAINTENANCE_LOG || path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.appendFileSync(file, `${new Date().toISOString()} ${message}\\n`);\n } catch { /* \u65E5\u5FD7\u5199\u5931\u8D25\u65F6\u4ECD\u4EE5\u4EFB\u52A1\u8BB0\u5F55\u4E3A\u51C6 */ }\n}\n\n/** \u672C\u8FDB\u7A0B\u5728\u72EC\u7ACB Node \u4E2D\u5E38\u9A7B\uFF0C\u5E73\u53F0\u6362\u5305\u4E0D\u4F1A\u5220\u9664\u5176\u5F53\u524D\u7248\u672C\u76EE\u5F55\u3002 */\nasync function main() {\n const requestFile = process.argv[2];\n if (!requestFile) throw new Error(\"\u7F3A\u5C11\u7EF4\u62A4\u4EFB\u52A1\u6587\u4EF6\");\n const request = readJson<{ id: string; target: string; running: string[]; previous: { entry: string } | null }>(requestFile, { id: \"\", target: \"\", running: [], previous: null });\n if (!/^[\\w-]+$/.test(request.id) || !/^\\d+\\.\\d+\\.\\d+$/.test(request.target)) throw new Error(\"\u975E\u6CD5\u7EF4\u62A4\u8BF7\u6C42\");\n const recordFile = path.join(ROOT, \".sfmc\", \"operations\", `${request.id}.json`);\n let record = readJson<OperationRecord>(recordFile, {} as OperationRecord);\n const phase = (name: string, message?: string) => {\n record = readJson<OperationRecord>(recordFile, record);\n const previous = record.phases.at(-1); if (previous?.status === \"running\") previous.status = \"done\";\n record.phases.push({ name, status: \"running\", ...(message ? { message } : {}) }); record.status = \"running\"; record.updatedAt = new Date().toISOString(); atomicJson(recordFile, record);\n maintenanceLog(`phase ${name}${message ? ` ${message}` : \"\"}`);\n };\n const runtime = path.join(ROOT, \".sfmc\", \"runtime\");\n const release = path.join(runtime, \"releases\", `${request.target}-${request.id}`);\n const activeFile = path.join(runtime, \"active.json\");\n let switched = false;\n let preparedEntry = \"\";\n /** \u65E7\u8FDB\u7A0B\u9000\u51FA\uFF0C\u4EE5\u53CA\u5B83\u7684\u65E5\u5FD7\u53E5\u67C4\u677E\u5F00\uFF0C\u5171\u7528\u8FD9\u4E00\u6BB5\u7B49\u5F85\u3002 */\n const daemonTeardownMs = 30_000;\n const waitExit = async (pid: number) => {\n const deadline = Date.now() + daemonTeardownMs;\n for (;;) {\n let alive = true; try { process.kill(pid, 0); } catch (error) { alive = (error as NodeJS.ErrnoException).code !== \"ESRCH\"; }\n if (!alive) return;\n if (Date.now() > deadline) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u5C1A\u672A\u9000\u51FA\uFF0C\u7981\u6B62\u5207\u6362\u5E73\u53F0\");\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n };\n /**\n * \u7B49\u5230 daemon.log \u53EF\u4EE5\u518D\u6B21\u8FFD\u52A0\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u5E73\u53F0\u6821\u9A8C\u4F1A\u62C9\u8D77\u4E0B\u4E00\u7248\u5B88\u62A4\u8FDB\u7A0B\uFF0C\u5B83\u4E00\u542F\u52A8\u5C31\u8FFD\u52A0\u8FD9\u4E2A\u6587\u4EF6\u3002\n * \u65E7\u65E5\u5FD7\u6CF5\u8981\u7B49 Node \u8FDB\u7A0B\u9000\u51FA\u540E\u624D\u5173\u95ED\u53E5\u67C4\uFF0C\u4E2D\u95F4\u4F1A\u8BA9\u6253\u5F00\u5931\u8D25\u3002\n */\n const waitDaemonLogAppendable = async () => {\n const log = daemonLogPath();\n const deadline = Date.now() + daemonTeardownMs;\n for (;;) {\n try {\n const fd = fs.openSync(log, \"a\");\n fs.closeSync(fd);\n return;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== \"EBUSY\") throw error;\n if (Date.now() > deadline) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u65E5\u5FD7\u4ECD\u88AB\u5360\u7528\uFF0C\u5DF2\u505C\u6B62\u5207\u6362\u5E73\u53F0\");\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n }\n };\n /** \u53EA\u505C\u670D\u52A1\uFF0C\u4E0D\u9000\u51FA\u5B88\u62A4\u8FDB\u7A0B\u3002\u5907\u4EFD\u5FC5\u987B\u53D1\u751F\u5728\u5B88\u62A4\u8FDB\u7A0B\u8FD8\u6D3B\u7740\u7684\u65F6\u5019\u3002 */\n const stopServices = async () => {\n const meta = readDaemonMeta();\n if (!meta) return meta;\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u670D\u52A1\u72B6\u6001\");\n if (status.rows.some(row => row.running)) {\n const stopped = await callDaemon(\"maintenanceStop\");\n if (stopped.kind !== \"status\" || stopped.rows.some(row => row.running)) throw new Error(\"\u670D\u52A1\u6CA1\u6709\u4F18\u96C5\u9000\u51FA\");\n }\n return meta;\n };\n /** \u6587\u4EF6\u5907\u4EFD\u5B8C\u6210\u540E\u518D\u8BA9\u65E7\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\uFF0C\u907F\u514D\u9000\u51FA\u65F6\u628A\u672C\u7EF4\u62A4\u8FDB\u7A0B\u4E00\u8D77\u5E26\u8D70\u3001\u5907\u4EFD\u505C\u5728\u534A\u622A\u3002 */\n const exitDaemon = async (meta: { pid: number } | null) => {\n if (!meta) return;\n await callDaemon(\"shutdown\"); disconnectDaemonClient(); await waitExit(meta.pid);\n };\n const shutdown = async () => { await exitDaemon(await stopServices()); };\n try {\n // \u63D0\u4EA4\u8FDB\u7A0B\u4FDD\u5B58 workerPid \u540E\u624D\u91CA\u653E\u9501\uFF1B\u7B49\u5F85\u63A5\u7BA1\uFF0C\u7981\u6B62\u7ADE\u6001\u8986\u76D6\u8BB0\u5F55\u3002\n await new Promise(resolve => setTimeout(resolve, 500));\n let acquired = false;\n for (let attempt = 0; attempt < 100 && !acquired; attempt++) {\n try {\n await withMaintenanceLock(ROOT, async () => {\n acquired = true;\n try {\n phase(\"prepare\", `\u51C6\u5907\u5E73\u53F0 ${request.target}`);\n fs.mkdirSync(release, { recursive: true });\n atomicJson(path.join(release, \"package.json\"), { private: true, dependencies: {} });\n await runPnpm([\"add\", \"--ignore-workspace\", \"--prod\", \"--ignore-scripts\", \"--save-exact\", `@sfmc-bds/sfmc@${request.target}`], release);\n const linked = path.join(release, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\");\n if (!fs.existsSync(linked)) throw new Error(\"\u5E73\u53F0\u5165\u53E3\u4E0D\u5B58\u5728\");\n // pnpm \u7684\u4F9D\u8D56\u94FE\u63A5\u5728\u771F\u5B9E\u5305\u76EE\u5F55\u65C1\u3002\u4ECE junction \u8DEF\u5F84 createRequire \u4F1A\u627E\u4E0D\u5230 @sfmc-bds/cli\u3002\n const entry = fs.realpathSync(linked);\n // \u53D1\u5E03\u7684\u5E73\u53F0\u5FC5\u987B\u5305\u542B\u672C\u6B21\u684C\u9762\u6240\u9700\u7BA1\u7406\u534F\u8BAE\u3002\n const resolver = createRequire(entry);\n const cli = path.join(path.dirname(resolver.resolve(\"@sfmc-bds/cli/package.json\")), \"dist\", \"management\", \"stdio.js\");\n if (!fs.existsSync(cli)) throw new Error(\"\u6700\u65B0\u5E73\u53F0\u5C1A\u672A\u53D1\u5E03\u7BA1\u7406\u5165\u53E3\uFF0C\u4FDD\u7559\u5F53\u524D\u90E8\u7F72\uFF1B\u8BF7\u5148\u5B8C\u6210\u5E73\u53F0\u53D1\u7248\");\n const targetSdk = readJson<{ version: string }>(resolver.resolve(\"@sfmc-bds/sdk/package.json\"), { version: \"\" }).version;\n const { satisfiesSdk } = await import(pathToFileURL(path.join(path.dirname(cli), \"../..\", \"scripts/module-install/lib/semver.mjs\")).href) as { satisfiesSdk: (version: string, range: string) => boolean };\n const modules = path.join(ROOT, \"modules\", \"packages\");\n if (fs.existsSync(modules)) for (const folder of fs.readdirSync(modules)) {\n const pkg = readJson<{ peerDependencies?: Record<string, string> }>(path.join(modules, folder, \"package.json\"), {});\n const manifest = readJson<{ sdk?: string }>(path.join(modules, folder, \"sapi\", \"manifest.json\"), {});\n const range = pkg.peerDependencies?.[\"@sfmc-bds/sdk\"] ?? manifest.sdk;\n if (range && !satisfiesSdk(targetSdk, range)) throw new Error(`\u6A21\u5757 ${folder} \u4E0E\u76EE\u6807 SDK ${targetSdk} \u4E0D\u517C\u5BB9 (${range})\uFF1B\u4FDD\u7559\u56FA\u5B9A\u7248\u672C\u53CA\u672C\u5730\u94FE\u63A5\uFF0C\u5DF2\u505C\u6B62\u5347\u7EA7`);\n }\n preparedEntry = entry;\n phase(\"stop-services\");\n const lock = readJson<{ nonce: string }>(path.join(ROOT, \".sfmc\", \"maintenance.lock\"), { nonce: \"\" });\n process.env.SFMC_MAINTENANCE_TOKEN = lock.nonce;\n const daemon = await stopServices();\n await createSnapshot(request.id, phase);\n await exitDaemon(daemon);\n phase(\"execute\"); atomicJson(activeFile, { entry, version: request.target, pnpm: process.env.SFMC_PNPM_ENTRY }); switched = true;\n const launcher = path.join(runtime, \"launcher.mjs\");\n fs.writeFileSync(launcher, \"import fs from 'node:fs'; import path from 'node:path'; import {pathToFileURL,fileURLToPath} from 'node:url'; const base=path.dirname(fileURLToPath(import.meta.url)); const active=JSON.parse(fs.readFileSync(path.join(base,'active.json'),'utf8')); process.env.SFMC_DAEMON_ENTRY=fileURLToPath(import.meta.url); process.env.SFMC_NODE_BINARY=process.execPath; if(active.pnpm) process.env.SFMC_PNPM_ENTRY=active.pnpm; await import(pathToFileURL(active.entry).href);\\n\");\n for (const key of Object.keys(process.env)) if (key.startsWith(\"SFMC_SERVICE_\") && key.endsWith(\"_ENTRY\") || [\"SFMC_FETCH_MODULE\", \"SFMC_PLATFORM_VERSION\"].includes(key)) delete process.env[key];\n process.env.SFMC_DAEMON_ENTRY = launcher;\n phase(\"verify\");\n await waitDaemonLogAppendable();\n await runProcess(process.execPath, [launcher, \"status\"], ROOT);\n const handshake = await callDaemon(\"management\", { request: { id: request.id, method: \"handshake\" } });\n if (handshake.kind !== \"management\" || !handshake.response.ok || (handshake.response.result as { platformVersion?: string }).platformVersion !== request.target) throw new Error(\"\u76EE\u6807\u5E73\u53F0\u63E1\u624B\u9A8C\u8BC1\u5931\u8D25\");\n phase(\"restore\");\n for (const service of START_ORDER.filter(name => request.running.includes(name))) await callDaemon(\"start\", { name: service });\n await new Promise(resolve => setTimeout(resolve, 3000));\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\" || request.running.some(name => !status.rows.some(row => row.name === name && row.running))) throw new Error(\"\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\u672A\u5168\u90E8\u6062\u590D\");\n record.result = { version: request.target, backupId: request.id }; record.status = \"succeeded\";\n } catch (error) {\n // \u56DE\u9000\u4E5F\u5FC5\u987B\u5728\u540C\u4E00\u628A\u9501\u5185\u5B8C\u6210\uFF0C\u9632\u6B62\u5176\u4ED6\u5BA2\u6237\u7AEF\u5728\u5165\u53E3\u5207\u6362\u671F\u95F4\u5199\u5165\u3002\n let rolledBack = false;\n if (switched && request.previous) {\n try { await shutdown(); atomicJson(activeFile, request.previous); rolledBack = true; }\n catch (rollbackError) { phase(\"rollback-blocked\", `\u65E0\u6CD5\u5B89\u5168\u505C\u6B62\u76EE\u6807\u7248\u672C\uFF1B\u4FDD\u7559\u5165\u53E3\u548C\u5907\u4EFD\uFF0C\u4E0D\u8986\u76D6\u73A9\u5BB6\u6570\u636E: ${String(rollbackError)}`); }\n }\n record.result = { programRolledBack: rolledBack, backupId: request.id, dataRestoreRequired: switched, preparedEntry };\n throw error;\n }\n });\n } catch (error) {\n if (!acquired && (error as { code?: string }).code === \"locked\") { await new Promise(resolve => setTimeout(resolve, 200)); continue; }\n throw error;\n }\n }\n if (!acquired) throw new Error(\"\u65E0\u6CD5\u63A5\u7BA1\u7EF4\u62A4\u9501\");\n } catch (error) {\n record.status = \"failed\"; record.error = toManagementError(error);\n record.result ??= { programRolledBack: false, backupId: request.id, dataRestoreRequired: switched, preparedEntry };\n maintenanceLog(`failed ${record.error.message}`);\n } finally {\n record.updatedAt = new Date().toISOString(); const last = record.phases.at(-1); if (last) last.status = record.status === \"succeeded\" ? \"done\" : \"failed\";\n atomicJson(recordFile, record);\n fs.rmSync(requestFile, { force: true });\n disconnectDaemonClient();\n }\n}\nvoid main().catch(error => { process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`); process.exitCode = 1; });\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,yBAA+C;AACxD,SAAS,kBAAkB;AAC3B,SAAS,UAAU,kBAAkB;AACrC,SAAS,
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\nimport { atomicJson } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport { runPnpm } from \"./maintenance.js\";\nimport { callDaemon, disconnectDaemonClient } from \"../daemon/client.js\";\nimport { ROOT } from \"../runtime.js\";\nimport { START_ORDER } from \"../services.js\";\nimport { daemonLogPath, readDaemonMeta } from \"../daemon/paths.js\";\n\n/**\n * \u7EF4\u62A4\u8FDB\u7A0B\u81EA\u5DF1\u5199\u65E5\u5FD7\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7236\u8FDB\u7A0B\u628A\u6807\u51C6\u8F93\u51FA\u63A5\u5230\u81EA\u5DF1\u7684\u6587\u4EF6\u53E5\u67C4\u65F6\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u4F1A\u5BFC\u81F4\u8FD9\u6BB5\u8F93\u51FA\u4E22\u5931\uFF1B\u8FD9\u91CC\u76F4\u63A5\u8FFD\u52A0\u5230\u65E5\u5FD7\u6587\u4EF6\u3002\n */\nfunction maintenanceLog(message: string) {\n const file = process.env.SFMC_MAINTENANCE_LOG || path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.appendFileSync(file, `${new Date().toISOString()} ${message}\\n`);\n } catch { /* \u65E5\u5FD7\u5199\u5931\u8D25\u65F6\u4ECD\u4EE5\u4EFB\u52A1\u8BB0\u5F55\u4E3A\u51C6 */ }\n}\n\n/** \u672C\u8FDB\u7A0B\u5728\u72EC\u7ACB Node \u4E2D\u5E38\u9A7B\uFF0C\u5E73\u53F0\u6362\u5305\u4E0D\u4F1A\u5220\u9664\u5176\u5F53\u524D\u7248\u672C\u76EE\u5F55\u3002 */\nasync function main() {\n const requestFile = process.argv[2];\n if (!requestFile) throw new Error(\"\u7F3A\u5C11\u7EF4\u62A4\u4EFB\u52A1\u6587\u4EF6\");\n const request = readJson<{ id: string; target: string; running: string[]; previous: { entry: string } | null }>(requestFile, { id: \"\", target: \"\", running: [], previous: null });\n if (!/^[\\w-]+$/.test(request.id) || !/^\\d+\\.\\d+\\.\\d+$/.test(request.target)) throw new Error(\"\u975E\u6CD5\u7EF4\u62A4\u8BF7\u6C42\");\n const recordFile = path.join(ROOT, \".sfmc\", \"operations\", `${request.id}.json`);\n let record = readJson<OperationRecord>(recordFile, {} as OperationRecord);\n const phase = (name: string, message?: string) => {\n record = readJson<OperationRecord>(recordFile, record);\n const previous = record.phases.at(-1); if (previous?.status === \"running\") previous.status = \"done\";\n record.phases.push({ name, status: \"running\", ...(message ? { message } : {}) }); record.status = \"running\"; record.updatedAt = new Date().toISOString(); atomicJson(recordFile, record);\n maintenanceLog(`phase ${name}${message ? ` ${message}` : \"\"}`);\n };\n const runtime = path.join(ROOT, \".sfmc\", \"runtime\");\n const release = path.join(runtime, \"releases\", `${request.target}-${request.id}`);\n const activeFile = path.join(runtime, \"active.json\");\n let switched = false;\n let preparedEntry = \"\";\n /** \u65E7\u8FDB\u7A0B\u9000\u51FA\uFF0C\u4EE5\u53CA\u5B83\u7684\u65E5\u5FD7\u53E5\u67C4\u677E\u5F00\uFF0C\u5171\u7528\u8FD9\u4E00\u6BB5\u7B49\u5F85\u3002 */\n const daemonTeardownMs = 30_000;\n const waitExit = async (pid: number) => {\n const deadline = Date.now() + daemonTeardownMs;\n for (;;) {\n let alive = true; try { process.kill(pid, 0); } catch (error) { alive = (error as NodeJS.ErrnoException).code !== \"ESRCH\"; }\n if (!alive) return;\n if (Date.now() > deadline) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u5C1A\u672A\u9000\u51FA\uFF0C\u7981\u6B62\u5207\u6362\u5E73\u53F0\");\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n };\n /**\n * \u7B49\u5230 daemon.log \u53EF\u4EE5\u518D\u6B21\u8FFD\u52A0\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u5E73\u53F0\u6821\u9A8C\u4F1A\u62C9\u8D77\u4E0B\u4E00\u7248\u5B88\u62A4\u8FDB\u7A0B\uFF0C\u5B83\u4E00\u542F\u52A8\u5C31\u8FFD\u52A0\u8FD9\u4E2A\u6587\u4EF6\u3002\n * \u65E7\u65E5\u5FD7\u6CF5\u8981\u7B49 Node \u8FDB\u7A0B\u9000\u51FA\u540E\u624D\u5173\u95ED\u53E5\u67C4\uFF0C\u4E2D\u95F4\u4F1A\u8BA9\u6253\u5F00\u5931\u8D25\u3002\n */\n const waitDaemonLogAppendable = async () => {\n const log = daemonLogPath();\n const deadline = Date.now() + daemonTeardownMs;\n for (;;) {\n try {\n const fd = fs.openSync(log, \"a\");\n fs.closeSync(fd);\n return;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== \"EBUSY\") throw error;\n if (Date.now() > deadline) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u65E5\u5FD7\u4ECD\u88AB\u5360\u7528\uFF0C\u5DF2\u505C\u6B62\u5207\u6362\u5E73\u53F0\");\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n }\n };\n /** \u786E\u8BA4\u670D\u52A1\u5168\u90E8\u505C\u6B62\u540E\uFF0C\u624D\u5141\u8BB8\u9000\u51FA\u5B88\u62A4\u8FDB\u7A0B\u5E76\u5207\u6362\u5E73\u53F0\u5165\u53E3\u3002 */\n const stopServices = async () => {\n const meta = readDaemonMeta();\n if (!meta) return meta;\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u670D\u52A1\u72B6\u6001\");\n if (status.rows.some(row => row.running)) {\n const stopped = await callDaemon(\"maintenanceStop\");\n if (stopped.kind !== \"status\" || stopped.rows.some(row => row.running)) throw new Error(\"\u670D\u52A1\u6CA1\u6709\u4F18\u96C5\u9000\u51FA\");\n }\n return meta;\n };\n /** \u9000\u51FA\u65E7\u5B88\u62A4\u8FDB\u7A0B\u5E76\u7B49\u5F85\u5B83\u91CA\u653E\u65E5\u5FD7\u53E5\u67C4\u3002 */\n const exitDaemon = async (meta: { pid: number } | null) => {\n if (!meta) return;\n await callDaemon(\"shutdown\"); disconnectDaemonClient(); await waitExit(meta.pid);\n };\n const shutdown = async () => { await exitDaemon(await stopServices()); };\n try {\n // \u63D0\u4EA4\u8FDB\u7A0B\u4FDD\u5B58 workerPid \u540E\u624D\u91CA\u653E\u9501\uFF1B\u7B49\u5F85\u63A5\u7BA1\uFF0C\u7981\u6B62\u7ADE\u6001\u8986\u76D6\u8BB0\u5F55\u3002\n await new Promise(resolve => setTimeout(resolve, 500));\n let acquired = false;\n for (let attempt = 0; attempt < 100 && !acquired; attempt++) {\n try {\n await withMaintenanceLock(ROOT, async () => {\n acquired = true;\n try {\n phase(\"prepare\", `\u51C6\u5907\u5E73\u53F0 ${request.target}`);\n fs.mkdirSync(release, { recursive: true });\n atomicJson(path.join(release, \"package.json\"), { private: true, dependencies: {} });\n await runPnpm([\"add\", \"--ignore-workspace\", \"--prod\", \"--ignore-scripts\", \"--save-exact\", `@sfmc-bds/sfmc@${request.target}`], release);\n const linked = path.join(release, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\");\n if (!fs.existsSync(linked)) throw new Error(\"\u5E73\u53F0\u5165\u53E3\u4E0D\u5B58\u5728\");\n // pnpm \u7684\u4F9D\u8D56\u94FE\u63A5\u5728\u771F\u5B9E\u5305\u76EE\u5F55\u65C1\u3002\u4ECE junction \u8DEF\u5F84 createRequire \u4F1A\u627E\u4E0D\u5230 @sfmc-bds/cli\u3002\n const entry = fs.realpathSync(linked);\n // \u53D1\u5E03\u7684\u5E73\u53F0\u5FC5\u987B\u5305\u542B\u672C\u6B21\u684C\u9762\u6240\u9700\u7BA1\u7406\u534F\u8BAE\u3002\n const resolver = createRequire(entry);\n const cli = path.join(path.dirname(resolver.resolve(\"@sfmc-bds/cli/package.json\")), \"dist\", \"management\", \"stdio.js\");\n if (!fs.existsSync(cli)) throw new Error(\"\u6700\u65B0\u5E73\u53F0\u5C1A\u672A\u53D1\u5E03\u7BA1\u7406\u5165\u53E3\uFF0C\u4FDD\u7559\u5F53\u524D\u90E8\u7F72\uFF1B\u8BF7\u5148\u5B8C\u6210\u5E73\u53F0\u53D1\u7248\");\n const targetSdk = readJson<{ version: string }>(resolver.resolve(\"@sfmc-bds/sdk/package.json\"), { version: \"\" }).version;\n const { satisfiesSdk } = await import(pathToFileURL(path.join(path.dirname(cli), \"../..\", \"scripts/module-install/lib/semver.mjs\")).href) as { satisfiesSdk: (version: string, range: string) => boolean };\n const modules = path.join(ROOT, \"modules\", \"packages\");\n if (fs.existsSync(modules)) for (const folder of fs.readdirSync(modules)) {\n const pkg = readJson<{ peerDependencies?: Record<string, string> }>(path.join(modules, folder, \"package.json\"), {});\n const manifest = readJson<{ sdk?: string }>(path.join(modules, folder, \"sapi\", \"manifest.json\"), {});\n const range = pkg.peerDependencies?.[\"@sfmc-bds/sdk\"] ?? manifest.sdk;\n if (range && !satisfiesSdk(targetSdk, range)) throw new Error(`\u6A21\u5757 ${folder} \u4E0E\u76EE\u6807 SDK ${targetSdk} \u4E0D\u517C\u5BB9 (${range})\uFF1B\u4FDD\u7559\u56FA\u5B9A\u7248\u672C\u53CA\u672C\u5730\u94FE\u63A5\uFF0C\u5DF2\u505C\u6B62\u5347\u7EA7`);\n }\n preparedEntry = entry;\n phase(\"stop-services\");\n const lock = readJson<{ nonce: string }>(path.join(ROOT, \".sfmc\", \"maintenance.lock\"), { nonce: \"\" });\n process.env.SFMC_MAINTENANCE_TOKEN = lock.nonce;\n const daemon = await stopServices();\n await exitDaemon(daemon);\n phase(\"execute\"); atomicJson(activeFile, { entry, version: request.target, pnpm: process.env.SFMC_PNPM_ENTRY }); switched = true;\n const launcher = path.join(runtime, \"launcher.mjs\");\n fs.writeFileSync(launcher, \"import fs from 'node:fs'; import path from 'node:path'; import {pathToFileURL,fileURLToPath} from 'node:url'; const base=path.dirname(fileURLToPath(import.meta.url)); const active=JSON.parse(fs.readFileSync(path.join(base,'active.json'),'utf8')); process.env.SFMC_DAEMON_ENTRY=fileURLToPath(import.meta.url); process.env.SFMC_NODE_BINARY=process.execPath; if(active.pnpm) process.env.SFMC_PNPM_ENTRY=active.pnpm; await import(pathToFileURL(active.entry).href);\\n\");\n for (const key of Object.keys(process.env)) if (key.startsWith(\"SFMC_SERVICE_\") && key.endsWith(\"_ENTRY\") || [\"SFMC_FETCH_MODULE\", \"SFMC_PLATFORM_VERSION\"].includes(key)) delete process.env[key];\n process.env.SFMC_DAEMON_ENTRY = launcher;\n phase(\"verify\");\n await waitDaemonLogAppendable();\n await runProcess(process.execPath, [launcher, \"status\"], ROOT);\n const handshake = await callDaemon(\"management\", { request: { id: request.id, method: \"handshake\" } });\n if (handshake.kind !== \"management\" || !handshake.response.ok || (handshake.response.result as { platformVersion?: string }).platformVersion !== request.target) throw new Error(\"\u76EE\u6807\u5E73\u53F0\u63E1\u624B\u9A8C\u8BC1\u5931\u8D25\");\n phase(\"restore\");\n for (const service of START_ORDER.filter(name => request.running.includes(name))) await callDaemon(\"start\", { name: service });\n await new Promise(resolve => setTimeout(resolve, 3000));\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\" || request.running.some(name => !status.rows.some(row => row.name === name && row.running))) throw new Error(\"\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\u672A\u5168\u90E8\u6062\u590D\");\n record.result = { version: request.target }; record.status = \"succeeded\";\n } catch (error) {\n // \u56DE\u9000\u4E5F\u5FC5\u987B\u5728\u540C\u4E00\u628A\u9501\u5185\u5B8C\u6210\uFF0C\u9632\u6B62\u5176\u4ED6\u5BA2\u6237\u7AEF\u5728\u5165\u53E3\u5207\u6362\u671F\u95F4\u5199\u5165\u3002\n let rolledBack = false;\n if (switched && request.previous) {\n try { await shutdown(); atomicJson(activeFile, request.previous); rolledBack = true; }\n catch (rollbackError) { phase(\"rollback-blocked\", `\u65E0\u6CD5\u5B89\u5168\u505C\u6B62\u76EE\u6807\u7248\u672C\uFF1B\u4FDD\u7559\u5F53\u524D\u5165\u53E3\uFF0C\u4E0D\u8986\u76D6\u73A9\u5BB6\u6570\u636E: ${String(rollbackError)}`); }\n }\n record.result = { programRolledBack: rolledBack, dataRestoreRequired: switched, preparedEntry };\n throw error;\n }\n });\n } catch (error) {\n if (!acquired && (error as { code?: string }).code === \"locked\") { await new Promise(resolve => setTimeout(resolve, 200)); continue; }\n throw error;\n }\n }\n if (!acquired) throw new Error(\"\u65E0\u6CD5\u63A5\u7BA1\u7EF4\u62A4\u9501\");\n } catch (error) {\n record.status = \"failed\"; record.error = toManagementError(error);\n record.result ??= { programRolledBack: false, dataRestoreRequired: switched, preparedEntry };\n maintenanceLog(`failed ${record.error.message}`);\n } finally {\n record.updatedAt = new Date().toISOString(); const last = record.phases.at(-1); if (last) last.status = record.status === \"succeeded\" ? \"done\" : \"failed\";\n atomicJson(recordFile, record);\n fs.rmSync(requestFile, { force: true });\n disconnectDaemonClient();\n }\n}\nvoid main().catch(error => { process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`); process.exitCode = 1; });\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/management/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAA+B,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAOjH,OAAO,EAAE,SAAS,EAAgC,MAAM,YAAY,CAAC;AAoBrE,wBAAgB,eAAe,cAQ9B;AACD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,2BAA+E;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/management/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAA+B,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAOjH,OAAO,EAAE,SAAS,EAAgC,MAAM,YAAY,CAAC;AAoBrE,wBAAgB,eAAe,cAQ9B;AACD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,2BAA+E;AAqC3J,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuKrF"}
|