@zintrust/workers 0.4.42 → 0.4.48
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/dist/WorkerFactory.js +50 -0
- package/dist/build-manifest.json +10 -38
- package/package.json +3 -3
- package/src/WorkerFactory.ts +62 -0
- package/dist/WorkerShutdownDurableObject.d.ts +0 -12
- package/dist/WorkerShutdownDurableObject.js +0 -41
- package/dist/ui/workers/index.html +0 -202
- package/dist/ui/workers/main.js +0 -1952
- package/dist/ui/workers/styles.css +0 -1350
- package/dist/ui/workers/zintrust.svg +0 -30
package/dist/WorkerFactory.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Central factory for creating workers with all advanced features
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
|
+
import * as ZintrustCoreModule from '@zintrust/core';
|
|
6
7
|
import { Cloudflare, createRedisConnection, databaseConfig, Env, ErrorFactory, generateUuid, getBullMQSafeQueueName, isFunction, isNonEmptyString, isObject, JobStateTracker, Logger, NodeSingletons, queueConfig, registerDatabasesFromRuntimeConfig, useEnsureDbConnected, workersConfig, ZintrustLang, } from '@zintrust/core';
|
|
7
8
|
import { Worker } from 'bullmq';
|
|
8
9
|
import { AutoScaler } from './AutoScaler.js';
|
|
@@ -34,6 +35,52 @@ const canUseProjectFileImports = () => typeof NodeSingletons?.fs?.writeFileSync
|
|
|
34
35
|
typeof NodeSingletons?.fs?.existsSync === 'function' &&
|
|
35
36
|
typeof NodeSingletons?.url?.pathToFileURL === 'function' &&
|
|
36
37
|
typeof NodeSingletons?.path?.join === 'function';
|
|
38
|
+
const getProcessorPackageBridgeGlobal = () => {
|
|
39
|
+
return globalThis;
|
|
40
|
+
};
|
|
41
|
+
const isValidBridgeExportName = (value) => {
|
|
42
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(value);
|
|
43
|
+
};
|
|
44
|
+
const resolveRuntimeBridgeModule = (specifier) => {
|
|
45
|
+
if (specifier === '@zintrust/core') {
|
|
46
|
+
return ZintrustCoreModule;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
};
|
|
50
|
+
const resolveRuntimeBridgeUrl = (specifier) => {
|
|
51
|
+
if (!isNodeRuntime() || !canUseProjectFileImports())
|
|
52
|
+
return null;
|
|
53
|
+
const bridgeModule = resolveRuntimeBridgeModule(specifier);
|
|
54
|
+
if (bridgeModule === null)
|
|
55
|
+
return null;
|
|
56
|
+
const dir = ensureProcessorSpecDir();
|
|
57
|
+
if (dir === null)
|
|
58
|
+
return null;
|
|
59
|
+
const bridgeGlobal = getProcessorPackageBridgeGlobal();
|
|
60
|
+
bridgeGlobal.__zintrustProcessorPackageBridges__ ??= new Map();
|
|
61
|
+
bridgeGlobal.__zintrustProcessorPackageBridges__.set(specifier, bridgeModule);
|
|
62
|
+
const safeName = specifier.replaceAll('@', '').replaceAll('/', '-');
|
|
63
|
+
const filePath = path.join(dir, `${safeName}.bridge.mjs`);
|
|
64
|
+
const exportLines = Object.keys(bridgeModule)
|
|
65
|
+
.filter((key) => key !== 'default' && isValidBridgeExportName(key))
|
|
66
|
+
.sort()
|
|
67
|
+
.map((key) => `export const ${key} = bridge[${JSON.stringify(key)}];`);
|
|
68
|
+
const code = [
|
|
69
|
+
'const bridgeMap = globalThis.__zintrustProcessorPackageBridges__;',
|
|
70
|
+
`const bridge = bridgeMap?.get(${JSON.stringify(specifier)}) ?? {};`,
|
|
71
|
+
'export default bridge;',
|
|
72
|
+
...exportLines,
|
|
73
|
+
'',
|
|
74
|
+
].join('\n');
|
|
75
|
+
try {
|
|
76
|
+
NodeSingletons.fs.writeFileSync(filePath, code, 'utf8');
|
|
77
|
+
return NodeSingletons.url.pathToFileURL(filePath).href;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
Logger.debug(`Failed to write processor bridge for ${specifier}`, error);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
37
84
|
const buildCandidatesForSpecifier = (specifier, root) => {
|
|
38
85
|
if (specifier === '@zintrust/core') {
|
|
39
86
|
return [
|
|
@@ -80,6 +127,9 @@ const resolveLocalPackageFallback = (specifier) => {
|
|
|
80
127
|
const resolvePackageSpecifierUrl = (specifier) => {
|
|
81
128
|
if (!isNodeRuntime() || !canUseProjectFileImports())
|
|
82
129
|
return null;
|
|
130
|
+
const bridgeUrl = resolveRuntimeBridgeUrl(specifier);
|
|
131
|
+
if (bridgeUrl)
|
|
132
|
+
return bridgeUrl;
|
|
83
133
|
if (typeof NodeSingletons?.module?.createRequire !== 'function') {
|
|
84
134
|
return resolveLocalPackageFallback(specifier);
|
|
85
135
|
}
|
package/dist/build-manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/workers",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"buildDate": "2026-04-
|
|
3
|
+
"version": "0.4.48",
|
|
4
|
+
"buildDate": "2026-04-03T10:00:24.111Z",
|
|
5
5
|
"buildEnvironment": {
|
|
6
|
-
"node": "
|
|
7
|
-
"platform": "
|
|
8
|
-
"arch": "
|
|
6
|
+
"node": "v20.20.1",
|
|
7
|
+
"platform": "linux",
|
|
8
|
+
"arch": "x64"
|
|
9
9
|
},
|
|
10
10
|
"git": {
|
|
11
|
-
"commit": "
|
|
12
|
-
"branch": "
|
|
11
|
+
"commit": "0ac23637",
|
|
12
|
+
"branch": "master"
|
|
13
13
|
},
|
|
14
14
|
"package": {
|
|
15
15
|
"engines": {
|
|
@@ -178,8 +178,8 @@
|
|
|
178
178
|
"sha256": "3869f960c87260588e40941ff91bffcfa0757be7a04815fd28b57dd4840c51df"
|
|
179
179
|
},
|
|
180
180
|
"WorkerFactory.js": {
|
|
181
|
-
"size":
|
|
182
|
-
"sha256": "
|
|
181
|
+
"size": 104787,
|
|
182
|
+
"sha256": "2ff4782577a8e15d28c5989f5bfe16af84ecdbdc4ecc860c716d0119dcc891e4"
|
|
183
183
|
},
|
|
184
184
|
"WorkerInit.d.ts": {
|
|
185
185
|
"size": 3284,
|
|
@@ -213,14 +213,6 @@
|
|
|
213
213
|
"size": 6643,
|
|
214
214
|
"sha256": "bef3a37ebc8292f4f548d5f66890ef7d6dd386e39c4c1cc61db058fd8815c927"
|
|
215
215
|
},
|
|
216
|
-
"WorkerShutdownDurableObject.d.ts": {
|
|
217
|
-
"size": 354,
|
|
218
|
-
"sha256": "521df11172067bc036bc7b95ff6adf40b7c594afa0454742963317ddfe776d37"
|
|
219
|
-
},
|
|
220
|
-
"WorkerShutdownDurableObject.js": {
|
|
221
|
-
"size": 1547,
|
|
222
|
-
"sha256": "9c7298133dfc4073fda80d3fe9a37ac00d31cacf500ce10c1dd43a70c3a8ddd7"
|
|
223
|
-
},
|
|
224
216
|
"WorkerVersioning.d.ts": {
|
|
225
217
|
"size": 2881,
|
|
226
218
|
"sha256": "a3f1f9e518e8a46201b181679bb5d7a484da7c021b4b7cfd5653cf98fe36e995"
|
|
@@ -229,10 +221,6 @@
|
|
|
229
221
|
"size": 10953,
|
|
230
222
|
"sha256": "8af20d462270e7044c6ea983821f5b6e6ce8a5caf39b6e8fefff07c9a0bf071e"
|
|
231
223
|
},
|
|
232
|
-
"build-manifest.json": {
|
|
233
|
-
"size": 19594,
|
|
234
|
-
"sha256": "ddecf0356aec87965be6bedce19a58e72583c92cbf812cc4d6f070c676a2f6ab"
|
|
235
|
-
},
|
|
236
224
|
"config/workerConfig.d.ts": {
|
|
237
225
|
"size": 132,
|
|
238
226
|
"sha256": "577486dd9e0ef5b5c27d070e0f6a383337d9d68725fae0f0bad258254b828a3b"
|
|
@@ -415,7 +403,7 @@
|
|
|
415
403
|
},
|
|
416
404
|
"index.js": {
|
|
417
405
|
"size": 2337,
|
|
418
|
-
"sha256": "
|
|
406
|
+
"sha256": "ec0e7e4ee51acd850c41f6120d54d4600cd1e462d0dd5c1696b16bc3517177e0"
|
|
419
407
|
},
|
|
420
408
|
"register.d.ts": {
|
|
421
409
|
"size": 256,
|
|
@@ -536,22 +524,6 @@
|
|
|
536
524
|
"ui/types/worker-ui.js": {
|
|
537
525
|
"size": 94,
|
|
538
526
|
"sha256": "7e9f752c2a8eb29dab365b0c836bac90fa7d3567aaf961537d3aa782e85c884e"
|
|
539
|
-
},
|
|
540
|
-
"ui/workers/index.html": {
|
|
541
|
-
"size": 7940,
|
|
542
|
-
"sha256": "a71e384de568f720ff23bf5c90dca60cc97d30c0bbb4ccf5c3b026a8086381b4"
|
|
543
|
-
},
|
|
544
|
-
"ui/workers/main.js": {
|
|
545
|
-
"size": 60723,
|
|
546
|
-
"sha256": "8437771f28963e1b039bee0b1721a8e83a8c65bed19dbd89af0e37d5a2882bb4"
|
|
547
|
-
},
|
|
548
|
-
"ui/workers/styles.css": {
|
|
549
|
-
"size": 22652,
|
|
550
|
-
"sha256": "6de91f206251b0b69ca1a41b71b454b00e3b1213a9762abd8fd8f0ff0381556b"
|
|
551
|
-
},
|
|
552
|
-
"ui/workers/zintrust.svg": {
|
|
553
|
-
"size": 1035,
|
|
554
|
-
"sha256": "9b791269aad740b4191b0ae9f005b08bb28a3407a79a8ed38c11d225fa443f69"
|
|
555
527
|
}
|
|
556
528
|
}
|
|
557
529
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/workers",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.48",
|
|
4
4
|
"description": "Worker orchestration and background job management for ZinTrust.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=20.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@zintrust/core": "^0.4.
|
|
43
|
+
"@zintrust/core": "^0.4.48",
|
|
44
44
|
"@zintrust/queue-monitor": "*",
|
|
45
45
|
"@zintrust/queue-redis": "*"
|
|
46
46
|
},
|
|
@@ -73,4 +73,4 @@
|
|
|
73
73
|
"prom-client": "^15.1.3",
|
|
74
74
|
"simple-statistics": "^7.8.9"
|
|
75
75
|
}
|
|
76
|
-
}
|
|
76
|
+
}
|
package/src/WorkerFactory.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import * as ZintrustCoreModule from '@zintrust/core';
|
|
7
8
|
import {
|
|
8
9
|
Cloudflare,
|
|
9
10
|
createRedisConnection,
|
|
@@ -55,6 +56,10 @@ import {
|
|
|
55
56
|
|
|
56
57
|
const path = NodeSingletons.path;
|
|
57
58
|
|
|
59
|
+
type ProcessorPackageBridgeGlobal = typeof globalThis & {
|
|
60
|
+
__zintrustProcessorPackageBridges__?: Map<string, Record<string, unknown>>;
|
|
61
|
+
};
|
|
62
|
+
|
|
58
63
|
const isNodeRuntime = (): boolean =>
|
|
59
64
|
typeof process !== 'undefined' && Boolean(process.versions?.node);
|
|
60
65
|
|
|
@@ -70,6 +75,59 @@ const canUseProjectFileImports = (): boolean =>
|
|
|
70
75
|
typeof NodeSingletons?.url?.pathToFileURL === 'function' &&
|
|
71
76
|
typeof NodeSingletons?.path?.join === 'function';
|
|
72
77
|
|
|
78
|
+
const getProcessorPackageBridgeGlobal = (): ProcessorPackageBridgeGlobal => {
|
|
79
|
+
return globalThis as ProcessorPackageBridgeGlobal;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const isValidBridgeExportName = (value: string): boolean => {
|
|
83
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(value);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const resolveRuntimeBridgeModule = (specifier: string): Record<string, unknown> | null => {
|
|
87
|
+
if (specifier === '@zintrust/core') {
|
|
88
|
+
return ZintrustCoreModule as Record<string, unknown>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const resolveRuntimeBridgeUrl = (specifier: string): string | null => {
|
|
95
|
+
if (!isNodeRuntime() || !canUseProjectFileImports()) return null;
|
|
96
|
+
|
|
97
|
+
const bridgeModule = resolveRuntimeBridgeModule(specifier);
|
|
98
|
+
if (bridgeModule === null) return null;
|
|
99
|
+
|
|
100
|
+
const dir = ensureProcessorSpecDir();
|
|
101
|
+
if (dir === null) return null;
|
|
102
|
+
|
|
103
|
+
const bridgeGlobal = getProcessorPackageBridgeGlobal();
|
|
104
|
+
bridgeGlobal.__zintrustProcessorPackageBridges__ ??= new Map<string, Record<string, unknown>>();
|
|
105
|
+
bridgeGlobal.__zintrustProcessorPackageBridges__.set(specifier, bridgeModule);
|
|
106
|
+
|
|
107
|
+
const safeName = specifier.replaceAll('@', '').replaceAll('/', '-');
|
|
108
|
+
const filePath = path.join(dir, `${safeName}.bridge.mjs`);
|
|
109
|
+
const exportLines = Object.keys(bridgeModule)
|
|
110
|
+
.filter((key) => key !== 'default' && isValidBridgeExportName(key))
|
|
111
|
+
.sort()
|
|
112
|
+
.map((key) => `export const ${key} = bridge[${JSON.stringify(key)}];`);
|
|
113
|
+
|
|
114
|
+
const code = [
|
|
115
|
+
'const bridgeMap = globalThis.__zintrustProcessorPackageBridges__;',
|
|
116
|
+
`const bridge = bridgeMap?.get(${JSON.stringify(specifier)}) ?? {};`,
|
|
117
|
+
'export default bridge;',
|
|
118
|
+
...exportLines,
|
|
119
|
+
'',
|
|
120
|
+
].join('\n');
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
NodeSingletons.fs.writeFileSync(filePath, code, 'utf8');
|
|
124
|
+
return NodeSingletons.url.pathToFileURL(filePath).href;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
Logger.debug(`Failed to write processor bridge for ${specifier}`, error);
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
73
131
|
const buildCandidatesForSpecifier = (specifier: string, root: string): string[] => {
|
|
74
132
|
if (specifier === '@zintrust/core') {
|
|
75
133
|
return [
|
|
@@ -117,6 +175,10 @@ const resolveLocalPackageFallback = (specifier: string): string | null => {
|
|
|
117
175
|
|
|
118
176
|
const resolvePackageSpecifierUrl = (specifier: string): string | null => {
|
|
119
177
|
if (!isNodeRuntime() || !canUseProjectFileImports()) return null;
|
|
178
|
+
|
|
179
|
+
const bridgeUrl = resolveRuntimeBridgeUrl(specifier);
|
|
180
|
+
if (bridgeUrl) return bridgeUrl;
|
|
181
|
+
|
|
120
182
|
if (typeof NodeSingletons?.module?.createRequire !== 'function') {
|
|
121
183
|
return resolveLocalPackageFallback(specifier);
|
|
122
184
|
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
type DurableObjectState = {
|
|
2
|
-
storage: {
|
|
3
|
-
get: (key: string) => Promise<unknown>;
|
|
4
|
-
put: (key: string, value: unknown) => Promise<void>;
|
|
5
|
-
};
|
|
6
|
-
};
|
|
7
|
-
export declare class ZinTrustWorkerShutdownDurableObject {
|
|
8
|
-
private readonly state;
|
|
9
|
-
constructor(state: DurableObjectState);
|
|
10
|
-
fetch(request: Request): Promise<Response>;
|
|
11
|
-
}
|
|
12
|
-
export {};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Logger } from '@zintrust/core';
|
|
2
|
-
const loadState = async (state) => {
|
|
3
|
-
const stored = (await state.storage.get('shutdown'));
|
|
4
|
-
return stored ?? { shuttingDown: false };
|
|
5
|
-
};
|
|
6
|
-
const saveState = async (state, value) => {
|
|
7
|
-
await state.storage.put('shutdown', value);
|
|
8
|
-
};
|
|
9
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
10
|
-
export class ZinTrustWorkerShutdownDurableObject {
|
|
11
|
-
state;
|
|
12
|
-
constructor(state) {
|
|
13
|
-
this.state = state;
|
|
14
|
-
}
|
|
15
|
-
async fetch(request) {
|
|
16
|
-
const url = new URL(request.url);
|
|
17
|
-
const path = url.pathname;
|
|
18
|
-
if (request.method === 'GET' && path === '/status') {
|
|
19
|
-
const current = await loadState(this.state);
|
|
20
|
-
return new Response(JSON.stringify(current), {
|
|
21
|
-
status: 200,
|
|
22
|
-
headers: { 'content-type': 'application/json' },
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
if (request.method === 'POST' && path === '/shutdown') {
|
|
26
|
-
const payload = (await request.json().catch(() => ({})));
|
|
27
|
-
const next = {
|
|
28
|
-
shuttingDown: true,
|
|
29
|
-
startedAt: new Date().toISOString(),
|
|
30
|
-
reason: payload.reason ?? 'manual',
|
|
31
|
-
};
|
|
32
|
-
await saveState(this.state, next);
|
|
33
|
-
Logger.info('Worker shutdown requested via Durable Object', next);
|
|
34
|
-
return new Response(JSON.stringify({ ok: true }), {
|
|
35
|
-
status: 202,
|
|
36
|
-
headers: { 'content-type': 'application/json' },
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return new Response('Not Found', { status: 404 });
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>ZinTrust Workers Dashboard</title>
|
|
7
|
-
<link rel="stylesheet" href="workers/styles.css" />
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div class="container">
|
|
11
|
-
<div class="header">
|
|
12
|
-
<div class="header-top">
|
|
13
|
-
<div style="display: flex; align-items: center; gap: 16px">
|
|
14
|
-
<div class="logo-frame">
|
|
15
|
-
<img src="workers/zintrust.svg" alt="ZinTrust" class="logo-img" />
|
|
16
|
-
</div>
|
|
17
|
-
<h1>ZinTrust Workers</h1>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="header-actions">
|
|
20
|
-
<button id="theme-toggle" class="theme-toggle">
|
|
21
|
-
<svg class="icon" viewBox="0 0 24 24">
|
|
22
|
-
<circle cx="12" cy="12" r="5" />
|
|
23
|
-
<path
|
|
24
|
-
d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"
|
|
25
|
-
/>
|
|
26
|
-
</svg>
|
|
27
|
-
Theme
|
|
28
|
-
</button>
|
|
29
|
-
<button id="auto-refresh-toggle" class="btn" onclick="toggleAutoRefresh()">
|
|
30
|
-
<svg id="auto-refresh-icon" class="icon" viewBox="0 0 24 24">
|
|
31
|
-
<polygon points="5 3 19 12 5 21 5 3" />
|
|
32
|
-
</svg>
|
|
33
|
-
<span id="auto-refresh-label">Auto Refresh</span>
|
|
34
|
-
</button>
|
|
35
|
-
<button class="btn" onclick="fetchData()">
|
|
36
|
-
<svg class="icon" viewBox="0 0 24 24">
|
|
37
|
-
<path d="M23 4v6h-6" />
|
|
38
|
-
<path d="M1 20v-6h6" />
|
|
39
|
-
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
|
40
|
-
</svg>
|
|
41
|
-
Refresh
|
|
42
|
-
</button>
|
|
43
|
-
<button class="btn btn-primary" onclick="showAddWorkerModal()">
|
|
44
|
-
<svg class="icon" viewBox="0 0 24 24">
|
|
45
|
-
<line x1="12" y1="5" x2="12" y2="19" />
|
|
46
|
-
<line x1="5" y1="12" x2="19" y2="12" />
|
|
47
|
-
</svg>
|
|
48
|
-
Add Worker
|
|
49
|
-
</button>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
|
|
53
|
-
<div class="nav-bar">
|
|
54
|
-
<nav class="nav-links">
|
|
55
|
-
<a href="/queue-monitor" class="nav-link">Queue Monitor</a>
|
|
56
|
-
<a href="/telemetry" class="nav-link">Telemetry</a>
|
|
57
|
-
<a href="/metrics" class="nav-link">Metrics</a>
|
|
58
|
-
</nav>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div class="filters-bar">
|
|
62
|
-
<div class="filter-group">
|
|
63
|
-
<span>Status:</span>
|
|
64
|
-
<select id="status-filter">
|
|
65
|
-
<option value="">All Status</option>
|
|
66
|
-
<option value="running">Running</option>
|
|
67
|
-
<option value="stopped">Stopped</option>
|
|
68
|
-
<option value="error">Error</option>
|
|
69
|
-
<option value="paused">Paused</option>
|
|
70
|
-
</select>
|
|
71
|
-
</div>
|
|
72
|
-
<div class="filter-group">
|
|
73
|
-
<span>Driver:</span>
|
|
74
|
-
<select id="driver-filter">
|
|
75
|
-
<option value="">All Drivers</option>
|
|
76
|
-
</select>
|
|
77
|
-
</div>
|
|
78
|
-
<div class="filter-group">
|
|
79
|
-
<span>Sort:</span>
|
|
80
|
-
<select id="sort-select">
|
|
81
|
-
<option value="name">Sort by Name</option>
|
|
82
|
-
<option value="status" selected>Sort by Status</option>
|
|
83
|
-
<option value="driver">Sort by Driver</option>
|
|
84
|
-
<option value="health">Sort by Health</option>
|
|
85
|
-
<option value="version">Sort by Version</option>
|
|
86
|
-
<option value="processed">Sort by Performance</option>
|
|
87
|
-
</select>
|
|
88
|
-
</div>
|
|
89
|
-
<div style="flex-grow: 1"></div>
|
|
90
|
-
<div class="search-box">
|
|
91
|
-
<svg class="search-icon" viewBox="0 0 24 24">
|
|
92
|
-
<circle cx="11" cy="11" r="8"></circle>
|
|
93
|
-
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
94
|
-
</svg>
|
|
95
|
-
<input type="text" id="search-input" placeholder="Search workers..." />
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
|
|
100
|
-
<div id="loading" style="text-align: center; padding: 40px; color: var(--muted)">
|
|
101
|
-
<div>Loading workers...</div>
|
|
102
|
-
</div>
|
|
103
|
-
|
|
104
|
-
<div
|
|
105
|
-
id="error"
|
|
106
|
-
style="display: none; text-align: center; padding: 40px; color: var(--danger)"
|
|
107
|
-
>
|
|
108
|
-
<div>Failed to load workers data</div>
|
|
109
|
-
<button class="btn" onclick="fetchData()" style="margin-top: 16px">Retry</button>
|
|
110
|
-
</div>
|
|
111
|
-
|
|
112
|
-
<div id="workers-content" style="display: none">
|
|
113
|
-
<div class="summary-bar" id="queue-summary">
|
|
114
|
-
<div class="summary-item">
|
|
115
|
-
<span class="summary-label">Queue Driver</span>
|
|
116
|
-
<span class="summary-value" id="queue-driver">-</span>
|
|
117
|
-
</div>
|
|
118
|
-
<div class="summary-item">
|
|
119
|
-
<span class="summary-label">Queues</span>
|
|
120
|
-
<span class="summary-value" id="queue-total">0</span>
|
|
121
|
-
</div>
|
|
122
|
-
<div class="summary-item">
|
|
123
|
-
<span class="summary-label">Jobs</span>
|
|
124
|
-
<span class="summary-value" id="queue-jobs">0</span>
|
|
125
|
-
</div>
|
|
126
|
-
<div class="summary-item">
|
|
127
|
-
<span class="summary-label">Processing</span>
|
|
128
|
-
<span class="summary-value" id="queue-processing">0</span>
|
|
129
|
-
</div>
|
|
130
|
-
<div class="summary-item">
|
|
131
|
-
<span class="summary-label">Failed</span>
|
|
132
|
-
<span class="summary-value" id="queue-failed">0</span>
|
|
133
|
-
</div>
|
|
134
|
-
<div class="summary-item">
|
|
135
|
-
<span class="summary-label">Drivers</span>
|
|
136
|
-
<div class="drivers-list" id="drivers-list"></div>
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
<div class="table-container">
|
|
140
|
-
<div class="table-wrapper">
|
|
141
|
-
<table>
|
|
142
|
-
<thead>
|
|
143
|
-
<tr>
|
|
144
|
-
<th style="width: 250px">Worker</th>
|
|
145
|
-
<th style="width: 120px">Status</th>
|
|
146
|
-
<th style="width: 120px">Health</th>
|
|
147
|
-
<th style="width: 100px">Driver</th>
|
|
148
|
-
<th style="width: 100px">Version</th>
|
|
149
|
-
<th style="width: 320px">Performance</th>
|
|
150
|
-
<th style="width: 180px">Actions</th>
|
|
151
|
-
</tr>
|
|
152
|
-
</thead>
|
|
153
|
-
<tbody id="workers-tbody">
|
|
154
|
-
<!-- Workers will be populated here -->
|
|
155
|
-
</tbody>
|
|
156
|
-
</table>
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
<div class="pagination">
|
|
160
|
-
<div class="pagination-info" id="pagination-info">Showing 0-0 of 0 workers</div>
|
|
161
|
-
<div class="pagination-controls">
|
|
162
|
-
<button class="page-btn" id="prev-btn" onclick="loadPage('prev')" disabled>
|
|
163
|
-
<svg
|
|
164
|
-
viewBox="0 0 24 24"
|
|
165
|
-
fill="none"
|
|
166
|
-
stroke="currentColor"
|
|
167
|
-
stroke-linecap="round"
|
|
168
|
-
stroke-linejoin="round"
|
|
169
|
-
>
|
|
170
|
-
<polyline points="15 18 9 12 15 6"></polyline>
|
|
171
|
-
</svg>
|
|
172
|
-
</button>
|
|
173
|
-
<div id="page-numbers" style="display: flex; gap: 8px"></div>
|
|
174
|
-
<button class="page-btn" id="next-btn" onclick="loadPage('next')" disabled>
|
|
175
|
-
<svg
|
|
176
|
-
viewBox="0 0 24 24"
|
|
177
|
-
fill="none"
|
|
178
|
-
stroke="currentColor"
|
|
179
|
-
stroke-linecap="round"
|
|
180
|
-
stroke-linejoin="round"
|
|
181
|
-
>
|
|
182
|
-
<polyline points="9 18 15 12 9 6"></polyline>
|
|
183
|
-
</svg>
|
|
184
|
-
</button>
|
|
185
|
-
|
|
186
|
-
<div class="page-size-selector">
|
|
187
|
-
<span>Show:</span>
|
|
188
|
-
<select id="limit-select" onchange="changeLimit(this.value)">
|
|
189
|
-
<option value="10">10</option>
|
|
190
|
-
<option value="25">25</option>
|
|
191
|
-
<option value="50">50</option>
|
|
192
|
-
<option value="100">100</option>
|
|
193
|
-
</select>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
</div>
|
|
199
|
-
</div>
|
|
200
|
-
<script src="workers/main.js"></script>
|
|
201
|
-
</body>
|
|
202
|
-
</html>
|