fiberx-backend-toolkit 0.0.26 → 0.0.28
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/dist/database/connectors/base_connector.d.ts +1 -1
- package/dist/database/connectors/sequelize_connector.d.ts +2 -2
- package/dist/database/connectors/sequelize_connector.js +44 -36
- package/dist/database/scripts/migration_runner_script.d.ts +1 -1
- package/dist/database/scripts/migration_runner_script.js +16 -15
- package/dist/database/scripts/seeder_runner_script.d.ts +1 -1
- package/dist/database/scripts/seeder_runner_script.js +14 -13
- package/package.json +1 -1
|
@@ -7,6 +7,6 @@ declare abstract class BaseConnector<TConnection> {
|
|
|
7
7
|
readonly env_manager: EnvManagerUtil;
|
|
8
8
|
readonly logger: LoggerUtil;
|
|
9
9
|
protected constructor(connector_name: string);
|
|
10
|
-
abstract connect(): TConnection
|
|
10
|
+
abstract connect(): Promise<TConnection>;
|
|
11
11
|
}
|
|
12
12
|
export default BaseConnector;
|
|
@@ -8,8 +8,8 @@ declare class SequelizeConnector extends BaseConnector<Sequelize> {
|
|
|
8
8
|
static getInstance(): SequelizeConnector;
|
|
9
9
|
private formatSQLQueryLog;
|
|
10
10
|
private ensureDatabaseExists;
|
|
11
|
-
connect(options?: SequelizeConnectionOptions): Sequelize
|
|
12
|
-
connectNamed(name: string, options?: SequelizeConnectionOptions): Sequelize
|
|
11
|
+
connect(options?: SequelizeConnectionOptions): Promise<Sequelize>;
|
|
12
|
+
connectNamed(name: string, options?: SequelizeConnectionOptions): Promise<Sequelize>;
|
|
13
13
|
getConnection(name?: string): Sequelize | undefined;
|
|
14
14
|
hasConnection(name?: string): boolean;
|
|
15
15
|
closeConnection(name?: string): Promise<void>;
|
|
@@ -55,6 +55,7 @@ class SequelizeConnector extends base_connector_1.default {
|
|
|
55
55
|
if (dialect === "mssql") {
|
|
56
56
|
admin_db = "master";
|
|
57
57
|
}
|
|
58
|
+
this.logger.info(`🔌 Connecting to admin database: ${admin_db}`);
|
|
58
59
|
const admin = new sequelize_1.Sequelize(admin_db, username, password, {
|
|
59
60
|
host,
|
|
60
61
|
port,
|
|
@@ -62,6 +63,7 @@ class SequelizeConnector extends base_connector_1.default {
|
|
|
62
63
|
logging: false,
|
|
63
64
|
});
|
|
64
65
|
await admin.authenticate();
|
|
66
|
+
this.logger.success(`🔌 Connected to admin database: ${admin_db}`);
|
|
65
67
|
this.logger.info(`🔍 Checking database existence: ${database}`);
|
|
66
68
|
switch (dialect) {
|
|
67
69
|
case "postgres":
|
|
@@ -95,47 +97,53 @@ class SequelizeConnector extends base_connector_1.default {
|
|
|
95
97
|
// ----------------------------------
|
|
96
98
|
// Single default connection
|
|
97
99
|
// ----------------------------------
|
|
98
|
-
connect(options = {}) {
|
|
99
|
-
return this.connectNamed(options.name ?? "default", options);
|
|
100
|
+
async connect(options = {}) {
|
|
101
|
+
return await this.connectNamed(options.name ?? "default", options);
|
|
100
102
|
}
|
|
101
103
|
// ----------------------------------
|
|
102
104
|
// Multiple named connections
|
|
103
105
|
// ----------------------------------
|
|
104
|
-
connectNamed(name, options = {}) {
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
async connectNamed(name, options = {}) {
|
|
107
|
+
try {
|
|
108
|
+
if (SequelizeConnector.connections.has(name)) {
|
|
109
|
+
return SequelizeConnector.connections.get(name);
|
|
110
|
+
}
|
|
111
|
+
const dialect = options.dialect ?? this.env_manager.getEnvVar("DB_DIALECT", "postgres");
|
|
112
|
+
const logging_enabled = options.logging ?? this.env_manager.getEnvVar("DB_LOGGING", false);
|
|
113
|
+
const logging = logging_enabled ? this.formatSQLQueryLog : false;
|
|
114
|
+
const database = options.database ?? this.env_manager.getEnvVar("DB_NAME");
|
|
115
|
+
const username = options.username ?? this.env_manager.getEnvVar("DB_USER");
|
|
116
|
+
const password = options.password ?? this.env_manager.getEnvVar("DB_PASSWORD");
|
|
117
|
+
const host = options.host ?? this.env_manager.getEnvVar("DB_HOST");
|
|
118
|
+
const port = options.port ?? this.env_manager.getEnvVar("DB_PORT", 5432);
|
|
119
|
+
// 🔴 IMPORTANT: Attempt to ensure DB exists BEFORE connecting
|
|
120
|
+
await this.ensureDatabaseExists({
|
|
121
|
+
dialect,
|
|
122
|
+
database,
|
|
123
|
+
username,
|
|
124
|
+
password,
|
|
125
|
+
host,
|
|
126
|
+
port,
|
|
127
|
+
logging,
|
|
128
|
+
}).catch(() => { });
|
|
129
|
+
const sequelize = new sequelize_1.Sequelize({
|
|
130
|
+
database,
|
|
131
|
+
username,
|
|
132
|
+
password,
|
|
133
|
+
host,
|
|
134
|
+
port,
|
|
135
|
+
dialect,
|
|
136
|
+
logging,
|
|
137
|
+
benchmark: logging_enabled,
|
|
138
|
+
});
|
|
139
|
+
SequelizeConnector.connections.set(name, sequelize);
|
|
140
|
+
this.logger.success(`✅ Sequelize connected [${name}]`);
|
|
141
|
+
return sequelize;
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
this.logger.error(`❌ Failed to connect Sequelize [${name}]`, { error });
|
|
145
|
+
throw error;
|
|
107
146
|
}
|
|
108
|
-
const dialect = options.dialect ?? this.env_manager.getEnvVar("DB_DIALECT", "postgres");
|
|
109
|
-
const logging_enabled = options.logging ?? this.env_manager.getEnvVar("DB_LOGGING", false);
|
|
110
|
-
const logging = logging_enabled ? this.formatSQLQueryLog : false;
|
|
111
|
-
const database = options.database ?? this.env_manager.getEnvVar("DB_NAME");
|
|
112
|
-
const username = options.username ?? this.env_manager.getEnvVar("DB_USER");
|
|
113
|
-
const password = options.password ?? this.env_manager.getEnvVar("DB_PASSWORD");
|
|
114
|
-
const host = options.host ?? this.env_manager.getEnvVar("DB_HOST");
|
|
115
|
-
const port = options.port ?? this.env_manager.getEnvVar("DB_PORT", 5432);
|
|
116
|
-
// 🔴 IMPORTANT: Attempt to ensure DB exists BEFORE connecting
|
|
117
|
-
this.ensureDatabaseExists({
|
|
118
|
-
dialect,
|
|
119
|
-
database,
|
|
120
|
-
username,
|
|
121
|
-
password,
|
|
122
|
-
host,
|
|
123
|
-
port,
|
|
124
|
-
logging,
|
|
125
|
-
}).catch(() => { });
|
|
126
|
-
const sequelize = new sequelize_1.Sequelize({
|
|
127
|
-
database,
|
|
128
|
-
username,
|
|
129
|
-
password,
|
|
130
|
-
host,
|
|
131
|
-
port,
|
|
132
|
-
dialect,
|
|
133
|
-
logging,
|
|
134
|
-
benchmark: logging_enabled,
|
|
135
|
-
});
|
|
136
|
-
SequelizeConnector.connections.set(name, sequelize);
|
|
137
|
-
this.logger.success(`✅ Sequelize connected [${name}]`);
|
|
138
|
-
return sequelize;
|
|
139
147
|
}
|
|
140
148
|
// ----------------------------------
|
|
141
149
|
// Utilities
|
|
@@ -4,7 +4,7 @@ declare class MigrationRunnerScript {
|
|
|
4
4
|
private readonly migrations_dir;
|
|
5
5
|
private readonly db_connection;
|
|
6
6
|
private readonly logger;
|
|
7
|
-
private
|
|
7
|
+
private sequelize_instance;
|
|
8
8
|
constructor();
|
|
9
9
|
private getMigrationFiles;
|
|
10
10
|
private ensureMetaTable;
|
|
@@ -14,10 +14,9 @@ class MigrationRunnerScript {
|
|
|
14
14
|
migrations_dir = constants_1.MIGRATIONS_DIR;
|
|
15
15
|
db_connection = main_1.SequelizeConnector?.getInstance?.();
|
|
16
16
|
logger = new main_2.LoggerUtil(this.module_name);
|
|
17
|
-
sequelize_instance;
|
|
17
|
+
sequelize_instance = null;
|
|
18
18
|
constructor() {
|
|
19
19
|
main_2.InputValidatorUtil.dirExists(this.migrations_dir, true);
|
|
20
|
-
this.sequelize_instance = this.db_connection.connect();
|
|
21
20
|
}
|
|
22
21
|
// Fetch all migration files
|
|
23
22
|
getMigrationFiles(schema_name) {
|
|
@@ -28,8 +27,8 @@ class MigrationRunnerScript {
|
|
|
28
27
|
return files;
|
|
29
28
|
}
|
|
30
29
|
// Ensure SequelizeMeta table exists
|
|
31
|
-
async ensureMetaTable() {
|
|
32
|
-
const queryInterface =
|
|
30
|
+
async ensureMetaTable(sequelize_instance) {
|
|
31
|
+
const queryInterface = sequelize_instance.getQueryInterface();
|
|
33
32
|
const table_names = await queryInterface.showAllTables();
|
|
34
33
|
if (!table_names.includes(constants_1.SEQUELIZE_META_TABLE_NAME)) {
|
|
35
34
|
this.logger.info("⏳ Creating SequelizeMeta table...");
|
|
@@ -44,12 +43,12 @@ class MigrationRunnerScript {
|
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
// Fetch already applied migrations
|
|
47
|
-
async getAppliedMigrations() {
|
|
48
|
-
const [results] = await
|
|
46
|
+
async getAppliedMigrations(sequelize_instance) {
|
|
47
|
+
const [results] = await sequelize_instance.query(`SELECT name FROM \"${constants_1.SEQUELIZE_META_TABLE_NAME}\";`);
|
|
49
48
|
return Array.isArray(results) ? results.map((r) => r.name) : [];
|
|
50
49
|
}
|
|
51
50
|
// Apply a single migration
|
|
52
|
-
async applyMigration(file_path, operation) {
|
|
51
|
+
async applyMigration(sequelize_instance, file_path, operation) {
|
|
53
52
|
const MigrationClass = require(file_path)?.default;
|
|
54
53
|
if (!MigrationClass) {
|
|
55
54
|
const log_msg = `Migration file has no default export: ${file_path}`;
|
|
@@ -57,22 +56,24 @@ class MigrationRunnerScript {
|
|
|
57
56
|
throw new Error(log_msg);
|
|
58
57
|
}
|
|
59
58
|
const migration_instance = new MigrationClass();
|
|
60
|
-
const queryInterface =
|
|
59
|
+
const queryInterface = sequelize_instance.getQueryInterface();
|
|
61
60
|
if (operation === "up") {
|
|
62
|
-
await migration_instance.up.bind(migration_instance)(queryInterface,
|
|
63
|
-
await
|
|
61
|
+
await migration_instance.up.bind(migration_instance)(queryInterface, sequelize_instance);
|
|
62
|
+
await sequelize_instance.getQueryInterface().bulkInsert(constants_1.SEQUELIZE_META_TABLE_NAME, [{ name: path_1.default.basename(file_path) }]);
|
|
64
63
|
}
|
|
65
64
|
else {
|
|
66
|
-
await migration_instance.down.bind(migration_instance)(queryInterface,
|
|
67
|
-
await
|
|
65
|
+
await migration_instance.down.bind(migration_instance)(queryInterface, sequelize_instance);
|
|
66
|
+
await sequelize_instance.getQueryInterface().bulkDelete(constants_1.SEQUELIZE_META_TABLE_NAME, { name: path_1.default.basename(file_path) });
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
// Main runner
|
|
71
70
|
async run(operation, schema_name, migration_file) {
|
|
72
71
|
this.logger.info(`🏃♂️ Starting Migration process Operation: ${operation.toUpperCase()}, Schema: ${schema_name}, File: ${migration_file || "all"}`);
|
|
72
|
+
this.logger.info("🔌 Connecting to database...");
|
|
73
|
+
this.sequelize_instance = await this.db_connection.connect();
|
|
73
74
|
await this.sequelize_instance?.authenticate();
|
|
74
75
|
this.logger.success("✅ Database connected.");
|
|
75
|
-
await this.ensureMetaTable();
|
|
76
|
+
await this.ensureMetaTable(this.sequelize_instance);
|
|
76
77
|
let files = [];
|
|
77
78
|
if (migration_file) {
|
|
78
79
|
const full_path = path_1.default.join(this.migrations_dir, migration_file);
|
|
@@ -99,7 +100,7 @@ class MigrationRunnerScript {
|
|
|
99
100
|
? a_priority - b_priority
|
|
100
101
|
: b_priority - a_priority;
|
|
101
102
|
});
|
|
102
|
-
const applied_migrations = await this.getAppliedMigrations();
|
|
103
|
+
const applied_migrations = await this.getAppliedMigrations(this.sequelize_instance);
|
|
103
104
|
for (const file of files) {
|
|
104
105
|
const file_name = path_1.default.basename(file);
|
|
105
106
|
if (operation === "up" && applied_migrations.includes(file_name)) {
|
|
@@ -111,7 +112,7 @@ class MigrationRunnerScript {
|
|
|
111
112
|
continue;
|
|
112
113
|
}
|
|
113
114
|
this.logger.info(`${operation === "up" ? "Applying" : "Reverting"} migration: ${file_name}`);
|
|
114
|
-
await this.applyMigration(file, operation);
|
|
115
|
+
await this.applyMigration(this.sequelize_instance, file, operation);
|
|
115
116
|
this.logger.success(`${operation === "up" ? "Applied" : "Reverted"} migration: ${file_name}`);
|
|
116
117
|
}
|
|
117
118
|
this.logger.success(`🏁 Migration process completed.`);
|
|
@@ -4,7 +4,7 @@ declare class SeederRunnerScript {
|
|
|
4
4
|
private readonly seeders_dir;
|
|
5
5
|
private readonly db_connection;
|
|
6
6
|
private readonly logger;
|
|
7
|
-
private
|
|
7
|
+
private sequelize_instance;
|
|
8
8
|
constructor();
|
|
9
9
|
private getSeederFiles;
|
|
10
10
|
private ensureMetaTable;
|
|
@@ -14,10 +14,9 @@ class SeederRunnerScript {
|
|
|
14
14
|
seeders_dir = constants_1.SEEDERS_DIR;
|
|
15
15
|
db_connection = main_1.SequelizeConnector?.getInstance?.();
|
|
16
16
|
logger = new main_2.LoggerUtil(this.name);
|
|
17
|
-
sequelize_instance;
|
|
17
|
+
sequelize_instance = null;
|
|
18
18
|
constructor() {
|
|
19
19
|
main_2.InputValidatorUtil.dirExists(this.seeders_dir, true);
|
|
20
|
-
this.sequelize_instance = this.db_connection.connect();
|
|
21
20
|
}
|
|
22
21
|
// Get all seeder files
|
|
23
22
|
getSeederFiles(seeder_name) {
|
|
@@ -28,8 +27,8 @@ class SeederRunnerScript {
|
|
|
28
27
|
return files;
|
|
29
28
|
}
|
|
30
29
|
// Ensure SequelizeSeederMeta table exists
|
|
31
|
-
async ensureMetaTable() {
|
|
32
|
-
const queryInterface =
|
|
30
|
+
async ensureMetaTable(sequelize_instance) {
|
|
31
|
+
const queryInterface = sequelize_instance.getQueryInterface();
|
|
33
32
|
const tables = await queryInterface.showAllTables();
|
|
34
33
|
if (!tables.includes(constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME)) {
|
|
35
34
|
this.logger.info("⏳ Creating Sequelize seeder meta table table...");
|
|
@@ -40,24 +39,24 @@ class SeederRunnerScript {
|
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
// Get already applied seeders
|
|
43
|
-
async getAppliedSeeders() {
|
|
44
|
-
const [results] = await
|
|
42
|
+
async getAppliedSeeders(sequelize_instance) {
|
|
43
|
+
const [results] = await sequelize_instance.query(`SELECT name FROM "${constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME}";`);
|
|
45
44
|
return Array.isArray(results) ? results.map((r) => r.name) : [];
|
|
46
45
|
}
|
|
47
46
|
// Apply a single seeder
|
|
48
|
-
async applySeeder(file_path, operation) {
|
|
47
|
+
async applySeeder(sequelize_instance, file_path, operation) {
|
|
49
48
|
const SeederClass = require(file_path)?.default;
|
|
50
49
|
if (!SeederClass) {
|
|
51
50
|
throw new Error(`Seeder file has no default export: ${file_path}`);
|
|
52
51
|
}
|
|
53
52
|
const seeder_instance = new SeederClass();
|
|
54
|
-
const queryInterface =
|
|
53
|
+
const queryInterface = sequelize_instance.getQueryInterface();
|
|
55
54
|
if (operation === "up") {
|
|
56
|
-
await seeder_instance.up(queryInterface,
|
|
55
|
+
await seeder_instance.up(queryInterface, sequelize_instance);
|
|
57
56
|
await queryInterface.bulkInsert(constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME, [{ name: path_1.default.basename(file_path) }]);
|
|
58
57
|
}
|
|
59
58
|
else {
|
|
60
|
-
await seeder_instance.down(queryInterface,
|
|
59
|
+
await seeder_instance.down(queryInterface, sequelize_instance);
|
|
61
60
|
await queryInterface.bulkDelete(constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME, { name: path_1.default.basename(file_path) });
|
|
62
61
|
}
|
|
63
62
|
}
|
|
@@ -65,9 +64,11 @@ class SeederRunnerScript {
|
|
|
65
64
|
async run(operation, seeder_name, seeder_file) {
|
|
66
65
|
try {
|
|
67
66
|
this.logger.info(`🏃♂️ Starting seeder process Operation: ${operation.toUpperCase()}, Seeder: ${seeder_name || "all"}, File: ${seeder_file || "none"}`);
|
|
67
|
+
this.logger.info("🔌 Connecting to database...");
|
|
68
|
+
this.sequelize_instance = await this.db_connection.connect();
|
|
68
69
|
await this.sequelize_instance?.authenticate();
|
|
69
70
|
this.logger.success("✅ Database connected.");
|
|
70
|
-
await this.ensureMetaTable();
|
|
71
|
+
await this.ensureMetaTable(this.sequelize_instance);
|
|
71
72
|
let files = [];
|
|
72
73
|
if (seeder_file) {
|
|
73
74
|
const full_path = path_1.default.join(this.seeders_dir, seeder_file);
|
|
@@ -82,7 +83,7 @@ class SeederRunnerScript {
|
|
|
82
83
|
}
|
|
83
84
|
// Sort by filename: ascending for UP, descending for DOWN
|
|
84
85
|
files.sort((a, b) => (operation === "up" ? a.localeCompare(b) : b.localeCompare(a)));
|
|
85
|
-
const applied_seeders = await this.getAppliedSeeders();
|
|
86
|
+
const applied_seeders = await this.getAppliedSeeders(this.sequelize_instance);
|
|
86
87
|
for (const file of files) {
|
|
87
88
|
const file_name = path_1.default.basename(file);
|
|
88
89
|
if (operation === "up" && applied_seeders.includes(file_name)) {
|
|
@@ -94,7 +95,7 @@ class SeederRunnerScript {
|
|
|
94
95
|
continue;
|
|
95
96
|
}
|
|
96
97
|
this.logger.info(`${operation === "up" ? "Applying" : "Reverting"} seeder: ${file_name}`);
|
|
97
|
-
await this.applySeeder(file, operation);
|
|
98
|
+
await this.applySeeder(this.sequelize_instance, file, operation);
|
|
98
99
|
this.logger.success(`${operation === "up" ? "Applied" : "Reverted"} seeder: ${file_name}`);
|
|
99
100
|
}
|
|
100
101
|
this.logger.success("🏁 Seeder process completed.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fiberx-backend-toolkit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
4
4
|
"description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./dist/index.js",
|