fiberx-backend-toolkit 0.0.1
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 +215 -0
- package/package.json +52 -0
- package/src/code_templates/sequelize_code_template.ts +362 -0
- package/src/config/constants.ts +14 -0
- package/src/database/connectors/base_connector.ts +21 -0
- package/src/database/connectors/sequelize_connector.ts +66 -0
- package/src/database/index.ts +27 -0
- package/src/database/schema/schema_diff_util.ts +96 -0
- package/src/database/schema/schema_normalizer_util.ts +54 -0
- package/src/database/scripts/create_schema_script.ts +108 -0
- package/src/database/scripts/create_seeder_script.ts +118 -0
- package/src/database/scripts/make_migrations_script.ts +275 -0
- package/src/database/scripts/migration_runner_script.ts +184 -0
- package/src/database/scripts/seeder_runner_script.ts +167 -0
- package/src/index.ts +2 -0
- package/src/types/index.ts +3 -0
- package/src/types/migration_type.ts +7 -0
- package/src/types/schema_type.ts +101 -0
- package/src/types/util_type.ts +36 -0
- package/src/utils/env_manager_util.ts +90 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/input_transformer_util.ts +433 -0
- package/src/utils/input_validator_util.ts +156 -0
- package/src/utils/logger_util.ts +119 -0
- package/src/utils/server_util.ts +71 -0
- package/src/utils/sql_formatter_util.ts +50 -0
- package/test.js +1 -0
- package/tsconfig.json +25 -0
- package/tsup.config.ts +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# FiberX Backend Toolkit
|
|
2
|
+
|
|
3
|
+
A comprehensive TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The FiberX Backend Toolkit is a centralized library designed to streamline backend development across FiberX services. It provides database abstraction layers, migration management, utilities for common operations, and TypeScript type definitions for consistency across the ecosystem.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Database Management**: Multi-connector support with Sequelize integration for ORM operations
|
|
12
|
+
- **Schema Migration System**: Automated schema management, migration generation, and execution
|
|
13
|
+
- **Seeding Support**: Database seeder generation and execution tools
|
|
14
|
+
- **Utility Functions**:
|
|
15
|
+
- Input validation and transformation
|
|
16
|
+
- Environment variable management
|
|
17
|
+
- SQL formatting and query building
|
|
18
|
+
- Structured logging with file persistence
|
|
19
|
+
- **TypeScript Support**: Fully typed exports for schema, migration, and utility types
|
|
20
|
+
- **Flexible Exports**: Modular exports for `utils`, `types`, and `database` modules
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install fiberx-backend-toolkit
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
- Node.js 20.x or higher
|
|
31
|
+
- npm or yarn package manager
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Import Types
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import type { SchemaInterface, MigrationInterface } from "fiberx-backend-toolkit/types";
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Import Utilities
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { LoggerUtil, InputValidatorUtil, EnvManagerUtil } from "fiberx-backend-toolkit/utils";
|
|
45
|
+
|
|
46
|
+
// Initialize logger
|
|
47
|
+
const logger = new LoggerUtil("my-module");
|
|
48
|
+
logger.info("Application started");
|
|
49
|
+
|
|
50
|
+
// Use environment manager
|
|
51
|
+
const apiKey = EnvManagerUtil.get("API_KEY");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Database Operations
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { SequelizeConnector, MigrationRunnerScript } from "fiberx-backend-toolkit/database";
|
|
58
|
+
|
|
59
|
+
// Initialize database connector
|
|
60
|
+
const connector = new SequelizeConnector(config);
|
|
61
|
+
|
|
62
|
+
// Run migrations
|
|
63
|
+
const migrationRunner = new MigrationRunnerScript(connector);
|
|
64
|
+
await migrationRunner.run();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Core Modules
|
|
68
|
+
|
|
69
|
+
### Utilities (`/src/utils`)
|
|
70
|
+
|
|
71
|
+
- **LoggerUtil**: Structured logging with file-based persistence
|
|
72
|
+
- **InputValidatorUtil**: Input validation and sanitization
|
|
73
|
+
- **InputTransformerUtil**: Data transformation utilities
|
|
74
|
+
- **EnvManagerUtil**: Environment variable management and parsing
|
|
75
|
+
- **SqlFormatterUtil**: SQL query formatting and beautification
|
|
76
|
+
|
|
77
|
+
### Database (`/src/database`)
|
|
78
|
+
|
|
79
|
+
#### Connectors
|
|
80
|
+
- **BaseConnector**: Abstract base class for database connections
|
|
81
|
+
- **SequelizeConnector**: Sequelize ORM connector implementation
|
|
82
|
+
|
|
83
|
+
#### Schema Utilities
|
|
84
|
+
- **SchemaDiffUtil**: Compare database schemas and detect changes
|
|
85
|
+
- **SchemaNormalizerUtil**: Normalize schema definitions for consistency
|
|
86
|
+
|
|
87
|
+
#### Scripts
|
|
88
|
+
- **CreateSchemaScript**: Generate initial database schema
|
|
89
|
+
- **MakeMigrationsScript**: Auto-generate migration files from schema changes
|
|
90
|
+
- **MigrationRunnerScript**: Execute pending database migrations
|
|
91
|
+
- **CreateSeederScript**: Generate seeder files for initial data
|
|
92
|
+
- **SeederRunnerScript**: Execute database seeders
|
|
93
|
+
|
|
94
|
+
### Types (`/src/types`)
|
|
95
|
+
|
|
96
|
+
- **SchemaInterface**: Database schema type definitions
|
|
97
|
+
- **MigrationInterface**: Migration type definitions
|
|
98
|
+
- **UtilInterface**: Utility function types
|
|
99
|
+
|
|
100
|
+
## Module Exports
|
|
101
|
+
|
|
102
|
+
The toolkit provides structured exports for easy integration:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// Main exports (all modules)
|
|
106
|
+
import * as toolkit from "fiberx-backend-toolkit";
|
|
107
|
+
|
|
108
|
+
// Utils only
|
|
109
|
+
import { LoggerUtil, InputValidatorUtil } from "fiberx-backend-toolkit/utils";
|
|
110
|
+
|
|
111
|
+
// Database only
|
|
112
|
+
import { SequelizeConnector, MigrationRunnerScript } from "fiberx-backend-toolkit/database";
|
|
113
|
+
|
|
114
|
+
// Types only
|
|
115
|
+
import type { SchemaInterface } from "fiberx-backend-toolkit/types";
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
The toolkit uses standardized directory conventions:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
project_root/
|
|
124
|
+
├── logs/ # Log files directory
|
|
125
|
+
├── environment_variables/ # Environment variable files
|
|
126
|
+
├── src/database/
|
|
127
|
+
│ ├── schemas/ # Schema definitions
|
|
128
|
+
│ ├── schema_snapshots/ # Schema snapshots for comparison
|
|
129
|
+
│ ├── models/ # Sequelize models
|
|
130
|
+
│ ├── migrations/ # Database migrations
|
|
131
|
+
│ └── seeders/ # Database seeders
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Dependencies
|
|
135
|
+
|
|
136
|
+
### Core Dependencies
|
|
137
|
+
- **sequelize**: ^6.37.7 - SQL ORM
|
|
138
|
+
- **sequelize-typescript**: ^2.1.6 - TypeScript decorators for Sequelize
|
|
139
|
+
- **axios**: ^1.11.0 - HTTP client
|
|
140
|
+
- **bcrypt**: ^6.0.0 - Password hashing
|
|
141
|
+
- **jsonwebtoken**: ^9.0.2 - JWT handling
|
|
142
|
+
- **dayjs**: ^1.11.18 - Date/time utilities
|
|
143
|
+
- **js-yaml**: ^4.1.0 - YAML parsing
|
|
144
|
+
- **uuid**: ^12.0.0 - UUID generation
|
|
145
|
+
|
|
146
|
+
## Build
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npm run build
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The build process uses `tsup` for optimized TypeScript bundling.
|
|
153
|
+
|
|
154
|
+
## API Documentation
|
|
155
|
+
|
|
156
|
+
### LoggerUtil
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const logger = new LoggerUtil("module-name");
|
|
160
|
+
|
|
161
|
+
logger.info("Information message");
|
|
162
|
+
logger.warn("Warning message");
|
|
163
|
+
logger.error("Error message");
|
|
164
|
+
logger.debug("Debug message");
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Logs are stored locally in the configured `LOG_DIR`.
|
|
168
|
+
|
|
169
|
+
### InputValidatorUtil
|
|
170
|
+
|
|
171
|
+
Validates and sanitizes user inputs with built-in rules and custom validators.
|
|
172
|
+
|
|
173
|
+
### EnvManagerUtil
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const dbHost = EnvManagerUtil.get("DB_HOST", "localhost");
|
|
177
|
+
const dbPort = EnvManagerUtil.getAsNumber("DB_PORT", 5432);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### SequelizeConnector
|
|
181
|
+
|
|
182
|
+
Provides abstraction over Sequelize for database operations with connection pooling and query management.
|
|
183
|
+
|
|
184
|
+
## Project Structure
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
fiberx-backend-toolkit/
|
|
188
|
+
├── src/
|
|
189
|
+
│ ├── code_templates/ # Code generation templates
|
|
190
|
+
│ ├── config/ # Configuration constants
|
|
191
|
+
│ ├── database/ # Database module
|
|
192
|
+
│ ├── types/ # TypeScript type definitions
|
|
193
|
+
│ ├── utils/ # Utility functions
|
|
194
|
+
│ └── index.ts # Main entry point
|
|
195
|
+
├── package.json
|
|
196
|
+
├── tsconfig.json
|
|
197
|
+
├── tsup.config.ts # Build configuration
|
|
198
|
+
└── README.md
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
ISC
|
|
204
|
+
|
|
205
|
+
## Author
|
|
206
|
+
|
|
207
|
+
David Matt-Ojo
|
|
208
|
+
|
|
209
|
+
## Repository
|
|
210
|
+
|
|
211
|
+
[GitHub - fiberx-backend-toolkit](https://github.com/fiberx-innovations/fiberx-backend-toolkit)
|
|
212
|
+
|
|
213
|
+
## Support
|
|
214
|
+
|
|
215
|
+
For issues, bugs, or feature requests, please visit the [issues page](https://github.com/fiberx-innovations/fiberx-backend-toolkit/issues).
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fiberx-backend-toolkit",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./utils": "./dist/utils/index.js",
|
|
11
|
+
"./types": "./dist/types/index.js",
|
|
12
|
+
"./database": "./dist/database/index.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/fiberx-innovations/fiberx-backend-toolkit#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/fiberx-innovations/fiberx-backend-toolkit/issues"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/fiberx-innovations/fiberx-backend-toolkit.git"
|
|
24
|
+
},
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"author": "David Matt-Ojo",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"axios": "^1.11.0",
|
|
29
|
+
"bcrypt": "^6.0.0",
|
|
30
|
+
"dayjs": "^1.11.18",
|
|
31
|
+
"js-yaml": "^4.1.0",
|
|
32
|
+
"jsonwebtoken": "^9.0.2",
|
|
33
|
+
"sequelize": "^6.37.7",
|
|
34
|
+
"sequelize-typescript": "^2.1.6",
|
|
35
|
+
"uuid": "^12.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/axios": "^0.9.36",
|
|
39
|
+
"@types/bcrypt": "^6.0.0",
|
|
40
|
+
"@types/js-yaml": "^4.0.9",
|
|
41
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
42
|
+
"@types/uuid": "^10.0.0",
|
|
43
|
+
"ts-node": "^10.9.2",
|
|
44
|
+
"tsc-alias": "^1.8.16",
|
|
45
|
+
"tsconfig-paths": "^4.2.0",
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
|
+
"typescript": "^5.8.3"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": "20.x"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IndexFieldOptionsInterface,
|
|
3
|
+
SchemaColumnInterface,
|
|
4
|
+
SchemaDiffInterface
|
|
5
|
+
} from "@/types/schema_type";
|
|
6
|
+
|
|
7
|
+
const SEQUELIZE_SCHEMA_CODE_TEMPLATE = (
|
|
8
|
+
schema_name: string,
|
|
9
|
+
table_name: string,
|
|
10
|
+
model_name: string,
|
|
11
|
+
migration_priority: number
|
|
12
|
+
): string =>
|
|
13
|
+
{
|
|
14
|
+
return `
|
|
15
|
+
import { DataTypes } from "sequelize";
|
|
16
|
+
import { SchemaDefinitionInterface } from "fiberx-backend-toolkit/types/schema_type";
|
|
17
|
+
|
|
18
|
+
const ${schema_name}: SchemaDefinitionInterface = {
|
|
19
|
+
database_name: "public",
|
|
20
|
+
migration_priority: ${migration_priority},
|
|
21
|
+
|
|
22
|
+
table_name: "${table_name}",
|
|
23
|
+
model_name: "${model_name}",
|
|
24
|
+
timestamps:true,
|
|
25
|
+
|
|
26
|
+
columns: {
|
|
27
|
+
id: {
|
|
28
|
+
type: DataTypes.BIGINT,
|
|
29
|
+
primaryKey: true,
|
|
30
|
+
autoIncrement: true,
|
|
31
|
+
allowNull: false,
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
created_at: {
|
|
35
|
+
type: DataTypes.DATE,
|
|
36
|
+
defaultValue: DataTypes.NOW,
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
updated_at: {
|
|
40
|
+
type: DataTypes.DATE,
|
|
41
|
+
allowNull: true,
|
|
42
|
+
defaultValue: null,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
indexes: [
|
|
47
|
+
{ fields: [""], name: "" },
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default ${schema_name};
|
|
52
|
+
`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (
|
|
56
|
+
schema_table_name: string,
|
|
57
|
+
schema_model_name: string,
|
|
58
|
+
schema_file_name: string
|
|
59
|
+
): string => `
|
|
60
|
+
import { QueryInterface, DataTypes, Sequelize } from "sequelize";
|
|
61
|
+
import { LoggerUtil } from "fiberx-backend-toolkit//utils";
|
|
62
|
+
import { SchemaDefinitionInterface } from "fiberx-backend-toolkit/types/schema_type";
|
|
63
|
+
import ${schema_model_name}Schema from "@/database/schemas/${schema_file_name.replace(".ts", "")}";
|
|
64
|
+
|
|
65
|
+
class Create${schema_model_name}TableMigration {
|
|
66
|
+
private name: string = "create_table_${schema_table_name}_migration_file";
|
|
67
|
+
private schema: SchemaDefinitionInterface = ${schema_model_name}Schema;
|
|
68
|
+
private readonly logger: LoggerUtil = new LoggerUtil(this.name);
|
|
69
|
+
|
|
70
|
+
constructor() {}
|
|
71
|
+
|
|
72
|
+
// Migration UP
|
|
73
|
+
public async up(
|
|
74
|
+
queryInterface: QueryInterface,
|
|
75
|
+
Sequelize: typeof import("sequelize")
|
|
76
|
+
) {
|
|
77
|
+
try {
|
|
78
|
+
const { table_name, columns = {}, indexes = [] } = this.schema;
|
|
79
|
+
|
|
80
|
+
await queryInterface.createTable(table_name, columns);
|
|
81
|
+
|
|
82
|
+
for (const index_field_obj of indexes) {
|
|
83
|
+
|
|
84
|
+
const { fields, name, index_options } = index_field_obj;
|
|
85
|
+
const index_name = \`idx_\${table_name}_\${fields.join("_")}\`
|
|
86
|
+
|
|
87
|
+
await queryInterface.addIndex(
|
|
88
|
+
table_name,
|
|
89
|
+
fields,
|
|
90
|
+
{ name: index_name, ...index_options }
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" created with "\${this.index_attributes.length}" indexed Fields.\`);
|
|
95
|
+
}
|
|
96
|
+
catch (error: any) {
|
|
97
|
+
this.logger.error(\`🚫 Migration failed: "\${error.message}"\`, { error });
|
|
98
|
+
throw new Error(\`🚫 Migration failed: "\${error.message}"\`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Migration DOWN
|
|
103
|
+
public async down(
|
|
104
|
+
queryInterface: QueryInterface,
|
|
105
|
+
Sequelize: typeof import("sequelize")
|
|
106
|
+
) {
|
|
107
|
+
try {
|
|
108
|
+
const { table_name, database_name, model_name } = this.schema;
|
|
109
|
+
await queryInterface.dropTable(table_name, {});
|
|
110
|
+
this.logger.success(\`🗑️ Table "\${table_name}" dropped successfully.\`);
|
|
111
|
+
} catch (error: any) {
|
|
112
|
+
this.logger.error(\`🚫 Migration rollback failed: "\${error.message}"\`, { error });
|
|
113
|
+
throw new Error(\`🚫 Migration rollback failed: "\${error.message}"\`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default Create${schema_model_name}TableMigration;
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (
|
|
122
|
+
schema_table_name: string,
|
|
123
|
+
schema_model_name: string,
|
|
124
|
+
diff: SchemaDiffInterface,
|
|
125
|
+
schema_file_name: string
|
|
126
|
+
): string => `
|
|
127
|
+
import { QueryInterface, DataTypes, Sequelize } from "sequelize";
|
|
128
|
+
import { LoggerUtil } from "fiberx-backend-toolkit//utils";
|
|
129
|
+
import { SchemaDefinitionInterface } from "fiberx-backend-toolkit/types/schema_type";
|
|
130
|
+
import ${schema_model_name}Schema from "@/database/schemas/${schema_file_name.replace(".ts", "")}";
|
|
131
|
+
|
|
132
|
+
class Update${schema_model_name}TableMigration {
|
|
133
|
+
private name: string = "update_table_${schema_table_name}_migration_file";
|
|
134
|
+
private schema: SchemaDefinitionInterface = ${schema_model_name}Schema;
|
|
135
|
+
private readonly logger: LoggerUtil = new LoggerUtil(this.name);
|
|
136
|
+
|
|
137
|
+
constructor() {}
|
|
138
|
+
|
|
139
|
+
public async up(
|
|
140
|
+
queryInterface: QueryInterface,
|
|
141
|
+
Sequelize: typeof import("sequelize")
|
|
142
|
+
) {
|
|
143
|
+
try {
|
|
144
|
+
const { table_name, model_name, columns = {}, indexes = [] } = this.schema;
|
|
145
|
+
|
|
146
|
+
${diff.table_renamed ? `
|
|
147
|
+
await queryInterface.renameTable(
|
|
148
|
+
"${diff.table_renamed.from}",
|
|
149
|
+
"${diff.table_renamed.to}"
|
|
150
|
+
);`
|
|
151
|
+
:
|
|
152
|
+
""
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Add columns
|
|
156
|
+
${diff.columns.added.map((column_name: string) => `
|
|
157
|
+
await queryInterface.addColumn(
|
|
158
|
+
table_name,
|
|
159
|
+
"${column_name}",
|
|
160
|
+
columns["${column_name}"]
|
|
161
|
+
);
|
|
162
|
+
`).join("")}
|
|
163
|
+
|
|
164
|
+
// Modify columns
|
|
165
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
|
|
166
|
+
await queryInterface.changeColumn(
|
|
167
|
+
table_name,
|
|
168
|
+
"${col_name}",
|
|
169
|
+
${JSON.stringify(col_modified?.after)}
|
|
170
|
+
);
|
|
171
|
+
`
|
|
172
|
+
).join("")}
|
|
173
|
+
|
|
174
|
+
// Remove columns
|
|
175
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
|
|
176
|
+
await queryInterface.removeColumn(
|
|
177
|
+
table_name,
|
|
178
|
+
"${col_name}"
|
|
179
|
+
);
|
|
180
|
+
`
|
|
181
|
+
).join("")}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
// Add indexes
|
|
185
|
+
${diff.indexes.added.map( (idx: IndexFieldOptionsInterface) => `
|
|
186
|
+
await queryInterface.addIndex(
|
|
187
|
+
table_name,
|
|
188
|
+
${JSON.stringify(idx.fields)},
|
|
189
|
+
${JSON.stringify(idx)}
|
|
190
|
+
);
|
|
191
|
+
`
|
|
192
|
+
).join("")}
|
|
193
|
+
|
|
194
|
+
// Modify indexes (remove + add)
|
|
195
|
+
${Object.entries(diff.indexes.modified).map(([idx_name, idx_modified]) => `
|
|
196
|
+
await queryInterface.removeIndex(
|
|
197
|
+
table_name,
|
|
198
|
+
${JSON.stringify(idx_modified.before.name)}
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
await queryInterface.addIndex(
|
|
202
|
+
table_name,
|
|
203
|
+
${JSON.stringify(idx_modified.after.fields)},
|
|
204
|
+
${JSON.stringify(idx_modified.after)}
|
|
205
|
+
);
|
|
206
|
+
`
|
|
207
|
+
).join("")}
|
|
208
|
+
|
|
209
|
+
// Remove indexes
|
|
210
|
+
${diff.indexes.removed.map( (idx: IndexFieldOptionsInterface) => `
|
|
211
|
+
await queryInterface.removeIndex(
|
|
212
|
+
table_name,
|
|
213
|
+
${JSON.stringify(idx.name)}
|
|
214
|
+
);
|
|
215
|
+
`
|
|
216
|
+
).join("")}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" updated successfully.\`);
|
|
220
|
+
}
|
|
221
|
+
catch (error: any) {
|
|
222
|
+
this.logger.error(\`🚫 Update migration failed: "\${error.message}"\`, { error });
|
|
223
|
+
throw new Error(\`🚫 Update migration failed: "\${error.message}"\`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public async down(
|
|
228
|
+
queryInterface: QueryInterface,
|
|
229
|
+
Sequelize: typeof import("sequelize")
|
|
230
|
+
) {
|
|
231
|
+
try {
|
|
232
|
+
const { table_name, model_name, columns = {} } = this.schema;
|
|
233
|
+
|
|
234
|
+
// Revert Added indexes
|
|
235
|
+
${diff.indexes.added.map( (idx: IndexFieldOptionsInterface) => `
|
|
236
|
+
await queryInterface.removeIndex(
|
|
237
|
+
table_name,
|
|
238
|
+
${JSON.stringify(idx.name)}
|
|
239
|
+
);`
|
|
240
|
+
).join("")}
|
|
241
|
+
|
|
242
|
+
// Revert Modified Indexes
|
|
243
|
+
${Object.entries(diff.indexes.modified).map(([idx_name, idx_modified]) => `
|
|
244
|
+
await queryInterface.removeIndex(
|
|
245
|
+
table_name,
|
|
246
|
+
${JSON.stringify(idx_modified.after.name)}
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
await queryInterface.addIndex(
|
|
251
|
+
table_name,
|
|
252
|
+
${JSON.stringify(idx_modified.before.fields)},
|
|
253
|
+
${JSON.stringify(idx_modified.before)}
|
|
254
|
+
);`
|
|
255
|
+
).join("")}
|
|
256
|
+
|
|
257
|
+
// Revert removed indexes
|
|
258
|
+
${diff.indexes.removed.map( (idx: IndexFieldOptionsInterface) => `
|
|
259
|
+
await queryInterface.addIndex(
|
|
260
|
+
table_name,
|
|
261
|
+
${JSON.stringify(idx.fields)},
|
|
262
|
+
${JSON.stringify(idx)}
|
|
263
|
+
);`
|
|
264
|
+
).join("")}
|
|
265
|
+
|
|
266
|
+
// Revert Modified columns
|
|
267
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
|
|
268
|
+
await queryInterface.changeColumn(
|
|
269
|
+
table_name,
|
|
270
|
+
"${col_name}",
|
|
271
|
+
${JSON.stringify(col_modified.before)}
|
|
272
|
+
);`
|
|
273
|
+
).join("")}
|
|
274
|
+
|
|
275
|
+
// Revert added columns
|
|
276
|
+
${diff.columns.added.map((column_name: string) => `
|
|
277
|
+
await queryInterface.removeColumn(table_name, "${column_name}");
|
|
278
|
+
`).join("")}
|
|
279
|
+
|
|
280
|
+
${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
|
|
281
|
+
await queryInterface.addColumn(
|
|
282
|
+
table_name,
|
|
283
|
+
"${col_name}",
|
|
284
|
+
${JSON.stringify(col_Def)}
|
|
285
|
+
);
|
|
286
|
+
`).join("")}
|
|
287
|
+
|
|
288
|
+
${diff.table_renamed ? `
|
|
289
|
+
await queryInterface.renameTable(
|
|
290
|
+
"${diff.table_renamed.to}",
|
|
291
|
+
"${diff.table_renamed.from}"
|
|
292
|
+
);` : ""
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
this.logger.success(\`🗑️ Rollback of table "\${table_name}" completed successfully.\`);
|
|
296
|
+
}
|
|
297
|
+
catch (error: any) {
|
|
298
|
+
this.logger.error(\`🚫 Rollback migration failed: "\${error.message}"\`, { error });
|
|
299
|
+
throw new Error(\`🚫 Rollback migration failed: "\${error.message}"\`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export default Update${schema_model_name}TableMigration;
|
|
305
|
+
`;
|
|
306
|
+
|
|
307
|
+
const SEQUELIZE_SEEDER_TEMPLATE = (
|
|
308
|
+
class_name: string,
|
|
309
|
+
table_name: string
|
|
310
|
+
): string => `
|
|
311
|
+
'use strict';
|
|
312
|
+
|
|
313
|
+
import { QueryInterface } from 'sequelize';
|
|
314
|
+
|
|
315
|
+
class ${class_name}Seeder {
|
|
316
|
+
/**
|
|
317
|
+
* Seed method
|
|
318
|
+
*/
|
|
319
|
+
public async up(queryInterface: QueryInterface): Promise<void> {
|
|
320
|
+
const seed_data = this.getSeedData();
|
|
321
|
+
|
|
322
|
+
if (!seed_data || seed_data.length === 0) {
|
|
323
|
+
throw new Error("❌ Seeder data for 'roles' is empty. Aborting bulk insert.");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
await queryInterface.bulkInsert('${table_name.toLowerCase()}', seed_data);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Undo seed method
|
|
331
|
+
*/
|
|
332
|
+
public async down(queryInterface: QueryInterface): Promise<void> {
|
|
333
|
+
const seed_data = this.getSeedData();
|
|
334
|
+
|
|
335
|
+
if (!seed_data || seed_data.length === 0) {
|
|
336
|
+
throw new Error("❌ Seeder data for 'roles' is empty. Nothing to delete.");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const ids = seed_data.map(row => row.id);
|
|
340
|
+
|
|
341
|
+
await queryInterface.bulkDelete('${table_name.toLowerCase()}', { id: ids });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Provide seed data array
|
|
346
|
+
*/
|
|
347
|
+
private getSeedData(): Array<Record<string, any>> {
|
|
348
|
+
// TODO: Replace this with real seed data
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export default ${class_name}Seeder;
|
|
354
|
+
`;
|
|
355
|
+
|
|
356
|
+
export {
|
|
357
|
+
SEQUELIZE_SCHEMA_CODE_TEMPLATE,
|
|
358
|
+
SEQUELIZE_SEEDER_TEMPLATE,
|
|
359
|
+
SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE,
|
|
360
|
+
SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE
|
|
361
|
+
}
|
|
362
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
|
|
3
|
+
// Database constants
|
|
4
|
+
export const BASE_DIR = process.cwd();
|
|
5
|
+
export const LOG_DIR = path.join(BASE_DIR, "logs");
|
|
6
|
+
export const ENV_VAR_DIR = path.join(BASE_DIR, "environment_varaiables");
|
|
7
|
+
export const SCHEMAS_DIR = path.join(BASE_DIR, "src/database/schemas");
|
|
8
|
+
export const SCHEMA_SNAPSHOTS_DIR = path.join(BASE_DIR, "src/database/schema_snapshots");
|
|
9
|
+
export const MODELS_DIR = path.join(BASE_DIR, "src/database/models");
|
|
10
|
+
export const MIGRATIONS_DIR = path.join(BASE_DIR, "src/database/migrations");
|
|
11
|
+
export const SEEDERS_DIR = path.join(BASE_DIR, "src/database/seeders");
|
|
12
|
+
|
|
13
|
+
export const SEQUELIZE_META_TABLE_NAME = "sequelize_database_tables_meta";
|
|
14
|
+
export const SEQUELIZE_SEEDER_META_TABLE_NAME = "sequelize_database_table_seeder_meta";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
import { LoggerUtil, EnvManagerUtil } from "@/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base connector abstraction for all DB / external service connectors
|
|
6
|
+
*/
|
|
7
|
+
abstract class BaseConnector<TConnection> {
|
|
8
|
+
public readonly env_manager = EnvManagerUtil.getInstance();
|
|
9
|
+
public readonly logger: LoggerUtil;
|
|
10
|
+
|
|
11
|
+
protected constructor(
|
|
12
|
+
protected readonly connector_name: string
|
|
13
|
+
) {
|
|
14
|
+
this.logger = new LoggerUtil(connector_name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Connect and return the underlying connection instance
|
|
18
|
+
public abstract connect(): TConnection;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default BaseConnector;
|