@zintrust/core 0.1.52 → 0.1.54
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 +1 -1
- package/src/boot/bootstrap.js +6 -5
- package/src/boot/registry/runtime.d.ts.map +1 -1
- package/src/boot/registry/runtime.js +10 -5
- package/src/cli/scaffolding/ProjectScaffolder.js +1 -1
- package/src/cli/scaffolding/env.d.ts.map +1 -1
- package/src/cli/scaffolding/env.js +2 -8
- package/src/config/app.d.ts +8 -0
- package/src/config/app.d.ts.map +1 -1
- package/src/config/app.js +8 -0
- package/src/config/env.d.ts +3 -1
- package/src/config/env.d.ts.map +1 -1
- package/src/config/env.js +4 -1
- package/src/config/index.d.ts +2 -0
- package/src/config/index.d.ts.map +1 -1
- package/src/index.js +3 -3
- package/src/orm/Database.d.ts.map +1 -1
- package/src/orm/Database.js +7 -4
- package/src/routes/doc.d.ts +1 -1
- package/src/routes/doc.d.ts.map +1 -1
- package/src/routes/doc.js +9 -8
- package/src/runtime/PluginAutoImports.d.ts.map +1 -1
- package/src/runtime/PluginAutoImports.js +0 -1
- package/src/runtime/detectRuntime.d.ts.map +1 -1
- package/src/runtime/detectRuntime.js +4 -1
- package/src/start.js +1 -1
- package/src/templates/project/basic/app/Controllers/AuthController.ts.tpl +35 -5
- package/src/templates/project/basic/app/Types/controller.ts.tpl +1 -0
- package/src/templates/project/basic/database/migrations/{create_users_table.ts.tpl → 20260109074544_create_users_table.ts.tpl} +6 -5
- package/src/templates/project/basic/database/migrations/20260218121500_create_jwt_revocations_table.ts.tpl +36 -0
- package/src/templates/project/basic/wrangler.jsonc.tpl +12 -0
- package/src/templates/project/basic/app/Middleware/ProfilerMiddleware.ts.tpl +0 -54
- package/src/templates/project/basic/app/Middleware/index.ts.tpl +0 -293
- package/src/templates/project/basic/database/migrations/create_tasks_table.ts.tpl +0 -28
package/package.json
CHANGED
package/src/boot/bootstrap.js
CHANGED
|
@@ -118,7 +118,8 @@ const gracefulShutdown = async (signal) => {
|
|
|
118
118
|
forceExitTimer.unref?.();
|
|
119
119
|
await withTimeout((async () => {
|
|
120
120
|
// Shutdown worker management system FIRST (before database closes)
|
|
121
|
-
if (
|
|
121
|
+
if (appConfig.worker === true &&
|
|
122
|
+
(appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda') &&
|
|
122
123
|
appConfig.dockerWorker === false) {
|
|
123
124
|
try {
|
|
124
125
|
const workers = await loadWorkersModule();
|
|
@@ -156,8 +157,7 @@ const gracefulShutdown = async (signal) => {
|
|
|
156
157
|
};
|
|
157
158
|
async function useWorkerStarter() {
|
|
158
159
|
// Check if workers are enabled in this environment
|
|
159
|
-
|
|
160
|
-
if (!workerEnabled || appConfig.dockerWorker === true) {
|
|
160
|
+
if (!appConfig.worker || appConfig.dockerWorker === true) {
|
|
161
161
|
Logger.info('Workers disabled in this runtime (WORKER_ENABLED=false && DOCKER_WORKER=true), skipping worker management initialization');
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
@@ -220,10 +220,11 @@ const BootstrapFunctions = Object.freeze({
|
|
|
220
220
|
// Start listening
|
|
221
221
|
await server.listen();
|
|
222
222
|
Logger.info(`Server running at http://${host}:${port}`);
|
|
223
|
-
Logger.info(`ZinTrust documentation at http://${host}:${port}/doc`);
|
|
223
|
+
Logger.info(`ZinTrust documentation at http://${host}:${port}/zintrust-doc`);
|
|
224
224
|
// Start schedules for long-running runtimes (Node.js / Fargate)
|
|
225
225
|
await startSchedulesIfNeeded(app);
|
|
226
|
-
if (appConfig.
|
|
226
|
+
if (appConfig.worker === true &&
|
|
227
|
+
appConfig.dockerWorker === false &&
|
|
227
228
|
(appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda')) {
|
|
228
229
|
await useWorkerStarter();
|
|
229
230
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/boot/registry/runtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/boot/registry/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAOvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAsJ9C,eAAO,MAAM,8BAA8B,GAAI,iBAAiB,gBAAgB,KAAG,IA4BlF,CAAC;AAyLF,eAAO,MAAM,eAAe,GAAI,QAAQ;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,gBAAgB,CAAC;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC,KAAG;IAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CA6E7D,CAAC"}
|
|
@@ -4,7 +4,6 @@ import { loadQueueMonitorModule, loadWorkersModule } from '../../runtime/Workers
|
|
|
4
4
|
import { registerCachesFromRuntimeConfig } from '../../cache/CacheRuntimeRegistration.js';
|
|
5
5
|
import broadcastConfig from '../../config/broadcast.js';
|
|
6
6
|
import { Cloudflare } from '../../config/cloudflare.js';
|
|
7
|
-
import { Env } from '../../config/env.js';
|
|
8
7
|
import { FeatureFlags } from '../../config/features.js';
|
|
9
8
|
import { Logger } from '../../config/logger.js';
|
|
10
9
|
import notificationConfig from '../../config/notification.js';
|
|
@@ -39,7 +38,12 @@ const readRuntimeConfig = (key, fallback) => {
|
|
|
39
38
|
return fallback;
|
|
40
39
|
}
|
|
41
40
|
};
|
|
42
|
-
const appConfig = readRuntimeConfig('appConfig', {
|
|
41
|
+
const appConfig = readRuntimeConfig('appConfig', {
|
|
42
|
+
port: 7777,
|
|
43
|
+
dockerWorker: false,
|
|
44
|
+
worker: false,
|
|
45
|
+
});
|
|
46
|
+
// exported solely for tests to exercise the default detectRuntime handler
|
|
43
47
|
const cacheConfig = readRuntimeConfig('cacheConfig', RuntimeConfig.cacheConfig);
|
|
44
48
|
const databaseConfig = readRuntimeConfig('databaseConfig', {
|
|
45
49
|
default: 'sqlite',
|
|
@@ -331,14 +335,15 @@ export const createLifecycle = (params) => {
|
|
|
331
335
|
await registerFromRuntimeConfig();
|
|
332
336
|
await initializeArtifactDirectories(params.resolvedBasePath);
|
|
333
337
|
await registerMasterRoutes(params.resolvedBasePath, params.router);
|
|
334
|
-
|
|
335
|
-
|
|
338
|
+
if (Cloudflare.getWorkersEnv() === null &&
|
|
339
|
+
appConfig.dockerWorker === false &&
|
|
340
|
+
appConfig.worker === true) {
|
|
336
341
|
await initializeWorkers(params.router);
|
|
337
342
|
await initializeQueueMonitor(params.router);
|
|
338
343
|
await initializeQueueHttpGateway(params.router);
|
|
339
344
|
await initializeScheduleHttpGateway(params.router);
|
|
340
345
|
}
|
|
341
|
-
else if (!
|
|
346
|
+
else if (!appConfig.dockerWorker) {
|
|
342
347
|
Logger.info('Skipping worker module initialization (WORKER_ENABLED=false).');
|
|
343
348
|
}
|
|
344
349
|
// Register service providers
|
|
@@ -503,7 +503,7 @@ zin s
|
|
|
503
503
|
|
|
504
504
|
- Health: http://localhost:{{port}}/health
|
|
505
505
|
- Tasks: http://localhost:{{port}}/api/tasks
|
|
506
|
-
- Doc: http://localhost:{{port}}/doc
|
|
506
|
+
- Doc: http://localhost:{{port}}/zintrust-doc
|
|
507
507
|
`;
|
|
508
508
|
}
|
|
509
509
|
return createFiles(state.projectPath, files, variables);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/env.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/env.ts"],"names":[],"mappings":"AAiiBA,eAAO,MAAM,OAAO,GAClB,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,oBAAoB,MAAM,EAC1B,SAAS,MAAM,EAAE,EACjB,cAAc,MAAM,KACnB,MAAM,EAWR,CAAC"}
|
|
@@ -147,12 +147,6 @@ const DatabaseAndCloudflare = (databaseNormalized, dbLines, sqliteDbPath) => [
|
|
|
147
147
|
...dbLines,
|
|
148
148
|
'',
|
|
149
149
|
'# Generic SQL defaults (used by some adapters)',
|
|
150
|
-
'DB_HOST=localhost',
|
|
151
|
-
'DB_PORT=5432',
|
|
152
|
-
'DB_DATABASE=',
|
|
153
|
-
'DB_USERNAME=postgres',
|
|
154
|
-
'DB_PASSWORD=',
|
|
155
|
-
'DB_READ_HOSTS=',
|
|
156
150
|
'',
|
|
157
151
|
'# PostgreSQL (optional)',
|
|
158
152
|
'DB_PORT_POSTGRESQL=5432',
|
|
@@ -355,14 +349,14 @@ const WorkersSchedulingAndSSE = () => [
|
|
|
355
349
|
'# WORKERS / SCHEDULER',
|
|
356
350
|
'# ============================================================================',
|
|
357
351
|
'',
|
|
358
|
-
'WORKER_ENABLED=
|
|
352
|
+
'WORKER_ENABLED=false',
|
|
359
353
|
'WORKER_AUTO_START=false',
|
|
360
354
|
'WORKER_CONCURRENCY=5',
|
|
361
355
|
'WORKER_TIMEOUT=60',
|
|
362
356
|
'WORKER_RETRIES=3',
|
|
363
357
|
'WORKER_PRIORITY=1',
|
|
364
358
|
'WORKER_HEALTH_CHECK_INTERVAL=60',
|
|
365
|
-
'WORKER_CLUSTER_MODE=
|
|
359
|
+
'WORKER_CLUSTER_MODE=false',
|
|
366
360
|
'WORKER_REGION=us-east-1',
|
|
367
361
|
'',
|
|
368
362
|
'WORKER_PERSISTENCE_DRIVER=memory',
|
package/src/config/app.d.ts
CHANGED
|
@@ -46,6 +46,14 @@ export declare const appConfig: Readonly<{
|
|
|
46
46
|
* Indicates if the application is running inside a Docker worker container
|
|
47
47
|
*/
|
|
48
48
|
readonly dockerWorker: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Indicates if the application is running as a Cloudflare Worker
|
|
51
|
+
*/
|
|
52
|
+
readonly cloudflareWorker: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Indicates if the application is running as a generic Worker (e.g. Cloudflare, AWS Lambda)
|
|
55
|
+
*/
|
|
56
|
+
readonly worker: boolean;
|
|
49
57
|
/**
|
|
50
58
|
* Application timezone
|
|
51
59
|
*/
|
package/src/config/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/config/app.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,cAAc,CAAC;AAoI3D,QAAA,MAAM,UAAU,QAAO,MAAM,CAAC,UAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/config/app.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,cAAc,CAAC;AAoI3D,QAAA,MAAM,UAAU,QAAO,MAAM,CAAC,UAA2B,CAAC;AAqG1D,eAAO,MAAM,SAAS;IArFpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;kCACc,OAAO;IAIxB;;OAEG;iCACa,OAAO;IAIvB;;OAEG;8BACU,OAAO;IAIpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;+BA9FkB,MAAM,CAAC,UAAU;kCAhDd,MAAM;EAqJoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC"}
|
package/src/config/app.js
CHANGED
|
@@ -169,6 +169,14 @@ const appConfigObj = {
|
|
|
169
169
|
* Indicates if the application is running inside a Docker worker container
|
|
170
170
|
*/
|
|
171
171
|
dockerWorker: readEnvBool('DOCKER_WORKER', Env.DOCKER_WORKER),
|
|
172
|
+
/**
|
|
173
|
+
* Indicates if the application is running as a Cloudflare Worker
|
|
174
|
+
*/
|
|
175
|
+
cloudflareWorker: readEnvBool('CLOUDFLARE_WORKER', Env.CLOUDFLARE_WORKER),
|
|
176
|
+
/**
|
|
177
|
+
* Indicates if the application is running as a generic Worker (e.g. Cloudflare, AWS Lambda)
|
|
178
|
+
*/
|
|
179
|
+
worker: readEnvBool('WORKER_ENABLED', Env.WORKER_ENABLED),
|
|
172
180
|
/**
|
|
173
181
|
* Application timezone
|
|
174
182
|
*/
|
package/src/config/env.d.ts
CHANGED
|
@@ -189,12 +189,14 @@ export declare const Env: Readonly<{
|
|
|
189
189
|
LOG_LEVEL: "debug" | "info" | "warn" | "error";
|
|
190
190
|
LOG_FORMAT: string;
|
|
191
191
|
LOG_CHANNEL: string;
|
|
192
|
-
DOCKER_WORKER: boolean;
|
|
193
192
|
DISABLE_LOGGING: boolean;
|
|
194
193
|
LOG_HTTP_REQUEST: boolean;
|
|
195
194
|
LOG_TO_FILE: boolean;
|
|
196
195
|
LOG_ROTATION_SIZE: number;
|
|
197
196
|
LOG_ROTATION_DAYS: number;
|
|
197
|
+
CLOUDFLARE_WORKER: boolean;
|
|
198
|
+
WORKER_ENABLED: boolean;
|
|
199
|
+
DOCKER_WORKER: boolean;
|
|
198
200
|
ZINTRUST_PROJECT_ROOT: string;
|
|
199
201
|
ZINTRUST_ALLOW_POSTINSTALL: string;
|
|
200
202
|
ZINTRUST_ENV_FILE: string;
|
package/src/config/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAuBlF,eAAO,MAAM,cAAc,QAAO,WAAW,GAAG,SAAwB,CAAC;AAEzE,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,MAKzE,CAAC;AAGF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAIxD,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,cAAc,MAAM,KAAG,MAI1D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAI7D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,eAAe,OAAO,KAAG,OAI7D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAGhD,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,IAInC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,GAAG,IAAI,KAAG,IAEpD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAKjE,CAAC;AACF,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAKxE,eAAO,MAAM,GAAG;eA3DS,MAAM,iBAAiB,MAAM,KAAG,MAAM;kBAMnC,MAAM,gBAAgB,MAAM,KAAG,MAAM;mBAYpC,MAAM,iBAAiB,OAAO,KAAG,OAAO;oBANvC,MAAM,iBAAiB,MAAM,KAAG,MAAM;eAY3C,MAAM,SAAS,MAAM,KAAG,IAAI;iBAK1B,MAAM,KAAG,IAAI;wBAMN,SAAS,GAAG,IAAI,KAAG,IAAI;oBAI7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;cAgCJ,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAgOpB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAuBlF,eAAO,MAAM,cAAc,QAAO,WAAW,GAAG,SAAwB,CAAC;AAEzE,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,MAKzE,CAAC;AAGF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAIxD,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,cAAc,MAAM,KAAG,MAI1D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAI7D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,eAAe,OAAO,KAAG,OAI7D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAGhD,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,IAInC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,GAAG,IAAI,KAAG,IAEpD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAKjE,CAAC;AACF,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAKxE,eAAO,MAAM,GAAG;eA3DS,MAAM,iBAAiB,MAAM,KAAG,MAAM;kBAMnC,MAAM,gBAAgB,MAAM,KAAG,MAAM;mBAYpC,MAAM,iBAAiB,OAAO,KAAG,OAAO;oBANvC,MAAM,iBAAiB,MAAM,KAAG,MAAM;eAY3C,MAAM,SAAS,MAAM,KAAG,IAAI;iBAK1B,MAAM,KAAG,IAAI;wBAMN,SAAS,GAAG,IAAI,KAAG,IAAI;oBAI7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;cAgCJ,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAgOpB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFxF,CAAC;AAEH,eAAO,MAAM,aAAa,QAAO,MAchC,CAAC"}
|
package/src/config/env.js
CHANGED
|
@@ -284,12 +284,15 @@ export const Env = Object.freeze({
|
|
|
284
284
|
LOG_LEVEL: get('LOG_LEVEL', getDefaultLogLevel()),
|
|
285
285
|
LOG_FORMAT: get('LOG_FORMAT', 'text'),
|
|
286
286
|
LOG_CHANNEL: get('LOG_CHANNEL', ''),
|
|
287
|
-
DOCKER_WORKER: getBool('DOCKER_WORKER', false),
|
|
288
287
|
DISABLE_LOGGING: getBool('DISABLE_LOGGING', false),
|
|
289
288
|
LOG_HTTP_REQUEST: getBool('LOG_HTTP_REQUEST', false),
|
|
290
289
|
LOG_TO_FILE: getBool('LOG_TO_FILE', false),
|
|
291
290
|
LOG_ROTATION_SIZE: getInt('LOG_ROTATION_SIZE', 10485760),
|
|
292
291
|
LOG_ROTATION_DAYS: getInt('LOG_ROTATION_DAYS', 7),
|
|
292
|
+
// Worker-specific
|
|
293
|
+
CLOUDFLARE_WORKER: getBool('CLOUDFLARE_WORKER', false),
|
|
294
|
+
WORKER_ENABLED: getBool('WORKER_ENABLED', false),
|
|
295
|
+
DOCKER_WORKER: getBool('DOCKER_WORKER', false),
|
|
293
296
|
// zintrust-specific
|
|
294
297
|
ZINTRUST_PROJECT_ROOT: get('ZINTRUST_PROJECT_ROOT', ''),
|
|
295
298
|
ZINTRUST_ALLOW_POSTINSTALL: get('ZINTRUST_ALLOW_POSTINSTALL', ''),
|
package/src/config/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export declare const config: Readonly<{
|
|
|
30
30
|
readonly isTesting: () => boolean;
|
|
31
31
|
readonly debug: boolean;
|
|
32
32
|
readonly dockerWorker: boolean;
|
|
33
|
+
readonly cloudflareWorker: boolean;
|
|
34
|
+
readonly worker: boolean;
|
|
33
35
|
readonly timezone: string;
|
|
34
36
|
readonly requestTimeout: number;
|
|
35
37
|
readonly maxBodySize: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;8BAZc,CAAC;;;;;;;;;;;;;;;;;;;;2EAAC,CAAA;;;;;;;;;;;;;;;;;;;;8BAD2B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA+C8X,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;EAHnb,CAAC;AAEZ,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @zintrust/core v0.1.
|
|
2
|
+
* @zintrust/core v0.1.54
|
|
3
3
|
*
|
|
4
4
|
* ZinTrust Framework - Production-Grade TypeScript Backend
|
|
5
5
|
* Built for performance, type safety, and exceptional developer experience
|
|
6
6
|
*
|
|
7
7
|
* Build Information:
|
|
8
|
-
* Built: 2026-
|
|
8
|
+
* Built: 2026-03-02T09:00:32.128Z
|
|
9
9
|
* Node: >=20.0.0
|
|
10
10
|
* License: MIT
|
|
11
11
|
*
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Available at runtime for debugging and health checks
|
|
22
22
|
*/
|
|
23
23
|
export const ZINTRUST_VERSION = '0.1.41';
|
|
24
|
-
export const ZINTRUST_BUILD_DATE = '2026-
|
|
24
|
+
export const ZINTRUST_BUILD_DATE = '2026-03-02T09:00:32.097Z'; // Replaced during build
|
|
25
25
|
export { Application } from './boot/Application.js';
|
|
26
26
|
export { AwsSigV4 } from './common/index.js';
|
|
27
27
|
export { SignedRequest } from './security/SignedRequest.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAWxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,eAAe,CAAC;IAC3B,SAAS,IAAI,cAAc,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAWxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,eAAe,CAAC;IAC3B,SAAS,IAAI,cAAc,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAqhBD,eAAO,MAAM,QAAQ;IACnB;;OAEG;oBACa,cAAc,GAAG,SAAS;EAI1C,CAAC;AAIH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,cAAc,GAAG,SAAqB,EAC9C,uBAA0B,KACzB,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAMxC,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,SAAY,GAAG,SAAS,CAoBtF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAWnD"}
|
package/src/orm/Database.js
CHANGED
|
@@ -116,7 +116,7 @@ const resolveWorkersAdapter = (cfg) => {
|
|
|
116
116
|
const workersEnv = Cloudflare.getWorkersEnv();
|
|
117
117
|
if (workersEnv === null)
|
|
118
118
|
return null;
|
|
119
|
-
Logger.
|
|
119
|
+
Logger.debug('[Database] Resolving adapter for Cloudflare Workers runtime', {
|
|
120
120
|
driver: cfg.driver,
|
|
121
121
|
useMySqlProxy: Env.getBool('USE_MYSQL_PROXY', false),
|
|
122
122
|
mysqlProxyUrlConfigured: Env.get('MYSQL_PROXY_URL', '').trim() !== '',
|
|
@@ -138,13 +138,16 @@ const createAdapter = (cfg) => {
|
|
|
138
138
|
const explicitProxy = resolveExplicitProxyAdapter(cfg);
|
|
139
139
|
if (explicitProxy)
|
|
140
140
|
return explicitProxy;
|
|
141
|
-
|
|
142
|
-
if (workersAdapter)
|
|
143
|
-
return workersAdapter;
|
|
141
|
+
// First: Check the specific configured driver via registry
|
|
144
142
|
const registered = DatabaseAdapterRegistry.get(cfg.driver);
|
|
145
143
|
if (registered !== undefined) {
|
|
146
144
|
return registered(cfg);
|
|
147
145
|
}
|
|
146
|
+
// Second: Try workers adapter resolution (only if registry lookup failed)
|
|
147
|
+
const workersAdapter = resolveWorkersAdapter(cfg);
|
|
148
|
+
if (workersAdapter)
|
|
149
|
+
return workersAdapter;
|
|
150
|
+
// Third: Fallback to direct adapter creation
|
|
148
151
|
switch (cfg.driver) {
|
|
149
152
|
case 'postgresql':
|
|
150
153
|
return PostgreSQLAdapter.create(cfg);
|
package/src/routes/doc.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Documentation Routes
|
|
3
|
-
* Serves static files from /doc/* paths with relaxed CSP headers.
|
|
3
|
+
* Serves static files from /zintrust-doc/* paths with relaxed CSP headers.
|
|
4
4
|
*/
|
|
5
5
|
import type { IRouter } from './Router';
|
|
6
6
|
import type { IResponse } from '../http/Response';
|
package/src/routes/doc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../../src/routes/doc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAInD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,aAAa,EACb,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../../src/routes/doc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAInD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,aAAa,EACb,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AAmCjC;;GAEG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,SAAS,KAAG,IAWhE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,SAAS,MAAM,EACf,UAAU,SAAS,KAClB,OAAO,CAAC,OAAO,CAgDjB,CAAC;AASF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,IAMnD,CAAC;;gCANwC,OAAO,KAAG,IAAI;2CA5EH,SAAS,KAAG,IAAI;2CAiB1D,MAAM,YACL,SAAS,KAClB,OAAO,CAAC,OAAO,CAAC;;AAiEnB,wBAIE"}
|
package/src/routes/doc.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Documentation Routes
|
|
3
|
-
* Serves static files from /doc/* paths with relaxed CSP headers.
|
|
3
|
+
* Serves static files from /zintrust-doc/* paths with relaxed CSP headers.
|
|
4
4
|
*/
|
|
5
5
|
import { HTTP_HEADERS } from '../config/constants.js';
|
|
6
6
|
import { MIME_TYPES_MAP, resolveSafePath, tryDecodeURIComponent } from './common.js';
|
|
@@ -16,6 +16,7 @@ export { MIME_TYPES_MAP } from './common.js';
|
|
|
16
16
|
*/
|
|
17
17
|
// Backward-compatible re-exports
|
|
18
18
|
export { findPackageRoot, findPackageRootAsync, getFrameworkPublicRoots, getFrameworkPublicRootsAsync, getPublicRoot, getPublicRootAsync, } from './publicRoot.js';
|
|
19
|
+
const docPath = 'zintrust-doc';
|
|
19
20
|
const PUBLIC_ROOT_CACHE_TTL_MS = 3000000000; // 50 minutes, can be adjusted as needed
|
|
20
21
|
let cachedPublicRoot = null;
|
|
21
22
|
const getCachedPublicRootAsync = async () => {
|
|
@@ -33,10 +34,10 @@ const getCachedPublicRootAsync = async () => {
|
|
|
33
34
|
const mapStaticPathAsync = async (urlPath) => {
|
|
34
35
|
const publicRoot = await getCachedPublicRootAsync();
|
|
35
36
|
const normalize = (p) => (p.startsWith('/') ? p.slice(1) : p);
|
|
36
|
-
if (urlPath ===
|
|
37
|
+
if (urlPath === `/${docPath}` || urlPath === `/${docPath}/`)
|
|
37
38
|
return publicRoot;
|
|
38
|
-
if (urlPath.startsWith(
|
|
39
|
-
const rawRelative = urlPath.slice(
|
|
39
|
+
if (urlPath.startsWith(`/${docPath}/`)) {
|
|
40
|
+
const rawRelative = urlPath.slice(`/${docPath}/`.length);
|
|
40
41
|
const normalizedRelative = tryDecodeURIComponent(rawRelative).replaceAll('\\', '/');
|
|
41
42
|
return resolveSafePath(publicRoot, normalize(normalizedRelative));
|
|
42
43
|
}
|
|
@@ -112,10 +113,10 @@ const handleDocRequest = async (req, res) => {
|
|
|
112
113
|
};
|
|
113
114
|
export const registerDocRoutes = (router) => {
|
|
114
115
|
// Root docs entrypoints.
|
|
115
|
-
Router.get(router,
|
|
116
|
-
Router.get(router,
|
|
117
|
-
// Greedy path match for nested assets like /doc/assets/app.js
|
|
118
|
-
Router.get(router,
|
|
116
|
+
Router.get(router, `/${docPath}`, handleDocRequest);
|
|
117
|
+
Router.get(router, `/${docPath}/`, handleDocRequest);
|
|
118
|
+
// Greedy path match for nested assets like /zintrust-doc/assets/app.js
|
|
119
|
+
Router.get(router, `/${docPath}/:path*`, handleDocRequest);
|
|
119
120
|
};
|
|
120
121
|
export default {
|
|
121
122
|
registerDocRoutes,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginAutoImports.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginAutoImports.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAChC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,GAAG,eAAe,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA0GN,eAAO,MAAM,iBAAiB;IAC5B;;;;;;OAMG;mCACkC,OAAO,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"PluginAutoImports.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginAutoImports.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAChC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,GAAG,eAAe,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA0GN,eAAO,MAAM,iBAAiB;IAC5B;;;;;;OAMG;mCACkC,OAAO,CAAC,YAAY,CAAC;qCAkEnB,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;EAavE,CAAC"}
|
|
@@ -107,7 +107,6 @@ export const PluginAutoImports = Object.freeze({
|
|
|
107
107
|
// Filter out non-existent candidates first
|
|
108
108
|
const existingCandidates = candidates.filter((candidate) => existsSync(candidate));
|
|
109
109
|
if (existingCandidates.length === 0) {
|
|
110
|
-
Logger.debug('[plugins] No plugin auto-imports file found', { projectRoot, candidates });
|
|
111
110
|
return { ok: false, reason: 'not-found' };
|
|
112
111
|
}
|
|
113
112
|
const tryImportCandidate = async (candidate) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detectRuntime.d.ts","sourceRoot":"","sources":["../../../src/runtime/detectRuntime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"detectRuntime.d.ts","sourceRoot":"","sources":["../../../src/runtime/detectRuntime.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG,oBAAoB,GAAG,YAAY,GAAG,aAAa,CAAC;AAE9E,eAAO,MAAM,aAAa,QAAO,OAWhC,CAAC;AAUF,eAAO,MAAM,cAAc,QAAO,WA6BjC,CAAC;AAEF,eAAO,MAAM,aAAa,QAAO;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAwBhB,CAAC"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { appConfig } from '../config/index.js';
|
|
1
2
|
import { ZintrustLang } from '../lang/lang.js';
|
|
2
3
|
export const isNodeRuntime = () => {
|
|
3
4
|
// Avoid importing any `node:*` modules so this file remains Worker-safe.
|
|
4
5
|
// In Workers/Deno, `process` is typically undefined.
|
|
5
|
-
|
|
6
|
+
const detectRuntime = appConfig.detectRuntime() !== 'cloudflare';
|
|
7
|
+
return (detectRuntime &&
|
|
8
|
+
typeof process !== ZintrustLang.UNDEFINED &&
|
|
6
9
|
typeof process === ZintrustLang.OBJECT &&
|
|
7
10
|
process !== null &&
|
|
8
11
|
typeof process.versions === ZintrustLang.OBJECT);
|
package/src/start.js
CHANGED
|
@@ -35,7 +35,7 @@ export const start = async () => {
|
|
|
35
35
|
// Compiled output places bootstrap at `dist/src/boot/bootstrap.js`.
|
|
36
36
|
// This file compiles to `dist/src/start.js`, so relative import is stable.
|
|
37
37
|
// In unit tests, importing bootstrap has heavy side effects (starts server + exits).
|
|
38
|
-
await import('./boot/'
|
|
38
|
+
await import('./boot/bootstrap.js');
|
|
39
39
|
};
|
|
40
40
|
/**
|
|
41
41
|
* Cloudflare Workers entry (module worker style).
|
|
@@ -10,11 +10,13 @@ import {
|
|
|
10
10
|
Auth,
|
|
11
11
|
JwtManager,
|
|
12
12
|
Logger,
|
|
13
|
-
TokenRevocation,
|
|
14
13
|
getString,
|
|
15
14
|
getValidatedBody,
|
|
15
|
+
isUndefinedOrNull,
|
|
16
16
|
} from '@zintrust/core';
|
|
17
17
|
|
|
18
|
+
|
|
19
|
+
|
|
18
20
|
const pickPublicUser = (row: UserRow): { id: unknown; name: string; email: string } => {
|
|
19
21
|
return {
|
|
20
22
|
id: row.id,
|
|
@@ -77,9 +79,15 @@ async function login(req: IRequest, res: IResponse): Promise<void> {
|
|
|
77
79
|
return undefined;
|
|
78
80
|
})();
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
// Bulletproof Auth (device binding) expects a device id header to match a JWT claim.
|
|
83
|
+
// For the example app, we mint a stable device id derived from the subject.
|
|
84
|
+
// Production apps should issue a per-device id and manage a per-device signing secret.
|
|
85
|
+
const deviceId = isUndefinedOrNull(subject) ? undefined : `dev-${subject}`;
|
|
86
|
+
|
|
87
|
+
const token = await JwtManager.signAccessToken({
|
|
81
88
|
sub: subject,
|
|
82
89
|
email,
|
|
90
|
+
...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
|
|
83
91
|
});
|
|
84
92
|
|
|
85
93
|
Logger.info('AuthController.login: successful login', {
|
|
@@ -92,6 +100,7 @@ async function login(req: IRequest, res: IResponse): Promise<void> {
|
|
|
92
100
|
res.json({
|
|
93
101
|
token,
|
|
94
102
|
token_type: 'Bearer',
|
|
103
|
+
...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
|
|
95
104
|
user,
|
|
96
105
|
});
|
|
97
106
|
} catch (error) {
|
|
@@ -166,7 +175,10 @@ async function register(req: IRequest, res: IResponse): Promise<void> {
|
|
|
166
175
|
|
|
167
176
|
res.setStatus(201).json({ message: 'Registered' });
|
|
168
177
|
} else {
|
|
169
|
-
Logger.error('Failed to retrieve inserted user ID'
|
|
178
|
+
Logger.error('Failed to retrieve inserted user ID', {
|
|
179
|
+
email,
|
|
180
|
+
ip: ipAddress,
|
|
181
|
+
});
|
|
170
182
|
res.setStatus(500).json({ error: 'Registration failed' });
|
|
171
183
|
}
|
|
172
184
|
return;
|
|
@@ -187,10 +199,27 @@ async function register(req: IRequest, res: IResponse): Promise<void> {
|
|
|
187
199
|
async function logout(req: IRequest, res: IResponse): Promise<void> {
|
|
188
200
|
const authHeader =
|
|
189
201
|
typeof req.getHeader === 'function' ? req.getHeader('authorization') : undefined;
|
|
190
|
-
await
|
|
202
|
+
await JwtManager.logout(authHeader);
|
|
191
203
|
res.json({ message: 'Logged out' });
|
|
192
204
|
}
|
|
193
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Logs out the current user from all devices by removing all active sessions for their subject.
|
|
208
|
+
*
|
|
209
|
+
* With session allowlist enforcement, deleting a user's session records causes any previously issued
|
|
210
|
+
* tokens to become unauthorized (401) immediately.
|
|
211
|
+
*/
|
|
212
|
+
async function logoutAll(req: IRequest, res: IResponse): Promise<void> {
|
|
213
|
+
const sub = typeof req.user?.sub === 'string' ? req.user.sub.trim() : '';
|
|
214
|
+
if (sub === '') {
|
|
215
|
+
res.setStatus(401).json({ error: 'Unauthorized' });
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
await JwtManager.logoutAll(sub);
|
|
220
|
+
res.json({ message: 'Logged out everywhere' });
|
|
221
|
+
}
|
|
222
|
+
|
|
194
223
|
/**
|
|
195
224
|
* Refreshes the user's JWT access token.
|
|
196
225
|
* Generates a new token with the same claims as the current user.
|
|
@@ -206,7 +235,7 @@ async function refresh(req: IRequest, res: IResponse): Promise<void> {
|
|
|
206
235
|
return;
|
|
207
236
|
}
|
|
208
237
|
|
|
209
|
-
const token = JwtManager.signAccessToken(user);
|
|
238
|
+
const token = await JwtManager.signAccessToken(user);
|
|
210
239
|
res.json({ token, token_type: 'Bearer' });
|
|
211
240
|
}
|
|
212
241
|
|
|
@@ -216,6 +245,7 @@ export const AuthController = Object.freeze({
|
|
|
216
245
|
login,
|
|
217
246
|
register,
|
|
218
247
|
logout,
|
|
248
|
+
logoutAll,
|
|
219
249
|
refresh,
|
|
220
250
|
};
|
|
221
251
|
},
|
|
@@ -23,6 +23,7 @@ export type AuthControllerApi = {
|
|
|
23
23
|
login(req: IRequest, res: IResponse): Promise<void>;
|
|
24
24
|
register(req: IRequest, res: IResponse): Promise<void>;
|
|
25
25
|
logout(req: IRequest, res: IResponse): Promise<void>;
|
|
26
|
+
logoutAll(req: IRequest, res: IResponse): Promise<void>;
|
|
26
27
|
refresh(req: IRequest, res: IResponse): Promise<void>;
|
|
27
28
|
};
|
|
28
29
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Migration: CreateUsersTable
|
|
3
|
+
* Creates users table
|
|
4
|
+
*/
|
|
5
|
+
import type { Blueprint, IDatabase } from '@zintrust/core';
|
|
6
|
+
import { MigrationSchema } from '@zintrust/core';
|
|
3
7
|
|
|
4
8
|
export interface Migration {
|
|
5
9
|
up(db: IDatabase): Promise<void>;
|
|
@@ -15,10 +19,7 @@ export const migration: Migration = {
|
|
|
15
19
|
table.string('name');
|
|
16
20
|
table.string('email').unique();
|
|
17
21
|
table.string('password');
|
|
18
|
-
table.timestamp('email_verified_at').nullable();
|
|
19
|
-
table.boolean('active').default(true);
|
|
20
22
|
table.timestamps();
|
|
21
|
-
table.timestamp('deleted_at').nullable();
|
|
22
23
|
});
|
|
23
24
|
},
|
|
24
25
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: CreateJwtRevocationsTable
|
|
3
|
+
* Creates a storage table for JWT token revocation (token invalidation).
|
|
4
|
+
*/
|
|
5
|
+
import type { Blueprint, IDatabase } from '@zintrust/core';
|
|
6
|
+
import { MigrationSchema } from '@zintrust/core';
|
|
7
|
+
|
|
8
|
+
export interface Migration {
|
|
9
|
+
up(db: IDatabase): Promise<void>;
|
|
10
|
+
down(db: IDatabase): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const migration: Migration = {
|
|
14
|
+
async up(db: IDatabase): Promise<void> {
|
|
15
|
+
const schema = MigrationSchema.create(db);
|
|
16
|
+
|
|
17
|
+
await schema.create('zintrust_jwt_revocations', (table: Blueprint) => {
|
|
18
|
+
table.id();
|
|
19
|
+
table.string('user_id', 191).nullable();
|
|
20
|
+
table.string('jti', 128).unique();
|
|
21
|
+
table.string('sub', 191).nullable();
|
|
22
|
+
table.string('kind', 16).notNullable().default('revoked');
|
|
23
|
+
table.bigInteger('expires_at_ms');
|
|
24
|
+
table.timestamp('revoked_at').notNullable().default('CURRENT_TIMESTAMP');
|
|
25
|
+
|
|
26
|
+
table.index(['user_id']);
|
|
27
|
+
table.index(['kind']);
|
|
28
|
+
table.index(['expires_at_ms']);
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async down(db: IDatabase): Promise<void> {
|
|
33
|
+
const schema = MigrationSchema.create(db);
|
|
34
|
+
await schema.dropIfExists('zintrust_jwt_revocations');
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -5,6 +5,18 @@
|
|
|
5
5
|
"compatibility_flags": ["nodejs_compat"],
|
|
6
6
|
"workers_dev": true,
|
|
7
7
|
"minify": false,
|
|
8
|
+
"alias": {
|
|
9
|
+
"@routes/api.ts": "./routes/api.ts",
|
|
10
|
+
"../zintrust.plugins.wg.js": "./src/zintrust.plugins.wg.ts",
|
|
11
|
+
"@runtime-config/broadcast.ts": "./config/broadcast.ts",
|
|
12
|
+
"@runtime-config/cache.ts": "./config/cache.ts",
|
|
13
|
+
"@runtime-config/database.ts": "./config/database.ts",
|
|
14
|
+
"@runtime-config/mail.ts": "./config/mail.ts",
|
|
15
|
+
"@runtime-config/storage.ts": "./config/storage.ts",
|
|
16
|
+
"@runtime-config/queue.ts": "./config/queue.ts",
|
|
17
|
+
"@runtime-config/notification.ts": "./config/notification.ts",
|
|
18
|
+
"@runtime-config/middleware.ts": "./config/middleware.ts"
|
|
19
|
+
},
|
|
8
20
|
|
|
9
21
|
"vars": {
|
|
10
22
|
"ENVIRONMENT": "development"
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck - Example middleware - WIP
|
|
2
|
-
/**
|
|
3
|
-
* Profiler Middleware
|
|
4
|
-
* Enables request profiling when ENABLE_PROFILER environment variable is set
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Middleware, Logger , RequestProfiler} from '@zintrust/core';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* ProfilerMiddleware wraps request execution with performance profiling
|
|
11
|
-
* Enabled via ENABLE_PROFILER=true environment variable
|
|
12
|
-
* Attaches profiling report to response headers
|
|
13
|
-
*/
|
|
14
|
-
export const ProfilerMiddleware: Middleware = async (req, res, next) => {
|
|
15
|
-
const isEnabled = process.env.ENABLE_PROFILER === 'true';
|
|
16
|
-
|
|
17
|
-
if (!isEnabled) {
|
|
18
|
-
// Pass through without profiling
|
|
19
|
-
await next();
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const profiler = RequestProfiler.create();
|
|
24
|
-
const queryLogger = profiler.getQueryLogger();
|
|
25
|
-
|
|
26
|
-
// Set up query logging if database is available
|
|
27
|
-
const db = req.context.db;
|
|
28
|
-
if (db !== undefined && db !== null && typeof db.onAfterQuery === 'function') {
|
|
29
|
-
db.onAfterQuery((sql: string, params: unknown[], duration: number) => {
|
|
30
|
-
queryLogger.logQuery(sql, params, duration, 'middleware-profiling');
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Capture request execution
|
|
35
|
-
const profile = await profiler.captureRequest(async () => next());
|
|
36
|
-
|
|
37
|
-
// Attach profile to response
|
|
38
|
-
res.locals.profile = profile;
|
|
39
|
-
|
|
40
|
-
// Add profiling report to response header
|
|
41
|
-
try {
|
|
42
|
-
const report = profiler.generateReport(profile);
|
|
43
|
-
res.setHeader('X-Profiler-Report', Buffer.from(report).toString('base64'));
|
|
44
|
-
res.setHeader('X-Profiler-Queries', profile.queriesExecuted.toString());
|
|
45
|
-
res.setHeader('X-Profiler-Duration', profile.duration.toString());
|
|
46
|
-
|
|
47
|
-
if (profile.n1Patterns.length > 0) {
|
|
48
|
-
res.setHeader('X-Profiler-N1-Patterns', profile.n1Patterns.length.toString());
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
// Silently fail if header encoding fails
|
|
52
|
-
Logger.error('Failed to encode profiler report header:', error);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example Middleware
|
|
3
|
-
* Common middleware patterns for ZinTrust
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { IJwtManager, JwtAlgorithm, JwtManagerType, ISchema, SchemaType, CsrfTokenManagerType, ICsrfTokenManager } from '@zintrust/core';
|
|
7
|
-
import { Logger , Validator, IRequest, IResponse, XssProtection} from '@zintrust/core';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type JwtManagerInput = IJwtManager | JwtManagerType;
|
|
11
|
-
type CsrfManagerInput = ICsrfTokenManager | CsrfTokenManagerType;
|
|
12
|
-
|
|
13
|
-
const resolveJwtManager = (jwtManager: JwtManagerInput): IJwtManager =>
|
|
14
|
-
'verify' in jwtManager ? jwtManager : jwtManager.create();
|
|
15
|
-
|
|
16
|
-
const resolveCsrfManager = (csrfManager: CsrfManagerInput): ICsrfTokenManager =>
|
|
17
|
-
'validateToken' in csrfManager ? csrfManager : csrfManager.create();
|
|
18
|
-
|
|
19
|
-
type ValidationSchema = ISchema | SchemaType;
|
|
20
|
-
|
|
21
|
-
const resolveSchema = (schema: ValidationSchema): ISchema =>
|
|
22
|
-
'getRules' in schema ? schema : schema.create();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Authentication Middleware
|
|
26
|
-
* Verify user is authenticated
|
|
27
|
-
*/
|
|
28
|
-
export const authMiddleware = async (
|
|
29
|
-
req: IRequest,
|
|
30
|
-
res: IResponse,
|
|
31
|
-
next: () => Promise<void>
|
|
32
|
-
): Promise<void> => {
|
|
33
|
-
const token = req.getHeader('authorization');
|
|
34
|
-
|
|
35
|
-
if (token === undefined || token === '') {
|
|
36
|
-
res.setStatus(401).json({ error: 'Unauthorized' });
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
await next();
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* CORS Middleware
|
|
45
|
-
* Handle CORS headers
|
|
46
|
-
*/
|
|
47
|
-
export const corsMiddleware = async (
|
|
48
|
-
req: IRequest,
|
|
49
|
-
res: IResponse,
|
|
50
|
-
next: () => Promise<void>
|
|
51
|
-
): Promise<void> => {
|
|
52
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
53
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
|
|
54
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
55
|
-
|
|
56
|
-
if (req.getMethod() === 'OPTIONS') {
|
|
57
|
-
res.setStatus(200).send('');
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
await next();
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* JSON Request Middleware
|
|
66
|
-
* Parse JSON request bodies
|
|
67
|
-
*/
|
|
68
|
-
export const jsonMiddleware = async (
|
|
69
|
-
req: IRequest,
|
|
70
|
-
res: IResponse,
|
|
71
|
-
next: () => Promise<void>
|
|
72
|
-
): Promise<void> => {
|
|
73
|
-
if (req.getMethod() === 'GET' || req.getMethod() === 'DELETE') {
|
|
74
|
-
await next();
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (req.isJson() === false) {
|
|
79
|
-
res.setStatus(415).json({ error: 'Content-Type must be application/json' });
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
await next();
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Logging Middleware
|
|
88
|
-
* Log all requests
|
|
89
|
-
*/
|
|
90
|
-
export const loggingMiddleware = async (
|
|
91
|
-
req: IRequest,
|
|
92
|
-
res: IResponse,
|
|
93
|
-
next: () => Promise<void>
|
|
94
|
-
): Promise<void> => {
|
|
95
|
-
const startTime = Date.now();
|
|
96
|
-
const method = req.getMethod();
|
|
97
|
-
const path = req.getPath();
|
|
98
|
-
|
|
99
|
-
Logger.info(`→ ${method} ${path}`);
|
|
100
|
-
|
|
101
|
-
await next();
|
|
102
|
-
|
|
103
|
-
const duration = Date.now() - startTime;
|
|
104
|
-
const status = res.getStatus();
|
|
105
|
-
Logger.info(`← ${status} ${method} ${path} (${duration}ms)`);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Rate Limiting Middleware
|
|
110
|
-
* Simple in-memory rate limiting
|
|
111
|
-
*/
|
|
112
|
-
const requestCounts = new Map<string, number[]>();
|
|
113
|
-
|
|
114
|
-
export const rateLimitMiddleware = async (
|
|
115
|
-
req: IRequest,
|
|
116
|
-
res: IResponse,
|
|
117
|
-
next: () => Promise<void>
|
|
118
|
-
): Promise<void> => {
|
|
119
|
-
const ip = req.getRaw().socket.remoteAddress ?? 'unknown';
|
|
120
|
-
const now = Date.now();
|
|
121
|
-
const windowMs = 60 * 1000; // 1 minute
|
|
122
|
-
const maxRequests = 100;
|
|
123
|
-
|
|
124
|
-
if (requestCounts.has(ip) === false) {
|
|
125
|
-
requestCounts.set(ip, []);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const requests = requestCounts.get(ip) ?? [];
|
|
129
|
-
const recentRequests = requests.filter((time) => now - time < windowMs);
|
|
130
|
-
|
|
131
|
-
if (recentRequests.length >= maxRequests) {
|
|
132
|
-
res.setStatus(429).json({ error: 'Too many requests' });
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
recentRequests.push(now);
|
|
137
|
-
requestCounts.set(ip, recentRequests);
|
|
138
|
-
|
|
139
|
-
await next();
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Trailing Slash Middleware
|
|
144
|
-
* Redirect URLs with trailing slashes
|
|
145
|
-
*/
|
|
146
|
-
export const trailingSlashMiddleware = async (
|
|
147
|
-
req: IRequest,
|
|
148
|
-
res: IResponse,
|
|
149
|
-
next: () => Promise<void>
|
|
150
|
-
): Promise<void> => {
|
|
151
|
-
const path = req.getPath();
|
|
152
|
-
|
|
153
|
-
if (path.length > 1 && path.endsWith('/') === true) {
|
|
154
|
-
const withoutSlash = path.slice(0, -1);
|
|
155
|
-
res.redirect(withoutSlash, 301);
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
await next();
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* JWT Authentication Middleware
|
|
164
|
-
* Verify JWT token and extract claims
|
|
165
|
-
*/
|
|
166
|
-
export const jwtMiddleware = (jwtManager: JwtManagerInput, algorithm: JwtAlgorithm = 'HS256') => {
|
|
167
|
-
return async (req: IRequest, res: IResponse, next: () => Promise<void>): Promise<void> => {
|
|
168
|
-
const authHeader = req.getHeader('authorization');
|
|
169
|
-
|
|
170
|
-
if (authHeader === undefined || authHeader === '') {
|
|
171
|
-
res.setStatus(401).json({ error: 'Missing authorization header' });
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const authHeaderStr = Array.isArray(authHeader) ? authHeader[0] : authHeader;
|
|
176
|
-
const [scheme, token] = authHeaderStr.split(' ');
|
|
177
|
-
|
|
178
|
-
if (scheme !== 'Bearer' || token === undefined || token === '') {
|
|
179
|
-
res.setStatus(401).json({ error: 'Invalid authorization header format' });
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const payload = resolveJwtManager(jwtManager).verify(token, algorithm);
|
|
185
|
-
// Store in request context (TypeScript allows dynamic properties)
|
|
186
|
-
req.user = payload;
|
|
187
|
-
await next();
|
|
188
|
-
} catch (error) {
|
|
189
|
-
Logger.error('JWT verification failed:', error);
|
|
190
|
-
res.setStatus(401).json({ error: 'Invalid or expired token' });
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* CSRF Protection Middleware
|
|
197
|
-
* Validate CSRF tokens for state-changing requests
|
|
198
|
-
*/
|
|
199
|
-
export const csrfMiddleware = (csrfManager: CsrfManagerInput) => {
|
|
200
|
-
return async (req: IRequest, res: IResponse, next: () => Promise<void>): Promise<void> => {
|
|
201
|
-
const method = req.getMethod();
|
|
202
|
-
|
|
203
|
-
// Only validate on state-changing requests
|
|
204
|
-
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) === false) {
|
|
205
|
-
await next();
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const sessionId = req.sessionId ?? req.getHeader('x-session-id');
|
|
210
|
-
|
|
211
|
-
if (sessionId === undefined || sessionId === '') {
|
|
212
|
-
res.setStatus(400).json({ error: 'Missing session ID' });
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const csrfToken = req.getHeader('x-csrf-token');
|
|
217
|
-
|
|
218
|
-
if (csrfToken === undefined || csrfToken === '') {
|
|
219
|
-
res.setStatus(403).json({ error: 'Missing CSRF token' });
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const isValid = await resolveCsrfManager(csrfManager).validateToken(
|
|
224
|
-
String(sessionId),
|
|
225
|
-
String(csrfToken)
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
if (isValid === false) {
|
|
229
|
-
res.setStatus(403).json({ error: 'Invalid or expired CSRF token' });
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
await next();
|
|
234
|
-
};
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Input Validation Middleware
|
|
239
|
-
* Validate request body against schema
|
|
240
|
-
*/
|
|
241
|
-
export const validationMiddleware = (schema: SchemaType) => {
|
|
242
|
-
return async (req: IRequest, res: IResponse, next: () => Promise<void>): Promise<void> => {
|
|
243
|
-
if (req.getMethod() === 'GET' || req.getMethod() === 'DELETE') {
|
|
244
|
-
await next();
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
try {
|
|
249
|
-
const body = req.body ?? {};
|
|
250
|
-
Validator.validate(body, resolveSchema(schema));
|
|
251
|
-
await next();
|
|
252
|
-
} catch (error: unknown) {
|
|
253
|
-
Logger.error('Validation error:', error);
|
|
254
|
-
const newError = error as Error & { toObject?: () => Record<string, unknown> };
|
|
255
|
-
if (
|
|
256
|
-
error !== undefined &&
|
|
257
|
-
'toObject' in newError &&
|
|
258
|
-
typeof newError.toObject === 'function'
|
|
259
|
-
) {
|
|
260
|
-
res.setStatus(422).json({ errors: newError.toObject() });
|
|
261
|
-
} else {
|
|
262
|
-
res.setStatus(400).json({ error: 'Invalid request body' });
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* XSS Protection Middleware
|
|
270
|
-
* Sanitize and escape user input
|
|
271
|
-
*/
|
|
272
|
-
export const xssProtectionMiddleware = async (
|
|
273
|
-
req: IRequest,
|
|
274
|
-
res: IResponse,
|
|
275
|
-
next: () => Promise<void>
|
|
276
|
-
): Promise<void> => {
|
|
277
|
-
// Add XSS protection headers
|
|
278
|
-
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
279
|
-
res.setHeader('X-Frame-Options', 'DENY');
|
|
280
|
-
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
281
|
-
|
|
282
|
-
// Sanitize request body if present
|
|
283
|
-
const body = req.body;
|
|
284
|
-
if (body !== undefined && body !== null && typeof body === 'object') {
|
|
285
|
-
for (const [key, value] of Object.entries(body)) {
|
|
286
|
-
if (typeof value === 'string') {
|
|
287
|
-
body[key] = XssProtection.escape(value);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
await next();
|
|
293
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { MigrationSchema, type Blueprint } from '@zintrust/core';
|
|
2
|
-
import type { IDatabase } from '@zintrust/core';
|
|
3
|
-
|
|
4
|
-
export interface Migration {
|
|
5
|
-
up(db: IDatabase): Promise<void>;
|
|
6
|
-
down(db: IDatabase): Promise<void>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const migration: Migration = {
|
|
10
|
-
async up(db: IDatabase): Promise<void> {
|
|
11
|
-
const schema = MigrationSchema.create(db);
|
|
12
|
-
|
|
13
|
-
await schema.create('tasks', (table: Blueprint) => {
|
|
14
|
-
table.id();
|
|
15
|
-
table.string('title');
|
|
16
|
-
table.text('description').nullable();
|
|
17
|
-
table.string('status').default('pending');
|
|
18
|
-
table.integer('user_id');
|
|
19
|
-
table.foreign('user_id').references('id').on('users').onDelete('CASCADE');
|
|
20
|
-
table.timestamps();
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
async down(db: IDatabase): Promise<void> {
|
|
25
|
-
const schema = MigrationSchema.create(db);
|
|
26
|
-
await schema.dropIfExists('tasks');
|
|
27
|
-
},
|
|
28
|
-
};
|