plugin-cluster-manager 1.1.5 → 1.1.7

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 (76) hide show
  1. package/README.md +17 -5
  2. package/dist/client/index.js +1 -1
  3. package/dist/externalVersion.js +5 -5
  4. package/dist/locale/en-US.json +28 -4
  5. package/dist/locale/vi-VN.json +28 -4
  6. package/dist/locale/zh-CN.json +28 -4
  7. package/dist/server/actions/event-queue-monitor.js +123 -1
  8. package/dist/server/actions/orchestrator.js +1 -1
  9. package/dist/server/actions/plugin-operations.js +171 -0
  10. package/dist/server/collections/cluster-manager-plugins.js +44 -0
  11. package/dist/server/plugin.js +8 -2
  12. package/package.json +9 -4
  13. package/src/client/ClusterManagerLayout.tsx +16 -10
  14. package/src/client/EventQueueMonitor.tsx +349 -202
  15. package/src/client/PluginOperations.tsx +234 -0
  16. package/src/locale/en-US.json +28 -4
  17. package/src/locale/vi-VN.json +28 -4
  18. package/src/locale/zh-CN.json +28 -4
  19. package/src/server/actions/event-queue-monitor.ts +234 -95
  20. package/src/server/actions/orchestrator.ts +1 -1
  21. package/src/server/actions/plugin-operations.ts +151 -0
  22. package/src/server/collections/cluster-manager-plugins.ts +19 -0
  23. package/src/server/orchestrator/PackageManager.ts +84 -17
  24. package/src/server/plugin.ts +28 -20
  25. package/dist/client/AclCacheManager.d.ts +0 -2
  26. package/dist/client/CacheMonitor.d.ts +0 -2
  27. package/dist/client/ClusterManagerLayout.d.ts +0 -2
  28. package/dist/client/ClusterNodes.d.ts +0 -2
  29. package/dist/client/ContainerOrchestrator.d.ts +0 -2
  30. package/dist/client/EventQueueMonitor.d.ts +0 -2
  31. package/dist/client/LockMonitor.d.ts +0 -2
  32. package/dist/client/PackageInstaller.d.ts +0 -2
  33. package/dist/client/RedisMonitor.d.ts +0 -2
  34. package/dist/client/TaskManager.d.ts +0 -2
  35. package/dist/client/WorkflowExecutions.d.ts +0 -2
  36. package/dist/client/index.d.ts +0 -5
  37. package/dist/client/utils.d.ts +0 -12
  38. package/dist/index.d.ts +0 -2
  39. package/dist/server/actions/acl-cache.d.ts +0 -53
  40. package/dist/server/actions/cache-monitor.d.ts +0 -23
  41. package/dist/server/actions/cluster-nodes.d.ts +0 -49
  42. package/dist/server/actions/event-queue-monitor.d.ts +0 -13
  43. package/dist/server/actions/lock-monitor.d.ts +0 -19
  44. package/dist/server/actions/orchestrator.d.ts +0 -58
  45. package/dist/server/actions/package-manager.d.ts +0 -6
  46. package/dist/server/actions/redis-monitor.d.ts +0 -12
  47. package/dist/server/actions/tasks.d.ts +0 -7
  48. package/dist/server/actions/workflow-executions.d.ts +0 -7
  49. package/dist/server/adapters/redis-lock-adapter.d.ts +0 -15
  50. package/dist/server/adapters/redis-node-registry.d.ts +0 -12
  51. package/dist/server/adapters/redis-pubsub-adapter.d.ts +0 -16
  52. package/dist/server/collections/app.d.ts +0 -8
  53. package/dist/server/collections/cluster-manager-acl-cache.d.ts +0 -22
  54. package/dist/server/collections/cluster-manager-cache-mgr.d.ts +0 -22
  55. package/dist/server/collections/cluster-manager-cluster.d.ts +0 -22
  56. package/dist/server/collections/cluster-manager-lock.d.ts +0 -22
  57. package/dist/server/collections/cluster-manager-queue.d.ts +0 -22
  58. package/dist/server/collections/cluster-manager-redis.d.ts +0 -22
  59. package/dist/server/collections/cluster-manager-workflow.d.ts +0 -22
  60. package/dist/server/collections/cluster-manager.d.ts +0 -22
  61. package/dist/server/collections/orchestrator-settings.d.ts +0 -59
  62. package/dist/server/collections/orchestrator-stacks.d.ts +0 -102
  63. package/dist/server/collections/worker-orchestrator.d.ts +0 -22
  64. package/dist/server/collections/worker-packages-configs.d.ts +0 -3
  65. package/dist/server/collections/worker-packages.d.ts +0 -22
  66. package/dist/server/index.d.ts +0 -1
  67. package/dist/server/orchestrator/PackageManager.d.ts +0 -37
  68. package/dist/server/orchestrator/docker-adapter.d.ts +0 -41
  69. package/dist/server/orchestrator/index.d.ts +0 -4
  70. package/dist/server/orchestrator/k8s-adapter.d.ts +0 -50
  71. package/dist/server/orchestrator/leader-election.d.ts +0 -48
  72. package/dist/server/orchestrator/types.d.ts +0 -84
  73. package/dist/server/plugin.d.ts +0 -26
  74. package/dist/server/utils/node.d.ts +0 -6
  75. package/dist/server/utils/redis.d.ts +0 -29
  76. package/dist/shared/packages.d.ts +0 -23
