fiberx-backend-toolkit 0.0.14 → 0.0.16
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/code_templates/sequelize_code_template.d.ts +8 -0
- package/dist/code_templates/sequelize_code_template.js +382 -0
- package/dist/config/constants.d.ts +20 -0
- package/dist/config/constants.js +24 -0
- package/dist/database/connectors/base_connector.d.ts +12 -0
- package/dist/database/connectors/base_connector.js +14 -0
- package/dist/database/connectors/sequelize_connector.d.ts +17 -0
- package/dist/database/connectors/sequelize_connector.js +93 -0
- package/dist/database/index.d.ts +11 -141
- package/dist/database/index.js +15 -1309
- package/dist/database/schema/schema_diff_util.d.ts +6 -0
- package/dist/database/schema/schema_diff_util.js +68 -0
- package/dist/database/schema/schema_normalizer_util.d.ts +23 -0
- package/dist/database/schema/schema_normalizer_util.js +39 -0
- package/dist/database/scripts/create_schema_script.d.ts +13 -0
- package/dist/database/scripts/create_schema_script.js +82 -0
- package/dist/database/scripts/create_seeder_script.d.ts +15 -0
- package/dist/database/scripts/create_seeder_script.js +86 -0
- package/dist/database/scripts/make_migrations_script.d.ts +16 -0
- package/dist/database/scripts/make_migrations_script.js +172 -0
- package/dist/database/scripts/migration_runner_script.d.ts +15 -0
- package/dist/database/scripts/migration_runner_script.js +139 -0
- package/dist/database/scripts/seeder_runner_script.d.ts +15 -0
- package/dist/database/scripts/seeder_runner_script.js +126 -0
- package/dist/database/scripts/sequelize_model_generator_script.d.ts +11 -0
- package/dist/database/scripts/sequelize_model_generator_script.js +108 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +5 -1
- package/dist/middle_ware/cookie_manager_middle_ware.d.ts +22 -0
- package/dist/middle_ware/cookie_manager_middle_ware.js +65 -0
- package/dist/middle_ware/cors_middle_ware.d.ts +20 -0
- package/dist/middle_ware/cors_middle_ware.js +120 -0
- package/dist/middle_ware/https_enforcement_middle_ware.d.ts +12 -0
- package/dist/middle_ware/https_enforcement_middle_ware.js +41 -0
- package/dist/middle_ware/index.d.ts +8 -107
- package/dist/middle_ware/index.js +8 -443
- package/dist/middle_ware/rate_limiter_middle_ware.d.ts +16 -0
- package/dist/middle_ware/rate_limiter_middle_ware.js +75 -0
- package/dist/middle_ware/request_logger_middle_ware.d.ts +8 -0
- package/dist/middle_ware/request_logger_middle_ware.js +35 -0
- package/dist/middle_ware/response_formatter_middle_ware.d.ts +16 -0
- package/dist/middle_ware/response_formatter_middle_ware.js +75 -0
- package/dist/middle_ware/secure_headers_middle_ware.d.ts +22 -0
- package/dist/middle_ware/secure_headers_middle_ware.js +97 -0
- package/dist/types/database_type.d.ts +16 -0
- package/dist/types/database_type.js +1 -0
- package/dist/types/index.d.ts +5 -4
- package/dist/types/index.js +5 -7
- package/dist/{middle_ware_type-D8LA7qF3.d.ts → types/middle_ware_type.d.ts} +8 -10
- package/dist/types/middle_ware_type.js +2 -0
- package/dist/types/migration_type.d.ts +5 -0
- package/dist/types/migration_type.js +1 -0
- package/dist/{database_type-C8LkcL6x.d.ts → types/schema_type.d.ts} +8 -33
- package/dist/types/schema_type.js +2 -0
- package/dist/{util_type-Yyo1nXmm.d.ts → types/util_type.d.ts} +7 -9
- package/dist/types/util_type.js +7 -0
- package/dist/utils/cache_util.d.ts +40 -0
- package/dist/utils/cache_util.js +81 -0
- package/dist/utils/env_manager_util.d.ts +16 -0
- package/dist/utils/env_manager_util.js +85 -0
- package/dist/utils/index.d.ts +9 -265
- package/dist/utils/index.js +9 -22
- package/dist/utils/input_transformer_util.d.ts +115 -0
- package/dist/utils/input_transformer_util.js +309 -0
- package/dist/utils/input_validator_util.d.ts +53 -0
- package/dist/utils/input_validator_util.js +116 -0
- package/dist/{env_manager_util-DLs1b9Q7.d.ts → utils/logger_util.d.ts} +2 -19
- package/dist/utils/logger_util.js +85 -0
- package/dist/utils/safe_execute_util.d.ts +25 -0
- package/dist/utils/safe_execute_util.js +141 -0
- package/dist/utils/server_util.d.ts +12 -0
- package/dist/utils/server_util.js +57 -0
- package/dist/utils/sql_formatter_util.d.ts +19 -0
- package/dist/utils/sql_formatter_util.js +43 -0
- package/package.json +2 -9
- package/dist/chunk-6AJLW7HC.js +0 -32
- package/dist/chunk-6AJLW7HC.js.map +0 -1
- package/dist/chunk-BLYKIQXQ.js +0 -1042
- package/dist/chunk-BLYKIQXQ.js.map +0 -1
- package/dist/database/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/middle_ware/index.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/index.js.map +0 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SchemaColumnInterface, SchemaDiffInterface } from "../types/schema_type";
|
|
2
|
+
declare const SEQUELIZE_SCHEMA_CODE_TEMPLATE: (schema_name: string, table_name: string, model_name: string, migration_priority: number) => string;
|
|
3
|
+
declare const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE: (schema_table_name: string, schema_model_name: string, schema_file_name: string) => string;
|
|
4
|
+
declare const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE: (schema_table_name: string, schema_model_name: string, diff: SchemaDiffInterface, schema_file_name: string) => string;
|
|
5
|
+
declare const SEQUELIZE_SEEDER_TEMPLATE: (class_name: string, table_name: string) => string;
|
|
6
|
+
declare const SEQUELIZE_MODEL_CODE_TEMPLATE: (schema_model_name: string, schema_file_name: string, schema_columns: Record<string, SchemaColumnInterface>) => string;
|
|
7
|
+
declare const SEQUELIZE_MODELS_INDEX_CODE_TEMPLATE: (model_names: string[], imports: string) => string;
|
|
8
|
+
export { SEQUELIZE_SCHEMA_CODE_TEMPLATE, SEQUELIZE_SEEDER_TEMPLATE, SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE, SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE, SEQUELIZE_MODEL_CODE_TEMPLATE, SEQUELIZE_MODELS_INDEX_CODE_TEMPLATE };
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
const SEQUELIZE_SCHEMA_CODE_TEMPLATE = (schema_name, table_name, model_name, migration_priority) => {
|
|
2
|
+
return `
|
|
3
|
+
import { DataTypes } from "sequelize";
|
|
4
|
+
import { SchemaDefinitionInterface } from "fiberx-backend-toolkit/types/schema_type";
|
|
5
|
+
|
|
6
|
+
const ${schema_name}: SchemaDefinitionInterface = {
|
|
7
|
+
database_name: "public",
|
|
8
|
+
migration_priority: ${migration_priority},
|
|
9
|
+
|
|
10
|
+
table_name: "${table_name}",
|
|
11
|
+
model_name: "${model_name}",
|
|
12
|
+
timestamps:true,
|
|
13
|
+
|
|
14
|
+
columns: {
|
|
15
|
+
id: {
|
|
16
|
+
type: DataTypes.BIGINT,
|
|
17
|
+
primaryKey: true,
|
|
18
|
+
autoIncrement: true,
|
|
19
|
+
allowNull: false,
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
created_at: {
|
|
23
|
+
type: DataTypes.DATE,
|
|
24
|
+
defaultValue: DataTypes.NOW,
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
updated_at: {
|
|
28
|
+
type: DataTypes.DATE,
|
|
29
|
+
allowNull: true,
|
|
30
|
+
defaultValue: null,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
indexes: [
|
|
35
|
+
{ fields: [""], name: "" },
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default ${schema_name};
|
|
40
|
+
`;
|
|
41
|
+
};
|
|
42
|
+
const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (schema_table_name, schema_model_name, schema_file_name) => `
|
|
43
|
+
import { QueryInterface, DataTypes, Sequelize } from "sequelize";
|
|
44
|
+
import { LoggerUtil } from "fiberx-backend-toolkit//utils";
|
|
45
|
+
import { SchemaDefinitionInterface } from "fiberx-backend-toolkit/types/schema_type";
|
|
46
|
+
import ${schema_model_name}Schema from "@/database/schemas/${schema_file_name.replace(".ts", "")}";
|
|
47
|
+
|
|
48
|
+
class Create${schema_model_name}TableMigration {
|
|
49
|
+
private name: string = "create_table_${schema_table_name}_migration_file";
|
|
50
|
+
private schema: SchemaDefinitionInterface = ${schema_model_name}Schema;
|
|
51
|
+
private readonly logger: LoggerUtil = new LoggerUtil(this.name);
|
|
52
|
+
|
|
53
|
+
constructor() {}
|
|
54
|
+
|
|
55
|
+
// Migration UP
|
|
56
|
+
public async up(
|
|
57
|
+
queryInterface: QueryInterface,
|
|
58
|
+
Sequelize: typeof import("sequelize")
|
|
59
|
+
) {
|
|
60
|
+
try {
|
|
61
|
+
const { table_name, columns = {}, indexes = [] } = this.schema;
|
|
62
|
+
|
|
63
|
+
await queryInterface.createTable(table_name, columns);
|
|
64
|
+
|
|
65
|
+
for (const index_field_obj of indexes) {
|
|
66
|
+
|
|
67
|
+
const { fields, name, index_options } = index_field_obj;
|
|
68
|
+
const index_name = \`idx_\${table_name}_\${fields.join("_")}\`
|
|
69
|
+
|
|
70
|
+
await queryInterface.addIndex(
|
|
71
|
+
table_name,
|
|
72
|
+
fields,
|
|
73
|
+
{ name: index_name, ...index_options }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" created with "\${this.index_attributes.length}" indexed Fields.\`);
|
|
78
|
+
}
|
|
79
|
+
catch (error: any) {
|
|
80
|
+
this.logger.error(\`🚫 Migration failed: "\${error.message}"\`, { error });
|
|
81
|
+
throw new Error(\`🚫 Migration failed: "\${error.message}"\`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Migration DOWN
|
|
86
|
+
public async down(
|
|
87
|
+
queryInterface: QueryInterface,
|
|
88
|
+
Sequelize: typeof import("sequelize")
|
|
89
|
+
) {
|
|
90
|
+
try {
|
|
91
|
+
const { table_name, database_name, model_name } = this.schema;
|
|
92
|
+
await queryInterface.dropTable(table_name, {});
|
|
93
|
+
this.logger.success(\`🗑️ Table "\${table_name}" dropped successfully.\`);
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
this.logger.error(\`🚫 Migration rollback failed: "\${error.message}"\`, { error });
|
|
96
|
+
throw new Error(\`🚫 Migration rollback failed: "\${error.message}"\`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default Create${schema_model_name}TableMigration;
|
|
102
|
+
`;
|
|
103
|
+
const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (schema_table_name, schema_model_name, diff, schema_file_name) => `
|
|
104
|
+
import { QueryInterface, DataTypes, Sequelize } from "sequelize";
|
|
105
|
+
import { LoggerUtil } from "fiberx-backend-toolkit//utils";
|
|
106
|
+
import { SchemaDefinitionInterface } from "fiberx-backend-toolkit/types/schema_type";
|
|
107
|
+
import ${schema_model_name}Schema from "@/database/schemas/${schema_file_name.replace(".ts", "")}";
|
|
108
|
+
|
|
109
|
+
class Update${schema_model_name}TableMigration {
|
|
110
|
+
private name: string = "update_table_${schema_table_name}_migration_file";
|
|
111
|
+
private schema: SchemaDefinitionInterface = ${schema_model_name}Schema;
|
|
112
|
+
private readonly logger: LoggerUtil = new LoggerUtil(this.name);
|
|
113
|
+
|
|
114
|
+
constructor() {}
|
|
115
|
+
|
|
116
|
+
public async up(
|
|
117
|
+
queryInterface: QueryInterface,
|
|
118
|
+
Sequelize: typeof import("sequelize")
|
|
119
|
+
) {
|
|
120
|
+
try {
|
|
121
|
+
const { table_name, model_name, columns = {}, indexes = [] } = this.schema;
|
|
122
|
+
|
|
123
|
+
${diff.table_renamed ? `
|
|
124
|
+
await queryInterface.renameTable(
|
|
125
|
+
"${diff.table_renamed.from}",
|
|
126
|
+
"${diff.table_renamed.to}"
|
|
127
|
+
);`
|
|
128
|
+
:
|
|
129
|
+
""}
|
|
130
|
+
|
|
131
|
+
// Add columns
|
|
132
|
+
${diff.columns.added.map((column_name) => `
|
|
133
|
+
await queryInterface.addColumn(
|
|
134
|
+
table_name,
|
|
135
|
+
"${column_name}",
|
|
136
|
+
columns["${column_name}"]
|
|
137
|
+
);
|
|
138
|
+
`).join("")}
|
|
139
|
+
|
|
140
|
+
// Modify columns
|
|
141
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
|
|
142
|
+
await queryInterface.changeColumn(
|
|
143
|
+
table_name,
|
|
144
|
+
"${col_name}",
|
|
145
|
+
${JSON.stringify(col_modified?.after)}
|
|
146
|
+
);
|
|
147
|
+
`).join("")}
|
|
148
|
+
|
|
149
|
+
// Remove columns
|
|
150
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
|
|
151
|
+
await queryInterface.removeColumn(
|
|
152
|
+
table_name,
|
|
153
|
+
"${col_name}"
|
|
154
|
+
);
|
|
155
|
+
`).join("")}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// Add indexes
|
|
159
|
+
${diff.indexes.added.map((idx) => `
|
|
160
|
+
await queryInterface.addIndex(
|
|
161
|
+
table_name,
|
|
162
|
+
${JSON.stringify(idx.fields)},
|
|
163
|
+
${JSON.stringify(idx)}
|
|
164
|
+
);
|
|
165
|
+
`).join("")}
|
|
166
|
+
|
|
167
|
+
// Modify indexes (remove + add)
|
|
168
|
+
${Object.entries(diff.indexes.modified).map(([idx_name, idx_modified]) => `
|
|
169
|
+
await queryInterface.removeIndex(
|
|
170
|
+
table_name,
|
|
171
|
+
${JSON.stringify(idx_modified.before.name)}
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
await queryInterface.addIndex(
|
|
175
|
+
table_name,
|
|
176
|
+
${JSON.stringify(idx_modified.after.fields)},
|
|
177
|
+
${JSON.stringify(idx_modified.after)}
|
|
178
|
+
);
|
|
179
|
+
`).join("")}
|
|
180
|
+
|
|
181
|
+
// Remove indexes
|
|
182
|
+
${diff.indexes.removed.map((idx) => `
|
|
183
|
+
await queryInterface.removeIndex(
|
|
184
|
+
table_name,
|
|
185
|
+
${JSON.stringify(idx.name)}
|
|
186
|
+
);
|
|
187
|
+
`).join("")}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" updated successfully.\`);
|
|
191
|
+
}
|
|
192
|
+
catch (error: any) {
|
|
193
|
+
this.logger.error(\`🚫 Update migration failed: "\${error.message}"\`, { error });
|
|
194
|
+
throw new Error(\`🚫 Update migration failed: "\${error.message}"\`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public async down(
|
|
199
|
+
queryInterface: QueryInterface,
|
|
200
|
+
Sequelize: typeof import("sequelize")
|
|
201
|
+
) {
|
|
202
|
+
try {
|
|
203
|
+
const { table_name, model_name, columns = {} } = this.schema;
|
|
204
|
+
|
|
205
|
+
// Revert Added indexes
|
|
206
|
+
${diff.indexes.added.map((idx) => `
|
|
207
|
+
await queryInterface.removeIndex(
|
|
208
|
+
table_name,
|
|
209
|
+
${JSON.stringify(idx.name)}
|
|
210
|
+
);`).join("")}
|
|
211
|
+
|
|
212
|
+
// Revert Modified Indexes
|
|
213
|
+
${Object.entries(diff.indexes.modified).map(([idx_name, idx_modified]) => `
|
|
214
|
+
await queryInterface.removeIndex(
|
|
215
|
+
table_name,
|
|
216
|
+
${JSON.stringify(idx_modified.after.name)}
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
await queryInterface.addIndex(
|
|
221
|
+
table_name,
|
|
222
|
+
${JSON.stringify(idx_modified.before.fields)},
|
|
223
|
+
${JSON.stringify(idx_modified.before)}
|
|
224
|
+
);`).join("")}
|
|
225
|
+
|
|
226
|
+
// Revert removed indexes
|
|
227
|
+
${diff.indexes.removed.map((idx) => `
|
|
228
|
+
await queryInterface.addIndex(
|
|
229
|
+
table_name,
|
|
230
|
+
${JSON.stringify(idx.fields)},
|
|
231
|
+
${JSON.stringify(idx)}
|
|
232
|
+
);`).join("")}
|
|
233
|
+
|
|
234
|
+
// Revert Modified columns
|
|
235
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
|
|
236
|
+
await queryInterface.changeColumn(
|
|
237
|
+
table_name,
|
|
238
|
+
"${col_name}",
|
|
239
|
+
${JSON.stringify(col_modified.before)}
|
|
240
|
+
);`).join("")}
|
|
241
|
+
|
|
242
|
+
// Revert added columns
|
|
243
|
+
${diff.columns.added.map((column_name) => `
|
|
244
|
+
await queryInterface.removeColumn(table_name, "${column_name}");
|
|
245
|
+
`).join("")}
|
|
246
|
+
|
|
247
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
|
|
248
|
+
await queryInterface.addColumn(
|
|
249
|
+
table_name,
|
|
250
|
+
"${col_name}",
|
|
251
|
+
${JSON.stringify(col_Def)}
|
|
252
|
+
);
|
|
253
|
+
`).join("")}
|
|
254
|
+
|
|
255
|
+
${diff.table_renamed ? `
|
|
256
|
+
await queryInterface.renameTable(
|
|
257
|
+
"${diff.table_renamed.to}",
|
|
258
|
+
"${diff.table_renamed.from}"
|
|
259
|
+
);` : ""}
|
|
260
|
+
|
|
261
|
+
this.logger.success(\`🗑️ Rollback of table "\${table_name}" completed successfully.\`);
|
|
262
|
+
}
|
|
263
|
+
catch (error: any) {
|
|
264
|
+
this.logger.error(\`🚫 Rollback migration failed: "\${error.message}"\`, { error });
|
|
265
|
+
throw new Error(\`🚫 Rollback migration failed: "\${error.message}"\`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export default Update${schema_model_name}TableMigration;
|
|
271
|
+
`;
|
|
272
|
+
const SEQUELIZE_SEEDER_TEMPLATE = (class_name, table_name) => `
|
|
273
|
+
'use strict';
|
|
274
|
+
|
|
275
|
+
import { QueryInterface } from 'sequelize';
|
|
276
|
+
|
|
277
|
+
class ${class_name}Seeder {
|
|
278
|
+
/**
|
|
279
|
+
* Seed method
|
|
280
|
+
*/
|
|
281
|
+
public async up(queryInterface: QueryInterface): Promise<void> {
|
|
282
|
+
const seed_data = this.getSeedData();
|
|
283
|
+
|
|
284
|
+
if (!seed_data || seed_data.length === 0) {
|
|
285
|
+
throw new Error("❌ Seeder data for 'roles' is empty. Aborting bulk insert.");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
await queryInterface.bulkInsert('${table_name.toLowerCase()}', seed_data);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Undo seed method
|
|
293
|
+
*/
|
|
294
|
+
public async down(queryInterface: QueryInterface): Promise<void> {
|
|
295
|
+
const seed_data = this.getSeedData();
|
|
296
|
+
|
|
297
|
+
if (!seed_data || seed_data.length === 0) {
|
|
298
|
+
throw new Error("❌ Seeder data for 'roles' is empty. Nothing to delete.");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const ids = seed_data.map(row => row.id);
|
|
302
|
+
|
|
303
|
+
await queryInterface.bulkDelete('${table_name.toLowerCase()}', { id: ids });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Provide seed data array
|
|
308
|
+
*/
|
|
309
|
+
private getSeedData(): Array<Record<string, any>> {
|
|
310
|
+
// TODO: Replace this with real seed data
|
|
311
|
+
return [];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export default ${class_name}Seeder;
|
|
316
|
+
`;
|
|
317
|
+
const SEQUELIZE_MODEL_CODE_TEMPLATE = (schema_model_name, schema_file_name, schema_columns) => {
|
|
318
|
+
const attributes = Object.keys(schema_columns)
|
|
319
|
+
.map(col => ` declare ${col}: any;`)
|
|
320
|
+
.join("\n");
|
|
321
|
+
return `
|
|
322
|
+
import { Model, Sequelize } from "sequelize";
|
|
323
|
+
import ${schema_model_name}Schema from "@/database/schemas/${schema_file_name.replace(".ts", "")}";
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class ${schema_model_name} extends Model {
|
|
327
|
+
${attributes}
|
|
328
|
+
|
|
329
|
+
public static initModel(sequelize: Sequelize): typeof ${schema_model_name} {
|
|
330
|
+
sequelize: Sequelize
|
|
331
|
+
): ${schema_model_name} {
|
|
332
|
+
${schema_model_name}.init(
|
|
333
|
+
${schema_model_name}Schema.columns,
|
|
334
|
+
{
|
|
335
|
+
sequelize,
|
|
336
|
+
tableName: ${schema_model_name}Schema.table_name,
|
|
337
|
+
modelName: ${schema_model_name}Schema.model_name,
|
|
338
|
+
timestamps: ${schema_model_name}Schema.timestamps,
|
|
339
|
+
indexes: ${schema_model_name}Schema.indexes,
|
|
340
|
+
createdAt: "created_at",
|
|
341
|
+
updatedAt: "updated_at",
|
|
342
|
+
hooks: {}
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
return ${schema_model_name};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
public static registerAssociations(): void {
|
|
349
|
+
// Example:
|
|
350
|
+
// User.belongsTo(models.Organization, { foreignKey: "org_id" });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export default ${schema_model_name}
|
|
355
|
+
`;
|
|
356
|
+
};
|
|
357
|
+
const SEQUELIZE_MODELS_INDEX_CODE_TEMPLATE = (model_names, imports) => {
|
|
358
|
+
const exports = model_names.join(",\n ");
|
|
359
|
+
return `
|
|
360
|
+
import { Sequelize } from "sequelize";
|
|
361
|
+
${imports}
|
|
362
|
+
|
|
363
|
+
const models = {
|
|
364
|
+
${exports}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
export const initModels = (sequelize: Sequelize) => {
|
|
368
|
+
Object.values(models).forEach(model => {
|
|
369
|
+
model.initModel(sequelize);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
Object.values(models).forEach(model => {
|
|
373
|
+
model.registerAssociations();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
return models;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
export default models;
|
|
380
|
+
`;
|
|
381
|
+
};
|
|
382
|
+
export { SEQUELIZE_SCHEMA_CODE_TEMPLATE, SEQUELIZE_SEEDER_TEMPLATE, SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE, SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE, SEQUELIZE_MODEL_CODE_TEMPLATE, SEQUELIZE_MODELS_INDEX_CODE_TEMPLATE };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const BASE_DIR: string;
|
|
2
|
+
export declare const LOG_DIR: string;
|
|
3
|
+
export declare const ENV_VAR_DIR: string;
|
|
4
|
+
export declare const SCHEMAS_DIR: string;
|
|
5
|
+
export declare const SCHEMA_SNAPSHOTS_DIR: string;
|
|
6
|
+
export declare const MODELS_DIR: string;
|
|
7
|
+
export declare const MIGRATIONS_DIR: string;
|
|
8
|
+
export declare const SEEDERS_DIR: string;
|
|
9
|
+
export declare const SEQUELIZE_META_TABLE_NAME = "sequelize_database_tables_meta";
|
|
10
|
+
export declare const SEQUELIZE_SEEDER_META_TABLE_NAME = "sequelize_database_table_seeder_meta";
|
|
11
|
+
export declare const REQUEST_ID_COOKIE_MAX_AGE: number;
|
|
12
|
+
export declare const CORS_ALLOWED_METHODS: string[];
|
|
13
|
+
export declare const CORS_ALLOWED_HEADERS: string[];
|
|
14
|
+
export declare const CORS_MAX_AGE_IN_SECONDS = 600;
|
|
15
|
+
export declare const CORS_MAX_AGE_IN_MICRO_SECONDS: number;
|
|
16
|
+
export declare const REQUEST_RATE_LIMITTER_OPTIONS: {
|
|
17
|
+
window_ms: number;
|
|
18
|
+
max_requests: number;
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
// Database constants
|
|
3
|
+
export const BASE_DIR = process.cwd();
|
|
4
|
+
export const LOG_DIR = path.join(BASE_DIR, "logs");
|
|
5
|
+
export const ENV_VAR_DIR = path.join(BASE_DIR, "environment_varaiables");
|
|
6
|
+
export const SCHEMAS_DIR = path.join(BASE_DIR, "src/database/schemas");
|
|
7
|
+
export const SCHEMA_SNAPSHOTS_DIR = path.join(BASE_DIR, "src/database/schema_snapshots");
|
|
8
|
+
export const MODELS_DIR = path.join(BASE_DIR, "src/database/models");
|
|
9
|
+
export const MIGRATIONS_DIR = path.join(BASE_DIR, "src/database/migrations");
|
|
10
|
+
export const SEEDERS_DIR = path.join(BASE_DIR, "src/database/seeders");
|
|
11
|
+
export const SEQUELIZE_META_TABLE_NAME = "sequelize_database_tables_meta";
|
|
12
|
+
export const SEQUELIZE_SEEDER_META_TABLE_NAME = "sequelize_database_table_seeder_meta";
|
|
13
|
+
export const REQUEST_ID_COOKIE_MAX_AGE = (1000 * 60 * 60 * 24 * 7); // 7 days
|
|
14
|
+
// CORS constants
|
|
15
|
+
export const CORS_ALLOWED_METHODS = ["GET", "POST", "PATCH", "DELETE", "OPTIONS"];
|
|
16
|
+
export const CORS_ALLOWED_HEADERS = ["Content-Type", "Authorization", "X-Device-Id", "X-Device-Name", "User-Agent"];
|
|
17
|
+
export const CORS_MAX_AGE_IN_SECONDS = (600); // default: 10 min
|
|
18
|
+
export const CORS_MAX_AGE_IN_MICRO_SECONDS = (1000 * 60 * 10); // 10 minutes
|
|
19
|
+
// Rate limitter
|
|
20
|
+
export const REQUEST_RATE_LIMITTER_OPTIONS = {
|
|
21
|
+
window_ms: (60 * 1000), // 1 MINS
|
|
22
|
+
max_requests: 50,
|
|
23
|
+
message: "⏳ Too many requests from this IP, please try again later"
|
|
24
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LoggerUtil, EnvManagerUtil } from "../../utils";
|
|
2
|
+
/**
|
|
3
|
+
* Base connector abstraction for all DB / external service connectors
|
|
4
|
+
*/
|
|
5
|
+
declare abstract class BaseConnector<TConnection> {
|
|
6
|
+
protected readonly connector_name: string;
|
|
7
|
+
readonly env_manager: EnvManagerUtil;
|
|
8
|
+
readonly logger: LoggerUtil;
|
|
9
|
+
protected constructor(connector_name: string);
|
|
10
|
+
abstract connect(): TConnection;
|
|
11
|
+
}
|
|
12
|
+
export default BaseConnector;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LoggerUtil, EnvManagerUtil } from "../../utils";
|
|
2
|
+
/**
|
|
3
|
+
* Base connector abstraction for all DB / external service connectors
|
|
4
|
+
*/
|
|
5
|
+
class BaseConnector {
|
|
6
|
+
connector_name;
|
|
7
|
+
env_manager = EnvManagerUtil.getInstance();
|
|
8
|
+
logger;
|
|
9
|
+
constructor(connector_name) {
|
|
10
|
+
this.connector_name = connector_name;
|
|
11
|
+
this.logger = new LoggerUtil(connector_name);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export default BaseConnector;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Sequelize } from "sequelize";
|
|
2
|
+
import BaseConnector from "./base_connector";
|
|
3
|
+
import { SequelizeConnectionOptions } from "../../types/database_type";
|
|
4
|
+
declare class SequelizeConnector extends BaseConnector<Sequelize> {
|
|
5
|
+
private static instance;
|
|
6
|
+
private static connections;
|
|
7
|
+
private constructor();
|
|
8
|
+
static getInstance(): SequelizeConnector;
|
|
9
|
+
private formatSQLQueryLog;
|
|
10
|
+
connect(options?: SequelizeConnectionOptions): Sequelize;
|
|
11
|
+
connectNamed(name: string, options?: SequelizeConnectionOptions): Sequelize;
|
|
12
|
+
getConnection(name?: string): Sequelize | undefined;
|
|
13
|
+
hasConnection(name?: string): boolean;
|
|
14
|
+
closeConnection(name?: string): Promise<void>;
|
|
15
|
+
closeAll(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export default SequelizeConnector;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Sequelize } from "sequelize";
|
|
2
|
+
import BaseConnector from "./base_connector";
|
|
3
|
+
import SqlFormatterUtil from "../../utils/sql_formatter_util";
|
|
4
|
+
class SequelizeConnector extends BaseConnector {
|
|
5
|
+
static instance;
|
|
6
|
+
static connections = new Map();
|
|
7
|
+
constructor() {
|
|
8
|
+
super("sequelize_connector");
|
|
9
|
+
}
|
|
10
|
+
static getInstance() {
|
|
11
|
+
if (!SequelizeConnector.instance) {
|
|
12
|
+
SequelizeConnector.instance = new SequelizeConnector();
|
|
13
|
+
}
|
|
14
|
+
return SequelizeConnector.instance;
|
|
15
|
+
}
|
|
16
|
+
// ----------------------------------
|
|
17
|
+
// SQL logging
|
|
18
|
+
// ----------------------------------
|
|
19
|
+
formatSQLQueryLog = (sql, timing) => {
|
|
20
|
+
try {
|
|
21
|
+
const formatted = SqlFormatterUtil.format(sql);
|
|
22
|
+
if (typeof timing === "number") {
|
|
23
|
+
this.logger.info(`[SQL ${timing}ms] ${formatted}`);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.logger.info(`[SQL] ${formatted}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
this.logger.error("Failed to log SQL query", { error });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// ----------------------------------
|
|
34
|
+
// Single default connection
|
|
35
|
+
// ----------------------------------
|
|
36
|
+
connect(options = {}) {
|
|
37
|
+
return this.connectNamed(options.name ?? "default", options);
|
|
38
|
+
}
|
|
39
|
+
// ----------------------------------
|
|
40
|
+
// Multiple named connections
|
|
41
|
+
// ----------------------------------
|
|
42
|
+
connectNamed(name, options = {}) {
|
|
43
|
+
if (SequelizeConnector.connections.has(name)) {
|
|
44
|
+
return SequelizeConnector.connections.get(name);
|
|
45
|
+
}
|
|
46
|
+
const dialect = options.dialect ?? this.env_manager.getEnvVar("DB_DIALECT", "postgres");
|
|
47
|
+
const logging_enabled = options.logging ?? this.env_manager.getEnvVar("DB_LOGGING", false);
|
|
48
|
+
const logging = logging_enabled ? this.formatSQLQueryLog : false;
|
|
49
|
+
const database = options.database ?? this.env_manager.getEnvVar("DB_NAME");
|
|
50
|
+
const username = options.username ?? this.env_manager.getEnvVar("DB_USER");
|
|
51
|
+
const password = options.password ?? this.env_manager.getEnvVar("DB_PASSWORD");
|
|
52
|
+
const host = options.host ?? this.env_manager.getEnvVar("DB_HOST");
|
|
53
|
+
const port = options.port ?? this.env_manager.getEnvVar("DB_PORT", 5432);
|
|
54
|
+
const sequelize = new Sequelize({
|
|
55
|
+
database,
|
|
56
|
+
username,
|
|
57
|
+
password,
|
|
58
|
+
host,
|
|
59
|
+
port,
|
|
60
|
+
dialect,
|
|
61
|
+
logging,
|
|
62
|
+
benchmark: logging_enabled,
|
|
63
|
+
});
|
|
64
|
+
SequelizeConnector.connections.set(name, sequelize);
|
|
65
|
+
this.logger.success(`✅ Sequelize connected [${name}]`);
|
|
66
|
+
return sequelize;
|
|
67
|
+
}
|
|
68
|
+
// ----------------------------------
|
|
69
|
+
// Utilities
|
|
70
|
+
// ----------------------------------
|
|
71
|
+
getConnection(name = "default") {
|
|
72
|
+
return SequelizeConnector.connections.get(name);
|
|
73
|
+
}
|
|
74
|
+
hasConnection(name = "default") {
|
|
75
|
+
return SequelizeConnector.connections.has(name);
|
|
76
|
+
}
|
|
77
|
+
async closeConnection(name = "default") {
|
|
78
|
+
const conn = SequelizeConnector.connections.get(name);
|
|
79
|
+
if (conn) {
|
|
80
|
+
await conn.close();
|
|
81
|
+
SequelizeConnector.connections.delete(name);
|
|
82
|
+
this.logger.info(`🔌 Sequelize connection closed [${name}]`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async closeAll() {
|
|
86
|
+
for (const [name, conn] of SequelizeConnector.connections.entries()) {
|
|
87
|
+
await conn.close();
|
|
88
|
+
this.logger.info(`🔌 Sequelize connection closed [${name}]`);
|
|
89
|
+
}
|
|
90
|
+
SequelizeConnector.connections.clear();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export default SequelizeConnector;
|