plugin-cluster-manager 1.1.7 → 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.
Files changed (108) hide show
  1. package/client.js +1 -0
  2. package/dist/client/AclCacheManager.d.ts +2 -0
  3. package/dist/client/CacheMonitor.d.ts +2 -0
  4. package/dist/client/ClusterManagerLayout.d.ts +2 -0
  5. package/dist/client/ClusterNodes.d.ts +2 -0
  6. package/dist/client/ContainerOrchestrator.d.ts +2 -0
  7. package/dist/client/Doctor.d.ts +2 -0
  8. package/dist/client/EventQueueMonitor.d.ts +2 -0
  9. package/dist/client/LockMonitor.d.ts +2 -0
  10. package/dist/client/NginxCacheManager.d.ts +2 -0
  11. package/dist/client/PackageInstaller.d.ts +2 -0
  12. package/dist/client/PluginOperations.d.ts +2 -0
  13. package/dist/client/RedisMonitor.d.ts +2 -0
  14. package/dist/client/TaskManager.d.ts +2 -0
  15. package/dist/client/WorkflowExecutions.d.ts +2 -0
  16. package/dist/client/index.d.ts +5 -0
  17. package/dist/client/index.js +1 -1
  18. package/dist/client/utils/clientSafeCache.d.ts +3 -0
  19. package/dist/client/utils/requestDedupInterceptor.d.ts +2 -0
  20. package/dist/client/utils.d.ts +12 -0
  21. package/dist/externalVersion.js +5 -5
  22. package/dist/index.d.ts +2 -0
  23. package/dist/locale/en-US.json +97 -1
  24. package/dist/locale/vi-VN.json +98 -1
  25. package/dist/locale/zh-CN.json +98 -1
  26. package/dist/server/actions/acl-cache.d.ts +53 -0
  27. package/dist/server/actions/acl-cache.js +1 -1
  28. package/dist/server/actions/cache-monitor.d.ts +33 -0
  29. package/dist/server/actions/cache-monitor.js +301 -0
  30. package/dist/server/actions/cluster-nodes.d.ts +64 -0
  31. package/dist/server/actions/cluster-nodes.js +394 -10
  32. package/dist/server/actions/doctor.d.ts +82 -0
  33. package/dist/server/actions/doctor.js +1250 -0
  34. package/dist/server/actions/event-queue-monitor.d.ts +13 -0
  35. package/dist/server/actions/lock-monitor.d.ts +19 -0
  36. package/dist/server/actions/orchestrator.d.ts +58 -0
  37. package/dist/server/actions/package-manager.d.ts +6 -0
  38. package/dist/server/actions/plugin-operations.d.ts +6 -0
  39. package/dist/server/actions/redis-monitor.d.ts +12 -0
  40. package/dist/server/actions/tasks.d.ts +7 -0
  41. package/dist/server/actions/workflow-executions.d.ts +7 -0
  42. package/dist/server/adapters/redis-lock-adapter.d.ts +15 -0
  43. package/dist/server/adapters/redis-node-registry.d.ts +12 -0
  44. package/dist/server/adapters/redis-pubsub-adapter.d.ts +16 -0
  45. package/dist/server/collections/app.d.ts +8 -0
  46. package/dist/server/collections/cluster-manager-acl-cache.d.ts +22 -0
  47. package/dist/server/collections/cluster-manager-cache-mgr.d.ts +22 -0
  48. package/dist/server/collections/cluster-manager-cluster.d.ts +22 -0
  49. package/dist/server/collections/cluster-manager-doctor-runs.d.ts +3 -0
  50. package/dist/server/collections/cluster-manager-doctor-runs.js +52 -0
  51. package/dist/server/collections/cluster-manager-doctor.d.ts +18 -0
  52. package/dist/server/collections/cluster-manager-doctor.js +44 -0
  53. package/dist/server/collections/cluster-manager-lock.d.ts +22 -0
  54. package/dist/server/collections/cluster-manager-plugins.d.ts +18 -0
  55. package/dist/server/collections/cluster-manager-queue.d.ts +22 -0
  56. package/dist/server/collections/cluster-manager-redis.d.ts +22 -0
  57. package/dist/server/collections/cluster-manager-workflow.d.ts +22 -0
  58. package/dist/server/collections/cluster-manager.d.ts +22 -0
  59. package/dist/server/collections/orchestrator-settings.d.ts +59 -0
  60. package/dist/server/collections/orchestrator-stacks.d.ts +102 -0
  61. package/dist/server/collections/worker-orchestrator.d.ts +22 -0
  62. package/dist/server/collections/worker-packages-configs.d.ts +3 -0
  63. package/dist/server/collections/worker-packages.d.ts +22 -0
  64. package/dist/server/hooks/cacheInvalidationHooks.d.ts +1 -0
  65. package/dist/server/hooks/cacheInvalidationHooks.js +81 -0
  66. package/dist/server/index.d.ts +1 -0
  67. package/dist/server/middlewares/listMetaCacheMiddleware.d.ts +2 -0
  68. package/dist/server/middlewares/listMetaCacheMiddleware.js +79 -0
  69. package/dist/server/orchestrator/PackageManager.d.ts +39 -0
  70. package/dist/server/orchestrator/PackageManager.js +83 -27
  71. package/dist/server/orchestrator/docker-adapter.d.ts +41 -0
  72. package/dist/server/orchestrator/index.d.ts +4 -0
  73. package/dist/server/orchestrator/k8s-adapter.d.ts +50 -0
  74. package/dist/server/orchestrator/leader-election.d.ts +48 -0
  75. package/dist/server/orchestrator/types.d.ts +84 -0
  76. package/dist/server/plugin.d.ts +26 -0
  77. package/dist/server/plugin.js +70 -8
  78. package/dist/server/utils/node.d.ts +6 -0
  79. package/dist/server/utils/redis.d.ts +29 -0
  80. package/dist/server/utils/versionManager.d.ts +10 -0
  81. package/dist/server/utils/versionManager.js +91 -0
  82. package/dist/shared/packages.d.ts +23 -0
  83. package/package.json +41 -41
  84. package/server.js +1 -0
  85. package/src/client/CacheMonitor.tsx +166 -179
  86. package/src/client/ClusterManagerLayout.tsx +48 -42
  87. package/src/client/ClusterNodes.tsx +691 -418
  88. package/src/client/Doctor.tsx +559 -0
  89. package/src/client/NginxCacheManager.tsx +415 -0
  90. package/src/client/PluginOperations.tsx +234 -234
  91. package/src/client/index.tsx +22 -14
  92. package/src/client/utils/clientSafeCache.ts +41 -0
  93. package/src/client/utils/requestDedupInterceptor.ts +213 -0
  94. package/src/locale/en-US.json +97 -1
  95. package/src/locale/vi-VN.json +98 -1
  96. package/src/locale/zh-CN.json +98 -1
  97. package/src/server/__tests__/doctor.test.ts +53 -0
  98. package/src/server/actions/acl-cache.ts +272 -272
  99. package/src/server/actions/cache-monitor.ts +453 -116
  100. package/src/server/actions/cluster-nodes.ts +882 -378
  101. package/src/server/actions/doctor.ts +1540 -0
  102. package/src/server/collections/cluster-manager-doctor-runs.ts +23 -0
  103. package/src/server/collections/cluster-manager-doctor.ts +19 -0
  104. package/src/server/hooks/cacheInvalidationHooks.ts +58 -0
  105. package/src/server/middlewares/listMetaCacheMiddleware.ts +55 -0
  106. package/src/server/orchestrator/PackageManager.ts +19 -15
  107. package/src/server/plugin.ts +353 -263
  108. package/src/server/utils/versionManager.ts +69 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Leader Election using native Redis commands
