plugin-cluster-manager 1.1.11 → 1.1.15

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 (116) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/dist/client/index.js +1 -1
  4. package/dist/client-v2/914.c0bce51908fd81d7.js +10 -0
  5. package/dist/client-v2/index.js +10 -0
  6. package/dist/externalVersion.js +6 -5
  7. package/dist/locale/en-US.json +138 -124
  8. package/dist/locale/vi-VN.json +139 -125
  9. package/dist/locale/zh-CN.json +140 -125
  10. package/dist/server/actions/cluster-nodes.js +2 -6
  11. package/dist/server/actions/doctor.js +1 -5
  12. package/dist/server/actions/orchestrator.js +37 -0
  13. package/dist/server/actions/queue-mappings.js +107 -0
  14. package/dist/server/collections/worker-queue-mappings.js +106 -0
  15. package/dist/server/orchestrator/PackageManager.js +1 -8
  16. package/dist/server/orchestrator/docker-adapter.js +49 -27
  17. package/dist/server/plugin.js +10 -8
  18. package/dist/server/queue-scanner.js +141 -0
  19. package/dist/server/utils/node.js +30 -2
  20. package/package.json +46 -42
  21. package/src/client/AclCacheManager.tsx +292 -287
  22. package/src/client/CacheMonitor.tsx +2 -2
  23. package/src/client/ClusterManagerLayout.tsx +6 -0
  24. package/src/client/ClusterNodes.tsx +11 -4
  25. package/src/client/ContainerOrchestrator.tsx +186 -104
  26. package/src/client/Doctor.tsx +2 -2
  27. package/src/client/EventQueueMonitor.tsx +2 -2
  28. package/src/client/LockMonitor.tsx +2 -2
  29. package/src/client/NginxCacheManager.tsx +2 -2
  30. package/src/client/PackageInstaller.tsx +2 -2
  31. package/src/client/PluginOperations.tsx +2 -2
  32. package/src/client/QueueAssignment.tsx +355 -0
  33. package/src/client/RedisMonitor.tsx +3 -3
  34. package/src/client/TaskManager.tsx +194 -187
  35. package/src/client/WorkflowExecutions.tsx +243 -238
  36. package/src/client/utils.ts +1 -1
  37. package/src/client-v2/plugin.tsx +24 -0
  38. package/src/locale/en-US.json +138 -124
  39. package/src/locale/vi-VN.json +139 -125
  40. package/src/locale/zh-CN.json +140 -125
  41. package/src/server/actions/cluster-nodes.ts +4 -7
  42. package/src/server/actions/doctor.ts +11 -9
  43. package/src/server/actions/orchestrator.ts +54 -2
  44. package/src/server/actions/queue-mappings.ts +94 -0
  45. package/src/server/adapters/redis-node-registry.ts +126 -131
  46. package/src/server/collections/worker-queue-mappings.ts +85 -0
  47. package/src/server/orchestrator/PackageManager.ts +2 -10
  48. package/src/server/orchestrator/docker-adapter.ts +74 -37
  49. package/src/server/plugin.ts +15 -12
  50. package/src/server/queue-scanner.ts +154 -0
  51. package/src/server/utils/node.ts +48 -0
  52. package/dist/client/AclCacheManager.d.ts +0 -2
  53. package/dist/client/CacheMonitor.d.ts +0 -2
  54. package/dist/client/ClusterManagerLayout.d.ts +0 -2
  55. package/dist/client/ClusterNodes.d.ts +0 -2
  56. package/dist/client/ContainerOrchestrator.d.ts +0 -2
  57. package/dist/client/Doctor.d.ts +0 -2
  58. package/dist/client/EventQueueMonitor.d.ts +0 -2
  59. package/dist/client/LockMonitor.d.ts +0 -2
  60. package/dist/client/NginxCacheManager.d.ts +0 -2
  61. package/dist/client/PackageInstaller.d.ts +0 -2
  62. package/dist/client/PluginOperations.d.ts +0 -2
  63. package/dist/client/RedisMonitor.d.ts +0 -2
  64. package/dist/client/TaskManager.d.ts +0 -2
  65. package/dist/client/WorkflowExecutions.d.ts +0 -2
  66. package/dist/client/index.d.ts +0 -5
  67. package/dist/client/utils/clientSafeCache.d.ts +0 -3
  68. package/dist/client/utils/requestDedupInterceptor.d.ts +0 -2
  69. package/dist/client/utils.d.ts +0 -12
  70. package/dist/index.d.ts +0 -2
  71. package/dist/server/actions/acl-cache.d.ts +0 -53
  72. package/dist/server/actions/cache-monitor.d.ts +0 -33
  73. package/dist/server/actions/cluster-nodes.d.ts +0 -64
  74. package/dist/server/actions/doctor.d.ts +0 -82
  75. package/dist/server/actions/event-queue-monitor.d.ts +0 -13
  76. package/dist/server/actions/lock-monitor.d.ts +0 -19
  77. package/dist/server/actions/orchestrator.d.ts +0 -58
  78. package/dist/server/actions/package-manager.d.ts +0 -6
  79. package/dist/server/actions/plugin-operations.d.ts +0 -6
  80. package/dist/server/actions/redis-monitor.d.ts +0 -12
  81. package/dist/server/actions/tasks.d.ts +0 -7
  82. package/dist/server/actions/workflow-executions.d.ts +0 -7
  83. package/dist/server/adapters/redis-lock-adapter.d.ts +0 -15
  84. package/dist/server/adapters/redis-node-registry.d.ts +0 -12
  85. package/dist/server/adapters/redis-pubsub-adapter.d.ts +0 -16
  86. package/dist/server/collections/app.d.ts +0 -8
  87. package/dist/server/collections/cluster-manager-acl-cache.d.ts +0 -22
  88. package/dist/server/collections/cluster-manager-cache-mgr.d.ts +0 -22
  89. package/dist/server/collections/cluster-manager-cluster.d.ts +0 -22
  90. package/dist/server/collections/cluster-manager-doctor-runs.d.ts +0 -3
  91. package/dist/server/collections/cluster-manager-doctor.d.ts +0 -18
  92. package/dist/server/collections/cluster-manager-lock.d.ts +0 -22
  93. package/dist/server/collections/cluster-manager-plugins.d.ts +0 -18
  94. package/dist/server/collections/cluster-manager-queue.d.ts +0 -22
  95. package/dist/server/collections/cluster-manager-redis.d.ts +0 -22
  96. package/dist/server/collections/cluster-manager-workflow.d.ts +0 -22
  97. package/dist/server/collections/cluster-manager.d.ts +0 -22
  98. package/dist/server/collections/orchestrator-settings.d.ts +0 -59
  99. package/dist/server/collections/orchestrator-stacks.d.ts +0 -102
  100. package/dist/server/collections/worker-orchestrator.d.ts +0 -22
  101. package/dist/server/collections/worker-packages-configs.d.ts +0 -3
  102. package/dist/server/collections/worker-packages.d.ts +0 -22
  103. package/dist/server/hooks/cacheInvalidationHooks.d.ts +0 -1
  104. package/dist/server/middlewares/listMetaCacheMiddleware.d.ts +0 -2
  105. package/dist/server/orchestrator/PackageManager.d.ts +0 -39
  106. package/dist/server/orchestrator/docker-adapter.d.ts +0 -41
  107. package/dist/server/orchestrator/index.d.ts +0 -4
  108. package/dist/server/orchestrator/k8s-adapter.d.ts +0 -50
  109. package/dist/server/orchestrator/leader-election.d.ts +0 -48
  110. package/dist/server/orchestrator/types.d.ts +0 -84
  111. package/dist/server/plugin.d.ts +0 -26
  112. package/dist/server/utils/node.d.ts +0 -6
  113. package/dist/server/utils/redis.d.ts +0 -29
  114. package/dist/server/utils/versionManager.d.ts +0 -10
  115. package/dist/shared/packages.d.ts +0 -23
  116. /package/{dist/server/index.d.ts → src/client-v2/index.tsx} +0 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "Cluster Manager": "集群管理",
