plugin-cluster-manager 1.1.10 → 1.1.11
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/client.js +1 -0
- package/dist/client/Doctor.d.ts +2 -0
- package/dist/client/NginxCacheManager.d.ts +2 -0
- package/dist/client/index.js +1 -1
- package/dist/client/utils/clientSafeCache.d.ts +3 -0
- package/dist/client/utils/requestDedupInterceptor.d.ts +2 -0
- package/dist/externalVersion.js +5 -5
- package/dist/locale/en-US.json +97 -1
- package/dist/locale/vi-VN.json +98 -1
- package/dist/locale/zh-CN.json +98 -1
- package/dist/server/actions/cache-monitor.d.ts +10 -0
- package/dist/server/actions/cache-monitor.js +301 -0
- package/dist/server/actions/cluster-nodes.d.ts +15 -0
- package/dist/server/actions/cluster-nodes.js +394 -10
- package/dist/server/actions/doctor.d.ts +82 -0
- package/dist/server/actions/doctor.js +1250 -0
- package/dist/server/collections/cluster-manager-doctor-runs.d.ts +3 -0
- package/dist/server/collections/cluster-manager-doctor-runs.js +52 -0
- package/dist/server/collections/cluster-manager-doctor.d.ts +18 -0
- package/dist/server/collections/cluster-manager-doctor.js +44 -0
- package/dist/server/hooks/cacheInvalidationHooks.d.ts +1 -0
- package/dist/server/hooks/cacheInvalidationHooks.js +81 -0
- package/dist/server/middlewares/listMetaCacheMiddleware.d.ts +2 -0
- package/dist/server/middlewares/listMetaCacheMiddleware.js +79 -0
- package/dist/server/orchestrator/PackageManager.js +20 -16
- package/dist/server/plugin.js +61 -8
- package/dist/server/utils/versionManager.d.ts +10 -0
- package/dist/server/utils/versionManager.js +91 -0
- package/package.json +41 -41
- package/server.js +1 -0
- package/src/client/CacheMonitor.tsx +166 -179
- package/src/client/ClusterManagerLayout.tsx +48 -42
- package/src/client/ClusterNodes.tsx +691 -418
- package/src/client/Doctor.tsx +559 -0
- package/src/client/NginxCacheManager.tsx +415 -0
- package/src/client/PluginOperations.tsx +234 -234
- package/src/client/index.tsx +22 -14
- package/src/client/utils/clientSafeCache.ts +41 -0
- package/src/client/utils/requestDedupInterceptor.ts +213 -0
- package/src/locale/en-US.json +97 -1
- package/src/locale/vi-VN.json +98 -1
- package/src/locale/zh-CN.json +98 -1
- package/src/server/__tests__/doctor.test.ts +53 -0
- package/src/server/actions/acl-cache.ts +272 -272
- package/src/server/actions/cache-monitor.ts +453 -116
- package/src/server/actions/cluster-nodes.ts +882 -378
- package/src/server/actions/doctor.ts +1540 -0
- package/src/server/collections/cluster-manager-doctor-runs.ts +23 -0
- package/src/server/collections/cluster-manager-doctor.ts +19 -0
- package/src/server/hooks/cacheInvalidationHooks.ts +58 -0
- package/src/server/middlewares/listMetaCacheMiddleware.ts +55 -0
- package/src/server/orchestrator/PackageManager.ts +19 -15
- package/src/server/plugin.ts +338 -263
- package/src/server/utils/versionManager.ts +69 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var cluster_manager_doctor_runs_exports = {};
|
|
28
|
+
__export(cluster_manager_doctor_runs_exports, {
|
|
29
|
+
default: () => cluster_manager_doctor_runs_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(cluster_manager_doctor_runs_exports);
|
|
32
|
+
var cluster_manager_doctor_runs_default = {
|
|
33
|
+
name: "clusterManagerDoctorRuns",
|
|
34
|
+
title: "Cluster Manager Doctor Runs",
|
|
35
|
+
fields: [
|
|
36
|
+
{ name: "id", type: "bigInt", autoIncrement: true, primaryKey: true },
|
|
37
|
+
{ name: "runId", type: "string", length: 64, unique: true, allowNull: false },
|
|
38
|
+
{ name: "status", type: "string", length: 20, defaultValue: "running", allowNull: false },
|
|
39
|
+
{ name: "durationMs", type: "integer", defaultValue: 12e4, allowNull: false },
|
|
40
|
+
{ name: "progress", type: "integer", defaultValue: 0, allowNull: false },
|
|
41
|
+
{ name: "startedAt", type: "date", allowNull: false },
|
|
42
|
+
{ name: "deadlineAt", type: "date", allowNull: false },
|
|
43
|
+
{ name: "finishedAt", type: "date", allowNull: true },
|
|
44
|
+
{ name: "finishReason", type: "string", length: 40, allowNull: true },
|
|
45
|
+
{ name: "startedBy", type: "string", length: 200, allowNull: true },
|
|
46
|
+
{ name: "summary", type: "json", allowNull: true },
|
|
47
|
+
{ name: "report", type: "json", allowNull: true },
|
|
48
|
+
{ name: "error", type: "text", allowNull: true },
|
|
49
|
+
{ name: "createdAt", type: "date" },
|
|
50
|
+
{ name: "updatedAt", type: "date" }
|
|
51
|
+
]
|
|
52
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DUMMY COLLECTION
|
|
3
|
+
* Keeps the `clusterManagerDoctor` resourcer compatible with NocoBase
|
|
4
|
+
* workflow/ACL collection lookups. Diagnostic run data lives in
|
|
5
|
+
* `clusterManagerDoctorRuns`.
|
|
6
|
+
*/
|
|
7
|
+
declare const _default: {
|
|
8
|
+
name: string;
|
|
9
|
+
dumpRules: string;
|
|
10
|
+
autoGenId: boolean;
|
|
11
|
+
createdAt: boolean;
|
|
12
|
+
updatedAt: boolean;
|
|
13
|
+
fields: {
|
|
14
|
+
name: string;
|
|
15
|
+
type: string;
|
|
16
|
+
}[];
|
|
17
|
+
};
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var cluster_manager_doctor_exports = {};
|
|
28
|
+
__export(cluster_manager_doctor_exports, {
|
|
29
|
+
default: () => cluster_manager_doctor_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(cluster_manager_doctor_exports);
|
|
32
|
+
var cluster_manager_doctor_default = {
|
|
33
|
+
name: "clusterManagerDoctor",
|
|
34
|
+
dumpRules: "skip",
|
|
35
|
+
autoGenId: true,
|
|
36
|
+
createdAt: false,
|
|
37
|
+
updatedAt: false,
|
|
38
|
+
fields: [
|
|
39
|
+
{
|
|
40
|
+
name: "name",
|
|
41
|
+
type: "string"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerCacheHooks(app: any): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var cacheInvalidationHooks_exports = {};
|
|
28
|
+
__export(cacheInvalidationHooks_exports, {
|
|
29
|
+
registerCacheHooks: () => registerCacheHooks
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(cacheInvalidationHooks_exports);
|
|
32
|
+
var import_versionManager = require("../utils/versionManager");
|
|
33
|
+
function registerCacheHooks(app) {
|
|
34
|
+
const db = app.db;
|
|
35
|
+
db.on("collections.afterSave", async () => {
|
|
36
|
+
await import_versionManager.cacheVersionManager.incrementCollectionVersion(app);
|
|
37
|
+
});
|
|
38
|
+
db.on("collections.afterDestroy", async () => {
|
|
39
|
+
await import_versionManager.cacheVersionManager.incrementCollectionVersion(app);
|
|
40
|
+
});
|
|
41
|
+
db.on("fields.afterSave", async () => {
|
|
42
|
+
await import_versionManager.cacheVersionManager.incrementCollectionVersion(app);
|
|
43
|
+
});
|
|
44
|
+
db.on("fields.afterDestroy", async () => {
|
|
45
|
+
await import_versionManager.cacheVersionManager.incrementCollectionVersion(app);
|
|
46
|
+
});
|
|
47
|
+
db.on("uiSchemas.afterSave", async () => {
|
|
48
|
+
await import_versionManager.cacheVersionManager.incrementSchemaVersion(app);
|
|
49
|
+
});
|
|
50
|
+
db.on("uiSchemas.afterDestroy", async () => {
|
|
51
|
+
await import_versionManager.cacheVersionManager.incrementSchemaVersion(app);
|
|
52
|
+
});
|
|
53
|
+
const invalidateRole = async (model) => {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
const roleName = ((_a = model.get) == null ? void 0 : _a.call(model, "roleName")) || ((_b = model.get) == null ? void 0 : _b.call(model, "name"));
|
|
56
|
+
if (roleName) {
|
|
57
|
+
await import_versionManager.cacheVersionManager.incrementAclVersion(app, roleName);
|
|
58
|
+
} else {
|
|
59
|
+
await import_versionManager.cacheVersionManager.incrementAllAclVersions(app);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
db.on("roles.afterSave", invalidateRole);
|
|
63
|
+
db.on("roles.afterDestroy", invalidateRole);
|
|
64
|
+
db.on("rolesResources.afterSave", invalidateRole);
|
|
65
|
+
db.on("rolesResources.afterDestroy", invalidateRole);
|
|
66
|
+
db.on("rolesResourcesActions.afterSave", invalidateRole);
|
|
67
|
+
db.on("rolesResourcesActions.afterDestroy", invalidateRole);
|
|
68
|
+
db.on("rolesUsers.afterSave", invalidateRole);
|
|
69
|
+
db.on("rolesUsers.afterDestroy", invalidateRole);
|
|
70
|
+
db.on("scopes.afterSave", async () => {
|
|
71
|
+
await import_versionManager.cacheVersionManager.incrementAllAclVersions(app);
|
|
72
|
+
});
|
|
73
|
+
db.on("scopes.afterDestroy", async () => {
|
|
74
|
+
await import_versionManager.cacheVersionManager.incrementAllAclVersions(app);
|
|
75
|
+
});
|
|
76
|
+
app.logger.info("[ClusterManager] Cache invalidation hooks registered successfully");
|
|
77
|
+
}
|
|
78
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
79
|
+
0 && (module.exports = {
|
|
80
|
+
registerCacheHooks
|
|
81
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var listMetaCacheMiddleware_exports = {};
|
|
28
|
+
__export(listMetaCacheMiddleware_exports, {
|
|
29
|
+
createListMetaCacheMiddleware: () => createListMetaCacheMiddleware
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(listMetaCacheMiddleware_exports);
|
|
32
|
+
var import_versionManager = require("../utils/versionManager");
|
|
33
|
+
const LIST_META_CACHE_TTL = 1e3 * 60 * 10;
|
|
34
|
+
function getErrorMessage(error) {
|
|
35
|
+
return error instanceof Error ? error.message : String(error);
|
|
36
|
+
}
|
|
37
|
+
function createListMetaCacheMiddleware(app) {
|
|
38
|
+
return async function listMetaCacheMiddleware(ctx, next) {
|
|
39
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
40
|
+
const cache = app.cache;
|
|
41
|
+
if (!cache || ((_a = ctx.action) == null ? void 0 : _a.resourceName) !== "collections" || ((_b = ctx.action) == null ? void 0 : _b.actionName) !== "listMeta") {
|
|
42
|
+
return next();
|
|
43
|
+
}
|
|
44
|
+
const currentRole = ((_c = ctx.state) == null ? void 0 : _c.currentRole) || "anonymous";
|
|
45
|
+
const appName = ctx.headers["x-app"] || "main";
|
|
46
|
+
const dataSource = ctx.headers["x-data-source"] || "main";
|
|
47
|
+
const locale = ctx.headers["x-locale"] || ctx.headers["accept-language"] || "en-US";
|
|
48
|
+
let cacheKey = "";
|
|
49
|
+
let version = 0;
|
|
50
|
+
try {
|
|
51
|
+
version = await import_versionManager.cacheVersionManager.getCollectionVersion(app);
|
|
52
|
+
cacheKey = `nb:cache:${appName}:meta:v${version}:ds:${dataSource}:role:${currentRole}:lang:${locale}`;
|
|
53
|
+
const cached = await cache.get(cacheKey);
|
|
54
|
+
if (cached !== void 0 && cached !== null) {
|
|
55
|
+
ctx.body = typeof cached === "string" ? JSON.parse(cached) : cached;
|
|
56
|
+
(_d = ctx.set) == null ? void 0 : _d.call(ctx, "X-Cache", "HIT");
|
|
57
|
+
(_e = ctx.set) == null ? void 0 : _e.call(ctx, "X-Collection-Version", String(version));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
} catch (err) {
|
|
61
|
+
app.logger.warn(`[ClusterManager] listMeta cache read skipped: ${getErrorMessage(err)}`);
|
|
62
|
+
}
|
|
63
|
+
await next();
|
|
64
|
+
if (ctx.status === 200 && ctx.body && cacheKey) {
|
|
65
|
+
try {
|
|
66
|
+
const valueToCache = typeof ctx.body === "string" ? ctx.body : JSON.stringify(ctx.body);
|
|
67
|
+
await cache.set(cacheKey, valueToCache, LIST_META_CACHE_TTL);
|
|
68
|
+
(_f = ctx.set) == null ? void 0 : _f.call(ctx, "X-Cache", "MISS");
|
|
69
|
+
(_g = ctx.set) == null ? void 0 : _g.call(ctx, "X-Collection-Version", String(version));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
app.logger.warn(`[ClusterManager] listMeta cache write skipped: ${getErrorMessage(err)}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
77
|
+
0 && (module.exports = {
|
|
78
|
+
createListMetaCacheMiddleware
|
|
79
|
+
});
|
|
@@ -41,6 +41,7 @@ __export(PackageManager_exports, {
|
|
|
41
41
|
module.exports = __toCommonJS(PackageManager_exports);
|
|
42
42
|
var import_child_process = require("child_process");
|
|
43
43
|
var import_redis = require("../utils/redis");
|
|
44
|
+
var import_node = require("../utils/node");
|
|
44
45
|
var import_fs = require("fs");
|
|
45
46
|
var import_path = __toESM(require("path"));
|
|
46
47
|
const SAFE_PKG_RE = /^(?:[a-zA-Z0-9_.@/-]|\[|\])+$/;
|
|
@@ -408,22 +409,25 @@ ${logs.join("\n")}`);
|
|
|
408
409
|
const redisClient = (0, import_redis.getRedisClient)(this.app);
|
|
409
410
|
const podName = process.env.POD_NAME || require("os").hostname();
|
|
410
411
|
if (redisClient) {
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
412
|
+
const statusPayload = JSON.stringify({
|
|
413
|
+
initStatus,
|
|
414
|
+
initProgressPercent,
|
|
415
|
+
initProgressLog,
|
|
416
|
+
lastInitAt: /* @__PURE__ */ new Date(),
|
|
417
|
+
lastInitLog: logs.join("\n"),
|
|
418
|
+
...extraValues
|
|
419
|
+
});
|
|
420
|
+
const keys = [`orchestrator:pkg-status:${podName}`, `cluster-manager:pkg-status:${(0, import_node.getLocalNodeId)(this.app)}`];
|
|
421
|
+
for (const key of keys) {
|
|
422
|
+
await redisClient.sendCommand([
|
|
423
|
+
"SET",
|
|
424
|
+
key,
|
|
425
|
+
statusPayload,
|
|
426
|
+
"EX",
|
|
427
|
+
"86400"
|
|
428
|
+
// expire after 1 day
|
|
429
|
+
]);
|
|
430
|
+
}
|
|
427
431
|
}
|
|
428
432
|
const repo = this.app.db.getRepository("workerPackagesConfigs");
|
|
429
433
|
let config = await repo.findOne();
|
package/dist/server/plugin.js
CHANGED
|
@@ -63,6 +63,9 @@ var import_k8s_adapter = require("./orchestrator/k8s-adapter");
|
|
|
63
63
|
var import_leader_election = require("./orchestrator/leader-election");
|
|
64
64
|
var import_package_manager = require("./actions/package-manager");
|
|
65
65
|
var import_PackageManager = require("./orchestrator/PackageManager");
|
|
66
|
+
var import_listMetaCacheMiddleware = require("./middlewares/listMetaCacheMiddleware");
|
|
67
|
+
var import_cacheInvalidationHooks = require("./hooks/cacheInvalidationHooks");
|
|
68
|
+
var import_doctor = require("./actions/doctor");
|
|
66
69
|
class PluginClusterManagerServer extends import_server.Plugin {
|
|
67
70
|
nodeRegistry;
|
|
68
71
|
orchestrator = null;
|
|
@@ -107,7 +110,7 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
107
110
|
try {
|
|
108
111
|
const customRaw = config.get("customPackages");
|
|
109
112
|
if (customRaw) custom = typeof customRaw === "string" ? JSON.parse(customRaw) : customRaw;
|
|
110
|
-
} catch {
|
|
113
|
+
} catch (err) {
|
|
111
114
|
}
|
|
112
115
|
const unique = (arr) => Array.from(new Set(arr.filter(Boolean)));
|
|
113
116
|
const packages = {
|
|
@@ -170,16 +173,51 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
170
173
|
if (!redis || !requestId) return;
|
|
171
174
|
const logData = await (0, import_cluster_nodes.readLocalLogs)(this.app, lines || 200);
|
|
172
175
|
const responseKey = `cluster-manager:log-response:${requestId}`;
|
|
176
|
+
await redis.sendCommand(["SET", responseKey, JSON.stringify(logData), "EX", "30"]);
|
|
177
|
+
this.app.logger.debug(`[ClusterManager] Served log request ${requestId} for ${targetNodeId}`);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
this.app.logger.error(`[ClusterManager] Error handling log request: ${err.message}`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
pubSub.subscribe(`cluster-manager:doctor-collect:${myNodeId}`, async (msg) => {
|
|
183
|
+
const redis = (0, import_redis.getRedisClient)(this.app);
|
|
184
|
+
let requestId = "";
|
|
185
|
+
try {
|
|
186
|
+
const parsed = typeof msg === "string" ? JSON.parse(msg) : msg;
|
|
187
|
+
requestId = parsed.requestId;
|
|
188
|
+
if (!redis || !requestId) return;
|
|
189
|
+
const snapshot = await (0, import_doctor.collectLocalDoctorSnapshot)(this.app, {
|
|
190
|
+
runId: parsed.runId,
|
|
191
|
+
sinceMs: parsed.sinceMs,
|
|
192
|
+
untilMs: parsed.untilMs,
|
|
193
|
+
maxLines: parsed.maxLines
|
|
194
|
+
});
|
|
173
195
|
await redis.sendCommand([
|
|
174
196
|
"SET",
|
|
175
|
-
|
|
176
|
-
JSON.stringify(
|
|
197
|
+
`cluster-manager:doctor-response:${requestId}`,
|
|
198
|
+
JSON.stringify(snapshot),
|
|
177
199
|
"EX",
|
|
178
|
-
"
|
|
200
|
+
"90"
|
|
179
201
|
]);
|
|
180
|
-
this.app.logger.debug(`[ClusterManager] Served
|
|
202
|
+
this.app.logger.debug(`[ClusterManager] Served doctor snapshot request ${requestId}`);
|
|
181
203
|
} catch (err) {
|
|
182
|
-
|
|
204
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
205
|
+
this.app.logger.error(`[ClusterManager] Error handling doctor snapshot request: ${message}`);
|
|
206
|
+
if (redis && requestId) {
|
|
207
|
+
const fallback = {
|
|
208
|
+
nodeId: (0, import_node.getLocalNodeId)(this.app),
|
|
209
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
210
|
+
error: message
|
|
211
|
+
};
|
|
212
|
+
await redis.sendCommand([
|
|
213
|
+
"SET",
|
|
214
|
+
`cluster-manager:doctor-response:${requestId}`,
|
|
215
|
+
JSON.stringify(fallback),
|
|
216
|
+
"EX",
|
|
217
|
+
"90"
|
|
218
|
+
]).catch(() => {
|
|
219
|
+
});
|
|
220
|
+
}
|
|
183
221
|
}
|
|
184
222
|
});
|
|
185
223
|
pubSub.subscribe("cluster-manager.install-packages", async (payload) => {
|
|
@@ -195,12 +233,16 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
195
233
|
try {
|
|
196
234
|
let target = msg;
|
|
197
235
|
let mode = "hard";
|
|
236
|
+
let targetNodeId = "";
|
|
198
237
|
if (msg.startsWith("{")) {
|
|
199
238
|
const parsed = JSON.parse(msg);
|
|
200
|
-
target = parsed.hostname;
|
|
239
|
+
target = parsed.hostname || parsed.target || "";
|
|
240
|
+
targetNodeId = parsed.targetNodeId || "";
|
|
201
241
|
mode = parsed.mode || "hard";
|
|
202
242
|
}
|
|
203
|
-
|
|
243
|
+
const myNodeId2 = (0, import_node.getLocalNodeId)(this.app);
|
|
244
|
+
const shouldRestart = targetNodeId ? targetNodeId === myNodeId2 : target === os.hostname() || target === "*";
|
|
245
|
+
if (shouldRestart) {
|
|
204
246
|
this.app.logger.warn(`[ClusterManager] Received ${mode} restart command for node ${os.hostname()}...`);
|
|
205
247
|
setTimeout(async () => {
|
|
206
248
|
try {
|
|
@@ -241,6 +283,10 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
241
283
|
name: "clusterManagerCluster",
|
|
242
284
|
actions: import_cluster_nodes.clusterActions
|
|
243
285
|
});
|
|
286
|
+
this.app.resourcer.define({
|
|
287
|
+
name: "clusterManagerDoctor",
|
|
288
|
+
actions: import_doctor.doctorActions
|
|
289
|
+
});
|
|
244
290
|
this.app.resourcer.define({
|
|
245
291
|
name: "clusterManagerQueue",
|
|
246
292
|
actions: import_event_queue_monitor.eventQueueActions
|
|
@@ -267,6 +313,12 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
267
313
|
before: "core",
|
|
268
314
|
after: "allow-manager"
|
|
269
315
|
});
|
|
316
|
+
const listMetaCacheMiddleware = (0, import_listMetaCacheMiddleware.createListMetaCacheMiddleware)(this.app);
|
|
317
|
+
this.app.resourcer.use(listMetaCacheMiddleware, {
|
|
318
|
+
tag: "listMetaCache",
|
|
319
|
+
after: "setCurrentRole"
|
|
320
|
+
});
|
|
321
|
+
(0, import_cacheInvalidationHooks.registerCacheHooks)(this.app);
|
|
270
322
|
this.app.use(async (ctx, next) => {
|
|
271
323
|
if (ctx.path === "/api/clusterManager:health" && (ctx.method === "GET" || ctx.method === "HEAD")) {
|
|
272
324
|
ctx.body = {
|
|
@@ -286,6 +338,7 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
286
338
|
"clusterManagerRedis:*",
|
|
287
339
|
"clusterManagerAclCache:*",
|
|
288
340
|
"clusterManagerCluster:*",
|
|
341
|
+
"clusterManagerDoctor:*",
|
|
289
342
|
"clusterManagerQueue:*",
|
|
290
343
|
"clusterManagerLock:*",
|
|
291
344
|
"clusterManagerCacheMgr:*",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const cacheVersionManager: {
|
|
2
|
+
getCollectionVersion(app: any): Promise<number>;
|
|
3
|
+
incrementCollectionVersion(app: any): Promise<number>;
|
|
4
|
+
getSchemaVersion(app: any): Promise<number>;
|
|
5
|
+
incrementSchemaVersion(app: any): Promise<number>;
|
|
6
|
+
getAclVersion(app: any, roleName: string): Promise<number>;
|
|
7
|
+
incrementAclVersion(app: any, roleName: string): Promise<number>;
|
|
8
|
+
incrementAllAclVersions(app: any): Promise<number>;
|
|
9
|
+
getGlobalAclVersion(app: any): Promise<number>;
|
|
10
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var versionManager_exports = {};
|
|
28
|
+
__export(versionManager_exports, {
|
|
29
|
+
cacheVersionManager: () => cacheVersionManager
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(versionManager_exports);
|
|
32
|
+
var import_redis = require("./redis");
|
|
33
|
+
const localCounters = /* @__PURE__ */ new Map();
|
|
34
|
+
async function getVersion(app, key) {
|
|
35
|
+
const redis = (0, import_redis.getRedisClient)(app);
|
|
36
|
+
if (redis) {
|
|
37
|
+
try {
|
|
38
|
+
const val = await redis.sendCommand(["GET", key]);
|
|
39
|
+
return val ? parseInt(val, 10) : 1;
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return localCounters.get(key) || 1;
|
|
44
|
+
}
|
|
45
|
+
async function incrementVersion(app, key) {
|
|
46
|
+
const redis = (0, import_redis.getRedisClient)(app);
|
|
47
|
+
if (redis) {
|
|
48
|
+
try {
|
|
49
|
+
const val = await redis.sendCommand(["INCR", key]);
|
|
50
|
+
return parseInt(val, 10);
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const next = (localCounters.get(key) || 1) + 1;
|
|
55
|
+
localCounters.set(key, next);
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
const cacheVersionManager = {
|
|
59
|
+
async getCollectionVersion(app) {
|
|
60
|
+
return getVersion(app, "nocobase:version:collections");
|
|
61
|
+
},
|
|
62
|
+
async incrementCollectionVersion(app) {
|
|
63
|
+
app.logger.info("[ClusterManager] Incrementing collections cache version due to schema change");
|
|
64
|
+
return incrementVersion(app, "nocobase:version:collections");
|
|
65
|
+
},
|
|
66
|
+
async getSchemaVersion(app) {
|
|
67
|
+
return getVersion(app, "nocobase:version:schemas");
|
|
68
|
+
},
|
|
69
|
+
async incrementSchemaVersion(app) {
|
|
70
|
+
app.logger.info("[ClusterManager] Incrementing uiSchemas cache version due to UI change");
|
|
71
|
+
return incrementVersion(app, "nocobase:version:schemas");
|
|
72
|
+
},
|
|
73
|
+
async getAclVersion(app, roleName) {
|
|
74
|
+
return getVersion(app, `nocobase:version:acl:role:${roleName}`);
|
|
75
|
+
},
|
|
76
|
+
async incrementAclVersion(app, roleName) {
|
|
77
|
+
app.logger.info(`[ClusterManager] Incrementing ACL cache version for role: ${roleName}`);
|
|
78
|
+
return incrementVersion(app, `nocobase:version:acl:role:${roleName}`);
|
|
79
|
+
},
|
|
80
|
+
async incrementAllAclVersions(app) {
|
|
81
|
+
app.logger.info("[ClusterManager] Incrementing global ACL cache version");
|
|
82
|
+
return incrementVersion(app, "nocobase:version:acl:global");
|
|
83
|
+
},
|
|
84
|
+
async getGlobalAclVersion(app) {
|
|
85
|
+
return getVersion(app, "nocobase:version:acl:global");
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
89
|
+
0 && (module.exports = {
|
|
90
|
+
cacheVersionManager
|
|
91
|
+
});
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "plugin-cluster-manager",
|
|
3
|
-
"displayName": "Cluster Manager",
|
|
4
|
-
"displayName.zh-CN": "Cluster Manager",
|
|
5
|
-
"description": "Cluster node tracking, task orchestration, worker management, and package distribution",
|
|
6
|
-
"version": "1.1.
|
|
7
|
-
"license": "Apache-2.0",
|
|
8
|
-
"main": "./dist/server/index.js",
|
|
9
|
-
"keywords": [
|
|
10
|
-
"Monitoring",
|
|
11
|
-
"DevOps"
|
|
12
|
-
],
|
|
13
|
-
"files": [
|
|
14
|
-
"dist",
|
|
15
|
-
"src",
|
|
16
|
-
"client.js",
|
|
17
|
-
"server.js",
|
|
18
|
-
"client.d.ts",
|
|
19
|
-
"server.d.ts"
|
|
20
|
-
],
|
|
21
|
-
"peerDependencies": {
|
|
22
|
-
"@nocobase/client": "2.x",
|
|
23
|
-
"@nocobase/server": "2.x",
|
|
24
|
-
"@nocobase/database": "2.x"
|
|
25
|
-
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
"@nocobase/lock-manager": "2.x"
|
|
28
|
-
},
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"@kubernetes/client-node": "^0.22.3",
|
|
31
|
-
"dockerode": "^3.3.5",
|
|
32
|
-
"redis": "^5.10.0",
|
|
33
|
-
"redlock": "5.0.0-beta.2",
|
|
34
|
-
"uuid": "^9.0.1"
|
|
35
|
-
},
|
|
36
|
-
"nocobase": {
|
|
37
|
-
"supportedVersions": [
|
|
38
|
-
"2.x"
|
|
39
|
-
],
|
|
40
|
-
"editionLevel": 0
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "plugin-cluster-manager",
|
|
3
|
+
"displayName": "Cluster Manager",
|
|
4
|
+
"displayName.zh-CN": "Cluster Manager",
|
|
5
|
+
"description": "Cluster node tracking, task orchestration, worker management, and package distribution",
|
|
6
|
+
"version": "1.1.11",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"main": "./dist/server/index.js",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"Monitoring",
|
|
11
|
+
"DevOps"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src",
|
|
16
|
+
"client.js",
|
|
17
|
+
"server.js",
|
|
18
|
+
"client.d.ts",
|
|
19
|
+
"server.d.ts"
|
|
20
|
+
],
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"@nocobase/client": "2.x",
|
|
23
|
+
"@nocobase/server": "2.x",
|
|
24
|
+
"@nocobase/database": "2.x"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@nocobase/lock-manager": "2.x"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@kubernetes/client-node": "^0.22.3",
|
|
31
|
+
"dockerode": "^3.3.5",
|
|
32
|
+
"redis": "^5.10.0",
|
|
33
|
+
"redlock": "5.0.0-beta.2",
|
|
34
|
+
"uuid": "^9.0.1"
|
|
35
|
+
},
|
|
36
|
+
"nocobase": {
|
|
37
|
+
"supportedVersions": [
|
|
38
|
+
"2.x"
|
|
39
|
+
],
|
|
40
|
+
"editionLevel": 0
|
|
41
|
+
}
|
|
42
42
|
}
|
package/server.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/server/index.js');
|