@zintrust/core 2.4.0 → 2.4.2
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/bin/zintrust-main.d.ts.map +1 -1
- package/bin/zintrust-main.js +11 -1
- package/package.json +2 -2
- package/src/cache/Cache.d.ts +6 -0
- package/src/cache/Cache.d.ts.map +1 -1
- package/src/cache/Cache.js +50 -0
- package/src/cache/CacheDriver.d.ts +8 -0
- package/src/cache/CacheDriver.d.ts.map +1 -1
- package/src/cache/drivers/RedisDriver.d.ts.map +1 -1
- package/src/cache/drivers/RedisDriver.js +64 -2
- package/src/cli/CLI.d.ts.map +1 -1
- package/src/cli/CLI.js +2 -0
- package/src/cli/commands/DoctorArchitectureCommand.d.ts.map +1 -1
- package/src/cli/commands/DoctorArchitectureCommand.js +4 -2
- package/src/cli/commands/RedisRpcCommand.d.ts +6 -0
- package/src/cli/commands/RedisRpcCommand.d.ts.map +1 -0
- package/src/cli/commands/RedisRpcCommand.js +96 -0
- package/src/cli/commands/index.d.ts +1 -0
- package/src/cli/commands/index.d.ts.map +1 -1
- package/src/cli/commands/index.js +1 -0
- package/src/cli/scaffolding/env.d.ts.map +1 -1
- package/src/cli/scaffolding/env.js +9 -0
- package/src/cli/services/WorkerStartupDiagnostics.d.ts.map +1 -1
- package/src/cli/services/WorkerStartupDiagnostics.js +4 -2
- package/src/config/app.d.ts.map +1 -1
- package/src/config/app.js +3 -0
- package/src/config/env.d.ts +7 -0
- package/src/config/env.d.ts.map +1 -1
- package/src/config/env.js +7 -0
- package/src/config/queue.d.ts.map +1 -1
- package/src/config/queue.js +4 -42
- package/src/config/workers.d.ts.map +1 -1
- package/src/config/workers.js +12 -6
- package/src/index.js +3 -3
- package/src/orm/adapters/D1RemoteAdapter.d.ts.map +1 -1
- package/src/orm/adapters/D1RemoteAdapter.js +2 -1
- package/src/proxy/redis/RedisProxyServer.d.ts.map +1 -1
- package/src/proxy/redis/RedisProxyServer.js +77 -241
- package/src/tools/mail/drivers/Cloudflare.d.ts.map +1 -1
- package/src/tools/mail/drivers/Cloudflare.js +2 -0
- package/src/tools/redis/RedisTransport.d.ts +2 -27
- package/src/tools/redis/RedisTransport.d.ts.map +1 -1
- package/src/tools/redis/RedisTransport.js +123 -155
- package/src/zintrust.plugins.d.ts +6 -3
- package/src/zintrust.plugins.d.ts.map +1 -1
- package/src/zintrust.plugins.js +6 -3
- package/src/proxy/redis/RedisProxyActions.d.ts +0 -15
- package/src/proxy/redis/RedisProxyActions.d.ts.map +0 -1
- package/src/proxy/redis/RedisProxyActions.js +0 -124
|
@@ -2,7 +2,6 @@ import { Env } from '../../config/env.js';
|
|
|
2
2
|
import { Logger } from '../../config/logger.js';
|
|
3
3
|
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
4
4
|
import { parseCustomHeadersFromEnv } from '../../orm/adapters/SqlProxyAdapterUtils.js';
|
|
5
|
-
import { resolveProxyRpcService, } from '../../proxy/CloudflareProxyShared.js';
|
|
6
5
|
import { SignedRequest } from '../../security/SignedRequest.js';
|
|
7
6
|
const loggedSelections = new Set();
|
|
8
7
|
const readEnvString = (key, fallback = '') => {
|
|
@@ -19,6 +18,20 @@ const readEnvBool = (key, fallback = false) => {
|
|
|
19
18
|
const value = Env[key];
|
|
20
19
|
return typeof value === 'boolean' ? value : fallback;
|
|
21
20
|
};
|
|
21
|
+
const readEnvInt = (key, fallback) => {
|
|
22
|
+
if (typeof Env.getInt === 'function') {
|
|
23
|
+
return Env.getInt(key, fallback);
|
|
24
|
+
}
|
|
25
|
+
const value = Env[key];
|
|
26
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
30
|
+
const parsed = Number.parseInt(value, 10);
|
|
31
|
+
return Number.isNaN(parsed) ? fallback : parsed;
|
|
32
|
+
}
|
|
33
|
+
return fallback;
|
|
34
|
+
};
|
|
22
35
|
const resolveSigningPrefix = (baseUrl) => {
|
|
23
36
|
try {
|
|
24
37
|
const parsed = new URL(baseUrl);
|
|
@@ -56,20 +69,19 @@ const resolveProxyBaseUrl = () => {
|
|
|
56
69
|
return explicit;
|
|
57
70
|
return `http://${Env.REDIS_PROXY_HOST}:${Env.REDIS_PROXY_PORT}`;
|
|
58
71
|
};
|
|
59
|
-
const
|
|
60
|
-
const crypto = globalThis.crypto;
|
|
61
|
-
if (typeof crypto?.randomUUID === 'function')
|
|
62
|
-
return crypto.randomUUID();
|
|
63
|
-
return `req_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
64
|
-
};
|
|
65
|
-
const resolveProxySettings = (_options) => ({
|
|
72
|
+
const resolveProxySettings = () => ({
|
|
66
73
|
baseUrl: resolveProxyBaseUrl(),
|
|
67
|
-
keyId: Env.
|
|
68
|
-
secret: Env.REDIS_PROXY_SECRET.trim() === '' ? undefined : Env.
|
|
74
|
+
keyId: Env.REDIS_PROXY_KEY_ID.trim() === '' ? undefined : Env.REDIS_PROXY_KEY_ID,
|
|
75
|
+
secret: Env.REDIS_PROXY_SECRET.trim() === '' ? undefined : Env.REDIS_PROXY_SECRET,
|
|
69
76
|
timeoutMs: Env.REDIS_PROXY_TIMEOUT_MS,
|
|
70
|
-
service: resolveProxyRpcService('redis'),
|
|
71
77
|
customHeaders: parseCustomHeadersFromEnv('REDIS'),
|
|
72
78
|
});
|
|
79
|
+
const isRedisRpcEnabled = () => readEnvBool('USE_REDIS_PROXY', false) && readEnvString('REDIS_RPC_URL', '').trim() !== '';
|
|
80
|
+
const resolveRpcSettings = () => ({
|
|
81
|
+
baseUrl: readEnvString('REDIS_RPC_URL', '').trim(),
|
|
82
|
+
secret: readEnvString('REDIS_RPC_SECRET', readEnvString('REDIS_PROXY_SECRET', readEnvString('APP_KEY', ''))),
|
|
83
|
+
timeoutMs: readEnvInt('REDIS_RPC_TIMEOUT_MS', 30000),
|
|
84
|
+
});
|
|
73
85
|
const buildHeaders = async (settings, requestUrl, body) => {
|
|
74
86
|
const headers = {
|
|
75
87
|
'Content-Type': 'application/json',
|
|
@@ -90,17 +102,11 @@ const buildHeaders = async (settings, requestUrl, body) => {
|
|
|
90
102
|
}
|
|
91
103
|
return headers;
|
|
92
104
|
};
|
|
93
|
-
const requestProxyCommand = async (settings,
|
|
105
|
+
const requestProxyCommand = async (settings, command, args) => {
|
|
94
106
|
if (settings.baseUrl.trim() === '') {
|
|
95
107
|
throw ErrorFactory.createConfigError('Redis proxy URL is missing (REDIS_PROXY_URL)');
|
|
96
108
|
}
|
|
97
|
-
const
|
|
98
|
-
service: settings.service,
|
|
99
|
-
action,
|
|
100
|
-
requestId: createRequestId(),
|
|
101
|
-
payload,
|
|
102
|
-
};
|
|
103
|
-
const body = JSON.stringify(envelope);
|
|
109
|
+
const body = JSON.stringify({ command, args });
|
|
104
110
|
const requestUrl = buildRequestUrl(settings.baseUrl, '/zin/redis/command');
|
|
105
111
|
const headers = await buildHeaders(settings, requestUrl, body);
|
|
106
112
|
const signal = typeof AbortSignal !== 'undefined' && 'timeout' in AbortSignal
|
|
@@ -114,33 +120,56 @@ const requestProxyCommand = async (settings, action, payload) => {
|
|
|
114
120
|
});
|
|
115
121
|
if (!response.ok) {
|
|
116
122
|
const text = await response.text();
|
|
117
|
-
|
|
118
|
-
const isHtml = text.trim().toLowerCase().startsWith('<!doctype html') ||
|
|
119
|
-
text.trim().toLowerCase().startsWith('<html');
|
|
120
|
-
const errorMessage = isHtml ? 'Non-JSON response from proxy (proxy may be unavailable)' : text;
|
|
121
|
-
throw ErrorFactory.createTryCatchError(`Redis proxy request failed (${response.status})`, errorMessage);
|
|
123
|
+
throw ErrorFactory.createTryCatchError(`Redis proxy request failed (${response.status})`, text);
|
|
122
124
|
}
|
|
123
125
|
const parsed = (await response.json());
|
|
124
126
|
return parsed.result;
|
|
125
127
|
};
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
130
|
-
return loaded;
|
|
131
|
-
};
|
|
132
|
-
const getDefinedScriptSha = async (settings, registry, command) => {
|
|
133
|
-
const cached = registry.shaByCommand.get(command);
|
|
134
|
-
if (cached !== undefined) {
|
|
135
|
-
return cached;
|
|
128
|
+
const requestRpcMethod = async (settings, method, payload) => {
|
|
129
|
+
if (settings.baseUrl.trim() === '') {
|
|
130
|
+
throw ErrorFactory.createConfigError('Redis RPC URL is missing. Set USE_REDIS_PROXY=true and REDIS_RPC_URL.');
|
|
136
131
|
}
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
const url = new URL('/rpc', settings.baseUrl);
|
|
133
|
+
const body = JSON.stringify({
|
|
134
|
+
service: 'redis',
|
|
135
|
+
method,
|
|
136
|
+
payload,
|
|
137
|
+
});
|
|
138
|
+
const signal = typeof AbortSignal !== 'undefined' && 'timeout' in AbortSignal
|
|
139
|
+
? AbortSignal.timeout(settings.timeoutMs)
|
|
140
|
+
: undefined;
|
|
141
|
+
const response = await fetch(url.toString(), {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
'Content-Type': 'application/json',
|
|
145
|
+
...(settings.secret !== undefined && settings.secret.trim() !== ''
|
|
146
|
+
? { 'x-redis-rpc-secret': settings.secret }
|
|
147
|
+
: {}),
|
|
148
|
+
},
|
|
149
|
+
body,
|
|
150
|
+
signal,
|
|
151
|
+
});
|
|
152
|
+
const parsed = (await response.json());
|
|
153
|
+
if (!response.ok || parsed.ok !== true) {
|
|
154
|
+
const errorMessage = parsed.error?.message;
|
|
155
|
+
throw ErrorFactory.createTryCatchError(typeof errorMessage === 'string' && errorMessage.trim() !== ''
|
|
156
|
+
? errorMessage
|
|
157
|
+
: `Redis RPC request failed (${response.status})`, parsed.error?.details);
|
|
140
158
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
159
|
+
return parsed.result;
|
|
160
|
+
};
|
|
161
|
+
const requestRpcCommand = async (settings, command, args) => requestRpcMethod(settings, 'call', { args: [command, ...args] });
|
|
162
|
+
const requestRpcPipeline = async (settings, commands, transaction) => {
|
|
163
|
+
const results = await requestRpcMethod(settings, transaction ? 'multi' : 'pipeline', { commands, transaction });
|
|
164
|
+
return results.map(([error, result]) => [
|
|
165
|
+
error === null ? null : ErrorFactory.createTryCatchError(error),
|
|
166
|
+
result,
|
|
167
|
+
]);
|
|
168
|
+
};
|
|
169
|
+
const requestRedisCommand = async (mode, settings, command, args) => {
|
|
170
|
+
return mode === 'rpc'
|
|
171
|
+
? requestRpcCommand(settings, command, args)
|
|
172
|
+
: requestProxyCommand(settings, command, args);
|
|
144
173
|
};
|
|
145
174
|
const logTransportSelection = (mode, config, options) => {
|
|
146
175
|
const rawSubsystem = options?.subsystem?.trim();
|
|
@@ -157,6 +186,7 @@ const logTransportSelection = (mode, config, options) => {
|
|
|
157
186
|
host: mode === 'direct' ? config.host : undefined,
|
|
158
187
|
port: mode === 'direct' ? config.port : undefined,
|
|
159
188
|
proxyUrl: mode === 'proxy' ? resolveProxyBaseUrl() : undefined,
|
|
189
|
+
rpcUrl: mode === 'rpc' ? readEnvString('REDIS_RPC_URL', '') : undefined,
|
|
160
190
|
});
|
|
161
191
|
};
|
|
162
192
|
const normalizeScanResponse = (value) => {
|
|
@@ -169,7 +199,7 @@ const normalizeScanResponse = (value) => {
|
|
|
169
199
|
: [];
|
|
170
200
|
return [cursor, batch];
|
|
171
201
|
};
|
|
172
|
-
const createScanStream = (settings, options) => {
|
|
202
|
+
const createScanStream = (settings, mode, options) => {
|
|
173
203
|
const handlers = new Map();
|
|
174
204
|
const stream = {
|
|
175
205
|
on(event, handler) {
|
|
@@ -201,9 +231,13 @@ const createScanStream = (settings, options) => {
|
|
|
201
231
|
let cursor = '0';
|
|
202
232
|
do {
|
|
203
233
|
// eslint-disable-next-line no-await-in-loop
|
|
204
|
-
const result = await
|
|
205
|
-
|
|
206
|
-
|
|
234
|
+
const result = await requestRedisCommand(mode, settings, 'SCAN', [
|
|
235
|
+
cursor,
|
|
236
|
+
'MATCH',
|
|
237
|
+
options?.match ?? '*',
|
|
238
|
+
'COUNT',
|
|
239
|
+
String(options?.count ?? 200),
|
|
240
|
+
]);
|
|
207
241
|
const [nextCursor, batch] = normalizeScanResponse(result);
|
|
208
242
|
cursor = nextCursor;
|
|
209
243
|
if (batch.length > 0) {
|
|
@@ -219,19 +253,18 @@ const createScanStream = (settings, options) => {
|
|
|
219
253
|
});
|
|
220
254
|
return stream;
|
|
221
255
|
};
|
|
222
|
-
const createPipeline = (settings) => {
|
|
256
|
+
const createPipeline = (settings, mode, transaction = false) => {
|
|
223
257
|
const commands = [];
|
|
224
258
|
const target = {
|
|
225
259
|
async exec() {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
});
|
|
260
|
+
if (mode === 'rpc') {
|
|
261
|
+
return requestRpcPipeline(settings, commands, transaction);
|
|
262
|
+
}
|
|
230
263
|
const results = [];
|
|
231
264
|
for (const entry of commands) {
|
|
232
265
|
try {
|
|
233
266
|
// eslint-disable-next-line no-await-in-loop
|
|
234
|
-
const result = await
|
|
267
|
+
const result = await requestRedisCommand(mode, settings, entry.command, entry.args);
|
|
235
268
|
results.push([null, result]);
|
|
236
269
|
}
|
|
237
270
|
catch (error) {
|
|
@@ -245,14 +278,6 @@ const createPipeline = (settings) => {
|
|
|
245
278
|
}
|
|
246
279
|
return results;
|
|
247
280
|
},
|
|
248
|
-
runCommand(name, args) {
|
|
249
|
-
Logger.debug('[redis][proxy][pipeline] runCommand called in pipeline', {
|
|
250
|
-
commandName: name,
|
|
251
|
-
argsCount: args.length,
|
|
252
|
-
});
|
|
253
|
-
commands.push({ command: name, args });
|
|
254
|
-
return pipeline;
|
|
255
|
-
},
|
|
256
281
|
};
|
|
257
282
|
const pipeline = new Proxy(target, {
|
|
258
283
|
get(obj, prop) {
|
|
@@ -261,7 +286,7 @@ const createPipeline = (settings) => {
|
|
|
261
286
|
if (prop in obj)
|
|
262
287
|
return Reflect.get(obj, prop);
|
|
263
288
|
return (...args) => {
|
|
264
|
-
commands.push({ command: prop, args });
|
|
289
|
+
commands.push({ command: prop.toUpperCase(), args });
|
|
265
290
|
return pipeline;
|
|
266
291
|
};
|
|
267
292
|
},
|
|
@@ -269,120 +294,63 @@ const createPipeline = (settings) => {
|
|
|
269
294
|
return pipeline;
|
|
270
295
|
};
|
|
271
296
|
export const resolveRedisTransportMode = () => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
297
|
+
if (isRedisRpcEnabled())
|
|
298
|
+
return 'rpc';
|
|
299
|
+
return readEnvBool('USE_REDIS_PROXY', false) ? 'proxy' : 'direct';
|
|
275
300
|
};
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const sha = await getDefinedScriptSha(settings, registry, command);
|
|
282
|
-
if (sha === undefined) {
|
|
283
|
-
return requestProxyCommand(settings, command, { args });
|
|
284
|
-
}
|
|
285
|
-
const definition = registry.definitions.get(command);
|
|
286
|
-
const numberOfKeys = definition?.numberOfKeys ?? 0;
|
|
287
|
-
return requestProxyCommand(settings, 'EVALSHA', {
|
|
288
|
-
args: [sha, numberOfKeys, ...args],
|
|
289
|
-
});
|
|
290
|
-
};
|
|
291
|
-
};
|
|
292
|
-
const createScriptsHandler = (settings) => {
|
|
293
|
-
return new Proxy({}, {
|
|
294
|
-
get(_target, prop) {
|
|
295
|
-
if (typeof prop !== 'string')
|
|
296
|
-
return undefined;
|
|
297
|
-
return async (...args) => requestProxyCommand(settings, prop, { args });
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
};
|
|
301
|
-
const handlePropertyAccess = (obj, prop, client, settings, registry) => {
|
|
302
|
-
if (typeof prop !== 'string')
|
|
303
|
-
return Reflect.get(obj, prop);
|
|
304
|
-
if (prop === 'then')
|
|
305
|
-
return undefined;
|
|
306
|
-
if (prop === 'setMaxListeners') {
|
|
307
|
-
return function (_count) {
|
|
308
|
-
return client;
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
if (prop === 'getMaxListeners') {
|
|
312
|
-
return function () {
|
|
313
|
-
return Infinity;
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
if (registry.definitions.has(prop)) {
|
|
317
|
-
return createDefinedScriptFunction(settings, prop, registry);
|
|
318
|
-
}
|
|
319
|
-
if (prop in obj) {
|
|
320
|
-
return Reflect.get(obj, prop);
|
|
301
|
+
export const createRedisProxyConnection = (config, options) => {
|
|
302
|
+
const mode = resolveRedisTransportMode();
|
|
303
|
+
const settings = mode === 'rpc' ? resolveRpcSettings() : resolveProxySettings();
|
|
304
|
+
if (mode === 'direct') {
|
|
305
|
+
throw ErrorFactory.createConfigError('Redis proxy connection requested while direct mode is active.');
|
|
321
306
|
}
|
|
322
|
-
|
|
323
|
-
};
|
|
324
|
-
const createProxyTarget = (config, options, settings, client, registry) => {
|
|
307
|
+
logTransportSelection(mode, config, options);
|
|
325
308
|
const target = {
|
|
326
|
-
__bullmq_iredis: true,
|
|
327
|
-
isCluster: false,
|
|
328
|
-
options: Object.freeze({}),
|
|
329
309
|
status: 'ready',
|
|
330
310
|
connect: async () => { },
|
|
331
311
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
332
312
|
quit: async () => 'OK',
|
|
333
313
|
disconnect: () => undefined,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
commandName: name,
|
|
340
|
-
numberOfKeys: definition.numberOfKeys,
|
|
341
|
-
luaLength: definition.lua.length,
|
|
342
|
-
});
|
|
343
|
-
},
|
|
344
|
-
runCommand: async (name, args) => requestProxyCommand(settings, name, { args }),
|
|
345
|
-
on: (_event, _handler) => client ?? target,
|
|
346
|
-
once: (_event, _handler) => client ?? target,
|
|
347
|
-
off: (_event, _handler) => client ?? target,
|
|
348
|
-
removeListener: (_event, _handler) => client ?? target,
|
|
349
|
-
setMaxListeners: (_count) => client ?? target,
|
|
314
|
+
on: (_event, _handler) => client,
|
|
315
|
+
once: (_event, _handler) => client,
|
|
316
|
+
off: (_event, _handler) => client,
|
|
317
|
+
removeListener: (_event, _handler) => client,
|
|
318
|
+
setMaxListeners: (_count) => client,
|
|
350
319
|
getMaxListeners: () => Infinity,
|
|
351
|
-
call: async (command, ...args) =>
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return target;
|
|
358
|
-
};
|
|
359
|
-
export const createRedisProxyConnection = (config, options, registry) => {
|
|
360
|
-
const settings = resolveProxySettings(options);
|
|
361
|
-
const scriptRegistry = registry ?? {
|
|
362
|
-
definitions: new Map(),
|
|
363
|
-
shaByCommand: new Map(),
|
|
320
|
+
call: async (command, ...args) => {
|
|
321
|
+
return requestRedisCommand(mode, settings, command, args);
|
|
322
|
+
},
|
|
323
|
+
pipeline: () => createPipeline(settings, mode),
|
|
324
|
+
multi: () => createPipeline(settings, mode, true),
|
|
325
|
+
scanStream: (scanOptions) => createScanStream(settings, mode, scanOptions),
|
|
364
326
|
};
|
|
365
|
-
|
|
366
|
-
Logger.info('[redis][proxy] Creating opaque proxy connection', {
|
|
367
|
-
transport: 'BullMQ',
|
|
368
|
-
});
|
|
369
|
-
const proxyTarget = createProxyTarget(config, options, settings, null, scriptRegistry);
|
|
370
|
-
const client = new Proxy(proxyTarget, {
|
|
327
|
+
const client = new Proxy(target, {
|
|
371
328
|
get(obj, prop) {
|
|
372
|
-
|
|
329
|
+
if (typeof prop !== 'string')
|
|
330
|
+
return Reflect.get(obj, prop);
|
|
331
|
+
if (prop === 'then')
|
|
332
|
+
return undefined;
|
|
333
|
+
if (prop === 'setMaxListeners') {
|
|
334
|
+
return function (_count) {
|
|
335
|
+
return client;
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
if (prop === 'getMaxListeners') {
|
|
339
|
+
return function () {
|
|
340
|
+
return Infinity;
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (prop in obj)
|
|
344
|
+
return Reflect.get(obj, prop);
|
|
345
|
+
return async (...args) => requestRedisCommand(mode, settings, prop.toUpperCase(), args);
|
|
373
346
|
},
|
|
374
347
|
});
|
|
375
348
|
return client;
|
|
376
349
|
};
|
|
377
350
|
export const ensureRedisTransportMode = (config, options) => {
|
|
378
351
|
const mode = resolveRedisTransportMode();
|
|
379
|
-
const subsystem = options?.subsystem ?? 'redis';
|
|
380
|
-
const requireDirectForScripts = options?.requireDirectForScripts ?? Env.REDIS_REQUIRE_DIRECT_FOR_SCRIPTS;
|
|
381
352
|
if (mode === 'proxy' && options?.requireDirect === true) {
|
|
382
|
-
throw ErrorFactory.createConfigError(`Redis subsystem '${subsystem}' requires a direct Redis connection, but proxy mode is enabled.`);
|
|
383
|
-
}
|
|
384
|
-
if (mode === 'proxy' && requireDirectForScripts) {
|
|
385
|
-
throw ErrorFactory.createConfigError(`Redis subsystem '${subsystem}' requires a direct Redis connection for scripts, but proxy mode is enabled.`);
|
|
353
|
+
throw ErrorFactory.createConfigError(`Redis subsystem '${options.subsystem ?? 'redis'}' requires a direct Redis connection, but proxy mode is enabled.`);
|
|
386
354
|
}
|
|
387
355
|
if (mode === 'direct') {
|
|
388
356
|
logTransportSelection(mode, config, options);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* ZinTrust plugin auto-imports
|
|
3
|
+
*
|
|
4
|
+
* In real projects, this file is managed by `zin plugin install` and contains
|
|
5
|
+
* side-effect imports (e.g. `@zintrust/db-sqlite/register`) that register
|
|
6
|
+
* optional adapters/drivers into core registries.
|
|
7
|
+
*
|
|
5
8
|
*/
|
|
6
9
|
export type {};
|
|
7
10
|
export declare const __zintrustGeneratedPluginStub = "zintrust.plugins.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zintrust.plugins.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"zintrust.plugins.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,YAAY,EAAE,CAAC;AAgBf,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;;AACnE,wBAAkB"}
|
package/src/zintrust.plugins.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* ZinTrust plugin auto-imports
|
|
3
|
+
*
|
|
4
|
+
* In real projects, this file is managed by `zin plugin install` and contains
|
|
5
|
+
* side-effect imports (e.g. `@zintrust/db-sqlite/register`) that register
|
|
6
|
+
* optional adapters/drivers into core registries.
|
|
7
|
+
*
|
|
5
8
|
*/
|
|
6
9
|
import * as TraceRuntime from './runtime/plugins/trace-runtime.js';
|
|
7
10
|
globalThis.__zintrust_system_trace_plugin_requested__ = true;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { QueueDriver } from '@zintrust/queue-monitor/driver';
|
|
2
|
-
import type { Metrics } from '@zintrust/queue-monitor/metrics';
|
|
3
|
-
export type QueueMonitorContext = Readonly<{
|
|
4
|
-
driver: QueueDriver;
|
|
5
|
-
metrics: Metrics;
|
|
6
|
-
}>;
|
|
7
|
-
export type RedisProxyRedisConfig = Readonly<{
|
|
8
|
-
host: string;
|
|
9
|
-
port: number;
|
|
10
|
-
password: string;
|
|
11
|
-
db: number;
|
|
12
|
-
}>;
|
|
13
|
-
export declare const createQueueMonitorContext: (redis: RedisProxyRedisConfig) => Promise<QueueMonitorContext>;
|
|
14
|
-
export declare const dispatchServiceCommand: (service: string, action: string, payload: Record<string, unknown>, queueMonitor: QueueMonitorContext) => Promise<unknown>;
|
|
15
|
-
//# sourceMappingURL=RedisProxyActions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RedisProxyActions.d.ts","sourceRoot":"","sources":["../../../../src/proxy/redis/RedisProxyActions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAG/D,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC,CAAC;AA4BH,eAAO,MAAM,yBAAyB,GACpC,OAAO,qBAAqB,KAC3B,OAAO,CAAC,mBAAmB,CAU7B,CAAC;AAuIF,eAAO,MAAM,sBAAsB,GACjC,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,cAAc,mBAAmB,KAChC,OAAO,CAAC,OAAO,CAUjB,CAAC"}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
2
|
-
const loadQueueMonitorDriverModule = async () => {
|
|
3
|
-
return import('@zintrust/queue-monitor/driver');
|
|
4
|
-
};
|
|
5
|
-
const loadQueueMonitorMetricsModule = async () => {
|
|
6
|
-
return import('@zintrust/queue-monitor/metrics');
|
|
7
|
-
};
|
|
8
|
-
const loadQueueMonitoringServiceModule = async () => {
|
|
9
|
-
return import('../../../packages/queue-monitor/src/QueueMonitoringService.js');
|
|
10
|
-
};
|
|
11
|
-
const loadWorkersApiModule = async () => {
|
|
12
|
-
return import('../../../packages/workers/src/dashboard/workers-api.js');
|
|
13
|
-
};
|
|
14
|
-
const loadWorkerFactoryModule = async () => {
|
|
15
|
-
return import('../../../packages/workers/src/WorkerFactory.js');
|
|
16
|
-
};
|
|
17
|
-
export const createQueueMonitorContext = async (redis) => {
|
|
18
|
-
const [driverModule, metricsModule] = await Promise.all([
|
|
19
|
-
loadQueueMonitorDriverModule(),
|
|
20
|
-
loadQueueMonitorMetricsModule(),
|
|
21
|
-
]);
|
|
22
|
-
return {
|
|
23
|
-
driver: driverModule.createBullMQDriver(redis),
|
|
24
|
-
metrics: metricsModule.createMetrics(redis),
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
const readString = (value) => {
|
|
28
|
-
return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
29
|
-
};
|
|
30
|
-
const readBoolean = (value) => value === true;
|
|
31
|
-
const readNumber = (value) => {
|
|
32
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
33
|
-
};
|
|
34
|
-
const readQueueNames = (value) => {
|
|
35
|
-
if (!Array.isArray(value))
|
|
36
|
-
return undefined;
|
|
37
|
-
const names = value
|
|
38
|
-
.filter((entry) => typeof entry === 'string')
|
|
39
|
-
.map((entry) => entry.trim())
|
|
40
|
-
.filter((entry) => entry.length > 0);
|
|
41
|
-
return names.length > 0 ? names : undefined;
|
|
42
|
-
};
|
|
43
|
-
const resolveWorkerPersistenceOverride = (payload) => {
|
|
44
|
-
const driver = readString(payload['driver']);
|
|
45
|
-
if (driver === 'redis')
|
|
46
|
-
return { driver: 'redis' };
|
|
47
|
-
if (driver === 'database')
|
|
48
|
-
return { driver: 'database' };
|
|
49
|
-
if (driver === 'memory')
|
|
50
|
-
return { driver: 'memory' };
|
|
51
|
-
return undefined;
|
|
52
|
-
};
|
|
53
|
-
const workerActionHandlers = {
|
|
54
|
-
getWorkers: async (payload) => {
|
|
55
|
-
const module = await loadWorkersApiModule();
|
|
56
|
-
return module.getWorkers(payload);
|
|
57
|
-
},
|
|
58
|
-
getWorkerDetails: async (payload) => {
|
|
59
|
-
const module = await loadWorkersApiModule();
|
|
60
|
-
return module.getWorkerDetails(readString(payload['name'] ?? payload['workerName']) ?? '', readString(payload['driver']));
|
|
61
|
-
},
|
|
62
|
-
toggleAutoStart: async (payload) => {
|
|
63
|
-
const module = await loadWorkersApiModule();
|
|
64
|
-
return module.toggleAutoStart(readString(payload['name'] ?? payload['workerName']) ?? '', readBoolean(payload['enabled']));
|
|
65
|
-
},
|
|
66
|
-
listPersistedRecords: async (payload) => {
|
|
67
|
-
const module = await loadWorkerFactoryModule();
|
|
68
|
-
return module.WorkerFactory.listPersistedRecords(resolveWorkerPersistenceOverride(payload), {
|
|
69
|
-
offset: readNumber(payload['offset']),
|
|
70
|
-
limit: readNumber(payload['limit']),
|
|
71
|
-
search: readString(payload['search']),
|
|
72
|
-
includeInactive: readBoolean(payload['includeInactive']),
|
|
73
|
-
});
|
|
74
|
-
},
|
|
75
|
-
listFileBackedRecords: async () => {
|
|
76
|
-
const module = await loadWorkerFactoryModule();
|
|
77
|
-
return module.WorkerFactory.listFileBackedRecords();
|
|
78
|
-
},
|
|
79
|
-
getPersisted: async (payload) => {
|
|
80
|
-
const module = await loadWorkerFactoryModule();
|
|
81
|
-
return module.WorkerFactory.getPersisted(readString(payload['name'] ?? payload['workerName']) ?? '', resolveWorkerPersistenceOverride(payload));
|
|
82
|
-
},
|
|
83
|
-
getHealth: async (payload) => {
|
|
84
|
-
const module = await loadWorkerFactoryModule();
|
|
85
|
-
return module.WorkerFactory.getHealth(readString(payload['name'] ?? payload['workerName']) ?? '');
|
|
86
|
-
},
|
|
87
|
-
getMetrics: async (payload) => {
|
|
88
|
-
const module = await loadWorkerFactoryModule();
|
|
89
|
-
return module.WorkerFactory.getMetrics(readString(payload['name'] ?? payload['workerName']) ?? '');
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
const queueMonitorActionHandlers = {
|
|
93
|
-
getRecentJobsForQueue: async (payload, queueMonitor) => {
|
|
94
|
-
const module = await loadQueueMonitoringServiceModule();
|
|
95
|
-
return module.getRecentJobsForQueue(readString(payload['queue'] ?? payload['queueName']) ?? '', queueMonitor.metrics, queueMonitor.driver);
|
|
96
|
-
},
|
|
97
|
-
getRecentJobsForSelection: async (payload, queueMonitor) => {
|
|
98
|
-
const module = await loadQueueMonitoringServiceModule();
|
|
99
|
-
return module.getRecentJobsForSelection(readString(payload['queue'] ?? payload['queueName']) ?? '', queueMonitor.metrics, queueMonitor.driver, readQueueNames(payload['queueNames']));
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
const dispatchWorkerAction = async (action, payload) => {
|
|
103
|
-
const handler = workerActionHandlers[action];
|
|
104
|
-
if (handler === undefined) {
|
|
105
|
-
throw ErrorFactory.createValidationError(`Unsupported worker action: ${action}`);
|
|
106
|
-
}
|
|
107
|
-
return handler(payload);
|
|
108
|
-
};
|
|
109
|
-
const dispatchQueueMonitorAction = async (action, payload, queueMonitor) => {
|
|
110
|
-
const handler = queueMonitorActionHandlers[action];
|
|
111
|
-
if (handler === undefined) {
|
|
112
|
-
throw ErrorFactory.createValidationError(`Unsupported queue-monitor action: ${action}`);
|
|
113
|
-
}
|
|
114
|
-
return handler(payload, queueMonitor);
|
|
115
|
-
};
|
|
116
|
-
export const dispatchServiceCommand = async (service, action, payload, queueMonitor) => {
|
|
117
|
-
if (service === 'worker') {
|
|
118
|
-
return dispatchWorkerAction(action, payload);
|
|
119
|
-
}
|
|
120
|
-
if (service === 'queue-monitor') {
|
|
121
|
-
return dispatchQueueMonitorAction(action, payload, queueMonitor);
|
|
122
|
-
}
|
|
123
|
-
throw ErrorFactory.createValidationError(`Unsupported RPC service: ${service}`);
|
|
124
|
-
};
|