2
+ "Cluster Manager": "集群管理",
3
3
  "Async Tasks": "异步任务",
4
4
  "Workflow Executions": "工作流执行",
5
5
  "Redis Monitor": "Redis 监控",
@@ -66,127 +66,142 @@
66
66
  "This will clear all cached data across all stores.": "这将清除所有存储中的缓存数据。",
67
67
  "All caches flushed": "所有缓存已清空",
68
68
  "Failed to flush caches": "清空缓存失败",
69
- "Sync Messages": "同步消息",
70
- "Subscribers": "订阅者",
71
- "Plugins": "插件",
72
- "Plugin": "插件",
73
- "Enabled": "已启用",
74
- "Disabled": "已禁用",
75
- "Installed": "已安装",
76
- "Not installed": "未安装",
77
- "Loaded": "已加载",
78
- "Not loaded": "未加载",
79
- "Protected": "受保护",
80
- "Description": "描述",
81
- "Force disable": "强制禁用",
82
- "Force remove": "强制移除",
83
- "Force disable this plugin?": "强制禁用此插件?",
84
- "Force remove this plugin?": "强制移除此插件?",
85
- "This updates the plugin registry directly. Restart or reload is required to fully unload runtime hooks.": "此操作会直接更新插件注册表。需要重启或重新加载后才能完全卸载运行时钩子。",
86
- "This removes the plugin registry record. Package files are not deleted. Restart or reload is required.": "此操作会移除插件注册记录。不会删除 package 文件。需要重启或重新加载。",
87
- "Force operations bypass plugin lifecycle hooks": "强制操作会绕过插件生命周期钩子",
88
- "Use this only when the normal plugin manager cannot disable or remove a broken plugin. Restart or reload the app after a successful operation.": "仅在普通插件管理器无法禁用或移除异常插件时使用。操作成功后请重启或重新加载应用。",
89
- "Search plugins": "搜索插件",
90
- "Plugin force disabled": "插件已强制禁用",
91
- "Plugin force removed": "插件已强制移除",
92
- "Failed to load plugins": "加载插件失败",
93
- "Failed to force disable plugin": "强制禁用插件失败",
94
- "Failed to force remove plugin": "强制移除插件失败",
95
- "Nginx Cache": "Nginx 缓存",
96
- "Nginx configuration file found at": "已找到 Nginx 配置文件于",
97
- "Nginx cache paths detected": "检测到的 Nginx 缓存路径",
98
- "Nginx is not detected on this node. You can still input a custom cache directory.": "此节点未检测到 Nginx。您仍可以输入自定义缓存目录。",
99
- "Clearing Method": "清除方式",
100
- "Physical Files": "物理文件",
101
- "HTTP Purge Request": "HTTP Purge 请求",
102
- "Select Cache Path": "选择缓存路径",
103
- "Custom Path": "自定义路径",
104
- "Purge URL": "Purge URL",
105
- "Headers (JSON)": "Headers (JSON)",
106
- "Clear Nginx Cache": "清除 Nginx 缓存",
107
- "Are you sure you want to clear Nginx cache? This will permanently delete all files in this directory.": "确定要清除 Nginx 缓存吗?这将永久删除该目录下的所有文件。",
108
- "Cache cleared successfully. Cleared {count} items.": "缓存清除成功。已清除 {count} 项。",
109
- "HTTP Purge request completed. Status: {status}": "HTTP Purge 请求已完成。状态:{status}",
110
- "Custom Cache Path": "自定义缓存路径",
111
- "HTTP Method": "HTTP 方法",
112
- "Send Purge Request": "发送 Purge 请求",
113
- "Please select or enter a cache path": "请选择或输入缓存路径",
114
- "Please enter a valid Purge URL": "请输入有效的 Purge URL",
115
- "Nginx cache path is required": "必须提供 Nginx 缓存路径",
116
- "Invalid headers JSON structure": "Headers JSON 结构无效",
117
- "Nginx Cache Status": "Nginx 缓存状态",
118
- "Nginx is installed": "Nginx 已安装",
119
- "Nginx is NOT installed": "Nginx 未安装",
120
- "Nginx cache clearing method": "Nginx 缓存清除方式",
121
- "Execution Logs": "执行日志",
122
- "Clear Nginx Cache?": "清除 Nginx 缓存?"
123
- ,
124
- "Rolling Restart": "Rolling Restart",
125
- "Worker nodes only": "Worker nodes only",
126
- "App nodes only": "App nodes only",
127
- "Sandbox nodes only": "Sandbox nodes only",
128
- "All nodes": "All nodes",
129
- "Soft restart": "Soft restart",
130
- "Hard restart": "Hard restart",
131
- "Start rolling restart?": "Start rolling restart?",
132
- "Nodes will receive restart commands one-by-one with the configured delay.": "Nodes will receive restart commands one-by-one with the configured delay.",
133
- "Start": "Start",
134
- "Rolling restart dispatched for {count} node(s)": "Rolling restart dispatched for {count} node(s)",
135
- "Failed to dispatch rolling restart": "Failed to dispatch rolling restart",
136
- "Cluster Drift": "Cluster Drift",
137
- "No cluster drift detected": "No cluster drift detected",
138
- "Cluster drift detected": "Cluster drift detected",
139
- "Reference version": "Reference version",
140
- "No reference version available": "No reference version available",
141
- "Checked Nodes": "Checked Nodes",
142
- "Version Drift": "Version Drift",
143
- "Runtime Drift": "Runtime Drift",
144
- "Package Drift": "Package Drift",
145
- "Runtime": "Runtime",
146
- "Role": "Role",
147
- "Expected": "Expected",
148
- "Actual": "Actual",
149
- "Package Status": "Package Status",
150
- "Missing Packages": "Missing Packages",
151
- "No missing packages": "No missing packages",
152
- "Legacy Multi-app Diagnostics": "Legacy Multi-app Diagnostics",
153
- "No legacy multi-app risk detected": "No legacy multi-app risk detected",
154
- "Legacy multi-app risk detected": "Legacy multi-app risk detected",
155
- "Legacy app records": "Legacy app records",
156
- "Deprecated multi-app manager is active. It runs apps in shared process memory and should not be used for production cluster isolation.": "Deprecated multi-app manager is active. It runs apps in shared process memory and should not be used for production cluster isolation.",
157
- "Deprecated multi-app share collection is active. Avoid schema/table sharing for new cluster deployments.": "Deprecated multi-app share collection is active. Avoid schema/table sharing for new cluster deployments.",
158
- "{count} legacy application record(s) were found in the applications collection.": "{count} legacy application record(s) were found in the applications collection.",
159
- "App Supervisor is not enabled. Use it for new multi-application management instead of deprecated multi-app plugins.": "App Supervisor is not enabled. Use it for new multi-application management instead of deprecated multi-app plugins.",
160
- "Doctor": "Doctor",
161
- "Duration": "Duration",
162
- "Start Doctor": "Start Doctor",
163
- "Stop Doctor": "Stop Doctor",
164
- "Download Report": "Download Report",
165
- "Diagnostic session started": "Diagnostic session started",
166
- "Diagnostic report is ready": "Diagnostic report is ready",
167
- "Failed to load diagnostic status": "Failed to load diagnostic status",
168
- "Failed to start diagnostic session": "Failed to start diagnostic session",
169
- "Failed to stop diagnostic session": "Failed to stop diagnostic session",
170
- "Failed to download diagnostic report": "Failed to download diagnostic report",
171
- "Running": "Running",
172
- "Run ID": "Run ID",
173
- "Started At": "Started At",
174
- "Finished At": "Finished At",
175
- "Finish Reason": "Finish Reason",
176
- "Report Status": "Report Status",
177
- "Nodes": "Nodes",
178
- "Errors": "Errors",
179
- "Warnings": "Warnings",
180
- "Plugin Drift": "Plugin Drift",
181
- "Findings": "Findings",
182
- "Node Log Distribution": "Node Log Distribution",
183
- "Top Error Signatures": "Top Error Signatures",
184
- "Level": "Level",
185
- "Count": "Count",
186
- "Signature": "Signature",
187
- "Node": "Node",
188
- "Log Files": "Log Files",
189
- "Package": "Package",
190
- "DB Version": "DB Version",
191
- "Runtime Versions": "Runtime Versions"
192
- }
69
+ "Sync Messages": "同步消息",
70
+ "Subscribers": "订阅者",
71
+ "Plugins": "插件",
72
+ "Plugin": "插件",
73
+ "Enabled": "已启用",
74
+ "Disabled": "已禁用",
75
+ "Installed": "已安装",
76
+ "Not installed": "未安装",
77
+ "Loaded": "已加载",
78
+ "Not loaded": "未加载",
79
+ "Protected": "受保护",
80
+ "Description": "描述",
81
+ "Force disable": "强制禁用",
82
+ "Force remove": "强制移除",
83
+ "Force disable this plugin?": "强制禁用此插件?",
84
+ "Force remove this plugin?": "强制移除此插件?",
85
+ "This updates the plugin registry directly. Restart or reload is required to fully unload runtime hooks.": "此操作会直接更新插件注册表。需要重启或重新加载后才能完全卸载运行时钩子。",
86
+ "This removes the plugin registry record. Package files are not deleted. Restart or reload is required.": "此操作会移除插件注册记录。不会删除 package 文件。需要重启或重新加载。",
87
+ "Force operations bypass plugin lifecycle hooks": "强制操作会绕过插件生命周期钩子",
88
+ "Use this only when the normal plugin manager cannot disable or remove a broken plugin. Restart or reload the app after a successful operation.": "仅在普通插件管理器无法禁用或移除异常插件时使用。操作成功后请重启或重新加载应用。",
89
+ "Search plugins": "搜索插件",
90
+ "Plugin force disabled": "插件已强制禁用",
91
+ "Plugin force removed": "插件已强制移除",
92
+ "Failed to load plugins": "加载插件失败",
93
+ "Failed to force disable plugin": "强制禁用插件失败",
94
+ "Failed to force remove plugin": "强制移除插件失败",
95
+ "Nginx Cache": "Nginx 缓存",
96
+ "Nginx configuration file found at": "已找到 Nginx 配置文件于",
97
+ "Nginx cache paths detected": "检测到的 Nginx 缓存路径",
98
+ "Nginx is not detected on this node. You can still input a custom cache directory.": "此节点未检测到 Nginx。您仍可以输入自定义缓存目录。",
99
+ "Clearing Method": "清除方式",
100
+ "Physical Files": "物理文件",
101
+ "HTTP Purge Request": "HTTP Purge 请求",
102
+ "Select Cache Path": "选择缓存路径",
103
+ "Custom Path": "自定义路径",
104
+ "Purge URL": "Purge URL",
105
+ "Headers (JSON)": "Headers (JSON)",
106
+ "Clear Nginx Cache": "清除 Nginx 缓存",
107
+ "Are you sure you want to clear Nginx cache? This will permanently delete all files in this directory.": "确定要清除 Nginx 缓存吗?这将永久删除该目录下的所有文件。",
108
+ "Cache cleared successfully. Cleared {count} items.": "缓存清除成功。已清除 {count} 项。",
109
+ "HTTP Purge request completed. Status: {status}": "HTTP Purge 请求已完成。状态:{status}",
110
+ "Custom Cache Path": "自定义缓存路径",
111
+ "HTTP Method": "HTTP 方法",
112
+ "Send Purge Request": "发送 Purge 请求",
113
+ "Please select or enter a cache path": "请选择或输入缓存路径",
114
+ "Please enter a valid Purge URL": "请输入有效的 Purge URL",
115
+ "Nginx cache path is required": "必须提供 Nginx 缓存路径",
116
+ "Invalid headers JSON structure": "Headers JSON 结构无效",
117
+ "Nginx Cache Status": "Nginx 缓存状态",
118
+ "Nginx is installed": "Nginx 已安装",
119
+ "Nginx is NOT installed": "Nginx 未安装",
120
+ "Nginx cache clearing method": "Nginx 缓存清除方式",
121
+ "Execution Logs": "执行日志",
122
+ "Clear Nginx Cache?": "清除 Nginx 缓存?"
123
+ ,
124
+ "Rolling Restart": "Rolling Restart",
125
+ "Worker nodes only": "Worker nodes only",
126
+ "App nodes only": "App nodes only",
127
+ "Sandbox nodes only": "Sandbox nodes only",
128
+ "All nodes": "All nodes",
129
+ "Soft restart": "Soft restart",
130
+ "Hard restart": "Hard restart",
131
+ "Start rolling restart?": "Start rolling restart?",
132
+ "Nodes will receive restart commands one-by-one with the configured delay.": "Nodes will receive restart commands one-by-one with the configured delay.",
133
+ "Start": "Start",
134
+ "Rolling restart dispatched for {count} node(s)": "Rolling restart dispatched for {count} node(s)",
135
+ "Failed to dispatch rolling restart": "Failed to dispatch rolling restart",
136
+ "Cluster Drift": "Cluster Drift",
137
+ "No cluster drift detected": "No cluster drift detected",
138
+ "Cluster drift detected": "Cluster drift detected",
139
+ "Reference version": "Reference version",
140
+ "No reference version available": "No reference version available",
141
+ "Checked Nodes": "Checked Nodes",
142
+ "Version Drift": "Version Drift",
143
+ "Runtime Drift": "Runtime Drift",
144
+ "Package Drift": "Package Drift",
145
+ "Runtime": "Runtime",
146
+ "Role": "Role",
147
+ "Expected": "Expected",
148
+ "Actual": "Actual",
149
+ "Package Status": "Package Status",
150
+ "Missing Packages": "Missing Packages",
151
+ "No missing packages": "No missing packages",
152
+ "Legacy Multi-app Diagnostics": "Legacy Multi-app Diagnostics",
153
+ "No legacy multi-app risk detected": "No legacy multi-app risk detected",
154
+ "Legacy multi-app risk detected": "Legacy multi-app risk detected",
155
+ "Legacy app records": "Legacy app records",
156
+ "Deprecated multi-app manager is active. It runs apps in shared process memory and should not be used for production cluster isolation.": "Deprecated multi-app manager is active. It runs apps in shared process memory and should not be used for production cluster isolation.",
157
+ "Deprecated multi-app share collection is active. Avoid schema/table sharing for new cluster deployments.": "Deprecated multi-app share collection is active. Avoid schema/table sharing for new cluster deployments.",
158
+ "{count} legacy application record(s) were found in the applications collection.": "{count} legacy application record(s) were found in the applications collection.",
159
+ "App Supervisor is not enabled. Use it for new multi-application management instead of deprecated multi-app plugins.": "App Supervisor is not enabled. Use it for new multi-application management instead of deprecated multi-app plugins.",
160
+ "License": "License",
161
+ "Doctor": "诊断",
162
+ "Duration": "Duration",
163
+ "Start Doctor": "Start Doctor",
164
+ "Stop Doctor": "Stop Doctor",
165
+ "Download Report": "Download Report",
166
+ "Diagnostic session started": "Diagnostic session started",
167
+ "Diagnostic report is ready": "Diagnostic report is ready",
168
+ "Failed to load diagnostic status": "Failed to load diagnostic status",
169
+ "Failed to start diagnostic session": "Failed to start diagnostic session",
170
+ "Failed to stop diagnostic session": "Failed to stop diagnostic session",
171
+ "Failed to download diagnostic report": "Failed to download diagnostic report",
172
+ "Running": "Running",
173
+ "Run ID": "Run ID",
174
+ "Started At": "Started At",
175
+ "Finished At": "Finished At",
176
+ "Finish Reason": "Finish Reason",
177
+ "Report Status": "Report Status",
178
+ "Nodes": "Nodes",
179
+ "Errors": "Errors",
180
+ "Warnings": "Warnings",
181
+ "Plugin Drift": "Plugin Drift",
182
+ "Findings": "Findings",
183
+ "Node Log Distribution": "Node Log Distribution",
184
+ "Top Error Signatures": "Top Error Signatures",
185
+ "Level": "Level",
186
+ "Count": "Count",
187
+ "Signature": "Signature",
188
+ "Node": "Node",
189
+ "Log Files": "Log Files",
190
+ "Package": "Package",
191
+ "DB Version": "DB Version",
192
+ "Runtime Versions": "Runtime Versions",
193
+ "Queue Assignment": "队列分配",
194
+ "Queue Name": "队列名称",
195
+ "Map queues to worker stacks. Unassigned queues run on all workers.": "将队列映射到工作节点栈。未分配的队列将在所有工作节点上运行。",
196
+ "Scan Queues": "扫描队列",
197
+ "Auto-map ({count})": "自动映射 ({count})",
198
+ "Register": "注册",
199
+ "Unregister": "注销",
200
+ "Assigned Stack": "分配栈",
201
+ "All (default)": "全部(默认)",
202
+ "Delete this mapping?": "删除此映射?",
203
+ "No queues discovered. Click \"Scan Queues\" to detect registered queues.": "未发现队列。点击\"扫描队列\"以检测已注册的队列。",
204
+ "Auto-mapped {count} queue(s)": "已自动映射 {count} 个队列",
205
+ "Queue Assignment updated": "队列分配已更新",
206
+ "Unassigned (worker runs all queues)": "未分配(工作节点运行所有队列)"
207
+ }
@@ -5,7 +5,7 @@ import path from 'path';
5
5
  import crypto from 'crypto';
