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
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { APIClient } from '@nocobase/client';
|
|
2
|
+
|
|
3
|
+
const CLIENT_CACHE_TTL_MS = 60 * 1000;
|
|
4
|
+
const CACHE_KEY_PREFIX = 'nb_cache';
|
|
5
|
+
const CACHE_VERSION_PREFIX = 'nb_cache_version';
|
|
6
|
+
|
|
7
|
+
const pendingRequests = new Map<string, Promise<unknown>>();
|
|
8
|
+
const wrappedClients = new WeakSet<APIClient>();
|
|
9
|
+
|
|
10
|
+
interface RequestConfig {
|
|
11
|
+
url?: string;
|
|
12
|
+
method?: string;
|
|
13
|
+
resource?: string;
|
|
14
|
+
resourceOf?: unknown;
|
|
15
|
+
action?: string;
|
|
16
|
+
params?: unknown;
|
|
17
|
+
data?: unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface CacheScope {
|
|
21
|
+
prefix: string;
|
|
22
|
+
versionKey: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface CacheEntry {
|
|
26
|
+
expiresAt: number;
|
|
27
|
+
scopeVersion: number;
|
|
28
|
+
response: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getRequestInfo(config: string | RequestConfig) {
|
|
32
|
+
if (typeof config === 'string') {
|
|
33
|
+
return {
|
|
34
|
+
url: config,
|
|
35
|
+
method: 'get',
|
|
36
|
+
params: {},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const cfg = config as RequestConfig;
|
|
41
|
+
let url = cfg.url || '';
|
|
42
|
+
let method = (cfg.method || 'get').toLowerCase();
|
|
43
|
+
const params = cfg.params || {};
|
|
44
|
+
|
|
45
|
+
if (cfg.resource && cfg.action) {
|
|
46
|
+
const of = cfg.resourceOf;
|
|
47
|
+
let computedUrl = cfg.resource.split('.').join(of ? `/${encodeURIComponent(String(of))}/` : '/');
|
|
48
|
+
computedUrl += `:${cfg.action}`;
|
|
49
|
+
url = computedUrl;
|
|
50
|
+
method = ['get', 'list', 'listMeta', 'getJsonSchema'].includes(cfg.action) ? 'get' : 'post';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { url, method, params };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getStorage() {
|
|
57
|
+
try {
|
|
58
|
+
return typeof sessionStorage === 'undefined' ? null : sessionStorage;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function stringifyParams(params: unknown) {
|
|
65
|
+
try {
|
|
66
|
+
return JSON.stringify(params || {});
|
|
67
|
+
} catch {
|
|
68
|
+
return '[unserializable]';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isCacheableSchemaRequest(url: string) {
|
|
73
|
+
return url.includes('collections:listMeta') || url.includes('uiSchemas:getJsonSchema');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getCacheScope(apiClient: APIClient): CacheScope {
|
|
77
|
+
const appName = apiClient.app?.getName() || 'main';
|
|
78
|
+
const role = apiClient.auth?.role || 'anonymous';
|
|
79
|
+
const token = apiClient.auth?.token || 'anonymous';
|
|
80
|
+
const safeToken = typeof token === 'string' ? token.slice(-8) : 'anonymous';
|
|
81
|
+
const scope = `${appName}:${safeToken}:${role}`;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
prefix: `${CACHE_KEY_PREFIX}:${scope}:`,
|
|
85
|
+
versionKey: `${CACHE_VERSION_PREFIX}:${scope}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getScopeVersion(storage: Storage, scope: CacheScope) {
|
|
90
|
+
try {
|
|
91
|
+
const raw = storage.getItem(scope.versionKey);
|
|
92
|
+
const version = raw ? Number(raw) : 0;
|
|
93
|
+
return Number.isFinite(version) ? version : 0;
|
|
94
|
+
} catch {
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function clearByPrefix(storage: Storage, prefix: string) {
|
|
100
|
+
const keys: string[] = [];
|
|
101
|
+
for (let index = 0; index < storage.length; index += 1) {
|
|
102
|
+
const key = storage.key(index);
|
|
103
|
+
if (key?.startsWith(prefix)) {
|
|
104
|
+
keys.push(key);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const key of keys) {
|
|
109
|
+
storage.removeItem(key);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function bumpScopeVersion(apiClient: APIClient) {
|
|
114
|
+
const storage = getStorage();
|
|
115
|
+
if (!storage) return;
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const scope = getCacheScope(apiClient);
|
|
119
|
+
const nextVersion = getScopeVersion(storage, scope) + 1;
|
|
120
|
+
storage.setItem(scope.versionKey, String(nextVersion));
|
|
121
|
+
clearByPrefix(storage, scope.prefix);
|
|
122
|
+
} catch {
|
|
123
|
+
// Ignore storage failures; the network response remains authoritative.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isSuccessfulResponse(response: unknown) {
|
|
128
|
+
const status =
|
|
129
|
+
response && typeof response === 'object' && 'status' in response
|
|
130
|
+
? (response as { status?: unknown }).status
|
|
131
|
+
: undefined;
|
|
132
|
+
return typeof status !== 'number' || (status >= 200 && status < 400);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function setupRequestDedupAndCache(apiClient: APIClient) {
|
|
136
|
+
if (!apiClient) return;
|
|
137
|
+
if (wrappedClients.has(apiClient)) return;
|
|
138
|
+
wrappedClients.add(apiClient);
|
|
139
|
+
|
|
140
|
+
const originalRequest = apiClient.request.bind(apiClient);
|
|
141
|
+
|
|
142
|
+
apiClient.request = async (config) => {
|
|
143
|
+
const { url, method, params } = getRequestInfo(config as string | RequestConfig);
|
|
144
|
+
|
|
145
|
+
if (method !== 'get') {
|
|
146
|
+
const response = await originalRequest(config);
|
|
147
|
+
if (isSuccessfulResponse(response)) {
|
|
148
|
+
bumpScopeVersion(apiClient);
|
|
149
|
+
}
|
|
150
|
+
return response;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Only cache/deduplicate listMeta and uiSchemas to avoid altering standard operations
|
|
154
|
+
if (!url || !isCacheableSchemaRequest(url)) {
|
|
155
|
+
return originalRequest(config);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const storage = getStorage();
|
|
159
|
+
const scope = getCacheScope(apiClient);
|
|
160
|
+
const scopeVersion = storage ? getScopeVersion(storage, scope) : 0;
|
|
161
|
+
const paramsKey = stringifyParams(params);
|
|
162
|
+
|
|
163
|
+
const cacheKey = `${scope.prefix}v${scopeVersion}:${url}:${paramsKey}`;
|
|
164
|
+
|
|
165
|
+
if (storage) {
|
|
166
|
+
try {
|
|
167
|
+
const cached = storage.getItem(cacheKey);
|
|
168
|
+
if (cached) {
|
|
169
|
+
const entry = JSON.parse(cached) as CacheEntry;
|
|
170
|
+
if (entry.expiresAt > Date.now() && entry.scopeVersion === scopeVersion) {
|
|
171
|
+
return entry.response;
|
|
172
|
+
}
|
|
173
|
+
storage.removeItem(cacheKey);
|
|
174
|
+
}
|
|
175
|
+
} catch {
|
|
176
|
+
// Fallback to the network on sessionStorage or JSON failures.
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const dedupKey = `${scope.prefix}v${scopeVersion}:${url}:${paramsKey}`;
|
|
181
|
+
if (pendingRequests.has(dedupKey)) {
|
|
182
|
+
return pendingRequests.get(dedupKey);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const promise = originalRequest(config).then(
|
|
186
|
+
(response) => {
|
|
187
|
+
pendingRequests.delete(dedupKey);
|
|
188
|
+
|
|
189
|
+
// 3. Cache successful responses
|
|
190
|
+
if (storage && response && response.status === 200 && response.data) {
|
|
191
|
+
try {
|
|
192
|
+
const entry: CacheEntry = {
|
|
193
|
+
expiresAt: Date.now() + CLIENT_CACHE_TTL_MS,
|
|
194
|
+
scopeVersion,
|
|
195
|
+
response,
|
|
196
|
+
};
|
|
197
|
+
storage.setItem(cacheKey, JSON.stringify(entry));
|
|
198
|
+
} catch {
|
|
199
|
+
// Ignore quota errors
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return response;
|
|
203
|
+
},
|
|
204
|
+
(error) => {
|
|
205
|
+
pendingRequests.delete(dedupKey);
|
|
206
|
+
throw error;
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
pendingRequests.set(dedupKey, promise);
|
|
211
|
+
return promise;
|
|
212
|
+
};
|
|
213
|
+
}
|
package/src/locale/en-US.json
CHANGED
|
@@ -91,5 +91,101 @@
|
|
|
91
91
|
"Plugin force removed": "Plugin force removed",
|
|
92
92
|
"Failed to load plugins": "Failed to load plugins",
|
|
93
93
|
"Failed to force disable plugin": "Failed to force disable plugin",
|
|
94
|
-
"Failed to force remove plugin": "Failed to force remove plugin"
|
|
94
|
+
"Failed to force remove plugin": "Failed to force remove plugin",
|
|
95
|
+
"Nginx Cache": "Nginx Cache",
|
|
96
|
+
"Nginx configuration file found at": "Nginx configuration file found at",
|
|
97
|
+
"Nginx cache paths detected": "Nginx cache paths detected",
|
|
98
|
+
"Nginx is not detected on this node. You can still input a custom cache directory.": "Nginx is not detected on this node. You can still input a custom cache directory.",
|
|
99
|
+
"Clearing Method": "Clearing Method",
|
|
100
|
+
"Physical Files": "Physical Files",
|
|
101
|
+
"HTTP Purge Request": "HTTP Purge Request",
|
|
102
|
+
"Select Cache Path": "Select Cache Path",
|
|
103
|
+
"Custom Path": "Custom Path",
|
|
104
|
+
"Purge URL": "Purge URL",
|
|
105
|
+
"Headers (JSON)": "Headers (JSON)",
|
|
106
|
+
"Clear Nginx Cache": "Clear Nginx Cache",
|
|
107
|
+
"Are you sure you want to clear Nginx cache? This will permanently delete all files in this directory.": "Are you sure you want to clear Nginx cache? This will permanently delete all files in this directory.",
|
|
108
|
+
"Cache cleared successfully. Cleared {count} items.": "Cache cleared successfully. Cleared {count} items.",
|
|
109
|
+
"HTTP Purge request completed. Status: {status}": "HTTP Purge request completed. Status: {status}",
|
|
110
|
+
"Custom Cache Path": "Custom Cache Path",
|
|
111
|
+
"HTTP Method": "HTTP Method",
|
|
112
|
+
"Send Purge Request": "Send Purge Request",
|
|
113
|
+
"Please select or enter a cache path": "Please select or enter a cache path",
|
|
114
|
+
"Please enter a valid Purge URL": "Please enter a valid Purge URL",
|
|
115
|
+
"Nginx cache path is required": "Nginx cache path is required",
|
|
116
|
+
"Invalid headers JSON structure": "Invalid headers JSON structure",
|
|
117
|
+
"Nginx Cache Status": "Nginx Cache Status",
|
|
118
|
+
"Nginx is installed": "Nginx is installed",
|
|
119
|
+
"Nginx is NOT installed": "Nginx is NOT installed",
|
|
120
|
+
"Nginx cache clearing method": "Nginx cache clearing method",
|
|
121
|
+
"Execution Logs": "Execution Logs",
|
|
122
|
+
"Clear Nginx Cache?": "Clear Nginx Cache?",
|
|
123
|
+
"Rolling Restart": "Rolling Restart",
|
|
124
|
+
"Worker nodes only": "Worker nodes only",
|
|
125
|
+
"App nodes only": "App nodes only",
|
|
126
|
+
"Sandbox nodes only": "Sandbox nodes only",
|
|
127
|
+
"All nodes": "All nodes",
|
|
128
|
+
"Soft restart": "Soft restart",
|
|
129
|
+
"Hard restart": "Hard restart",
|
|
130
|
+
"Start rolling restart?": "Start rolling restart?",
|
|
131
|
+
"Nodes will receive restart commands one-by-one with the configured delay.": "Nodes will receive restart commands one-by-one with the configured delay.",
|
|
132
|
+
"Start": "Start",
|
|
133
|
+
"Rolling restart dispatched for {count} node(s)": "Rolling restart dispatched for {count} node(s)",
|
|
134
|
+
"Failed to dispatch rolling restart": "Failed to dispatch rolling restart",
|
|
135
|
+
"Cluster Drift": "Cluster Drift",
|
|
136
|
+
"No cluster drift detected": "No cluster drift detected",
|
|
137
|
+
"Cluster drift detected": "Cluster drift detected",
|
|
138
|
+
"Reference version": "Reference version",
|
|
139
|
+
"No reference version available": "No reference version available",
|
|
140
|
+
"Checked Nodes": "Checked Nodes",
|
|
141
|
+
"Version Drift": "Version Drift",
|
|
142
|
+
"Runtime Drift": "Runtime Drift",
|
|
143
|
+
"Package Drift": "Package Drift",
|
|
144
|
+
"Runtime": "Runtime",
|
|
145
|
+
"Role": "Role",
|
|
146
|
+
"Expected": "Expected",
|
|
147
|
+
"Actual": "Actual",
|
|
148
|
+
"Package Status": "Package Status",
|
|
149
|
+
"Missing Packages": "Missing Packages",
|
|
150
|
+
"No missing packages": "No missing packages",
|
|
151
|
+
"Legacy Multi-app Diagnostics": "Legacy Multi-app Diagnostics",
|
|
152
|
+
"No legacy multi-app risk detected": "No legacy multi-app risk detected",
|
|
153
|
+
"Legacy multi-app risk detected": "Legacy multi-app risk detected",
|
|
154
|
+
"Legacy app records": "Legacy app records",
|
|
155
|
+
"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.",
|
|
156
|
+
"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.",
|
|
157
|
+
"{count} legacy application record(s) were found in the applications collection.": "{count} legacy application record(s) were found in the applications collection.",
|
|
158
|
+
"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.",
|
|
159
|
+
"Doctor": "Doctor",
|
|
160
|
+
"Duration": "Duration",
|
|
161
|
+
"Start Doctor": "Start Doctor",
|
|
162
|
+
"Stop Doctor": "Stop Doctor",
|
|
163
|
+
"Download Report": "Download Report",
|
|
164
|
+
"Diagnostic session started": "Diagnostic session started",
|
|
165
|
+
"Diagnostic report is ready": "Diagnostic report is ready",
|
|
166
|
+
"Failed to load diagnostic status": "Failed to load diagnostic status",
|
|
167
|
+
"Failed to start diagnostic session": "Failed to start diagnostic session",
|
|
168
|
+
"Failed to stop diagnostic session": "Failed to stop diagnostic session",
|
|
169
|
+
"Failed to download diagnostic report": "Failed to download diagnostic report",
|
|
170
|
+
"Running": "Running",
|
|
171
|
+
"Run ID": "Run ID",
|
|
172
|
+
"Started At": "Started At",
|
|
173
|
+
"Finished At": "Finished At",
|
|
174
|
+
"Finish Reason": "Finish Reason",
|
|
175
|
+
"Report Status": "Report Status",
|
|
176
|
+
"Nodes": "Nodes",
|
|
177
|
+
"Errors": "Errors",
|
|
178
|
+
"Warnings": "Warnings",
|
|
179
|
+
"Plugin Drift": "Plugin Drift",
|
|
180
|
+
"Findings": "Findings",
|
|
181
|
+
"Node Log Distribution": "Node Log Distribution",
|
|
182
|
+
"Top Error Signatures": "Top Error Signatures",
|
|
183
|
+
"Level": "Level",
|
|
184
|
+
"Count": "Count",
|
|
185
|
+
"Signature": "Signature",
|
|
186
|
+
"Node": "Node",
|
|
187
|
+
"Log Files": "Log Files",
|
|
188
|
+
"Package": "Package",
|
|
189
|
+
"DB Version": "DB Version",
|
|
190
|
+
"Runtime Versions": "Runtime Versions"
|
|
95
191
|
}
|
package/src/locale/vi-VN.json
CHANGED
|
@@ -91,5 +91,102 @@
|
|
|
91
91
|
"Plugin force removed": "Đã buộc xóa plugin",
|
|
92
92
|
"Failed to load plugins": "Tải danh sách plugin thất bại",
|
|
93
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"
|
|
94
|
+
"Failed to force remove plugin": "Buộc xóa plugin thất bại",
|
|
95
|
+
"Nginx Cache": "Bộ nhớ đệm Nginx",
|
|
96
|
+
"Nginx configuration file found at": "Tìm thấy tệp cấu hình Nginx tại",
|
|
97
|
+
"Nginx cache paths detected": "Đường dẫn bộ nhớ đệm Nginx phát hiện được",
|
|
98
|
+
"Nginx is not detected on this node. You can still input a custom cache directory.": "Không phát hiện Nginx trên nút này. Bạn vẫn có thể nhập đường dẫn bộ nhớ đệm tùy chỉnh.",
|
|
99
|
+
"Clearing Method": "Phương thức xóa",
|
|
100
|
+
"Physical Files": "Tệp vật lý",
|
|
101
|
+
"HTTP Purge Request": "Yêu cầu HTTP Purge",
|
|
102
|
+
"Select Cache Path": "Chọn đường dẫn cache",
|
|
103
|
+
"Custom Path": "Đường dẫn tùy chỉnh",
|
|
104
|
+
"Purge URL": "Purge URL",
|
|
105
|
+
"Headers (JSON)": "Headers (JSON)",
|
|
106
|
+
"Clear Nginx Cache": "Xóa bộ nhớ đệm Nginx",
|
|
107
|
+
"Are you sure you want to clear Nginx cache? This will permanently delete all files in this directory.": "Bạn có chắc chắn muốn xóa bộ nhớ đệm Nginx? Hành động này sẽ xóa vĩnh viễn toàn bộ tệp trong thư mục.",
|
|
108
|
+
"Cache cleared successfully. Cleared {count} items.": "Xóa bộ nhớ đệm thành công. Đã dọn dẹp {count} mục.",
|
|
109
|
+
"HTTP Purge request completed. Status: {status}": "Yêu cầu HTTP Purge hoàn tất. Trạng thái: {status}",
|
|
110
|
+
"Custom Cache Path": "Đường dẫn Cache tùy chỉnh",
|
|
111
|
+
"HTTP Method": "Phương thức HTTP",
|
|
112
|
+
"Send Purge Request": "Gửi yêu cầu Purge",
|
|
113
|
+
"Please select or enter a cache path": "Vui lòng chọn hoặc nhập đường dẫn cache",
|
|
114
|
+
"Please enter a valid Purge URL": "Vui lòng nhập Purge URL hợp lệ",
|
|
115
|
+
"Nginx cache path is required": "Yêu cầu đường dẫn cache Nginx",
|
|
116
|
+
"Invalid headers JSON structure": "Cấu trúc JSON headers không hợp lệ",
|
|
117
|
+
"Nginx Cache Status": "Trạng thái bộ nhớ đệm Nginx",
|
|
118
|
+
"Nginx is installed": "Nginx đã được cài đặt",
|
|
119
|
+
"Nginx is NOT installed": "Nginx CHƯA được cài đặt",
|
|
120
|
+
"Nginx cache clearing method": "Phương thức xóa bộ nhớ đệm Nginx",
|
|
121
|
+
"Execution Logs": "Nhật ký thực thi",
|
|
122
|
+
"Clear Nginx Cache?": "Xóa bộ nhớ đệm 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"
|
|
95
192
|
}
|
package/src/locale/zh-CN.json
CHANGED
|
@@ -91,5 +91,102 @@
|
|
|
91
91
|
"Plugin force removed": "插件已强制移除",
|
|
92
92
|
"Failed to load plugins": "加载插件失败",
|
|
93
93
|
"Failed to force disable plugin": "强制禁用插件失败",
|
|
94
|
-
"Failed to force remove 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"
|
|
95
192
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createMockServer, MockServer } from '@nocobase/test';
|
|
2
|
+
import PluginClusterManagerServer from '../plugin';
|
|
3
|
+
|
|
4
|
+
describe('plugin-cluster-manager doctor', () => {
|
|
5
|
+
let app: MockServer;
|
|
6
|
+
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
app = await createMockServer({
|
|
9
|
+
plugins: ['error-handler', PluginClusterManagerServer],
|
|
10
|
+
acl: false,
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
await app?.destroy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('runs one diagnostic session at a time and produces a downloadable report', async () => {
|
|
19
|
+
const agent = app.agent();
|
|
20
|
+
|
|
21
|
+
const started = await agent.resource('clusterManagerDoctor').start({
|
|
22
|
+
values: { durationMs: 10000 },
|
|
23
|
+
});
|
|
24
|
+
expect(started.status).toBe(200);
|
|
25
|
+
expect(started.body.data.status).toBe('running');
|
|
26
|
+
expect(started.body.data.runId).toBeTruthy();
|
|
27
|
+
|
|
28
|
+
const rejected = await agent.resource('clusterManagerDoctor').start({
|
|
29
|
+
values: { durationMs: 10000 },
|
|
30
|
+
});
|
|
31
|
+
expect(rejected.status).toBe(409);
|
|
32
|
+
|
|
33
|
+
const stopped = await agent.resource('clusterManagerDoctor').stop({
|
|
34
|
+
values: { runId: started.body.data.runId },
|
|
35
|
+
});
|
|
36
|
+
expect(stopped.status).toBe(200);
|
|
37
|
+
expect(stopped.body.data.status).toBe('finished');
|
|
38
|
+
expect(stopped.body.data.report.summary.nodes).toBeGreaterThan(0);
|
|
39
|
+
expect(stopped.body.data.report.runId).toBe(started.body.data.runId);
|
|
40
|
+
|
|
41
|
+
const report = await agent.resource('clusterManagerDoctor').report({
|
|
42
|
+
runId: started.body.data.runId,
|
|
43
|
+
});
|
|
44
|
+
expect(report.status).toBe(200);
|
|
45
|
+
expect(report.body.data.report.summary).toBeTruthy();
|
|
46
|
+
|
|
47
|
+
const download = await agent.resource('clusterManagerDoctor').download({
|
|
48
|
+
runId: started.body.data.runId,
|
|
49
|
+
});
|
|
50
|
+
expect(download.status).toBe(200);
|
|
51
|
+
expect(download.headers['content-type']).toContain('application/json');
|
|
52
|
+
});
|
|
53
|
+
});
|