@zintrust/core 0.1.20 → 0.1.21
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 +2 -1
- package/src/boot/Application.d.ts.map +1 -1
- package/src/boot/Application.js +48 -10
- package/src/boot/bootstrap.js +2 -0
- package/src/cli/commands/MigrateCommand.d.ts.map +1 -1
- package/src/cli/commands/MigrateCommand.js +36 -3
- package/src/cli/d1/D1SqlMigrations.d.ts.map +1 -1
- package/src/cli/d1/D1SqlMigrations.js +6 -1
- package/src/cli/scaffolding/ControllerGenerator.js +4 -4
- package/src/cli/scaffolding/GovernanceScaffolder.js +1 -1
- package/src/cli/scaffolding/MigrationGenerator.js +1 -1
- package/src/cli/scaffolding/ModelGenerator.js +1 -1
- package/src/cli/scaffolding/RouteGenerator.js +1 -1
- package/src/cli/scaffolding/ServiceScaffolder.js +4 -4
- package/src/config/broadcast.d.ts +14 -28
- package/src/config/broadcast.d.ts.map +1 -1
- package/src/config/broadcast.js +69 -35
- package/src/config/cache.d.ts +13 -45
- package/src/config/cache.d.ts.map +1 -1
- package/src/config/cache.js +69 -25
- package/src/config/database.d.ts +22 -64
- package/src/config/database.d.ts.map +1 -1
- package/src/config/database.js +99 -31
- package/src/config/env.d.ts +6 -0
- package/src/config/env.d.ts.map +1 -1
- package/src/config/env.js +7 -0
- package/src/config/index.d.ts +32 -136
- package/src/config/index.d.ts.map +1 -1
- package/src/config/mail.d.ts +19 -55
- package/src/config/mail.d.ts.map +1 -1
- package/src/config/mail.js +63 -21
- package/src/config/middleware.d.ts +24 -0
- package/src/config/middleware.d.ts.map +1 -1
- package/src/config/middleware.js +72 -52
- package/src/config/notification.d.ts +14 -27
- package/src/config/notification.d.ts.map +1 -1
- package/src/config/notification.js +82 -36
- package/src/config/queue.d.ts +21 -51
- package/src/config/queue.d.ts.map +1 -1
- package/src/config/queue.js +72 -27
- package/src/config/storage.d.ts +27 -34
- package/src/config/storage.d.ts.map +1 -1
- package/src/config/storage.js +97 -56
- package/src/config/type.d.ts +12 -1
- package/src/config/type.d.ts.map +1 -1
- package/src/http/parsers/MultipartParser.d.ts.map +1 -1
- package/src/http/parsers/MultipartParser.js +69 -42
- package/src/index.d.ts +9 -5
- package/src/index.d.ts.map +1 -1
- package/src/index.js +1 -0
- package/src/microservices/PostgresAdapter.d.ts.map +1 -1
- package/src/microservices/PostgresAdapter.js +0 -1
- package/src/migrations/MigratorFactory.d.ts.map +1 -1
- package/src/migrations/MigratorFactory.js +18 -2
- package/src/node-singletons/fs.d.ts +1 -1
- package/src/node-singletons/fs.d.ts.map +1 -1
- package/src/node-singletons/fs.js +1 -1
- package/src/orm/Database.d.ts +2 -1
- package/src/orm/Database.d.ts.map +1 -1
- package/src/orm/Database.js +110 -67
- package/src/orm/DatabaseAdapter.d.ts +1 -0
- package/src/orm/DatabaseAdapter.d.ts.map +1 -1
- package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -1
- package/src/orm/DatabaseRuntimeRegistration.js +12 -0
- package/src/orm/QueryBuilder.d.ts +1 -1
- package/src/orm/QueryBuilder.d.ts.map +1 -1
- package/src/orm/QueryBuilder.js +4 -3
- package/src/orm/adapters/SQLiteAdapter.js +1 -1
- package/src/performance/Optimizer.d.ts +6 -6
- package/src/performance/Optimizer.d.ts.map +1 -1
- package/src/performance/Optimizer.js +133 -52
- package/src/routing/doc.d.ts +4 -5
- package/src/routing/doc.d.ts.map +1 -1
- package/src/routing/doc.js +35 -20
- package/src/routing/publicRoot.d.ts +9 -0
- package/src/routing/publicRoot.d.ts.map +1 -1
- package/src/routing/publicRoot.js +63 -2
- package/src/runtime/StartupConfigFileRegistry.d.ts +20 -0
- package/src/runtime/StartupConfigFileRegistry.d.ts.map +1 -0
- package/src/runtime/StartupConfigFileRegistry.js +44 -0
- package/src/runtime/useFileLoader.d.ts +26 -0
- package/src/runtime/useFileLoader.d.ts.map +1 -0
- package/src/runtime/useFileLoader.js +188 -0
- package/src/scripts/TemplateSync.js +4 -4
- package/src/security/XssProtection.d.ts.map +1 -1
- package/src/security/XssProtection.js +62 -14
- package/src/templates/project/basic/config/broadcast.ts.tpl +33 -17
- package/src/templates/project/basic/config/cache.ts.tpl +35 -17
- package/src/templates/project/basic/config/database.ts.tpl +68 -32
- package/src/templates/project/basic/config/logging/HttpLogger.ts.tpl +7 -114
- package/src/templates/project/basic/config/mail.ts.tpl +59 -13
- package/src/templates/project/basic/config/notification.ts.tpl +28 -17
- package/src/templates/project/basic/config/queue.ts.tpl +49 -17
- package/src/templates/project/basic/config/storage.ts.tpl +55 -18
- package/src/templates/project/basic/config/type.ts.tpl +0 -1
- package/src/templates/project/basic/src/index.ts.tpl +3 -0
- package/src/templates/project/basic/config/logging/KvLogger.ts.tpl +0 -181
- package/src/templates/project/basic/config/logging/SlackLogger.ts.tpl +0 -156
package/src/config/mail.d.ts
CHANGED
|
@@ -3,61 +3,25 @@
|
|
|
3
3
|
* Runtime mail drivers and settings
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
|
-
import type { MailDriverConfig } from './type';
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Default "From" identity
|
|
14
|
-
*/
|
|
15
|
-
readonly from: {
|
|
16
|
-
readonly address: string;
|
|
17
|
-
readonly name: string;
|
|
6
|
+
import type { MailConfigInput, MailDriverConfig } from './type';
|
|
7
|
+
export type MailConfigOverrides = Partial<{
|
|
8
|
+
default: string;
|
|
9
|
+
from: {
|
|
10
|
+
address: string;
|
|
11
|
+
name: string;
|
|
18
12
|
};
|
|
19
|
-
|
|
20
|
-
* Driver configs
|
|
21
|
-
*/
|
|
22
|
-
readonly drivers: {
|
|
23
|
-
readonly disabled: {
|
|
24
|
-
readonly driver: "disabled";
|
|
25
|
-
};
|
|
26
|
-
readonly sendgrid: {
|
|
27
|
-
readonly driver: "sendgrid";
|
|
28
|
-
readonly apiKey: string;
|
|
29
|
-
};
|
|
30
|
-
readonly mailgun: {
|
|
31
|
-
readonly driver: "mailgun";
|
|
32
|
-
readonly apiKey: string;
|
|
33
|
-
readonly domain: string;
|
|
34
|
-
readonly baseUrl: string;
|
|
35
|
-
};
|
|
36
|
-
readonly smtp: {
|
|
37
|
-
readonly driver: "smtp";
|
|
38
|
-
readonly host: string;
|
|
39
|
-
readonly port: number;
|
|
40
|
-
readonly username: string;
|
|
41
|
-
readonly password: string;
|
|
42
|
-
readonly secure: boolean | "starttls";
|
|
43
|
-
};
|
|
44
|
-
readonly nodemailer: {
|
|
45
|
-
readonly driver: "nodemailer";
|
|
46
|
-
readonly host: string;
|
|
47
|
-
readonly port: number;
|
|
48
|
-
readonly username: string;
|
|
49
|
-
readonly password: string;
|
|
50
|
-
readonly secure: boolean | "starttls";
|
|
51
|
-
};
|
|
52
|
-
readonly ses: {
|
|
53
|
-
readonly driver: "ses";
|
|
54
|
-
readonly region: string;
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* Get selected driver config
|
|
59
|
-
*/
|
|
60
|
-
readonly getDriver: (name?: string) => MailDriverConfig;
|
|
13
|
+
drivers: Record<string, MailDriverConfig>;
|
|
61
14
|
}>;
|
|
62
|
-
|
|
15
|
+
declare const createMailConfig: () => {
|
|
16
|
+
default: string;
|
|
17
|
+
from: {
|
|
18
|
+
address: string;
|
|
19
|
+
name: string;
|
|
20
|
+
};
|
|
21
|
+
drivers: Record<string, MailDriverConfig>;
|
|
22
|
+
getDriver: (this: MailConfigInput, name?: string) => MailDriverConfig;
|
|
23
|
+
};
|
|
24
|
+
export type MailConfig = ReturnType<typeof createMailConfig>;
|
|
25
|
+
export declare const mailConfig: MailConfig;
|
|
26
|
+
export {};
|
|
63
27
|
//# sourceMappingURL=mail.d.ts.map
|
package/src/config/mail.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail.d.ts","sourceRoot":"","sources":["../../../src/config/mail.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"mail.d.ts","sourceRoot":"","sources":["../../../src/config/mail.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC3C,CAAC,CAAC;AAmCH,QAAA,MAAM,gBAAgB,QAAO;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC1C,SAAS,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,gBAAgB,CAAC;CAgGvE,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAqB7D,eAAO,MAAM,UAAU,EAAE,UAYvB,CAAC"}
|
package/src/config/mail.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Runtime mail drivers and settings
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
|
+
import { StartupConfigFile, StartupConfigFileRegistry } from '../runtime/StartupConfigFileRegistry.js';
|
|
6
7
|
import { Env } from './env.js';
|
|
7
8
|
import { ErrorFactory } from '../exceptions/ZintrustError.js';
|
|
8
9
|
const isMailDriverConfig = (value) => {
|
|
@@ -34,22 +35,16 @@ const getMailDriver = (config, name) => {
|
|
|
34
35
|
}
|
|
35
36
|
throw ErrorFactory.createConfigError(`Mail driver not configured: ${selected}`);
|
|
36
37
|
};
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
* Default "From" identity
|
|
44
|
-
*/
|
|
45
|
-
from: {
|
|
38
|
+
const createMailConfig = () => {
|
|
39
|
+
const overrides = StartupConfigFileRegistry.get(StartupConfigFile.Mail) ?? {};
|
|
40
|
+
const baseDefault = Env.get('MAIL_CONNECTION', Env.get('MAIL_DRIVER', 'disabled'))
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase();
|
|
43
|
+
const baseFrom = {
|
|
46
44
|
address: Env.get('MAIL_FROM_ADDRESS', ''),
|
|
47
45
|
name: Env.get('MAIL_FROM_NAME', ''),
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
* Driver configs
|
|
51
|
-
*/
|
|
52
|
-
drivers: {
|
|
46
|
+
};
|
|
47
|
+
const baseDrivers = {
|
|
53
48
|
disabled: {
|
|
54
49
|
driver: 'disabled',
|
|
55
50
|
},
|
|
@@ -101,12 +96,59 @@ const mailConfigObj = {
|
|
|
101
96
|
driver: 'ses',
|
|
102
97
|
region: Env.get('AWS_REGION', 'us-east-1'),
|
|
103
98
|
},
|
|
99
|
+
};
|
|
100
|
+
const mailConfigObj = {
|
|
101
|
+
/**
|
|
102
|
+
* Default mail driver
|
|
103
|
+
*/
|
|
104
|
+
default: (overrides.default ?? baseDefault).trim().toLowerCase(),
|
|
105
|
+
/**
|
|
106
|
+
* Default "From" identity
|
|
107
|
+
*/
|
|
108
|
+
from: {
|
|
109
|
+
...baseFrom,
|
|
110
|
+
...(overrides.from ?? {}),
|
|
111
|
+
},
|
|
112
|
+
/**
|
|
113
|
+
* Driver configs
|
|
114
|
+
*/
|
|
115
|
+
drivers: {
|
|
116
|
+
...baseDrivers,
|
|
117
|
+
...(overrides.drivers ?? {}),
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Get selected driver config
|
|
121
|
+
*/
|
|
122
|
+
getDriver(name) {
|
|
123
|
+
return getMailDriver(this, name);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
return Object.freeze(mailConfigObj);
|
|
127
|
+
};
|
|
128
|
+
let cached = null;
|
|
129
|
+
const proxyTarget = {};
|
|
130
|
+
const ensureMailConfig = () => {
|
|
131
|
+
if (cached)
|
|
132
|
+
return cached;
|
|
133
|
+
cached = createMailConfig();
|
|
134
|
+
try {
|
|
135
|
+
Object.defineProperties(proxyTarget, Object.getOwnPropertyDescriptors(cached));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// best-effort
|
|
139
|
+
}
|
|
140
|
+
return cached;
|
|
141
|
+
};
|
|
142
|
+
export const mailConfig = new Proxy(proxyTarget, {
|
|
143
|
+
get(_target, prop) {
|
|
144
|
+
return ensureMailConfig()[prop];
|
|
104
145
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
getDriver(name) {
|
|
109
|
-
return getMailDriver(this, name);
|
|
146
|
+
ownKeys() {
|
|
147
|
+
ensureMailConfig();
|
|
148
|
+
return Reflect.ownKeys(proxyTarget);
|
|
110
149
|
},
|
|
111
|
-
|
|
112
|
-
|
|
150
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
151
|
+
ensureMailConfig();
|
|
152
|
+
return Object.getOwnPropertyDescriptor(proxyTarget, prop);
|
|
153
|
+
},
|
|
154
|
+
});
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import type { MiddlewareConfigType } from './type';
|
|
2
|
+
export declare enum MiddlewareBody {
|
|
3
|
+
email = "email",
|
|
4
|
+
password = "password",
|
|
5
|
+
name = "name",
|
|
6
|
+
count = "count"
|
|
7
|
+
}
|
|
8
|
+
export type MiddlewaresType = {
|
|
9
|
+
skipPaths: string[];
|
|
10
|
+
fillRateLimit: {
|
|
11
|
+
windowMs: number;
|
|
12
|
+
max: number;
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
authRateLimit: {
|
|
16
|
+
windowMs: number;
|
|
17
|
+
max: number;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
userMutationRateLimit: {
|
|
21
|
+
windowMs: number;
|
|
22
|
+
max: number;
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
2
26
|
export declare const MiddlewareKeys: Readonly<{
|
|
3
27
|
log: true;
|
|
4
28
|
error: true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/config/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AA8DzD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;EAiBuB,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/config/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AA8DzD,oBAAY,cAAc;IACxB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,qBAAqB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;EAiBuB,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,cAAc,CAAC;AA0MxD,wBAAgB,sBAAsB,IAAI,oBAAoB,CAqB7D;AA0BD,eAAO,MAAM,gBAAgB,EAAE,oBAY7B,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
|
package/src/config/middleware.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StartupConfigFile, StartupConfigFileRegistry } from '../runtime/StartupConfigFileRegistry.js';
|
|
2
2
|
import { bodyParsingMiddleware } from '../http/middleware/BodyParsingMiddleware.js';
|
|
3
3
|
import { fileUploadMiddleware } from '../http/middleware/FileUploadMiddleware.js';
|
|
4
4
|
import { AuthMiddleware } from '../middleware/AuthMiddleware.js';
|
|
@@ -12,6 +12,13 @@ import { SecurityMiddleware } from '../middleware/SecurityMiddleware.js';
|
|
|
12
12
|
import { ValidationMiddleware } from '../middleware/ValidationMiddleware.js';
|
|
13
13
|
import { Sanitizer } from '../security/Sanitizer.js';
|
|
14
14
|
import { Schema } from '../validation/Validator.js';
|
|
15
|
+
export var MiddlewareBody;
|
|
16
|
+
(function (MiddlewareBody) {
|
|
17
|
+
MiddlewareBody["email"] = "email";
|
|
18
|
+
MiddlewareBody["password"] = "password";
|
|
19
|
+
MiddlewareBody["name"] = "name";
|
|
20
|
+
MiddlewareBody["count"] = "count";
|
|
21
|
+
})(MiddlewareBody || (MiddlewareBody = {}));
|
|
15
22
|
export const MiddlewareKeys = Object.freeze({
|
|
16
23
|
log: true,
|
|
17
24
|
error: true,
|
|
@@ -33,45 +40,60 @@ export const MiddlewareKeys = Object.freeze({
|
|
|
33
40
|
// Ensure `ValidationMiddleware` is strongly typed here to avoid `error`-typed values
|
|
34
41
|
// triggering `@typescript-eslint/no-unsafe-*` rules.
|
|
35
42
|
const Validation = ValidationMiddleware;
|
|
36
|
-
|
|
43
|
+
const resolveFillRateLimit = (loadMiddlewareConfig) => {
|
|
44
|
+
return {
|
|
45
|
+
windowMs: loadMiddlewareConfig.fillRateLimit?.windowMs ?? 60_000,
|
|
46
|
+
max: loadMiddlewareConfig.fillRateLimit?.max ?? 5,
|
|
47
|
+
message: loadMiddlewareConfig.fillRateLimit?.message ??
|
|
48
|
+
'Too many requests, please try again after 1 minute.',
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const resolveAuthRateLimit = (loadMiddlewareConfig) => {
|
|
52
|
+
return {
|
|
53
|
+
windowMs: loadMiddlewareConfig.authRateLimit?.windowMs ?? 60_000,
|
|
54
|
+
max: loadMiddlewareConfig.authRateLimit?.max ?? 10,
|
|
55
|
+
message: loadMiddlewareConfig.authRateLimit?.message ??
|
|
56
|
+
'Too many login attempts, please try again after 1 minute.',
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const resolveUserMutationRateLimit = (loadMiddlewareConfig) => {
|
|
60
|
+
return {
|
|
61
|
+
windowMs: loadMiddlewareConfig.userMutationRateLimit?.windowMs ?? 60_000,
|
|
62
|
+
max: loadMiddlewareConfig.userMutationRateLimit?.max ?? 20,
|
|
63
|
+
message: loadMiddlewareConfig.userMutationRateLimit?.message ??
|
|
64
|
+
'Too many user requests, please try again after 1 minute.',
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
function createRateLimitMiddlewares(loadMiddlewareConfig) {
|
|
68
|
+
const fillRateLimit = RateLimiter.create(resolveFillRateLimit(loadMiddlewareConfig));
|
|
69
|
+
const authRateLimit = RateLimiter.create(resolveAuthRateLimit(loadMiddlewareConfig));
|
|
70
|
+
const userMutationRateLimit = RateLimiter.create(resolveUserMutationRateLimit(loadMiddlewareConfig));
|
|
37
71
|
return Object.freeze({
|
|
38
72
|
rateLimit: RateLimiter.create(),
|
|
39
|
-
fillRateLimit
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
message: 'Too many fill requests, please try again later.',
|
|
43
|
-
}),
|
|
44
|
-
authRateLimit: RateLimiter.create({
|
|
45
|
-
windowMs: 60_000,
|
|
46
|
-
max: 10,
|
|
47
|
-
message: 'Too many authentication requests, please try again later.',
|
|
48
|
-
}),
|
|
49
|
-
userMutationRateLimit: RateLimiter.create({
|
|
50
|
-
windowMs: 60_000,
|
|
51
|
-
max: 20,
|
|
52
|
-
message: 'Too many write requests, please try again later.',
|
|
53
|
-
}),
|
|
73
|
+
fillRateLimit,
|
|
74
|
+
authRateLimit,
|
|
75
|
+
userMutationRateLimit,
|
|
54
76
|
});
|
|
55
77
|
}
|
|
56
78
|
function createAuthValidationMiddlewares() {
|
|
57
79
|
return {
|
|
58
80
|
validateLogin: Validation.createBodyWithSanitization(Schema.typed()
|
|
59
|
-
.required(
|
|
60
|
-
.email(
|
|
61
|
-
.required(
|
|
62
|
-
.string(
|
|
81
|
+
.required(MiddlewareBody.email)
|
|
82
|
+
.email(MiddlewareBody.email)
|
|
83
|
+
.required(MiddlewareBody.password)
|
|
84
|
+
.string(MiddlewareBody.password), {
|
|
63
85
|
email: (v) => Sanitizer.email(v).trim().toLowerCase(),
|
|
64
86
|
password: (v) => Sanitizer.safePasswordChars(v),
|
|
65
87
|
}),
|
|
66
88
|
validateRegister: Validation.createBodyWithSanitization(Schema.typed()
|
|
67
|
-
.required(
|
|
68
|
-
.string(
|
|
69
|
-
.minLength(
|
|
70
|
-
.required(
|
|
71
|
-
.email(
|
|
72
|
-
.required(
|
|
73
|
-
.string(
|
|
74
|
-
.minLength(
|
|
89
|
+
.required(MiddlewareBody.name)
|
|
90
|
+
.string(MiddlewareBody.name)
|
|
91
|
+
.minLength(MiddlewareBody.name, 1)
|
|
92
|
+
.required(MiddlewareBody.email)
|
|
93
|
+
.email(MiddlewareBody.email)
|
|
94
|
+
.required(MiddlewareBody.password)
|
|
95
|
+
.string(MiddlewareBody.password)
|
|
96
|
+
.minLength(MiddlewareBody.password, 8), {
|
|
75
97
|
name: (v) => Sanitizer.nameText(v).trim(),
|
|
76
98
|
email: (v) => Sanitizer.email(v).trim().toLowerCase(),
|
|
77
99
|
password: (v) => Sanitizer.safePasswordChars(v),
|
|
@@ -81,32 +103,32 @@ function createAuthValidationMiddlewares() {
|
|
|
81
103
|
function createUserValidationMiddlewares() {
|
|
82
104
|
return {
|
|
83
105
|
validateUserStore: Validation.createBodyWithSanitization(Schema.typed()
|
|
84
|
-
.required(
|
|
85
|
-
.string(
|
|
86
|
-
.minLength(
|
|
87
|
-
.required(
|
|
88
|
-
.email(
|
|
89
|
-
.required(
|
|
90
|
-
.string(
|
|
91
|
-
.minLength(
|
|
106
|
+
.required(MiddlewareBody.name)
|
|
107
|
+
.string(MiddlewareBody.name)
|
|
108
|
+
.minLength(MiddlewareBody.name, 1)
|
|
109
|
+
.required(MiddlewareBody.email)
|
|
110
|
+
.email(MiddlewareBody.email)
|
|
111
|
+
.required(MiddlewareBody.password)
|
|
112
|
+
.string(MiddlewareBody.password)
|
|
113
|
+
.minLength(MiddlewareBody.password, 8), {
|
|
92
114
|
name: (v) => Sanitizer.nameText(v).trim(),
|
|
93
115
|
email: (v) => Sanitizer.email(v).trim().toLowerCase(),
|
|
94
116
|
password: (v) => Sanitizer.safePasswordChars(v),
|
|
95
117
|
}),
|
|
96
118
|
validateUserUpdate: Validation.createBodyWithSanitization(Schema.typed()
|
|
97
|
-
.custom(
|
|
98
|
-
.minLength(
|
|
99
|
-
.custom(
|
|
100
|
-
.custom(
|
|
101
|
-
.minLength(
|
|
119
|
+
.custom(MiddlewareBody.name, (v) => v === undefined || typeof v === 'string', MiddlewareBody.name + ' must be a string')
|
|
120
|
+
.minLength(MiddlewareBody.name, 1)
|
|
121
|
+
.custom(MiddlewareBody.email, (v) => v === undefined || typeof v === 'string', MiddlewareBody.email + ' must be a string')
|
|
122
|
+
.custom(MiddlewareBody.password, (v) => v === undefined || typeof v === 'string', MiddlewareBody.password + ' must be a string')
|
|
123
|
+
.minLength(MiddlewareBody.password, 8), {
|
|
102
124
|
name: (v) => Sanitizer.nameText(v).trim(),
|
|
103
125
|
email: (v) => Sanitizer.email(v).trim().toLowerCase(),
|
|
104
126
|
password: (v) => Sanitizer.safePasswordChars(v),
|
|
105
127
|
}),
|
|
106
128
|
validateUserFill: Validation.createBodyWithSanitization(Schema.typed()
|
|
107
|
-
.custom(
|
|
108
|
-
.min(
|
|
109
|
-
.max(
|
|
129
|
+
.custom(MiddlewareBody.count, (v) => v === undefined || (typeof v === 'number' && Number.isFinite(v)), MiddlewareBody.count + ' must be a number')
|
|
130
|
+
.min(MiddlewareBody.count, 1)
|
|
131
|
+
.max(MiddlewareBody.count, 100)),
|
|
110
132
|
};
|
|
111
133
|
}
|
|
112
134
|
function createValidationMiddlewares() {
|
|
@@ -115,8 +137,8 @@ function createValidationMiddlewares() {
|
|
|
115
137
|
...createUserValidationMiddlewares(),
|
|
116
138
|
});
|
|
117
139
|
}
|
|
118
|
-
function createSharedMiddlewares() {
|
|
119
|
-
const rateLimits = createRateLimitMiddlewares();
|
|
140
|
+
function createSharedMiddlewares(loadMiddlewareConfig) {
|
|
141
|
+
const rateLimits = createRateLimitMiddlewares(loadMiddlewareConfig);
|
|
120
142
|
const validations = createValidationMiddlewares();
|
|
121
143
|
return Object.freeze({
|
|
122
144
|
log: LoggingMiddleware.create(),
|
|
@@ -125,10 +147,7 @@ function createSharedMiddlewares() {
|
|
|
125
147
|
sanitizeBody: SanitizeBodyMiddleware.create(),
|
|
126
148
|
...rateLimits,
|
|
127
149
|
csrf: CsrfMiddleware.create({
|
|
128
|
-
skipPaths:
|
|
129
|
-
.split(',')
|
|
130
|
-
.map((p) => p.trim())
|
|
131
|
-
.filter((p) => p.length > 0),
|
|
150
|
+
skipPaths: loadMiddlewareConfig?.skipPaths ?? [],
|
|
132
151
|
}),
|
|
133
152
|
auth: AuthMiddleware.create(),
|
|
134
153
|
jwt: JwtAuthMiddleware.create(),
|
|
@@ -136,7 +155,8 @@ function createSharedMiddlewares() {
|
|
|
136
155
|
});
|
|
137
156
|
}
|
|
138
157
|
export function createMiddlewareConfig() {
|
|
139
|
-
const
|
|
158
|
+
const loadMiddlewareConfig = StartupConfigFileRegistry.get(StartupConfigFile.Middleware) ?? {};
|
|
159
|
+
const shared = createSharedMiddlewares(loadMiddlewareConfig);
|
|
140
160
|
const middlewareConfigObj = {
|
|
141
161
|
global: [
|
|
142
162
|
shared.log,
|
|
@@ -5,33 +5,20 @@
|
|
|
5
5
|
* Driver selection must be dynamic (tests may mutate process.env).
|
|
6
6
|
*/
|
|
7
7
|
import type { KnownNotificationDriverConfig, NotificationDrivers, NotificationProviders } from './type';
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
readonly default: string;
|
|
13
|
-
/**
|
|
14
|
-
* Notification channels.
|
|
15
|
-
*
|
|
16
|
-
* You may add custom named channels (e.g. `opsSlack`, `smsMarketing`) that
|
|
17
|
-
* point to any known driver config.
|
|
18
|
-
*/
|
|
19
|
-
readonly drivers: NotificationDrivers;
|
|
20
|
-
/**
|
|
21
|
-
* Legacy provider configs (kept for backwards compatibility with wrappers).
|
|
22
|
-
*/
|
|
23
|
-
readonly providers: NotificationProviders;
|
|
24
|
-
/**
|
|
25
|
-
* Normalized notification channel name.
|
|
26
|
-
*/
|
|
27
|
-
readonly getDriverName: () => string;
|
|
28
|
-
/**
|
|
29
|
-
* Resolve a channel config.
|
|
30
|
-
* - Unknown names throw when explicitly selected.
|
|
31
|
-
* - `default` is a reserved alias of the configured default.
|
|
32
|
-
*/
|
|
33
|
-
readonly getDriverConfig: (name?: string) => KnownNotificationDriverConfig;
|
|
8
|
+
export type NotificationConfigOverrides = Partial<{
|
|
9
|
+
default: string;
|
|
10
|
+
drivers: NotificationDrivers;
|
|
11
|
+
providers: NotificationProviders;
|
|
34
12
|
}>;
|
|
35
|
-
|
|
13
|
+
type NotificationRuntimeConfig = {
|
|
14
|
+
default: string;
|
|
15
|
+
drivers: NotificationDrivers;
|
|
16
|
+
providers: NotificationProviders;
|
|
17
|
+
getDriverName: () => string;
|
|
18
|
+
getDriverConfig: (name?: string) => KnownNotificationDriverConfig;
|
|
19
|
+
};
|
|
20
|
+
declare const createNotificationConfig: () => NotificationRuntimeConfig;
|
|
21
|
+
export type NotificationConfig = ReturnType<typeof createNotificationConfig>;
|
|
22
|
+
export declare const notificationConfig: NotificationConfig;
|
|
36
23
|
export default notificationConfig;
|
|
37
24
|
//# sourceMappingURL=notification.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../../../src/config/notification.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../../../src/config/notification.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,6BAA6B,EAE7B,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,cAAc,CAAC;AAGtB,MAAM,MAAM,2BAA2B,GAAG,OAAO,CAAC;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAAC;AAEH,KAAK,yBAAyB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,eAAe,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,6BAA6B,CAAC;CACnE,CAAC;AA2EF,QAAA,MAAM,wBAAwB,QAAO,yBA+DpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAqB7E,eAAO,MAAM,kBAAkB,EAAE,kBAY/B,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Config-first mapping of notification providers/channels.
|
|
5
5
|
* Driver selection must be dynamic (tests may mutate process.env).
|
|
6
6
|
*/
|
|
7
|
+
import { StartupConfigFile, StartupConfigFileRegistry } from '../runtime/StartupConfigFileRegistry.js';
|
|
7
8
|
import { Env } from './env.js';
|
|
8
9
|
import { ErrorFactory } from '../exceptions/ZintrustError.js';
|
|
9
10
|
const normalizeName = (value) => value.trim().toLowerCase();
|
|
@@ -61,43 +62,88 @@ const getBaseProviders = () => {
|
|
|
61
62
|
},
|
|
62
63
|
};
|
|
63
64
|
};
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
65
|
+
const createNotificationConfig = () => {
|
|
66
|
+
const overrides = StartupConfigFileRegistry.get(StartupConfigFile.Notification) ??
|
|
67
|
+
{};
|
|
68
|
+
const notificationConfigObj = {
|
|
69
|
+
/**
|
|
70
|
+
* Default notification channel name (normalized).
|
|
71
|
+
*/
|
|
72
|
+
get default() {
|
|
73
|
+
const overrideDefault = overrides.default;
|
|
74
|
+
if (typeof overrideDefault === 'string' && overrideDefault.trim().length > 0) {
|
|
75
|
+
const value = normalizeName(overrideDefault);
|
|
76
|
+
if (value.length > 0 && hasOwn(this.drivers, value))
|
|
77
|
+
return value;
|
|
78
|
+
throw ErrorFactory.createConfigError(`Notification channel not configured: ${value}`);
|
|
79
|
+
}
|
|
80
|
+
return getDefaultChannel(this.drivers);
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Notification channels.
|
|
84
|
+
*
|
|
85
|
+
* You may add custom named channels (e.g. `opsSlack`, `smsMarketing`) that
|
|
86
|
+
* point to any known driver config.
|
|
87
|
+
*/
|
|
88
|
+
get drivers() {
|
|
89
|
+
// Return a record of channels; can be extended by app-level config.
|
|
90
|
+
return {
|
|
91
|
+
...getBaseProviders(),
|
|
92
|
+
...(overrides.providers ?? {}),
|
|
93
|
+
...(overrides.drivers ?? {}),
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
/**
|
|
97
|
+
* Legacy provider configs (kept for backwards compatibility with wrappers).
|
|
98
|
+
*/
|
|
99
|
+
get providers() {
|
|
100
|
+
return {
|
|
101
|
+
...getBaseProviders(),
|
|
102
|
+
...(overrides.providers ?? {}),
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* Normalized notification channel name.
|
|
107
|
+
*/
|
|
108
|
+
getDriverName() {
|
|
109
|
+
return normalizeName(this.default);
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Resolve a channel config.
|
|
113
|
+
* - Unknown names throw when explicitly selected.
|
|
114
|
+
* - `default` is a reserved alias of the configured default.
|
|
115
|
+
*/
|
|
116
|
+
getDriverConfig(name) {
|
|
117
|
+
return getNotificationDriver(this, name);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
return Object.freeze(notificationConfigObj);
|
|
121
|
+
};
|
|
122
|
+
let cached = null;
|
|
123
|
+
const proxyTarget = {};
|
|
124
|
+
const ensureNotificationConfig = () => {
|
|
125
|
+
if (cached)
|
|
126
|
+
return cached;
|
|
127
|
+
cached = createNotificationConfig();
|
|
128
|
+
try {
|
|
129
|
+
Object.defineProperties(proxyTarget, Object.getOwnPropertyDescriptors(cached));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// best-effort
|
|
133
|
+
}
|
|
134
|
+
return cached;
|
|
135
|
+
};
|
|
136
|
+
export const notificationConfig = new Proxy(proxyTarget, {
|
|
137
|
+
get(_target, prop) {
|
|
138
|
+
return ensureNotificationConfig()[prop];
|
|
86
139
|
},
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
getDriverName() {
|
|
91
|
-
return normalizeName(this.default);
|
|
140
|
+
ownKeys() {
|
|
141
|
+
ensureNotificationConfig();
|
|
142
|
+
return Reflect.ownKeys(proxyTarget);
|
|
92
143
|
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
* - `default` is a reserved alias of the configured default.
|
|
97
|
-
*/
|
|
98
|
-
getDriverConfig(name) {
|
|
99
|
-
return getNotificationDriver(this, name);
|
|
144
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
145
|
+
ensureNotificationConfig();
|
|
146
|
+
return Object.getOwnPropertyDescriptor(proxyTarget, prop);
|
|
100
147
|
},
|
|
101
|
-
};
|
|
102
|
-
export const notificationConfig = Object.freeze(notificationConfigObj);
|
|
148
|
+
});
|
|
103
149
|
export default notificationConfig;
|