plugin-cluster-manager 1.1.10 → 1.1.13

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 (119) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/client.js +1 -0
  4. package/dist/client/index.js +1 -1
  5. package/dist/client-v2/914.5dc1105cf3ada6a6.js +10 -0
  6. package/dist/client-v2/index.js +10 -0
  7. package/dist/externalVersion.js +6 -5
  8. package/dist/locale/en-US.json +138 -28
  9. package/dist/locale/vi-VN.json +139 -28
  10. package/dist/locale/zh-CN.json +140 -28
  11. package/dist/server/actions/cache-monitor.js +301 -0
  12. package/dist/server/actions/cluster-nodes.js +391 -11
  13. package/dist/server/actions/doctor.js +1246 -0
  14. package/dist/server/actions/orchestrator.js +37 -0
  15. package/dist/server/actions/queue-mappings.js +107 -0
  16. package/dist/server/collections/cluster-manager-doctor-runs.js +52 -0
  17. package/dist/server/collections/cluster-manager-doctor.js +44 -0
  18. package/dist/server/collections/worker-queue-mappings.js +106 -0
  19. package/dist/server/hooks/cacheInvalidationHooks.js +81 -0
  20. package/dist/server/middlewares/listMetaCacheMiddleware.js +79 -0
  21. package/dist/server/orchestrator/PackageManager.js +21 -24
  22. package/dist/server/orchestrator/docker-adapter.js +49 -27
  23. package/dist/server/plugin.js +71 -16
  24. package/dist/server/queue-scanner.js +141 -0
  25. package/dist/server/utils/node.js +30 -2
  26. package/dist/server/utils/versionManager.js +91 -0
  27. package/package.json +9 -5
  28. package/server.js +1 -0
  29. package/src/client/AclCacheManager.tsx +292 -287
  30. package/src/client/CacheMonitor.tsx +166 -179
  31. package/src/client/ClusterManagerLayout.tsx +54 -42
  32. package/src/client/ClusterNodes.tsx +698 -418
  33. package/src/client/ContainerOrchestrator.tsx +184 -102
  34. package/src/client/Doctor.tsx +559 -0
  35. package/src/client/NginxCacheManager.tsx +415 -0
  36. package/src/client/PluginOperations.tsx +234 -234
  37. package/src/client/QueueAssignment.tsx +355 -0
  38. package/src/client/TaskManager.tsx +194 -187
  39. package/src/client/WorkflowExecutions.tsx +243 -238
  40. package/src/client/index.tsx +22 -14
  41. package/src/client/utils/clientSafeCache.ts +41 -0
  42. package/src/client/utils/requestDedupInterceptor.ts +213 -0
  43. package/src/client-v2/plugin.tsx +24 -0
  44. package/src/locale/en-US.json +138 -28
  45. package/src/locale/vi-VN.json +139 -28
  46. package/src/locale/zh-CN.json +140 -28
  47. package/src/server/__tests__/doctor.test.ts +53 -0
  48. package/src/server/actions/acl-cache.ts +272 -272
  49. package/src/server/actions/cache-monitor.ts +453 -116
  50. package/src/server/actions/cluster-nodes.ts +878 -378
  51. package/src/server/actions/doctor.ts +1536 -0
  52. package/src/server/actions/orchestrator.ts +54 -2
  53. package/src/server/actions/queue-mappings.ts +94 -0
  54. package/src/server/collections/cluster-manager-doctor-runs.ts +23 -0
  55. package/src/server/collections/cluster-manager-doctor.ts +19 -0
  56. package/src/server/collections/worker-queue-mappings.ts +85 -0
  57. package/src/server/hooks/cacheInvalidationHooks.ts +58 -0
  58. package/src/server/middlewares/listMetaCacheMiddleware.ts +55 -0
  59. package/src/server/orchestrator/PackageManager.ts +20 -24
  60. package/src/server/orchestrator/docker-adapter.ts +74 -37
  61. package/src/server/plugin.ts +347 -270
  62. package/src/server/queue-scanner.ts +154 -0
  63. package/src/server/utils/node.ts +48 -0
  64. package/src/server/utils/versionManager.ts +69 -0
  65. package/dist/client/AclCacheManager.d.ts +0 -2
  66. package/dist/client/CacheMonitor.d.ts +0 -2
  67. package/dist/client/ClusterManagerLayout.d.ts +0 -2
  68. package/dist/client/ClusterNodes.d.ts +0 -2
  69. package/dist/client/ContainerOrchestrator.d.ts +0 -2
  70. package/dist/client/EventQueueMonitor.d.ts +0 -2
  71. package/dist/client/LockMonitor.d.ts +0 -2
  72. package/dist/client/PackageInstaller.d.ts +0 -2
  73. package/dist/client/PluginOperations.d.ts +0 -2
  74. package/dist/client/RedisMonitor.d.ts +0 -2
  75. package/dist/client/TaskManager.d.ts +0 -2
  76. package/dist/client/WorkflowExecutions.d.ts +0 -2
  77. package/dist/client/index.d.ts +0 -5
  78. package/dist/client/utils.d.ts +0 -12
  79. package/dist/index.d.ts +0 -2
  80. package/dist/server/actions/acl-cache.d.ts +0 -53
  81. package/dist/server/actions/cache-monitor.d.ts +0 -23
  82. package/dist/server/actions/cluster-nodes.d.ts +0 -49
  83. package/dist/server/actions/event-queue-monitor.d.ts +0 -13
  84. package/dist/server/actions/lock-monitor.d.ts +0 -19
  85. package/dist/server/actions/orchestrator.d.ts +0 -58
  86. package/dist/server/actions/package-manager.d.ts +0 -6
  87. package/dist/server/actions/plugin-operations.d.ts +0 -6
  88. package/dist/server/actions/redis-monitor.d.ts +0 -12
  89. package/dist/server/actions/tasks.d.ts +0 -7
  90. package/dist/server/actions/workflow-executions.d.ts +0 -7
  91. package/dist/server/adapters/redis-lock-adapter.d.ts +0 -15
  92. package/dist/server/adapters/redis-node-registry.d.ts +0 -12
  93. package/dist/server/adapters/redis-pubsub-adapter.d.ts +0 -16
  94. package/dist/server/collections/app.d.ts +0 -8
  95. package/dist/server/collections/cluster-manager-acl-cache.d.ts +0 -22
  96. package/dist/server/collections/cluster-manager-cache-mgr.d.ts +0 -22
  97. package/dist/server/collections/cluster-manager-cluster.d.ts +0 -22
  98. package/dist/server/collections/cluster-manager-lock.d.ts +0 -22
  99. package/dist/server/collections/cluster-manager-plugins.d.ts +0 -18
  100. package/dist/server/collections/cluster-manager-queue.d.ts +0 -22
  101. package/dist/server/collections/cluster-manager-redis.d.ts +0 -22
  102. package/dist/server/collections/cluster-manager-workflow.d.ts +0 -22
  103. package/dist/server/collections/cluster-manager.d.ts +0 -22
  104. package/dist/server/collections/orchestrator-settings.d.ts +0 -59
  105. package/dist/server/collections/orchestrator-stacks.d.ts +0 -102
  106. package/dist/server/collections/worker-orchestrator.d.ts +0 -22
  107. package/dist/server/collections/worker-packages-configs.d.ts +0 -3
  108. package/dist/server/collections/worker-packages.d.ts +0 -22
  109. package/dist/server/orchestrator/PackageManager.d.ts +0 -39
  110. package/dist/server/orchestrator/docker-adapter.d.ts +0 -41
  111. package/dist/server/orchestrator/index.d.ts +0 -4
  112. package/dist/server/orchestrator/k8s-adapter.d.ts +0 -50
  113. package/dist/server/orchestrator/leader-election.d.ts +0 -48
  114. package/dist/server/orchestrator/types.d.ts +0 -84
  115. package/dist/server/plugin.d.ts +0 -26
  116. package/dist/server/utils/node.d.ts +0 -6
  117. package/dist/server/utils/redis.d.ts +0 -29
  118. package/dist/shared/packages.d.ts +0 -23
  119. /package/{dist/server/index.d.ts → src/client-v2/index.tsx} +0 -0
