@aetherframework/database 1.1.1 → 1.1.2
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/examples/mysql-test-pressure.js +1530 -0
- package/examples/test-direct.js +116 -0
- package/examples/transaction_example.js +127 -0
- package/package.json +3 -1
- package/src/DatabaseManager.js +565 -0
- package/src/core/ConnectionManager.js +351 -0
- package/src/core/DatabaseFactory.js +188 -0
- package/src/core/MongoQueryBuilder.js +576 -0
- package/src/core/PluginManager.js +968 -0
- package/src/core/QueryBuilder.js +4394 -0
- package/src/core/TransactionManager.js +40 -0
- package/src/drivers/clickhouse-driver.js +272 -0
- package/src/drivers/index.js +273 -0
- package/src/drivers/mongodb-driver.js +87 -0
- package/src/drivers/mssql-driver.js +117 -0
- package/src/drivers/mysql-driver.js +169 -0
- package/src/drivers/oracle-driver.js +101 -0
- package/src/drivers/postgres-driver.js +234 -0
- package/src/drivers/redis-driver.js +52 -0
- package/src/drivers/sqlite-driver.js +67 -0
- package/src/middleware/connection-pool.js +455 -0
- package/src/middleware/performance-monitor.js +652 -0
- package/src/middleware/query-cache.js +500 -0
- package/src/middleware/query-logger.js +262 -0
- package/src/plugins/AuditPlugin.js +447 -0
- package/src/plugins/BasePlugin.js +418 -0
- package/src/plugins/BatchOperationPlugin.js +165 -0
- package/src/plugins/CachePlugin.js +407 -0
- package/src/plugins/CtePlugin.js +523 -0
- package/src/plugins/DistributedPlugin.js +543 -0
- package/src/plugins/EncryptionPlugin.js +211 -0
- package/src/plugins/FullTextSearchPlugin.js +164 -0
- package/src/plugins/GeospatialPlugin.js +219 -0
- package/src/plugins/GraphQLPlugin.js +162 -0
- package/src/plugins/HookPlugin.js +211 -0
- package/src/plugins/JsonPlugin.js +366 -0
- package/src/plugins/OptimisticLockPlugin.js +374 -0
- package/src/plugins/PerformancePlugin.js +175 -0
- package/src/plugins/ResiliencePlugin.js +114 -0
- package/src/plugins/ShardingPlugin.js +227 -0
- package/src/plugins/SoftDeletePlugin.js +258 -0
- package/src/plugins/SyncPlugin.js +373 -0
- package/src/plugins/VersioningPlugin.js +314 -0
- package/src/plugins/WindowFunctionPlugin.js +343 -0
- package/src/utils/config-loader.js +632 -0
- package/src/utils/error-handler.js +724 -0
- package/src/utils/migration-runner.js +1066 -0
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/src/utils/config-loader
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Configuration Loader - Loads and validates database configuration
|
|
15
|
+
*/
|
|
16
|
+
class ConfigLoader {
|
|
17
|
+
/**
|
|
18
|
+
* Load configuration from multiple sources
|
|
19
|
+
* @param {Object} options - Loader options
|
|
20
|
+
* @returns {Object} Configuration object
|
|
21
|
+
*/
|
|
22
|
+
static load(options = {}) {
|
|
23
|
+
const config = {
|
|
24
|
+
// Default configuration
|
|
25
|
+
enabled: true,
|
|
26
|
+
crossDb: false,
|
|
27
|
+
default: 'primary',
|
|
28
|
+
connections: {},
|
|
29
|
+
driverModules: {},
|
|
30
|
+
externalDrivers: {
|
|
31
|
+
enabled: false,
|
|
32
|
+
path: './external-drivers'
|
|
33
|
+
},
|
|
34
|
+
cache: {
|
|
35
|
+
enabled: false,
|
|
36
|
+
ttl: 300000,
|
|
37
|
+
maxSize: 1000,
|
|
38
|
+
strategy: 'lru'
|
|
39
|
+
},
|
|
40
|
+
pool: {
|
|
41
|
+
min: 2,
|
|
42
|
+
max: 10,
|
|
43
|
+
idleTimeout: 30000,
|
|
44
|
+
acquireTimeout: 10000,
|
|
45
|
+
evictionRunInterval: 60000
|
|
46
|
+
},
|
|
47
|
+
retry: {
|
|
48
|
+
maxAttempts: 3,
|
|
49
|
+
delay: 1000,
|
|
50
|
+
backoff: true
|
|
51
|
+
},
|
|
52
|
+
middleware: {
|
|
53
|
+
queryLogger: {
|
|
54
|
+
enabled: true,
|
|
55
|
+
logLevel: 'info',
|
|
56
|
+
slowQueryThreshold: 1000,
|
|
57
|
+
logToConsole: true,
|
|
58
|
+
logToFile: false,
|
|
59
|
+
logFile: 'query.log'
|
|
60
|
+
},
|
|
61
|
+
connectionPool: {
|
|
62
|
+
enabled: true,
|
|
63
|
+
maxConnections: 10,
|
|
64
|
+
minConnections: 2,
|
|
65
|
+
idleTimeout: 30000,
|
|
66
|
+
acquireTimeout: 10000,
|
|
67
|
+
evictionRunInterval: 60000,
|
|
68
|
+
testOnBorrow: true,
|
|
69
|
+
testOnReturn: true
|
|
70
|
+
},
|
|
71
|
+
queryCache: {
|
|
72
|
+
enabled: false,
|
|
73
|
+
ttl: 300000,
|
|
74
|
+
maxSize: 1000,
|
|
75
|
+
strategy: 'lru',
|
|
76
|
+
cacheNullResults: true,
|
|
77
|
+
cacheErrors: false
|
|
78
|
+
},
|
|
79
|
+
performanceMonitor: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
slowQueryThreshold: 1000,
|
|
82
|
+
maxQueryHistory: 1000,
|
|
83
|
+
collectMetrics: true,
|
|
84
|
+
metricsInterval: 60000,
|
|
85
|
+
alertThresholds: {
|
|
86
|
+
slowQueriesPerMinute: 10,
|
|
87
|
+
errorRate: 0.1,
|
|
88
|
+
connectionErrors: 5
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Load from environment variables
|
|
95
|
+
this.loadFromEnv(config);
|
|
96
|
+
|
|
97
|
+
// Load from config file
|
|
98
|
+
if (options.configFile) {
|
|
99
|
+
this.loadFromFile(config, options.configFile);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Load from command line arguments
|
|
103
|
+
if (options.args) {
|
|
104
|
+
this.loadFromArgs(config, options.args);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Merge with provided options
|
|
108
|
+
this.mergeConfig(config, options);
|
|
109
|
+
|
|
110
|
+
// Validate configuration
|
|
111
|
+
this.validateConfig(config);
|
|
112
|
+
|
|
113
|
+
return config;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Load configuration from environment variables
|
|
118
|
+
* @param {Object} config - Configuration object
|
|
119
|
+
*/
|
|
120
|
+
static loadFromEnv(config) {
|
|
121
|
+
// Database module enabled
|
|
122
|
+
if (process.env.DB_ENABLED !== undefined) {
|
|
123
|
+
config.enabled = process.env.DB_ENABLED === 'true';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Cross-database queries
|
|
127
|
+
if (process.env.DB_CROSS_DB !== undefined) {
|
|
128
|
+
config.crossDb = process.env.DB_CROSS_DB === 'true';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Default connection
|
|
132
|
+
if (process.env.DB_DEFAULT_CONNECTION) {
|
|
133
|
+
config.default = process.env.DB_DEFAULT_CONNECTION;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Driver module switches
|
|
137
|
+
const drivers = ['mysql', 'postgres', 'sqlite', 'mongodb', 'redis', 'mssql', 'oracle'];
|
|
138
|
+
drivers.forEach(driver => {
|
|
139
|
+
const envVar = `DB_DRIVER_${driver.toUpperCase()}_ENABLED`;
|
|
140
|
+
if (process.env[envVar] !== undefined) {
|
|
141
|
+
config.driverModules[driver] = process.env[envVar] === 'true';
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// External drivers
|
|
146
|
+
if (process.env.DB_EXTERNAL_DRIVERS_ENABLED !== undefined) {
|
|
147
|
+
config.externalDrivers.enabled = process.env.DB_EXTERNAL_DRIVERS_ENABLED === 'true';
|
|
148
|
+
}
|
|
149
|
+
if (process.env.DB_EXTERNAL_DRIVERS_PATH) {
|
|
150
|
+
config.externalDrivers.path = process.env.DB_EXTERNAL_DRIVERS_PATH;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Cache configuration
|
|
154
|
+
if (process.env.DB_CACHE_ENABLED !== undefined) {
|
|
155
|
+
config.cache.enabled = process.env.DB_CACHE_ENABLED === 'true';
|
|
156
|
+
}
|
|
157
|
+
if (process.env.DB_CACHE_TTL) {
|
|
158
|
+
config.cache.ttl = parseInt(process.env.DB_CACHE_TTL);
|
|
159
|
+
}
|
|
160
|
+
if (process.env.DB_CACHE_MAX_SIZE) {
|
|
161
|
+
config.cache.maxSize = parseInt(process.env.DB_CACHE_MAX_SIZE);
|
|
162
|
+
}
|
|
163
|
+
if (process.env.DB_CACHE_STRATEGY) {
|
|
164
|
+
config.cache.strategy = process.env.DB_CACHE_STRATEGY;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Connection pool configuration
|
|
168
|
+
if (process.env.DB_POOL_MIN) {
|
|
169
|
+
config.pool.min = parseInt(process.env.DB_POOL_MIN);
|
|
170
|
+
}
|
|
171
|
+
if (process.env.DB_POOL_MAX) {
|
|
172
|
+
config.pool.max = parseInt(process.env.DB_POOL_MAX);
|
|
173
|
+
}
|
|
174
|
+
if (process.env.DB_POOL_IDLE_TIMEOUT) {
|
|
175
|
+
config.pool.idleTimeout = parseInt(process.env.DB_POOL_IDLE_TIMEOUT);
|
|
176
|
+
}
|
|
177
|
+
if (process.env.DB_POOL_ACQUIRE_TIMEOUT) {
|
|
178
|
+
config.pool.acquireTimeout = parseInt(process.env.DB_POOL_ACQUIRE_TIMEOUT);
|
|
179
|
+
}
|
|
180
|
+
if (process.env.DB_POOL_EVICTION_RUN_INTERVAL) {
|
|
181
|
+
config.pool.evictionRunInterval = parseInt(process.env.DB_POOL_EVICTION_RUN_INTERVAL);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Retry configuration
|
|
185
|
+
if (process.env.DB_RETRY_MAX_ATTEMPTS) {
|
|
186
|
+
config.retry.maxAttempts = parseInt(process.env.DB_RETRY_MAX_ATTEMPTS);
|
|
187
|
+
}
|
|
188
|
+
if (process.env.DB_RETRY_DELAY) {
|
|
189
|
+
config.retry.delay = parseInt(process.env.DB_RETRY_DELAY);
|
|
190
|
+
}
|
|
191
|
+
if (process.env.DB_RETRY_BACKOFF !== undefined) {
|
|
192
|
+
config.retry.backoff = process.env.DB_RETRY_BACKOFF === 'true';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Load connection configurations for enabled drivers
|
|
196
|
+
this.loadConnectionsFromEnv(config);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Load connection configurations from environment variables
|
|
201
|
+
* @param {Object} config - Configuration object
|
|
202
|
+
*/
|
|
203
|
+
static loadConnectionsFromEnv(config) {
|
|
204
|
+
const connectionConfigs = {
|
|
205
|
+
mysql: {
|
|
206
|
+
type: 'mysql',
|
|
207
|
+
host: process.env.DB_DRIVER_MYSQL_HOST,
|
|
208
|
+
port: process.env.DB_DRIVER_MYSQL_PORT,
|
|
209
|
+
user: process.env.DB_DRIVER_MYSQL_USER,
|
|
210
|
+
password: process.env.DB_DRIVER_MYSQL_PASSWORD,
|
|
211
|
+
database: process.env.DB_DRIVER_MYSQL_DATABASE,
|
|
212
|
+
charset: process.env.DB_DRIVER_MYSQL_CHARSET,
|
|
213
|
+
timezone: process.env.DB_DRIVER_MYSQL_TIMEZONE
|
|
214
|
+
},
|
|
215
|
+
postgres: {
|
|
216
|
+
type: 'postgresql',
|
|
217
|
+
host: process.env.DB_DRIVER_POSTGRES_HOST,
|
|
218
|
+
port: process.env.DB_DRIVER_POSTGRES_PORT,
|
|
219
|
+
user: process.env.DB_DRIVER_POSTGRES_USER,
|
|
220
|
+
password: process.env.DB_DRIVER_POSTGRES_PASSWORD,
|
|
221
|
+
database: process.env.DB_DRIVER_POSTGRES_DATABASE,
|
|
222
|
+
ssl: process.env.DB_DRIVER_POSTGRES_SSL === 'true'
|
|
223
|
+
},
|
|
224
|
+
sqlite: {
|
|
225
|
+
type: 'sqlite',
|
|
226
|
+
database: process.env.DB_DRIVER_SQLITE_DATABASE || ':memory:',
|
|
227
|
+
mode: process.env.DB_DRIVER_SQLITE_MODE || 'memory'
|
|
228
|
+
},
|
|
229
|
+
mongodb: {
|
|
230
|
+
type: 'mongodb',
|
|
231
|
+
host: process.env.DB_DRIVER_MONGODB_HOST,
|
|
232
|
+
port: process.env.DB_DRIVER_MONGODB_PORT,
|
|
233
|
+
user: process.env.DB_DRIVER_MONGODB_USER,
|
|
234
|
+
password: process.env.DB_DRIVER_MONGODB_PASSWORD,
|
|
235
|
+
database: process.env.DB_DRIVER_MONGODB_DATABASE,
|
|
236
|
+
authSource: process.env.DB_DRIVER_MONGODB_AUTH_SOURCE || 'admin'
|
|
237
|
+
},
|
|
238
|
+
redis: {
|
|
239
|
+
type: 'redis',
|
|
240
|
+
host: process.env.DB_DRIVER_REDIS_HOST,
|
|
241
|
+
port: process.env.DB_DRIVER_REDIS_PORT,
|
|
242
|
+
password: process.env.DB_DRIVER_REDIS_PASSWORD,
|
|
243
|
+
db: parseInt(process.env.DB_DRIVER_REDIS_DB) || 0,
|
|
244
|
+
keyPrefix: process.env.DB_DRIVER_REDIS_KEY_PREFIX || ''
|
|
245
|
+
},
|
|
246
|
+
mssql: {
|
|
247
|
+
type: 'mssql',
|
|
248
|
+
host: process.env.DB_DRIVER_MSSQL_HOST,
|
|
249
|
+
port: process.env.DB_DRIVER_MSSQL_PORT,
|
|
250
|
+
user: process.env.DB_DRIVER_MSSQL_USER,
|
|
251
|
+
password: process.env.DB_DRIVER_MSSQL_PASSWORD,
|
|
252
|
+
database: process.env.DB_DRIVER_MSSQL_DATABASE,
|
|
253
|
+
encrypt: process.env.DB_DRIVER_MSSQL_ENCRYPT === 'true',
|
|
254
|
+
trustServerCertificate: process.env.DB_DRIVER_MSSQL_TRUST_SERVER_CERTIFICATE === 'true'
|
|
255
|
+
},
|
|
256
|
+
oracle: {
|
|
257
|
+
type: 'oracle',
|
|
258
|
+
host: process.env.DB_DRIVER_ORACLE_HOST,
|
|
259
|
+
port: process.env.DB_DRIVER_ORACLE_PORT,
|
|
260
|
+
user: process.env.DB_DRIVER_ORACLE_USER,
|
|
261
|
+
password: process.env.DB_DRIVER_ORACLE_PASSWORD,
|
|
262
|
+
serviceName: process.env.DB_DRIVER_ORACLE_SERVICE_NAME || 'ORCL',
|
|
263
|
+
connectString: process.env.DB_DRIVER_ORACLE_CONNECT_STRING
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Only add connections for enabled modules
|
|
268
|
+
for (const [name, enabled] of Object.entries(config.driverModules)) {
|
|
269
|
+
if (enabled && connectionConfigs[name]) {
|
|
270
|
+
const connConfig = connectionConfigs[name];
|
|
271
|
+
const filteredConfig = Object.fromEntries(
|
|
272
|
+
Object.entries(connConfig).filter(([_, value]) => value !== undefined)
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
if (Object.keys(filteredConfig).length > 0) {
|
|
276
|
+
config.connections[name] = {
|
|
277
|
+
...filteredConfig,
|
|
278
|
+
pool: config.pool
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// If no connections found and SQLite is enabled, create default SQLite connection
|
|
285
|
+
if (Object.keys(config.connections).length === 0 && config.driverModules.sqlite) {
|
|
286
|
+
config.connections.default = {
|
|
287
|
+
type: 'sqlite',
|
|
288
|
+
database: ':memory:',
|
|
289
|
+
pool: config.pool
|
|
290
|
+
};
|
|
291
|
+
config.default = 'default';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Load configuration from file
|
|
297
|
+
* @param {Object} config - Configuration object
|
|
298
|
+
* @param {string} configFile - Configuration file path
|
|
299
|
+
*/
|
|
300
|
+
static loadFromFile(config, configFile) {
|
|
301
|
+
try {
|
|
302
|
+
const filePath = path.isAbsolute(configFile)
|
|
303
|
+
? configFile
|
|
304
|
+
: path.join(process.cwd(), configFile);
|
|
305
|
+
|
|
306
|
+
if (fs.existsSync(filePath)) {
|
|
307
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
308
|
+
const fileConfig = JSON.parse(fileContent);
|
|
309
|
+
this.mergeConfig(config, fileConfig);
|
|
310
|
+
}
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.warn(`⚠️ Failed to load configuration from file: ${configFile}`, error.message);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Load configuration from command line arguments
|
|
318
|
+
* @param {Object} config - Configuration object
|
|
319
|
+
* @param {Array} args - Command line arguments
|
|
320
|
+
*/
|
|
321
|
+
static loadFromArgs(config, args) {
|
|
322
|
+
// Parse command line arguments
|
|
323
|
+
// Format: --db.host=localhost --db.port=3306
|
|
324
|
+
for (const arg of args) {
|
|
325
|
+
if (arg.startsWith('--db.')) {
|
|
326
|
+
const [key, value] = arg.slice(5).split('=');
|
|
327
|
+
if (key && value !== undefined) {
|
|
328
|
+
this.setNestedValue(config, key.split('.'), value);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Set nested value in object
|
|
336
|
+
* @param {Object} obj - Object to modify
|
|
337
|
+
* @param {Array} path - Path array
|
|
338
|
+
* @param {*} value - Value to set
|
|
339
|
+
*/
|
|
340
|
+
static setNestedValue(obj, path, value) {
|
|
341
|
+
const lastKey = path.pop();
|
|
342
|
+
const target = path.reduce((acc, key) => {
|
|
343
|
+
if (!acc[key]) acc[key] = {};
|
|
344
|
+
return acc[key];
|
|
345
|
+
}, obj);
|
|
346
|
+
|
|
347
|
+
// Convert string values to appropriate types
|
|
348
|
+
let finalValue = value;
|
|
349
|
+
if (value === 'true') finalValue = true;
|
|
350
|
+
else if (value === 'false') finalValue = false;
|
|
351
|
+
else if (!isNaN(value) && value.trim() !== '') finalValue = Number(value);
|
|
352
|
+
|
|
353
|
+
target[lastKey] = finalValue;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Merge configurations
|
|
358
|
+
* @param {Object} target - Target configuration
|
|
359
|
+
* @param {Object} source - Source configuration
|
|
360
|
+
*/
|
|
361
|
+
static mergeConfig(target, source) {
|
|
362
|
+
for (const key in source) {
|
|
363
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
364
|
+
if (!target[key]) target[key] = {};
|
|
365
|
+
this.mergeConfig(target[key], source[key]);
|
|
366
|
+
} else {
|
|
367
|
+
target[key] = source[key];
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Validate configuration
|
|
374
|
+
* @param {Object} config - Configuration object
|
|
375
|
+
* @throws {Error} If configuration is invalid
|
|
376
|
+
*/
|
|
377
|
+
static validateConfig(config) {
|
|
378
|
+
// Validate enabled drivers
|
|
379
|
+
if (typeof config.enabled !== 'boolean') {
|
|
380
|
+
throw new Error('DB_ENABLED must be a boolean');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Validate driver modules
|
|
384
|
+
if (typeof config.driverModules !== 'object') {
|
|
385
|
+
throw new Error('driverModules must be an object');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Validate connections
|
|
389
|
+
if (typeof config.connections !== 'object') {
|
|
390
|
+
throw new Error('connections must be an object');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Validate pool configuration
|
|
394
|
+
if (config.pool.min > config.pool.max) {
|
|
395
|
+
throw new Error('DB_POOL_MIN cannot be greater than DB_POOL_MAX');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (config.pool.min < 0 || config.pool.max < 1) {
|
|
399
|
+
throw new Error('Pool size values must be positive');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Validate cache configuration
|
|
403
|
+
if (config.cache.ttl < 0) {
|
|
404
|
+
throw new Error('DB_CACHE_TTL must be non-negative');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (config.cache.maxSize < 0) {
|
|
408
|
+
throw new Error('DB_CACHE_MAX_SIZE must be non-negative');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Validate retry configuration
|
|
412
|
+
if (config.retry.maxAttempts < 0) {
|
|
413
|
+
throw new Error('DB_RETRY_MAX_ATTEMPTS must be non-negative');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (config.retry.delay < 0) {
|
|
417
|
+
throw new Error('DB_RETRY_DELAY must be non-negative');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Validate middleware configurations
|
|
421
|
+
if (config.middleware) {
|
|
422
|
+
if (config.middleware.queryLogger) {
|
|
423
|
+
if (config.middleware.queryLogger.slowQueryThreshold < 0) {
|
|
424
|
+
throw new Error('slowQueryThreshold must be non-negative');
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (config.middleware.performanceMonitor) {
|
|
429
|
+
if (config.middleware.performanceMonitor.slowQueryThreshold < 0) {
|
|
430
|
+
throw new Error('performanceMonitor.slowQueryThreshold must be non-negative');
|
|
431
|
+
}
|
|
432
|
+
if (config.middleware.performanceMonitor.alertThresholds.errorRate < 0 || config.middleware.performanceMonitor.alertThresholds.errorRate > 1) {
|
|
433
|
+
throw new Error('performanceMonitor.alertThresholds.errorRate must be between 0 and 1');
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Log validation warnings
|
|
439
|
+
this.logValidationWarnings(config);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Log validation warnings
|
|
444
|
+
* @param {Object} config - Configuration object
|
|
445
|
+
*/
|
|
446
|
+
static logValidationWarnings(config) {
|
|
447
|
+
// Check for enabled drivers without connections
|
|
448
|
+
for (const [driver, enabled] of Object.entries(config.driverModules)) {
|
|
449
|
+
if (enabled && !config.connections[driver]) {
|
|
450
|
+
console.warn(`⚠️ Driver "${driver}" is enabled but no connection configuration found.`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Check for connections without enabled drivers
|
|
455
|
+
for (const [name, connConfig] of Object.entries(config.connections)) {
|
|
456
|
+
const driverType = connConfig.type?.toLowerCase().replace(/[^a-z]/g, '');
|
|
457
|
+
if (driverType && !config.driverModules[driverType]) {
|
|
458
|
+
console.warn(`⚠️ Connection "${name}" uses driver "${driverType}" which is not enabled.`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Check for missing default connection
|
|
463
|
+
if (config.default && !config.connections[config.default]) {
|
|
464
|
+
console.warn(`⚠️ Default connection "${config.default}" not found in connections.`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Generate configuration template
|
|
470
|
+
* @returns {Object} Configuration template
|
|
471
|
+
*/
|
|
472
|
+
static generateTemplate() {
|
|
473
|
+
return {
|
|
474
|
+
enabled: true,
|
|
475
|
+
crossDb: false,
|
|
476
|
+
default: 'primary',
|
|
477
|
+
connections: {
|
|
478
|
+
mysql: {
|
|
479
|
+
type: 'mysql',
|
|
480
|
+
host: 'localhost',
|
|
481
|
+
port: 3306,
|
|
482
|
+
user: 'root',
|
|
483
|
+
password: 'password',
|
|
484
|
+
database: 'mydb',
|
|
485
|
+
charset: 'utf8mb4',
|
|
486
|
+
timezone: '+00:00'
|
|
487
|
+
},
|
|
488
|
+
postgres: {
|
|
489
|
+
type: 'postgresql',
|
|
490
|
+
host: 'localhost',
|
|
491
|
+
port: 5432,
|
|
492
|
+
user: 'postgres',
|
|
493
|
+
password: 'password',
|
|
494
|
+
database: 'mydb',
|
|
495
|
+
ssl: false
|
|
496
|
+
},
|
|
497
|
+
sqlite: {
|
|
498
|
+
type: 'sqlite',
|
|
499
|
+
database: './data/database.sqlite',
|
|
500
|
+
mode: 'file'
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
driverModules: {
|
|
504
|
+
mysql: true,
|
|
505
|
+
postgres: true,
|
|
506
|
+
sqlite: true,
|
|
507
|
+
mongodb: false,
|
|
508
|
+
redis: false,
|
|
509
|
+
mssql: false,
|
|
510
|
+
oracle: false
|
|
511
|
+
},
|
|
512
|
+
externalDrivers: {
|
|
513
|
+
enabled: false,
|
|
514
|
+
path: './external-drivers'
|
|
515
|
+
},
|
|
516
|
+
cache: {
|
|
517
|
+
enabled: false,
|
|
518
|
+
ttl: 300000,
|
|
519
|
+
maxSize: 1000,
|
|
520
|
+
strategy: 'lru'
|
|
521
|
+
},
|
|
522
|
+
pool: {
|
|
523
|
+
min: 2,
|
|
524
|
+
max: 10,
|
|
525
|
+
idleTimeout: 30000,
|
|
526
|
+
acquireTimeout: 10000,
|
|
527
|
+
evictionRunInterval: 60000
|
|
528
|
+
},
|
|
529
|
+
retry: {
|
|
530
|
+
maxAttempts: 3,
|
|
531
|
+
delay: 1000,
|
|
532
|
+
backoff: true
|
|
533
|
+
},
|
|
534
|
+
middleware: {
|
|
535
|
+
queryLogger: {
|
|
536
|
+
enabled: true,
|
|
537
|
+
logLevel: 'info',
|
|
538
|
+
slowQueryThreshold: 1000,
|
|
539
|
+
logToConsole: true,
|
|
540
|
+
logToFile: false,
|
|
541
|
+
logFile: 'query.log'
|
|
542
|
+
},
|
|
543
|
+
connectionPool: {
|
|
544
|
+
enabled: true,
|
|
545
|
+
maxConnections: 10,
|
|
546
|
+
minConnections: 2,
|
|
547
|
+
idleTimeout: 30000,
|
|
548
|
+
acquireTimeout: 10000,
|
|
549
|
+
evictionRunInterval: 60000,
|
|
550
|
+
testOnBorrow: true,
|
|
551
|
+
testOnReturn: true
|
|
552
|
+
},
|
|
553
|
+
queryCache: {
|
|
554
|
+
enabled: false,
|
|
555
|
+
ttl: 300000,
|
|
556
|
+
maxSize: 1000,
|
|
557
|
+
strategy: 'lru',
|
|
558
|
+
cacheNullResults: true,
|
|
559
|
+
cacheErrors: false
|
|
560
|
+
},
|
|
561
|
+
performanceMonitor: {
|
|
562
|
+
enabled: true,
|
|
563
|
+
slowQueryThreshold: 1000,
|
|
564
|
+
maxQueryHistory: 1000,
|
|
565
|
+
collectMetrics: true,
|
|
566
|
+
metricsInterval: 60000,
|
|
567
|
+
alertThresholds: {
|
|
568
|
+
slowQueriesPerMinute: 10,
|
|
569
|
+
errorRate: 0.1,
|
|
570
|
+
connectionErrors: 5
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Save configuration to file
|
|
579
|
+
* @param {Object} config - Configuration object
|
|
580
|
+
* @param {string} filePath - File path
|
|
581
|
+
*/
|
|
582
|
+
static saveToFile(config, filePath) {
|
|
583
|
+
try {
|
|
584
|
+
const dir = path.dirname(filePath);
|
|
585
|
+
if (!fs.existsSync(dir)) {
|
|
586
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const configStr = JSON.stringify(config, null, 2);
|
|
590
|
+
fs.writeFileSync(filePath, configStr, 'utf8');
|
|
591
|
+
} catch (error) {
|
|
592
|
+
throw error;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Get configuration summary
|
|
598
|
+
* @param {Object} config - Configuration object
|
|
599
|
+
* @returns {Object} Configuration summary
|
|
600
|
+
*/
|
|
601
|
+
static getSummary(config) {
|
|
602
|
+
const enabledDrivers = Object.entries(config.driverModules)
|
|
603
|
+
.filter(([_, enabled]) => enabled)
|
|
604
|
+
.map(([name]) => name);
|
|
605
|
+
|
|
606
|
+
const disabledDrivers = Object.entries(config.driverModules)
|
|
607
|
+
.filter(([_, enabled]) => !enabled)
|
|
608
|
+
.map(([name]) => name);
|
|
609
|
+
|
|
610
|
+
const connections = Object.keys(config.connections);
|
|
611
|
+
|
|
612
|
+
return {
|
|
613
|
+
enabled: config.enabled,
|
|
614
|
+
crossDb: config.crossDb,
|
|
615
|
+
defaultConnection: config.default,
|
|
616
|
+
enabledDrivers,
|
|
617
|
+
disabledDrivers,
|
|
618
|
+
connections,
|
|
619
|
+
connectionCount: connections.length,
|
|
620
|
+
cacheEnabled: config.cache.enabled,
|
|
621
|
+
poolSize: `${config.pool.min}-${config.pool.max}`,
|
|
622
|
+
middleware: {
|
|
623
|
+
queryLogger: config.middleware?.queryLogger?.enabled || false,
|
|
624
|
+
connectionPool: config.middleware?.connectionPool?.enabled || false,
|
|
625
|
+
queryCache: config.middleware?.queryCache?.enabled || false,
|
|
626
|
+
performanceMonitor: config.middleware?.performanceMonitor?.enabled || false
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export default ConfigLoader;
|