3
+ *
4
+ * Ensures only ONE app instance runs orchestrator write operations (scale, start, stop).
5
+ * Other instances remain read-only followers.
6
+ *
7
+ * Flow:
8
+ * 1. After app starts, tryBecomeLeader() attempts to acquire the Redis lock using SET NX PX.
9
+ * 2. If acquired → this node is leader; a renewal timer extends the lock periodically using a Lua script.
10
+ * 3. If not acquired → this node is follower; it retries periodically.
11
+ * 4. On app stop → release the lock gracefully using a Lua script.
12
+ * 5. On crash → lock auto-expires after TTL.
13
+ */
14
+ export declare class LeaderElection {
15
+ private app;
16
+ private renewTimer;
17
+ private retryTimer;
18
+ private _isLeader;
19
+ private _leaderId;
20
+ private standaloneMode;
21
+ private enabled;
22
+ private _disabledReason;
23
+ private redis;
24
+ constructor(app: any, options?: {
25
+ standaloneMode?: boolean;
26
+ enabled?: boolean;
27
+ disabledReason?: string;
28
+ });
29
+ get isLeader(): boolean;
30
+ get leaderId(): string;
31
+ get disabledReason(): string;
32
+ /**
33
+ * Initialize Redis client. Must be called after Redis is connected.
34
+ */
35
+ init(): Promise<boolean>;
36
+ /**
37
+ * Attempt to become the orchestrator leader.
38
+ * If successful, starts a renewal loop.
39
+ * If not, starts a retry loop.
40
+ */
41
+ tryBecomeLeader(): Promise<boolean>;
42
+ /**
43
+ * Gracefully release leadership.
44
+ */
45
+ release(): Promise<void>;
46
+ private startRenewal;
47
+ private startRetry;
48
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Orchestrator Adapter Interface
3
+ *
4
+ * Abstraction layer for container runtimes (Docker, K8s, Swarm).
5
+ * Each adapter implements this interface so the plugin logic is runtime-agnostic.
6
+ */
7
+ export interface ContainerInfo {
8
+ id: string;
9
+ name: string;
10
+ status: 'running' | 'stopped' | 'pending' | 'error' | 'creating' | 'exited';
11
+ image: string;
12
+ createdAt: Date;
13
+ cpu?: number;
14
+ memory?: number;
15
+ labels?: Record<string, string>;
16
+ }
17
+ export interface ScaleResult {
18
+ previousReplicas: number;
19
+ currentReplicas: number;
20
+ containersCreated?: string[];
21
+ containersRemoved?: string[];
22
+ }
23
+ export interface ContainerStats {
24
+ cpu: number;
25
+ memory: number;
26
+ memoryLimit: number;
27
+ networkRx: number;
28
+ networkTx: number;
29
+ }
30
+ export interface StackConfig {
31
+ id?: number;
32
+ name: string;
33
+ adapter: 'docker' | 'kubernetes';
34
+ image: string;
35
+ command?: string;
36
+ envVars?: Record<string, string>;
37
+ volumes?: string[];
38
+ networks?: string[];
39
+ resourceLimits?: {
40
+ memory?: string;
41
+ cpu?: string;
42
+ };
43
+ replicas: number;
44
+ desiredReplicas: number;
45
+ enabled: boolean;
46
+ namespace?: string;
47
+ deploymentName?: string;
48
+ serviceAccountName?: string;
49
+ imagePullPolicy?: string;
50
+ k8sContainerName?: string;
51
+ k8sEnv?: Record<string, any>[];
52
+ k8sEnvFrom?: Record<string, any>[];
53
+ k8sVolumeMounts?: Record<string, any>[];
54
+ k8sVolumes?: Record<string, any>[];
55
+ networkMode?: string;
56
+ restartPolicy?: string;
57
+ }
58
+ export interface IOrchestratorAdapter {
59
+ /** Human-readable adapter name */
60
+ readonly name: string;
61
+ /** Test connectivity to the runtime */
62
+ ping(): Promise<boolean>;
63
+ /** List containers managed by a stack */
64
+ listContainers(stack: StackConfig): Promise<ContainerInfo[]>;
65
+ /** Verify that a runtime container/pod belongs to the requested stack */
66
+ assertManagedByStack(stack: StackConfig, containerId: string): Promise<void>;
67
+ /** Scale stack to N replicas */
68
+ scale(stack: StackConfig, replicas: number): Promise<ScaleResult>;
69
+ /** Start a stopped container */
70
+ startContainer(containerId: string): Promise<void>;
71
+ /** Gracefully stop a running container */
72
+ stopContainer(containerId: string, timeoutSecs?: number): Promise<void>;
73
+ /** Remove/delete a container (force) */
74
+ removeContainer(containerId: string): Promise<void>;
75
+ /** Get real-time resource stats for a container */
76
+ getStats(containerId: string): Promise<ContainerStats>;
77
+ /** Get tail logs from a container */
78
+ getLogs(containerId: string, tail?: number): Promise<string>;
79
+ /** List available networks (if supported by adapter) */
80
+ listNetworks?(): Promise<{
81
+ id: string;
82
+ name: string;
83
+ }[]>;
84
+ }
@@ -0,0 +1,26 @@
1
+ import { Plugin } from '@nocobase/server';
2
+ import { RedisNodeRegistry } from './adapters/redis-node-registry';
3
+ import type { IOrchestratorAdapter } from './orchestrator/types';
4
+ import { LeaderElection } from './orchestrator/leader-election';
5
+ export declare class PluginClusterManagerServer extends Plugin {
6
+ nodeRegistry: RedisNodeRegistry;
7
+ orchestrator: IOrchestratorAdapter | null;
8
+ leaderElection: LeaderElection | null;
9
+ beforeLoad(): Promise<void>;
10
+ load(): Promise<void>;
11
+ private registerPubSubAdapter;
12
+ /**
13
+ * Initialize the Container Orchestrator subsystem.
14
+ * Config is loaded from DB (orchestratorSettings collection) first,
15
+ * then falls back to ORCHESTRATOR_ADAPTER env var.
16
+ * This allows manual configuration via the NocoBase admin UI.
17
+ */
18
+ private initOrchestrator;
19
+ /**
20
+ * Connect (or reconnect) the orchestrator adapter based on settings.
21
+ * Can be called at startup or when user saves new settings via UI.
22
+ */
23
+ connectAdapter(settings: any): Promise<boolean>;
24
+ private isWorkerOnlyNode;
25
+ }
26
+ export default PluginClusterManagerServer;
@@ -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;
@@ -71,6 +74,15 @@ class PluginClusterManagerServer extends import_server.Plugin {
71
74
  await this.db.import({ directory: import_path.default.resolve(__dirname, "collections") });
72
75
  }
73
76
  async load() {
77
+ this.db.extendCollection({
78
+ name: "attachments",
79
+ fields: [
80
+ {
81
+ type: "bigInt",
82
+ name: "createdById"
83
+ }
84
+ ]
85
+ });
74
86
  this.nodeRegistry = new import_redis_node_registry.RedisNodeRegistry(this.app);
75
87
  this.app.on("afterStart", () => {
76
88
  var _a;
@@ -98,7 +110,7 @@ class PluginClusterManagerServer extends import_server.Plugin {
98
110
  try {
99
111
  const customRaw = config.get("customPackages");
100
112
  if (customRaw) custom = typeof customRaw === "string" ? JSON.parse(customRaw) : customRaw;
101
- } catch {
113
+ } catch (err) {
102
114
  }
103
115
  const unique = (arr) => Array.from(new Set(arr.filter(Boolean)));
104
116
  const packages = {
@@ -161,16 +173,51 @@ class PluginClusterManagerServer extends import_server.Plugin {
161
173
  if (!redis || !requestId) return;
162
174
  const logData = await (0, import_cluster_nodes.readLocalLogs)(this.app, lines || 200);
163
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
+ });
164
195
  await redis.sendCommand([
165
196
  "SET",
166
- responseKey,
167
- JSON.stringify(logData),
197
+ `cluster-manager:doctor-response:${requestId}`,
198
+ JSON.stringify(snapshot),
168
199
  "EX",
169
- "30"
200
+ "90"
170
201
  ]);
171
- this.app.logger.debug(`[ClusterManager] Served log request ${requestId} for ${targetNodeId}`);
202
+ this.app.logger.debug(`[ClusterManager] Served doctor snapshot request ${requestId}`);
172
203
  } catch (err) {
173
- this.app.logger.error(`[ClusterManager] Error handling log request: ${err.message}`);
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
+ }
174
221
  }
175
222
  });
176
223
  pubSub.subscribe("cluster-manager.install-packages", async (payload) => {
@@ -186,12 +233,16 @@ class PluginClusterManagerServer extends import_server.Plugin {
186
233
  try {
187
234
  let target = msg;
188
235
  let mode = "hard";
236
+ let targetNodeId = "";
189
237
  if (msg.startsWith("{")) {
190
238
  const parsed = JSON.parse(msg);
191
- target = parsed.hostname;
239
+ target = parsed.hostname || parsed.target || "";
240
+ targetNodeId = parsed.targetNodeId || "";
192
241
  mode = parsed.mode || "hard";
193
242
  }
194
- if (target === os.hostname() || target === "*") {
243
+ const myNodeId2 = (0, import_node.getLocalNodeId)(this.app);
244
+ const shouldRestart = targetNodeId ? targetNodeId === myNodeId2 : target === os.hostname() || target === "*";
245
+ if (shouldRestart) {
195
246
  this.app.logger.warn(`[ClusterManager] Received ${mode} restart command for node ${os.hostname()}...`);
196
247
  setTimeout(async () => {
197
248
  try {
@@ -232,6 +283,10 @@ class PluginClusterManagerServer extends import_server.Plugin {
232
283
  name: "clusterManagerCluster",
233
284
  actions: import_cluster_nodes.clusterActions
234
285
  });
286
+ this.app.resourcer.define({
287
+ name: "clusterManagerDoctor",
288
+ actions: import_doctor.doctorActions
289
+ });
235
290
  this.app.resourcer.define({
236
291
  name: "clusterManagerQueue",
237
292
  actions: import_event_queue_monitor.eventQueueActions
@@ -258,6 +313,12 @@ class PluginClusterManagerServer extends import_server.Plugin {
258
313
  before: "core",
259
314
  after: "allow-manager"
260
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);
261
322
  this.app.use(async (ctx, next) => {
262
323
  if (ctx.path === "/api/clusterManager:health" && (ctx.method === "GET" || ctx.method === "HEAD")) {
263
324
  ctx.body = {
@@ -277,6 +338,7 @@ class PluginClusterManagerServer extends import_server.Plugin {
277
338
  "clusterManagerRedis:*",
278
339
  "clusterManagerAclCache:*",
279
340
  "clusterManagerCluster:*",
341
+ "clusterManagerDoctor:*",
280
342
  "clusterManagerQueue:*",
281
343
  "clusterManagerLock:*",
282
344
  "clusterManagerCacheMgr:*",
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Generate a universally unique identifier for this specific Node.js process.
3
+ * Combines app name, worker mode, hostname, port, and PID to ensure uniqueness
4
+ * even when multiple workers run on the exact same host.
5
+ */
6
+ export declare function getLocalNodeId(app: any): string;
@@ -0,0 +1,29 @@
1
+ import { Context } from '@nocobase/actions';
2
+ export declare function getRedisClient(app?: any): any;
3
+ /**
4
+ * Get the shared Redis connection from the app's connection manager.
5
+ * Returns undefined if Redis is not configured.
6
+ */
7
+ export declare function getRedis(ctx: Context): any;
8
+ /**
9
+ * Get Redis connection or throw 503 if not available.
10
+ */
11
+ export declare function getRedisOrThrow(ctx: Context): any;
12
+ /**
13
+ * Scan Redis keys using SCAN (cursor-based) instead of the blocking KEYS command.
14
+ * Safe for production use — never blocks the Redis event loop.
15
+ *
16
+ * @param redis - Redis client instance
17
+ * @param pattern - Glob pattern to match keys (e.g. "acl:can:*")
18
+ * @param batchSize - Number of keys to scan per iteration (default 200)
19
+ */
20
+ export declare function scanKeys(redis: any, pattern: string, batchSize?: number): Promise<string[]>;
21
+ /**
22
+ * Delete keys in chunked batches to avoid exceeding Redis command argument limits.
23
+ *
24
+ * @param redis - Redis client instance
25
+ * @param keys - Array of keys to delete
26
+ * @param chunkSize - Max keys per DEL command (default 500)
27
+ * @returns Total number of keys deleted
28
+ */
29
+ export declare function deleteKeysChunked(redis: any, keys: string[], chunkSize?: number): Promise<number>;
@@ -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
+ });
@@ -0,0 +1,23 @@
1
+ export declare const DEFAULT_WORKER_PACKAGES: {
2
+ apt: string[];
3
+ python: string[];
4
+ npm: string[];
5
+ };
6
+ export interface WorkerPackageMap {
7
+ apt?: string[];
8
+ npm?: string[];
9
+ python?: string[];
10
+ }
11
+ export interface CustomPackageMap {
12
+ python?: string[];
13
+ node?: string[];
14
+ npm?: string[];
15
+ }
16
+ export declare function normalizePackages(packages?: Array<string | undefined>): string[];
17
+ export declare function parsePackageText(value: unknown, fallback?: string[]): string[];
18
+ export declare function formatPackageText(packages?: string[]): string;
19
+ export declare function packagesFromConfig(config: {
20
+ aptPackages?: unknown;
21
+ pythonPackages?: unknown;
22
+ npmPackages?: unknown;
23
+ }): WorkerPackageMap;
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",
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');