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.
- package/README.md +17 -5
- package/dist/client/index.js +1 -1
- package/dist/externalVersion.js +5 -5
- package/dist/locale/en-US.json +28 -4
- package/dist/locale/vi-VN.json +28 -4
- package/dist/locale/zh-CN.json +28 -4
- package/dist/server/actions/event-queue-monitor.js +123 -1
- package/dist/server/actions/orchestrator.js +1 -1
- package/dist/server/actions/plugin-operations.js +171 -0
- package/dist/server/collections/cluster-manager-plugins.js +44 -0
- package/dist/server/plugin.js +8 -2
- package/package.json +9 -4
- package/src/client/ClusterManagerLayout.tsx +16 -10
- package/src/client/EventQueueMonitor.tsx +349 -202
- package/src/client/PluginOperations.tsx +234 -0
- package/src/locale/en-US.json +28 -4
- package/src/locale/vi-VN.json +28 -4
- package/src/locale/zh-CN.json +28 -4
- package/src/server/actions/event-queue-monitor.ts +234 -95
- package/src/server/actions/orchestrator.ts +1 -1
- package/src/server/actions/plugin-operations.ts +151 -0
- package/src/server/collections/cluster-manager-plugins.ts +19 -0
- package/src/server/orchestrator/PackageManager.ts +84 -17
- package/src/server/plugin.ts +28 -20
- package/dist/client/AclCacheManager.d.ts +0 -2
- package/dist/client/CacheMonitor.d.ts +0 -2
- package/dist/client/ClusterManagerLayout.d.ts +0 -2
- package/dist/client/ClusterNodes.d.ts +0 -2
- package/dist/client/ContainerOrchestrator.d.ts +0 -2
- package/dist/client/EventQueueMonitor.d.ts +0 -2
- package/dist/client/LockMonitor.d.ts +0 -2
- package/dist/client/PackageInstaller.d.ts +0 -2
- package/dist/client/RedisMonitor.d.ts +0 -2
- package/dist/client/TaskManager.d.ts +0 -2
- package/dist/client/WorkflowExecutions.d.ts +0 -2
- package/dist/client/index.d.ts +0 -5
- package/dist/client/utils.d.ts +0 -12
- package/dist/index.d.ts +0 -2
- package/dist/server/actions/acl-cache.d.ts +0 -53
- package/dist/server/actions/cache-monitor.d.ts +0 -23
- package/dist/server/actions/cluster-nodes.d.ts +0 -49
- package/dist/server/actions/event-queue-monitor.d.ts +0 -13
- package/dist/server/actions/lock-monitor.d.ts +0 -19
- package/dist/server/actions/orchestrator.d.ts +0 -58
- package/dist/server/actions/package-manager.d.ts +0 -6
- package/dist/server/actions/redis-monitor.d.ts +0 -12
- package/dist/server/actions/tasks.d.ts +0 -7
- package/dist/server/actions/workflow-executions.d.ts +0 -7
- package/dist/server/adapters/redis-lock-adapter.d.ts +0 -15
- package/dist/server/adapters/redis-node-registry.d.ts +0 -12
- package/dist/server/adapters/redis-pubsub-adapter.d.ts +0 -16
- package/dist/server/collections/app.d.ts +0 -8
- package/dist/server/collections/cluster-manager-acl-cache.d.ts +0 -22
- package/dist/server/collections/cluster-manager-cache-mgr.d.ts +0 -22
- package/dist/server/collections/cluster-manager-cluster.d.ts +0 -22
- package/dist/server/collections/cluster-manager-lock.d.ts +0 -22
- package/dist/server/collections/cluster-manager-queue.d.ts +0 -22
- package/dist/server/collections/cluster-manager-redis.d.ts +0 -22
- package/dist/server/collections/cluster-manager-workflow.d.ts +0 -22
- package/dist/server/collections/cluster-manager.d.ts +0 -22
- package/dist/server/collections/orchestrator-settings.d.ts +0 -59
- package/dist/server/collections/orchestrator-stacks.d.ts +0 -102
- package/dist/server/collections/worker-orchestrator.d.ts +0 -22
- package/dist/server/collections/worker-packages-configs.d.ts +0 -3
- package/dist/server/collections/worker-packages.d.ts +0 -22
- package/dist/server/index.d.ts +0 -1
- package/dist/server/orchestrator/PackageManager.d.ts +0 -37
- package/dist/server/orchestrator/docker-adapter.d.ts +0 -41
- package/dist/server/orchestrator/index.d.ts +0 -4
- package/dist/server/orchestrator/k8s-adapter.d.ts +0 -50
- package/dist/server/orchestrator/leader-election.d.ts +0 -48
- package/dist/server/orchestrator/types.d.ts +0 -84
- package/dist/server/plugin.d.ts +0 -26
- package/dist/server/utils/node.d.ts +0 -6
- package/dist/server/utils/redis.d.ts +0 -29
- package/dist/shared/packages.d.ts +0 -23
package/dist/externalVersion.js
CHANGED
|
@@ -11,10 +11,10 @@ module.exports = {
|
|
|
11
11
|
"react": "18.2.0",
|
|
12
12
|
"antd": "5.24.2",
|
|
13
13
|
"@ant-design/icons": "5.6.1",
|
|
14
|
-
"@nocobase/client": "2.0.
|
|
14
|
+
"@nocobase/client": "2.0.56",
|
|
15
15
|
"dayjs": "1.11.13",
|
|
16
|
-
"@nocobase/server": "2.0.
|
|
17
|
-
"@nocobase/actions": "2.0.
|
|
18
|
-
"@nocobase/lock-manager": "2.0.
|
|
19
|
-
"@nocobase/database": "2.0.
|
|
16
|
+
"@nocobase/server": "2.0.56",
|
|
17
|
+
"@nocobase/actions": "2.0.56",
|
|
18
|
+
"@nocobase/lock-manager": "2.0.56",
|
|
19
|
+
"@nocobase/database": "2.0.56"
|
|
20
20
|
};
|
package/dist/locale/en-US.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"Cluster Manager": "Cluster Manager",
|
|
3
3
|
"Async Tasks": "Async Tasks",
|
|
4
4
|
"Workflow Executions": "Workflow Executions",
|
|
5
5
|
"Redis Monitor": "Redis Monitor",
|
|
@@ -66,6 +66,30 @@
|
|
|
66
66
|
"This will clear all cached data across all stores.": "This will clear all cached data across all stores.",
|
|
67
67
|
"All caches flushed": "All caches flushed",
|
|
68
68
|
"Failed to flush caches": "Failed to flush caches",
|
|
69
|
-
"Sync Messages": "Sync Messages",
|
|
70
|
-
"Subscribers": "Subscribers"
|
|
71
|
-
|
|
69
|
+
"Sync Messages": "Sync Messages",
|
|
70
|
+
"Subscribers": "Subscribers",
|
|
71
|
+
"Plugins": "Plugins",
|
|
72
|
+
"Plugin": "Plugin",
|
|
73
|
+
"Enabled": "Enabled",
|
|
74
|
+
"Disabled": "Disabled",
|
|
75
|
+
"Installed": "Installed",
|
|
76
|
+
"Not installed": "Not installed",
|
|
77
|
+
"Loaded": "Loaded",
|
|
78
|
+
"Not loaded": "Not loaded",
|
|
79
|
+
"Protected": "Protected",
|
|
80
|
+
"Description": "Description",
|
|
81
|
+
"Force disable": "Force disable",
|
|
82
|
+
"Force remove": "Force remove",
|
|
83
|
+
"Force disable this plugin?": "Force disable this plugin?",
|
|
84
|
+
"Force remove this plugin?": "Force remove this plugin?",
|
|
85
|
+
"This updates the plugin registry directly. Restart or reload is required to fully unload runtime hooks.": "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.": "This removes the plugin registry record. Package files are not deleted. Restart or reload is required.",
|
|
87
|
+
"Force operations bypass plugin lifecycle hooks": "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.": "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": "Search plugins",
|
|
90
|
+
"Plugin force disabled": "Plugin force disabled",
|
|
91
|
+
"Plugin force removed": "Plugin force removed",
|
|
92
|
+
"Failed to load plugins": "Failed to load plugins",
|
|
93
|
+
"Failed to force disable plugin": "Failed to force disable plugin",
|
|
94
|
+
"Failed to force remove plugin": "Failed to force remove plugin"
|
|
95
|
+
}
|
package/dist/locale/vi-VN.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"Cluster Manager": "Quản lý cụm",
|
|
3
3
|
"Async Tasks": "Tác vụ bất đồng bộ",
|
|
4
4
|
"Workflow Executions": "Thực thi Workflow",
|
|
5
5
|
"Redis Monitor": "Giám sát Redis",
|
|
@@ -66,6 +66,30 @@
|
|
|
66
66
|
"This will clear all cached data across all stores.": "Điều này sẽ xóa tất cả dữ liệu đệm trong mọi kho.",
|
|
67
67
|
"All caches flushed": "Đã xóa tất cả bộ nhớ đệm",
|
|
68
68
|
"Failed to flush caches": "Xóa bộ nhớ đệm thất bại",
|
|
69
|
-
"Sync Messages": "Tin nhắn đồng bộ",
|
|
70
|
-
"Subscribers": "Người đăng ký"
|
|
71
|
-
|
|
69
|
+
"Sync Messages": "Tin nhắn đồng bộ",
|
|
70
|
+
"Subscribers": "Người đăng ký",
|
|
71
|
+
"Plugins": "Plugin",
|
|
72
|
+
"Plugin": "Plugin",
|
|
73
|
+
"Enabled": "Đã bật",
|
|
74
|
+
"Disabled": "Đã tắt",
|
|
75
|
+
"Installed": "Đã cài đặt",
|
|
76
|
+
"Not installed": "Chưa cài đặt",
|
|
77
|
+
"Loaded": "Đã tải",
|
|
78
|
+
"Not loaded": "Chưa tải",
|
|
79
|
+
"Protected": "Được bảo vệ",
|
|
80
|
+
"Description": "Mô tả",
|
|
81
|
+
"Force disable": "Buộc tắt",
|
|
82
|
+
"Force remove": "Buộc xóa",
|
|
83
|
+
"Force disable this plugin?": "Buộc tắt plugin này?",
|
|
84
|
+
"Force remove this plugin?": "Buộc xóa plugin này?",
|
|
85
|
+
"This updates the plugin registry directly. Restart or reload is required to fully unload runtime hooks.": "Thao tác này cập nhật trực tiếp registry plugin. Cần restart hoặc reload để gỡ hoàn toàn các hook runtime.",
|
|
86
|
+
"This removes the plugin registry record. Package files are not deleted. Restart or reload is required.": "Thao tác này xóa bản ghi plugin trong registry. File package không bị xóa. Cần restart hoặc reload.",
|
|
87
|
+
"Force operations bypass plugin lifecycle hooks": "Thao tác force bỏ qua lifecycle hook của plugin",
|
|
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.": "Chỉ dùng khi plugin manager bình thường không thể tắt hoặc xóa plugin lỗi. Restart hoặc reload ứng dụng sau khi thao tác thành công.",
|
|
89
|
+
"Search plugins": "Tìm plugin",
|
|
90
|
+
"Plugin force disabled": "Đã buộc tắt plugin",
|
|
91
|
+
"Plugin force removed": "Đã buộc xóa plugin",
|
|
92
|
+
"Failed to load plugins": "Tải danh sách plugin thất bại",
|
|
93
|
+
"Failed to force disable plugin": "Buộc tắt plugin thất bại",
|
|
94
|
+
"Failed to force remove plugin": "Buộc xóa plugin thất bại"
|
|
95
|
+
}
|
package/dist/locale/zh-CN.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"Cluster Manager": "集群管理",
|
|
3
3
|
"Async Tasks": "异步任务",
|
|
4
4
|
"Workflow Executions": "工作流执行",
|
|
5
5
|
"Redis Monitor": "Redis 监控",
|
|
@@ -66,6 +66,30 @@
|
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -29,6 +29,99 @@ __export(event_queue_monitor_exports, {
|
|
|
29
29
|
eventQueueActions: () => eventQueueActions
|
|
30
30
|
});
|
|
31
31
|
module.exports = __toCommonJS(event_queue_monitor_exports);
|
|
32
|
+
var import_redis = require("../utils/redis");
|
|
33
|
+
const REDIS_QUEUE_CONNECTION = "cluster-manager:queue-monitor";
|
|
34
|
+
const REDIS_QUEUE_PATTERNS = ["*:plugin-git-manager:review:queue", "*:plugin-build-guide-block:build:queue"];
|
|
35
|
+
function getQueueRedisUrl() {
|
|
36
|
+
return process.env.QUEUE_ADAPTER_REDIS_URL || process.env.REDIS_URL;
|
|
37
|
+
}
|
|
38
|
+
async function getQueueRedis(ctx) {
|
|
39
|
+
const url = getQueueRedisUrl();
|
|
40
|
+
const manager = ctx.app.redisConnectionManager;
|
|
41
|
+
if (!url || !(manager == null ? void 0 : manager.getConnectionSync)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return manager.getConnectionSync(REDIS_QUEUE_CONNECTION, { connectionString: url });
|
|
45
|
+
}
|
|
46
|
+
function knownRedisQueueKeys(ctx) {
|
|
47
|
+
const appName = ctx.app.name || process.env.APP_NAME || "main";
|
|
48
|
+
return [`${appName}:plugin-git-manager:review:queue`, `${appName}:plugin-build-guide-block:build:queue`];
|
|
49
|
+
}
|
|
50
|
+
function isKnownRedisQueueKey(key) {
|
|
51
|
+
return REDIS_QUEUE_PATTERNS.some((pattern) => {
|
|
52
|
+
const suffix = pattern.replace("*:", "");
|
|
53
|
+
return key === suffix || key.endsWith(`:${suffix}`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function describeRedisQueueKey(key) {
|
|
57
|
+
const parts = String(key).split(":");
|
|
58
|
+
const queue = parts[parts.length - 2] || key;
|
|
59
|
+
const plugin = parts[parts.length - 3] || "unknown";
|
|
60
|
+
const appName = parts.slice(0, Math.max(1, parts.length - 3)).join(":") || "main";
|
|
61
|
+
return {
|
|
62
|
+
appName,
|
|
63
|
+
plugin,
|
|
64
|
+
queue,
|
|
65
|
+
channel: `${plugin}.${queue}`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async function getRedisQueues(ctx) {
|
|
69
|
+
const redis = await getQueueRedis(ctx);
|
|
70
|
+
if (!redis) {
|
|
71
|
+
return {
|
|
72
|
+
connected: false,
|
|
73
|
+
urlConfigured: Boolean(getQueueRedisUrl()),
|
|
74
|
+
queues: [],
|
|
75
|
+
note: "Redis queue connection is not configured"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const keys = new Set(knownRedisQueueKeys(ctx));
|
|
79
|
+
for (const pattern of REDIS_QUEUE_PATTERNS) {
|
|
80
|
+
try {
|
|
81
|
+
const scanned = await (0, import_redis.scanKeys)(redis, pattern, 200);
|
|
82
|
+
scanned.forEach((key) => keys.add(key));
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const queues = [];
|
|
87
|
+
for (const key of keys) {
|
|
88
|
+
if (!isKnownRedisQueueKey(key)) continue;
|
|
89
|
+
let pending = 0;
|
|
90
|
+
try {
|
|
91
|
+
pending = Number(await redis.sendCommand(["LLEN", key])) || 0;
|
|
92
|
+
} catch {
|
|
93
|
+
pending = 0;
|
|
94
|
+
}
|
|
95
|
+
queues.push({
|
|
96
|
+
source: "redis",
|
|
97
|
+
key,
|
|
98
|
+
type: "list",
|
|
99
|
+
pending,
|
|
100
|
+
...describeRedisQueueKey(key)
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
connected: true,
|
|
105
|
+
urlConfigured: true,
|
|
106
|
+
queues,
|
|
107
|
+
totalPending: queues.reduce((sum, queue) => sum + (queue.pending || 0), 0)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function parseRedisQueueMessage(raw, key, index) {
|
|
111
|
+
let content = raw;
|
|
112
|
+
try {
|
|
113
|
+
content = JSON.parse(raw);
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
const queuedAt = (content == null ? void 0 : content.queuedAt) ? Date.parse(content.queuedAt) : null;
|
|
117
|
+
return {
|
|
118
|
+
id: `${key}:${index}`,
|
|
119
|
+
index,
|
|
120
|
+
content,
|
|
121
|
+
raw,
|
|
122
|
+
timestamp: Number.isFinite(queuedAt) ? queuedAt : null
|
|
123
|
+
};
|
|
124
|
+
}
|
|
32
125
|
const eventQueueActions = {
|
|
33
126
|
/**
|
|
34
127
|
* GET /clusterManagerQueue:stats
|
|
@@ -61,11 +154,13 @@ const eventQueueActions = {
|
|
|
61
154
|
}
|
|
62
155
|
}
|
|
63
156
|
const totalPending = channels.reduce((sum, c) => sum + (c.pending || 0), 0);
|
|
157
|
+
const redisQueues = await getRedisQueues(ctx);
|
|
64
158
|
ctx.body = {
|
|
65
159
|
adapter: adapterName,
|
|
66
160
|
connected,
|
|
67
161
|
totalChannels: channels.length,
|
|
68
162
|
totalPending,
|
|
163
|
+
redisQueues,
|
|
69
164
|
channels
|
|
70
165
|
};
|
|
71
166
|
await next();
|
|
@@ -80,7 +175,34 @@ const eventQueueActions = {
|
|
|
80
175
|
if (!eq) {
|
|
81
176
|
ctx.throw(503, "Event queue is not available");
|
|
82
177
|
}
|
|
83
|
-
const { channel, page = 1, pageSize = 20 } = ctx.action.params;
|
|
178
|
+
const { channel, key, source, page = 1, pageSize = 20 } = ctx.action.params;
|
|
179
|
+
if (source === "redis") {
|
|
180
|
+
const redisKey = String(key || channel || "");
|
|
181
|
+
if (!redisKey || !isKnownRedisQueueKey(redisKey)) {
|
|
182
|
+
ctx.throw(400, "Invalid Redis queue key");
|
|
183
|
+
}
|
|
184
|
+
const redis = await getQueueRedis(ctx);
|
|
185
|
+
if (!redis) {
|
|
186
|
+
ctx.body = {
|
|
187
|
+
data: [],
|
|
188
|
+
meta: { count: 0, page: 1, pageSize: 20, note: "Redis queue connection is not configured" }
|
|
189
|
+
};
|
|
190
|
+
await next();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const currentPage = Number(page);
|
|
194
|
+
const currentPageSize = Number(pageSize);
|
|
195
|
+
const start2 = (currentPage - 1) * currentPageSize;
|
|
196
|
+
const end = start2 + currentPageSize - 1;
|
|
197
|
+
const count = Number(await redis.sendCommand(["LLEN", redisKey])) || 0;
|
|
198
|
+
const rows = await redis.sendCommand(["LRANGE", redisKey, String(start2), String(end)]);
|
|
199
|
+
ctx.body = {
|
|
200
|
+
data: rows.map((raw, offset) => parseRedisQueueMessage(raw, redisKey, start2 + offset)),
|
|
201
|
+
meta: { count, page: currentPage, pageSize: currentPageSize, source: "redis", key: redisKey }
|
|
202
|
+
};
|
|
203
|
+
await next();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
84
206
|
const adapter = eq.adapter;
|
|
85
207
|
if (!(adapter == null ? void 0 : adapter.queues)) {
|
|
86
208
|
ctx.body = {
|
|
@@ -33,7 +33,7 @@ var import_redis = require("../utils/redis");
|
|
|
33
33
|
function getAdapter(ctx) {
|
|
34
34
|
const plugin = ctx.app.pm.get("plugin-cluster-manager");
|
|
35
35
|
if (!(plugin == null ? void 0 : plugin.orchestrator)) {
|
|
36
|
-
ctx.throw(503, "Orchestrator adapter not configured. Configure it in
|
|
36
|
+
ctx.throw(503, "Orchestrator adapter not configured. Configure it in Cluster Manager settings.");
|
|
37
37
|
}
|
|
38
38
|
return plugin.orchestrator;
|
|
39
39
|
}
|
|
@@ -0,0 +1,171 @@
|
|
|
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 plugin_operations_exports = {};
|
|
28
|
+
__export(plugin_operations_exports, {
|
|
29
|
+
pluginOperationsActions: () => pluginOperationsActions
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(plugin_operations_exports);
|
|
32
|
+
const PROTECTED_PLUGIN_NAMES = /* @__PURE__ */ new Set(["nocobase", "plugin-cluster-manager"]);
|
|
33
|
+
const PROTECTED_PACKAGE_NAMES = /* @__PURE__ */ new Set(["nocobase", "@nocobase/preset-nocobase", "plugin-cluster-manager"]);
|
|
34
|
+
function getPayload(ctx) {
|
|
35
|
+
var _a, _b, _c;
|
|
36
|
+
return ctx.action.params.values || ((_b = (_a = ctx.request) == null ? void 0 : _a.body) == null ? void 0 : _b.values) || ((_c = ctx.request) == null ? void 0 : _c.body) || {};
|
|
37
|
+
}
|
|
38
|
+
function pluginDisplayName(plugin) {
|
|
39
|
+
return plugin.displayName || plugin.name || plugin.packageName;
|
|
40
|
+
}
|
|
41
|
+
function isProtectedPlugin(plugin) {
|
|
42
|
+
return PROTECTED_PLUGIN_NAMES.has(plugin.name) || PROTECTED_PACKAGE_NAMES.has(plugin.packageName);
|
|
43
|
+
}
|
|
44
|
+
async function getApplicationPlugins(ctx) {
|
|
45
|
+
const repo = ctx.db.getRepository("applicationPlugins");
|
|
46
|
+
const rows = await repo.find({ sort: ["name"] });
|
|
47
|
+
return rows.map((row) => row.toJSON());
|
|
48
|
+
}
|
|
49
|
+
async function findPlugin(ctx, requestedName) {
|
|
50
|
+
const name = String(requestedName || "").trim();
|
|
51
|
+
if (!name) {
|
|
52
|
+
ctx.throw(400, "Plugin name is required.");
|
|
53
|
+
}
|
|
54
|
+
const plugins = await getApplicationPlugins(ctx);
|
|
55
|
+
const plugin = plugins.find((item) => item.name === name || item.packageName === name);
|
|
56
|
+
if (!plugin) {
|
|
57
|
+
ctx.throw(404, `Plugin "${name}" was not found in applicationPlugins.`);
|
|
58
|
+
}
|
|
59
|
+
return plugin;
|
|
60
|
+
}
|
|
61
|
+
async function getLoadedPluginInfo(ctx, plugin, locale) {
|
|
62
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
63
|
+
const pm = ctx.app.pm;
|
|
64
|
+
const instance = ((_a = pm == null ? void 0 : pm.get) == null ? void 0 : _a.call(pm, plugin.name)) || ((_b = pm == null ? void 0 : pm.get) == null ? void 0 : _b.call(pm, plugin.packageName));
|
|
65
|
+
if (!instance) {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return await instance.toJSON({ locale, withOutOpenFile: true });
|
|
70
|
+
} catch {
|
|
71
|
+
return {
|
|
72
|
+
name: instance.name,
|
|
73
|
+
packageName: (_c = instance.options) == null ? void 0 : _c.packageName,
|
|
74
|
+
displayName: (_e = (_d = instance.options) == null ? void 0 : _d.packageJson) == null ? void 0 : _e.displayName,
|
|
75
|
+
description: (_g = (_f = instance.options) == null ? void 0 : _f.packageJson) == null ? void 0 : _g.description
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function getPackageInfo(ctx, plugin, locale) {
|
|
80
|
+
var _a;
|
|
81
|
+
const loadedInfo = await getLoadedPluginInfo(ctx, plugin, locale);
|
|
82
|
+
if (loadedInfo.displayName || loadedInfo.description) {
|
|
83
|
+
return loadedInfo;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const pmCtor = (_a = ctx.app.pm) == null ? void 0 : _a.constructor;
|
|
87
|
+
const pkgJson = await pmCtor.getPackageJson(plugin.packageName);
|
|
88
|
+
return {
|
|
89
|
+
displayName: (pkgJson == null ? void 0 : pkgJson[`displayName.${locale}`]) || (pkgJson == null ? void 0 : pkgJson.displayName) || plugin.name,
|
|
90
|
+
description: (pkgJson == null ? void 0 : pkgJson[`description.${locale}`]) || (pkgJson == null ? void 0 : pkgJson.description),
|
|
91
|
+
keywords: pkgJson == null ? void 0 : pkgJson.keywords
|
|
92
|
+
};
|
|
93
|
+
} catch {
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const pluginOperationsActions = {
|
|
98
|
+
async list(ctx, next) {
|
|
99
|
+
var _a;
|
|
100
|
+
const locale = ((_a = ctx.getCurrentLocale) == null ? void 0 : _a.call(ctx)) || "en-US";
|
|
101
|
+
const plugins = await getApplicationPlugins(ctx);
|
|
102
|
+
const data = await Promise.all(
|
|
103
|
+
plugins.map(async (plugin) => {
|
|
104
|
+
var _a2, _b, _c, _d;
|
|
105
|
+
const info = await getPackageInfo(ctx, plugin, locale);
|
|
106
|
+
const loaded = Boolean(((_b = (_a2 = ctx.app.pm) == null ? void 0 : _a2.get) == null ? void 0 : _b.call(_a2, plugin.name)) || ((_d = (_c = ctx.app.pm) == null ? void 0 : _c.get) == null ? void 0 : _d.call(_c, plugin.packageName)));
|
|
107
|
+
return {
|
|
108
|
+
...plugin,
|
|
109
|
+
displayName: info.displayName || plugin.name,
|
|
110
|
+
description: info.description || "",
|
|
111
|
+
keywords: info.keywords || [],
|
|
112
|
+
loaded,
|
|
113
|
+
protected: isProtectedPlugin(plugin)
|
|
114
|
+
};
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
ctx.body = {
|
|
118
|
+
data,
|
|
119
|
+
meta: { count: data.length }
|
|
120
|
+
};
|
|
121
|
+
await next();
|
|
122
|
+
},
|
|
123
|
+
async forceDisable(ctx, next) {
|
|
124
|
+
var _a, _b, _c, _d;
|
|
125
|
+
const payload = getPayload(ctx);
|
|
126
|
+
const plugin = await findPlugin(ctx, payload.name || payload.packageName || ctx.action.params.filterByTk);
|
|
127
|
+
if (isProtectedPlugin(plugin)) {
|
|
128
|
+
ctx.throw(400, `Plugin "${pluginDisplayName(plugin)}" is protected and cannot be disabled from Cluster Manager.`);
|
|
129
|
+
}
|
|
130
|
+
const repo = ctx.db.getRepository("applicationPlugins");
|
|
131
|
+
await repo.update({
|
|
132
|
+
filter: { name: plugin.name },
|
|
133
|
+
values: { enabled: false }
|
|
134
|
+
});
|
|
135
|
+
const instance = ((_b = (_a = ctx.app.pm) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, plugin.name)) || ((_d = (_c = ctx.app.pm) == null ? void 0 : _c.get) == null ? void 0 : _d.call(_c, plugin.packageName));
|
|
136
|
+
if (instance) {
|
|
137
|
+
instance.enabled = false;
|
|
138
|
+
}
|
|
139
|
+
ctx.body = {
|
|
140
|
+
success: true,
|
|
141
|
+
name: plugin.name,
|
|
142
|
+
packageName: plugin.packageName,
|
|
143
|
+
restartRequired: true,
|
|
144
|
+
message: `Plugin "${pluginDisplayName(plugin)}" was force disabled. Restart or reload the app to fully unload it.`
|
|
145
|
+
};
|
|
146
|
+
await next();
|
|
147
|
+
},
|
|
148
|
+
async forceRemove(ctx, next) {
|
|
149
|
+
const payload = getPayload(ctx);
|
|
150
|
+
const plugin = await findPlugin(ctx, payload.name || payload.packageName || ctx.action.params.filterByTk);
|
|
151
|
+
if (isProtectedPlugin(plugin)) {
|
|
152
|
+
ctx.throw(400, `Plugin "${pluginDisplayName(plugin)}" is protected and cannot be removed from Cluster Manager.`);
|
|
153
|
+
}
|
|
154
|
+
const repo = ctx.db.getRepository("applicationPlugins");
|
|
155
|
+
await repo.destroy({
|
|
156
|
+
filter: { name: plugin.name }
|
|
157
|
+
});
|
|
158
|
+
ctx.body = {
|
|
159
|
+
success: true,
|
|
160
|
+
name: plugin.name,
|
|
161
|
+
packageName: plugin.packageName,
|
|
162
|
+
restartRequired: true,
|
|
163
|
+
message: `Plugin "${pluginDisplayName(plugin)}" was force removed from the application registry. Package files were not deleted.`
|
|
164
|
+
};
|
|
165
|
+
await next();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
169
|
+
0 && (module.exports = {
|
|
170
|
+
pluginOperationsActions
|
|
171
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var cluster_manager_plugins_exports = {};
|
|
28
|
+
__export(cluster_manager_plugins_exports, {
|
|
29
|
+
default: () => cluster_manager_plugins_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(cluster_manager_plugins_exports);
|
|
32
|
+
var cluster_manager_plugins_default = {
|
|
33
|
+
name: "clusterManagerPlugins",
|
|
34
|
+
dumpRules: "skip",
|
|
35
|
+
autoGenId: true,
|
|
36
|
+
createdAt: false,
|
|
37
|
+
updatedAt: false,
|
|
38
|
+
fields: [
|
|
39
|
+
{
|
|
40
|
+
name: "name",
|
|
41
|
+
type: "string"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
};
|
package/dist/server/plugin.js
CHANGED
|
@@ -57,6 +57,7 @@ var import_redis_pubsub_adapter = require("./adapters/redis-pubsub-adapter");
|
|
|
57
57
|
var import_redis_node_registry = require("./adapters/redis-node-registry");
|
|
58
58
|
var import_redis_lock_adapter = require("./adapters/redis-lock-adapter");
|
|
59
59
|
var import_orchestrator = require("./actions/orchestrator");
|
|
60
|
+
var import_plugin_operations = require("./actions/plugin-operations");
|
|
60
61
|
var import_docker_adapter = require("./orchestrator/docker-adapter");
|
|
61
62
|
var import_k8s_adapter = require("./orchestrator/k8s-adapter");
|
|
62
63
|
var import_leader_election = require("./orchestrator/leader-election");
|
|
@@ -247,6 +248,10 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
247
248
|
name: "workerPackages",
|
|
248
249
|
actions: import_package_manager.packageManagerActions
|
|
249
250
|
});
|
|
251
|
+
this.app.resourcer.define({
|
|
252
|
+
name: "clusterManagerPlugins",
|
|
253
|
+
actions: import_plugin_operations.pluginOperationsActions
|
|
254
|
+
});
|
|
250
255
|
const aclCacheMiddleware = (0, import_acl_cache.createAclCacheMiddleware)(this.app);
|
|
251
256
|
this.app.acl.use(aclCacheMiddleware, {
|
|
252
257
|
tag: "aclCache",
|
|
@@ -277,7 +282,8 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
277
282
|
"clusterManagerCacheMgr:*",
|
|
278
283
|
"workerOrchestrator:*",
|
|
279
284
|
"orchestratorStacks:*",
|
|
280
|
-
"workerPackages:*"
|
|
285
|
+
"workerPackages:*",
|
|
286
|
+
"clusterManagerPlugins:*"
|
|
281
287
|
]
|
|
282
288
|
});
|
|
283
289
|
await this.initOrchestrator();
|
|
@@ -354,7 +360,7 @@ class PluginClusterManagerServer extends import_server.Plugin {
|
|
|
354
360
|
k8sNamespace: "nocobase"
|
|
355
361
|
});
|
|
356
362
|
} else {
|
|
357
|
-
this.app.logger.info("[Orchestrator] No adapter configured \u2014 configurable via
|
|
363
|
+
this.app.logger.info("[Orchestrator] No adapter configured \u2014 configurable via Cluster Manager UI");
|
|
358
364
|
}
|
|
359
365
|
}
|
|
360
366
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
"displayName": "Cluster Manager",
|
|
4
4
|
"displayName.zh-CN": "Cluster Manager",
|
|
5
5
|
"description": "Cluster node tracking, task orchestration, worker management, and package distribution",
|
|
6
|
-
"version": "1.1.
|
|
6
|
+
"version": "1.1.7",
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
8
|
"main": "./dist/server/index.js",
|
|
9
|
-
"keywords": [
|
|
9
|
+
"keywords": [
|
|
10
|
+
"Monitoring",
|
|
11
|
+
"DevOps"
|
|
12
|
+
],
|
|
10
13
|
"files": [
|
|
11
14
|
"dist",
|
|
12
15
|
"src",
|
|
@@ -31,7 +34,9 @@
|
|
|
31
34
|
"uuid": "^9.0.1"
|
|
32
35
|
},
|
|
33
36
|
"nocobase": {
|
|
34
|
-
"supportedVersions": [
|
|
37
|
+
"supportedVersions": [
|
|
38
|
+
"2.x"
|
|
39
|
+
],
|
|
35
40
|
"editionLevel": 0
|
|
36
41
|
}
|
|
37
|
-
}
|
|
42
|
+
}
|
|
@@ -4,9 +4,10 @@ import { TaskManager } from './TaskManager';
|
|
|
4
4
|
import { WorkflowExecutions } from './WorkflowExecutions';
|
|
5
5
|
import { ClusterNodes } from './ClusterNodes';
|
|
6
6
|
import { CacheMonitor } from './CacheMonitor';
|
|
7
|
-
import { ContainerOrchestrator } from './ContainerOrchestrator';
|
|
8
|
-
import { PackageInstaller } from './PackageInstaller';
|
|
9
|
-
import {
|
|
7
|
+
import { ContainerOrchestrator } from './ContainerOrchestrator';
|
|
8
|
+
import { PackageInstaller } from './PackageInstaller';
|
|
9
|
+
import { PluginOperations } from './PluginOperations';
|
|
10
|
+
import { useT } from './utils';
|
|
10
11
|
|
|
11
12
|
export function ClusterManagerLayout() {
|
|
12
13
|
const t = useT();
|
|
@@ -40,13 +41,18 @@ export function ClusterManagerLayout() {
|
|
|
40
41
|
label: t('Container Orchestrator'),
|
|
41
42
|
children: <ContainerOrchestrator />,
|
|
42
43
|
},
|
|
43
|
-
{
|
|
44
|
-
key: 'packages',
|
|
45
|
-
label: t('Packages'),
|
|
46
|
-
children: <PackageInstaller />,
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
{
|
|
45
|
+
key: 'packages',
|
|
46
|
+
label: t('Packages'),
|
|
47
|
+
children: <PackageInstaller />,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'plugins',
|
|
51
|
+
label: t('Plugins'),
|
|
52
|
+
children: <PluginOperations />,
|
|
53
|
+
},
|
|
54
|
+
]}
|
|
55
|
+
/>
|
|
50
56
|
</div>
|
|
51
57
|
);
|
|
52
58
|
}
|