@zintrust/core 0.1.13 → 0.1.15
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/Application.d.ts.map +1 -1
- package/src/boot/Application.js +110 -11
- package/src/cache/Cache.d.ts +13 -0
- package/src/cache/Cache.d.ts.map +1 -1
- package/src/cache/Cache.js +48 -6
- package/src/cache/CacheRuntimeRegistration.d.ts +11 -0
- package/src/cache/CacheRuntimeRegistration.d.ts.map +1 -0
- package/src/cache/CacheRuntimeRegistration.js +21 -0
- package/src/config/broadcast.d.ts +18 -4
- package/src/config/broadcast.d.ts.map +1 -1
- package/src/config/broadcast.js +62 -12
- package/src/config/cache.d.ts +1 -1
- package/src/config/cache.d.ts.map +1 -1
- package/src/config/cache.js +26 -9
- package/src/config/database.d.ts +1 -1
- package/src/config/database.d.ts.map +1 -1
- package/src/config/database.js +61 -43
- package/src/config/index.d.ts +23 -2
- package/src/config/index.d.ts.map +1 -1
- package/src/config/index.js +6 -0
- package/src/config/mail.d.ts +3 -3
- package/src/config/mail.d.ts.map +1 -1
- package/src/config/mail.js +31 -9
- package/src/config/notification.d.ts +25 -29
- package/src/config/notification.d.ts.map +1 -1
- package/src/config/notification.js +80 -20
- package/src/config/storage.d.ts +8 -0
- package/src/config/storage.d.ts.map +1 -1
- package/src/config/storage.js +29 -8
- package/src/config/type.d.ts +32 -31
- package/src/config/type.d.ts.map +1 -1
- package/src/index.d.ts +5 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +5 -0
- package/src/node.d.ts +1 -3
- package/src/node.d.ts.map +1 -1
- package/src/node.js +1 -3
- package/src/orm/Database.d.ts.map +1 -1
- package/src/orm/Database.js +4 -0
- package/src/orm/DatabaseRuntimeRegistration.d.ts +17 -0
- package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -0
- package/src/orm/DatabaseRuntimeRegistration.js +57 -0
- package/src/orm/Model.d.ts +3 -9
- package/src/orm/Model.d.ts.map +1 -1
- package/src/orm/Model.js +9 -7
- package/src/templates/project/basic/config/broadcast.ts.tpl +83 -13
- package/src/templates/project/basic/config/cache.ts.tpl +35 -11
- package/src/templates/project/basic/config/database.ts.tpl +70 -51
- package/src/templates/project/basic/config/index.ts.tpl +6 -0
- package/src/templates/project/basic/config/mail.ts.tpl +34 -11
- package/src/templates/project/basic/config/notification.ts.tpl +110 -25
- package/src/templates/project/basic/config/storage.ts.tpl +37 -19
- package/src/templates/project/basic/config/type.ts.tpl +46 -33
- package/src/templates/project/basic/routes/health.ts.tpl +21 -11
- package/src/tools/broadcast/Broadcast.d.ts +5 -3
- package/src/tools/broadcast/Broadcast.d.ts.map +1 -1
- package/src/tools/broadcast/Broadcast.js +48 -24
- package/src/tools/broadcast/BroadcastRegistry.d.ts +15 -0
- package/src/tools/broadcast/BroadcastRegistry.d.ts.map +1 -0
- package/src/tools/broadcast/BroadcastRegistry.js +29 -0
- package/src/tools/broadcast/BroadcastRuntimeRegistration.d.ts +11 -0
- package/src/tools/broadcast/BroadcastRuntimeRegistration.d.ts.map +1 -0
- package/src/tools/broadcast/BroadcastRuntimeRegistration.js +23 -0
- package/src/tools/broadcast/index.d.ts +2 -0
- package/src/tools/broadcast/index.d.ts.map +1 -1
- package/src/tools/broadcast/index.js +2 -0
- package/src/tools/mail/Mail.d.ts +8 -0
- package/src/tools/mail/Mail.d.ts.map +1 -1
- package/src/tools/mail/Mail.js +15 -2
- package/src/tools/mail/MailDriverRegistry.d.ts +2 -0
- package/src/tools/mail/MailDriverRegistry.d.ts.map +1 -1
- package/src/tools/mail/MailDriverRegistry.js +4 -0
- package/src/tools/notification/Notification.d.ts +3 -0
- package/src/tools/notification/Notification.d.ts.map +1 -1
- package/src/tools/notification/Notification.js +3 -0
- package/src/tools/notification/NotificationChannelRegistry.d.ts +15 -0
- package/src/tools/notification/NotificationChannelRegistry.d.ts.map +1 -0
- package/src/tools/notification/NotificationChannelRegistry.js +36 -0
- package/src/tools/notification/NotificationRuntimeRegistration.d.ts +13 -0
- package/src/tools/notification/NotificationRuntimeRegistration.d.ts.map +1 -0
- package/src/tools/notification/NotificationRuntimeRegistration.js +25 -0
- package/src/tools/notification/Registry.d.ts.map +1 -1
- package/src/tools/notification/Registry.js +4 -0
- package/src/tools/notification/Service.d.ts +1 -0
- package/src/tools/notification/Service.d.ts.map +1 -1
- package/src/tools/notification/Service.js +77 -4
- package/src/tools/notification/drivers/SlackNotification.d.ts +8 -0
- package/src/tools/notification/drivers/SlackNotification.d.ts.map +1 -0
- package/src/tools/notification/drivers/SlackNotification.js +13 -0
- package/src/tools/notification/drivers/TwilioNotification.d.ts +8 -0
- package/src/tools/notification/drivers/TwilioNotification.d.ts.map +1 -0
- package/src/tools/notification/drivers/TwilioNotification.js +13 -0
- package/src/tools/queue/Queue.d.ts +1 -0
- package/src/tools/queue/Queue.d.ts.map +1 -1
- package/src/tools/queue/Queue.js +7 -1
- package/src/tools/queue/QueueRuntimeRegistration.d.ts +11 -0
- package/src/tools/queue/QueueRuntimeRegistration.d.ts.map +1 -0
- package/src/tools/queue/QueueRuntimeRegistration.js +25 -0
- package/src/tools/storage/StorageDiskRegistry.d.ts +14 -0
- package/src/tools/storage/StorageDiskRegistry.d.ts.map +1 -0
- package/src/tools/storage/StorageDiskRegistry.js +36 -0
- package/src/tools/storage/StorageRuntimeRegistration.d.ts +10 -0
- package/src/tools/storage/StorageRuntimeRegistration.d.ts.map +1 -0
- package/src/tools/storage/StorageRuntimeRegistration.js +21 -0
- package/src/tools/storage/index.d.ts +1 -0
- package/src/tools/storage/index.d.ts.map +1 -1
- package/src/tools/storage/index.js +40 -6
|
@@ -4,25 +4,49 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import { CacheConfigInput, CacheDriverConfig
|
|
7
|
+
import { Env } from '@config/env';
|
|
8
|
+
import { CacheConfigInput, CacheDriverConfig } from '@config/type';
|
|
9
|
+
import { ErrorFactory } from '@zintrust/core';
|
|
9
10
|
|
|
10
|
-
const getCacheDriver = (config: CacheConfigInput): CacheDriverConfig => {
|
|
11
|
-
const
|
|
11
|
+
const getCacheDriver = (config: CacheConfigInput, name?: string): CacheDriverConfig => {
|
|
12
|
+
const selected = String(name ?? config.default).trim();
|
|
13
|
+
const storeName = selected === 'default' ? String(config.default).trim() : selected;
|
|
14
|
+
const isExplicitSelection =
|
|
15
|
+
name !== undefined && String(name).trim().length > 0 && String(name).trim() !== 'default';
|
|
12
16
|
|
|
13
|
-
if (Object.hasOwn(config.drivers,
|
|
14
|
-
const
|
|
15
|
-
return
|
|
17
|
+
if (storeName.length > 0 && Object.hasOwn(config.drivers, storeName)) {
|
|
18
|
+
const resolved = (config.drivers as Record<string, CacheDriverConfig>)[storeName];
|
|
19
|
+
if (resolved !== undefined) return resolved;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
if (isExplicitSelection) {
|
|
23
|
+
throw ErrorFactory.createConfigError(`Cache store not configured: ${storeName}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (Object.keys(config.drivers ?? {}).length === 0) {
|
|
27
|
+
throw ErrorFactory.createConfigError('No cache stores are configured');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
throw ErrorFactory.createConfigError(
|
|
31
|
+
`Cache default store not configured: ${storeName || '<empty>'}`
|
|
32
|
+
);
|
|
19
33
|
};
|
|
20
34
|
|
|
21
35
|
const cacheConfigObj = {
|
|
22
36
|
/**
|
|
23
37
|
* Default cache driver
|
|
24
38
|
*/
|
|
25
|
-
default:
|
|
39
|
+
default: (() => {
|
|
40
|
+
const envConnection = Env.get('CACHE_CONNECTION', '').trim();
|
|
41
|
+
|
|
42
|
+
const envDriver =
|
|
43
|
+
typeof (Env as unknown as { CACHE_DRIVER?: unknown }).CACHE_DRIVER === 'string'
|
|
44
|
+
? String((Env as unknown as { CACHE_DRIVER?: unknown }).CACHE_DRIVER)
|
|
45
|
+
: Env.get('CACHE_DRIVER', 'memory');
|
|
46
|
+
|
|
47
|
+
const selected = envConnection.length > 0 ? envConnection : String(envDriver ?? 'memory');
|
|
48
|
+
return selected.trim().toLowerCase();
|
|
49
|
+
})(),
|
|
26
50
|
|
|
27
51
|
/**
|
|
28
52
|
* Cache drivers
|
|
@@ -57,8 +81,8 @@ const cacheConfigObj = {
|
|
|
57
81
|
/**
|
|
58
82
|
* Get cache driver config
|
|
59
83
|
*/
|
|
60
|
-
getDriver(): CacheDriverConfig {
|
|
61
|
-
return getCacheDriver(this);
|
|
84
|
+
getDriver(name?: string): CacheDriverConfig {
|
|
85
|
+
return getCacheDriver(this, name);
|
|
62
86
|
},
|
|
63
87
|
|
|
64
88
|
/**
|
|
@@ -4,72 +4,91 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const isDatabaseConnectionName = (value: string): value is DatabaseConnectionName => {
|
|
15
|
-
return value === 'sqlite' || value === 'postgresql' || value === 'mysql';
|
|
7
|
+
import { Env } from '@config/env';
|
|
8
|
+
import { DatabaseConfigShape, DatabaseConnectionConfig, DatabaseConnections } from '@config/type';
|
|
9
|
+
import { ErrorFactory } from '@zintrust/core';
|
|
10
|
+
|
|
11
|
+
const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
|
|
12
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
16
13
|
};
|
|
17
14
|
|
|
18
|
-
const getDefaultConnection = ():
|
|
19
|
-
const
|
|
20
|
-
|
|
15
|
+
const getDefaultConnection = (connections: DatabaseConnections): string => {
|
|
16
|
+
const envSelectedRaw = Env.get('DB_CONNECTION', '');
|
|
17
|
+
const value = String(envSelectedRaw ?? '').trim();
|
|
18
|
+
|
|
19
|
+
if (value.length > 0 && hasOwn(connections, value)) return value;
|
|
20
|
+
|
|
21
|
+
if (envSelectedRaw.trim().length > 0) {
|
|
22
|
+
throw ErrorFactory.createConfigError(`Database connection not configured: ${value}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return hasOwn(connections, 'sqlite') ? 'sqlite' : (Object.keys(connections)[0] ?? 'sqlite');
|
|
21
26
|
};
|
|
22
27
|
|
|
23
28
|
const getDatabaseConnection = (config: DatabaseConfigShape): DatabaseConnectionConfig => {
|
|
24
|
-
const connName
|
|
25
|
-
|
|
29
|
+
const connName = config.default;
|
|
30
|
+
const resolved = config.connections[connName];
|
|
31
|
+
if (resolved !== undefined) return resolved;
|
|
32
|
+
|
|
33
|
+
// Backwards-compatible fallback.
|
|
34
|
+
const sqliteFallback = config.connections['sqlite'];
|
|
35
|
+
if (sqliteFallback !== undefined) return sqliteFallback;
|
|
36
|
+
|
|
37
|
+
const first = Object.values(config.connections)[0];
|
|
38
|
+
if (first !== undefined) return first;
|
|
39
|
+
|
|
40
|
+
throw ErrorFactory.createConfigError(
|
|
41
|
+
`No database connections are configured (default='${connName}').`
|
|
42
|
+
);
|
|
26
43
|
};
|
|
27
44
|
|
|
45
|
+
const connections = {
|
|
46
|
+
sqlite: {
|
|
47
|
+
driver: 'sqlite' as const,
|
|
48
|
+
database: Env.DB_DATABASE,
|
|
49
|
+
migrations: 'database/migrations',
|
|
50
|
+
},
|
|
51
|
+
postgresql: {
|
|
52
|
+
driver: 'postgresql' as const,
|
|
53
|
+
host: Env.DB_HOST,
|
|
54
|
+
port: Env.DB_PORT,
|
|
55
|
+
database: Env.DB_DATABASE,
|
|
56
|
+
username: Env.DB_USERNAME,
|
|
57
|
+
password: Env.DB_PASSWORD,
|
|
58
|
+
ssl: Env.getBool('DB_SSL', false),
|
|
59
|
+
pooling: {
|
|
60
|
+
enabled: Env.getBool('DB_POOLING', true),
|
|
61
|
+
min: Env.getInt('DB_POOL_MIN', 5),
|
|
62
|
+
max: Env.getInt('DB_POOL_MAX', 20),
|
|
63
|
+
idleTimeout: Env.getInt('DB_IDLE_TIMEOUT', 30000),
|
|
64
|
+
connectionTimeout: Env.getInt('DB_CONNECTION_TIMEOUT', 10000),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
mysql: {
|
|
68
|
+
driver: 'mysql' as const,
|
|
69
|
+
host: Env.DB_HOST,
|
|
70
|
+
port: Env.DB_PORT,
|
|
71
|
+
database: Env.DB_DATABASE,
|
|
72
|
+
username: Env.DB_USERNAME,
|
|
73
|
+
password: Env.DB_PASSWORD,
|
|
74
|
+
pooling: {
|
|
75
|
+
enabled: Env.getBool('DB_POOLING', true),
|
|
76
|
+
min: Env.getInt('DB_POOL_MIN', 5),
|
|
77
|
+
max: Env.getInt('DB_POOL_MAX', 20),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
} satisfies DatabaseConnections;
|
|
81
|
+
|
|
28
82
|
const databaseConfigObj = {
|
|
29
83
|
/**
|
|
30
84
|
* Default database connection
|
|
31
85
|
*/
|
|
32
|
-
default: getDefaultConnection(),
|
|
86
|
+
default: getDefaultConnection(connections),
|
|
33
87
|
|
|
34
88
|
/**
|
|
35
89
|
* Database connections
|
|
36
90
|
*/
|
|
37
|
-
connections
|
|
38
|
-
sqlite: {
|
|
39
|
-
driver: 'sqlite' as const,
|
|
40
|
-
database: Env.DB_DATABASE,
|
|
41
|
-
migrations: 'database/migrations',
|
|
42
|
-
},
|
|
43
|
-
postgresql: {
|
|
44
|
-
driver: 'postgresql' as const,
|
|
45
|
-
host: Env.DB_HOST,
|
|
46
|
-
port: Env.DB_PORT,
|
|
47
|
-
database: Env.DB_DATABASE,
|
|
48
|
-
username: Env.DB_USERNAME,
|
|
49
|
-
password: Env.DB_PASSWORD,
|
|
50
|
-
ssl: Env.getBool('DB_SSL', false),
|
|
51
|
-
pooling: {
|
|
52
|
-
enabled: Env.getBool('DB_POOLING', true),
|
|
53
|
-
min: Env.getInt('DB_POOL_MIN', 5),
|
|
54
|
-
max: Env.getInt('DB_POOL_MAX', 20),
|
|
55
|
-
idleTimeout: Env.getInt('DB_IDLE_TIMEOUT', 30000),
|
|
56
|
-
connectionTimeout: Env.getInt('DB_CONNECTION_TIMEOUT', 10000),
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
mysql: {
|
|
60
|
-
driver: 'mysql' as const,
|
|
61
|
-
host: Env.DB_HOST,
|
|
62
|
-
port: Env.DB_PORT,
|
|
63
|
-
database: Env.DB_DATABASE,
|
|
64
|
-
username: Env.DB_USERNAME,
|
|
65
|
-
password: Env.DB_PASSWORD,
|
|
66
|
-
pooling: {
|
|
67
|
-
enabled: Env.getBool('DB_POOLING', true),
|
|
68
|
-
min: Env.getInt('DB_POOL_MIN', 5),
|
|
69
|
-
max: Env.getInt('DB_POOL_MAX', 20),
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
},
|
|
91
|
+
connections,
|
|
73
92
|
|
|
74
93
|
/**
|
|
75
94
|
* Get current connection config
|
|
@@ -4,19 +4,23 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { appConfig } from './app';
|
|
7
|
+
import broadcastConfig from './broadcast';
|
|
7
8
|
import { cacheConfig } from './cache';
|
|
8
9
|
import { databaseConfig } from './database';
|
|
9
10
|
import { microservicesConfig } from './microservices';
|
|
10
11
|
import { middlewareConfig } from './middleware';
|
|
12
|
+
import notificationConfig from './notification';
|
|
11
13
|
import { queueConfig } from './queue';
|
|
12
14
|
import { securityConfig } from './security';
|
|
13
15
|
import { storageConfig } from './storage';
|
|
14
16
|
|
|
15
17
|
export { appConfig, type AppConfig } from './app';
|
|
18
|
+
export { default as broadcastConfig } from './broadcast';
|
|
16
19
|
export { cacheConfig, type CacheConfig } from './cache';
|
|
17
20
|
export { databaseConfig, type DatabaseConfig } from './database';
|
|
18
21
|
export { microservicesConfig, type MicroservicesConfig } from './microservices';
|
|
19
22
|
export { middlewareConfig } from './middleware';
|
|
23
|
+
export { notificationConfig, type NotificationConfig } from './notification';
|
|
20
24
|
export { queueConfig, type QueueConfig } from './queue';
|
|
21
25
|
export { securityConfig } from './security';
|
|
22
26
|
export { storageConfig, type StorageConfig } from './storage';
|
|
@@ -27,8 +31,10 @@ export { storageConfig, type StorageConfig } from './storage';
|
|
|
27
31
|
*/
|
|
28
32
|
export const config = Object.freeze({
|
|
29
33
|
app: appConfig,
|
|
34
|
+
broadcast: broadcastConfig,
|
|
30
35
|
database: databaseConfig,
|
|
31
36
|
storage: storageConfig,
|
|
37
|
+
notification: notificationConfig,
|
|
32
38
|
security: securityConfig,
|
|
33
39
|
middleware: middlewareConfig,
|
|
34
40
|
microservices: microservicesConfig,
|
|
@@ -4,25 +4,48 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import type { MailConfigInput, MailDriverConfig
|
|
7
|
+
import { Env } from '@config/env';
|
|
8
|
+
import type { MailConfigInput, MailDriverConfig } from '@config/type';
|
|
9
|
+
import { ErrorFactory } from '@zintrust/core';
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
|
|
11
|
+
const isMailDriverConfig = (value: unknown): value is MailDriverConfig => {
|
|
12
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
13
|
+
if (!('driver' in value)) return false;
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const driver = (value as { driver?: unknown }).driver;
|
|
16
|
+
return typeof driver === 'string' && driver.trim().length > 0;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getMailDriver = (config: MailConfigInput, name?: string): MailDriverConfig => {
|
|
20
|
+
const drivers = config.drivers as Record<string, unknown>;
|
|
21
|
+
const envSelectedRaw = Env.get('MAIL_CONNECTION', Env.get('MAIL_DRIVER', '')).trim();
|
|
22
|
+
const selected = (
|
|
23
|
+
name ??
|
|
24
|
+
(envSelectedRaw.length > 0 ? envSelectedRaw : undefined) ??
|
|
25
|
+
config.default
|
|
26
|
+
)
|
|
27
|
+
.toString()
|
|
28
|
+
.trim();
|
|
29
|
+
|
|
30
|
+
if (selected.length === 0) {
|
|
31
|
+
const disabled = drivers['disabled'];
|
|
32
|
+
if (isMailDriverConfig(disabled)) return disabled;
|
|
33
|
+
throw ErrorFactory.createConfigError('Mail driver not configured: disabled');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (Object.hasOwn(drivers, selected)) {
|
|
37
|
+
const resolved = drivers[selected];
|
|
38
|
+
if (isMailDriverConfig(resolved)) return resolved;
|
|
16
39
|
}
|
|
17
40
|
|
|
18
|
-
|
|
41
|
+
throw ErrorFactory.createConfigError(`Mail driver not configured: ${selected}`);
|
|
19
42
|
};
|
|
20
43
|
|
|
21
44
|
const mailConfigObj = {
|
|
22
45
|
/**
|
|
23
46
|
* Default mail driver
|
|
24
47
|
*/
|
|
25
|
-
default: (Env.get('MAIL_DRIVER', 'disabled')
|
|
48
|
+
default: Env.get('MAIL_CONNECTION', Env.get('MAIL_DRIVER', 'disabled')).trim().toLowerCase(),
|
|
26
49
|
|
|
27
50
|
/**
|
|
28
51
|
* Default "From" identity
|
|
@@ -91,8 +114,8 @@ const mailConfigObj = {
|
|
|
91
114
|
/**
|
|
92
115
|
* Get selected driver config
|
|
93
116
|
*/
|
|
94
|
-
getDriver(): MailDriverConfig {
|
|
95
|
-
return getMailDriver(this);
|
|
117
|
+
getDriver(name?: string): MailDriverConfig {
|
|
118
|
+
return getMailDriver(this, name);
|
|
96
119
|
},
|
|
97
120
|
} as const;
|
|
98
121
|
|
|
@@ -1,51 +1,136 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Notification Configuration
|
|
3
3
|
*
|
|
4
|
-
* Config-first mapping of notification providers.
|
|
5
|
-
*
|
|
4
|
+
* Config-first mapping of notification providers/channels.
|
|
5
|
+
* Driver selection must be dynamic (tests may mutate process.env).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Env } from '
|
|
9
|
-
import type {
|
|
8
|
+
import { Env } from '@config/env';
|
|
9
|
+
import type {
|
|
10
|
+
KnownNotificationDriverConfig,
|
|
11
|
+
NotificationConfigInput,
|
|
12
|
+
NotificationDrivers,
|
|
13
|
+
NotificationProviders,
|
|
14
|
+
} from '@config/type';
|
|
15
|
+
import { ErrorFactory } from '@zintrust/core';
|
|
10
16
|
|
|
11
|
-
const
|
|
12
|
-
/**
|
|
13
|
-
* Normalized notification driver name.
|
|
14
|
-
*
|
|
15
|
-
* NOTE: This intentionally supports custom driver names (e.g. project-specific drivers),
|
|
16
|
-
* so it returns a string rather than a strict union.
|
|
17
|
-
*/
|
|
18
|
-
getDriverName(): string {
|
|
19
|
-
return Env.get('NOTIFICATION_DRIVER', 'console').trim().toLowerCase();
|
|
20
|
-
},
|
|
17
|
+
const normalizeName = (value: string): string => value.trim().toLowerCase();
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
|
|
20
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getDefaultChannel = (drivers: NotificationDrivers): string => {
|
|
24
|
+
const envSelectedRaw = Env.get(
|
|
25
|
+
'NOTIFICATION_CONNECTION',
|
|
26
|
+
Env.get('NOTIFICATION_DRIVER', 'console')
|
|
27
|
+
);
|
|
28
|
+
const value = normalizeName(envSelectedRaw ?? 'console');
|
|
29
|
+
|
|
30
|
+
if (value.length > 0 && hasOwn(drivers, value)) return value;
|
|
31
|
+
|
|
32
|
+
if (envSelectedRaw.trim().length > 0) {
|
|
33
|
+
throw ErrorFactory.createConfigError(`Notification channel not configured: ${value}`);
|
|
34
|
+
}
|
|
29
35
|
|
|
36
|
+
return hasOwn(drivers, 'console') ? 'console' : (Object.keys(drivers)[0] ?? 'console');
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const getNotificationDriver = (
|
|
40
|
+
config: NotificationConfigInput,
|
|
41
|
+
name?: string
|
|
42
|
+
): KnownNotificationDriverConfig => {
|
|
43
|
+
const selected = normalizeName(String(name ?? config.default));
|
|
44
|
+
const channelName = selected === 'default' ? normalizeName(config.default) : selected;
|
|
45
|
+
|
|
46
|
+
const isExplicitSelection =
|
|
47
|
+
name !== undefined &&
|
|
48
|
+
String(name).trim().length > 0 &&
|
|
49
|
+
normalizeName(String(name)) !== 'default';
|
|
50
|
+
|
|
51
|
+
if (channelName.length > 0 && hasOwn(config.drivers, channelName)) {
|
|
52
|
+
const resolved = config.drivers[channelName];
|
|
53
|
+
if (resolved !== undefined) return resolved;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (Object.keys(config.drivers ?? {}).length === 0) {
|
|
57
|
+
throw ErrorFactory.createConfigError('No notification channels are configured');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isExplicitSelection) {
|
|
61
|
+
throw ErrorFactory.createConfigError(`Notification channel not configured: ${channelName}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Default selection is strict: if `default` points at an unconfigured channel, throw.
|
|
65
|
+
throw ErrorFactory.createConfigError(`Notification channel not configured: ${channelName}`);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const getBaseProviders = (): NotificationProviders => {
|
|
69
|
+
return {
|
|
70
|
+
console: { driver: 'console' as const },
|
|
30
71
|
termii: {
|
|
31
72
|
driver: 'termii' as const,
|
|
32
73
|
apiKey: Env.get('TERMII_API_KEY', ''),
|
|
33
74
|
sender: Env.get('TERMII_SENDER', 'Zintrust'),
|
|
34
75
|
endpoint: Env.get('TERMII_ENDPOINT', 'https://api.termii.com/sms/send'),
|
|
35
76
|
},
|
|
36
|
-
|
|
37
77
|
twilio: {
|
|
38
78
|
driver: 'twilio' as const,
|
|
39
79
|
accountSid: Env.get('TWILIO_ACCOUNT_SID', ''),
|
|
40
80
|
authToken: Env.get('TWILIO_AUTH_TOKEN', ''),
|
|
41
81
|
fromNumber: Env.get('TWILIO_FROM_NUMBER', ''),
|
|
42
82
|
},
|
|
43
|
-
|
|
44
83
|
slack: {
|
|
45
84
|
driver: 'slack' as const,
|
|
46
85
|
webhookUrl: Env.get('SLACK_WEBHOOK_URL', ''),
|
|
47
86
|
},
|
|
48
|
-
}
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const notificationConfigObj = {
|
|
91
|
+
/**
|
|
92
|
+
* Default notification channel name (normalized).
|
|
93
|
+
*/
|
|
94
|
+
get default(): string {
|
|
95
|
+
return getDefaultChannel(this.drivers);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Notification channels.
|
|
100
|
+
*
|
|
101
|
+
* You may add custom named channels (e.g. `opsSlack`, `smsMarketing`) that
|
|
102
|
+
* point to any known driver config.
|
|
103
|
+
*/
|
|
104
|
+
get drivers(): NotificationDrivers {
|
|
105
|
+
// Return a record of channels; can be extended by app-level config.
|
|
106
|
+
return getBaseProviders() as unknown as NotificationDrivers;
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Legacy provider configs (kept for backwards compatibility with wrappers).
|
|
111
|
+
*/
|
|
112
|
+
get providers(): NotificationProviders {
|
|
113
|
+
return getBaseProviders();
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Normalized notification channel name.
|
|
118
|
+
*/
|
|
119
|
+
getDriverName(): string {
|
|
120
|
+
return normalizeName(this.default);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Resolve a channel config.
|
|
125
|
+
* - Unknown names throw when explicitly selected.
|
|
126
|
+
* - `default` is a reserved alias of the configured default.
|
|
127
|
+
*/
|
|
128
|
+
getDriverConfig(name?: string): KnownNotificationDriverConfig {
|
|
129
|
+
return getNotificationDriver(this, name);
|
|
130
|
+
},
|
|
49
131
|
} as const;
|
|
50
132
|
|
|
51
|
-
export
|
|
133
|
+
export const notificationConfig = Object.freeze(notificationConfigObj);
|
|
134
|
+
export type NotificationConfig = typeof notificationConfig;
|
|
135
|
+
|
|
136
|
+
export default notificationConfig;
|
|
@@ -4,29 +4,36 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import type {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from './type';
|
|
14
|
-
|
|
15
|
-
const isStorageDriverName = (
|
|
16
|
-
value: string,
|
|
17
|
-
drivers: StorageDrivers
|
|
18
|
-
): value is StorageDriverName => {
|
|
19
|
-
return value in drivers;
|
|
7
|
+
import { Env } from '@config/env';
|
|
8
|
+
import type { StorageConfigRuntime, StorageDriverConfig, StorageDrivers } from '@config/type';
|
|
9
|
+
import { ErrorFactory } from '@zintrust/core';
|
|
10
|
+
|
|
11
|
+
const hasOwn = <T extends object>(obj: T, key: PropertyKey): key is keyof T => {
|
|
12
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
20
13
|
};
|
|
21
14
|
|
|
22
|
-
const getStorageDriver = (config: StorageConfigRuntime): StorageDriverConfig => {
|
|
23
|
-
const
|
|
15
|
+
const getStorageDriver = (config: StorageConfigRuntime, name?: string): StorageDriverConfig => {
|
|
16
|
+
const selected = String(name ?? config.default).trim();
|
|
17
|
+
const diskName = selected === 'default' ? String(config.default).trim() : selected;
|
|
18
|
+
const isExplicitSelection =
|
|
19
|
+
name !== undefined && String(name).trim().length > 0 && String(name).trim() !== 'default';
|
|
20
|
+
|
|
21
|
+
if (diskName !== '' && hasOwn(config.drivers, diskName)) {
|
|
22
|
+
const resolved = config.drivers[diskName];
|
|
23
|
+
if (resolved !== undefined) return resolved;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (isExplicitSelection) {
|
|
27
|
+
throw ErrorFactory.createConfigError(`Storage disk not configured: ${diskName}`);
|
|
28
|
+
}
|
|
24
29
|
|
|
25
|
-
if (
|
|
26
|
-
|
|
30
|
+
if (Object.keys(config.drivers ?? {}).length === 0) {
|
|
31
|
+
throw ErrorFactory.createConfigError('No storage disks are configured');
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
throw ErrorFactory.createConfigError(
|
|
35
|
+
`Storage default disk not configured: ${diskName || '<empty>'}`
|
|
36
|
+
);
|
|
30
37
|
};
|
|
31
38
|
|
|
32
39
|
const getDrivers = (): StorageDrivers => ({
|
|
@@ -69,7 +76,7 @@ const storageConfigObj = {
|
|
|
69
76
|
* Default storage driver (dynamic; tests may mutate process.env)
|
|
70
77
|
*/
|
|
71
78
|
get default(): string {
|
|
72
|
-
return Env.get('STORAGE_DRIVER', 'local');
|
|
79
|
+
return Env.get('STORAGE_CONNECTION', Env.get('STORAGE_DRIVER', 'local')).trim().toLowerCase();
|
|
73
80
|
},
|
|
74
81
|
|
|
75
82
|
/**
|
|
@@ -86,6 +93,17 @@ const storageConfigObj = {
|
|
|
86
93
|
return getStorageDriver(this);
|
|
87
94
|
},
|
|
88
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Get a storage disk configuration by name.
|
|
98
|
+
*
|
|
99
|
+
* - When `name` is provided and not configured, this throws.
|
|
100
|
+
* - When `name` is omitted, it resolves the configured default with a backwards-compatible fallback.
|
|
101
|
+
* - Reserved name `default` aliases the configured default.
|
|
102
|
+
*/
|
|
103
|
+
getDriverConfig(this: StorageConfigRuntime, name?: string): StorageDriverConfig {
|
|
104
|
+
return getStorageDriver(this, name);
|
|
105
|
+
},
|
|
106
|
+
|
|
89
107
|
/**
|
|
90
108
|
* Temporary file settings
|
|
91
109
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Env } from '
|
|
2
|
-
import type { Middleware as MiddlewareFn } from '
|
|
1
|
+
import { Env } from '@config/env';
|
|
2
|
+
import type { Middleware as MiddlewareFn } from '@middleware/MiddlewareStack';
|
|
3
3
|
|
|
4
4
|
export type Environment =
|
|
5
5
|
| 'development'
|
|
@@ -50,15 +50,15 @@ export type GcsStorageDriverConfig = {
|
|
|
50
50
|
url: EnvGetValue;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
export type
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
export type StorageDriverConfig =
|
|
54
|
+
| LocalStorageDriverConfig
|
|
55
|
+
| S3StorageDriverConfig
|
|
56
|
+
| R2StorageDriverConfig
|
|
57
|
+
| GcsStorageDriverConfig;
|
|
58
|
+
|
|
59
|
+
export type StorageDrivers = Record<string, StorageDriverConfig>;
|
|
59
60
|
|
|
60
61
|
export type StorageDriverName = keyof StorageDrivers;
|
|
61
|
-
export type StorageDriverConfig = StorageDrivers[StorageDriverName];
|
|
62
62
|
|
|
63
63
|
export type StorageConfigRuntime = {
|
|
64
64
|
readonly default: string;
|
|
@@ -194,6 +194,13 @@ export type KnownNotificationDriverConfig =
|
|
|
194
194
|
| TwilioNotificationDriverConfig
|
|
195
195
|
| SlackNotificationDriverConfig;
|
|
196
196
|
|
|
197
|
+
export type NotificationDrivers = Record<string, KnownNotificationDriverConfig>;
|
|
198
|
+
|
|
199
|
+
export type NotificationConfigInput = {
|
|
200
|
+
default: string;
|
|
201
|
+
drivers: NotificationDrivers;
|
|
202
|
+
};
|
|
203
|
+
|
|
197
204
|
export type NotificationProviders = {
|
|
198
205
|
console: ConsoleNotificationDriverConfig;
|
|
199
206
|
termii: TermiiNotificationDriverConfig;
|
|
@@ -256,17 +263,16 @@ export type MailDriverConfig =
|
|
|
256
263
|
| NodemailerMailDriverConfig
|
|
257
264
|
| SesMailDriverConfig;
|
|
258
265
|
|
|
259
|
-
export type MailDrivers =
|
|
260
|
-
disabled: DisabledMailDriverConfig;
|
|
261
|
-
sendgrid: SendGridMailDriverConfig;
|
|
262
|
-
mailgun: MailgunMailDriverConfig;
|
|
263
|
-
smtp: SmtpMailDriverConfig;
|
|
264
|
-
nodemailer: NodemailerMailDriverConfig;
|
|
265
|
-
ses: SesMailDriverConfig;
|
|
266
|
-
};
|
|
266
|
+
export type MailDrivers = Record<string, MailDriverConfig>;
|
|
267
267
|
|
|
268
268
|
export type MailConfigInput = {
|
|
269
|
-
|
|
269
|
+
/**
|
|
270
|
+
* Default mailer key name.
|
|
271
|
+
*
|
|
272
|
+
* This is intentionally a string to support named mailers (e.g. 'transactional', 'marketing').
|
|
273
|
+
* The underlying driver is selected by the `driver` field within each mailer config.
|
|
274
|
+
*/
|
|
275
|
+
default: string;
|
|
270
276
|
from: {
|
|
271
277
|
address: string;
|
|
272
278
|
name: string;
|
|
@@ -317,17 +323,23 @@ export type MysqlConnectionConfig = {
|
|
|
317
323
|
};
|
|
318
324
|
};
|
|
319
325
|
|
|
320
|
-
export type
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
};
|
|
326
|
+
export type DatabaseConnectionConfig =
|
|
327
|
+
| SqliteConnectionConfig
|
|
328
|
+
| PostgresqlConnectionConfig
|
|
329
|
+
| MysqlConnectionConfig;
|
|
325
330
|
|
|
326
|
-
|
|
327
|
-
|
|
331
|
+
/**
|
|
332
|
+
* Named database connection configs.
|
|
333
|
+
*
|
|
334
|
+
* Keys represent connection names (e.g. 'default', 'auth', 'tasks', 'db1').
|
|
335
|
+
*/
|
|
336
|
+
export type DatabaseConnections = Record<string, DatabaseConnectionConfig>;
|
|
328
337
|
|
|
338
|
+
/**
|
|
339
|
+
* Default connection name.
|
|
340
|
+
*/
|
|
329
341
|
export type DatabaseConfigShape = {
|
|
330
|
-
default:
|
|
342
|
+
default: string;
|
|
331
343
|
connections: DatabaseConnections;
|
|
332
344
|
};
|
|
333
345
|
|
|
@@ -382,13 +394,7 @@ export type CacheDriverConfig =
|
|
|
382
394
|
| KvCacheDriverConfig
|
|
383
395
|
| KvRemoteCacheDriverConfig;
|
|
384
396
|
|
|
385
|
-
export type CacheDrivers =
|
|
386
|
-
memory: MemoryCacheDriverConfig;
|
|
387
|
-
redis: RedisCacheDriverConfig;
|
|
388
|
-
mongodb: MongoCacheDriverConfig;
|
|
389
|
-
kv: KvCacheDriverConfig;
|
|
390
|
-
'kv-remote': KvRemoteCacheDriverConfig;
|
|
391
|
-
};
|
|
397
|
+
export type CacheDrivers = Record<string, CacheDriverConfig>;
|
|
392
398
|
|
|
393
399
|
export type CacheConfigInput = {
|
|
394
400
|
default: string;
|
|
@@ -430,3 +436,10 @@ export type KnownBroadcastDriverConfig =
|
|
|
430
436
|
| PusherBroadcastDriverConfig
|
|
431
437
|
| RedisBroadcastDriverConfig
|
|
432
438
|
| RedisHttpsBroadcastDriverConfig;
|
|
439
|
+
|
|
440
|
+
export type BroadcastDrivers = Record<string, KnownBroadcastDriverConfig>;
|
|
441
|
+
|
|
442
|
+
export type BroadcastConfigInput = {
|
|
443
|
+
default: string;
|
|
444
|
+
drivers: BroadcastDrivers;
|
|
445
|
+
};
|