@flusys/nestjs-core 9.1.2 ā 9.2.0
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/README.md +39 -6
- package/config/env-config.service.d.ts +4 -2
- package/fesm/229.js +189 -135
- package/fesm/config/index.js +41 -19
- package/fesm/docs/index.js +81 -166
- package/fesm/index.js +1 -1
- package/fesm/migration/index.js +1 -1
- package/fesm/utils/index.js +15 -26
- package/migration/migration.runner.d.ts +10 -1
- package/package.json +1 -2
package/fesm/229.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import * as __rspack_external_fs from "fs";
|
|
1
2
|
import * as __rspack_external_path from "path";
|
|
2
3
|
import * as __rspack_external_typeorm from "typeorm";
|
|
3
4
|
import * as __rspack_external_typeorm_naming_strategies_48a68f8e from "typeorm-naming-strategies";
|
|
4
5
|
import * as __rspack_external_url from "url";
|
|
5
6
|
import * as __rspack_external_dotenv from "dotenv";
|
|
6
7
|
import * as __rspack_external_child_process from "child_process";
|
|
7
|
-
import * as __rspack_external_fs from "fs";
|
|
8
8
|
export const __rspack_esm_id = 229;
|
|
9
9
|
export const __rspack_esm_ids = [229];
|
|
10
10
|
export const __webpack_modules__ = {
|
|
11
|
-
|
|
11
|
+
631(module) {
|
|
12
12
|
function __rspack_empty_async_context(req) {
|
|
13
13
|
// Here Promise.resolve().then() is used instead of new Promise() to prevent
|
|
14
14
|
// uncaught exception popping up in devtools
|
|
@@ -20,7 +20,7 @@ function __rspack_empty_async_context(req) {
|
|
|
20
20
|
}
|
|
21
21
|
__rspack_empty_async_context.keys = () => ([]);
|
|
22
22
|
__rspack_empty_async_context.resolve = __rspack_empty_async_context;
|
|
23
|
-
__rspack_empty_async_context.id =
|
|
23
|
+
__rspack_empty_async_context.id = 631;
|
|
24
24
|
module.exports = __rspack_empty_async_context;
|
|
25
25
|
|
|
26
26
|
|
|
@@ -37,10 +37,20 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
37
37
|
;// CONCATENATED MODULE: ./projects/nestjs-core/src/config/env-config.service.ts
|
|
38
38
|
|
|
39
39
|
__rspack_external_dotenv.config();
|
|
40
|
+
const DATABASE_TYPES = [
|
|
41
|
+
'mysql',
|
|
42
|
+
'postgres',
|
|
43
|
+
'mariadb',
|
|
44
|
+
'sqlite',
|
|
45
|
+
'mssql'
|
|
46
|
+
];
|
|
47
|
+
const DEFAULT_DB_PORTS = {
|
|
48
|
+
postgres: 5432,
|
|
49
|
+
mssql: 1433
|
|
50
|
+
};
|
|
40
51
|
let env_config_service_EnvConfigService = class EnvConfigService {
|
|
41
52
|
env;
|
|
42
53
|
restrictedKeys = {
|
|
43
|
-
FRONTEND_URL: 'getFrontendUrl',
|
|
44
54
|
ALLOW_ORIGINS: 'getOrigins',
|
|
45
55
|
PORT: 'getPort',
|
|
46
56
|
MODE: 'isProduction',
|
|
@@ -56,10 +66,13 @@ let env_config_service_EnvConfigService = class EnvConfigService {
|
|
|
56
66
|
}
|
|
57
67
|
getValue(key, throwOnMissing = true) {
|
|
58
68
|
const value = this.env[key];
|
|
59
|
-
if (!value
|
|
60
|
-
|
|
69
|
+
if (!value?.trim()) {
|
|
70
|
+
if (throwOnMissing) {
|
|
71
|
+
throw new Error(`Config error - missing env.${key}`);
|
|
72
|
+
}
|
|
73
|
+
return '';
|
|
61
74
|
}
|
|
62
|
-
return value
|
|
75
|
+
return value;
|
|
63
76
|
}
|
|
64
77
|
tryGetValue(key, throwOnMissing = false) {
|
|
65
78
|
if (this.restrictedKeys[key]) {
|
|
@@ -68,30 +81,39 @@ let env_config_service_EnvConfigService = class EnvConfigService {
|
|
|
68
81
|
return this.getValue(key, throwOnMissing);
|
|
69
82
|
}
|
|
70
83
|
getNumber(key, throwOnMissing = true) {
|
|
71
|
-
|
|
84
|
+
const raw = this.getValue(key, throwOnMissing).trim();
|
|
85
|
+
if (!raw) return undefined;
|
|
86
|
+
const value = Number(raw);
|
|
87
|
+
if (Number.isFinite(value)) return value;
|
|
88
|
+
if (throwOnMissing) {
|
|
89
|
+
throw new Error(`Config error - env.${key} must be a number, got "${raw}"`);
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
72
92
|
}
|
|
73
93
|
getBoolean(key, throwOnMissing = true) {
|
|
74
|
-
return this.getValue(key, throwOnMissing).toLowerCase() === 'true';
|
|
94
|
+
return this.getValue(key, throwOnMissing).trim().toLowerCase() === 'true';
|
|
75
95
|
}
|
|
76
96
|
getPort() {
|
|
77
97
|
return this.getNumber('PORT', false) || 3000;
|
|
78
98
|
}
|
|
79
99
|
isProduction() {
|
|
80
|
-
return this.getValue('MODE', false).toUpperCase() !== 'DEV';
|
|
100
|
+
return this.getValue('MODE', false).trim().toUpperCase() !== 'DEV';
|
|
81
101
|
}
|
|
82
102
|
getOrigins() {
|
|
83
|
-
|
|
84
|
-
return origins ? origins.split(',').map((o)=>o.trim()) : [];
|
|
103
|
+
return this.getValue('ALLOW_ORIGINS').split(',').map((o)=>o.trim()).filter(Boolean);
|
|
85
104
|
}
|
|
86
105
|
getTypeOrmConfig() {
|
|
87
|
-
const
|
|
88
|
-
|
|
106
|
+
const rawType = this.getValue('DB_TYPE', false).trim().toLowerCase() || 'mysql';
|
|
107
|
+
if (!DATABASE_TYPES.includes(rawType)) {
|
|
108
|
+
throw new Error(`Config error - unsupported env.DB_TYPE "${rawType}". Expected one of: ${DATABASE_TYPES.join(', ')}`);
|
|
109
|
+
}
|
|
110
|
+
const type = rawType;
|
|
89
111
|
return {
|
|
90
112
|
type,
|
|
91
113
|
host: this.getValue('DB_HOST', false) || 'localhost',
|
|
92
|
-
port: this.getNumber('DB_PORT', false) ||
|
|
114
|
+
port: this.getNumber('DB_PORT', false) || DEFAULT_DB_PORTS[type] || 3306,
|
|
93
115
|
username: this.getValue('DB_USER', false) || 'root',
|
|
94
|
-
password: this.getValue('DB_PASSWORD', false)
|
|
116
|
+
password: this.getValue('DB_PASSWORD', false),
|
|
95
117
|
database: this.getValue('DB_NAME', false) || 'flusys_main'
|
|
96
118
|
};
|
|
97
119
|
}
|
|
@@ -108,15 +130,15 @@ let env_config_service_EnvConfigService = class EnvConfigService {
|
|
|
108
130
|
}
|
|
109
131
|
getMailConfig() {
|
|
110
132
|
return {
|
|
111
|
-
MAIL_FROM: this.getValue('MAIL_FROM', false)
|
|
112
|
-
MAIL_APP_PASSWORD: this.getValue('MAIL_APP_PASSWORD', false)
|
|
133
|
+
MAIL_FROM: this.getValue('MAIL_FROM', false),
|
|
134
|
+
MAIL_APP_PASSWORD: this.getValue('MAIL_APP_PASSWORD', false)
|
|
113
135
|
};
|
|
114
136
|
}
|
|
115
137
|
getTenantId() {
|
|
116
|
-
return this.getValue('TENANT_ID', false) || null;
|
|
138
|
+
return this.getValue('TENANT_ID', false).trim() || null;
|
|
117
139
|
}
|
|
118
140
|
useTenantMode() {
|
|
119
|
-
return this.
|
|
141
|
+
return this.getBoolean('USE_TENANT_MODE', false);
|
|
120
142
|
}
|
|
121
143
|
getLogConfig() {
|
|
122
144
|
return {
|
|
@@ -124,11 +146,11 @@ let env_config_service_EnvConfigService = class EnvConfigService {
|
|
|
124
146
|
level: this.getValue('LOG_LEVEL', false) || (this.isProduction() ? 'info' : 'debug'),
|
|
125
147
|
maxSize: this.getValue('LOG_MAX_SIZE', false) || '20m',
|
|
126
148
|
maxFiles: this.getValue('LOG_MAX_FILES', false) || '14d',
|
|
127
|
-
disableHttpLogging: this.
|
|
149
|
+
disableHttpLogging: this.getBoolean('DISABLE_HTTP_LOGGING', false)
|
|
128
150
|
};
|
|
129
151
|
}
|
|
130
152
|
getEnv() {
|
|
131
|
-
return
|
|
153
|
+
return this.env;
|
|
132
154
|
}
|
|
133
155
|
};
|
|
134
156
|
const envConfig = new env_config_service_EnvConfigService(process.env);
|
|
@@ -160,9 +182,9 @@ var datasource_config_builder = __webpack_require__(722);
|
|
|
160
182
|
function createMigrationDataSource(options) {
|
|
161
183
|
const { config, tenantId } = options;
|
|
162
184
|
const { database, tenant, tenantConfig } = (0,datasource_config_builder/* .getDatabaseForTenant */.LH)(config, tenantId);
|
|
163
|
-
console.log(`\nš¦ Database: ${database}${tenant ?
|
|
164
|
-
console.log(`š Migrations:
|
|
165
|
-
if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants
|
|
185
|
+
console.log(`\nš¦ Database: ${database} [${tenant ? tenant.name ?? tenant.id : 'default'}]`);
|
|
186
|
+
console.log(`š Migrations: ${(0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(config.migrationsPath, tenantId)}/`);
|
|
187
|
+
if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants?.length) {
|
|
166
188
|
console.log(`\nš” Multi-tenant mode. Available tenants:`);
|
|
167
189
|
config.tenants.forEach((t)=>console.log(` - TENANT_ID=${t.id} ā ${t.database}`));
|
|
168
190
|
}
|
|
@@ -189,6 +211,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
189
211
|
w: () => (/* binding */ runMigrationCli)
|
|
190
212
|
});
|
|
191
213
|
|
|
214
|
+
// EXTERNAL MODULE: external "fs"
|
|
215
|
+
var external_fs_ = __webpack_require__(421);
|
|
192
216
|
// EXTERNAL MODULE: external "path"
|
|
193
217
|
var external_path_ = __webpack_require__(521);
|
|
194
218
|
;// CONCATENATED MODULE: external "url"
|
|
@@ -197,14 +221,15 @@ var external_path_ = __webpack_require__(521);
|
|
|
197
221
|
var src_config = __webpack_require__(892);
|
|
198
222
|
// EXTERNAL MODULE: ./projects/nestjs-core/src/utils/datasource-config.builder.ts
|
|
199
223
|
var datasource_config_builder = __webpack_require__(722);
|
|
200
|
-
// EXTERNAL MODULE: ./projects/nestjs-core/src/migration/migration.runner.ts +
|
|
201
|
-
var migration_runner = __webpack_require__(
|
|
224
|
+
// EXTERNAL MODULE: ./projects/nestjs-core/src/migration/migration.runner.ts + 1 modules
|
|
225
|
+
var migration_runner = __webpack_require__(989);
|
|
202
226
|
;// CONCATENATED MODULE: ./projects/nestjs-core/src/migration/migration.cli.ts
|
|
203
227
|
|
|
204
228
|
|
|
205
229
|
|
|
206
230
|
|
|
207
231
|
|
|
232
|
+
|
|
208
233
|
function showHelp() {
|
|
209
234
|
console.log(`
|
|
210
235
|
Migration CLI
|
|
@@ -239,21 +264,23 @@ Examples:
|
|
|
239
264
|
npm run migration reset
|
|
240
265
|
`);
|
|
241
266
|
}
|
|
267
|
+
function readFlag(arg, flag) {
|
|
268
|
+
const prefix = `--${flag}=`;
|
|
269
|
+
return arg.startsWith(prefix) ? arg.slice(prefix.length).trim() || undefined : undefined;
|
|
270
|
+
}
|
|
242
271
|
function parseArgs() {
|
|
243
|
-
const args = process.argv.slice(2);
|
|
244
272
|
let command;
|
|
245
273
|
let name;
|
|
246
274
|
let configPath;
|
|
247
275
|
let datasourcePath;
|
|
248
276
|
let force = false;
|
|
249
|
-
for(
|
|
250
|
-
const arg = args[i];
|
|
277
|
+
for (const arg of process.argv.slice(2)){
|
|
251
278
|
if (arg.startsWith('--name=')) {
|
|
252
|
-
name = arg
|
|
279
|
+
name = readFlag(arg, 'name');
|
|
253
280
|
} else if (arg.startsWith('--config=')) {
|
|
254
|
-
configPath = arg
|
|
281
|
+
configPath = readFlag(arg, 'config');
|
|
255
282
|
} else if (arg.startsWith('--datasource=')) {
|
|
256
|
-
datasourcePath = arg
|
|
283
|
+
datasourcePath = readFlag(arg, 'datasource');
|
|
257
284
|
} else if (arg === '--force') {
|
|
258
285
|
force = true;
|
|
259
286
|
} else if (!arg.startsWith('-') && !command) {
|
|
@@ -268,17 +295,21 @@ function parseArgs() {
|
|
|
268
295
|
force
|
|
269
296
|
};
|
|
270
297
|
}
|
|
298
|
+
function resolveConfigPath(configPath) {
|
|
299
|
+
return external_path_.isAbsolute(configPath) ? configPath : external_path_.resolve(process.cwd(), configPath);
|
|
300
|
+
}
|
|
271
301
|
async function loadConfig(configPath) {
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
|
|
302
|
+
const module = await __webpack_require__(631)((0,__rspack_external_url.pathToFileURL)(resolveConfigPath(configPath)).href);
|
|
303
|
+
const config = module.migrationConfig ?? module.default?.migrationConfig ?? module.default;
|
|
304
|
+
if (!config?.defaultDatabaseConfig || !config.migrationsPath) {
|
|
305
|
+
throw new Error(`Migration config '${configPath}' must export a 'migrationConfig' with defaultDatabaseConfig and migrationsPath`);
|
|
306
|
+
}
|
|
307
|
+
return config;
|
|
275
308
|
}
|
|
276
309
|
async function runMigrationCli(configOrPath) {
|
|
277
310
|
const { command, name, configPath: argConfigPath, datasourcePath, force } = parseArgs();
|
|
278
311
|
const tenantId = src_config/* .envConfig.getTenantId */.K.getTenantId() ?? undefined;
|
|
279
|
-
// npm passes --name=X as npm_config_name
|
|
280
312
|
const migrationName = src_config/* .envConfig.tryGetValue */.K.tryGetValue('npm_config_name') || src_config/* .envConfig.tryGetValue */.K.tryGetValue('MIGRATION_NAME') || name;
|
|
281
|
-
// Resolve config
|
|
282
313
|
let config;
|
|
283
314
|
let dsPath = datasourcePath;
|
|
284
315
|
if (typeof configOrPath === 'string') {
|
|
@@ -290,7 +321,6 @@ async function runMigrationCli(configOrPath) {
|
|
|
290
321
|
config = await loadConfig(argConfigPath);
|
|
291
322
|
dsPath = dsPath || argConfigPath;
|
|
292
323
|
} else {
|
|
293
|
-
// Try paths in order: env var, then common defaults
|
|
294
324
|
const envConfigPath = src_config/* .envConfig.tryGetValue */.K.tryGetValue('MIGRATION_CONFIG');
|
|
295
325
|
const defaultPaths = [
|
|
296
326
|
envConfigPath,
|
|
@@ -299,18 +329,11 @@ async function runMigrationCli(configOrPath) {
|
|
|
299
329
|
'persistence/migration.config.ts',
|
|
300
330
|
'persistence/migration.config.js'
|
|
301
331
|
].filter(Boolean);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
loaded = true;
|
|
308
|
-
break;
|
|
309
|
-
} catch {
|
|
310
|
-
// Try next path
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
if (!loaded) {
|
|
332
|
+
const foundPath = defaultPaths.find((p)=>external_fs_.existsSync(resolveConfigPath(p)));
|
|
333
|
+
if (foundPath) {
|
|
334
|
+
config = await loadConfig(foundPath);
|
|
335
|
+
dsPath = dsPath || foundPath;
|
|
336
|
+
} else {
|
|
314
337
|
console.error('ā Config not found. Use --config=<path> or set MIGRATION_CONFIG env var');
|
|
315
338
|
console.error(' Tried paths:', defaultPaths.join(', '));
|
|
316
339
|
showHelp();
|
|
@@ -321,18 +344,21 @@ async function runMigrationCli(configOrPath) {
|
|
|
321
344
|
showHelp();
|
|
322
345
|
return;
|
|
323
346
|
}
|
|
324
|
-
// Multi-tenant batch commands
|
|
325
347
|
if (command.endsWith(':all')) {
|
|
348
|
+
const action = command.slice(0, -':all'.length);
|
|
349
|
+
if (!(0,migration_runner/* .isTenantMigrationAction */.x_)(action)) {
|
|
350
|
+
console.error(`ā Unknown command: ${command}`);
|
|
351
|
+
showHelp();
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
326
354
|
if (config.bootstrapAppConfig?.databaseMode === 'single') {
|
|
327
355
|
console.error('ā Multi-tenant mode not enabled');
|
|
328
356
|
process.exit(1);
|
|
329
357
|
}
|
|
330
|
-
const action = command.split(':')[0];
|
|
331
358
|
const results = await (0,migration_runner/* .runForAllTenants */.XR)(config, action);
|
|
332
359
|
if (results.some((r)=>!r.success)) process.exit(1);
|
|
333
360
|
return;
|
|
334
361
|
}
|
|
335
|
-
// Show target info
|
|
336
362
|
const { database } = (0,datasource_config_builder/* .getDatabaseForTenant */.LH)(config, tenantId);
|
|
337
363
|
const folder = (0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(config.migrationsPath, tenantId);
|
|
338
364
|
console.log(`\nš¦ Database: ${database}`);
|
|
@@ -363,16 +389,21 @@ async function runMigrationCli(configOrPath) {
|
|
|
363
389
|
console.error('ā Refusing to reset database outside dev mode. Pass --force to override.');
|
|
364
390
|
process.exit(1);
|
|
365
391
|
}
|
|
392
|
+
const initName = migrationName || 'Init';
|
|
393
|
+
(0,migration_runner/* .assertValidMigrationName */.On)(initName);
|
|
366
394
|
const result = await (0,migration_runner/* .resetDatabase */.$7)(config, tenantId);
|
|
367
395
|
if (!result.success) process.exit(1);
|
|
368
|
-
await (0,migration_runner/* .generateMigration */.iL)(config,
|
|
396
|
+
await (0,migration_runner/* .generateMigration */.iL)(config, initName, tenantId, dsPath);
|
|
369
397
|
const runResult = await (0,migration_runner/* .runMigrations */.vI)(config, tenantId);
|
|
370
398
|
if (!runResult.success) process.exit(1);
|
|
371
399
|
break;
|
|
372
400
|
}
|
|
373
401
|
case 'status':
|
|
374
|
-
|
|
375
|
-
|
|
402
|
+
{
|
|
403
|
+
const result = await (0,migration_runner/* .migrationStatus */.vd)(config, tenantId);
|
|
404
|
+
if (!result.success) process.exit(1);
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
376
407
|
default:
|
|
377
408
|
console.error(`ā Unknown command: ${command}`);
|
|
378
409
|
showHelp();
|
|
@@ -382,12 +413,14 @@ async function runMigrationCli(configOrPath) {
|
|
|
382
413
|
|
|
383
414
|
|
|
384
415
|
},
|
|
385
|
-
|
|
416
|
+
989(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
386
417
|
|
|
387
418
|
// EXPORTS
|
|
388
419
|
__webpack_require__.d(__webpack_exports__, {
|
|
420
|
+
On: () => (/* binding */ assertValidMigrationName),
|
|
389
421
|
Mg: () => (/* binding */ ensureMigrationsFolder),
|
|
390
422
|
iL: () => (/* binding */ generateMigration),
|
|
423
|
+
x_: () => (/* binding */ isTenantMigrationAction),
|
|
391
424
|
vd: () => (/* binding */ migrationStatus),
|
|
392
425
|
$7: () => (/* binding */ resetDatabase),
|
|
393
426
|
$N: () => (/* binding */ revertMigration),
|
|
@@ -399,8 +432,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
399
432
|
|
|
400
433
|
;// CONCATENATED MODULE: external "child_process"
|
|
401
434
|
|
|
402
|
-
|
|
403
|
-
|
|
435
|
+
// EXTERNAL MODULE: external "fs"
|
|
436
|
+
var external_fs_ = __webpack_require__(421);
|
|
404
437
|
// EXTERNAL MODULE: external "path"
|
|
405
438
|
var external_path_ = __webpack_require__(521);
|
|
406
439
|
// EXTERNAL MODULE: external "typeorm"
|
|
@@ -419,19 +452,38 @@ var datasource_factory = __webpack_require__(193);
|
|
|
419
452
|
|
|
420
453
|
|
|
421
454
|
|
|
455
|
+
const MIGRATION_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
456
|
+
function assertValidMigrationName(name) {
|
|
457
|
+
if (!MIGRATION_NAME_PATTERN.test(name)) {
|
|
458
|
+
throw new Error(`Invalid migration name '${name}'. Use letters, digits, "_" or "-", starting with a letter.`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
422
461
|
function getLabel(tenantId) {
|
|
423
462
|
return tenantId ? `[${tenantId}]` : '[default]';
|
|
424
463
|
}
|
|
464
|
+
function toErrorMessage(error) {
|
|
465
|
+
return error instanceof Error ? error.message : String(error);
|
|
466
|
+
}
|
|
467
|
+
function failure(label, tenantId, database, error) {
|
|
468
|
+
const errorMessage = toErrorMessage(error);
|
|
469
|
+
console.error(`${label} ā Failed: ${errorMessage}`);
|
|
470
|
+
return {
|
|
471
|
+
tenantId,
|
|
472
|
+
database,
|
|
473
|
+
success: false,
|
|
474
|
+
error: errorMessage
|
|
475
|
+
};
|
|
476
|
+
}
|
|
425
477
|
async function withDataSource(config, tenantId, operation) {
|
|
426
478
|
const { database } = (0,datasource_config_builder/* .getDatabaseForTenant */.LH)(config, tenantId);
|
|
427
479
|
const label = getLabel(tenantId);
|
|
480
|
+
let ds;
|
|
428
481
|
try {
|
|
429
|
-
|
|
482
|
+
ds = await (0,datasource_factory/* .initializeDataSource */.w)({
|
|
430
483
|
config,
|
|
431
484
|
tenantId
|
|
432
485
|
});
|
|
433
486
|
const partial = await operation(ds, label);
|
|
434
|
-
await ds.destroy();
|
|
435
487
|
return {
|
|
436
488
|
tenantId,
|
|
437
489
|
database,
|
|
@@ -439,20 +491,21 @@ async function withDataSource(config, tenantId, operation) {
|
|
|
439
491
|
...partial
|
|
440
492
|
};
|
|
441
493
|
} catch (error) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
494
|
+
return failure(label, tenantId, database, error);
|
|
495
|
+
} finally{
|
|
496
|
+
if (ds) {
|
|
497
|
+
try {
|
|
498
|
+
await ds.destroy();
|
|
499
|
+
} catch (error) {
|
|
500
|
+
console.error(`${label} ā Failed to close connection: ${toErrorMessage(error)}`);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
450
503
|
}
|
|
451
504
|
}
|
|
452
505
|
function ensureMigrationsFolder(basePath, tenantId) {
|
|
453
506
|
const folder = (0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(basePath, tenantId);
|
|
454
|
-
if (!
|
|
455
|
-
|
|
507
|
+
if (!external_fs_.existsSync(folder)) {
|
|
508
|
+
external_fs_.mkdirSync(folder, {
|
|
456
509
|
recursive: true
|
|
457
510
|
});
|
|
458
511
|
console.log(`š Created: ${folder}`);
|
|
@@ -460,7 +513,8 @@ function ensureMigrationsFolder(basePath, tenantId) {
|
|
|
460
513
|
return folder;
|
|
461
514
|
}
|
|
462
515
|
async function generateMigration(config, name, tenantId, datasourcePath) {
|
|
463
|
-
|
|
516
|
+
assertValidMigrationName(name);
|
|
517
|
+
const label = getLabel(tenantId);
|
|
464
518
|
const folder = ensureMigrationsFolder(config.migrationsPath, tenantId);
|
|
465
519
|
console.log(`${label} Generating migration: ${name}`);
|
|
466
520
|
console.log(`${label} Target folder: ${folder}`);
|
|
@@ -483,14 +537,11 @@ async function generateMigration(config, name, tenantId, datasourcePath) {
|
|
|
483
537
|
}
|
|
484
538
|
console.log(`${label} ā Generated!`);
|
|
485
539
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
* mentions an ignored table or index name. Returns how many statements were removed.
|
|
489
|
-
*/ function stripIgnoredTableStatements(folder, name, ignoreTable) {
|
|
490
|
-
const file = __rspack_external_fs.readdirSync(folder).filter((f)=>f.endsWith(`-${name}.ts`)).sort().pop();
|
|
540
|
+
function stripIgnoredTableStatements(folder, name, ignoreTable) {
|
|
541
|
+
const file = external_fs_.readdirSync(folder).filter((f)=>f.endsWith(`-${name}.ts`)).toSorted().at(-1);
|
|
491
542
|
if (!file) return 0;
|
|
492
543
|
const filePath = external_path_.join(folder, file);
|
|
493
|
-
const lines =
|
|
544
|
+
const lines = external_fs_.readFileSync(filePath, 'utf8').split('\n');
|
|
494
545
|
let removed = 0;
|
|
495
546
|
const kept = lines.filter((line)=>{
|
|
496
547
|
if (!line.includes('queryRunner.query(')) return true;
|
|
@@ -501,7 +552,7 @@ async function generateMigration(config, name, tenantId, datasourcePath) {
|
|
|
501
552
|
if (touches) removed++;
|
|
502
553
|
return !touches;
|
|
503
554
|
});
|
|
504
|
-
if (removed > 0)
|
|
555
|
+
if (removed > 0) external_fs_.writeFileSync(filePath, kept.join('\n'));
|
|
505
556
|
return removed;
|
|
506
557
|
}
|
|
507
558
|
function findDatasourcePath() {
|
|
@@ -510,7 +561,7 @@ function findDatasourcePath() {
|
|
|
510
561
|
'projects/flusysnest/src/persistence/migration.config.ts'
|
|
511
562
|
];
|
|
512
563
|
for (const p of possiblePaths){
|
|
513
|
-
if (
|
|
564
|
+
if (external_fs_.existsSync(external_path_.join(process.cwd(), p))) {
|
|
514
565
|
return p;
|
|
515
566
|
}
|
|
516
567
|
}
|
|
@@ -539,21 +590,30 @@ async function revertMigration(config, tenantId) {
|
|
|
539
590
|
}
|
|
540
591
|
function clearMigrationFiles(basePath, tenantId, label) {
|
|
541
592
|
const folder = (0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(basePath, tenantId);
|
|
542
|
-
if (!
|
|
543
|
-
const files =
|
|
544
|
-
files.forEach((file)=>
|
|
593
|
+
if (!external_fs_.existsSync(folder)) return;
|
|
594
|
+
const files = external_fs_.readdirSync(folder).filter((f)=>f.endsWith('.ts') || f.endsWith('.js'));
|
|
595
|
+
files.forEach((file)=>external_fs_.rmSync(external_path_.join(folder, file)));
|
|
545
596
|
console.log(`${label} ā Removed ${files.length} migration file(s) from ${folder}`);
|
|
546
597
|
}
|
|
598
|
+
function quoteIdentifier(name, quote) {
|
|
599
|
+
return `${quote}${name.split(quote).join(quote + quote)}${quote}`;
|
|
600
|
+
}
|
|
547
601
|
async function dropAndRecreateDatabase(dbConfig, label) {
|
|
548
602
|
const { type, database } = dbConfig;
|
|
603
|
+
if (!database) {
|
|
604
|
+
throw new Error('Database reset requires a database name');
|
|
605
|
+
}
|
|
549
606
|
if (type === 'sqlite') {
|
|
550
|
-
if (
|
|
607
|
+
if (external_fs_.existsSync(database)) external_fs_.rmSync(database);
|
|
551
608
|
console.log(`${label} ā Deleted sqlite file`);
|
|
552
609
|
return;
|
|
553
610
|
}
|
|
554
611
|
if (type !== 'mysql' && type !== 'mariadb' && type !== 'postgres') {
|
|
555
612
|
throw new Error(`Database reset is not supported for type '${type}'`);
|
|
556
613
|
}
|
|
614
|
+
if (type === 'postgres' && database === 'postgres') {
|
|
615
|
+
throw new Error("Refusing to reset the 'postgres' maintenance database");
|
|
616
|
+
}
|
|
557
617
|
const adminDs = new external_typeorm_.DataSource({
|
|
558
618
|
type,
|
|
559
619
|
host: dbConfig.host,
|
|
@@ -568,11 +628,13 @@ async function dropAndRecreateDatabase(dbConfig, label) {
|
|
|
568
628
|
await adminDs.query(`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()`, [
|
|
569
629
|
database
|
|
570
630
|
]);
|
|
571
|
-
|
|
572
|
-
await adminDs.query(`
|
|
631
|
+
const quoted = quoteIdentifier(database, '"');
|
|
632
|
+
await adminDs.query(`DROP DATABASE IF EXISTS ${quoted}`);
|
|
633
|
+
await adminDs.query(`CREATE DATABASE ${quoted}`);
|
|
573
634
|
} else {
|
|
574
|
-
|
|
575
|
-
await adminDs.query(`
|
|
635
|
+
const quoted = quoteIdentifier(database, '`');
|
|
636
|
+
await adminDs.query(`DROP DATABASE IF EXISTS ${quoted}`);
|
|
637
|
+
await adminDs.query(`CREATE DATABASE ${quoted} CHARACTER SET utf8mb4`);
|
|
576
638
|
}
|
|
577
639
|
console.log(`${label} ā Database '${database}' dropped and recreated`);
|
|
578
640
|
} finally{
|
|
@@ -592,14 +654,7 @@ async function resetDatabase(config, tenantId) {
|
|
|
592
654
|
success: true
|
|
593
655
|
};
|
|
594
656
|
} catch (error) {
|
|
595
|
-
|
|
596
|
-
console.error(`${label} ā Failed: ${errorMessage}`);
|
|
597
|
-
return {
|
|
598
|
-
tenantId,
|
|
599
|
-
database,
|
|
600
|
-
success: false,
|
|
601
|
-
error: errorMessage
|
|
602
|
-
};
|
|
657
|
+
return failure(label, tenantId, database, error);
|
|
603
658
|
}
|
|
604
659
|
}
|
|
605
660
|
async function migrationStatus(config, tenantId) {
|
|
@@ -613,24 +668,28 @@ async function migrationStatus(config, tenantId) {
|
|
|
613
668
|
};
|
|
614
669
|
});
|
|
615
670
|
}
|
|
671
|
+
const TENANT_ACTIONS = {
|
|
672
|
+
run: runMigrations,
|
|
673
|
+
revert: revertMigration,
|
|
674
|
+
status: migrationStatus
|
|
675
|
+
};
|
|
676
|
+
function isTenantMigrationAction(action) {
|
|
677
|
+
return Object.hasOwn(TENANT_ACTIONS, action);
|
|
678
|
+
}
|
|
616
679
|
async function runForAllTenants(config, command) {
|
|
680
|
+
if (!isTenantMigrationAction(command)) {
|
|
681
|
+
throw new Error(`Unsupported multi-tenant command '${command}'. Use run, revert or status.`);
|
|
682
|
+
}
|
|
683
|
+
const action = TENANT_ACTIONS[command];
|
|
617
684
|
const tenants = (0,datasource_config_builder/* .getActiveTenants */.SP)(config);
|
|
618
685
|
console.log(`\nš Running '${command}' for ${tenants.length} tenant(s)...\n`);
|
|
619
686
|
const results = [];
|
|
620
687
|
for (const tenant of tenants){
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
break;
|
|
626
|
-
case 'revert':
|
|
627
|
-
result = await revertMigration(config, tenant.id);
|
|
628
|
-
break;
|
|
629
|
-
case 'status':
|
|
630
|
-
result = await migrationStatus(config, tenant.id);
|
|
631
|
-
break;
|
|
688
|
+
try {
|
|
689
|
+
results.push(await action(config, tenant.id));
|
|
690
|
+
} catch (error) {
|
|
691
|
+
results.push(failure(getLabel(tenant.id), tenant.id, tenant.database, error));
|
|
632
692
|
}
|
|
633
|
-
results.push(result);
|
|
634
693
|
console.log('');
|
|
635
694
|
}
|
|
636
695
|
console.log('\n=== Summary ===');
|
|
@@ -645,9 +704,7 @@ async function runForAllTenants(config, command) {
|
|
|
645
704
|
|
|
646
705
|
},
|
|
647
706
|
722(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
648
|
-
|
|
649
|
-
* Build DataSource options from database config
|
|
650
|
-
*/ function buildDataSourceOptions(config, entities, migrationsPath, migrationsTableName = 'migrations') {
|
|
707
|
+
function buildDataSourceOptions(config, entities, migrationsPath, migrationsTableName = 'migrations') {
|
|
651
708
|
return {
|
|
652
709
|
type: config.type,
|
|
653
710
|
host: config.host,
|
|
@@ -663,24 +720,20 @@ async function runForAllTenants(config, command) {
|
|
|
663
720
|
synchronize: false
|
|
664
721
|
};
|
|
665
722
|
}
|
|
666
|
-
|
|
667
|
-
* Get migration folder path for a tenant
|
|
668
|
-
*/ function getMigrationsFolderPath(basePath, tenantId) {
|
|
723
|
+
function getMigrationsFolderPath(basePath, tenantId) {
|
|
669
724
|
const folder = tenantId || 'default';
|
|
670
725
|
return `${basePath}/${folder}`;
|
|
671
726
|
}
|
|
672
|
-
|
|
673
|
-
* Get migrations glob pattern for a tenant
|
|
674
|
-
*/ function getMigrationsGlobPattern(basePath, tenantId) {
|
|
727
|
+
function getMigrationsGlobPattern(basePath, tenantId) {
|
|
675
728
|
return `${getMigrationsFolderPath(basePath, tenantId)}/*{.ts,.js}`;
|
|
676
729
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const tenant = config.tenants.find((t)=>t.id === tenantId);
|
|
730
|
+
function getDatabaseForTenant(config, tenantId) {
|
|
731
|
+
if (tenantId) {
|
|
732
|
+
const tenants = config.tenants ?? [];
|
|
733
|
+
const tenant = tenants.find((t)=>t.id === tenantId);
|
|
682
734
|
if (!tenant) {
|
|
683
|
-
|
|
735
|
+
const available = tenants.map((t)=>t.id).join(', ') || 'none';
|
|
736
|
+
throw new Error(`Tenant '${tenantId}' not found. Available: ${available}`);
|
|
684
737
|
}
|
|
685
738
|
return {
|
|
686
739
|
database: tenant.database,
|
|
@@ -688,21 +741,20 @@ async function runForAllTenants(config, command) {
|
|
|
688
741
|
tenantConfig: tenant
|
|
689
742
|
};
|
|
690
743
|
}
|
|
691
|
-
// Default config - use bootstrapAppConfig for feature flags
|
|
692
744
|
const defaultTenantConfig = {
|
|
693
745
|
id: 'default',
|
|
694
746
|
database: config.defaultDatabaseConfig.database || 'default',
|
|
695
747
|
enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false,
|
|
696
|
-
permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL'
|
|
748
|
+
permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL',
|
|
749
|
+
enableEmailVerification: config.bootstrapAppConfig?.enableEmailVerification,
|
|
750
|
+
enableSignUp: config.bootstrapAppConfig?.enableSignUp
|
|
697
751
|
};
|
|
698
752
|
return {
|
|
699
753
|
database: defaultTenantConfig.database,
|
|
700
754
|
tenantConfig: defaultTenantConfig
|
|
701
755
|
};
|
|
702
756
|
}
|
|
703
|
-
|
|
704
|
-
* Resolve full connection details for a tenant.
|
|
705
|
-
*/ function resolveDatabaseConnection(config, tenantId) {
|
|
757
|
+
function resolveDatabaseConnection(config, tenantId) {
|
|
706
758
|
const { database, tenant } = getDatabaseForTenant(config, tenantId);
|
|
707
759
|
return tenant ? {
|
|
708
760
|
...config.defaultDatabaseConfig,
|
|
@@ -716,17 +768,13 @@ async function runForAllTenants(config, command) {
|
|
|
716
768
|
database
|
|
717
769
|
};
|
|
718
770
|
}
|
|
719
|
-
|
|
720
|
-
* Resolve entities based on tenant config
|
|
721
|
-
*/ function resolveEntities(config, tenantConfig) {
|
|
771
|
+
function resolveEntities(config, tenantConfig) {
|
|
722
772
|
if (typeof config.entities === 'function') {
|
|
723
773
|
return config.entities(tenantConfig);
|
|
724
774
|
}
|
|
725
775
|
return config.entities;
|
|
726
776
|
}
|
|
727
|
-
|
|
728
|
-
* Get active tenants from config
|
|
729
|
-
*/ function getActiveTenants(config) {
|
|
777
|
+
function getActiveTenants(config) {
|
|
730
778
|
return config.tenants || [];
|
|
731
779
|
}
|
|
732
780
|
|
|
@@ -741,6 +789,12 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
741
789
|
});
|
|
742
790
|
|
|
743
791
|
|
|
792
|
+
},
|
|
793
|
+
421(module) {
|
|
794
|
+
|
|
795
|
+
module.exports = __rspack_external_fs;
|
|
796
|
+
|
|
797
|
+
|
|
744
798
|
},
|
|
745
799
|
521(module) {
|
|
746
800
|
|