plugin-cluster-manager 1.1.7 → 1.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +1 -0
- package/dist/client/AclCacheManager.d.ts +2 -0
- package/dist/client/CacheMonitor.d.ts +2 -0
- package/dist/client/ClusterManagerLayout.d.ts +2 -0
- package/dist/client/ClusterNodes.d.ts +2 -0
- package/dist/client/ContainerOrchestrator.d.ts +2 -0
- package/dist/client/Doctor.d.ts +2 -0
- package/dist/client/EventQueueMonitor.d.ts +2 -0
- package/dist/client/LockMonitor.d.ts +2 -0
- package/dist/client/NginxCacheManager.d.ts +2 -0
- package/dist/client/PackageInstaller.d.ts +2 -0
- package/dist/client/PluginOperations.d.ts +2 -0
- package/dist/client/RedisMonitor.d.ts +2 -0
- package/dist/client/TaskManager.d.ts +2 -0
- package/dist/client/WorkflowExecutions.d.ts +2 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +1 -1
- package/dist/client/utils/clientSafeCache.d.ts +3 -0
- package/dist/client/utils/requestDedupInterceptor.d.ts +2 -0
- package/dist/client/utils.d.ts +12 -0
- package/dist/externalVersion.js +5 -5
- package/dist/index.d.ts +2 -0
- package/dist/locale/en-US.json +97 -1
- package/dist/locale/vi-VN.json +98 -1
- package/dist/locale/zh-CN.json +98 -1
- package/dist/server/actions/acl-cache.d.ts +53 -0
- package/dist/server/actions/acl-cache.js +1 -1
- package/dist/server/actions/cache-monitor.d.ts +33 -0
- package/dist/server/actions/cache-monitor.js +301 -0
- package/dist/server/actions/cluster-nodes.d.ts +64 -0
- package/dist/server/actions/cluster-nodes.js +394 -10
- package/dist/server/actions/doctor.d.ts +82 -0
- package/dist/server/actions/doctor.js +1250 -0
- package/dist/server/actions/event-queue-monitor.d.ts +13 -0
- package/dist/server/actions/lock-monitor.d.ts +19 -0
- package/dist/server/actions/orchestrator.d.ts +58 -0
- package/dist/server/actions/package-manager.d.ts +6 -0
- package/dist/server/actions/plugin-operations.d.ts +6 -0
- package/dist/server/actions/redis-monitor.d.ts +12 -0
- package/dist/server/actions/tasks.d.ts +7 -0
- package/dist/server/actions/workflow-executions.d.ts +7 -0
- package/dist/server/adapters/redis-lock-adapter.d.ts +15 -0
- package/dist/server/adapters/redis-node-registry.d.ts +12 -0
- package/dist/server/adapters/redis-pubsub-adapter.d.ts +16 -0
- package/dist/server/collections/app.d.ts +8 -0
- package/dist/server/collections/cluster-manager-acl-cache.d.ts +22 -0
- package/dist/server/collections/cluster-manager-cache-mgr.d.ts +22 -0
- package/dist/server/collections/cluster-manager-cluster.d.ts +22 -0
- package/dist/server/collections/cluster-manager-doctor-runs.d.ts +3 -0
- package/dist/server/collections/cluster-manager-doctor-runs.js +52 -0
- package/dist/server/collections/cluster-manager-doctor.d.ts +18 -0
- package/dist/server/collections/cluster-manager-doctor.js +44 -0
- package/dist/server/collections/cluster-manager-lock.d.ts +22 -0
- package/dist/server/collections/cluster-manager-plugins.d.ts +18 -0
- package/dist/server/collections/cluster-manager-queue.d.ts +22 -0
- package/dist/server/collections/cluster-manager-redis.d.ts +22 -0
- package/dist/server/collections/cluster-manager-workflow.d.ts +22 -0
- package/dist/server/collections/cluster-manager.d.ts +22 -0
- package/dist/server/collections/orchestrator-settings.d.ts +59 -0
- package/dist/server/collections/orchestrator-stacks.d.ts +102 -0
- package/dist/server/collections/worker-orchestrator.d.ts +22 -0
- package/dist/server/collections/worker-packages-configs.d.ts +3 -0
- package/dist/server/collections/worker-packages.d.ts +22 -0
- package/dist/server/hooks/cacheInvalidationHooks.d.ts +1 -0
- package/dist/server/hooks/cacheInvalidationHooks.js +81 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/middlewares/listMetaCacheMiddleware.d.ts +2 -0
- package/dist/server/middlewares/listMetaCacheMiddleware.js +79 -0
- package/dist/server/orchestrator/PackageManager.d.ts +39 -0
- package/dist/server/orchestrator/PackageManager.js +83 -27
- package/dist/server/orchestrator/docker-adapter.d.ts +41 -0
- package/dist/server/orchestrator/index.d.ts +4 -0
- package/dist/server/orchestrator/k8s-adapter.d.ts +50 -0
- package/dist/server/orchestrator/leader-election.d.ts +48 -0
- package/dist/server/orchestrator/types.d.ts +84 -0
- package/dist/server/plugin.d.ts +26 -0
- package/dist/server/plugin.js +70 -8
- package/dist/server/utils/node.d.ts +6 -0
- package/dist/server/utils/redis.d.ts +29 -0
- package/dist/server/utils/versionManager.d.ts +10 -0
- package/dist/server/utils/versionManager.js +91 -0
- package/dist/shared/packages.d.ts +23 -0
- package/package.json +41 -41
- package/server.js +1 -0
- package/src/client/CacheMonitor.tsx +166 -179
- package/src/client/ClusterManagerLayout.tsx +48 -42
- package/src/client/ClusterNodes.tsx +691 -418
- package/src/client/Doctor.tsx +559 -0
- package/src/client/NginxCacheManager.tsx +415 -0
- package/src/client/PluginOperations.tsx +234 -234
- package/src/client/index.tsx +22 -14
- package/src/client/utils/clientSafeCache.ts +41 -0
- package/src/client/utils/requestDedupInterceptor.ts +213 -0
- package/src/locale/en-US.json +97 -1
- package/src/locale/vi-VN.json +98 -1
- package/src/locale/zh-CN.json +98 -1
- package/src/server/__tests__/doctor.test.ts +53 -0
- package/src/server/actions/acl-cache.ts +272 -272
- package/src/server/actions/cache-monitor.ts +453 -116
- package/src/server/actions/cluster-nodes.ts +882 -378
- package/src/server/actions/doctor.ts +1540 -0
- package/src/server/collections/cluster-manager-doctor-runs.ts +23 -0
- package/src/server/collections/cluster-manager-doctor.ts +19 -0
- package/src/server/hooks/cacheInvalidationHooks.ts +58 -0
- package/src/server/middlewares/listMetaCacheMiddleware.ts +55 -0
- package/src/server/orchestrator/PackageManager.ts +19 -15
- package/src/server/plugin.ts +353 -263
- package/src/server/utils/versionManager.ts +69 -0
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
var __create = Object.create;
|
|
10
11
|
var __defProp = Object.defineProperty;
|
|
11
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
16
|
var __export = (target, all) => {
|
|
15
17
|
for (var name in all)
|
|
@@ -23,6 +25,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
23
25
|
}
|
|
24
26
|
return to;
|
|
25
27
|
};
|
|
28
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
29
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
30
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
31
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
32
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
33
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
34
|
+
mod
|
|
35
|
+
));
|
|
26
36
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
37
|
var cache_monitor_exports = {};
|
|
28
38
|
__export(cache_monitor_exports, {
|
|
@@ -30,6 +40,220 @@ __export(cache_monitor_exports, {
|
|
|
30
40
|
});
|
|
31
41
|
module.exports = __toCommonJS(cache_monitor_exports);
|
|
32
42
|
var import_redis = require("../utils/redis");
|
|
43
|
+
var import_fs = require("fs");
|
|
44
|
+
var import_path = __toESM(require("path"));
|
|
45
|
+
var import_child_process = require("child_process");
|
|
46
|
+
var import_http = __toESM(require("http"));
|
|
47
|
+
var import_https = __toESM(require("https"));
|
|
48
|
+
async function exists(p) {
|
|
49
|
+
try {
|
|
50
|
+
await import_fs.promises.access(p);
|
|
51
|
+
return true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isSafePath(dirPath) {
|
|
57
|
+
if (!dirPath) return false;
|
|
58
|
+
const resolved = import_path.default.resolve(dirPath);
|
|
59
|
+
const normalized = resolved.toLowerCase().replace(/\\/g, "/");
|
|
60
|
+
const restrictedPatterns = [
|
|
61
|
+
"^/$",
|
|
62
|
+
"^[a-z]:/?$",
|
|
63
|
+
"^/[^/]+$",
|
|
64
|
+
"^[a-z]:/[^/]+$",
|
|
65
|
+
"/windows",
|
|
66
|
+
"/system32",
|
|
67
|
+
"/program files",
|
|
68
|
+
"/etc",
|
|
69
|
+
"/var$",
|
|
70
|
+
"/usr$",
|
|
71
|
+
"/boot",
|
|
72
|
+
"/sys",
|
|
73
|
+
"/proc",
|
|
74
|
+
"/dev",
|
|
75
|
+
"/home$",
|
|
76
|
+
"/root$"
|
|
77
|
+
];
|
|
78
|
+
for (const pat of restrictedPatterns) {
|
|
79
|
+
const re = new RegExp(pat);
|
|
80
|
+
if (re.test(normalized)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
85
|
+
const nonDriveParts = parts.filter((p) => !/^[a-z]:$/.test(p));
|
|
86
|
+
if (nonDriveParts.length < 2) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
async function findNginxConfig() {
|
|
92
|
+
const nginxConfPath = await new Promise((resolve) => {
|
|
93
|
+
(0, import_child_process.exec)("nginx -V", (err, stdout, stderr) => {
|
|
94
|
+
if (err) return resolve(null);
|
|
95
|
+
const output = stdout + stderr;
|
|
96
|
+
const match = output.match(/--conf-path=([^\s]+)/);
|
|
97
|
+
if (match && match[1]) {
|
|
98
|
+
resolve(match[1]);
|
|
99
|
+
} else {
|
|
100
|
+
resolve(null);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
if (nginxConfPath && await exists(nginxConfPath)) {
|
|
105
|
+
return nginxConfPath;
|
|
106
|
+
}
|
|
107
|
+
const searchPaths = [
|
|
108
|
+
"/etc/nginx/nginx.conf",
|
|
109
|
+
"/usr/local/nginx/conf/nginx.conf",
|
|
110
|
+
"/usr/local/etc/nginx/nginx.conf",
|
|
111
|
+
"/opt/homebrew/etc/nginx/nginx.conf",
|
|
112
|
+
"C:\\nginx\\conf\\nginx.conf"
|
|
113
|
+
];
|
|
114
|
+
for (const p of searchPaths) {
|
|
115
|
+
if (await exists(p)) {
|
|
116
|
+
return p;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
async function readConfigRecursive(configPath, visited = /* @__PURE__ */ new Set()) {
|
|
122
|
+
const resolvedPath = import_path.default.resolve(configPath);
|
|
123
|
+
if (visited.has(resolvedPath)) return "";
|
|
124
|
+
visited.add(resolvedPath);
|
|
125
|
+
try {
|
|
126
|
+
const content = await import_fs.promises.readFile(resolvedPath, "utf8");
|
|
127
|
+
const lines = content.split(/\r?\n/);
|
|
128
|
+
let fullText = content + "\n";
|
|
129
|
+
const configDir = import_path.default.dirname(resolvedPath);
|
|
130
|
+
for (const line of lines) {
|
|
131
|
+
const trimmed = line.trim();
|
|
132
|
+
if (trimmed.startsWith("#")) continue;
|
|
133
|
+
const includeMatch = trimmed.match(/^include\s+([^\s;]+)/);
|
|
134
|
+
if (includeMatch && includeMatch[1]) {
|
|
135
|
+
let includePattern = includeMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
136
|
+
if (!import_path.default.isAbsolute(includePattern)) {
|
|
137
|
+
includePattern = import_path.default.join(configDir, includePattern);
|
|
138
|
+
}
|
|
139
|
+
if (includePattern.includes("*")) {
|
|
140
|
+
try {
|
|
141
|
+
const patternDir = import_path.default.dirname(includePattern);
|
|
142
|
+
const ext = import_path.default.extname(includePattern);
|
|
143
|
+
if (await exists(patternDir)) {
|
|
144
|
+
const files = await import_fs.promises.readdir(patternDir);
|
|
145
|
+
for (const file of files) {
|
|
146
|
+
if (file.endsWith(ext) || includePattern.endsWith("*")) {
|
|
147
|
+
const filePath = import_path.default.join(patternDir, file);
|
|
148
|
+
fullText += await readConfigRecursive(filePath, visited) + "\n";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
if (await exists(includePattern)) {
|
|
156
|
+
fullText += await readConfigRecursive(includePattern, visited) + "\n";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return fullText;
|
|
162
|
+
} catch {
|
|
163
|
+
return "";
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function extractCachePaths(configText) {
|
|
167
|
+
const paths = [];
|
|
168
|
+
const lines = configText.split(/\r?\n/);
|
|
169
|
+
for (const line of lines) {
|
|
170
|
+
const trimmed = line.trim();
|
|
171
|
+
if (trimmed.startsWith("#")) continue;
|
|
172
|
+
const match = trimmed.match(/^(?:proxy|fastcgi|scgi|uwsgi)_cache_path\s+([^\s;]+)/);
|
|
173
|
+
if (match && match[1]) {
|
|
174
|
+
const p = match[1].trim();
|
|
175
|
+
const cleanPath = p.replace(/^["']|["']$/g, "");
|
|
176
|
+
if (cleanPath && !paths.includes(cleanPath)) {
|
|
177
|
+
paths.push(cleanPath);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return paths;
|
|
182
|
+
}
|
|
183
|
+
async function emptyDirectory(dirPath) {
|
|
184
|
+
if (!isSafePath(dirPath)) {
|
|
185
|
+
return { success: false, clearedCount: 0, error: "Path is classified as unsafe or restricted" };
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
if (!await exists(dirPath)) {
|
|
189
|
+
return { success: false, clearedCount: 0, error: "Directory does not exist" };
|
|
190
|
+
}
|
|
191
|
+
const stats = await import_fs.promises.stat(dirPath);
|
|
192
|
+
if (!stats.isDirectory()) {
|
|
193
|
+
return { success: false, clearedCount: 0, error: "Path is not a directory" };
|
|
194
|
+
}
|
|
195
|
+
const files = await import_fs.promises.readdir(dirPath);
|
|
196
|
+
let clearedCount = 0;
|
|
197
|
+
for (const file of files) {
|
|
198
|
+
const fullPath = import_path.default.join(dirPath, file);
|
|
199
|
+
await import_fs.promises.rm(fullPath, { recursive: true, force: true });
|
|
200
|
+
clearedCount++;
|
|
201
|
+
}
|
|
202
|
+
return { success: true, clearedCount };
|
|
203
|
+
} catch (err) {
|
|
204
|
+
return { success: false, clearedCount: 0, error: err.message };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function makePurgeRequest(urlStr, method = "PURGE", headers = {}) {
|
|
208
|
+
return new Promise((resolve) => {
|
|
209
|
+
try {
|
|
210
|
+
const url = new URL(urlStr);
|
|
211
|
+
const isHttps = url.protocol === "https:";
|
|
212
|
+
const client = isHttps ? import_https.default : import_http.default;
|
|
213
|
+
const reqHeaders = { ...headers };
|
|
214
|
+
const req = client.request(
|
|
215
|
+
urlStr,
|
|
216
|
+
{
|
|
217
|
+
method,
|
|
218
|
+
headers: reqHeaders,
|
|
219
|
+
timeout: 1e4
|
|
220
|
+
},
|
|
221
|
+
(res) => {
|
|
222
|
+
let body = "";
|
|
223
|
+
res.on("data", (chunk) => {
|
|
224
|
+
body += chunk;
|
|
225
|
+
});
|
|
226
|
+
res.on("end", () => {
|
|
227
|
+
resolve({
|
|
228
|
+
success: (res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300,
|
|
229
|
+
status: res.statusCode,
|
|
230
|
+
data: body
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
req.on("error", (err) => {
|
|
236
|
+
resolve({
|
|
237
|
+
success: false,
|
|
238
|
+
error: err.message
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
req.on("timeout", () => {
|
|
242
|
+
req.destroy();
|
|
243
|
+
resolve({
|
|
244
|
+
success: false,
|
|
245
|
+
error: "Request timed out after 10 seconds"
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
req.end();
|
|
249
|
+
} catch (err) {
|
|
250
|
+
resolve({
|
|
251
|
+
success: false,
|
|
252
|
+
error: err.message
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
33
257
|
const cacheMonitorActions = {
|
|
34
258
|
/**
|
|
35
259
|
* GET /clusterManagerCacheMgr:stores
|
|
@@ -125,6 +349,83 @@ const cacheMonitorActions = {
|
|
|
125
349
|
await cm.flushAll();
|
|
126
350
|
ctx.body = { success: true };
|
|
127
351
|
await next();
|
|
352
|
+
},
|
|
353
|
+
/**
|
|
354
|
+
* GET /clusterManagerCacheMgr:nginxCacheStatus
|
|
355
|
+
* Detect if Nginx is installed, locate conf, and auto-load cache paths
|
|
356
|
+
*/
|
|
357
|
+
async nginxCacheStatus(ctx, next) {
|
|
358
|
+
let nginxInstalled = false;
|
|
359
|
+
let mainConfigPath = null;
|
|
360
|
+
let detectedPaths = [];
|
|
361
|
+
try {
|
|
362
|
+
const configText = await new Promise((resolve) => {
|
|
363
|
+
(0, import_child_process.exec)("nginx -T", { maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
364
|
+
if (err) {
|
|
365
|
+
resolve(null);
|
|
366
|
+
} else {
|
|
367
|
+
nginxInstalled = true;
|
|
368
|
+
resolve(stdout);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
if (configText) {
|
|
373
|
+
detectedPaths = extractCachePaths(configText);
|
|
374
|
+
mainConfigPath = await findNginxConfig();
|
|
375
|
+
} else {
|
|
376
|
+
mainConfigPath = await findNginxConfig();
|
|
377
|
+
if (mainConfigPath) {
|
|
378
|
+
nginxInstalled = true;
|
|
379
|
+
const fullConfigText = await readConfigRecursive(mainConfigPath);
|
|
380
|
+
detectedPaths = extractCachePaths(fullConfigText);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
} catch (err) {
|
|
384
|
+
}
|
|
385
|
+
ctx.body = {
|
|
386
|
+
nginxInstalled,
|
|
387
|
+
mainConfigPath,
|
|
388
|
+
detectedPaths
|
|
389
|
+
};
|
|
390
|
+
await next();
|
|
391
|
+
},
|
|
392
|
+
/**
|
|
393
|
+
* POST /clusterManagerCacheMgr:clearNginxCache
|
|
394
|
+
* Clear physical cache files or send an HTTP Purge request
|
|
395
|
+
*/
|
|
396
|
+
async clearNginxCache(ctx, next) {
|
|
397
|
+
const { method = "directory", directory, url, httpMethod = "PURGE", headers = {} } = ctx.action.params.values || {};
|
|
398
|
+
if (method === "directory") {
|
|
399
|
+
if (!directory) {
|
|
400
|
+
ctx.throw(400, "Directory path is required for physical cache clearing");
|
|
401
|
+
}
|
|
402
|
+
const result = await emptyDirectory(directory);
|
|
403
|
+
if (!result.success) {
|
|
404
|
+
ctx.throw(400, result.error || "Failed to clear cache directory");
|
|
405
|
+
}
|
|
406
|
+
ctx.body = {
|
|
407
|
+
success: true,
|
|
408
|
+
message: `Successfully cleared physical cache directory`,
|
|
409
|
+
clearedCount: result.clearedCount
|
|
410
|
+
};
|
|
411
|
+
} else if (method === "purgeRequest") {
|
|
412
|
+
if (!url) {
|
|
413
|
+
ctx.throw(400, "Purge URL is required for HTTP Purge request method");
|
|
414
|
+
}
|
|
415
|
+
const result = await makePurgeRequest(url, httpMethod, headers);
|
|
416
|
+
if (!result.success) {
|
|
417
|
+
ctx.throw(400, result.error || `HTTP Purge request failed with status: ${result.status}`);
|
|
418
|
+
}
|
|
419
|
+
ctx.body = {
|
|
420
|
+
success: true,
|
|
421
|
+
message: `HTTP Purge request sent successfully`,
|
|
422
|
+
status: result.status,
|
|
423
|
+
data: result.data
|
|
424
|
+
};
|
|
425
|
+
} else {
|
|
426
|
+
ctx.throw(400, `Unknown clearing method: ${method}`);
|
|
427
|
+
}
|
|
428
|
+
await next();
|
|
128
429
|
}
|
|
129
430
|
};
|
|
130
431
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -0,0 +1,64 @@
|
|
|
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:drift
|
|
29
|
+
* Reports version/runtime/package drift across active cluster nodes.
|
|
30
|
+
*/
|
|
31
|
+
drift(ctx: Context, next: () => Promise<void>): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* GET /clusterManagerCluster:legacyDiagnostics
|
|
34
|
+
* Detects deprecated legacy multi-app plugins and leftover application records.
|
|
35
|
+
*/
|
|
36
|
+
legacyDiagnostics(ctx: Context, next: () => Promise<void>): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* GET /clusterManagerCluster:health
|
|
39
|
+
* Health check for all subsystems
|
|
40
|
+
*/
|
|
41
|
+
health(ctx: Context, next: () => Promise<void>): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* POST /clusterManagerCluster:restart
|
|
44
|
+
* Publishes a restart signal to target nodes orchestrating a soft NocoBase restart or a hard docker daemon rebirth
|
|
45
|
+
*/
|
|
46
|
+
restart(ctx: Context, next: () => Promise<void>): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* POST /clusterManagerCluster:rollingRestart
|
|
49
|
+
* Restarts online nodes one-by-one, optionally filtered by role.
|
|
50
|
+
*/
|
|
51
|
+
rollingRestart(ctx: Context, next: () => Promise<void>): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* GET /clusterManagerCluster:logs?targetNodeId=xxx&lines=200
|
|
54
|
+
*
|
|
55
|
+
* HA-aware log viewer. Reads logs from a specific node in the cluster.
|
|
56
|
+
*
|
|
57
|
+
* Flow:
|
|
58
|
+
* 1. If targetNodeId matches current node (or is empty) → read local FS directly
|
|
59
|
+
* 2. Otherwise → publish a log request via PubSub → target node reads its local FS
|
|
60
|
+
* and writes the result to a Redis key → this handler polls Redis until the
|
|
61
|
+
* response arrives (max 10s) → returns it to the client
|
|
62
|
+
*/
|
|
63
|
+
logs(ctx: Context, next: () => Promise<void>): Promise<void>;
|
|
64
|
+
};
|