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