6
6
  import { RedisNodeRegistry } from '../adapters/redis-node-registry';
7
7
  import { getRedis } from '../utils/redis';
8
- import { getLocalNodeId } from '../utils/node';
8
+ import { getLocalNodeId, getNodeRoleFrom, isWorkerMode } from '../utils/node';
9
9
  import { packagesFromConfig, type CustomPackageMap, type WorkerPackageMap } from '../../shared/packages';
10
10
 
11
11
  const LOG_RESPONSE_KEY_PREFIX = 'cluster-manager:log-response:';
@@ -17,6 +17,7 @@ interface ClusterNodeRecord {
17
17
  hostname?: string;
18
18
  appVersion?: string;
19
19
  workerMode?: string;
20
+ appRole?: string;
20
21
  isSandbox?: boolean;
21
22
  status?: string;
22
23
  url?: string | null;
@@ -162,11 +163,7 @@ function getErrorMessage(error: unknown) {
162
163
  }
163
164
 
164
165
  function getNodeRole(node: ClusterNodeRecord): 'app' | 'worker' | 'sandbox' {
165
- if (node.isSandbox) {
166
- return 'sandbox';
167
- }
168
- const workerMode = node.workerMode || 'main';
169
- return workerMode === 'worker' || workerMode === 'task' || workerMode === '*' ? 'worker' : 'app';
166
+ return getNodeRoleFrom({ workerMode: node.workerMode, appRole: node.appRole, isSandbox: node.isSandbox });
170
167
  }
171
168
 
172
169
  function getReferenceVersion(nodes: ClusterNodeRecord[]) {
@@ -304,7 +301,7 @@ export const clusterActions = {
304
301
  */
305
302
  async current(ctx: Context, next: () => Promise<void>) {
306
303
  const currentMode = process.env.WORKER_MODE || 'main';
307
- const isApp = currentMode === 'main' || currentMode === '' || currentMode === 'app';
304
+ const isApp = !isWorkerMode(process.env.WORKER_MODE);
308
305
 
309
306
  if (isApp) {
310
307
  // This process IS the APP node — return local data directly
@@ -6,7 +6,7 @@ import os from 'os';
6
6
  import path from 'path';
7
7
  import { RedisNodeRegistry } from '../adapters/redis-node-registry';
8
8
  import type { ContainerInfo, StackConfig } from '../orchestrator/types';
9
- import { getLocalNodeId } from '../utils/node';
9
+ import { getLocalNodeId, getNodeRoleFrom } from '../utils/node';
10
10
  import { getRedisClient, scanKeys } from '../utils/redis';
11
11
  import { packagesFromConfig, type CustomPackageMap, type WorkerPackageMap } from '../../shared/packages';
12
12
 
@@ -83,6 +83,7 @@ interface DoctorNodeRecord {
83
83
  hostname?: string;
84
84
  appVersion?: string;
85
85
  workerMode?: string;
86
+ appRole?: string;
86
87
  isSandbox?: boolean;
87
88
  status?: string;
88
89
  lastHeartbeatAt?: number;
@@ -281,12 +282,12 @@ function countPackages(packages: NormalizedPackages) {
281
282
  return packages.apt.length + packages.npm.length + packages.python.length;
282
283
  }
283
284
 
284
- function getNodeRole(node: { workerMode?: string; isSandbox?: boolean }): 'app' | 'worker' | 'sandbox' {
285
- if (node.isSandbox) {
286
- return 'sandbox';
287
- }
288
- const workerMode = node.workerMode || 'main';
289
- return workerMode === 'worker' || workerMode === 'task' || workerMode === '*' ? 'worker' : 'app';
285
+ function getNodeRole(node: {
286
+ workerMode?: string;
287
+ appRole?: string;
288
+ isSandbox?: boolean;
289
+ }): 'app' | 'worker' | 'sandbox' {
290
+ return getNodeRoleFrom({ workerMode: node.workerMode, appRole: node.appRole, isSandbox: node.isSandbox });
290
291
  }
291
292
 
292
293
  function getSafeEnv() {
@@ -539,11 +540,12 @@ export async function collectLocalDoctorSnapshot(
539
540
  options: DoctorSnapshotOptions = {},
540
541
  ): Promise<DoctorNodeSnapshot> {
541
542
  const workerMode = process.env.WORKER_MODE || 'main';
543
+ const appRole = process.env.APP_ROLE;
542
544
  const node = {
543
545
  hostname: os.hostname(),
544
546
  pid: process.pid,
545
547
  workerMode,
546
- role: getNodeRole({ workerMode, isSandbox: process.env.SKILL_HUB_SANDBOX === 'true' }),
548
+ role: getNodeRole({ workerMode, appRole, isSandbox: process.env.SKILL_HUB_SANDBOX === 'true' }),
547
549
  appVersion: process.env.NOCOBASE_VERSION || process.version,
548
550
  nodeVersion: process.version,
549
551
  platform: process.platform,
@@ -773,7 +775,7 @@ async function collectNodeSnapshots(
773
775
  hostname: node.hostname || 'unknown',
774
776
  pid: Number(node.pid || 0),
775
777
  workerMode,
776
- role: getNodeRole({ workerMode, isSandbox: node.isSandbox }),
778
+ role: getNodeRole({ workerMode, appRole: node.appRole, isSandbox: node.isSandbox }),
777
779
  appVersion: node.appVersion || '',
778
780
  nodeVersion: '',
779
781
  platform: '',
@@ -41,7 +41,12 @@ async function getStack(ctx: Context, stackId?: number | string): Promise<StackC
41
41
  return stack.toJSON() as StackConfig;
42
42
  }
43
43
 
44
- async function assertManagedContainer(ctx: Context, adapter: IOrchestratorAdapter, stack: StackConfig, containerId: string) {
44
+ async function assertManagedContainer(
45
+ ctx: Context,
46
+ adapter: IOrchestratorAdapter,
47
+ stack: StackConfig,
48
+ containerId: string,
49
+ ) {
45
50
  try {
46
51
  await adapter.assertManagedByStack(stack, containerId);
47
52
  } catch (err: any) {
@@ -87,7 +92,9 @@ export const orchestratorActions = {
87
92
  if (rawStatus && typeof rawStatus === 'string') {
88
93
  (c as any).packageStatus = JSON.parse(rawStatus);
89
94
  }
90
- } catch {}
95
+ } catch {
96
+ // Redis may not be configured — skip package status
97
+ }
91
98
  }
92
99
  }
93
100
 
@@ -114,6 +121,10 @@ export const orchestratorActions = {
114
121
  * POST /workerOrchestrator:scale
115
122
  * Body: { stackId: 1, replicas: 3 }
116
123
  * Leader-only
124
+ *
125
+ * Before scaling, resolves queue-to-stack mappings and injects
126
+ * WORKER_MODE into the stack's envVars so new containers only
127
+ * process assigned queues.
117
128
  */
118
129
  async scale(ctx: Context, next: () => Promise<void>) {
119
130
  assertLeader(ctx);
@@ -124,6 +135,47 @@ export const orchestratorActions = {
124
135
  if (replicas < 0 || replicas > 20) ctx.throw(400, 'replicas must be between 0 and 20');
125
136
 
126
137
  const stack = await getStack(ctx, stackId);
138
+
139
+ // ── Resolve queue assignments for this stack ──
140
+ try {
141
+ const mappingsRepo = ctx.db.getRepository('workerQueueMappings');
142
+ if (mappingsRepo) {
143
+ const assigned = await mappingsRepo.find({
144
+ filter: {
145
+ stackId: stack.id,
146
+ enabled: true,
147
+ },
148
+ });
149
+
150
+ const queueNames = assigned.map((m: any) => m.get('queueName') as string).filter(Boolean);
151
+
152
+ if (queueNames.length > 0) {
153
+ const workerMode = queueNames.join(',');
154
+ ctx.app.logger.info(
155
+ `[Orchestrator] Injecting WORKER_MODE=${workerMode} for stack "${stack.name}" (${queueNames.length} queue(s) assigned)`,
156
+ );
157
+ // Merge into envVars; adapter code merges envVars over inherited env
158
+ stack.envVars = {
159
+ ...(stack.envVars || {}),
160
+ WORKER_MODE: workerMode,
161
+ };
162
+ } else {
163
+ // No specific assignment → default to all queues (backwards compatible)
164
+ stack.envVars = {
165
+ ...(stack.envVars || {}),
166
+ WORKER_MODE: '*',
167
+ };
168
+ }
169
+ }
170
+ } catch (err: any) {
171
+ // If workerQueueMappings table doesn't exist yet, fall back gracefully
172
+ ctx.app.logger.debug(`[Orchestrator] Queue mappings not available: ${err.message}`);
173
+ stack.envVars = {
174
+ ...(stack.envVars || {}),
175
+ WORKER_MODE: '*',
176
+ };
177
+ }
178
+
127
179
  const result = await adapter.scale(stack, Number(replicas));
128
180
 
129
181
  // Update DB
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Queue Mappings Actions
3
+ *
4
+ * CRUD for workerQueueMappings + scanQueues action that discovers
5
+ * all registered queues via QueueScanner.
6
+ */
7
+
8
+ import { Context } from '@nocobase/actions';
9
+ import { scanQueues } from '../queue-scanner';
10
+
11
+ export const queueMappingsActions = {
12
+ /**
13
+ * GET /workerQueueMappings:scanQueues
14
+ * Scans all registered queues (EventQueue + Redis) and merges with existing mappings.
15
+ */
16
+ async scanQueues(ctx: Context, next: () => Promise<void>) {
17
+ const result = await scanQueues(ctx.app);
18
+
19
+ // Load existing mappings from DB
20
+ const repo = ctx.db.getRepository('workerQueueMappings');
21
+ let existingMappings: any[] = [];
22
+ try {
23
+ existingMappings = await repo.find();
24
+ } catch {
25
+ // Table may not exist yet
26
+ }
27
+
28
+ const mappedNames = new Set(existingMappings.map((m) => m.get('queueName') as string));
29
+
30
+ ctx.body = {
31
+ discovered: result.queues,
32
+ total: result.total,
33
+ registered: existingMappings.map((m) => ({
34
+ id: m.get('id'),
35
+ queueName: m.get('queueName'),
36
+ label: m.get('label'),
37
+ stackId: m.get('stackId'),
38
+ enabled: m.get('enabled'),
39
+ type: m.get('type'),
40
+ })),
41
+ unmapped: result.queues
42
+ .filter((q) => !mappedNames.has(q.name))
43
+ .map((q) => ({
44
+ name: q.name,
45
+ type: q.type,
46
+ label: q.label,
47
+ description: q.description,
48
+ })),
49
+ };
50
+ await next();
51
+ },
52
+
53
+ /**
54
+ * POST /workerQueueMappings:autoMap
55
+ * Auto-create mappings for any discovered queues that don't have one yet.
56
+ * Body: { stackId?: number } — optional default stack for new mappings
57
+ */
58
+ async autoMap(ctx: Context, next: () => Promise<void>) {
59
+ const { stackId } = ctx.action.params.values || {};
60
+ const result = await scanQueues(ctx.app);
61
+ const repo = ctx.db.getRepository('workerQueueMappings');
62
+
63
+ let existingMappings: any[] = [];
64
+ try {
65
+ existingMappings = await repo.find();
66
+ } catch {
67
+ // Table may not exist yet
68
+ }
69
+
70
+ const mappedNames = new Set(existingMappings.map((m) => m.get('queueName') as string));
71
+ const created: string[] = [];
72
+
73
+ for (const q of result.queues) {
74
+ if (mappedNames.has(q.name)) continue;
75
+ await repo.create({
76
+ values: {
77
+ queueName: q.name,
78
+ label: q.label,
79
+ description: q.description,
80
+ type: q.type,
81
+ stackId: stackId || null,
82
+ enabled: true,
83
+ },
84
+ });
85
+ created.push(q.name);
86
+ }
87
+
88
+ ctx.body = {
89
+ created,
90
+ count: created.length,
91
+ };
92
+ await next();
93
+ },
94
+ };