@@ -17,9 +17,7 @@ function getDockerode() {
17
17
  try {
18
18
  Dockerode = require('dockerode');
19
19
  } catch {
20
- throw new Error(
21
- '[DockerAdapter] "dockerode" package not found. Install it: yarn add dockerode',
22
- );
20
+ throw new Error('[DockerAdapter] "dockerode" package not found. Install it: yarn add dockerode');
23
21
  }
24
22
  }
25
23
  return Dockerode;
@@ -58,11 +56,7 @@ export class DockerAdapter implements IOrchestratorAdapter {
58
56
  const containers = await this.docker.listContainers({
59
57
  all: true,
60
58
  filters: {
61
- label: [
62
- `${LABEL_STACK}=${stack.name}`,
63
- `${LABEL_MANAGED}=true`,
64
- ...this.buildLabelFilters(this.workerLabels),
65
- ],
59
+ label: [`${LABEL_STACK}=${stack.name}`, `${LABEL_MANAGED}=true`, ...this.buildLabelFilters(this.workerLabels)],
66
60
  },
67
61
  });
68
62
 
@@ -105,9 +99,20 @@ export class DockerAdapter implements IOrchestratorAdapter {
105
99
  if (diff > 0) {
106
100
  // Scale UP
107
101
  let targetNetworks = stack.networks && stack.networks.length > 0 ? stack.networks : [];
108
- let targetNetworkMode = stack.networkMode;
102
+ const targetNetworkMode = stack.networkMode;
109
103
  let targetEnvVars = this.buildEnvArray(stack.envVars);
110
104
  let targetVolumes = stack.volumes || [];
105
+ // Default the worker image to whatever the app container is running, so
106
+ // workers stay version-locked with the app even when the stack record
107
+ // has a stale/empty image. An explicit stack.image still wins.
108
+ let targetImage = stack.image;
109
+ // Inherit the app container's startup command/entrypoint so workers boot
110
+ // identically (e.g. source-tarball extraction + `yarn start`). Without
111
+ // this, a worker created from the bare image runs the image default
112
+ // command, skips the app's bootstrap, never finishes booting, and never
113
+ // registers a heartbeat — so it never appears in Cluster Nodes.
114
+ let inheritedCmd: string[] | undefined;
115
+ let inheritedEntrypoint: string[] | undefined;
111
116
 
112
117
  // Auto-detect current container's configuration to inherit networks and env vars
113
118
  try {
@@ -115,50 +120,70 @@ export class DockerAdapter implements IOrchestratorAdapter {
115
120
  const myContainerId = os.hostname();
116
121
  const myContainer = this.docker.getContainer(myContainerId);
117
122
  const myInfo = await myContainer.inspect();
118
-
123
+
124
+ // Inherit the app container's image when the stack does not pin one
125
+ if (!targetImage && myInfo?.Config?.Image) {
126
+ targetImage = myInfo.Config.Image;
127
+ console.log('[DockerAdapter] Inherited image from app container:', targetImage);
128
+ }
129
+
119
130
  // Always inherit Networks so worker can communicate with main app
120
131
  if (myInfo?.NetworkSettings?.Networks) {
121
132
  const inheritedNetworks = Object.keys(myInfo.NetworkSettings.Networks);
122
133
  targetNetworks = Array.from(new Set([...inheritedNetworks, ...targetNetworks]));
123
134
  console.log('[DockerAdapter] Inherited networks:', targetNetworks);
124
135
  }
125
-
136
+
126
137
  // Inherit Environment Variables and merge with stack.envVars
127
138
  if (myInfo?.Config?.Env) {
128
- const envDict: Record<string, string> = {};
129
- myInfo.Config.Env.forEach((e: string) => {
130
- const idx = e.indexOf('=');
131
- if (idx !== -1) {
132
- envDict[e.substring(0, idx)] = e.substring(idx + 1);
133
- }
134
- });
135
- // Overwrite with explicitly defined env vars
136
- Object.assign(envDict, stack.envVars || {});
137
-
138
- targetEnvVars = Object.entries(envDict).map(([k, v]) => `${k}=${v}`);
139
+ const envDict: Record<string, string> = {};
140
+ myInfo.Config.Env.forEach((e: string) => {
141
+ const idx = e.indexOf('=');
142
+ if (idx !== -1) {
143
+ envDict[e.substring(0, idx)] = e.substring(idx + 1);
144
+ }
145
+ });
146
+ // Overwrite with explicitly defined env vars
147
+ Object.assign(envDict, stack.envVars || {});
148
+
149
+ targetEnvVars = Object.entries(envDict).map(([k, v]) => `${k}=${v}`);
139
150
  }
140
151
  // Inherit Volumes (Binds)
141
152
  if (myInfo?.HostConfig?.Binds) {
142
153
  const inheritedBinds = myInfo.HostConfig.Binds as string[];
143
154
  targetVolumes = Array.from(new Set([...inheritedBinds, ...targetVolumes]));
144
155
  }
156
+ // Inherit the startup Cmd/Entrypoint so the worker runs the same bootstrap
157
+ // as the app container (used only when the stack pins no explicit command).
158
+ if (Array.isArray(myInfo?.Config?.Cmd) && myInfo.Config.Cmd.length > 0) {
159
+ inheritedCmd = myInfo.Config.Cmd as string[];
160
+ }
161
+ if (Array.isArray(myInfo?.Config?.Entrypoint) && myInfo.Config.Entrypoint.length > 0) {
162
+ inheritedEntrypoint = myInfo.Config.Entrypoint as string[];
163
+ }
145
164
  } catch (e: any) {
146
165
  // Ignore error if not running in a container or cannot inspect
147
166
  console.error('[DockerAdapter] Failed to inherit container config:', e.message);
148
167
  }
149
168
 
150
169
  // Automatically separate logs for workers to prevent log interleaving with the main app
151
- const hasLoggerBase = targetEnvVars.some(e => e.startsWith('LOGGER_BASE_PATH='));
170
+ const hasLoggerBase = targetEnvVars.some((e) => e.startsWith('LOGGER_BASE_PATH='));
152
171
  if (!hasLoggerBase) {
153
172
  targetEnvVars.push(`LOGGER_BASE_PATH=/app/nocobase/storage/logs/${stack.name}`);
154
173
  }
155
174
 
175
+ if (!targetImage) {
176
+ throw new Error(
177
+ `[DockerAdapter] No image configured for stack "${stack.name}" and the app container image could not be determined.`,
178
+ );
179
+ }
180
+
156
181
  for (let i = 0; i < diff; i++) {
157
182
  const suffix = `${Date.now()}-${Math.random().toString(36).substring(2, 6)}`;
158
183
  const containerName = `${stack.name}-${suffix}`;
159
184
 
160
185
  const createOpts: any = {
161
- Image: stack.image,
186
+ Image: targetImage,
162
187
  name: containerName,
163
188
  Env: targetEnvVars,
164
189
  Labels: {
@@ -174,6 +199,15 @@ export class DockerAdapter implements IOrchestratorAdapter {
174
199
 
175
200
  if (stack.command) {
176
201
  createOpts.Cmd = ['/bin/sh', '-c', stack.command];
202
+ } else {
203
+ // No explicit command: replay the app container's bootstrap so the
204
+ // worker boots NocoBase the same way and registers a heartbeat.
205
+ if (inheritedEntrypoint) {
206
+ createOpts.Entrypoint = inheritedEntrypoint;
207
+ }
208
+ if (inheritedCmd) {
209
+ createOpts.Cmd = inheritedCmd;
210
+ }
177
211
  }
178
212
 
179
213
  if (stack.resourceLimits?.memory) {
@@ -190,7 +224,7 @@ export class DockerAdapter implements IOrchestratorAdapter {
190
224
  createOpts.HostConfig.SecurityOpt = ['no-new-privileges:true'];
191
225
 
192
226
  const container = await this.docker.createContainer(createOpts);
193
-
227
+
194
228
  // Connect to additional networks before starting
195
229
  if (targetNetworks.length > 0) {
196
230
  const startIndex = targetNetworkMode ? 0 : 1;
@@ -199,7 +233,9 @@ export class DockerAdapter implements IOrchestratorAdapter {
199
233
  const net = this.docker.getNetwork(targetNetworks[i]);
200
234
  await net.connect({ Container: container.id });
201
235
  } catch (err: any) {
202
- console.warn(`[DockerAdapter] Failed to connect container ${container.id} to network ${targetNetworks[i]}: ${err.message}`);
236
+ console.warn(
237
+ `[DockerAdapter] Failed to connect container ${container.id} to network ${targetNetworks[i]}: ${err.message}`,
238
+ );
203
239
  }
204
240
  }
205
241
  }
@@ -209,9 +245,7 @@ export class DockerAdapter implements IOrchestratorAdapter {
209
245
  }
210
246
  } else if (diff < 0) {
211
247
  // Scale DOWN — remove newest first (LIFO)
212
- const sorted = running.sort(
213
- (a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
214
- );
248
+ const sorted = running.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
215
249
  const toRemove = sorted.slice(0, Math.abs(diff));
216
250
 
217
251
  for (const c of toRemove) {
@@ -334,14 +368,17 @@ export class DockerAdapter implements IOrchestratorAdapter {
334
368
  .split(',')
335
369
  .map((part) => part.trim())
336
370
  .filter(Boolean)
337
- .reduce((acc, part) => {
338
- const [key, ...valueParts] = part.split('=');
339
- const value = valueParts.join('=');
340
- if (key?.trim() && value?.trim()) {
341
- acc[key.trim()] = value.trim();
342
- }
343
- return acc;
344
- }, {} as Record<string, string>);
371
+ .reduce(
372
+ (acc, part) => {
373
+ const [key, ...valueParts] = part.split('=');
374
+ const value = valueParts.join('=');
375
+ if (key?.trim() && value?.trim()) {
376
+ acc[key.trim()] = value.trim();
377
+ }
378
+ return acc;
379
+ },
380
+ {} as Record<string, string>,
381
+ );
345
382
  }
346
383
 
347
384
  private labelsMatch(labels: Record<string, string>, expected: Record<string, string>): boolean {