@zintrust/core 0.1.12 → 0.1.14
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 +142 -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 +60 -12
- package/src/config/cache.d.ts +1 -1
- package/src/config/cache.d.ts.map +1 -1
- package/src/config/cache.js +19 -8
- package/src/config/database.d.ts +1 -1
- package/src/config/database.d.ts.map +1 -1
- package/src/config/database.js +59 -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 +24 -9
- package/src/config/notification.d.ts +25 -29
- package/src/config/notification.d.ts.map +1 -1
- package/src/config/notification.js +75 -20
- package/src/config/storage.d.ts +8 -0
- package/src/config/storage.d.ts.map +1 -1
- package/src/config/storage.js +29 -7
- package/src/config/type.d.ts +32 -31
- package/src/config/type.d.ts.map +1 -1
- package/src/index.d.ts +16 -3
- package/src/index.d.ts.map +1 -1
- package/src/index.js +16 -3
- package/src/node.d.ts +6 -4
- package/src/node.d.ts.map +1 -1
- package/src/node.js +6 -4
- 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 +55 -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 +79 -11
- package/src/templates/project/basic/config/cache.ts.tpl +21 -9
- package/src/templates/project/basic/config/database.ts.tpl +67 -50
- package/src/templates/project/basic/config/index.ts.tpl +6 -0
- package/src/templates/project/basic/config/mail.ts.tpl +24 -11
- package/src/templates/project/basic/config/notification.ts.tpl +98 -25
- package/src/templates/project/basic/config/storage.ts.tpl +33 -17
- 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 +20 -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 +22 -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 +3 -0
- 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 +28 -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 +23 -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
|
@@ -7,14 +7,33 @@
|
|
|
7
7
|
|
|
8
8
|
import { Env } from './env';
|
|
9
9
|
import {
|
|
10
|
+
BroadcastConfigInput,
|
|
11
|
+
BroadcastDrivers,
|
|
12
|
+
InMemoryBroadcastDriverConfig,
|
|
10
13
|
KnownBroadcastDriverConfig,
|
|
11
14
|
PusherBroadcastDriverConfig,
|
|
12
15
|
RedisBroadcastDriverConfig,
|
|
13
16
|
RedisHttpsBroadcastDriverConfig,
|
|
14
17
|
} from './type';
|
|
18
|
+
import { ErrorFactory } from '@zintrust/core';
|
|
15
19
|
|
|
16
20
|
const normalizeDriverName = (value: string): string => value.trim().toLowerCase();
|
|
17
21
|
|
|
22
|
+
const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
|
|
23
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const getDefaultBroadcaster = (drivers: BroadcastDrivers): string => {
|
|
27
|
+
const value = normalizeDriverName(Env.get('BROADCAST_DRIVER', 'inmemory'));
|
|
28
|
+
|
|
29
|
+
if (value.length > 0 && hasOwn(drivers, value)) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Backwards-compatible default.
|
|
34
|
+
return hasOwn(drivers, 'inmemory') ? 'inmemory' : (Object.keys(drivers)[0] ?? 'inmemory');
|
|
35
|
+
};
|
|
36
|
+
|
|
18
37
|
const getPusherConfig = (): PusherBroadcastDriverConfig => ({
|
|
19
38
|
driver: 'pusher',
|
|
20
39
|
appId: Env.get('PUSHER_APP_ID', ''),
|
|
@@ -39,28 +58,77 @@ const getRedisHttpsConfig = (): RedisHttpsBroadcastDriverConfig => ({
|
|
|
39
58
|
channelPrefix: Env.get('BROADCAST_CHANNEL_PREFIX', 'broadcast:'),
|
|
40
59
|
});
|
|
41
60
|
|
|
61
|
+
const getBroadcastDriver = (
|
|
62
|
+
config: BroadcastConfigInput,
|
|
63
|
+
name?: string
|
|
64
|
+
): KnownBroadcastDriverConfig => {
|
|
65
|
+
const selected = normalizeDriverName(String(name ?? config.default));
|
|
66
|
+
const broadcasterName = selected === 'default' ? normalizeDriverName(config.default) : selected;
|
|
67
|
+
|
|
68
|
+
const isExplicitSelection =
|
|
69
|
+
name !== undefined &&
|
|
70
|
+
String(name).trim().length > 0 &&
|
|
71
|
+
normalizeDriverName(String(name)) !== 'default';
|
|
72
|
+
|
|
73
|
+
if (broadcasterName.length > 0 && hasOwn(config.drivers, broadcasterName)) {
|
|
74
|
+
const resolved = (config.drivers as Record<string, KnownBroadcastDriverConfig>)[
|
|
75
|
+
broadcasterName
|
|
76
|
+
];
|
|
77
|
+
if (resolved !== undefined) return resolved;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isExplicitSelection) {
|
|
81
|
+
throw ErrorFactory.createConfigError(`Broadcast driver not configured: ${broadcasterName}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const fallback = config.drivers['inmemory'] ?? Object.values(config.drivers)[0];
|
|
85
|
+
if (fallback !== undefined) return fallback;
|
|
86
|
+
|
|
87
|
+
throw ErrorFactory.createConfigError('No broadcast drivers are configured');
|
|
88
|
+
};
|
|
89
|
+
|
|
42
90
|
const broadcastConfigObj = {
|
|
43
91
|
/**
|
|
44
|
-
*
|
|
92
|
+
* Default broadcaster name (normalized).
|
|
93
|
+
*/
|
|
94
|
+
get default(): string {
|
|
95
|
+
return getDefaultBroadcaster(this.drivers);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Broadcast drivers.
|
|
45
100
|
*
|
|
46
|
-
*
|
|
101
|
+
* You may add custom named broadcasters (e.g. `ops`, `billing`) that point to any
|
|
102
|
+
* known driver config.
|
|
103
|
+
*/
|
|
104
|
+
drivers: {
|
|
105
|
+
get inmemory(): InMemoryBroadcastDriverConfig {
|
|
106
|
+
return { driver: 'inmemory' };
|
|
107
|
+
},
|
|
108
|
+
get pusher() {
|
|
109
|
+
return getPusherConfig();
|
|
110
|
+
},
|
|
111
|
+
get redis() {
|
|
112
|
+
return getRedisConfig();
|
|
113
|
+
},
|
|
114
|
+
get redishttps() {
|
|
115
|
+
return getRedisHttpsConfig();
|
|
116
|
+
},
|
|
117
|
+
} satisfies BroadcastDrivers,
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Normalized broadcast driver name for the default broadcaster.
|
|
47
121
|
*/
|
|
48
122
|
getDriverName(): string {
|
|
49
|
-
return normalizeDriverName(
|
|
123
|
+
return normalizeDriverName(this.default);
|
|
50
124
|
},
|
|
51
125
|
|
|
52
126
|
/**
|
|
53
127
|
* Get a config object for the currently selected driver.
|
|
54
128
|
* Defaults to inmemory for unknown/unsupported names.
|
|
55
129
|
*/
|
|
56
|
-
getDriverConfig(): KnownBroadcastDriverConfig {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (driver === 'pusher') return getPusherConfig();
|
|
60
|
-
if (driver === 'redis') return getRedisConfig();
|
|
61
|
-
if (driver === 'redishttps') return getRedisHttpsConfig();
|
|
62
|
-
|
|
63
|
-
return { driver: 'inmemory' };
|
|
130
|
+
getDriverConfig(name?: string): KnownBroadcastDriverConfig {
|
|
131
|
+
return getBroadcastDriver(this, name);
|
|
64
132
|
},
|
|
65
133
|
} as const;
|
|
66
134
|
|
|
@@ -5,17 +5,29 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Env } from './env';
|
|
8
|
-
import { CacheConfigInput, CacheDriverConfig
|
|
8
|
+
import { CacheConfigInput, CacheDriverConfig } from './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
|
+
// Backwards-compatible fallback.
|
|
27
|
+
const fallback = config.drivers['memory'] ?? Object.values(config.drivers)[0];
|
|
28
|
+
if (fallback !== undefined) return fallback;
|
|
29
|
+
|
|
30
|
+
throw ErrorFactory.createConfigError('No cache stores are configured');
|
|
19
31
|
};
|
|
20
32
|
|
|
21
33
|
const cacheConfigObj = {
|
|
@@ -57,8 +69,8 @@ const cacheConfigObj = {
|
|
|
57
69
|
/**
|
|
58
70
|
* Get cache driver config
|
|
59
71
|
*/
|
|
60
|
-
getDriver(): CacheDriverConfig {
|
|
61
|
-
return getCacheDriver(this);
|
|
72
|
+
getDriver(name?: string): CacheDriverConfig {
|
|
73
|
+
return getCacheDriver(this, name);
|
|
62
74
|
},
|
|
63
75
|
|
|
64
76
|
/**
|
|
@@ -5,71 +5,88 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Env } from './env';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const isDatabaseConnectionName = (value: string): value is DatabaseConnectionName => {
|
|
15
|
-
return value === 'sqlite' || value === 'postgresql' || value === 'mysql';
|
|
8
|
+
import { DatabaseConfigShape, DatabaseConnectionConfig, DatabaseConnections } from './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 value = Env.DB_CONNECTION;
|
|
20
|
-
|
|
15
|
+
const getDefaultConnection = (connections: DatabaseConnections): string => {
|
|
16
|
+
const value = String(Env.DB_CONNECTION || '').trim();
|
|
17
|
+
|
|
18
|
+
if (value.length > 0 && hasOwn(connections, value)) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Backwards-compatible default.
|
|
23
|
+
return hasOwn(connections, 'sqlite') ? 'sqlite' : (Object.keys(connections)[0] ?? 'sqlite');
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
const getDatabaseConnection = (config: DatabaseConfigShape): DatabaseConnectionConfig => {
|
|
24
|
-
const connName
|
|
25
|
-
|
|
27
|
+
const connName = config.default;
|
|
28
|
+
const resolved = config.connections[connName];
|
|
29
|
+
if (resolved !== undefined) return resolved;
|
|
30
|
+
|
|
31
|
+
// Backwards-compatible fallback.
|
|
32
|
+
const sqliteFallback = config.connections['sqlite'];
|
|
33
|
+
if (sqliteFallback !== undefined) return sqliteFallback;
|
|
34
|
+
|
|
35
|
+
const first = Object.values(config.connections)[0];
|
|
36
|
+
if (first !== undefined) return first;
|
|
37
|
+
|
|
38
|
+
throw ErrorFactory.createConfigError(
|
|
39
|
+
`No database connections are configured (default='${connName}').`
|
|
40
|
+
);
|
|
26
41
|
};
|
|
27
42
|
|
|
43
|
+
const connections = {
|
|
44
|
+
sqlite: {
|
|
45
|
+
driver: 'sqlite' as const,
|
|
46
|
+
database: Env.DB_DATABASE,
|
|
47
|
+
migrations: 'database/migrations',
|
|
48
|
+
},
|
|
49
|
+
postgresql: {
|
|
50
|
+
driver: 'postgresql' as const,
|
|
51
|
+
host: Env.DB_HOST,
|
|
52
|
+
port: Env.DB_PORT,
|
|
53
|
+
database: Env.DB_DATABASE,
|
|
54
|
+
username: Env.DB_USERNAME,
|
|
55
|
+
password: Env.DB_PASSWORD,
|
|
56
|
+
ssl: Env.getBool('DB_SSL', false),
|
|
57
|
+
pooling: {
|
|
58
|
+
enabled: Env.getBool('DB_POOLING', true),
|
|
59
|
+
min: Env.getInt('DB_POOL_MIN', 5),
|
|
60
|
+
max: Env.getInt('DB_POOL_MAX', 20),
|
|
61
|
+
idleTimeout: Env.getInt('DB_IDLE_TIMEOUT', 30000),
|
|
62
|
+
connectionTimeout: Env.getInt('DB_CONNECTION_TIMEOUT', 10000),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
mysql: {
|
|
66
|
+
driver: 'mysql' as const,
|
|
67
|
+
host: Env.DB_HOST,
|
|
68
|
+
port: Env.DB_PORT,
|
|
69
|
+
database: Env.DB_DATABASE,
|
|
70
|
+
username: Env.DB_USERNAME,
|
|
71
|
+
password: Env.DB_PASSWORD,
|
|
72
|
+
pooling: {
|
|
73
|
+
enabled: Env.getBool('DB_POOLING', true),
|
|
74
|
+
min: Env.getInt('DB_POOL_MIN', 5),
|
|
75
|
+
max: Env.getInt('DB_POOL_MAX', 20),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
} satisfies DatabaseConnections;
|
|
79
|
+
|
|
28
80
|
const databaseConfigObj = {
|
|
29
81
|
/**
|
|
30
82
|
* Default database connection
|
|
31
83
|
*/
|
|
32
|
-
default: getDefaultConnection(),
|
|
84
|
+
default: getDefaultConnection(connections),
|
|
33
85
|
|
|
34
86
|
/**
|
|
35
87
|
* Database connections
|
|
36
88
|
*/
|
|
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
|
-
},
|
|
89
|
+
connections,
|
|
73
90
|
|
|
74
91
|
/**
|
|
75
92
|
* 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,38 @@
|
|
|
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 getMailDriver = (config: MailConfigInput): MailDriverConfig => {
|
|
11
|
-
const
|
|
11
|
+
const getMailDriver = (config: MailConfigInput, name?: string): MailDriverConfig => {
|
|
12
|
+
const selected = (name ?? config.default).toString().trim();
|
|
13
|
+
if (selected.length === 0) {
|
|
14
|
+
const disabled = config.drivers['disabled'];
|
|
15
|
+
if (disabled !== undefined) return disabled;
|
|
16
|
+
throw ErrorFactory.createConfigError('Mail driver not configured: disabled');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (Object.hasOwn(config.drivers, selected)) {
|
|
20
|
+
const resolved = config.drivers[selected];
|
|
21
|
+
if (resolved !== undefined) return resolved;
|
|
22
|
+
}
|
|
12
23
|
|
|
13
|
-
if
|
|
14
|
-
|
|
15
|
-
|
|
24
|
+
// Backward-compatible fallback: if the default is misconfigured, treat mail as disabled.
|
|
25
|
+
if (name === undefined) {
|
|
26
|
+
const disabled = config.drivers['disabled'];
|
|
27
|
+
if (disabled !== undefined) return disabled;
|
|
28
|
+
throw ErrorFactory.createConfigError('Mail driver not configured: disabled');
|
|
16
29
|
}
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
throw ErrorFactory.createConfigError(`Mail driver not configured: ${selected}`);
|
|
19
32
|
};
|
|
20
33
|
|
|
21
34
|
const mailConfigObj = {
|
|
22
35
|
/**
|
|
23
36
|
* Default mail driver
|
|
24
37
|
*/
|
|
25
|
-
default:
|
|
38
|
+
default: Env.get('MAIL_DRIVER', 'disabled').trim().toLowerCase() || 'disabled',
|
|
26
39
|
|
|
27
40
|
/**
|
|
28
41
|
* Default "From" identity
|
|
@@ -91,8 +104,8 @@ const mailConfigObj = {
|
|
|
91
104
|
/**
|
|
92
105
|
* Get selected driver config
|
|
93
106
|
*/
|
|
94
|
-
getDriver(): MailDriverConfig {
|
|
95
|
-
return getMailDriver(this);
|
|
107
|
+
getDriver(name?: string): MailDriverConfig {
|
|
108
|
+
return getMailDriver(this, name);
|
|
96
109
|
},
|
|
97
110
|
} as const;
|
|
98
111
|
|
|
@@ -1,51 +1,124 @@
|
|
|
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 value = normalizeName(Env.get('NOTIFICATION_DRIVER', 'console'));
|
|
25
|
+
if (value.length > 0 && hasOwn(drivers, value)) return value;
|
|
26
|
+
return hasOwn(drivers, 'console') ? 'console' : (Object.keys(drivers)[0] ?? 'console');
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const getNotificationDriver = (
|
|
30
|
+
config: NotificationConfigInput,
|
|
31
|
+
name?: string
|
|
32
|
+
): KnownNotificationDriverConfig => {
|
|
33
|
+
const selected = normalizeName(String(name ?? config.default));
|
|
34
|
+
const channelName = selected === 'default' ? normalizeName(config.default) : selected;
|
|
29
35
|
|
|
36
|
+
const isExplicitSelection =
|
|
37
|
+
name !== undefined &&
|
|
38
|
+
String(name).trim().length > 0 &&
|
|
39
|
+
normalizeName(String(name)) !== 'default';
|
|
40
|
+
|
|
41
|
+
if (channelName.length > 0 && hasOwn(config.drivers, channelName)) {
|
|
42
|
+
const resolved = config.drivers[channelName];
|
|
43
|
+
if (resolved !== undefined) return resolved;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isExplicitSelection) {
|
|
47
|
+
throw ErrorFactory.createConfigError(`Notification channel not configured: ${channelName}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const fallback = config.drivers['console'] ?? Object.values(config.drivers)[0];
|
|
51
|
+
if (fallback !== undefined) return fallback;
|
|
52
|
+
|
|
53
|
+
throw ErrorFactory.createConfigError('No notification channels are configured');
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const getBaseProviders = (): NotificationProviders => {
|
|
57
|
+
return {
|
|
58
|
+
console: { driver: 'console' as const },
|
|
30
59
|
termii: {
|
|
31
60
|
driver: 'termii' as const,
|
|
32
61
|
apiKey: Env.get('TERMII_API_KEY', ''),
|
|
33
62
|
sender: Env.get('TERMII_SENDER', 'Zintrust'),
|
|
34
63
|
endpoint: Env.get('TERMII_ENDPOINT', 'https://api.termii.com/sms/send'),
|
|
35
64
|
},
|
|
36
|
-
|
|
37
65
|
twilio: {
|
|
38
66
|
driver: 'twilio' as const,
|
|
39
67
|
accountSid: Env.get('TWILIO_ACCOUNT_SID', ''),
|
|
40
68
|
authToken: Env.get('TWILIO_AUTH_TOKEN', ''),
|
|
41
69
|
fromNumber: Env.get('TWILIO_FROM_NUMBER', ''),
|
|
42
70
|
},
|
|
43
|
-
|
|
44
71
|
slack: {
|
|
45
72
|
driver: 'slack' as const,
|
|
46
73
|
webhookUrl: Env.get('SLACK_WEBHOOK_URL', ''),
|
|
47
74
|
},
|
|
48
|
-
}
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const notificationConfigObj = {
|
|
79
|
+
/**
|
|
80
|
+
* Default notification channel name (normalized).
|
|
81
|
+
*/
|
|
82
|
+
get default(): string {
|
|
83
|
+
return getDefaultChannel(this.drivers);
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Notification channels.
|
|
88
|
+
*
|
|
89
|
+
* You may add custom named channels (e.g. `opsSlack`, `smsMarketing`) that
|
|
90
|
+
* point to any known driver config.
|
|
91
|
+
*/
|
|
92
|
+
get drivers(): NotificationDrivers {
|
|
93
|
+
// Return a record of channels; can be extended by app-level config.
|
|
94
|
+
return getBaseProviders() as unknown as NotificationDrivers;
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Legacy provider configs (kept for backwards compatibility with wrappers).
|
|
99
|
+
*/
|
|
100
|
+
get providers(): NotificationProviders {
|
|
101
|
+
return getBaseProviders();
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Normalized notification channel name.
|
|
106
|
+
*/
|
|
107
|
+
getDriverName(): string {
|
|
108
|
+
return normalizeName(this.default);
|
|
109
|
+
},
|
|
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?: string): KnownNotificationDriverConfig {
|
|
117
|
+
return getNotificationDriver(this, name);
|
|
118
|
+
},
|
|
49
119
|
} as const;
|
|
50
120
|
|
|
51
|
-
export
|
|
121
|
+
export const notificationConfig = Object.freeze(notificationConfigObj);
|
|
122
|
+
export type NotificationConfig = typeof notificationConfig;
|
|
123
|
+
|
|
124
|
+
export default notificationConfig;
|
|
@@ -5,28 +5,33 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Env } from './env';
|
|
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;
|
|
8
|
+
import type { StorageConfigRuntime, StorageDriverConfig, StorageDrivers } from './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);
|
|
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
|
+
}
|
|
24
25
|
|
|
25
|
-
if (
|
|
26
|
-
|
|
26
|
+
if (isExplicitSelection) {
|
|
27
|
+
throw ErrorFactory.createConfigError(`Storage disk not configured: ${diskName}`);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
// Backwards-compatible fallback.
|
|
31
|
+
const fallback = config.drivers['local'] ?? Object.values(config.drivers)[0];
|
|
32
|
+
if (fallback !== undefined) return fallback;
|
|
33
|
+
|
|
34
|
+
throw ErrorFactory.createConfigError('No storage disks are configured');
|
|
30
35
|
};
|
|
31
36
|
|
|
32
37
|
const getDrivers = (): StorageDrivers => ({
|
|
@@ -86,6 +91,17 @@ const storageConfigObj = {
|
|
|
86
91
|
return getStorageDriver(this);
|
|
87
92
|
},
|
|
88
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Get a storage disk configuration by name.
|
|
96
|
+
*
|
|
97
|
+
* - When `name` is provided and not configured, this throws.
|
|
98
|
+
* - When `name` is omitted, it resolves the configured default with a backwards-compatible fallback.
|
|
99
|
+
* - Reserved name `default` aliases the configured default.
|
|
100
|
+
*/
|
|
101
|
+
getDriverConfig(this: StorageConfigRuntime, name?: string): StorageDriverConfig {
|
|
102
|
+
return getStorageDriver(this, name);
|
|
103
|
+
},
|
|
104
|
+
|
|
89
105
|
/**
|
|
90
106
|
* Temporary file settings
|
|
91
107
|
*/
|