@zintrust/workers 0.1.27
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 +861 -0
- package/dist/AnomalyDetection.d.ts +102 -0
- package/dist/AnomalyDetection.js +321 -0
- package/dist/AutoScaler.d.ts +127 -0
- package/dist/AutoScaler.js +425 -0
- package/dist/BroadcastWorker.d.ts +21 -0
- package/dist/BroadcastWorker.js +24 -0
- package/dist/CanaryController.d.ts +103 -0
- package/dist/CanaryController.js +380 -0
- package/dist/ChaosEngineering.d.ts +79 -0
- package/dist/ChaosEngineering.js +216 -0
- package/dist/CircuitBreaker.d.ts +106 -0
- package/dist/CircuitBreaker.js +374 -0
- package/dist/ClusterLock.d.ts +90 -0
- package/dist/ClusterLock.js +385 -0
- package/dist/ComplianceManager.d.ts +177 -0
- package/dist/ComplianceManager.js +556 -0
- package/dist/DatacenterOrchestrator.d.ts +133 -0
- package/dist/DatacenterOrchestrator.js +404 -0
- package/dist/DeadLetterQueue.d.ts +122 -0
- package/dist/DeadLetterQueue.js +539 -0
- package/dist/HealthMonitor.d.ts +42 -0
- package/dist/HealthMonitor.js +301 -0
- package/dist/MultiQueueWorker.d.ts +89 -0
- package/dist/MultiQueueWorker.js +277 -0
- package/dist/NotificationWorker.d.ts +21 -0
- package/dist/NotificationWorker.js +23 -0
- package/dist/Observability.d.ts +153 -0
- package/dist/Observability.js +530 -0
- package/dist/PluginManager.d.ts +123 -0
- package/dist/PluginManager.js +392 -0
- package/dist/PriorityQueue.d.ts +117 -0
- package/dist/PriorityQueue.js +244 -0
- package/dist/ResourceMonitor.d.ts +164 -0
- package/dist/ResourceMonitor.js +605 -0
- package/dist/SLAMonitor.d.ts +110 -0
- package/dist/SLAMonitor.js +274 -0
- package/dist/WorkerFactory.d.ts +193 -0
- package/dist/WorkerFactory.js +1507 -0
- package/dist/WorkerInit.d.ts +85 -0
- package/dist/WorkerInit.js +223 -0
- package/dist/WorkerMetrics.d.ts +114 -0
- package/dist/WorkerMetrics.js +509 -0
- package/dist/WorkerRegistry.d.ts +145 -0
- package/dist/WorkerRegistry.js +319 -0
- package/dist/WorkerShutdown.d.ts +61 -0
- package/dist/WorkerShutdown.js +159 -0
- package/dist/WorkerVersioning.d.ts +107 -0
- package/dist/WorkerVersioning.js +300 -0
- package/dist/build-manifest.json +462 -0
- package/dist/config/workerConfig.d.ts +3 -0
- package/dist/config/workerConfig.js +19 -0
- package/dist/createQueueWorker.d.ts +23 -0
- package/dist/createQueueWorker.js +113 -0
- package/dist/dashboard/index.d.ts +1 -0
- package/dist/dashboard/index.js +1 -0
- package/dist/dashboard/types.d.ts +117 -0
- package/dist/dashboard/types.js +1 -0
- package/dist/dashboard/workers-api.d.ts +4 -0
- package/dist/dashboard/workers-api.js +638 -0
- package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
- package/dist/dashboard/workers-dashboard-ui.js +1026 -0
- package/dist/dashboard/workers-dashboard.d.ts +4 -0
- package/dist/dashboard/workers-dashboard.js +904 -0
- package/dist/helper/index.d.ts +5 -0
- package/dist/helper/index.js +10 -0
- package/dist/http/WorkerApiController.d.ts +38 -0
- package/dist/http/WorkerApiController.js +312 -0
- package/dist/http/WorkerController.d.ts +374 -0
- package/dist/http/WorkerController.js +1351 -0
- package/dist/http/middleware/CustomValidation.d.ts +92 -0
- package/dist/http/middleware/CustomValidation.js +270 -0
- package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
- package/dist/http/middleware/DatacenterValidator.js +94 -0
- package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
- package/dist/http/middleware/EditWorkerValidation.js +55 -0
- package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
- package/dist/http/middleware/FeaturesValidator.js +60 -0
- package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
- package/dist/http/middleware/InfrastructureValidator.js +226 -0
- package/dist/http/middleware/OptionsValidator.d.ts +3 -0
- package/dist/http/middleware/OptionsValidator.js +112 -0
- package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
- package/dist/http/middleware/PayloadSanitizer.js +42 -0
- package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
- package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
- package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/QueueNameSanitizer.js +45 -0
- package/dist/http/middleware/ValidateDriver.d.ts +7 -0
- package/dist/http/middleware/ValidateDriver.js +20 -0
- package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
- package/dist/http/middleware/VersionSanitizer.js +25 -0
- package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
- package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
- package/dist/http/middleware/WorkerValidationChain.js +185 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/routes/workers.d.ts +12 -0
- package/dist/routes/workers.js +81 -0
- package/dist/storage/WorkerStore.d.ts +45 -0
- package/dist/storage/WorkerStore.js +195 -0
- package/dist/type.d.ts +76 -0
- package/dist/type.js +1 -0
- package/dist/ui/router/ui.d.ts +3 -0
- package/dist/ui/router/ui.js +83 -0
- package/dist/ui/types/worker-ui.d.ts +229 -0
- package/dist/ui/types/worker-ui.js +5 -0
- package/package.json +53 -0
- package/src/AnomalyDetection.ts +434 -0
- package/src/AutoScaler.ts +654 -0
- package/src/BroadcastWorker.ts +34 -0
- package/src/CanaryController.ts +531 -0
- package/src/ChaosEngineering.ts +301 -0
- package/src/CircuitBreaker.ts +495 -0
- package/src/ClusterLock.ts +499 -0
- package/src/ComplianceManager.ts +815 -0
- package/src/DatacenterOrchestrator.ts +561 -0
- package/src/DeadLetterQueue.ts +733 -0
- package/src/HealthMonitor.ts +390 -0
- package/src/MultiQueueWorker.ts +431 -0
- package/src/NotificationWorker.ts +33 -0
- package/src/Observability.ts +696 -0
- package/src/PluginManager.ts +551 -0
- package/src/PriorityQueue.ts +351 -0
- package/src/ResourceMonitor.ts +769 -0
- package/src/SLAMonitor.ts +408 -0
- package/src/WorkerFactory.ts +2108 -0
- package/src/WorkerInit.ts +313 -0
- package/src/WorkerMetrics.ts +709 -0
- package/src/WorkerRegistry.ts +443 -0
- package/src/WorkerShutdown.ts +210 -0
- package/src/WorkerVersioning.ts +422 -0
- package/src/config/workerConfig.ts +25 -0
- package/src/createQueueWorker.ts +174 -0
- package/src/dashboard/index.ts +6 -0
- package/src/dashboard/types.ts +141 -0
- package/src/dashboard/workers-api.ts +785 -0
- package/src/dashboard/zintrust.svg +30 -0
- package/src/helper/index.ts +11 -0
- package/src/http/WorkerApiController.ts +369 -0
- package/src/http/WorkerController.ts +1512 -0
- package/src/http/middleware/CustomValidation.ts +360 -0
- package/src/http/middleware/DatacenterValidator.ts +124 -0
- package/src/http/middleware/EditWorkerValidation.ts +74 -0
- package/src/http/middleware/FeaturesValidator.ts +82 -0
- package/src/http/middleware/InfrastructureValidator.ts +295 -0
- package/src/http/middleware/OptionsValidator.ts +144 -0
- package/src/http/middleware/PayloadSanitizer.ts +52 -0
- package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
- package/src/http/middleware/QueueNameSanitizer.ts +55 -0
- package/src/http/middleware/ValidateDriver.ts +29 -0
- package/src/http/middleware/VersionSanitizer.ts +30 -0
- package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
- package/src/http/middleware/WorkerValidationChain.ts +230 -0
- package/src/index.ts +98 -0
- package/src/routes/workers.ts +154 -0
- package/src/storage/WorkerStore.ts +240 -0
- package/src/type.ts +89 -0
- package/src/types/queue-monitor.d.ts +38 -0
- package/src/types/queue-redis.d.ts +38 -0
- package/src/ui/README.md +13 -0
- package/src/ui/components/JsonEditor.js +670 -0
- package/src/ui/components/JsonViewer.js +387 -0
- package/src/ui/components/WorkerCard.js +178 -0
- package/src/ui/components/WorkerExpandPanel.js +257 -0
- package/src/ui/components/fetcher.js +42 -0
- package/src/ui/components/sla-scorecard.js +32 -0
- package/src/ui/components/styles.css +30 -0
- package/src/ui/components/table-expander.js +34 -0
- package/src/ui/integration/worker-ui-integration.js +565 -0
- package/src/ui/router/ui.ts +99 -0
- package/src/ui/services/workerApi.js +240 -0
- package/src/ui/types/worker-ui.ts +283 -0
- package/src/ui/utils/jsonValidator.js +444 -0
- package/src/ui/workers/index.html +202 -0
- package/src/ui/workers/main.js +1781 -0
- package/src/ui/workers/styles.css +1350 -0
|
@@ -0,0 +1,1351 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
2
|
+
/**
|
|
3
|
+
* Worker Controller
|
|
4
|
+
* HTTP handlers for worker management API
|
|
5
|
+
*/
|
|
6
|
+
import { Logger, getValidatedBody } from '@zintrust/core';
|
|
7
|
+
import { CanaryController } from '../CanaryController';
|
|
8
|
+
import { getWorkers } from '../dashboard/workers-api';
|
|
9
|
+
import { HealthMonitor } from '../HealthMonitor';
|
|
10
|
+
import { getParam } from '../helper';
|
|
11
|
+
import { SLAMonitor } from '../index';
|
|
12
|
+
import { ResourceMonitor } from '../ResourceMonitor';
|
|
13
|
+
import { WorkerFactory } from '../WorkerFactory';
|
|
14
|
+
import { WorkerRegistry } from '../WorkerRegistry';
|
|
15
|
+
import { WorkerShutdown } from '../WorkerShutdown';
|
|
16
|
+
import { WorkerVersioning } from '../WorkerVersioning';
|
|
17
|
+
/**
|
|
18
|
+
* Helper to get request body
|
|
19
|
+
*/
|
|
20
|
+
const getBody = (req) => {
|
|
21
|
+
return (getValidatedBody(req) ??
|
|
22
|
+
req.getBody?.() ??
|
|
23
|
+
req.body ??
|
|
24
|
+
{});
|
|
25
|
+
};
|
|
26
|
+
// ==================== Core Worker Operations ====================
|
|
27
|
+
/**
|
|
28
|
+
* Create a new worker instance
|
|
29
|
+
* @param req.body.name - Worker name (required)
|
|
30
|
+
* @param req.body.queueName - Queue name (required)
|
|
31
|
+
* @param req.body.processor - Job processor function (required; internal only)
|
|
32
|
+
* @param req.body.version - Worker version (optional)
|
|
33
|
+
* @param req.body.options - BullMQ worker options (optional)
|
|
34
|
+
* @param req.body.infrastructure - Infrastructure config (optional)
|
|
35
|
+
* @param req.body.features - Feature flags (optional)
|
|
36
|
+
* @param req.body.datacenter - Datacenter placement config (optional)
|
|
37
|
+
* @returns Success response with worker name
|
|
38
|
+
*/
|
|
39
|
+
async function create(req, res) {
|
|
40
|
+
Logger.info('WorkerController.create called');
|
|
41
|
+
try {
|
|
42
|
+
const body = req.data();
|
|
43
|
+
// Validate required fields
|
|
44
|
+
if (!body.name || !body.queueName || !body.processor || !body.version) {
|
|
45
|
+
return res.setStatus(400).json({
|
|
46
|
+
error: 'Missing required fields',
|
|
47
|
+
message: 'name, queueName, processor, and version are required',
|
|
48
|
+
code: 'MISSING_REQUIRED_FIELDS',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const rawProcessor = body.processor;
|
|
52
|
+
let processor;
|
|
53
|
+
let processorPath;
|
|
54
|
+
if (typeof rawProcessor === 'string') {
|
|
55
|
+
processorPath = rawProcessor;
|
|
56
|
+
const resolved = await WorkerFactory.resolveProcessorPath(rawProcessor);
|
|
57
|
+
if (!resolved) {
|
|
58
|
+
res.setStatus(400).json({ error: 'Processor path could not be resolved' });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
processor = resolved;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
processor = rawProcessor;
|
|
65
|
+
}
|
|
66
|
+
if (typeof processor !== 'function') {
|
|
67
|
+
res.setStatus(400).json({ error: 'Processor must be a function or resolvable path' });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const config = {
|
|
71
|
+
...body,
|
|
72
|
+
processor,
|
|
73
|
+
processorPath,
|
|
74
|
+
};
|
|
75
|
+
await WorkerFactory.create(config);
|
|
76
|
+
res.json({
|
|
77
|
+
ok: true,
|
|
78
|
+
workerName: config.name,
|
|
79
|
+
status: 'creating',
|
|
80
|
+
message: 'Worker creation started. Check status endpoint for progress.',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
Logger.error('WorkerController.create failed', error);
|
|
85
|
+
res.setStatus(500).json({ error: error.message });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Start a worker
|
|
90
|
+
* @param req.params.name - Worker name
|
|
91
|
+
* @returns Success message
|
|
92
|
+
*/
|
|
93
|
+
async function start(req, res) {
|
|
94
|
+
try {
|
|
95
|
+
const name = getParam(req, 'name');
|
|
96
|
+
if (!name) {
|
|
97
|
+
res.setStatus(400).json({ error: 'Worker name is required' });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
101
|
+
const registered = WorkerRegistry.list().includes(name);
|
|
102
|
+
if (!registered) {
|
|
103
|
+
await WorkerFactory.startFromPersisted(name, persistenceOverride);
|
|
104
|
+
res.json({ ok: true, message: `Worker ${name} registered and started` });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await WorkerFactory.start(name, persistenceOverride);
|
|
108
|
+
res.json({ ok: true, message: `Worker ${name} started` });
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
Logger.error('WorkerController.start failed', error);
|
|
112
|
+
res.setStatus(500).json({ error: error.message });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Stop a worker
|
|
117
|
+
* @param req.params.name - Worker name
|
|
118
|
+
* @returns Success message
|
|
119
|
+
*/
|
|
120
|
+
async function stop(req, res) {
|
|
121
|
+
try {
|
|
122
|
+
const name = getParam(req, 'name');
|
|
123
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
124
|
+
await WorkerFactory.stop(name, persistenceOverride);
|
|
125
|
+
res.json({ ok: true, message: `Worker ${name} stopped` });
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
Logger.error('WorkerController.stop failed', error);
|
|
129
|
+
res.setStatus(500).json({ error: error.message });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Restart a worker
|
|
134
|
+
* @param req.params.name - Worker name
|
|
135
|
+
* @returns Success message
|
|
136
|
+
*/
|
|
137
|
+
async function restart(req, res) {
|
|
138
|
+
try {
|
|
139
|
+
const name = getParam(req, 'name');
|
|
140
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
141
|
+
await WorkerFactory.restart(name, persistenceOverride);
|
|
142
|
+
res.json({ ok: true, message: `Worker ${name} restarted` });
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
Logger.error('WorkerController.restart failed', error);
|
|
146
|
+
res.setStatus(500).json({ error: error.message });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Toggle worker auto-start
|
|
151
|
+
* @param req.params.name - Worker name
|
|
152
|
+
* @param req.query.enabled - true/false
|
|
153
|
+
* @returns Success message
|
|
154
|
+
*/
|
|
155
|
+
async function setAutoStart(req, res) {
|
|
156
|
+
try {
|
|
157
|
+
const data = req.data();
|
|
158
|
+
const name = data['name'];
|
|
159
|
+
if (!name) {
|
|
160
|
+
res.setStatus(400).json({ error: 'Worker name is required' });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const rawEnabled = data['enabled'];
|
|
164
|
+
let enabled;
|
|
165
|
+
if (typeof rawEnabled === 'boolean') {
|
|
166
|
+
enabled = rawEnabled;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
const enabledStr = normalizeQueryValue(rawEnabled) ?? '';
|
|
170
|
+
enabled = ['true', '1', 'yes', 'on'].includes(enabledStr.toLowerCase());
|
|
171
|
+
}
|
|
172
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
173
|
+
await WorkerFactory.setAutoStart(name, enabled, persistenceOverride);
|
|
174
|
+
res.json({ ok: true, message: `Worker ${name} autoStart set to ${enabled}` });
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
Logger.error('WorkerController.setAutoStart failed', error);
|
|
178
|
+
res.setStatus(500).json({ error: error.message });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Pause a worker
|
|
183
|
+
* @param req.params.name - Worker name
|
|
184
|
+
* @returns Success message
|
|
185
|
+
*/
|
|
186
|
+
async function pause(req, res) {
|
|
187
|
+
try {
|
|
188
|
+
const name = getParam(req, 'name');
|
|
189
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
190
|
+
await WorkerFactory.pause(name, persistenceOverride);
|
|
191
|
+
res.json({ ok: true, message: `Worker ${name} paused` });
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
Logger.error('WorkerController.pause failed', error);
|
|
195
|
+
res.setStatus(500).json({ error: error.message });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Resume a paused worker
|
|
200
|
+
* @param req.params.name - Worker name
|
|
201
|
+
* @returns Success message
|
|
202
|
+
*/
|
|
203
|
+
async function resume(req, res) {
|
|
204
|
+
try {
|
|
205
|
+
const name = getParam(req, 'name');
|
|
206
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
207
|
+
await WorkerFactory.resume(name, persistenceOverride);
|
|
208
|
+
res.json({ ok: true, message: `Worker ${name} resumed` });
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
Logger.error('WorkerController.resume failed', error);
|
|
212
|
+
res.setStatus(500).json({ error: error.message });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Remove a worker instance
|
|
217
|
+
* @param req.params.name - Worker name
|
|
218
|
+
* @returns Success message
|
|
219
|
+
*/
|
|
220
|
+
async function remove(req, res) {
|
|
221
|
+
try {
|
|
222
|
+
const name = getParam(req, 'name');
|
|
223
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
224
|
+
await WorkerFactory.remove(name, persistenceOverride);
|
|
225
|
+
res.json({ ok: true, message: `Worker ${name} removed` });
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
Logger.error('WorkerController.remove failed', error);
|
|
229
|
+
res.setStatus(500).json({ error: error.message });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// ==================== Worker Information ====================
|
|
233
|
+
/**
|
|
234
|
+
* List all workers
|
|
235
|
+
* @returns Array of worker instances
|
|
236
|
+
*/
|
|
237
|
+
const normalizeQueryValue = (value) => {
|
|
238
|
+
if (Array.isArray(value))
|
|
239
|
+
return value[0];
|
|
240
|
+
if (typeof value === 'string' && value.trim().length > 0)
|
|
241
|
+
return value;
|
|
242
|
+
return undefined;
|
|
243
|
+
};
|
|
244
|
+
const resolvePersistenceOverride = (req) => {
|
|
245
|
+
// Check for 'driver' parameter first (from frontend), then fallback to 'storage'
|
|
246
|
+
const driverRaw = normalizeQueryValue(req.getQueryParam?.('driver')) ||
|
|
247
|
+
normalizeQueryValue(req.getQueryParam?.('storage'));
|
|
248
|
+
const driver = driverRaw?.toLowerCase();
|
|
249
|
+
// Validate driver parameter (accept 'db' as transitional alias)
|
|
250
|
+
if (driver && !['memory', 'redis', 'db', 'database'].includes(driver)) {
|
|
251
|
+
Logger.error(`Invalid driver parameter: ${driver}. Must be one of: memory, redis, database`);
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
if (driver === 'memory') {
|
|
255
|
+
return { driver: 'memory' };
|
|
256
|
+
}
|
|
257
|
+
if (driver === 'redis') {
|
|
258
|
+
return {
|
|
259
|
+
driver: 'redis',
|
|
260
|
+
redis: { env: true },
|
|
261
|
+
keyPrefix: normalizeQueryValue(req.getQueryParam?.('keyPrefix')),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (driver === 'db' || driver === 'database') {
|
|
265
|
+
return {
|
|
266
|
+
driver: 'database',
|
|
267
|
+
connection: normalizeQueryValue(req.getQueryParam?.('connection')),
|
|
268
|
+
table: normalizeQueryValue(req.getQueryParam?.('table')),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return undefined;
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* Get a specific worker instance
|
|
275
|
+
* @param req.params.name - Worker name
|
|
276
|
+
* @returns Worker instance details
|
|
277
|
+
*/
|
|
278
|
+
async function get(req, res) {
|
|
279
|
+
try {
|
|
280
|
+
const name = getParam(req, 'name');
|
|
281
|
+
const instance = WorkerFactory.get(name);
|
|
282
|
+
if (!instance) {
|
|
283
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
284
|
+
const persisted = await WorkerFactory.getPersisted(name, persistenceOverride);
|
|
285
|
+
if (!persisted) {
|
|
286
|
+
res.setStatus(404).json({ error: `Worker ${name} not found` });
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
res.json({ ok: true, worker: persisted, persisted: true });
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
res.json({ ok: true, worker: instance });
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
Logger.error('WorkerController.get failed', error);
|
|
296
|
+
res.setStatus(500).json({ error: error.message });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Update worker configuration
|
|
301
|
+
* @param req.params.name - Worker name
|
|
302
|
+
* @param req.body - Updated worker configuration
|
|
303
|
+
* @returns Success message
|
|
304
|
+
*/
|
|
305
|
+
async function update(req, res) {
|
|
306
|
+
try {
|
|
307
|
+
const reqData = req.data();
|
|
308
|
+
const name = reqData['name'];
|
|
309
|
+
const driver = reqData['driver'];
|
|
310
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
311
|
+
// Get current worker record
|
|
312
|
+
const currentRecord = await WorkerFactory.getPersisted(name, persistenceOverride);
|
|
313
|
+
if (!currentRecord) {
|
|
314
|
+
res.setStatus(404).json({ error: `Worker ${name} not found` });
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
// Validate and merge updates (excluding immutable fields)
|
|
318
|
+
const { name: _name, driver: _driver, ...updateData } = reqData; // Remove immutable fields
|
|
319
|
+
// Note: driver is determined by persistence configuration, not stored in worker record
|
|
320
|
+
const updatedRecord = {
|
|
321
|
+
...currentRecord,
|
|
322
|
+
...updateData,
|
|
323
|
+
name,
|
|
324
|
+
updatedAt: new Date(),
|
|
325
|
+
};
|
|
326
|
+
updatedRecord.infrastructure.persistence.driver = driver;
|
|
327
|
+
// Update persistence store with the complete updated record
|
|
328
|
+
try {
|
|
329
|
+
// Persist merged record via WorkerFactory API
|
|
330
|
+
await WorkerFactory.update(name, updatedRecord, persistenceOverride);
|
|
331
|
+
Logger.info(`Worker ${name} persistence updated with fields:`, Object.keys(updateData));
|
|
332
|
+
}
|
|
333
|
+
catch (persistError) {
|
|
334
|
+
Logger.warn(`Failed to persist some updates for ${name}`, persistError);
|
|
335
|
+
// Continue with restart even if persistence update partially fails
|
|
336
|
+
}
|
|
337
|
+
// If worker is currently running, restart it to apply new configuration changes
|
|
338
|
+
// This ensures new concurrency, queue settings, and other config take effect
|
|
339
|
+
const currentInstance = WorkerFactory.get(name);
|
|
340
|
+
let restartError;
|
|
341
|
+
if (currentInstance && currentInstance.status === 'running') {
|
|
342
|
+
try {
|
|
343
|
+
Logger.info(`Restarting worker ${name} to apply configuration changes`);
|
|
344
|
+
await WorkerFactory.restart(name, persistenceOverride);
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
restartError = error.message;
|
|
348
|
+
Logger.warn(`Failed to restart worker ${name} after update`, error);
|
|
349
|
+
// Don't fail the update, but warn about restart failure
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
Logger.info(`Worker ${name} is not running (status: ${currentInstance?.status || 'not found'}), skipping restart`);
|
|
354
|
+
}
|
|
355
|
+
// Worker configuration updated in persistence and memory
|
|
356
|
+
Logger.info(`Worker configuration updated: ${name}`, {
|
|
357
|
+
updatedFields: Object.keys(updateData),
|
|
358
|
+
driver: persistenceOverride?.driver || 'default',
|
|
359
|
+
restartError,
|
|
360
|
+
});
|
|
361
|
+
res.json({
|
|
362
|
+
ok: true,
|
|
363
|
+
message: `Worker ${name} updated successfully`,
|
|
364
|
+
worker: updatedRecord,
|
|
365
|
+
updatedFields: Object.keys(updateData),
|
|
366
|
+
restartError,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
Logger.error('WorkerController.update failed', error);
|
|
371
|
+
res.setStatus(500).json({ error: error.message });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Get worker status
|
|
376
|
+
* @param req.params.name - Worker name
|
|
377
|
+
* @returns Worker status information
|
|
378
|
+
*/
|
|
379
|
+
async function status(req, res) {
|
|
380
|
+
try {
|
|
381
|
+
const name = getParam(req, 'name');
|
|
382
|
+
const workerStatus = await WorkerRegistry.status(name);
|
|
383
|
+
res.json({ ok: true, status: workerStatus });
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
Logger.error('WorkerController.status failed', error);
|
|
387
|
+
res.setStatus(500).json({ error: error.message });
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Get worker creation status for polling
|
|
392
|
+
* @param req.params.name - Worker name
|
|
393
|
+
* @returns Worker creation status with progress information
|
|
394
|
+
*/
|
|
395
|
+
async function getCreationStatus(req, res) {
|
|
396
|
+
try {
|
|
397
|
+
const name = getParam(req, 'name');
|
|
398
|
+
const persistenceOverride = resolvePersistenceOverride(req);
|
|
399
|
+
const record = await WorkerFactory.getPersisted(name, persistenceOverride);
|
|
400
|
+
if (!record) {
|
|
401
|
+
res.setStatus(404).json({ error: `Worker ${name} not found` });
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
res.json({
|
|
405
|
+
ok: true,
|
|
406
|
+
workerName: name,
|
|
407
|
+
status: record.status,
|
|
408
|
+
createdAt: record.createdAt,
|
|
409
|
+
updatedAt: record.updatedAt,
|
|
410
|
+
lastError: record.lastError,
|
|
411
|
+
connectionState: record.connectionState,
|
|
412
|
+
lastHealthCheck: record.lastHealthCheck,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
Logger.error('WorkerController.getCreationStatus failed', error);
|
|
417
|
+
res.setStatus(500).json({ error: error.message });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Get worker metrics
|
|
422
|
+
* @param req.params.name - Worker name
|
|
423
|
+
* @returns Worker metrics data
|
|
424
|
+
*/
|
|
425
|
+
async function metrics(req, res) {
|
|
426
|
+
try {
|
|
427
|
+
const name = getParam(req, 'name');
|
|
428
|
+
const workerMetrics = await WorkerFactory.getMetrics(name);
|
|
429
|
+
res.json({ ok: true, metrics: workerMetrics });
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
Logger.error('WorkerController.metrics failed', error);
|
|
433
|
+
res.setStatus(500).json({ error: error.message });
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Get worker health information
|
|
438
|
+
* @param req.params.name - Worker name
|
|
439
|
+
* @returns Worker health data
|
|
440
|
+
*/
|
|
441
|
+
async function health(req, res) {
|
|
442
|
+
try {
|
|
443
|
+
const name = getParam(req, 'name');
|
|
444
|
+
const workerHealth = await WorkerFactory.getHealth(name);
|
|
445
|
+
res.json({ ok: true, health: workerHealth });
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
Logger.error('WorkerController.health failed', error);
|
|
449
|
+
res.setStatus(500).json({ error: error.message });
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// ==================== Health Monitoring ====================
|
|
453
|
+
/**
|
|
454
|
+
* Start health monitoring for a worker
|
|
455
|
+
* @param req.params.name - Worker name
|
|
456
|
+
* @param req.body.checkInterval - Interval in seconds between checks (optional)
|
|
457
|
+
* @param req.body.thresholds - Thresholds for errorRate/latency/throughput/cpu/memory/queueSize (optional)
|
|
458
|
+
* @param req.body.alerting - Alerting config (optional)
|
|
459
|
+
* @returns Success message
|
|
460
|
+
*/
|
|
461
|
+
async function startMonitoring(req, res) {
|
|
462
|
+
try {
|
|
463
|
+
const name = getParam(req, 'name');
|
|
464
|
+
const body = getBody(req);
|
|
465
|
+
HealthMonitor.startMonitoring(name, body);
|
|
466
|
+
res.json({ ok: true, message: `Health monitoring started for ${name}` });
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
Logger.error('WorkerController.startMonitoring failed', error);
|
|
470
|
+
res.setStatus(500).json({ error: error.message });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Stop health monitoring for a worker
|
|
475
|
+
* @param req.params.name - Worker name
|
|
476
|
+
* @returns Success message
|
|
477
|
+
*/
|
|
478
|
+
async function stopMonitoring(req, res) {
|
|
479
|
+
try {
|
|
480
|
+
const name = getParam(req, 'name');
|
|
481
|
+
HealthMonitor.stopMonitoring(name);
|
|
482
|
+
res.json({ ok: true, message: `Health monitoring stopped for ${name}` });
|
|
483
|
+
}
|
|
484
|
+
catch (error) {
|
|
485
|
+
Logger.error('WorkerController.stopMonitoring failed', error);
|
|
486
|
+
res.setStatus(500).json({ error: error.message });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Get health check history for a worker
|
|
491
|
+
* @param req.params.name - Worker name
|
|
492
|
+
* @param req.body.limit - Optional limit for number of history entries
|
|
493
|
+
* @returns Array of health check records
|
|
494
|
+
*/
|
|
495
|
+
async function healthHistory(req, res) {
|
|
496
|
+
try {
|
|
497
|
+
const name = getParam(req, 'name');
|
|
498
|
+
const body = getBody(req);
|
|
499
|
+
const limitRaw = body['limit'];
|
|
500
|
+
const limit = limitRaw ? Number(limitRaw) : undefined;
|
|
501
|
+
const history = HealthMonitor.getHealthHistory(name, limit);
|
|
502
|
+
res.json({ ok: true, history });
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
Logger.error('WorkerController.healthHistory failed', error);
|
|
506
|
+
res.setStatus(500).json({ error: error.message });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Get health trend analysis for a worker
|
|
511
|
+
* @param req.params.name - Worker name
|
|
512
|
+
* @returns Health trend data
|
|
513
|
+
*/
|
|
514
|
+
async function healthTrend(req, res) {
|
|
515
|
+
try {
|
|
516
|
+
const name = getParam(req, 'name');
|
|
517
|
+
const trend = HealthMonitor.getHealthTrend(name);
|
|
518
|
+
res.json({ ok: true, trend });
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
Logger.error('WorkerController.healthTrend failed', error);
|
|
522
|
+
res.setStatus(500).json({ error: error.message });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Get SLA status for a worker
|
|
527
|
+
* @param req.params.name - Worker name
|
|
528
|
+
* @returns SLA compliance status with checks and metrics
|
|
529
|
+
*/
|
|
530
|
+
async function getSlaStatus(req, res) {
|
|
531
|
+
try {
|
|
532
|
+
const name = getParam(req, 'name');
|
|
533
|
+
const slaStatus = await SLAMonitor.checkCompliance(name);
|
|
534
|
+
res.json({ ok: true, status: slaStatus });
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
Logger.error('WorkerController.getSlaStatus failed', error);
|
|
538
|
+
if (error.message.includes('SLA config not found')) {
|
|
539
|
+
res.setStatus(404).json({ error: 'SLA config not found for worker' });
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
res.setStatus(500).json({ error: error.message });
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Update monitoring configuration for a worker
|
|
548
|
+
* @param req.params.name - Worker name
|
|
549
|
+
* @param req.body.checkInterval - Interval in seconds between checks (optional)
|
|
550
|
+
* @param req.body.thresholds - Thresholds for errorRate/latency/throughput/cpu/memory/queueSize (optional)
|
|
551
|
+
* @param req.body.alerting - Alerting config (optional)
|
|
552
|
+
* @returns Success message
|
|
553
|
+
*/
|
|
554
|
+
async function updateMonitoringConfig(req, res) {
|
|
555
|
+
try {
|
|
556
|
+
const name = getParam(req, 'name');
|
|
557
|
+
const body = getBody(req);
|
|
558
|
+
HealthMonitor.updateConfig(name, body);
|
|
559
|
+
res.json({ ok: true, message: `Monitoring config updated for ${name}` });
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
Logger.error('WorkerController.updateMonitoringConfig failed', error);
|
|
563
|
+
res.setStatus(500).json({ error: error.message });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// ==================== Continue with remaining handlers... ====================
|
|
567
|
+
// (Due to length, I'll create additional placeholders that should be implemented)
|
|
568
|
+
/**
|
|
569
|
+
* Register a new worker version
|
|
570
|
+
* @param req.params.name - Worker name
|
|
571
|
+
* @param req.body.version - Semantic version object { major, minor, patch, prerelease?, build? }
|
|
572
|
+
* @param req.body.migrationPath - Migration path/version string (optional)
|
|
573
|
+
* @param req.body.eolDate - End of life date (optional)
|
|
574
|
+
* @param req.body.changelog - Changelog text (optional)
|
|
575
|
+
* @param req.body.breakingChanges - Array of breaking changes (optional)
|
|
576
|
+
* @returns Success message
|
|
577
|
+
*/
|
|
578
|
+
async function registerVersion(req, res) {
|
|
579
|
+
try {
|
|
580
|
+
const name = getParam(req, 'name');
|
|
581
|
+
const body = getBody(req);
|
|
582
|
+
WorkerVersioning.register({ workerName: name, ...body });
|
|
583
|
+
res.json({ ok: true, message: 'Version registered' });
|
|
584
|
+
}
|
|
585
|
+
catch (error) {
|
|
586
|
+
Logger.error('WorkerController.registerVersion failed', error);
|
|
587
|
+
res.setStatus(500).json({ error: error.message });
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* List all versions of a worker
|
|
592
|
+
* @param req.params.name - Worker name
|
|
593
|
+
* @param req.body.includeDeprecated - Optional flag to include deprecated versions
|
|
594
|
+
* @returns Array of version information
|
|
595
|
+
*/
|
|
596
|
+
async function listVersions(req, res) {
|
|
597
|
+
try {
|
|
598
|
+
const name = getParam(req, 'name');
|
|
599
|
+
const includeDeprecated = getBody(req)['includeDeprecated'] === 'true';
|
|
600
|
+
const versions = WorkerVersioning.getVersions(name, includeDeprecated);
|
|
601
|
+
res.json({ ok: true, versions });
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
Logger.error('WorkerController.listVersions failed', error);
|
|
605
|
+
res.setStatus(500).json({ error: error.message });
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Get specific version information
|
|
610
|
+
* @param req.params.name - Worker name
|
|
611
|
+
* @param req.params.version - Version identifier
|
|
612
|
+
* @returns Version details
|
|
613
|
+
*/
|
|
614
|
+
async function getVersion(req, res) {
|
|
615
|
+
try {
|
|
616
|
+
const name = getParam(req, 'name');
|
|
617
|
+
const version = getParam(req, 'version');
|
|
618
|
+
const versionInfo = WorkerVersioning.getVersion(name, version);
|
|
619
|
+
res.json({ ok: true, version: versionInfo });
|
|
620
|
+
}
|
|
621
|
+
catch (error) {
|
|
622
|
+
Logger.error('WorkerController.getVersion failed', error);
|
|
623
|
+
res.setStatus(500).json({ error: error.message });
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Deprecate a worker version
|
|
628
|
+
* @param req.params.name - Worker name
|
|
629
|
+
* @param req.params.version - Version to deprecate
|
|
630
|
+
* @param req.body.migrationPath - Migration instructions
|
|
631
|
+
* @param req.body.eolDate - End of life date
|
|
632
|
+
* @returns Success message
|
|
633
|
+
*/
|
|
634
|
+
async function deprecateVersion(req, res) {
|
|
635
|
+
try {
|
|
636
|
+
const name = getParam(req, 'name');
|
|
637
|
+
const version = getParam(req, 'version');
|
|
638
|
+
const body = getBody(req);
|
|
639
|
+
WorkerVersioning.deprecate(name, version, body['migrationPath'], body['eolDate']);
|
|
640
|
+
res.json({ ok: true, message: 'Version deprecated' });
|
|
641
|
+
}
|
|
642
|
+
catch (error) {
|
|
643
|
+
Logger.error('WorkerController.deprecateVersion failed', error);
|
|
644
|
+
res.setStatus(500).json({ error: error.message });
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Activate a worker version
|
|
649
|
+
* @param req.params.name - Worker name
|
|
650
|
+
* @param req.params.version - Version to activate
|
|
651
|
+
* @returns Success message
|
|
652
|
+
*/
|
|
653
|
+
async function activateVersion(req, res) {
|
|
654
|
+
try {
|
|
655
|
+
const name = getParam(req, 'name');
|
|
656
|
+
const version = getParam(req, 'version');
|
|
657
|
+
WorkerVersioning.activate(name, version);
|
|
658
|
+
res.json({ ok: true, message: 'Version activated' });
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
Logger.error('WorkerController.activateVersion failed', error);
|
|
662
|
+
res.setStatus(500).json({ error: error.message });
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Deactivate a worker version
|
|
667
|
+
* @param req.params.name - Worker name
|
|
668
|
+
* @param req.params.version - Version to deactivate
|
|
669
|
+
* @returns Success message
|
|
670
|
+
*/
|
|
671
|
+
async function deactivateVersion(req, res) {
|
|
672
|
+
try {
|
|
673
|
+
const name = getParam(req, 'name');
|
|
674
|
+
const version = getParam(req, 'version');
|
|
675
|
+
WorkerVersioning.deactivate(name, version);
|
|
676
|
+
res.json({ ok: true, message: 'Version deactivated' });
|
|
677
|
+
}
|
|
678
|
+
catch (error) {
|
|
679
|
+
Logger.error('WorkerController.deactivateVersion failed', error);
|
|
680
|
+
res.setStatus(500).json({ error: error.message });
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Check compatibility between worker versions
|
|
685
|
+
* @param req.params.name - Worker name
|
|
686
|
+
* @param req.body.sourceVersion - Source version string (e.g., "1.2.3")
|
|
687
|
+
* @param req.body.targetVersion - Target version string (e.g., "1.3.0")
|
|
688
|
+
* @returns Compatibility information
|
|
689
|
+
*/
|
|
690
|
+
async function checkCompatibility(req, res) {
|
|
691
|
+
try {
|
|
692
|
+
const name = getParam(req, 'name');
|
|
693
|
+
const body = getBody(req);
|
|
694
|
+
const compatibility = WorkerVersioning.checkCompatibility(name, body['sourceVersion'], body['targetVersion']);
|
|
695
|
+
res.json({ ok: true, compatibility });
|
|
696
|
+
}
|
|
697
|
+
catch (error) {
|
|
698
|
+
Logger.error('WorkerController.checkCompatibility failed', error);
|
|
699
|
+
res.setStatus(500).json({ error: error.message });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
// ==================== Canary Deployments ====================
|
|
703
|
+
/**
|
|
704
|
+
* Start a canary deployment
|
|
705
|
+
* @param req.params.name - Worker name
|
|
706
|
+
* @param req.body.currentVersion - Current version string
|
|
707
|
+
* @param req.body.canaryVersion - Canary version string
|
|
708
|
+
* @param req.body.initialTrafficPercent - Starting traffic percent
|
|
709
|
+
* @param req.body.targetTrafficPercent - Target traffic percent
|
|
710
|
+
* @param req.body.incrementPercent - Increment per step
|
|
711
|
+
* @param req.body.incrementInterval - Seconds between increments
|
|
712
|
+
* @param req.body.monitoringDuration - Seconds per monitoring step
|
|
713
|
+
* @param req.body.errorThreshold - Error rate threshold (0-1)
|
|
714
|
+
* @param req.body.latencyThreshold - P95 latency threshold (ms)
|
|
715
|
+
* @param req.body.minSuccessRate - Minimum success rate (0-1)
|
|
716
|
+
* @param req.body.autoRollback - Auto rollback flag
|
|
717
|
+
* @returns Success message
|
|
718
|
+
*/
|
|
719
|
+
async function startCanary(req, res) {
|
|
720
|
+
try {
|
|
721
|
+
const name = getParam(req, 'name');
|
|
722
|
+
const body = getBody(req);
|
|
723
|
+
await CanaryController.start({ workerName: name, ...body });
|
|
724
|
+
res.json({ ok: true, message: 'Canary deployment started' });
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
Logger.error('WorkerController.startCanary failed', error);
|
|
728
|
+
res.setStatus(500).json({ error: error.message });
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Pause a canary deployment
|
|
733
|
+
* @param req.params.name - Worker name
|
|
734
|
+
* @returns Success message
|
|
735
|
+
*/
|
|
736
|
+
async function pauseCanary(req, res) {
|
|
737
|
+
try {
|
|
738
|
+
const name = getParam(req, 'name');
|
|
739
|
+
CanaryController.pause(name);
|
|
740
|
+
res.json({ ok: true, message: 'Canary deployment paused' });
|
|
741
|
+
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
Logger.error('WorkerController.pauseCanary failed', error);
|
|
744
|
+
res.setStatus(500).json({ error: error.message });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Resume a paused canary deployment
|
|
749
|
+
* @param req.params.name - Worker name
|
|
750
|
+
* @returns Success message
|
|
751
|
+
*/
|
|
752
|
+
async function resumeCanary(req, res) {
|
|
753
|
+
try {
|
|
754
|
+
const name = getParam(req, 'name');
|
|
755
|
+
CanaryController.resume(name);
|
|
756
|
+
res.json({ ok: true, message: 'Canary deployment resumed' });
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
Logger.error('WorkerController.resumeCanary failed', error);
|
|
760
|
+
res.setStatus(500).json({ error: error.message });
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Rollback a canary deployment
|
|
765
|
+
* @param req.params.name - Worker name
|
|
766
|
+
* @param req.body.reason - Optional rollback reason
|
|
767
|
+
* @returns Success message
|
|
768
|
+
*/
|
|
769
|
+
async function rollbackCanary(req, res) {
|
|
770
|
+
try {
|
|
771
|
+
const name = getParam(req, 'name');
|
|
772
|
+
const body = getBody(req);
|
|
773
|
+
await CanaryController.rollback(name, body['reason'] || 'Manual rollback');
|
|
774
|
+
res.json({ ok: true, message: 'Canary deployment rolled back' });
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
Logger.error('WorkerController.rollbackCanary failed', error);
|
|
778
|
+
res.setStatus(500).json({ error: error.message });
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Get canary deployment status
|
|
783
|
+
* @param req.params.name - Worker name
|
|
784
|
+
* @returns Canary status information
|
|
785
|
+
*/
|
|
786
|
+
async function canaryStatus(req, res) {
|
|
787
|
+
try {
|
|
788
|
+
const name = getParam(req, 'name');
|
|
789
|
+
const canaryStatusRes = CanaryController.getStatus(name);
|
|
790
|
+
res.json({ ok: true, status: canaryStatusRes });
|
|
791
|
+
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
Logger.error('WorkerController.canaryStatus failed', error);
|
|
794
|
+
res.setStatus(500).json({ error: error.message });
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Get canary deployment history
|
|
799
|
+
* @param req.params.name - Worker name
|
|
800
|
+
* @returns Array of past canary deployments
|
|
801
|
+
*/
|
|
802
|
+
async function canaryHistory(req, res) {
|
|
803
|
+
try {
|
|
804
|
+
const name = getParam(req, 'name');
|
|
805
|
+
const history = CanaryController.getHistory(name);
|
|
806
|
+
res.json({ ok: true, history });
|
|
807
|
+
}
|
|
808
|
+
catch (error) {
|
|
809
|
+
Logger.error('WorkerController.canaryHistory failed', error);
|
|
810
|
+
res.setStatus(500).json({ error: error.message });
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
// ==================== Placeholder stubs for remaining endpoints ====================
|
|
814
|
+
// These would be fully implemented similarly to the above
|
|
815
|
+
const circuitBreakerState = async (_req, res) => {
|
|
816
|
+
res.json({ ok: true, message: 'Circuit breaker state endpoint - implementation pending' });
|
|
817
|
+
};
|
|
818
|
+
const resetCircuitBreaker = async (_req, res) => {
|
|
819
|
+
res.json({ ok: true, message: 'Reset circuit breaker endpoint - implementation pending' });
|
|
820
|
+
};
|
|
821
|
+
const forceOpenCircuit = async (_req, res) => {
|
|
822
|
+
res.json({ ok: true, message: 'Force open circuit endpoint - implementation pending' });
|
|
823
|
+
};
|
|
824
|
+
const circuitBreakerEvents = async (_req, res) => {
|
|
825
|
+
res.json({ ok: true, message: 'Circuit breaker events endpoint - implementation pending' });
|
|
826
|
+
};
|
|
827
|
+
const listFailedJobs = async (_req, res) => {
|
|
828
|
+
res.json({ ok: true, message: 'List failed jobs endpoint - implementation pending' });
|
|
829
|
+
};
|
|
830
|
+
const getFailedJob = async (_req, res) => {
|
|
831
|
+
res.json({ ok: true, message: 'Get failed job endpoint - implementation pending' });
|
|
832
|
+
};
|
|
833
|
+
const retryFailedJob = async (_req, res) => {
|
|
834
|
+
res.json({ ok: true, message: 'Retry failed job endpoint - implementation pending' });
|
|
835
|
+
};
|
|
836
|
+
const deleteFailedJob = async (_req, res) => {
|
|
837
|
+
res.json({ ok: true, message: 'Delete failed job endpoint - implementation pending' });
|
|
838
|
+
};
|
|
839
|
+
const anonymizeFailedJob = async (_req, res) => {
|
|
840
|
+
res.json({ ok: true, message: 'Anonymize failed job endpoint - implementation pending' });
|
|
841
|
+
};
|
|
842
|
+
const dlqAuditLog = async (_req, res) => {
|
|
843
|
+
res.json({ ok: true, message: 'DLQ audit log endpoint - implementation pending' });
|
|
844
|
+
};
|
|
845
|
+
const dlqStats = async (_req, res) => {
|
|
846
|
+
res.json({ ok: true, message: 'DLQ stats endpoint - implementation pending' });
|
|
847
|
+
};
|
|
848
|
+
const registerPlugin = async (_req, res) => {
|
|
849
|
+
res.json({ ok: true, message: 'Register plugin endpoint - implementation pending' });
|
|
850
|
+
};
|
|
851
|
+
const unregisterPlugin = async (_req, res) => {
|
|
852
|
+
res.json({ ok: true, message: 'Unregister plugin endpoint - implementation pending' });
|
|
853
|
+
};
|
|
854
|
+
const enablePlugin = async (_req, res) => {
|
|
855
|
+
res.json({ ok: true, message: 'Enable plugin endpoint - implementation pending' });
|
|
856
|
+
};
|
|
857
|
+
const disablePlugin = async (_req, res) => {
|
|
858
|
+
res.json({ ok: true, message: 'Disable plugin endpoint - implementation pending' });
|
|
859
|
+
};
|
|
860
|
+
const listPlugins = async (_req, res) => {
|
|
861
|
+
res.json({ ok: true, message: 'List plugins endpoint - implementation pending' });
|
|
862
|
+
};
|
|
863
|
+
const pluginExecutionHistory = async (_req, res) => {
|
|
864
|
+
res.json({ ok: true, message: 'Plugin execution history endpoint - implementation pending' });
|
|
865
|
+
};
|
|
866
|
+
const pluginStatistics = async (_req, res) => {
|
|
867
|
+
res.json({ ok: true, message: 'Plugin statistics endpoint - implementation pending' });
|
|
868
|
+
};
|
|
869
|
+
const createMultiQueue = async (_req, res) => {
|
|
870
|
+
res.json({ ok: true, message: 'Create multi-queue endpoint - implementation pending' });
|
|
871
|
+
};
|
|
872
|
+
const startQueue = async (_req, res) => {
|
|
873
|
+
res.json({ ok: true, message: 'Start queue endpoint - implementation pending' });
|
|
874
|
+
};
|
|
875
|
+
const stopQueue = async (_req, res) => {
|
|
876
|
+
res.json({ ok: true, message: 'Stop queue endpoint - implementation pending' });
|
|
877
|
+
};
|
|
878
|
+
const queueStats = async (_req, res) => {
|
|
879
|
+
res.json({ ok: true, message: 'Queue stats endpoint - implementation pending' });
|
|
880
|
+
};
|
|
881
|
+
const updateQueuePriority = async (_req, res) => {
|
|
882
|
+
res.json({ ok: true, message: 'Update queue priority endpoint - implementation pending' });
|
|
883
|
+
};
|
|
884
|
+
const updateQueueConcurrency = async (_req, res) => {
|
|
885
|
+
res.json({ ok: true, message: 'Update queue concurrency endpoint - implementation pending' });
|
|
886
|
+
};
|
|
887
|
+
const registerRegion = async (_req, res) => {
|
|
888
|
+
res.json({ ok: true, message: 'Register region endpoint - implementation pending' });
|
|
889
|
+
};
|
|
890
|
+
const unregisterRegion = async (_req, res) => {
|
|
891
|
+
res.json({ ok: true, message: 'Unregister region endpoint - implementation pending' });
|
|
892
|
+
};
|
|
893
|
+
const listRegions = async (_req, res) => {
|
|
894
|
+
res.json({ ok: true, message: 'List regions endpoint - implementation pending' });
|
|
895
|
+
};
|
|
896
|
+
const getRegion = async (_req, res) => {
|
|
897
|
+
res.json({ ok: true, message: 'Get region endpoint - implementation pending' });
|
|
898
|
+
};
|
|
899
|
+
const updateRegionHealth = async (_req, res) => {
|
|
900
|
+
res.json({ ok: true, message: 'Update region health endpoint - implementation pending' });
|
|
901
|
+
};
|
|
902
|
+
const updateRegionLoad = async (_req, res) => {
|
|
903
|
+
res.json({ ok: true, message: 'Update region load endpoint - implementation pending' });
|
|
904
|
+
};
|
|
905
|
+
const placeWorker = async (_req, res) => {
|
|
906
|
+
res.json({ ok: true, message: 'Place worker endpoint - implementation pending' });
|
|
907
|
+
};
|
|
908
|
+
const getPlacement = async (_req, res) => {
|
|
909
|
+
res.json({ ok: true, message: 'Get placement endpoint - implementation pending' });
|
|
910
|
+
};
|
|
911
|
+
const updatePlacement = async (_req, res) => {
|
|
912
|
+
res.json({ ok: true, message: 'Update placement endpoint - implementation pending' });
|
|
913
|
+
};
|
|
914
|
+
const findOptimalRegion = async (_req, res) => {
|
|
915
|
+
res.json({ ok: true, message: 'Find optimal region endpoint - implementation pending' });
|
|
916
|
+
};
|
|
917
|
+
const setFailoverPolicy = async (_req, res) => {
|
|
918
|
+
res.json({ ok: true, message: 'Set failover policy endpoint - implementation pending' });
|
|
919
|
+
};
|
|
920
|
+
const getFailoverPolicy = async (_req, res) => {
|
|
921
|
+
res.json({ ok: true, message: 'Get failover policy endpoint - implementation pending' });
|
|
922
|
+
};
|
|
923
|
+
const startHealthChecks = async (_req, res) => {
|
|
924
|
+
res.json({ ok: true, message: 'Start health checks endpoint - implementation pending' });
|
|
925
|
+
};
|
|
926
|
+
const stopHealthChecks = async (_req, res) => {
|
|
927
|
+
res.json({ ok: true, message: 'Stop health checks endpoint - implementation pending' });
|
|
928
|
+
};
|
|
929
|
+
const getTopology = async (_req, res) => {
|
|
930
|
+
res.json({ ok: true, message: 'Get topology endpoint - implementation pending' });
|
|
931
|
+
};
|
|
932
|
+
const getLoadBalancingRecommendation = async (_req, res) => {
|
|
933
|
+
res.json({
|
|
934
|
+
ok: true,
|
|
935
|
+
message: 'Get load balancing recommendation endpoint - implementation pending',
|
|
936
|
+
});
|
|
937
|
+
};
|
|
938
|
+
const startAutoScaling = async (_req, res) => {
|
|
939
|
+
res.json({ ok: true, message: 'Start auto-scaling endpoint - implementation pending' });
|
|
940
|
+
};
|
|
941
|
+
const stopAutoScaling = async (_req, res) => {
|
|
942
|
+
res.json({ ok: true, message: 'Stop auto-scaling endpoint - implementation pending' });
|
|
943
|
+
};
|
|
944
|
+
const evaluateScaling = async (_req, res) => {
|
|
945
|
+
res.json({ ok: true, message: 'Evaluate scaling endpoint - implementation pending' });
|
|
946
|
+
};
|
|
947
|
+
const lastScalingDecision = async (_req, res) => {
|
|
948
|
+
res.json({ ok: true, message: 'Last scaling decision endpoint - implementation pending' });
|
|
949
|
+
};
|
|
950
|
+
const scalingHistory = async (_req, res) => {
|
|
951
|
+
res.json({ ok: true, message: 'Scaling history endpoint - implementation pending' });
|
|
952
|
+
};
|
|
953
|
+
const costSummary = async (_req, res) => {
|
|
954
|
+
res.json({ ok: true, message: 'Cost summary endpoint - implementation pending' });
|
|
955
|
+
};
|
|
956
|
+
const setScalingPolicy = async (_req, res) => {
|
|
957
|
+
res.json({ ok: true, message: 'Set scaling policy endpoint - implementation pending' });
|
|
958
|
+
};
|
|
959
|
+
const getScalingPolicy = async (_req, res) => {
|
|
960
|
+
res.json({ ok: true, message: 'Get scaling policy endpoint - implementation pending' });
|
|
961
|
+
};
|
|
962
|
+
/**
|
|
963
|
+
* Stop Resource Monitoring
|
|
964
|
+
* Stops the resource monitor that captures CPU/memory snapshots
|
|
965
|
+
* @remarks
|
|
966
|
+
* - Stops periodic resource snapshots (no more [DEBUG] logs)
|
|
967
|
+
* - Disables cost estimation
|
|
968
|
+
* - Disables resource alerts (CPU/memory warnings)
|
|
969
|
+
* - May impact auto-scaling decisions
|
|
970
|
+
* @returns Success message
|
|
971
|
+
*/
|
|
972
|
+
const stopResourceMonitoring = async (_req, res) => {
|
|
973
|
+
try {
|
|
974
|
+
ResourceMonitor.stop();
|
|
975
|
+
res.json({ ok: true, message: 'Resource monitoring stopped' });
|
|
976
|
+
}
|
|
977
|
+
catch (error) {
|
|
978
|
+
Logger.error('WorkerController.stopResourceMonitoring failed', error);
|
|
979
|
+
res.setStatus(500).json({ error: error.message });
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
/**
|
|
983
|
+
* Start Resource Monitoring
|
|
984
|
+
* Starts the resource monitor to capture CPU/memory snapshots
|
|
985
|
+
* @remarks
|
|
986
|
+
* - Enables periodic resource snapshots (every 30s by default)
|
|
987
|
+
* - Enables cost estimation and tracking
|
|
988
|
+
* - Enables resource alerts for high CPU/memory usage
|
|
989
|
+
* - Required for resource-based auto-scaling
|
|
990
|
+
* @returns Success message
|
|
991
|
+
*/
|
|
992
|
+
const startResourceMonitoring = async (_req, res) => {
|
|
993
|
+
try {
|
|
994
|
+
ResourceMonitor.start();
|
|
995
|
+
res.json({ ok: true, message: 'Resource monitoring started' });
|
|
996
|
+
}
|
|
997
|
+
catch (error) {
|
|
998
|
+
Logger.error('WorkerController.startResourceMonitoring failed', error);
|
|
999
|
+
res.setStatus(500).json({ error: error.message });
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
const getCurrentResourceUsage = async (_req, res) => {
|
|
1003
|
+
res.json({ ok: true, message: 'Get current resource usage endpoint - implementation pending' });
|
|
1004
|
+
};
|
|
1005
|
+
const resourceHistory = async (_req, res) => {
|
|
1006
|
+
res.json({ ok: true, message: 'Resource history endpoint - implementation pending' });
|
|
1007
|
+
};
|
|
1008
|
+
const resourceAlerts = async (_req, res) => {
|
|
1009
|
+
res.json({ ok: true, message: 'Resource alerts endpoint - implementation pending' });
|
|
1010
|
+
};
|
|
1011
|
+
const resourceTrends = async (_req, res) => {
|
|
1012
|
+
res.json({ ok: true, message: 'Resource trends endpoint - implementation pending' });
|
|
1013
|
+
};
|
|
1014
|
+
const workerResourceTrend = async (_req, res) => {
|
|
1015
|
+
res.json({ ok: true, message: 'Worker resource trend endpoint - implementation pending' });
|
|
1016
|
+
};
|
|
1017
|
+
const updateCostConfig = async (_req, res) => {
|
|
1018
|
+
res.json({ ok: true, message: 'Update cost config endpoint - implementation pending' });
|
|
1019
|
+
};
|
|
1020
|
+
const calculateProjectedCost = async (_req, res) => {
|
|
1021
|
+
res.json({ ok: true, message: 'Calculate projected cost endpoint - implementation pending' });
|
|
1022
|
+
};
|
|
1023
|
+
const getSystemInfo = async (_req, res) => {
|
|
1024
|
+
res.json({ ok: true, message: 'Get system info endpoint - implementation pending' });
|
|
1025
|
+
};
|
|
1026
|
+
const registerDataSubject = async (_req, res) => {
|
|
1027
|
+
res.json({ ok: true, message: 'Register data subject endpoint - implementation pending' });
|
|
1028
|
+
};
|
|
1029
|
+
const recordConsent = async (_req, res) => {
|
|
1030
|
+
res.json({ ok: true, message: 'Record consent endpoint - implementation pending' });
|
|
1031
|
+
};
|
|
1032
|
+
const checkCompliance = async (_req, res) => {
|
|
1033
|
+
res.json({ ok: true, message: 'Check compliance endpoint - implementation pending' });
|
|
1034
|
+
};
|
|
1035
|
+
const createAccessRequest = async (_req, res) => {
|
|
1036
|
+
res.json({ ok: true, message: 'Create access request endpoint - implementation pending' });
|
|
1037
|
+
};
|
|
1038
|
+
const processAccessRequest = async (_req, res) => {
|
|
1039
|
+
res.json({ ok: true, message: 'Process access request endpoint - implementation pending' });
|
|
1040
|
+
};
|
|
1041
|
+
const encryptSensitiveData = async (_req, res) => {
|
|
1042
|
+
res.json({ ok: true, message: 'Encrypt sensitive data endpoint - implementation pending' });
|
|
1043
|
+
};
|
|
1044
|
+
const decryptSensitiveData = async (_req, res) => {
|
|
1045
|
+
res.json({ ok: true, message: 'Decrypt sensitive data endpoint - implementation pending' });
|
|
1046
|
+
};
|
|
1047
|
+
const recordViolation = async (_req, res) => {
|
|
1048
|
+
res.json({ ok: true, message: 'Record violation endpoint - implementation pending' });
|
|
1049
|
+
};
|
|
1050
|
+
const complianceAuditLogs = async (_req, res) => {
|
|
1051
|
+
res.json({ ok: true, message: 'Compliance audit logs endpoint - implementation pending' });
|
|
1052
|
+
};
|
|
1053
|
+
const complianceSummary = async (_req, res) => {
|
|
1054
|
+
res.json({ ok: true, message: 'Compliance summary endpoint - implementation pending' });
|
|
1055
|
+
};
|
|
1056
|
+
const prometheusMetrics = async (_req, res) => {
|
|
1057
|
+
res.json({ ok: true, message: 'Prometheus metrics endpoint - implementation pending' });
|
|
1058
|
+
};
|
|
1059
|
+
const recordCustomMetric = async (_req, res) => {
|
|
1060
|
+
res.json({ ok: true, message: 'Record custom metric endpoint - implementation pending' });
|
|
1061
|
+
};
|
|
1062
|
+
const startTrace = async (_req, res) => {
|
|
1063
|
+
res.json({ ok: true, message: 'Start trace endpoint - implementation pending' });
|
|
1064
|
+
};
|
|
1065
|
+
const endTrace = async (_req, res) => {
|
|
1066
|
+
res.json({ ok: true, message: 'End trace endpoint - implementation pending' });
|
|
1067
|
+
};
|
|
1068
|
+
/**
|
|
1069
|
+
* Get system-wide summary of all workers and monitoring
|
|
1070
|
+
* @returns System summary with worker count and monitoring data
|
|
1071
|
+
*/
|
|
1072
|
+
async function systemSummary(_req, res) {
|
|
1073
|
+
try {
|
|
1074
|
+
const workers = WorkerFactory.list();
|
|
1075
|
+
const monitoringSummaryData = await HealthMonitor.getSummary();
|
|
1076
|
+
res.json({
|
|
1077
|
+
ok: true,
|
|
1078
|
+
summary: {
|
|
1079
|
+
totalWorkers: workers.length,
|
|
1080
|
+
workers: workers,
|
|
1081
|
+
monitoring: monitoringSummaryData,
|
|
1082
|
+
},
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
Logger.error('WorkerController.systemSummary failed', error);
|
|
1087
|
+
res.setStatus(500).json({ error: error.message });
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Initiate graceful system shutdown
|
|
1092
|
+
* @returns Success message
|
|
1093
|
+
*/
|
|
1094
|
+
async function shutdown(_req, res) {
|
|
1095
|
+
try {
|
|
1096
|
+
// Use the centralized shutdown coordinator
|
|
1097
|
+
await WorkerShutdown.shutdown({ signal: 'API', timeout: 30000, forceExit: false });
|
|
1098
|
+
res.json({ ok: true, message: 'Graceful shutdown initiated successfully' });
|
|
1099
|
+
}
|
|
1100
|
+
catch (error) {
|
|
1101
|
+
Logger.error('WorkerController.shutdown failed', error);
|
|
1102
|
+
res.setStatus(500).json({ error: error.message });
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Get monitoring summary for all workers
|
|
1107
|
+
* @returns Monitoring summary data
|
|
1108
|
+
*/
|
|
1109
|
+
async function monitoringSummary(_req, res) {
|
|
1110
|
+
try {
|
|
1111
|
+
const summary = await HealthMonitor.getSummary();
|
|
1112
|
+
res.json({ ok: true, summary });
|
|
1113
|
+
}
|
|
1114
|
+
catch (error) {
|
|
1115
|
+
Logger.error('WorkerController.monitoringSummary failed', error);
|
|
1116
|
+
res.setStatus(500).json({ error: error.message });
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* SSE endpoint: stream worker and monitoring events
|
|
1121
|
+
* GET /api/workers/events
|
|
1122
|
+
*/
|
|
1123
|
+
const eventsStream = async (_req, res) => {
|
|
1124
|
+
const raw = res.getRaw();
|
|
1125
|
+
raw.writeHead(200, {
|
|
1126
|
+
'Content-Type': 'text/event-stream',
|
|
1127
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
1128
|
+
Connection: 'keep-alive',
|
|
1129
|
+
'X-Accel-Buffering': 'no',
|
|
1130
|
+
});
|
|
1131
|
+
let closed = false;
|
|
1132
|
+
const send = async (payload) => {
|
|
1133
|
+
try {
|
|
1134
|
+
const data = JSON.stringify(payload);
|
|
1135
|
+
raw.write(`data: ${data}\n\n`);
|
|
1136
|
+
}
|
|
1137
|
+
catch (err) {
|
|
1138
|
+
Logger.error('WorkerController.eventsStream failed', err);
|
|
1139
|
+
// ignore serialization errors
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
// Send initial hello
|
|
1143
|
+
await send({ type: 'hello', ts: new Date().toISOString() });
|
|
1144
|
+
// Periodic snapshot sender
|
|
1145
|
+
const intervalMs = 5000;
|
|
1146
|
+
const interval = setInterval(async () => {
|
|
1147
|
+
try {
|
|
1148
|
+
const monitoring = await HealthMonitor.getSummary();
|
|
1149
|
+
// include full workers listing with metrics/pagination to allow clients to patch the UI
|
|
1150
|
+
const workersPayload = await getWorkers({ page: 1, limit: 200 });
|
|
1151
|
+
await send({
|
|
1152
|
+
type: 'snapshot',
|
|
1153
|
+
ts: new Date().toISOString(),
|
|
1154
|
+
monitoring,
|
|
1155
|
+
workers: workersPayload,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
catch (err) {
|
|
1159
|
+
// send error event
|
|
1160
|
+
await send({ type: 'error', ts: new Date().toISOString(), message: err.message });
|
|
1161
|
+
}
|
|
1162
|
+
}, intervalMs);
|
|
1163
|
+
// Heartbeat to keep connection alive
|
|
1164
|
+
const hb = setInterval(() => {
|
|
1165
|
+
if (!closed)
|
|
1166
|
+
raw.write(': ping\n\n');
|
|
1167
|
+
}, 15000);
|
|
1168
|
+
// Clean up when client disconnects
|
|
1169
|
+
raw.on('close', () => {
|
|
1170
|
+
closed = true;
|
|
1171
|
+
clearInterval(interval);
|
|
1172
|
+
clearInterval(hb);
|
|
1173
|
+
});
|
|
1174
|
+
};
|
|
1175
|
+
/**
|
|
1176
|
+
* Builders that group related handlers to keep the create() method small.
|
|
1177
|
+
* Each builder returns a plain object with the relevant handler references.
|
|
1178
|
+
*/
|
|
1179
|
+
const buildCoreOperations = () => ({
|
|
1180
|
+
// Core operations
|
|
1181
|
+
create,
|
|
1182
|
+
start,
|
|
1183
|
+
stop,
|
|
1184
|
+
restart,
|
|
1185
|
+
pause,
|
|
1186
|
+
setAutoStart,
|
|
1187
|
+
resume,
|
|
1188
|
+
remove,
|
|
1189
|
+
get,
|
|
1190
|
+
update,
|
|
1191
|
+
status,
|
|
1192
|
+
getCreationStatus,
|
|
1193
|
+
metrics,
|
|
1194
|
+
health,
|
|
1195
|
+
});
|
|
1196
|
+
const buildHealthMonitoring = () => ({
|
|
1197
|
+
// Health monitoring
|
|
1198
|
+
startMonitoring,
|
|
1199
|
+
stopMonitoring,
|
|
1200
|
+
healthHistory,
|
|
1201
|
+
healthTrend,
|
|
1202
|
+
updateMonitoringConfig,
|
|
1203
|
+
eventsStream,
|
|
1204
|
+
getSlaStatus,
|
|
1205
|
+
});
|
|
1206
|
+
const buildVersioning = () => ({
|
|
1207
|
+
// Versioning
|
|
1208
|
+
registerVersion,
|
|
1209
|
+
listVersions,
|
|
1210
|
+
getVersion,
|
|
1211
|
+
deprecateVersion,
|
|
1212
|
+
activateVersion,
|
|
1213
|
+
deactivateVersion,
|
|
1214
|
+
checkCompatibility,
|
|
1215
|
+
});
|
|
1216
|
+
const buildCanary = () => ({
|
|
1217
|
+
// Canary deployments
|
|
1218
|
+
startCanary,
|
|
1219
|
+
pauseCanary,
|
|
1220
|
+
resumeCanary,
|
|
1221
|
+
rollbackCanary,
|
|
1222
|
+
canaryStatus,
|
|
1223
|
+
canaryHistory,
|
|
1224
|
+
});
|
|
1225
|
+
const buildCircuitBreaker = () => ({
|
|
1226
|
+
// Circuit breaker
|
|
1227
|
+
circuitBreakerState,
|
|
1228
|
+
resetCircuitBreaker,
|
|
1229
|
+
forceOpenCircuit,
|
|
1230
|
+
circuitBreakerEvents,
|
|
1231
|
+
});
|
|
1232
|
+
const buildDLQ = () => ({
|
|
1233
|
+
// Dead letter queue
|
|
1234
|
+
listFailedJobs,
|
|
1235
|
+
getFailedJob,
|
|
1236
|
+
retryFailedJob,
|
|
1237
|
+
deleteFailedJob,
|
|
1238
|
+
anonymizeFailedJob,
|
|
1239
|
+
dlqAuditLog,
|
|
1240
|
+
dlqStats,
|
|
1241
|
+
});
|
|
1242
|
+
const buildPlugins = () => ({
|
|
1243
|
+
// Plugins
|
|
1244
|
+
registerPlugin,
|
|
1245
|
+
unregisterPlugin,
|
|
1246
|
+
enablePlugin,
|
|
1247
|
+
disablePlugin,
|
|
1248
|
+
listPlugins,
|
|
1249
|
+
pluginExecutionHistory,
|
|
1250
|
+
pluginStatistics,
|
|
1251
|
+
});
|
|
1252
|
+
const buildMultiQueue = () => ({
|
|
1253
|
+
// Multi-queue
|
|
1254
|
+
createMultiQueue,
|
|
1255
|
+
startQueue,
|
|
1256
|
+
stopQueue,
|
|
1257
|
+
queueStats,
|
|
1258
|
+
updateQueuePriority,
|
|
1259
|
+
updateQueueConcurrency,
|
|
1260
|
+
});
|
|
1261
|
+
const buildDatacenter = () => ({
|
|
1262
|
+
// Datacenter
|
|
1263
|
+
registerRegion,
|
|
1264
|
+
unregisterRegion,
|
|
1265
|
+
listRegions,
|
|
1266
|
+
getRegion,
|
|
1267
|
+
updateRegionHealth,
|
|
1268
|
+
updateRegionLoad,
|
|
1269
|
+
placeWorker,
|
|
1270
|
+
getPlacement,
|
|
1271
|
+
updatePlacement,
|
|
1272
|
+
findOptimalRegion,
|
|
1273
|
+
setFailoverPolicy,
|
|
1274
|
+
getFailoverPolicy,
|
|
1275
|
+
startHealthChecks,
|
|
1276
|
+
stopHealthChecks,
|
|
1277
|
+
getTopology,
|
|
1278
|
+
getLoadBalancingRecommendation,
|
|
1279
|
+
});
|
|
1280
|
+
const buildAutoScaling = () => ({
|
|
1281
|
+
// Auto-scaling
|
|
1282
|
+
startAutoScaling,
|
|
1283
|
+
stopAutoScaling,
|
|
1284
|
+
evaluateScaling,
|
|
1285
|
+
lastScalingDecision,
|
|
1286
|
+
scalingHistory,
|
|
1287
|
+
costSummary,
|
|
1288
|
+
setScalingPolicy,
|
|
1289
|
+
getScalingPolicy,
|
|
1290
|
+
});
|
|
1291
|
+
const buildResources = () => ({
|
|
1292
|
+
// Resources
|
|
1293
|
+
stopResourceMonitoring,
|
|
1294
|
+
startResourceMonitoring,
|
|
1295
|
+
getCurrentResourceUsage,
|
|
1296
|
+
resourceHistory,
|
|
1297
|
+
resourceAlerts,
|
|
1298
|
+
resourceTrends,
|
|
1299
|
+
workerResourceTrend,
|
|
1300
|
+
updateCostConfig,
|
|
1301
|
+
calculateProjectedCost,
|
|
1302
|
+
getSystemInfo,
|
|
1303
|
+
});
|
|
1304
|
+
const buildCompliance = () => ({
|
|
1305
|
+
// Compliance
|
|
1306
|
+
registerDataSubject,
|
|
1307
|
+
recordConsent,
|
|
1308
|
+
checkCompliance,
|
|
1309
|
+
createAccessRequest,
|
|
1310
|
+
processAccessRequest,
|
|
1311
|
+
encryptSensitiveData,
|
|
1312
|
+
decryptSensitiveData,
|
|
1313
|
+
recordViolation,
|
|
1314
|
+
complianceAuditLogs,
|
|
1315
|
+
complianceSummary,
|
|
1316
|
+
});
|
|
1317
|
+
const buildObservability = () => ({
|
|
1318
|
+
// Observability
|
|
1319
|
+
prometheusMetrics,
|
|
1320
|
+
recordCustomMetric,
|
|
1321
|
+
startTrace,
|
|
1322
|
+
endTrace,
|
|
1323
|
+
});
|
|
1324
|
+
const buildSystem = () => ({
|
|
1325
|
+
// System
|
|
1326
|
+
systemSummary,
|
|
1327
|
+
shutdown,
|
|
1328
|
+
monitoringSummary,
|
|
1329
|
+
});
|
|
1330
|
+
export const WorkerController = Object.freeze({
|
|
1331
|
+
create() {
|
|
1332
|
+
// Compose grouped handlers to keep this function short
|
|
1333
|
+
return {
|
|
1334
|
+
...buildCoreOperations(),
|
|
1335
|
+
...buildHealthMonitoring(),
|
|
1336
|
+
...buildVersioning(),
|
|
1337
|
+
...buildCanary(),
|
|
1338
|
+
...buildCircuitBreaker(),
|
|
1339
|
+
...buildDLQ(),
|
|
1340
|
+
...buildPlugins(),
|
|
1341
|
+
...buildMultiQueue(),
|
|
1342
|
+
...buildDatacenter(),
|
|
1343
|
+
...buildAutoScaling(),
|
|
1344
|
+
...buildResources(),
|
|
1345
|
+
...buildCompliance(),
|
|
1346
|
+
...buildObservability(),
|
|
1347
|
+
...buildSystem(),
|
|
1348
|
+
};
|
|
1349
|
+
},
|
|
1350
|
+
});
|
|
1351
|
+
export default WorkerController;
|