@@ -5,7 +5,7 @@ import path from 'path';
5
5
  import Application from '@nocobase/server';
6
6
 
7
7
  /** Allow only safe package name characters: letters, digits, dash, underscore, dot, @, /, [, ] */
8
- const SAFE_PKG_RE = /^[a-zA-Z0-9_\-\.@\/\[\]]+$/;
8
+ const SAFE_PKG_RE = /^(?:[a-zA-Z0-9_.@/-]|\[|\])+$/;
9
9
  const INSTALL_CHANNEL = 'cluster-manager.install-packages';
10
10
 
11
11
  type TargetRole = 'app' | 'worker' | 'sandbox' | 'all';
@@ -17,6 +17,11 @@ interface InstallPayload {
17
17
  registryConfig?: { aptMirrorUrl?: string; npmRegistryUrl?: string; pypiIndexUrl?: string; pypiTrustedHost?: string };
18
18
  }
19
19
 
20
+ interface AptOsInfo {
21
+ id: string;
22
+ codename: string;
23
+ }
24
+
20
25
  function sanitizePkg(name: string): string {
21
26
  if (typeof name !== 'string') {
22
27
  throw new Error('Package name must be a string');
@@ -81,10 +86,44 @@ function formatCommand(command: string, args: string[]): string {
81
86
  .join(' ');
82
87
  }
83
88
 
89
+ function parseOsRelease(content: string): Record<string, string> {
90
+ const values: Record<string, string> = {};
91
+ for (const line of content.split('\n')) {
92
+ const match = line.match(/^([A-Z0-9_]+)=(.*)$/);
93
+ if (!match) continue;
94
+
95
+ values[match[1]] = match[2].replace(/^"|"$/g, '');
96
+ }
97
+ return values;
98
+ }
99
+
100
+ function normalizeMirrorUrl(value: string): string {
101
+ return value.endsWith('/') ? value : `${value}/`;
102
+ }
103
+
104
+ function isInternalCacheMirror(url: URL): boolean {
105
+ return url.hostname === 'nginx-cache-registry';
106
+ }
107
+
108
+ function resolveAptMirrorForOs(aptMirrorUrl: string, osInfo: AptOsInfo, logs: string[]): string {
109
+ const url = new URL(normalizeMirrorUrl(aptMirrorUrl));
110
+ const original = url.toString();
111
+
112
+ if (isInternalCacheMirror(url) && osInfo.id === 'debian' && url.pathname === '/ubuntu/') {
113
+ url.pathname = '/debian/';
114
+ } else if (isInternalCacheMirror(url) && osInfo.id === 'ubuntu' && url.pathname === '/debian/') {
115
+ url.pathname = '/ubuntu/';
116
+ }
117
+
118
+ const resolved = url.toString();
119
+ if (resolved !== original) {
120
+ logs.push(`Adjusted APT mirror for ${osInfo.id}: ${redactUrl(resolved)}`);
121
+ }
122
+ return resolved;
123
+ }
124
+
84
125
  export class PackageManager {
85
- constructor(
86
- private app: Application,
87
- ) {}
126
+ constructor(private app: Application) {}
88
127
 
89
128
  /**
90
129
  * Called from REST action when admin clicks "Install Packages".
@@ -107,9 +146,7 @@ export class PackageManager {
107
146
  // Filter by role
108
147
  const currentRole = getCurrentRole();
109
148
  const roleMatches =
110
- targetRole === 'all' ||
111
- targetRole === currentRole ||
112
- (targetRole === 'worker' && currentRole === 'sandbox');
149
+ targetRole === 'all' || targetRole === currentRole || (targetRole === 'worker' && currentRole === 'sandbox');
113
150
 
114
151
  if (!roleMatches) {
115
152
  return; // Skip if role doesn't match
@@ -138,7 +175,7 @@ export class PackageManager {
138
175
  if (registryConfig.aptMirrorUrl) {
139
176
  const aptMirrorUrl = sanitizeHttpUrl(registryConfig.aptMirrorUrl, 'APT mirror URL');
140
177
  logs.push(`Applying APT mirror: ${redactUrl(aptMirrorUrl)}`);
141
- await this.configureAptMirror(aptMirrorUrl);
178
+ await this.configureAptMirror(aptMirrorUrl, logs);
142
179
  }
143
180
 
144
181
  await this.updateInstallStatus('running', 20, 'Installing APT packages...', logs);
@@ -241,7 +278,13 @@ export class PackageManager {
241
278
  /**
242
279
  * Run a command without a shell so registry URLs and package names are not re-parsed as shell syntax.
243
280
  */
244
- private async runCommand(command: string, args: string[], label: string, logs: string[], timeoutMs = 1200000): Promise<void> {
281
+ private async runCommand(
282
+ command: string,
283
+ args: string[],
284
+ label: string,
285
+ logs: string[],
286
+ timeoutMs = 1200000,
287
+ ): Promise<void> {
245
288
  logs.push(`RUNNING: ${formatCommand(command, args)}`);
246
289
  logs.push(`${label}`);
247
290
 
@@ -250,12 +293,12 @@ export class PackageManager {
250
293
  let stdout = '';
251
294
  let stderr = '';
252
295
  let settled = false;
253
- let timer: NodeJS.Timeout;
296
+ const state: { timer?: NodeJS.Timeout } = {};
254
297
 
255
298
  const finish = (error?: Error) => {
256
299
  if (settled) return;
257
300
  settled = true;
258
- clearTimeout(timer);
301
+ if (state.timer) clearTimeout(state.timer);
259
302
  if (stdout) logs.push(stdout.slice(0, 500));
260
303
  if (stderr) logs.push(`WARN: ${stderr.slice(0, 300)}`);
261
304
  if (error) {
@@ -266,7 +309,7 @@ export class PackageManager {
266
309
  }
267
310
  };
268
311
 
269
- timer = setTimeout(() => {
312
+ state.timer = setTimeout(() => {
270
313
  child.kill('SIGTERM');
271
314
  finish(new Error(`${command} timed out after ${timeoutMs}ms`));
272
315
  }, timeoutMs);
@@ -324,16 +367,16 @@ export class PackageManager {
324
367
  const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'ignore'] });
325
368
  let stdout = '';
326
369
  let settled = false;
327
- let timer: NodeJS.Timeout;
370
+ const state: { timer?: NodeJS.Timeout } = {};
328
371
 
329
372
  const finish = (ok: boolean) => {
330
373
  if (settled) return;
331
374
  settled = true;
332
- clearTimeout(timer);
375
+ if (state.timer) clearTimeout(state.timer);
333
376
  resolve(ok);
334
377
  };
335
378
 
336
- timer = setTimeout(() => {
379
+ state.timer = setTimeout(() => {
337
380
  child.kill('SIGTERM');
338
381
  finish(false);
339
382
  }, timeoutMs);
@@ -346,7 +389,31 @@ export class PackageManager {
346
389
  });
347
390
  }
348
391
 
349
- private async configureAptMirror(aptMirrorUrl: string): Promise<void> {
392
+ private async getAptOsInfo(): Promise<AptOsInfo> {
393
+ const values = parseOsRelease(await fsp.readFile('/etc/os-release', 'utf8'));
394
+ const id = (values.ID || '').toLowerCase();
395
+ const codename = values.VERSION_CODENAME || values.UBUNTU_CODENAME;
396
+
397
+ if (!id || !codename) {
398
+ throw new Error('Cannot detect OS ID/version codename from /etc/os-release for APT mirror configuration.');
399
+ }
400
+ return { id, codename };
401
+ }
402
+
403
+ private buildAptSources(aptMirrorUrl: string, osInfo: AptOsInfo): string {
404
+ const mirror = normalizeMirrorUrl(aptMirrorUrl);
405
+ if (osInfo.id === 'ubuntu') {
406
+ return `deb ${mirror} ${osInfo.codename} main universe restricted multiverse\n`;
407
+ }
408
+ if (osInfo.id === 'debian') {
409
+ return `deb ${mirror} ${osInfo.codename} main contrib non-free non-free-firmware\n`;
410
+ }
411
+ return `deb ${mirror} ${osInfo.codename} main\n`;
412
+ }
413
+
414
+ private async configureAptMirror(aptMirrorUrl: string, logs: string[]): Promise<void> {
415
+ const osInfo = await this.getAptOsInfo();
416
+ const resolvedMirrorUrl = resolveAptMirrorForOs(aptMirrorUrl, osInfo, logs);
350
417
  const backupDir = '/etc/apt/sources.list.d.bak';
351
418
  await fsp.mkdir(backupDir, { recursive: true });
352
419
 
@@ -362,7 +429,7 @@ export class PackageManager {
362
429
  // sources.list.d may not exist in minimal images.
363
430
  }
364
431
 
365
- await fsp.writeFile('/etc/apt/sources.list', `deb ${aptMirrorUrl} bookworm main\n`, 'utf8');
432
+ await fsp.writeFile('/etc/apt/sources.list', this.buildAptSources(resolvedMirrorUrl, osInfo), 'utf8');
366
433
  }
367
434
 
368
435
  private async moveIfExists(from: string, to: string): Promise<void> {
@@ -15,6 +15,7 @@ import { RedisPubSubAdapter } from './adapters/redis-pubsub-adapter';
15
15
  import { RedisNodeRegistry } from './adapters/redis-node-registry';
16
16
  import { RedisLockAdapter } from './adapters/redis-lock-adapter';
17
17
  import { orchestratorActions } from './actions/orchestrator';
18
+ import { pluginOperationsActions } from './actions/plugin-operations';
18
19
  import type { IOrchestratorAdapter } from './orchestrator/types';
19
20
  import { DockerAdapter } from './orchestrator/docker-adapter';
20
21
  import { K8sAdapter } from './orchestrator/k8s-adapter';
@@ -240,11 +241,17 @@ export class PluginClusterManagerServer extends Plugin {
240
241
  actions: cacheMonitorActions,
241
242
  });
242
243
 
243
- // Package manager (installs apt/npm/python packages across nodes)
244
- this.app.resourcer.define({
245
- name: 'workerPackages',
246
- actions: packageManagerActions,
247
- });
244
+ // Package manager (installs apt/npm/python packages across nodes)
245
+ this.app.resourcer.define({
246
+ name: 'workerPackages',
247
+ actions: packageManagerActions,
248
+ });
249
+
250
+ // Plugin operations (force disable/remove application plugin records)
251
+ this.app.resourcer.define({
252
+ name: 'clusterManagerPlugins',
253
+ actions: pluginOperationsActions,
254
+ });
248
255
 
249
256
  // Install ACL cache middleware inside the ACL chain so cached permissions are not overwritten.
250
257
  const aclCacheMiddleware = createAclCacheMiddleware(this.app);
@@ -279,11 +286,12 @@ export class PluginClusterManagerServer extends Plugin {
279
286
  'clusterManagerQueue:*',
280
287
  'clusterManagerLock:*',
281
288
  'clusterManagerCacheMgr:*',
282
- 'workerOrchestrator:*',
283
- 'orchestratorStacks:*',
284
- 'workerPackages:*',
285
- ],
286
- });
289
+ 'workerOrchestrator:*',
290
+ 'orchestratorStacks:*',
291
+ 'workerPackages:*',
292
+ 'clusterManagerPlugins:*',
293
+ ],
294
+ });
287
295
 
288
296
  // ── Container Orchestrator ──
289
297
  await this.initOrchestrator();
@@ -365,16 +373,16 @@ export class PluginClusterManagerServer extends Plugin {
365
373
  } else {
366
374
  // Fall back to env var for initial setup
367
375
  const envAdapter = process.env.ORCHESTRATOR_ADAPTER;
368
- if (envAdapter && envAdapter !== 'none') {
369
- await this.connectAdapter({
370
- adapterType: envAdapter,
371
- dockerSocketPath: process.env.DOCKER_SOCKET || '/var/run/docker.sock',
372
- k8sNamespace: 'nocobase',
373
- });
374
- } else {
375
- this.app.logger.info('[Orchestrator] No adapter configured — configurable via Worker Monitor UI');
376
- }
377
- }
376
+ if (envAdapter && envAdapter !== 'none') {
377
+ await this.connectAdapter({
378
+ adapterType: envAdapter,
379
+ dockerSocketPath: process.env.DOCKER_SOCKET || '/var/run/docker.sock',
380
+ k8sNamespace: 'nocobase',
381
+ });
382
+ } else {
383
+ this.app.logger.info('[Orchestrator] No adapter configured — configurable via Cluster Manager UI');
384
+ }
385
+ }
378
386
  } catch (err: any) {
379
387
  this.app.logger.warn(`[Orchestrator] Could not load settings: ${err.message}`);
380
388
  }
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function AclCacheManager(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function CacheMonitor(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function ClusterManagerLayout(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function ClusterNodes(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function ContainerOrchestrator(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function EventQueueMonitor(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function LockMonitor(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare const PackageInstaller: React.FC;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function RedisMonitor(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function TaskManager(): React.JSX.Element;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function WorkflowExecutions(): React.JSX.Element;
@@ -1,5 +0,0 @@
1
- import { Plugin } from '@nocobase/client';
2
- export declare class PluginClusterManagerClient extends Plugin {
3
- load(): Promise<void>;
4
- }
5
- export default PluginClusterManagerClient;
@@ -1,12 +0,0 @@
1
- /**
2
- * Shared i18n hook for the cluster-manager plugin.
3
- */
4
- export declare function useT(): (key: string) => string;
5
- /**
6
- * Format bytes into human-readable string (B, KB, MB, GB).
7
- */
8
- export declare function formatBytes(bytes: number): string;
9
- /**
10
- * Format seconds into human-readable uptime string (e.g., "2d 5h 30m").
11
- */
12
- export declare function formatUptime(seconds: number): string;
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './server';
2
- export { default } from './server';
@@ -1,53 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- /**
3
- * In-memory ACL stats counter.
4
- * Tracks total checks, cache hits/misses per role:resource:action.
5
- */
6
- export interface AclCacheStats {
7
- totalChecks: number;
8
- cacheHits: number;
9
- cacheMisses: number;
10
- startedAt: string;
11
- detailByRole: Record<string, {
12
- checks: number;
13
- hits: number;
14
- misses: number;
15
- }>;
16
- }
17
- /**
18
- * Middleware that caches the permission object computed by the ACL middleware.
19
- * Install via: app.acl.use(aclCacheMiddleware, { tag: 'aclCache', before: 'core', after: 'allow-manager' })
20
- *
21
- * FIX: Previously monkey-patched ctx.app.acl.can per-request, which is a race condition
22
- * because acl is a shared singleton across concurrent requests. Now we use a post-check
23
- * approach that reads from cache first and writes after the ACL middleware runs, without
24
- * ever replacing the shared acl.can method.
25
- */
26
- export declare function createAclCacheMiddleware(app: any): (ctx: Context, next: () => Promise<void>) => Promise<void>;
27
- export declare const aclCacheActions: {
28
- /**
29
- * GET /clusterManagerAclCache:stats
30
- * Returns ACL cache hit/miss statistics
31
- */
32
- stats(ctx: Context, next: () => Promise<void>): Promise<void>;
33
- /**
34
- * GET /clusterManagerAclCache:listKeys
35
- * Lists all cached ACL permission keys
36
- */
37
- listKeys(ctx: Context, next: () => Promise<void>): Promise<void>;
38
- /**
39
- * POST /clusterManagerAclCache:clear
40
- * Clear all ACL cache entries and reset stats
41
- */
42
- clear(ctx: Context, next: () => Promise<void>): Promise<void>;
43
- /**
44
- * POST /clusterManagerAclCache:resetStats
45
- * Reset the in-memory ACL stats counters
46
- */
47
- resetStats(ctx: Context, next: () => Promise<void>): Promise<void>;
48
- /**
49
- * POST /clusterManagerAclCache:clearRole
50
- * Clear ACL cache entries for a specific role
51
- */
52
- clearRole(ctx: Context, next: () => Promise<void>): Promise<void>;
53
- };
@@ -1,23 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- export declare const cacheMonitorActions: {
3
- /**
4
- * GET /clusterManagerCacheMgr:stores
5
- * List all registered cache stores and their config
6
- */
7
- stores(ctx: Context, next: () => Promise<void>): Promise<void>;
8
- /**
9
- * GET /clusterManagerCacheMgr:caches
10
- * List all created named caches
11
- */
12
- caches(ctx: Context, next: () => Promise<void>): Promise<void>;
13
- /**
14
- * GET /clusterManagerCacheMgr:redisMemory
15
- * Get Redis memory usage for cache keys
16
- */
17
- redisMemory(ctx: Context, next: () => Promise<void>): Promise<void>;
18
- /**
19
- * POST /clusterManagerCacheMgr:flushAll
20
- * Flush all caches via CacheManager
21
- */
22
- flushAll(ctx: Context, next: () => Promise<void>): Promise<void>;
23
- };
@@ -1,49 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- /**
3
- * Read the last N lines from the local system log file.
4
- * Extracted so it can be called from both the HTTP action and the PubSub subscriber.
5
- */
6
- export declare function readLocalLogs(app: any, maxLines: number): Promise<{
7
- node: {
8
- hostname: string;
9
- pid: number;
10
- workerMode: string;
11
- };
12
- lines: string[];
13
- file: string;
14
- }>;
15
- export declare const clusterActions: {
16
- /**
17
- * GET /clusterManagerCluster:current
18
- * Always returns info about the APP node (not workers).
19
- * If this request is handled by a worker, we look up the APP node from Redis.
20
- */
21
- current(ctx: Context, next: () => Promise<void>): Promise<void>;
22
- /**
23
- * GET /clusterManagerCluster:list
24
- * Returns all known cluster environments/nodes (if discovery adapter supports it)
25
- */
26
- list(ctx: Context, next: () => Promise<void>): Promise<void>;
27
- /**
28
- * GET /clusterManagerCluster:health
29
- * Health check for all subsystems
30
- */
31
- health(ctx: Context, next: () => Promise<void>): Promise<void>;
32
- /**
33
- * POST /clusterManagerCluster:restart
34
- * Publishes a restart signal to target nodes orchestrating a soft NocoBase restart or a hard docker daemon rebirth
35
- */
36
- restart(ctx: Context, next: () => Promise<void>): Promise<void>;
37
- /**
38
- * GET /clusterManagerCluster:logs?targetNodeId=xxx&lines=200
39
- *
40
- * HA-aware log viewer. Reads logs from a specific node in the cluster.
41
- *
42
- * Flow:
43
- * 1. If targetNodeId matches current node (or is empty) → read local FS directly
44
- * 2. Otherwise → publish a log request via PubSub → target node reads its local FS
45
- * and writes the result to a Redis key → this handler polls Redis until the
46
- * response arrives (max 10s) → returns it to the client
47
- */
48
- logs(ctx: Context, next: () => Promise<void>): Promise<void>;
49
- };
@@ -1,13 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- export declare const eventQueueActions: {
3
- /**
4
- * GET /clusterManagerQueue:stats
5
- * Returns event queue statistics
6
- */
7
- stats(ctx: Context, next: () => Promise<void>): Promise<void>;
8
- /**
9
- * GET /clusterManagerQueue:messages
10
- * List pending messages in a specific channel (memory adapter only)
11
- */
12
- messages(ctx: Context, next: () => Promise<void>): Promise<void>;
13
- };
@@ -1,19 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- export declare const lockActions: {
3
- /**
4
- * GET /clusterManagerLock:info
5
- * Returns lock manager info
6
- */
7
- info(ctx: Context, next: () => Promise<void>): Promise<void>;
8
- /**
9
- * GET /clusterManagerLock:list
10
- * List active locks
11
- */
12
- list(ctx: Context, next: () => Promise<void>): Promise<void>;
13
- /**
14
- * POST /clusterManagerLock:release
15
- * Force release a stuck lock (admin emergency action)
16
- * Uses compare-and-swap via DEL for Redis locks with the correct key prefix.
17
- */
18
- release(ctx: Context, next: () => Promise<void>): Promise<void>;
19
- };
@@ -1,58 +0,0 @@
1
- /**
2
- * Orchestrator Actions
3
- *
4
- * API endpoints for container management. Write operations (scale, start, stop, remove)
5
- * are guarded by leader election — only the leader node can execute them.
6
- */
7
- import { Context } from '@nocobase/actions';
8
- export declare const orchestratorActions: {
9
- /**
10
- * GET /workerOrchestrator:ping
11
- * Test connectivity to the container runtime
12
- */
13
- ping(ctx: Context, next: () => Promise<void>): Promise<void>;
14
- /**
15
- * GET /workerOrchestrator:containers?stackId=1
16
- * List live containers for a stack
17
- */
18
- containers(ctx: Context, next: () => Promise<void>): Promise<void>;
19
- /**
20
- * POST /workerOrchestrator:scale
21
- * Body: { stackId: 1, replicas: 3 }
22
- * Leader-only
23
- */
24
- scale(ctx: Context, next: () => Promise<void>): Promise<void>;
25
- /**
26
- * POST /workerOrchestrator:start
27
- * Body: { stackId: 1, containerId: "abc123" }
28
- * Leader-only
29
- */
30
- start(ctx: Context, next: () => Promise<void>): Promise<void>;
31
- /**
32
- * POST /workerOrchestrator:stop
33
- * Body: { stackId: 1, containerId: "abc123" }
34
- * Leader-only
35
- */
36
- stop(ctx: Context, next: () => Promise<void>): Promise<void>;
37
- /**
38
- * POST /workerOrchestrator:remove
39
- * Body: { stackId: 1, containerId: "abc123" }
40
- * Leader-only
41
- */
42
- remove(ctx: Context, next: () => Promise<void>): Promise<void>;
43
- /**
44
- * GET /workerOrchestrator:stats?containerId=abc123
45
- * Get real-time stats for a container
46
- */
47
- stats(ctx: Context, next: () => Promise<void>): Promise<void>;
48
- /**
49
- * GET /workerOrchestrator:logs?containerId=abc123&tail=100
50
- * Get tail logs from a container
51
- */
52
- logs(ctx: Context, next: () => Promise<void>): Promise<void>;
53
- /**
54
- * GET /workerOrchestrator:networks
55
- * List available networks (if supported by adapter)
56
- */
57
- networks(ctx: Context, next: () => Promise<void>): Promise<void>;
58
- };
@@ -1,6 +0,0 @@
1
- export declare const packageManagerActions: {
2
- installPackages(ctx: any, next: () => Promise<void>): Promise<void>;
3
- getPackageConfig(ctx: any, next: () => Promise<void>): Promise<void>;
4
- savePackageConfig(ctx: any, next: () => Promise<void>): Promise<void>;
5
- resetInitStatus(ctx: any, next: () => Promise<void>): Promise<void>;
6
- };
@@ -1,12 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- export declare const redisActions: {
3
- info(ctx: Context, next: () => Promise<void>): Promise<void>;
4
- clients(ctx: Context, next: () => Promise<void>): Promise<void>;
5
- pubsub(ctx: Context, next: () => Promise<void>): Promise<void>;
6
- slowlog(ctx: Context, next: () => Promise<void>): Promise<void>;
7
- /**
8
- * GET /clusterManagerRedis:syncMessages
9
- * Returns info about NocoBase sync message channels (pub/sub layer)
10
- */
11
- syncMessages(ctx: Context, next: () => Promise<void>): Promise<void>;
12
- };
@@ -1,7 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- export declare const tasksActions: {
3
- list(ctx: Context, next: () => Promise<void>): Promise<void>;
4
- cancel(ctx: Context, next: () => Promise<void>): Promise<void>;
5
- retry(ctx: Context, next: () => Promise<void>): Promise<void>;
6
- purge(ctx: Context, next: () => Promise<void>): Promise<void>;
7
- };
@@ -1,7 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- export declare const workflowActions: {
3
- list(ctx: Context, next: () => Promise<void>): Promise<void>;
4
- getJobs(ctx: Context, next: () => Promise<void>): Promise<void>;
5
- cancel(ctx: Context, next: () => Promise<void>): Promise<void>;
6
- purge(ctx: Context, next: () => Promise<void>): Promise<void>;
7
- };
@@ -1,15 +0,0 @@
1
- import { ILockAdapter, ILock, Releaser } from '@nocobase/lock-manager';
2
- import { Application } from '@nocobase/server';
3
- export declare class RedisLockAdapter implements ILockAdapter {
4
- private options;
5
- private client;
6
- constructor(options: {
7
- url?: string;
8
- app: Application;
9
- });
10
- connect(): Promise<void>;
11
- close(): Promise<void>;
12
- acquire(key: string, ttl: number): Promise<Releaser>;
13
- runExclusive<T>(key: string, fn: () => Promise<T>, ttl: number): Promise<T>;
14
- tryAcquire(key: string, timeout?: number): Promise<ILock>;
15
- }
@@ -1,12 +0,0 @@
1
- export declare class RedisNodeRegistry {
2
- private app;
3
- private timer;
4
- private readonly ttlSecs;
5
- private readonly intervalMs;
6
- private readonly keyPrefix;
7
- constructor(app: any);
8
- start(): void;
9
- stop(): void;
10
- private heartbeat;
11
- getNodes(): Promise<any[]>;
12
- }
@@ -1,16 +0,0 @@
1
- import { IPubSubAdapter, PubSubCallback } from '@nocobase/server';
2
- export declare class RedisPubSubAdapter implements IPubSubAdapter {
3
- private url;
4
- private logger?;
5
- private publisher;
6
- private subscriber;
7
- private connected;
8
- private subscriptions;
9
- constructor(url: string, logger?: any);
10
- isConnected(): boolean;
11
- connect(): Promise<void>;
12
- close(): Promise<void>;
13
- subscribe(channel: string, callback: PubSubCallback): Promise<void>;
14
- unsubscribe(channel: string, callback: PubSubCallback): Promise<void>;
15
- publish(channel: string, message: string): Promise<void>;
16
- }
@@ -1,8 +0,0 @@
1
- declare const _default: {
2
- name: string;
3
- autoCreate: boolean;
4
- dumpRules: string;
5
- model: string;
6
- fields: any[];
7
- };
8
- export default _default;