@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,226 @@
|
|
|
1
|
+
import { Logger } from '@zintrust/core';
|
|
2
|
+
const VALID_DRIVERS = new Set(['database', 'redis', 'memory']);
|
|
3
|
+
const VALID_DEAD_LETTER_POLICIES = new Set(['expire', 'retry', 'dead-letter']);
|
|
4
|
+
const validatePersistence = (persistence) => {
|
|
5
|
+
if (!persistence)
|
|
6
|
+
return 'Persistence configuration is required';
|
|
7
|
+
// Validate persistence driver
|
|
8
|
+
if (!persistence.driver) {
|
|
9
|
+
return 'Persistence driver is required';
|
|
10
|
+
}
|
|
11
|
+
if (!VALID_DRIVERS.has(persistence.driver)) {
|
|
12
|
+
return 'Persistence driver must be one of: database, redis, memory';
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
};
|
|
16
|
+
const validateRedis = (redis) => {
|
|
17
|
+
if (!redis)
|
|
18
|
+
return 'Redis configuration is required';
|
|
19
|
+
// Validate env flag
|
|
20
|
+
if (typeof redis.env !== 'boolean') {
|
|
21
|
+
return 'Redis env flag must be a boolean';
|
|
22
|
+
}
|
|
23
|
+
// Validate required fields when not using env
|
|
24
|
+
if (!redis.env) {
|
|
25
|
+
const requiredFieldsError = validateRequiredRedisFields(redis);
|
|
26
|
+
if (requiredFieldsError)
|
|
27
|
+
return requiredFieldsError;
|
|
28
|
+
}
|
|
29
|
+
// Validate string fields
|
|
30
|
+
return validateRedisStringFields(redis);
|
|
31
|
+
};
|
|
32
|
+
const validateRequiredRedisFields = (redis) => {
|
|
33
|
+
if (!redis.host || typeof redis.host !== 'string') {
|
|
34
|
+
return 'Redis host is required when env is false';
|
|
35
|
+
}
|
|
36
|
+
if (redis.port === undefined || redis.port === null) {
|
|
37
|
+
return 'Redis port is required when env is false';
|
|
38
|
+
}
|
|
39
|
+
if (typeof redis.port !== 'string' && typeof redis.port !== 'number') {
|
|
40
|
+
return 'Redis port must be a string or number';
|
|
41
|
+
}
|
|
42
|
+
if (!redis.db || typeof redis.db !== 'string') {
|
|
43
|
+
return 'Redis db is required when env is false';
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
const validateRedisStringFields = (redis) => {
|
|
48
|
+
if (redis.host && typeof redis.host !== 'string') {
|
|
49
|
+
return 'Redis host must be a string';
|
|
50
|
+
}
|
|
51
|
+
if (redis.port && typeof redis.port !== 'string' && typeof redis.port !== 'number') {
|
|
52
|
+
return 'Redis port must be a string or number';
|
|
53
|
+
}
|
|
54
|
+
if (redis.db && typeof redis.db !== 'string') {
|
|
55
|
+
return 'Redis db must be a string';
|
|
56
|
+
}
|
|
57
|
+
if (redis.password && typeof redis.password !== 'string') {
|
|
58
|
+
return 'Redis password must be a string';
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
const validateDeadLetterQueue = (deadLetterQueue) => {
|
|
63
|
+
if (!deadLetterQueue)
|
|
64
|
+
return 'DeadLetterQueue configuration is required';
|
|
65
|
+
// Validate policy
|
|
66
|
+
if (!deadLetterQueue.policy) {
|
|
67
|
+
return 'DeadLetterQueue policy is required';
|
|
68
|
+
}
|
|
69
|
+
if (!VALID_DEAD_LETTER_POLICIES.has(deadLetterQueue.policy)) {
|
|
70
|
+
return 'Policy must be one of: expire, retry, dead-letter';
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
};
|
|
74
|
+
const validateCompliance = (compliance) => {
|
|
75
|
+
if (!compliance)
|
|
76
|
+
return 'Compliance configuration is required';
|
|
77
|
+
if (!compliance.config) {
|
|
78
|
+
return 'Compliance config is required';
|
|
79
|
+
}
|
|
80
|
+
if (typeof compliance.config.retentionDays !== 'number' || compliance.config.retentionDays < 0) {
|
|
81
|
+
return 'Retention days must be a non-negative number';
|
|
82
|
+
}
|
|
83
|
+
const MAX_RETENTION_DAYS = 3650; // ~10 years
|
|
84
|
+
if (compliance.config.retentionDays > MAX_RETENTION_DAYS) {
|
|
85
|
+
return `Retention days cannot exceed ${MAX_RETENTION_DAYS}`;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
const validateObservability = (observability) => {
|
|
90
|
+
if (!observability)
|
|
91
|
+
return 'Observability configuration is required';
|
|
92
|
+
if (typeof observability.enabled !== 'boolean') {
|
|
93
|
+
return 'Observability enabled flag must be a boolean';
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
};
|
|
97
|
+
const validateAutoScaler = (autoScaler) => {
|
|
98
|
+
if (!autoScaler)
|
|
99
|
+
return 'AutoScaler configuration is required';
|
|
100
|
+
if (typeof autoScaler.enabled !== 'boolean') {
|
|
101
|
+
return 'AutoScaler enabled flag must be a boolean';
|
|
102
|
+
}
|
|
103
|
+
if (autoScaler.enabled) {
|
|
104
|
+
const minWorkersError = validateWorkerCount(autoScaler.minWorkers, 'minWorkers');
|
|
105
|
+
if (minWorkersError)
|
|
106
|
+
return minWorkersError;
|
|
107
|
+
const maxWorkersError = validateWorkerCount(autoScaler.maxWorkers, 'maxWorkers');
|
|
108
|
+
if (maxWorkersError)
|
|
109
|
+
return maxWorkersError;
|
|
110
|
+
if (autoScaler.minWorkers > autoScaler.maxWorkers) {
|
|
111
|
+
return 'AutoScaler minWorkers cannot be greater than maxWorkers';
|
|
112
|
+
}
|
|
113
|
+
const MAX_AUTOSCALER_WORKERS = 1000;
|
|
114
|
+
if (autoScaler.maxWorkers > MAX_AUTOSCALER_WORKERS) {
|
|
115
|
+
return `AutoScaler maxWorkers cannot exceed ${MAX_AUTOSCALER_WORKERS}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
};
|
|
120
|
+
const validateWorkerCount = (value, fieldName) => {
|
|
121
|
+
if (typeof value !== 'number') {
|
|
122
|
+
return `AutoScaler ${fieldName} must be a number`;
|
|
123
|
+
}
|
|
124
|
+
if (!Number.isInteger(value)) {
|
|
125
|
+
return `AutoScaler ${fieldName} must be a whole number (integer)`;
|
|
126
|
+
}
|
|
127
|
+
if (value < 0) {
|
|
128
|
+
return `AutoScaler ${fieldName} must be a non-negative number`;
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
};
|
|
132
|
+
const sanitizeInfrastructure = (infrastructure) => {
|
|
133
|
+
return {
|
|
134
|
+
...infrastructure,
|
|
135
|
+
autoScaler: {
|
|
136
|
+
...infrastructure.autoScaler,
|
|
137
|
+
minWorkers: Math.floor(infrastructure.autoScaler.minWorkers),
|
|
138
|
+
maxWorkers: Math.floor(infrastructure.autoScaler.maxWorkers),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
export const withInfrastructureValidation = (handler) => {
|
|
143
|
+
return async (req, res) => {
|
|
144
|
+
try {
|
|
145
|
+
const data = req.data();
|
|
146
|
+
const infrastructure = data['infrastructure'];
|
|
147
|
+
if (!infrastructure) {
|
|
148
|
+
return res.setStatus(400).json({
|
|
149
|
+
error: 'Infrastructure configuration is required',
|
|
150
|
+
code: 'MISSING_INFRASTRUCTURE',
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Validate persistence
|
|
154
|
+
const persistenceError = validatePersistence(infrastructure.persistence);
|
|
155
|
+
if (persistenceError) {
|
|
156
|
+
return res.setStatus(400).json({
|
|
157
|
+
error: 'Invalid persistence configuration',
|
|
158
|
+
message: persistenceError,
|
|
159
|
+
code: 'INVALID_PERSISTENCE_CONFIG',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// Validate redis
|
|
163
|
+
const redisError = validateRedis(infrastructure.redis);
|
|
164
|
+
if (redisError) {
|
|
165
|
+
return res.setStatus(400).json({
|
|
166
|
+
error: 'Invalid redis configuration',
|
|
167
|
+
message: redisError,
|
|
168
|
+
code: 'INVALID_REDIS_CONFIG',
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
// Validate deadLetterQueue
|
|
172
|
+
const deadLetterQueueError = validateDeadLetterQueue(infrastructure.deadLetterQueue);
|
|
173
|
+
if (deadLetterQueueError) {
|
|
174
|
+
return res.setStatus(400).json({
|
|
175
|
+
error: 'Invalid deadLetterQueue configuration',
|
|
176
|
+
message: deadLetterQueueError,
|
|
177
|
+
code: 'INVALID_DEAD_LETTER_QUEUE_CONFIG',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// Validate compliance
|
|
181
|
+
const complianceError = validateCompliance(infrastructure.compliance);
|
|
182
|
+
if (complianceError) {
|
|
183
|
+
return res.setStatus(400).json({
|
|
184
|
+
error: 'Invalid compliance configuration',
|
|
185
|
+
message: complianceError,
|
|
186
|
+
code: 'INVALID_COMPLIANCE_CONFIG',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
// Validate observability
|
|
190
|
+
const observabilityError = validateObservability(infrastructure.observability);
|
|
191
|
+
if (observabilityError) {
|
|
192
|
+
return res.setStatus(400).json({
|
|
193
|
+
error: 'Invalid observability configuration',
|
|
194
|
+
message: observabilityError,
|
|
195
|
+
code: 'INVALID_OBSERVABILITY_CONFIG',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
// Validate autoScaler
|
|
199
|
+
const autoScalerError = validateAutoScaler(infrastructure.autoScaler);
|
|
200
|
+
if (autoScalerError) {
|
|
201
|
+
return res.setStatus(400).json({
|
|
202
|
+
error: 'Invalid autoScaler configuration',
|
|
203
|
+
message: autoScalerError,
|
|
204
|
+
code: 'INVALID_AUTO_SCALER_CONFIG',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// Sanitize infrastructure values
|
|
208
|
+
const currentBody = req.getBody();
|
|
209
|
+
const sanitizedInfrastructure = sanitizeInfrastructure(infrastructure);
|
|
210
|
+
// Update the infrastructure in the request body
|
|
211
|
+
const updatedBody = {
|
|
212
|
+
...currentBody,
|
|
213
|
+
infrastructure: sanitizedInfrastructure,
|
|
214
|
+
};
|
|
215
|
+
req.setBody(updatedBody);
|
|
216
|
+
return handler(req, res);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
Logger.error('Infrastructure validation failed', error);
|
|
220
|
+
return res.setStatus(500).json({
|
|
221
|
+
error: 'Internal validation error',
|
|
222
|
+
code: 'VALIDATION_ERROR',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Logger } from '@zintrust/core';
|
|
2
|
+
const validateConcurrency = (concurrency) => {
|
|
3
|
+
if (concurrency === undefined || concurrency === null) {
|
|
4
|
+
return 'Concurrency is required';
|
|
5
|
+
}
|
|
6
|
+
if (typeof concurrency !== 'number') {
|
|
7
|
+
return 'Concurrency must be a number';
|
|
8
|
+
}
|
|
9
|
+
if (!Number.isInteger(concurrency)) {
|
|
10
|
+
return 'Concurrency must be a whole number (integer)';
|
|
11
|
+
}
|
|
12
|
+
if (concurrency < 1) {
|
|
13
|
+
return 'Concurrency must be at least 1';
|
|
14
|
+
}
|
|
15
|
+
const MAX_CONCURRENCY = 200;
|
|
16
|
+
if (concurrency > MAX_CONCURRENCY) {
|
|
17
|
+
return `Concurrency cannot exceed ${MAX_CONCURRENCY}`;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
const validateLimiter = (limiter) => {
|
|
22
|
+
if (!limiter) {
|
|
23
|
+
return 'Limiter configuration is required';
|
|
24
|
+
}
|
|
25
|
+
// Validate max
|
|
26
|
+
if (limiter.max === undefined || limiter.max === null) {
|
|
27
|
+
return 'Limiter max is required';
|
|
28
|
+
}
|
|
29
|
+
if (typeof limiter.max !== 'number') {
|
|
30
|
+
return 'Limiter max must be a number';
|
|
31
|
+
}
|
|
32
|
+
if (!Number.isInteger(limiter.max)) {
|
|
33
|
+
return 'Limiter max must be a whole number (integer)';
|
|
34
|
+
}
|
|
35
|
+
if (limiter.max < 1) {
|
|
36
|
+
return 'Limiter max must be at least 1';
|
|
37
|
+
}
|
|
38
|
+
const MAX_LIMITER_MAX = 100000;
|
|
39
|
+
if (limiter.max > MAX_LIMITER_MAX) {
|
|
40
|
+
return `Limiter max cannot exceed ${MAX_LIMITER_MAX}`;
|
|
41
|
+
}
|
|
42
|
+
// Validate duration
|
|
43
|
+
if (limiter.duration === undefined || limiter.duration === null) {
|
|
44
|
+
return 'Limiter duration is required';
|
|
45
|
+
}
|
|
46
|
+
if (typeof limiter.duration !== 'number') {
|
|
47
|
+
return 'Limiter duration must be a number';
|
|
48
|
+
}
|
|
49
|
+
if (!Number.isInteger(limiter.duration)) {
|
|
50
|
+
return 'Limiter duration must be a whole number (integer)';
|
|
51
|
+
}
|
|
52
|
+
if (limiter.duration < 1000) {
|
|
53
|
+
return 'Limiter duration must be at least 1000ms';
|
|
54
|
+
}
|
|
55
|
+
const MAX_LIMITER_DURATION = 24 * 60 * 60 * 1000; // 1 day
|
|
56
|
+
if (limiter.duration > MAX_LIMITER_DURATION) {
|
|
57
|
+
return `Limiter duration cannot exceed ${MAX_LIMITER_DURATION} ms`;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
};
|
|
61
|
+
export const withOptionsValidation = (handler) => {
|
|
62
|
+
return async (req, res) => {
|
|
63
|
+
try {
|
|
64
|
+
const data = req.data();
|
|
65
|
+
const options = data['options'];
|
|
66
|
+
if (!options) {
|
|
67
|
+
return res.setStatus(400).json({
|
|
68
|
+
error: 'Options configuration is required',
|
|
69
|
+
code: 'MISSING_OPTIONS',
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Validate concurrency
|
|
73
|
+
const concurrencyError = validateConcurrency(options.concurrency);
|
|
74
|
+
if (concurrencyError) {
|
|
75
|
+
return res.setStatus(400).json({
|
|
76
|
+
error: 'Invalid concurrency',
|
|
77
|
+
message: concurrencyError,
|
|
78
|
+
code: 'INVALID_CONCURRENCY',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Validate limiter
|
|
82
|
+
const limiterError = validateLimiter(options.limiter);
|
|
83
|
+
if (limiterError) {
|
|
84
|
+
return res.setStatus(400).json({
|
|
85
|
+
error: 'Invalid limiter configuration',
|
|
86
|
+
message: limiterError,
|
|
87
|
+
code: 'INVALID_LIMITER_CONFIG',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Sanitize concurrency to ensure it's an integer
|
|
91
|
+
const currentBody = req.getBody();
|
|
92
|
+
const sanitizedOptions = {
|
|
93
|
+
...options,
|
|
94
|
+
concurrency: Math.floor(options.concurrency),
|
|
95
|
+
limiter: {
|
|
96
|
+
...options.limiter,
|
|
97
|
+
max: Math.floor(options.limiter.max),
|
|
98
|
+
duration: Math.floor(options.limiter.duration),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
req.setBody({ ...currentBody, options: sanitizedOptions });
|
|
102
|
+
return handler(req, res);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
Logger.error('Options validation failed', error);
|
|
106
|
+
return res.setStatus(500).json({
|
|
107
|
+
error: 'Internal validation error',
|
|
108
|
+
code: 'VALIDATION_ERROR',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type IRequest, type IResponse } from '@zintrust/core';
|
|
2
|
+
export type RouteHandler = (req: IRequest, res: IResponse) => Promise<void> | void;
|
|
3
|
+
/**
|
|
4
|
+
* Middleware to strip unknown properties from the request body.
|
|
5
|
+
* Only properties included in the allowedKeys list are preserved.
|
|
6
|
+
*/
|
|
7
|
+
export declare const withStrictPayloadKeys: (allowedKeys: string[], handler: RouteHandler) => RouteHandler;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Logger } from '@zintrust/core';
|
|
2
|
+
/**
|
|
3
|
+
* Middleware to strip unknown properties from the request body.
|
|
4
|
+
* Only properties included in the allowedKeys list are preserved.
|
|
5
|
+
*/
|
|
6
|
+
export const withStrictPayloadKeys = (allowedKeys, handler) => {
|
|
7
|
+
const allowedSet = new Set(allowedKeys);
|
|
8
|
+
return async (req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
const data = req.data();
|
|
11
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
12
|
+
// If body is not an object, skip stripping or strictly enforce object?
|
|
13
|
+
// For worker creation, it must be an object.
|
|
14
|
+
// Let's rely on downstream validators to complain if data is missing/wrong type.
|
|
15
|
+
return handler(req, res);
|
|
16
|
+
}
|
|
17
|
+
const body = data;
|
|
18
|
+
const strippedBody = {};
|
|
19
|
+
let hasUnknowns = false;
|
|
20
|
+
for (const key of Object.keys(body)) {
|
|
21
|
+
if (allowedSet.has(key)) {
|
|
22
|
+
strippedBody[key] = body[key];
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
hasUnknowns = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (hasUnknowns) {
|
|
29
|
+
// Update the body with sanitized version
|
|
30
|
+
req.setBody(strippedBody);
|
|
31
|
+
}
|
|
32
|
+
return handler(req, res);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
Logger.error('Strict payload validation failed', error);
|
|
36
|
+
return res.setStatus(500).json({
|
|
37
|
+
error: 'Internal validation error',
|
|
38
|
+
code: 'VALIDATION_ERROR',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Logger, NodeSingletons } from '@zintrust/core';
|
|
2
|
+
const PROCESSOR_PATH_PATTERN = /^[a-zA-Z0-9/_.-]+\.(ts|js)$/;
|
|
3
|
+
const decodeProcessorPath = (processor) => {
|
|
4
|
+
return processor
|
|
5
|
+
.replaceAll('/', '/') // HTML hex entity for /
|
|
6
|
+
.replaceAll('%2F', '/') // URL encoding for /
|
|
7
|
+
.replaceAll('.', '.') // HTML hex entity for .
|
|
8
|
+
.replaceAll('%2E', '.') // URL encoding for .
|
|
9
|
+
.replaceAll('_', '_') // HTML hex entity for _
|
|
10
|
+
.replaceAll('%5F', '_') // URL encoding for _
|
|
11
|
+
.replaceAll('-', '-') // HTML hex entity for -
|
|
12
|
+
.replaceAll('%2D', '-'); // URL encoding for -
|
|
13
|
+
};
|
|
14
|
+
export const withProcessorPathValidation = (handler) => {
|
|
15
|
+
return async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const data = req.data();
|
|
18
|
+
let processor = data['processor'];
|
|
19
|
+
if (!processor) {
|
|
20
|
+
return res.setStatus(400).json({
|
|
21
|
+
error: 'Processor path is required',
|
|
22
|
+
code: 'MISSING_PROCESSOR_PATH',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// Decode URL-encoded characters
|
|
26
|
+
processor = decodeProcessorPath(processor);
|
|
27
|
+
// Trim whitespace
|
|
28
|
+
processor = processor.trim();
|
|
29
|
+
// Prevent path traversal
|
|
30
|
+
if (processor.includes('..') || processor.startsWith('/')) {
|
|
31
|
+
return res.setStatus(400).json({
|
|
32
|
+
error: 'Invalid processor path',
|
|
33
|
+
message: 'Processor path must be relative and cannot contain path traversal',
|
|
34
|
+
code: 'INVALID_PROCESSOR_PATH',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (!PROCESSOR_PATH_PATTERN.test(processor)) {
|
|
38
|
+
Logger.error('Processor path validation failed', {
|
|
39
|
+
processor,
|
|
40
|
+
pattern: PROCESSOR_PATH_PATTERN.toString(),
|
|
41
|
+
testResult: PROCESSOR_PATH_PATTERN.test(processor),
|
|
42
|
+
});
|
|
43
|
+
return res.setStatus(400).json({
|
|
44
|
+
error: 'Invalid processor path',
|
|
45
|
+
message: `Processor must be a TypeScript or JavaScript file. Got: "${processor}"`,
|
|
46
|
+
code: 'INVALID_PROCESSOR_EXTENSION',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Sanitize the processor path (remove any invalid characters just in case)
|
|
50
|
+
const sanitizedProcessor = processor.replaceAll(/[^a-zA-Z0-9/_.-]/g, '');
|
|
51
|
+
const path = NodeSingletons.path;
|
|
52
|
+
// Ensure resolved path stays within repository/app scope
|
|
53
|
+
const BASE_PROCESSOR_DIR = path.resolve(process.cwd());
|
|
54
|
+
const resolved = path.resolve(BASE_PROCESSOR_DIR, sanitizedProcessor);
|
|
55
|
+
if (!resolved.startsWith(BASE_PROCESSOR_DIR)) {
|
|
56
|
+
return res.setStatus(400).json({
|
|
57
|
+
error: 'Invalid processor path',
|
|
58
|
+
message: 'Processor path resolves outside of allowed base directory',
|
|
59
|
+
code: 'INVALID_PROCESSOR_PATH_TRAVERSAL',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const currentBody = req.getBody();
|
|
63
|
+
req.setBody({ ...currentBody, processor: sanitizedProcessor });
|
|
64
|
+
return handler(req, res);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
Logger.error('Processor path validation failed', error);
|
|
68
|
+
return res.setStatus(500).json({
|
|
69
|
+
error: 'Internal validation error',
|
|
70
|
+
code: 'VALIDATION_ERROR',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Logger } from '@zintrust/core';
|
|
2
|
+
const QUEUE_NAME_PATTERN = /^[a-zA-Z0-9_-]{3,50}$/;
|
|
3
|
+
export const withQueueNameValidation = (handler) => {
|
|
4
|
+
return async (req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const data = req.data();
|
|
7
|
+
const queueName = data['queueName'];
|
|
8
|
+
if (!queueName) {
|
|
9
|
+
return res.setStatus(400).json({
|
|
10
|
+
error: 'Queue name is required',
|
|
11
|
+
code: 'MISSING_QUEUE_NAME',
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
if (!QUEUE_NAME_PATTERN.test(queueName)) {
|
|
15
|
+
return res.setStatus(400).json({
|
|
16
|
+
error: 'Invalid queue name',
|
|
17
|
+
message: 'Queue name must be 3-50 characters long and contain only letters, numbers, hyphens, and underscores',
|
|
18
|
+
code: 'INVALID_QUEUE_NAME',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
// Sanitize the queue name
|
|
22
|
+
const sanitizedQueueName = queueName
|
|
23
|
+
.trim()
|
|
24
|
+
.replaceAll(/[^a-zA-Z0-9_-]/g, '')
|
|
25
|
+
.substring(0, 50);
|
|
26
|
+
if (sanitizedQueueName.length < 3) {
|
|
27
|
+
return res.setStatus(400).json({
|
|
28
|
+
error: 'Queue name too short after sanitization',
|
|
29
|
+
message: 'Queue name must be at least 3 characters long',
|
|
30
|
+
code: 'QUEUE_NAME_TOO_SHORT',
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const currentBody = req.getBody();
|
|
34
|
+
req.setBody({ ...currentBody, queueName: sanitizedQueueName });
|
|
35
|
+
return handler(req, res);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
Logger.error('Queue name validation failed', error);
|
|
39
|
+
return res.setStatus(500).json({
|
|
40
|
+
error: 'Internal validation error',
|
|
41
|
+
code: 'VALIDATION_ERROR',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Driver Middleware
|
|
3
|
+
* Ensures the 'driver' query parameter is valid if present.
|
|
4
|
+
*/
|
|
5
|
+
import type { IRequest, IResponse } from '@zintrust/core';
|
|
6
|
+
export type RouteHandler = (req: IRequest, res: IResponse) => Promise<void> | void;
|
|
7
|
+
export declare const withDriverValidation: (handler: RouteHandler) => RouteHandler;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Driver Middleware
|
|
3
|
+
* Ensures the 'driver' query parameter is valid if present.
|
|
4
|
+
*/
|
|
5
|
+
const VALID_DRIVERS = new Set(['database', 'redis', 'memory']);
|
|
6
|
+
export const withDriverValidation = (handler) => {
|
|
7
|
+
return async (req, res) => {
|
|
8
|
+
const driver = req.getQueryParam('driver');
|
|
9
|
+
// Normalize to string if it's an array (take first)
|
|
10
|
+
const driverValue = Array.isArray(driver) ? driver[0] : driver;
|
|
11
|
+
if (driverValue && !VALID_DRIVERS.has(driverValue)) {
|
|
12
|
+
res.setStatus(400).json({
|
|
13
|
+
error: 'Invalid driver parameter',
|
|
14
|
+
message: 'Driver must be one of: database, redis, memory',
|
|
15
|
+
});
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
return handler(req, res);
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Logger } from '@zintrust/core';
|
|
2
|
+
const VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
3
|
+
export const withVersionValidation = (handler) => {
|
|
4
|
+
return async (req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const data = req.data();
|
|
7
|
+
const version = data['version'];
|
|
8
|
+
if (version && !VERSION_PATTERN.test(version)) {
|
|
9
|
+
return res.setStatus(400).json({
|
|
10
|
+
error: 'Invalid version format',
|
|
11
|
+
message: 'Version must follow semantic versioning (e.g., 1.0.0)',
|
|
12
|
+
code: 'INVALID_VERSION_FORMAT',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return handler(req, res);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
Logger.error('Version validation failed', error);
|
|
19
|
+
return res.setStatus(500).json({
|
|
20
|
+
error: 'Internal validation error',
|
|
21
|
+
code: 'VALIDATION_ERROR',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Logger } from '@zintrust/core';
|
|
2
|
+
const WORKER_NAME_PATTERN = /^[a-zA-Z0-9_-]{3,50}$/;
|
|
3
|
+
export const withWorkerNameValidation = (handler) => {
|
|
4
|
+
return async (req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const data = req.data();
|
|
7
|
+
const workerName = data['name'];
|
|
8
|
+
if (!workerName) {
|
|
9
|
+
return res.setStatus(400).json({
|
|
10
|
+
error: 'Worker name is required',
|
|
11
|
+
code: 'MISSING_WORKER_NAME',
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
if (!WORKER_NAME_PATTERN.test(workerName)) {
|
|
15
|
+
return res.setStatus(400).json({
|
|
16
|
+
error: 'Invalid worker name',
|
|
17
|
+
message: 'Worker name must be 3-50 characters long and contain only letters, numbers, hyphens, and underscores',
|
|
18
|
+
code: 'INVALID_WORKER_NAME',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
// Sanitize the name
|
|
22
|
+
const sanitizedName = workerName
|
|
23
|
+
.trim()
|
|
24
|
+
.replaceAll(/[^a-zA-Z0-9_-]/g, '')
|
|
25
|
+
.substring(0, 50);
|
|
26
|
+
if (sanitizedName.length < 3) {
|
|
27
|
+
return res.setStatus(400).json({
|
|
28
|
+
error: 'Worker name too short after sanitization',
|
|
29
|
+
message: 'Worker name must be at least 3 characters long',
|
|
30
|
+
code: 'WORKER_NAME_TOO_SHORT',
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// Update the request data with sanitized value
|
|
34
|
+
const currentBody = req.getBody();
|
|
35
|
+
req.setBody({ ...currentBody, name: sanitizedName });
|
|
36
|
+
return handler(req, res);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
Logger.error('Worker name validation failed', error);
|
|
40
|
+
return res.setStatus(500).json({
|
|
41
|
+
error: 'Internal validation error',
|
|
42
|
+
code: 'VALIDATION_ERROR',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type IRequest, type IResponse } from '@zintrust/core';
|
|
2
|
+
export type RouteHandler = (req: IRequest, res: IResponse) => Promise<void> | void;
|
|
3
|
+
/**
|
|
4
|
+
* Composite middleware for worker creation validation
|
|
5
|
+
* Validates all required fields for creating a new worker
|
|
6
|
+
*/
|
|
7
|
+
export declare const withCreateWorkerValidation: (handler: RouteHandler) => RouteHandler;
|
|
8
|
+
/**
|
|
9
|
+
* Composite middleware for worker update validation
|
|
10
|
+
* Validates optional fields for updating an existing worker
|
|
11
|
+
*/
|
|
12
|
+
export declare const withUpdateWorkerValidation: (handler: RouteHandler) => RouteHandler;
|
|
13
|
+
/**
|
|
14
|
+
* Composite middleware for worker operation validation
|
|
15
|
+
* Validates worker name for operations like start, stop, restart, etc.
|
|
16
|
+
*/
|
|
17
|
+
export declare const withWorkerOperationValidation: (handler: RouteHandler) => RouteHandler;
|
|
18
|
+
/**
|
|
19
|
+
* Composite middleware for bulk operations validation
|
|
20
|
+
* Validates arrays of worker names and operation parameters
|
|
21
|
+
*/
|
|
22
|
+
export declare const withBulkOperationValidation: (handler: RouteHandler) => RouteHandler;
|
|
23
|
+
/**
|
|
24
|
+
* Composite middleware for canary deployment validation
|
|
25
|
+
* Validates canary-specific parameters
|
|
26
|
+
*/
|
|
27
|
+
export declare const withCanaryDeploymentValidation: (handler: RouteHandler) => RouteHandler;
|