@zintrust/core 0.1.23 → 0.1.25
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/package.json +4 -3
- package/src/auth/Auth.d.ts.map +1 -0
- package/src/boot/Application.d.ts.map +1 -1
- package/src/boot/Application.js +8 -0
- package/src/boot/bootstrap.js +34 -15
- package/src/cache/drivers/RedisDriver.d.ts.map +1 -1
- package/src/cache/drivers/RedisDriver.js +10 -5
- package/src/cli/CLI.d.ts.map +1 -1
- package/src/cli/CLI.js +6 -0
- package/src/cli/commands/DbSeedCommand.d.ts.map +1 -1
- package/src/cli/commands/DbSeedCommand.js +6 -38
- package/src/cli/commands/MigrateCommand.d.ts.map +1 -1
- package/src/cli/commands/MigrateCommand.js +12 -55
- package/src/cli/commands/MigrateWorkerCommand.d.ts.map +1 -1
- package/src/cli/commands/MigrateWorkerCommand.js +8 -54
- package/src/cli/commands/NewCommand.d.ts.map +1 -1
- package/src/cli/commands/NewCommand.js +1 -13
- package/src/cli/commands/QueueCommand.d.ts.map +1 -1
- package/src/cli/commands/QueueCommand.js +89 -39
- package/src/cli/commands/QueueLockCommand.d.ts +7 -0
- package/src/cli/commands/QueueLockCommand.d.ts.map +1 -0
- package/src/cli/commands/QueueLockCommand.js +138 -0
- package/src/cli/commands/StartCommand.d.ts.map +1 -1
- package/src/cli/commands/StartCommand.js +16 -16
- package/src/cli/commands/TemplatesCommand.js +1 -1
- package/src/cli/commands/WorkerCommands.d.ts.map +1 -1
- package/src/cli/commands/WorkerCommands.js +46 -22
- package/src/cli/scaffolding/ProjectScaffolder.js +2 -2
- package/src/cli/scaffolding/RouteGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/RouteGenerator.js +27 -28
- package/src/cli/services/VersionChecker.d.ts +53 -0
- package/src/cli/services/VersionChecker.d.ts.map +1 -0
- package/src/cli/services/VersionChecker.js +176 -0
- package/src/cli/utils/DatabaseCliUtils.d.ts +20 -0
- package/src/cli/utils/DatabaseCliUtils.d.ts.map +1 -0
- package/src/cli/utils/DatabaseCliUtils.js +54 -0
- package/src/cli/workers/QueueWorkRunner.d.ts.map +1 -1
- package/src/cli/workers/QueueWorkRunner.js +128 -7
- package/src/common/ExternalServiceUtils.d.ts +2 -2
- package/src/config/app.d.ts +4 -0
- package/src/config/app.d.ts.map +1 -1
- package/src/config/app.js +9 -0
- package/src/config/constants.d.ts +140 -10
- package/src/config/constants.d.ts.map +1 -1
- package/src/config/constants.js +86 -5
- package/src/config/index.d.ts +1 -0
- package/src/config/index.d.ts.map +1 -1
- package/src/config/middleware.d.ts +6 -6
- package/src/config/middleware.d.ts.map +1 -1
- package/src/config/middleware.js +6 -7
- package/src/config/queue.d.ts +4 -0
- package/src/config/queue.d.ts.map +1 -1
- package/src/config/queue.js +1 -1
- package/src/config/redis.d.ts +17 -0
- package/src/config/redis.d.ts.map +1 -0
- package/src/config/redis.js +54 -0
- package/src/config/type.d.ts +3 -0
- package/src/config/type.d.ts.map +1 -1
- package/src/http/Request.d.ts +10 -1
- package/src/http/Request.d.ts.map +1 -1
- package/src/http/Request.js +79 -7
- package/src/http/error-pages/ErrorPageRenderer.d.ts.map +1 -1
- package/src/http/error-pages/ErrorPageRenderer.js +4 -3
- package/src/index.d.ts +14 -11
- package/src/index.d.ts.map +1 -1
- package/src/index.js +18 -11
- package/src/lang/lang.d.ts +23 -0
- package/src/lang/lang.d.ts.map +1 -0
- package/src/lang/lang.js +22 -0
- package/src/middleware/ErrorHandlerMiddleware.d.ts.map +1 -1
- package/src/middleware/ErrorHandlerMiddleware.js +9 -1
- package/src/migrations/schema/SchemaCompiler.js +1 -1
- package/src/migrations/schema/types.d.ts +1 -1
- package/src/migrations/schema/types.d.ts.map +1 -1
- package/src/node.d.ts +1 -1
- package/src/node.d.ts.map +1 -1
- package/src/node.js +1 -1
- package/src/orm/Database.d.ts +1 -1
- package/src/orm/Database.d.ts.map +1 -1
- package/src/orm/Database.js +22 -3
- package/src/performance/Optimizer.js +1 -1
- package/src/routing/Router.d.ts +6 -2
- package/src/routing/Router.d.ts.map +1 -1
- package/src/routing/Router.js +19 -4
- package/src/runtime/PluginAutoImports.d.ts.map +1 -1
- package/src/runtime/PluginAutoImports.js +1 -13
- package/src/runtime/PluginManager.d.ts.map +1 -1
- package/src/runtime/PluginManager.js +2 -14
- package/src/runtime/PluginRegistry.js +2 -2
- package/src/start.d.ts.map +1 -1
- package/src/start.js +8 -7
- package/src/templates/TemplateRegistry.js +2 -2
- package/src/templates/TemplateRegistry.ts +2 -2
- package/src/templates/feature/Queue.ts.tpl +114 -0
- package/src/templates/project/basic/app/Controllers/UserController.ts.tpl +22 -0
- package/src/templates/project/basic/config/queue.ts.tpl +19 -0
- package/src/templates/project/basic/package.json.tpl +2 -1
- package/src/templates/project/basic/src/index.ts.tpl +0 -3
- package/src/toolkit/Secrets/providers/AwsSecretsManager.d.ts.map +1 -1
- package/src/toolkit/Secrets/providers/AwsSecretsManager.js +1 -13
- package/src/toolkit/Secrets/providers/CloudflareKv.d.ts.map +1 -1
- package/src/toolkit/Secrets/providers/CloudflareKv.js +4 -16
- package/src/tools/broadcast/drivers/Redis.d.ts.map +1 -1
- package/src/tools/broadcast/drivers/Redis.js +8 -56
- package/src/tools/mail/Mail.d.ts +1 -29
- package/src/tools/mail/Mail.d.ts.map +1 -1
- package/src/tools/mail/Mail.js +1 -111
- package/src/tools/mail/drivers/SendGrid.d.ts.map +1 -1
- package/src/tools/mail/drivers/SendGrid.js +4 -3
- package/src/tools/mail/drivers/Smtp.d.ts.map +1 -1
- package/src/tools/mail/drivers/Smtp.js +32 -10
- package/src/tools/mail/index.d.ts +40 -0
- package/src/tools/mail/index.d.ts.map +1 -0
- package/src/tools/mail/index.js +129 -0
- package/src/tools/mail/template-loader.d.ts +10 -0
- package/src/tools/mail/template-loader.d.ts.map +1 -0
- package/src/tools/mail/template-loader.js +101 -0
- package/src/tools/mail/template-utils.d.ts +10 -0
- package/src/tools/mail/template-utils.d.ts.map +1 -0
- package/src/tools/mail/template-utils.js +16 -0
- package/src/tools/mail/templates/index.d.ts +30 -0
- package/src/tools/mail/templates/index.d.ts.map +1 -1
- package/src/tools/mail/templates/index.js +69 -0
- package/src/tools/queue/AdvancedQueue.d.ts +19 -0
- package/src/tools/queue/AdvancedQueue.d.ts.map +1 -0
- package/src/tools/queue/AdvancedQueue.js +352 -0
- package/src/tools/queue/DeduplicationBuilder.d.ts +20 -0
- package/src/tools/queue/DeduplicationBuilder.d.ts.map +1 -0
- package/src/tools/queue/DeduplicationBuilder.js +77 -0
- package/src/tools/queue/LockProvider.d.ts +25 -0
- package/src/tools/queue/LockProvider.d.ts.map +1 -0
- package/src/tools/queue/LockProvider.js +276 -0
- package/src/tools/queue/Queue.d.ts.map +1 -1
- package/src/tools/queue/Queue.js +2 -1
- package/src/tools/queue/QueueExtensions.d.ts +46 -0
- package/src/tools/queue/QueueExtensions.d.ts.map +1 -0
- package/src/tools/queue/QueueExtensions.js +129 -0
- package/src/tools/queue/QueueRuntimeRegistration.d.ts.map +1 -1
- package/src/tools/queue/QueueRuntimeRegistration.js +2 -2
- package/src/tools/queue/drivers/Database.d.ts +23 -0
- package/src/tools/queue/drivers/Database.d.ts.map +1 -0
- package/src/tools/queue/drivers/Database.js +123 -0
- package/src/tools/queue/drivers/Redis.d.ts.map +1 -1
- package/src/tools/queue/drivers/Redis.js +11 -82
- package/src/tools/queue/index.d.ts +9 -0
- package/src/tools/queue/index.d.ts.map +1 -0
- package/src/tools/queue/index.js +7 -0
- package/src/tools/redis/RedisKeyManager.d.ts +64 -0
- package/src/tools/redis/RedisKeyManager.d.ts.map +1 -0
- package/src/tools/redis/RedisKeyManager.js +124 -0
- package/src/tools/storage/drivers/S3.d.ts.map +1 -1
- package/src/tools/storage/drivers/S3.js +4 -16
- package/src/types/Queue.d.ts +62 -0
- package/src/types/Queue.d.ts.map +1 -0
- package/src/types/Queue.js +5 -0
- package/src/features/Auth.d.ts.map +0 -1
- package/src/features/Queue.d.ts +0 -21
- package/src/features/Queue.d.ts.map +0 -1
- package/src/features/Queue.js +0 -33
- package/src/templates/features/Queue.ts.tpl +0 -47
- package/src/tools/mail/templates/markdown/index.d.ts +0 -17
- package/src/tools/mail/templates/markdown/index.d.ts.map +0 -1
- package/src/tools/mail/templates/markdown/index.js +0 -49
- package/src/tools/mail/templates/markdown/registry.d.ts +0 -15
- package/src/tools/mail/templates/markdown/registry.d.ts.map +0 -1
- package/src/tools/mail/templates/markdown/registry.js +0 -34
- package/src/tools/mail/templates/markdown/validator.d.ts +0 -16
- package/src/tools/mail/templates/markdown/validator.d.ts.map +0 -1
- package/src/tools/mail/templates/markdown/validator.js +0 -24
- /package/src/{features → auth}/Auth.d.ts +0 -0
- /package/src/{features → auth}/Auth.js +0 -0
- /package/src/templates/{features → auth}/Auth.ts.tpl +0 -0
|
@@ -3,8 +3,93 @@
|
|
|
3
3
|
* Run queued jobs via the framework CLI.
|
|
4
4
|
*/
|
|
5
5
|
import { BaseCommand } from '../BaseCommand.js';
|
|
6
|
+
import { setupQueueLockCommands } from '../commands/QueueLockCommand.js';
|
|
6
7
|
import { QueueWorkCommandUtils } from '../commands/QueueWorkCommandUtils.js';
|
|
7
8
|
import { QueueWorkRunner } from '../workers/QueueWorkRunner.js';
|
|
9
|
+
/**
|
|
10
|
+
* Setup work subcommand with explicit kind
|
|
11
|
+
*/
|
|
12
|
+
const setupWorkCommand = (command) => {
|
|
13
|
+
command
|
|
14
|
+
.command('work <kind> <queueName>')
|
|
15
|
+
.alias('w')
|
|
16
|
+
.description('Work a queue with explicit kind (broadcast|notification)')
|
|
17
|
+
.option('--timeout <seconds>', 'Stop after this many seconds (default: 10)')
|
|
18
|
+
.option('--retry <count>', 'Retries after first attempt (default: 3)')
|
|
19
|
+
.option('--max-items <count>', 'Max items to process in one run (default: 1000)')
|
|
20
|
+
.option('--driver <name>', 'Queue driver name (default: from QUEUE_DRIVER)')
|
|
21
|
+
.action(async (kindRaw, queueName, subOptions) => {
|
|
22
|
+
const kind = QueueWorkRunner.parseKind(kindRaw);
|
|
23
|
+
const timeoutSeconds = QueueWorkCommandUtils.parsePositiveInt(subOptions['timeout'], '--timeout');
|
|
24
|
+
const retry = QueueWorkCommandUtils.parseNonNegativeInt(subOptions['retry'], '--retry');
|
|
25
|
+
const maxItems = QueueWorkCommandUtils.parsePositiveInt(subOptions['maxItems'], '--max-items');
|
|
26
|
+
const driverName = QueueWorkCommandUtils.normalizeDriverName(subOptions['driver']);
|
|
27
|
+
const result = await QueueWorkRunner.run({
|
|
28
|
+
kind,
|
|
29
|
+
queueName,
|
|
30
|
+
timeoutSeconds,
|
|
31
|
+
retry,
|
|
32
|
+
maxItems,
|
|
33
|
+
driverName,
|
|
34
|
+
});
|
|
35
|
+
QueueWorkCommandUtils.logSummary(queueName, kind, result);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Setup prune subcommand
|
|
40
|
+
*/
|
|
41
|
+
const setupPruneCommand = (command) => {
|
|
42
|
+
command
|
|
43
|
+
.command('prune')
|
|
44
|
+
.description('Prune failed jobs from the database queue')
|
|
45
|
+
.option('--hours <count>', 'Prune jobs older than this many hours (default: 168)', '168')
|
|
46
|
+
.action(async (options) => {
|
|
47
|
+
const { Logger } = await import('../../config/logger.js');
|
|
48
|
+
const { QueryBuilder } = await import('../../orm/QueryBuilder.js');
|
|
49
|
+
const { useEnsureDbConnected } = await import('../../orm/Database.js');
|
|
50
|
+
const { databaseConfig } = await import('../../config/database.js');
|
|
51
|
+
const hours = Number.parseInt(options.hours ?? '168', 10);
|
|
52
|
+
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
|
|
53
|
+
Logger.info(`[Queue] Pruning failed jobs older than ${cutoff.toISOString()}...`);
|
|
54
|
+
try {
|
|
55
|
+
// Resolved connection config (Queue prune usually runs on default DB)
|
|
56
|
+
const config = databaseConfig.getConnection();
|
|
57
|
+
const db = await useEnsureDbConnected(config);
|
|
58
|
+
const deleted = await QueryBuilder.create('queue_jobs_failed', db)
|
|
59
|
+
.where('failed_at', '<', cutoff)
|
|
60
|
+
.delete();
|
|
61
|
+
Logger.info(`[Queue] Pruned ${deleted} failed jobs.`);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
65
|
+
if (msg.includes('no such table')) {
|
|
66
|
+
Logger.warn('[Queue] Table queue_jobs_failed not found. Skipping prune.');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
Logger.error('[Queue] Prune failed', err);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Parse command options and run queue work
|
|
77
|
+
*/
|
|
78
|
+
const executeQueueWork = async (options) => {
|
|
79
|
+
const queueName = QueueWorkCommandUtils.requireQueueNameFromArgs(options.args, 'zin queue --help');
|
|
80
|
+
const timeoutSeconds = QueueWorkCommandUtils.parsePositiveInt(options.timeout, '--timeout');
|
|
81
|
+
const retry = QueueWorkCommandUtils.parseNonNegativeInt(options.retry, '--retry');
|
|
82
|
+
const maxItems = QueueWorkCommandUtils.parsePositiveInt(options.maxItems, '--max-items');
|
|
83
|
+
const driverName = QueueWorkCommandUtils.normalizeDriverName(options.driver);
|
|
84
|
+
const result = await QueueWorkRunner.run({
|
|
85
|
+
queueName,
|
|
86
|
+
timeoutSeconds,
|
|
87
|
+
retry,
|
|
88
|
+
maxItems,
|
|
89
|
+
driverName,
|
|
90
|
+
});
|
|
91
|
+
QueueWorkCommandUtils.logSummary(queueName, 'auto', result);
|
|
92
|
+
};
|
|
8
93
|
export const QueueCommand = Object.freeze({
|
|
9
94
|
create() {
|
|
10
95
|
return BaseCommand.create({
|
|
@@ -17,46 +102,11 @@ export const QueueCommand = Object.freeze({
|
|
|
17
102
|
.option('--retry <count>', 'Retries after first attempt (default: 3)')
|
|
18
103
|
.option('--max-items <count>', 'Max items to process in one run (default: 1000)')
|
|
19
104
|
.option('--driver <name>', 'Queue driver name (default: from QUEUE_DRIVER)');
|
|
20
|
-
command
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.description('Work a queue with explicit kind (broadcast|notification)')
|
|
24
|
-
.option('--timeout <seconds>', 'Stop after this many seconds (default: 10)')
|
|
25
|
-
.option('--retry <count>', 'Retries after first attempt (default: 3)')
|
|
26
|
-
.option('--max-items <count>', 'Max items to process in one run (default: 1000)')
|
|
27
|
-
.option('--driver <name>', 'Queue driver name (default: from QUEUE_DRIVER)')
|
|
28
|
-
.action(async (kindRaw, queueName, subOptions) => {
|
|
29
|
-
const kind = QueueWorkRunner.parseKind(kindRaw);
|
|
30
|
-
const timeoutSeconds = QueueWorkCommandUtils.parsePositiveInt(subOptions['timeout'], '--timeout');
|
|
31
|
-
const retry = QueueWorkCommandUtils.parseNonNegativeInt(subOptions['retry'], '--retry');
|
|
32
|
-
const maxItems = QueueWorkCommandUtils.parsePositiveInt(subOptions['maxItems'], '--max-items');
|
|
33
|
-
const driverName = QueueWorkCommandUtils.normalizeDriverName(subOptions['driver']);
|
|
34
|
-
const result = await QueueWorkRunner.run({
|
|
35
|
-
kind,
|
|
36
|
-
queueName,
|
|
37
|
-
timeoutSeconds,
|
|
38
|
-
retry,
|
|
39
|
-
maxItems,
|
|
40
|
-
driverName,
|
|
41
|
-
});
|
|
42
|
-
QueueWorkCommandUtils.logSummary(queueName, kind, result);
|
|
43
|
-
});
|
|
44
|
-
},
|
|
45
|
-
execute: async (options) => {
|
|
46
|
-
const queueName = QueueWorkCommandUtils.requireQueueNameFromArgs(options.args, 'zin queue --help');
|
|
47
|
-
const timeoutSeconds = QueueWorkCommandUtils.parsePositiveInt(options.timeout, '--timeout');
|
|
48
|
-
const retry = QueueWorkCommandUtils.parseNonNegativeInt(options.retry, '--retry');
|
|
49
|
-
const maxItems = QueueWorkCommandUtils.parsePositiveInt(options.maxItems, '--max-items');
|
|
50
|
-
const driverName = QueueWorkCommandUtils.normalizeDriverName(options.driver);
|
|
51
|
-
const result = await QueueWorkRunner.run({
|
|
52
|
-
queueName,
|
|
53
|
-
timeoutSeconds,
|
|
54
|
-
retry,
|
|
55
|
-
maxItems,
|
|
56
|
-
driverName,
|
|
57
|
-
});
|
|
58
|
-
QueueWorkCommandUtils.logSummary(queueName, 'auto', result);
|
|
105
|
+
setupWorkCommand(command);
|
|
106
|
+
setupPruneCommand(command);
|
|
107
|
+
setupQueueLockCommands(command);
|
|
59
108
|
},
|
|
109
|
+
execute: executeQueueWork,
|
|
60
110
|
});
|
|
61
111
|
},
|
|
62
112
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue Lock Command Extensions
|
|
3
|
+
* Implements CLI commands for managing queue locks and deduplication
|
|
4
|
+
*/
|
|
5
|
+
import type { Command } from 'commander';
|
|
6
|
+
export declare function setupQueueLockCommands(command: Command): void;
|
|
7
|
+
//# sourceMappingURL=QueueLockCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueueLockCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/QueueLockCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2IzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK7D"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue Lock Command Extensions
|
|
3
|
+
* Implements CLI commands for managing queue locks and deduplication
|
|
4
|
+
*/
|
|
5
|
+
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
6
|
+
import { ZintrustLang } from '../../lang/lang.js';
|
|
7
|
+
import { Logger } from '../../config/logger.js';
|
|
8
|
+
import { createAdvancedQueue } from '../../tools/queue/AdvancedQueue.js';
|
|
9
|
+
import { getLockProvider } from '../../tools/queue/LockProvider.js';
|
|
10
|
+
// Reusing initialization logic from AdvancedQueue
|
|
11
|
+
// But we need direct access to lock provider for maintenance
|
|
12
|
+
// AdvancedQueue only exposes limited interface
|
|
13
|
+
function getProvider(providerName = 'redis') {
|
|
14
|
+
// Create proper QueueConfig object that matches the expected interface
|
|
15
|
+
const advancedQueueConfig = {
|
|
16
|
+
name: ZintrustLang.CLI_LOCKS,
|
|
17
|
+
connection: undefined, // No specific connection needed for CLI operations
|
|
18
|
+
defaultDedupTtl: ZintrustLang.ZINTRUST_LOCKS_TTL,
|
|
19
|
+
lockProvider: providerName,
|
|
20
|
+
};
|
|
21
|
+
// Need to ensure provider is registered. creating AdvancedQueue triggers registration.
|
|
22
|
+
createAdvancedQueue(advancedQueueConfig);
|
|
23
|
+
const provider = getLockProvider(providerName);
|
|
24
|
+
if (!provider) {
|
|
25
|
+
throw ErrorFactory.createCliError(`Lock provider '${providerName}' not found.`);
|
|
26
|
+
}
|
|
27
|
+
return provider;
|
|
28
|
+
}
|
|
29
|
+
const setupLockListCommand = (command) => {
|
|
30
|
+
command
|
|
31
|
+
.command('lock:list')
|
|
32
|
+
.description('List active deduplication locks')
|
|
33
|
+
.option('--pattern <pattern>', 'Key pattern to match (default: *)', '*')
|
|
34
|
+
.option('--provider <name>', 'Lock provider name (default: redis)', 'redis')
|
|
35
|
+
.action(async (options) => {
|
|
36
|
+
try {
|
|
37
|
+
const provider = getProvider(options.provider);
|
|
38
|
+
const locks = await provider.list(options?.pattern ?? '*');
|
|
39
|
+
if (locks.length === 0) {
|
|
40
|
+
Logger.info('No locks found.');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
Logger.info(`Found ${locks.length} locks:`);
|
|
44
|
+
// Batch all status checks for better performance
|
|
45
|
+
const statusPromises = locks.map(async (key) => {
|
|
46
|
+
const status = await provider.status(key);
|
|
47
|
+
const expires = status.expires ? status.expires.toISOString() : 'never';
|
|
48
|
+
const ttl = typeof status.ttl === 'number' ? `${Math.round(status.ttl / 1000)}s` : 'unknown';
|
|
49
|
+
return { key, ttl, expires };
|
|
50
|
+
});
|
|
51
|
+
const lockStatuses = await Promise.all(statusPromises);
|
|
52
|
+
for (const { key, ttl, expires } of lockStatuses) {
|
|
53
|
+
Logger.info(`- [${key}] (TTL: ${ttl}, Expires: ${expires})`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
Logger.error('Failed to list locks', error);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const setupLockReleaseCommand = (command) => {
|
|
62
|
+
command
|
|
63
|
+
.command('lock:release <key>')
|
|
64
|
+
.description('Manually release a deduplication lock')
|
|
65
|
+
.option('--provider <name>', 'Lock provider name (default: redis)', 'redis')
|
|
66
|
+
.action(async (key, options) => {
|
|
67
|
+
try {
|
|
68
|
+
const provider = getProvider(options.provider);
|
|
69
|
+
const status = await provider.status(key);
|
|
70
|
+
if (!status.exists) {
|
|
71
|
+
Logger.info(`Lock '${key}' does not exist.`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
await provider.release({ key, ttl: 0, acquired: true, expires: new Date() });
|
|
75
|
+
Logger.info(`Lock '${key}' released successfully.`);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
Logger.error(`Failed to release lock ${key}`, error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
const setupLockExtendCommand = (command) => {
|
|
83
|
+
command
|
|
84
|
+
.command('lock:extend <key> <seconds>')
|
|
85
|
+
.description('Extend TTL of an existing lock')
|
|
86
|
+
.option('--provider <name>', 'Lock provider name (default: redis)', 'redis')
|
|
87
|
+
.action(async (key, seconds, options) => {
|
|
88
|
+
try {
|
|
89
|
+
const ttl = Number.parseInt(seconds, 10) * 1000;
|
|
90
|
+
const provider = getProvider(options.provider);
|
|
91
|
+
// Mock lock object for extension
|
|
92
|
+
const lock = { key, ttl: 0, acquired: true, expires: new Date() };
|
|
93
|
+
const extended = await provider.extend(lock, ttl);
|
|
94
|
+
if (extended) {
|
|
95
|
+
Logger.info(`Lock '${key}' extended by ${seconds} seconds.`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
Logger.info(`Failed to extend lock '${key}' (may not exist).`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
Logger.error(`Failed to extend lock ${key}`, error);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const setupDedupeStatusCommand = (command) => {
|
|
107
|
+
command
|
|
108
|
+
.command('dedupe:status <id>')
|
|
109
|
+
.description('Check deduplication status of a job ID')
|
|
110
|
+
.option('--provider <name>', 'Lock provider name (default: redis)', 'redis')
|
|
111
|
+
.action(async (id, options) => {
|
|
112
|
+
try {
|
|
113
|
+
const provider = getProvider(options.provider);
|
|
114
|
+
const status = await provider.status(id);
|
|
115
|
+
if (status.exists) {
|
|
116
|
+
Logger.info(`Job ID '${id}' is currently LOCKED (Deduplicated).`);
|
|
117
|
+
const ttlMs = status.ttl;
|
|
118
|
+
const ttlSeconds = ttlMs !== null && ttlMs !== undefined && !Number.isNaN(ttlMs) && ttlMs > 0
|
|
119
|
+
? Math.round(ttlMs / 1000) + 's'
|
|
120
|
+
: 'unknown';
|
|
121
|
+
Logger.info(`TTL Remaining: ${ttlSeconds}`);
|
|
122
|
+
Logger.info(`Expires At: ${status.expires ? status.expires.toISOString() : 'unknown'}`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
Logger.info(`Job ID '${id}' is NOT locked (Ready for processing or expired).`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
Logger.error(`Failed to check status for ${id}`, error);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
export function setupQueueLockCommands(command) {
|
|
134
|
+
setupLockListCommand(command);
|
|
135
|
+
setupLockReleaseCommand(command);
|
|
136
|
+
setupLockExtendCommand(command);
|
|
137
|
+
setupDedupeStatusCommand(command);
|
|
138
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAqavF,eAAO,MAAM,YAAY;cACb,YAAY;EAyBtB,CAAC"}
|
|
@@ -2,8 +2,8 @@ import { BaseCommand } from '../BaseCommand.js';
|
|
|
2
2
|
import { DENO_RUNNER_SOURCE, LAMBDA_RUNNER_SOURCE } from '../commands/runner/index.js';
|
|
3
3
|
import { EnvFileLoader } from '../utils/EnvFileLoader.js';
|
|
4
4
|
import { SpawnUtil } from '../utils/spawn.js';
|
|
5
|
+
import { readEnvString } from '../../common/ExternalServiceUtils.js';
|
|
5
6
|
import { resolveNpmPath } from '../../common/index.js';
|
|
6
|
-
import { Env } from '../../config/env.js';
|
|
7
7
|
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
|
|
9
9
|
import * as path from '../../node-singletons/path.js';
|
|
@@ -20,18 +20,6 @@ const normalizeMode = (value) => {
|
|
|
20
20
|
return 'testing';
|
|
21
21
|
return 'development';
|
|
22
22
|
};
|
|
23
|
-
const readEnvString = (key) => {
|
|
24
|
-
const anyEnv = Env;
|
|
25
|
-
const fromEnv = typeof anyEnv.get === 'function' ? anyEnv.get(key, '') : '';
|
|
26
|
-
if (typeof fromEnv === 'string' && fromEnv.trim() !== '')
|
|
27
|
-
return fromEnv;
|
|
28
|
-
if (typeof process !== 'undefined') {
|
|
29
|
-
const raw = process.env?.[key];
|
|
30
|
-
if (typeof raw === 'string')
|
|
31
|
-
return raw;
|
|
32
|
-
}
|
|
33
|
-
return fromEnv ?? '';
|
|
34
|
-
};
|
|
35
23
|
const resolveModeFromAppMode = () => {
|
|
36
24
|
const raw = readEnvString('APP_MODE').trim();
|
|
37
25
|
const normalized = raw.toLowerCase();
|
|
@@ -258,18 +246,30 @@ const executeNodeStart = async (cmd, cwd, mode, watchEnabled) => {
|
|
|
258
246
|
cmd.warn('Watch mode disabled; starting once.');
|
|
259
247
|
const bootstrap = resolveBootstrapEntryTs(cwd);
|
|
260
248
|
const args = bootstrap === undefined ? ['src/index.ts'] : [bootstrap];
|
|
261
|
-
const exitCode = await SpawnUtil.spawnAndWait({
|
|
249
|
+
const exitCode = await SpawnUtil.spawnAndWait({
|
|
250
|
+
command: 'tsx',
|
|
251
|
+
args,
|
|
252
|
+
forwardSignals: false,
|
|
253
|
+
});
|
|
262
254
|
process.exit(exitCode);
|
|
263
255
|
}
|
|
264
256
|
const packageJson = readPackageJson(cwd);
|
|
265
257
|
const dev = resolveNodeDevCommand(cwd, packageJson);
|
|
266
258
|
cmd.info('Starting in development mode (watch enabled)...');
|
|
267
|
-
const exitCode = await SpawnUtil.spawnAndWait({
|
|
259
|
+
const exitCode = await SpawnUtil.spawnAndWait({
|
|
260
|
+
command: dev.command,
|
|
261
|
+
args: dev.args,
|
|
262
|
+
forwardSignals: false,
|
|
263
|
+
});
|
|
268
264
|
process.exit(exitCode);
|
|
269
265
|
}
|
|
270
266
|
const prod = resolveNodeProdCommand(cwd);
|
|
271
267
|
cmd.info('Starting in production mode...');
|
|
272
|
-
const exitCode = await SpawnUtil.spawnAndWait({
|
|
268
|
+
const exitCode = await SpawnUtil.spawnAndWait({
|
|
269
|
+
command: prod.command,
|
|
270
|
+
args: prod.args,
|
|
271
|
+
forwardSignals: false,
|
|
272
|
+
});
|
|
273
273
|
process.exit(exitCode);
|
|
274
274
|
};
|
|
275
275
|
const executeStart = async (options, cmd) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseCommand } from '../BaseCommand.js';
|
|
2
2
|
import { ErrorHandler } from '../ErrorHandler.js';
|
|
3
3
|
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
4
|
-
import { listTemplates as listMail, renderTemplate as renderMail } from '../../tools/mail/templates/
|
|
4
|
+
import { listTemplates as listMail, renderTemplate as renderMail } from '../../tools/mail/templates/index.js';
|
|
5
5
|
import { listTemplates as listNotification, renderTemplate as renderNotification, } from '../../tools/notification/templates/markdown/index.js';
|
|
6
6
|
const listForScope = (scope) => {
|
|
7
7
|
let items = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerCommands.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/WorkerCommands.ts"],"names":[],"mappings":"AACA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"WorkerCommands.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/WorkerCommands.ts"],"names":[],"mappings":"AACA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAmWrD;;GAEG;AACH,eAAO,MAAM,cAAc;mCAjPS,YAAY;qCA4CV,YAAY;oCAiDb,YAAY;mCA+Bb,YAAY;sCAgCT,YAAY;sCAgCZ,YAAY;CA4DlD,CAAC"}
|
|
@@ -3,28 +3,51 @@
|
|
|
3
3
|
* Worker Management CLI Commands
|
|
4
4
|
* Command-line interface for managing workers
|
|
5
5
|
*/
|
|
6
|
+
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
6
7
|
import { BaseCommand } from '../BaseCommand.js';
|
|
7
8
|
import { Logger } from '../../config/logger.js';
|
|
8
|
-
import { HealthMonitor as HealthMonitorAny, ResourceMonitor as ResourceMonitorAny, WorkerFactory as WorkerFactoryAny, WorkerRegistry as WorkerRegistryAny, } from '../../../packages/workers/src/index.js';
|
|
9
9
|
// Lazy initialization to prevent temporal dead zone issues
|
|
10
10
|
let WorkerFactory;
|
|
11
11
|
let WorkerRegistry;
|
|
12
12
|
let HealthMonitor;
|
|
13
13
|
let ResourceMonitor;
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
let workersModulePromise;
|
|
15
|
+
const loadWorkersModule = async () => {
|
|
16
|
+
workersModulePromise ??= import('../../../packages/workers/src/index.js');
|
|
17
|
+
try {
|
|
18
|
+
return await workersModulePromise;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
Logger.error('Failed to load optional package "@zintrust/workers"; worker commands require this package.', error);
|
|
22
|
+
throw ErrorFactory.createCliError('Optional package "@zintrust/workers" is required for worker commands. Install it to use worker:* commands.');
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const getWorkerFactory = async () => {
|
|
26
|
+
if (!WorkerFactory) {
|
|
27
|
+
const mod = await loadWorkersModule();
|
|
28
|
+
WorkerFactory = mod.WorkerFactory;
|
|
29
|
+
}
|
|
16
30
|
return WorkerFactory;
|
|
17
31
|
};
|
|
18
|
-
const getWorkerRegistry = () => {
|
|
19
|
-
WorkerRegistry
|
|
32
|
+
const getWorkerRegistry = async () => {
|
|
33
|
+
if (!WorkerRegistry) {
|
|
34
|
+
const mod = await loadWorkersModule();
|
|
35
|
+
WorkerRegistry = mod.WorkerRegistry;
|
|
36
|
+
}
|
|
20
37
|
return WorkerRegistry;
|
|
21
38
|
};
|
|
22
|
-
const getHealthMonitor = () => {
|
|
23
|
-
HealthMonitor
|
|
39
|
+
const getHealthMonitor = async () => {
|
|
40
|
+
if (!HealthMonitor) {
|
|
41
|
+
const mod = await loadWorkersModule();
|
|
42
|
+
HealthMonitor = mod.HealthMonitor;
|
|
43
|
+
}
|
|
24
44
|
return HealthMonitor;
|
|
25
45
|
};
|
|
26
|
-
const getResourceMonitor = () => {
|
|
27
|
-
ResourceMonitor
|
|
46
|
+
const getResourceMonitor = async () => {
|
|
47
|
+
if (!ResourceMonitor) {
|
|
48
|
+
const mod = await loadWorkersModule();
|
|
49
|
+
ResourceMonitor = mod.ResourceMonitor;
|
|
50
|
+
}
|
|
28
51
|
return ResourceMonitor;
|
|
29
52
|
};
|
|
30
53
|
/**
|
|
@@ -46,14 +69,15 @@ const formatTable = (headers, rows) => {
|
|
|
46
69
|
const createWorkerListCommand = () => {
|
|
47
70
|
const ext = async () => {
|
|
48
71
|
try {
|
|
49
|
-
const workers = await getWorkerFactory().listPersisted();
|
|
72
|
+
const workers = await (await getWorkerFactory()).listPersisted();
|
|
50
73
|
console.log(`\nTotal Workers: ${workers.length}\n`);
|
|
51
74
|
if (workers.length === 0) {
|
|
52
75
|
console.log('No workers found.');
|
|
53
76
|
return;
|
|
54
77
|
}
|
|
78
|
+
const registry = await getWorkerRegistry();
|
|
55
79
|
const rows = workers.map((name) => {
|
|
56
|
-
const status =
|
|
80
|
+
const status = registry.status(name);
|
|
57
81
|
return [
|
|
58
82
|
name,
|
|
59
83
|
status?.status ?? 'unknown',
|
|
@@ -87,12 +111,12 @@ const createWorkerStatusCommand = () => {
|
|
|
87
111
|
Logger.error('Error: Worker name is required');
|
|
88
112
|
process.exit(1);
|
|
89
113
|
}
|
|
90
|
-
const status = getWorkerRegistry().status(name);
|
|
91
|
-
const health = await getWorkerFactory().getHealth(name);
|
|
114
|
+
const status = (await getWorkerRegistry()).status(name);
|
|
115
|
+
const health = await (await getWorkerFactory()).getHealth(name);
|
|
92
116
|
const healthData = typeof health === 'object' && health !== null
|
|
93
117
|
? health
|
|
94
118
|
: {};
|
|
95
|
-
const metrics = await getWorkerFactory().getMetrics(name);
|
|
119
|
+
const metrics = await (await getWorkerFactory()).getMetrics(name);
|
|
96
120
|
console.log(`\n=== Worker: ${name} ===\n`);
|
|
97
121
|
console.log(`Status: ${status?.status}`);
|
|
98
122
|
console.log(`Version: ${status?.version}`);
|
|
@@ -131,7 +155,7 @@ const createWorkerStartCommand = () => {
|
|
|
131
155
|
Logger.error('Error: Worker name is required');
|
|
132
156
|
process.exit(1);
|
|
133
157
|
}
|
|
134
|
-
await getWorkerFactory().start(name);
|
|
158
|
+
await (await getWorkerFactory()).start(name);
|
|
135
159
|
Logger.info(`✓ Worker "${name}" started successfully`);
|
|
136
160
|
}
|
|
137
161
|
catch (error) {
|
|
@@ -159,7 +183,7 @@ const createWorkerStopCommand = () => {
|
|
|
159
183
|
console.error('Error: Worker name is required');
|
|
160
184
|
process.exit(1);
|
|
161
185
|
}
|
|
162
|
-
await getWorkerFactory().stop(name);
|
|
186
|
+
await (await getWorkerFactory()).stop(name);
|
|
163
187
|
console.log(`✓ Worker "${name}" stopped successfully`);
|
|
164
188
|
}
|
|
165
189
|
catch (error) {
|
|
@@ -188,7 +212,7 @@ const createWorkerRestartCommand = () => {
|
|
|
188
212
|
console.error('Error: Worker name is required');
|
|
189
213
|
process.exit(1);
|
|
190
214
|
}
|
|
191
|
-
await getWorkerFactory().restart(name);
|
|
215
|
+
await (await getWorkerFactory()).restart(name);
|
|
192
216
|
console.log(`✓ Worker "${name}" restarted successfully`);
|
|
193
217
|
}
|
|
194
218
|
catch (error) {
|
|
@@ -211,11 +235,11 @@ const createWorkerRestartCommand = () => {
|
|
|
211
235
|
* Worker Summary Command
|
|
212
236
|
*/
|
|
213
237
|
const createWorkerSummaryCommand = () => {
|
|
214
|
-
const ext = () => {
|
|
238
|
+
const ext = async () => {
|
|
215
239
|
try {
|
|
216
|
-
const workers = getWorkerFactory().list();
|
|
217
|
-
const monitoringSummary = getHealthMonitor().getSummary();
|
|
218
|
-
const resourceUsage = getResourceMonitor().getCurrentUsage('system');
|
|
240
|
+
const workers = (await getWorkerFactory()).list();
|
|
241
|
+
const monitoringSummary = (await getHealthMonitor()).getSummary();
|
|
242
|
+
const resourceUsage = (await getResourceMonitor()).getCurrentUsage('system');
|
|
219
243
|
console.log(`\n=== Worker System Summary ===\n`);
|
|
220
244
|
console.log(`Total Workers: ${workers.length}`);
|
|
221
245
|
console.log(`\nHealth Overview:`);
|
|
@@ -247,7 +271,7 @@ const createWorkerSummaryCommand = () => {
|
|
|
247
271
|
const cmd = BaseCommand.create({
|
|
248
272
|
name: 'worker:summary',
|
|
249
273
|
description: 'Get system-wide worker summary',
|
|
250
|
-
execute: () => ext(),
|
|
274
|
+
execute: async () => ext(),
|
|
251
275
|
});
|
|
252
276
|
return cmd;
|
|
253
277
|
};
|
|
@@ -199,8 +199,8 @@ const createEnvFile = (projectPath, variables) => {
|
|
|
199
199
|
'',
|
|
200
200
|
'# Logging',
|
|
201
201
|
'LOG_LEVEL=debug',
|
|
202
|
-
'LOG_CHANNEL=
|
|
203
|
-
'LOG_FORMAT=
|
|
202
|
+
'LOG_CHANNEL=file',
|
|
203
|
+
'LOG_FORMAT=text',
|
|
204
204
|
'',
|
|
205
205
|
'# Auth / Security',
|
|
206
206
|
'JWT_SECRET=',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteGenerator.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/RouteGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAuBD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAqB3F;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA4CnF;
|
|
1
|
+
{"version":3,"file":"RouteGenerator.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/RouteGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAuBD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAqB3F;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA4CnF;AA4LD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,eAAe,EAAE,CAsCpD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,eAAe,EAAE,CA6BjD;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,eAAe,EAAE,CA+BlD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,EAAE,CAEhD;AAED;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;EAOzB,CAAC"}
|
|
@@ -8,21 +8,21 @@ import * as path from '../../node-singletons/path.js';
|
|
|
8
8
|
// Escape characters that can cause issues when embedding JSON.stringify output
|
|
9
9
|
// into generated JavaScript source (e.g., inside <script> tags).
|
|
10
10
|
const unsafeCharMap = {
|
|
11
|
-
'<':
|
|
12
|
-
'>':
|
|
13
|
-
'/':
|
|
11
|
+
'<': String.raw `\u003C`,
|
|
12
|
+
'>': String.raw `\u003E`,
|
|
13
|
+
'/': String.raw `\u002F`,
|
|
14
14
|
'\\': '\\\\',
|
|
15
|
-
'\b':
|
|
16
|
-
'\f':
|
|
17
|
-
'\n':
|
|
18
|
-
'\r':
|
|
19
|
-
'\t':
|
|
20
|
-
'\0':
|
|
21
|
-
'\u2028':
|
|
22
|
-
'\u2029':
|
|
15
|
+
'\b': String.raw `\b`,
|
|
16
|
+
'\f': String.raw `\f`,
|
|
17
|
+
'\n': String.raw `\n`,
|
|
18
|
+
'\r': String.raw `\r`,
|
|
19
|
+
'\t': String.raw `\t`,
|
|
20
|
+
'\0': String.raw `\0`,
|
|
21
|
+
'\u2028': String.raw `\u2028`,
|
|
22
|
+
'\u2029': String.raw `\u2029`,
|
|
23
23
|
};
|
|
24
24
|
function escapeUnsafeChars(str) {
|
|
25
|
-
return str.
|
|
25
|
+
return str.replaceAll(/[<>/\\\b\f\n\r\t\0\u2028\u2029]/g, (ch) => unsafeCharMap[ch] ?? ch);
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Validate route options
|
|
@@ -181,6 +181,19 @@ function buildRouteCode(route, router, groupMiddlewareList) {
|
|
|
181
181
|
return buildMethodRoute(route, router, groupMiddlewareList);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Build middleware property string for routes
|
|
186
|
+
*/
|
|
187
|
+
function buildMiddlewareProp(route, groupMiddlewareList) {
|
|
188
|
+
const localMiddlewareList = (route.middleware ?? []).map((m) => `'${m}'`).join(', ');
|
|
189
|
+
const hasGroup = groupMiddlewareList.trim() !== '';
|
|
190
|
+
const hasLocal = localMiddlewareList.trim() !== '';
|
|
191
|
+
return hasGroup || hasLocal
|
|
192
|
+
? `middleware: [${[hasGroup ? groupMiddlewareList : '', hasLocal ? localMiddlewareList : '']
|
|
193
|
+
.filter((v) => v.trim() !== '')
|
|
194
|
+
.join(', ')}]`
|
|
195
|
+
: '';
|
|
196
|
+
}
|
|
184
197
|
/**
|
|
185
198
|
* Build standard method route (GET, POST, etc.)
|
|
186
199
|
*/
|
|
@@ -192,14 +205,7 @@ function buildMethodRoute(route, router, groupMiddlewareList) {
|
|
|
192
205
|
const tag = toTag(controller);
|
|
193
206
|
const summary = `${method.toUpperCase()} ${routePath}`;
|
|
194
207
|
const controllerVar = toControllerVar(controller);
|
|
195
|
-
const
|
|
196
|
-
const hasGroup = groupMiddlewareList.trim() !== '';
|
|
197
|
-
const hasLocal = localMiddlewareList.trim() !== '';
|
|
198
|
-
const middlewareProp = hasGroup || hasLocal
|
|
199
|
-
? `middleware: [${[hasGroup ? groupMiddlewareList : '', hasLocal ? localMiddlewareList : '']
|
|
200
|
-
.filter((v) => v.trim() !== '')
|
|
201
|
-
.join(', ')}]`
|
|
202
|
-
: '';
|
|
208
|
+
const middlewareProp = buildMiddlewareProp(route, groupMiddlewareList);
|
|
203
209
|
const metaProp = `meta: { summary: ${escapeUnsafeChars(JSON.stringify(summary))}, tags: [${escapeUnsafeChars(JSON.stringify(tag))}] }`;
|
|
204
210
|
const options = `{ ${[middlewareProp, metaProp].filter((v) => v !== '').join(', ')} }`;
|
|
205
211
|
return ` Router.${method}(${router}, '${routePath}', (req, res) => ${controllerVar}.${action}(req, res), ${options});\n`;
|
|
@@ -212,14 +218,7 @@ function buildResourceRoute(route, router, groupMiddlewareList) {
|
|
|
212
218
|
const controller = route.controller;
|
|
213
219
|
const tag = toTag(controller);
|
|
214
220
|
const controllerVar = toControllerVar(controller);
|
|
215
|
-
const
|
|
216
|
-
const hasGroup = groupMiddlewareList.trim() !== '';
|
|
217
|
-
const hasLocal = localMiddlewareList.trim() !== '';
|
|
218
|
-
const middlewareProp = hasGroup || hasLocal
|
|
219
|
-
? `middleware: [${[hasGroup ? groupMiddlewareList : '', hasLocal ? localMiddlewareList : '']
|
|
220
|
-
.filter((v) => v.trim() !== '')
|
|
221
|
-
.join(', ')}]`
|
|
222
|
-
: '';
|
|
221
|
+
const middlewareProp = buildMiddlewareProp(route, groupMiddlewareList);
|
|
223
222
|
const resourceMeta = (action, routePattern) => `meta: { summary: ${escapeUnsafeChars(JSON.stringify(action.toUpperCase() + ' ' + routePattern))}, tags: [${escapeUnsafeChars(JSON.stringify(tag))}] }`;
|
|
224
223
|
const pathId = routePath + '/:id';
|
|
225
224
|
const optsParts = [
|