@elsikora/nestjs-crud-config 1.0.1-dev.1 → 1.1.0-dev.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 +644 -286
- package/dist/cjs/index.js +26 -10
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/modules/config/config.module.js +67 -1
- package/dist/cjs/modules/config/config.module.js.map +1 -1
- package/dist/cjs/modules/config/config.service.js +55 -98
- package/dist/cjs/modules/config/config.service.js.map +1 -1
- package/dist/cjs/modules/config/data/entity/entity.js +1 -0
- package/dist/cjs/modules/config/data/entity/entity.js.map +1 -1
- package/dist/cjs/modules/config/index.d.ts +2 -0
- package/dist/cjs/modules/config/migration/entity/entity.d.ts +12 -0
- package/dist/cjs/modules/config/migration/entity/entity.js +139 -0
- package/dist/cjs/modules/config/migration/entity/entity.js.map +1 -0
- package/dist/cjs/modules/config/migration/entity/index.d.ts +1 -0
- package/dist/cjs/modules/config/migration/enum/index.d.ts +1 -0
- package/dist/cjs/modules/config/migration/enum/status.d.ts +7 -0
- package/dist/cjs/modules/config/migration/enum/status.js +11 -0
- package/dist/cjs/modules/config/migration/enum/status.js.map +1 -0
- package/dist/cjs/modules/config/migration/index.d.ts +5 -0
- package/dist/cjs/modules/config/migration/interface/index.d.ts +3 -0
- package/dist/cjs/modules/config/migration/interface/interface.d.ts +11 -0
- package/dist/cjs/modules/config/migration/interface/migration-definition.interface.d.ts +29 -0
- package/dist/cjs/modules/config/migration/interface/migration-options.interface.d.ts +34 -0
- package/dist/cjs/modules/config/migration/migration-runner.service.d.ts +22 -0
- package/dist/cjs/modules/config/migration/migration-runner.service.js +85 -0
- package/dist/cjs/modules/config/migration/migration-runner.service.js.map +1 -0
- package/dist/cjs/modules/config/migration/migration.service.d.ts +68 -0
- package/dist/cjs/modules/config/migration/migration.service.js +331 -0
- package/dist/cjs/modules/config/migration/migration.service.js.map +1 -0
- package/dist/cjs/modules/config/section/entity/entity.js +1 -0
- package/dist/cjs/modules/config/section/entity/entity.js.map +1 -1
- package/dist/cjs/package.json +2 -1
- package/dist/cjs/shared/constant/config/index.d.ts +1 -0
- package/dist/cjs/shared/constant/config/migration.constant.d.ts +6 -0
- package/dist/cjs/shared/constant/config/migration.constant.js +15 -0
- package/dist/cjs/shared/constant/config/migration.constant.js.map +1 -0
- package/dist/cjs/shared/constant/token.constant.d.ts +2 -0
- package/dist/cjs/shared/constant/token.constant.js +4 -0
- package/dist/cjs/shared/constant/token.constant.js.map +1 -1
- package/dist/cjs/shared/interface/config/options.interface.d.ts +3 -2
- package/dist/cjs/shared/utility/index.d.ts +1 -0
- package/dist/cjs/shared/utility/logger.utility.d.ts +21 -0
- package/dist/cjs/shared/utility/logger.utility.js +67 -0
- package/dist/cjs/shared/utility/logger.utility.js.map +1 -0
- package/dist/esm/index.js +10 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/modules/config/config.module.js +67 -1
- package/dist/esm/modules/config/config.module.js.map +1 -1
- package/dist/esm/modules/config/config.service.js +56 -99
- package/dist/esm/modules/config/config.service.js.map +1 -1
- package/dist/esm/modules/config/data/entity/entity.js +1 -0
- package/dist/esm/modules/config/data/entity/entity.js.map +1 -1
- package/dist/esm/modules/config/index.d.ts +2 -0
- package/dist/esm/modules/config/migration/entity/entity.d.ts +12 -0
- package/dist/esm/modules/config/migration/entity/entity.js +137 -0
- package/dist/esm/modules/config/migration/entity/entity.js.map +1 -0
- package/dist/esm/modules/config/migration/entity/index.d.ts +1 -0
- package/dist/esm/modules/config/migration/enum/index.d.ts +1 -0
- package/dist/esm/modules/config/migration/enum/status.d.ts +7 -0
- package/dist/esm/modules/config/migration/enum/status.js +11 -0
- package/dist/esm/modules/config/migration/enum/status.js.map +1 -0
- package/dist/esm/modules/config/migration/index.d.ts +5 -0
- package/dist/esm/modules/config/migration/interface/index.d.ts +3 -0
- package/dist/esm/modules/config/migration/interface/interface.d.ts +11 -0
- package/dist/esm/modules/config/migration/interface/migration-definition.interface.d.ts +29 -0
- package/dist/esm/modules/config/migration/interface/migration-options.interface.d.ts +34 -0
- package/dist/esm/modules/config/migration/migration-runner.service.d.ts +22 -0
- package/dist/esm/modules/config/migration/migration-runner.service.js +85 -0
- package/dist/esm/modules/config/migration/migration-runner.service.js.map +1 -0
- package/dist/esm/modules/config/migration/migration.service.d.ts +68 -0
- package/dist/esm/modules/config/migration/migration.service.js +331 -0
- package/dist/esm/modules/config/migration/migration.service.js.map +1 -0
- package/dist/esm/modules/config/section/entity/entity.js +1 -0
- package/dist/esm/modules/config/section/entity/entity.js.map +1 -1
- package/dist/esm/package.json +2 -1
- package/dist/esm/shared/constant/config/index.d.ts +1 -0
- package/dist/esm/shared/constant/config/migration.constant.d.ts +6 -0
- package/dist/esm/shared/constant/config/migration.constant.js +13 -0
- package/dist/esm/shared/constant/config/migration.constant.js.map +1 -0
- package/dist/esm/shared/constant/token.constant.d.ts +2 -0
- package/dist/esm/shared/constant/token.constant.js +4 -0
- package/dist/esm/shared/constant/token.constant.js.map +1 -1
- package/dist/esm/shared/interface/config/options.interface.d.ts +3 -2
- package/dist/esm/shared/utility/index.d.ts +1 -0
- package/dist/esm/shared/utility/logger.utility.d.ts +21 -0
- package/dist/esm/shared/utility/logger.utility.js +67 -0
- package/dist/esm/shared/utility/logger.utility.js.map +1 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
</a> <img src="https://img.shields.io/badge/TypeScript-3178C6.svg?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"> <img src="https://img.shields.io/badge/NestJS-E0234E.svg?style=for-the-badge&logo=nestjs&logoColor=white" alt="NestJS"> <img src="https://img.shields.io/badge/Node.js-339933.svg?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js"> <img src="https://img.shields.io/badge/PostgreSQL-4169E1.svg?style=for-the-badge&logo=postgresql&logoColor=white" alt="PostgreSQL"> <img src="https://img.shields.io/badge/MySQL-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white" alt="MySQL"> <img src="https://img.shields.io/badge/SQLite-003B57.svg?style=for-the-badge&logo=sqlite&logoColor=white" alt="SQLite"> <img src="https://img.shields.io/badge/MongoDB-47A248.svg?style=for-the-badge&logo=mongodb&logoColor=white" alt="MongoDB"> <img src="https://img.shields.io/badge/Swagger-85EA2D.svg?style=for-the-badge&logo=swagger&logoColor=black" alt="Swagger">
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
## 📚 Table of Contents
|
|
15
|
+
|
|
16
16
|
- [Description](#-description)
|
|
17
17
|
- [Features](#-features)
|
|
18
18
|
- [Installation](#-installation)
|
|
19
19
|
- [Usage](#-usage)
|
|
20
|
+
- [Migration System](#-migration-system)
|
|
20
21
|
- [API Documentation](#-api-documentation)
|
|
21
22
|
- [Database Schema](#-database-schema)
|
|
22
23
|
- [Advanced Configuration](#-advanced-configuration)
|
|
@@ -24,7 +25,6 @@
|
|
|
24
25
|
- [FAQ](#-faq)
|
|
25
26
|
- [License](#-license)
|
|
26
27
|
|
|
27
|
-
|
|
28
28
|
## 📖 Description
|
|
29
29
|
|
|
30
30
|
NestJS CRUD Config is a powerful configuration management module that revolutionizes how NestJS applications handle configuration data. Unlike traditional environment variable approaches, this library stores configuration in a database with full CRUD operations, making it perfect for dynamic configuration management across multiple environments and services.
|
|
@@ -50,6 +50,7 @@ Whether you're managing API keys across different environments, storing feature
|
|
|
50
50
|
- **🚦 Flexible controller configuration** - Customize API paths, disable endpoints, or run in headless mode
|
|
51
51
|
- **🔧 Advanced CRUD customization** - Full control over routes, swagger documentation, and controller behavior
|
|
52
52
|
- **📦 Modular architecture** - Enable/disable features as needed for your use case
|
|
53
|
+
- **🚀 Production-ready migration system** - Initialize and manage configuration state during application startup
|
|
53
54
|
|
|
54
55
|
## 🛠 Installation
|
|
55
56
|
|
|
@@ -75,6 +76,7 @@ npm install @nestjs/common @nestjs/typeorm typeorm @elsikora/nestjs-crud-automat
|
|
|
75
76
|
### Database Support
|
|
76
77
|
|
|
77
78
|
This package works with any database supported by TypeORM:
|
|
79
|
+
|
|
78
80
|
- PostgreSQL
|
|
79
81
|
- MySQL/MariaDB
|
|
80
82
|
- SQLite
|
|
@@ -89,91 +91,88 @@ This package works with any database supported by TypeORM:
|
|
|
89
91
|
|
|
90
92
|
```typescript
|
|
91
93
|
// app.module.ts
|
|
92
|
-
import { Module } from
|
|
93
|
-
import { TypeOrmModule } from
|
|
94
|
-
import { CrudConfigModule, TOKEN_CONSTANT } from
|
|
94
|
+
import { Module } from "@nestjs/common";
|
|
95
|
+
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
96
|
+
import { CrudConfigModule, TOKEN_CONSTANT } from "@elsikora/nestjs-crud-config";
|
|
95
97
|
|
|
96
98
|
@Module({
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}),
|
|
175
|
-
}),
|
|
176
|
-
],
|
|
99
|
+
imports: [
|
|
100
|
+
// First register CrudConfigModule to create dynamic entities
|
|
101
|
+
CrudConfigModule.register({
|
|
102
|
+
environment: "development",
|
|
103
|
+
isVerbose: true,
|
|
104
|
+
shouldAutoCreateSections: true,
|
|
105
|
+
|
|
106
|
+
// Cache configuration
|
|
107
|
+
cacheOptions: {
|
|
108
|
+
isEnabled: true,
|
|
109
|
+
maxCacheItems: 1000,
|
|
110
|
+
maxCacheTTL: 3600000, // 1 hour in milliseconds
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// Encryption configuration
|
|
114
|
+
encryptionOptions: {
|
|
115
|
+
isEnabled: true,
|
|
116
|
+
encryptionKey: process.env.CONFIG_ENCRYPTION_KEY, // 32+ character key
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
// Controller configuration (optional)
|
|
120
|
+
controllersOptions: {
|
|
121
|
+
section: {
|
|
122
|
+
isEnabled: true,
|
|
123
|
+
properties: {
|
|
124
|
+
path: "api/config/sections",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
data: {
|
|
128
|
+
isEnabled: true,
|
|
129
|
+
properties: {
|
|
130
|
+
path: "api/config/data",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// Entity customization
|
|
136
|
+
entityOptions: {
|
|
137
|
+
tablePrefix: "app_",
|
|
138
|
+
configSection: {
|
|
139
|
+
tableName: "config_sections",
|
|
140
|
+
maxNameLength: 128,
|
|
141
|
+
maxDescriptionLength: 512,
|
|
142
|
+
},
|
|
143
|
+
configData: {
|
|
144
|
+
tableName: "config_data",
|
|
145
|
+
maxValueLength: 8192,
|
|
146
|
+
maxEnvironmentLength: 64,
|
|
147
|
+
maxNameLength: 128,
|
|
148
|
+
maxDescriptionLength: 512,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
|
|
153
|
+
// Then register TypeORM with dynamic entities using registerAsync
|
|
154
|
+
TypeOrmModule.forRootAsync({
|
|
155
|
+
imports: [CrudConfigModule],
|
|
156
|
+
inject: [TOKEN_CONSTANT.CONFIG_SECTION_ENTITY, TOKEN_CONSTANT.CONFIG_DATA_ENTITY],
|
|
157
|
+
useFactory: async (sectionEntity, dataEntity) => ({
|
|
158
|
+
type: "postgres",
|
|
159
|
+
host: "localhost",
|
|
160
|
+
port: 5432,
|
|
161
|
+
username: "postgres",
|
|
162
|
+
password: "postgres",
|
|
163
|
+
database: "config_db",
|
|
164
|
+
entities: [
|
|
165
|
+
// Your other entities
|
|
166
|
+
// UserEntity, ProductEntity, etc.
|
|
167
|
+
|
|
168
|
+
// Dynamic entities from CrudConfigModule
|
|
169
|
+
sectionEntity,
|
|
170
|
+
dataEntity,
|
|
171
|
+
],
|
|
172
|
+
synchronize: true,
|
|
173
|
+
}),
|
|
174
|
+
}),
|
|
175
|
+
],
|
|
177
176
|
})
|
|
178
177
|
export class AppModule {}
|
|
179
178
|
```
|
|
@@ -182,95 +181,446 @@ export class AppModule {}
|
|
|
182
181
|
|
|
183
182
|
```typescript
|
|
184
183
|
// config.service.ts
|
|
185
|
-
import { Injectable } from
|
|
186
|
-
import { CrudConfigService } from
|
|
184
|
+
import { Injectable } from "@nestjs/common";
|
|
185
|
+
import { CrudConfigService } from "@elsikora/nestjs-crud-config";
|
|
187
186
|
|
|
188
187
|
@Injectable()
|
|
189
188
|
export class MyConfigService {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
189
|
+
constructor(private readonly configService: CrudConfigService) {}
|
|
190
|
+
|
|
191
|
+
async setupApplicationConfig() {
|
|
192
|
+
// Set a configuration value
|
|
193
|
+
await this.configService.set({
|
|
194
|
+
section: "api-settings",
|
|
195
|
+
name: "API_KEY",
|
|
196
|
+
value: "my-secret-api-key",
|
|
197
|
+
description: "Production API key",
|
|
198
|
+
environment: "production",
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Retrieve configuration by section and name
|
|
202
|
+
const apiConfig = await this.configService.get({
|
|
203
|
+
section: "api-settings",
|
|
204
|
+
name: "API_KEY",
|
|
205
|
+
environment: "production",
|
|
206
|
+
shouldLoadSectionInfo: true,
|
|
207
|
+
useCache: true,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
console.log("API Configuration:", apiConfig);
|
|
211
|
+
// apiConfig.value will be automatically decrypted if it was encrypted
|
|
212
|
+
|
|
213
|
+
return apiConfig;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async getConfigurationList() {
|
|
217
|
+
// Get all configurations in a section
|
|
218
|
+
const configs = await this.configService.getList({
|
|
219
|
+
section: "api-settings",
|
|
220
|
+
environment: "production",
|
|
221
|
+
useCache: true,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return configs;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async deleteConfiguration() {
|
|
228
|
+
// Delete a configuration
|
|
229
|
+
await this.configService.delete({
|
|
230
|
+
section: "api-settings",
|
|
231
|
+
name: "API_KEY",
|
|
232
|
+
environment: "production",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
203
237
|
|
|
204
|
-
|
|
205
|
-
const apiConfig = await this.configService.get({
|
|
206
|
-
section: 'api-settings',
|
|
207
|
-
name: 'API_KEY',
|
|
208
|
-
environment: 'production',
|
|
209
|
-
shouldLoadSectionInfo: true,
|
|
210
|
-
useCache: true,
|
|
211
|
-
});
|
|
238
|
+
### Async Module Registration
|
|
212
239
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
240
|
+
```typescript
|
|
241
|
+
// app.module.ts
|
|
242
|
+
import { Module } from "@nestjs/common";
|
|
243
|
+
import { ConfigModule, ConfigService } from "@nestjs/config";
|
|
244
|
+
import { CrudConfigModule } from "@elsikora/nestjs-crud-config";
|
|
218
245
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
246
|
+
@Module({
|
|
247
|
+
imports: [
|
|
248
|
+
ConfigModule.forRoot(),
|
|
249
|
+
CrudConfigModule.registerAsync({
|
|
250
|
+
imports: [ConfigModule],
|
|
251
|
+
inject: [ConfigService],
|
|
252
|
+
useFactory: async (configService: ConfigService) => ({
|
|
253
|
+
environment: configService.get("NODE_ENV", "development"),
|
|
254
|
+
isVerbose: configService.get("VERBOSE", false),
|
|
255
|
+
encryptionOptions: {
|
|
256
|
+
isEnabled: true,
|
|
257
|
+
encryptionKey: configService.get("ENCRYPTION_KEY"),
|
|
258
|
+
},
|
|
259
|
+
cacheOptions: {
|
|
260
|
+
isEnabled: true,
|
|
261
|
+
maxCacheItems: 1000,
|
|
262
|
+
maxCacheTTL: 3600000,
|
|
263
|
+
},
|
|
264
|
+
}),
|
|
265
|
+
}),
|
|
266
|
+
],
|
|
267
|
+
})
|
|
268
|
+
export class AppModule {}
|
|
269
|
+
```
|
|
226
270
|
|
|
227
|
-
|
|
228
|
-
}
|
|
271
|
+
## 🚀 Migration System
|
|
229
272
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
273
|
+
The NestJS CRUD Config module includes a powerful **production-ready migration system** that allows you to initialize and manage configuration state during application startup. This system ensures your application always has the required configuration data across different environments and deployments.
|
|
274
|
+
|
|
275
|
+
### Key Features
|
|
276
|
+
|
|
277
|
+
- **🔄 Automatic execution on startup** - Migrations run automatically when the application bootstraps
|
|
278
|
+
- **💾 Transaction support** - All migrations run within database transactions for data consistency
|
|
279
|
+
- **🌍 Environment-specific configurations** - Create different configurations for different environments
|
|
280
|
+
- **📊 Migration tracking** - Complete audit trail with timestamps and execution status
|
|
281
|
+
- **🔙 Rollback support** - Define rollback procedures for each migration
|
|
282
|
+
- **⚡ Stuck migration cleanup** - Automatic timeout and cleanup of crashed migrations
|
|
283
|
+
- **🎯 Type-safe status tracking** - Uses `EConfigMigrationStatus` enum for reliable status management
|
|
284
|
+
- **🛡️ Race condition protection** - Thread-safe execution preventing concurrent conflicts
|
|
285
|
+
|
|
286
|
+
### Migration Status Enum
|
|
287
|
+
|
|
288
|
+
The migration system uses the `EConfigMigrationStatus` enum to track migration states:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import { EConfigMigrationStatus } from "@your-org/nestjs-crud-config";
|
|
292
|
+
|
|
293
|
+
// Available statuses:
|
|
294
|
+
EConfigMigrationStatus.PENDING; // Migration is pending execution
|
|
295
|
+
EConfigMigrationStatus.RUNNING; // Migration is currently running
|
|
296
|
+
EConfigMigrationStatus.COMPLETED; // Migration completed successfully
|
|
297
|
+
EConfigMigrationStatus.FAILED; // Migration failed with error
|
|
298
|
+
EConfigMigrationStatus.STUCK; // Migration is stuck (timeout)
|
|
239
299
|
```
|
|
240
300
|
|
|
241
|
-
###
|
|
301
|
+
### Basic Migration Configuration
|
|
302
|
+
|
|
303
|
+
Configure migrations in your module registration:
|
|
242
304
|
|
|
243
305
|
```typescript
|
|
244
|
-
|
|
245
|
-
import {
|
|
246
|
-
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
247
|
-
import { CrudConfigModule } from '@elsikora/nestjs-crud-config';
|
|
306
|
+
import { CrudConfigModule } from "@your-org/nestjs-crud-config";
|
|
307
|
+
import { initialAppConfigMigration } from "./migrations/001-initial-app-config.migration";
|
|
248
308
|
|
|
249
309
|
@Module({
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
310
|
+
imports: [
|
|
311
|
+
CrudConfigModule.register({
|
|
312
|
+
environment: process.env.NODE_ENV || "development",
|
|
313
|
+
|
|
314
|
+
// Migration configuration
|
|
315
|
+
migrationOptions: {
|
|
316
|
+
isEnabled: true,
|
|
317
|
+
shouldRunOnStartup: true,
|
|
318
|
+
useTransaction: true,
|
|
319
|
+
stuckMigrationTimeoutMinutes: 30,
|
|
320
|
+
tableName: "config_migrations", // Optional: custom table name
|
|
321
|
+
|
|
322
|
+
migrations: [
|
|
323
|
+
initialAppConfigMigration,
|
|
324
|
+
// Add more migrations here
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
// Other configuration options...
|
|
329
|
+
}),
|
|
330
|
+
],
|
|
270
331
|
})
|
|
271
332
|
export class AppModule {}
|
|
272
333
|
```
|
|
273
334
|
|
|
335
|
+
### Creating Migration Files
|
|
336
|
+
|
|
337
|
+
Create a migration file with the required structure:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
// migrations/001-initial-app-config.migration.ts
|
|
341
|
+
import type { IConfigMigrationDefinition } from "@your-org/nestjs-crud-config";
|
|
342
|
+
import type { CrudConfigService } from "@your-org/nestjs-crud-config";
|
|
343
|
+
import type { EntityManager } from "typeorm";
|
|
344
|
+
|
|
345
|
+
export const initialAppConfigMigration: IConfigMigrationDefinition = {
|
|
346
|
+
name: "001_initial_app_config",
|
|
347
|
+
description: "Initialize basic application configuration",
|
|
348
|
+
|
|
349
|
+
async up(configService: CrudConfigService, entityManager?: EntityManager): Promise<void> {
|
|
350
|
+
// Create application settings
|
|
351
|
+
await configService.set({
|
|
352
|
+
section: "app-settings",
|
|
353
|
+
name: "APP_NAME",
|
|
354
|
+
value: "My Application",
|
|
355
|
+
description: "Application name",
|
|
356
|
+
environment: "default",
|
|
357
|
+
eventManager: entityManager,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
await configService.set({
|
|
361
|
+
section: "app-settings",
|
|
362
|
+
name: "APP_VERSION",
|
|
363
|
+
value: "1.0.0",
|
|
364
|
+
description: "Application version",
|
|
365
|
+
environment: "default",
|
|
366
|
+
eventManager: entityManager,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Create API settings
|
|
370
|
+
await configService.set({
|
|
371
|
+
section: "api-settings",
|
|
372
|
+
name: "API_RATE_LIMIT",
|
|
373
|
+
value: "100",
|
|
374
|
+
description: "API rate limit per minute",
|
|
375
|
+
environment: "default",
|
|
376
|
+
eventManager: entityManager,
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
async down(configService: CrudConfigService, entityManager?: EntityManager): Promise<void> {
|
|
381
|
+
// Rollback: remove configurations created by this migration
|
|
382
|
+
const configurationsToRemove = [
|
|
383
|
+
{ name: "APP_NAME", section: "app-settings" },
|
|
384
|
+
{ name: "APP_VERSION", section: "app-settings" },
|
|
385
|
+
{ name: "API_RATE_LIMIT", section: "api-settings" },
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
for (const config of configurationsToRemove) {
|
|
389
|
+
try {
|
|
390
|
+
await configService.delete({
|
|
391
|
+
section: config.section,
|
|
392
|
+
name: config.name,
|
|
393
|
+
environment: "default",
|
|
394
|
+
eventManager: entityManager,
|
|
395
|
+
});
|
|
396
|
+
} catch (error) {
|
|
397
|
+
// Expected if config was never created
|
|
398
|
+
console.warn(`Failed to delete config ${config.section}:${config.name} during rollback`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Environment-Specific Migrations
|
|
406
|
+
|
|
407
|
+
Create migrations that work with different environments:
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
// migrations/002-environment-specific-config.migration.ts
|
|
411
|
+
export const environmentSpecificConfigMigration: IConfigMigrationDefinition = {
|
|
412
|
+
name: "002_environment_specific_config",
|
|
413
|
+
description: "Environment-specific configuration settings",
|
|
414
|
+
|
|
415
|
+
async up(configService: CrudConfigService, entityManager?: EntityManager): Promise<void> {
|
|
416
|
+
const environments = ["development", "staging", "production"];
|
|
417
|
+
|
|
418
|
+
for (const env of environments) {
|
|
419
|
+
// Environment-specific debug settings
|
|
420
|
+
await configService.set({
|
|
421
|
+
section: "app-settings",
|
|
422
|
+
name: "DEBUG_MODE",
|
|
423
|
+
value: env === "development" ? "true" : "false",
|
|
424
|
+
description: "Enable debug mode",
|
|
425
|
+
environment: env,
|
|
426
|
+
eventManager: entityManager,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Environment-specific API limits
|
|
430
|
+
const rateLimits = {
|
|
431
|
+
development: "1000",
|
|
432
|
+
staging: "500",
|
|
433
|
+
production: "100",
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
await configService.set({
|
|
437
|
+
section: "api-settings",
|
|
438
|
+
name: "API_RATE_LIMIT",
|
|
439
|
+
value: rateLimits[env],
|
|
440
|
+
description: "API rate limit per minute",
|
|
441
|
+
environment: env,
|
|
442
|
+
eventManager: entityManager,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
async down(configService: CrudConfigService, entityManager?: EntityManager): Promise<void> {
|
|
448
|
+
const environments = ["development", "staging", "production"];
|
|
449
|
+
const configurationsToRemove = [
|
|
450
|
+
{ name: "DEBUG_MODE", section: "app-settings" },
|
|
451
|
+
{ name: "API_RATE_LIMIT", section: "api-settings" },
|
|
452
|
+
];
|
|
453
|
+
|
|
454
|
+
for (const config of configurationsToRemove) {
|
|
455
|
+
for (const environment of environments) {
|
|
456
|
+
try {
|
|
457
|
+
await configService.delete({
|
|
458
|
+
section: config.section,
|
|
459
|
+
name: config.name,
|
|
460
|
+
environment: environment,
|
|
461
|
+
eventManager: entityManager,
|
|
462
|
+
});
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.warn(
|
|
465
|
+
`Failed to delete config ${config.section}:${config.name}:${environment} during rollback`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Advanced Migration Configuration
|
|
475
|
+
|
|
476
|
+
Configure migrations with custom options:
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
CrudConfigModule.registerAsync({
|
|
480
|
+
useFactory: async (configService: ConfigService) => ({
|
|
481
|
+
environment: configService.get("NODE_ENV", "development"),
|
|
482
|
+
|
|
483
|
+
migrationOptions: {
|
|
484
|
+
isEnabled: true,
|
|
485
|
+
shouldRunOnStartup: true,
|
|
486
|
+
useTransaction: true,
|
|
487
|
+
stuckMigrationTimeoutMinutes: 60, // Custom timeout
|
|
488
|
+
tableName: "my_migrations", // Custom table name
|
|
489
|
+
|
|
490
|
+
migrations: [
|
|
491
|
+
initialAppConfigMigration,
|
|
492
|
+
environmentSpecificConfigMigration,
|
|
493
|
+
// Add more migrations in order
|
|
494
|
+
],
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
// Other options...
|
|
498
|
+
}),
|
|
499
|
+
inject: [ConfigService],
|
|
500
|
+
});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Migration Best Practices
|
|
504
|
+
|
|
505
|
+
1. **Naming Convention**: Use descriptive names with sequence numbers
|
|
506
|
+
- ✅ `001_initial_app_config`
|
|
507
|
+
- ✅ `002_add_feature_flags`
|
|
508
|
+
- ❌ `migration1`, `config_setup`
|
|
509
|
+
|
|
510
|
+
2. **Idempotency**: Migrations should be safe to run multiple times
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
// Good: Check if configuration exists before creating
|
|
514
|
+
const existing = await configService.get({
|
|
515
|
+
section: 'app-settings',
|
|
516
|
+
name: 'APP_NAME',
|
|
517
|
+
environment: 'default',
|
|
518
|
+
}).catch(() => null);
|
|
519
|
+
|
|
520
|
+
if (!existing) {
|
|
521
|
+
await configService.set({...});
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
3. **Error Handling**: Always handle errors gracefully
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
try {
|
|
529
|
+
await configService.set({...});
|
|
530
|
+
} catch (error) {
|
|
531
|
+
console.error('Migration failed:', error);
|
|
532
|
+
throw error; // Re-throw to mark migration as failed
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
4. **Rollback Support**: Always implement the `down` method
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
async down(configService: CrudConfigService, entityManager?: EntityManager): Promise<void> {
|
|
540
|
+
// Implement rollback logic
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
5. **Environment Awareness**: Use environment-specific configurations
|
|
545
|
+
```typescript
|
|
546
|
+
const currentEnv = process.env.NODE_ENV || "development";
|
|
547
|
+
await configService.set({
|
|
548
|
+
environment: currentEnv,
|
|
549
|
+
// ... other options
|
|
550
|
+
});
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### Migration Monitoring
|
|
554
|
+
|
|
555
|
+
Monitor migration execution with logging:
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
import { EConfigMigrationStatus } from "@your-org/nestjs-crud-config";
|
|
559
|
+
|
|
560
|
+
@Injectable()
|
|
561
|
+
export class MigrationMonitorService {
|
|
562
|
+
constructor(private readonly migrationService: ConfigMigrationService) {}
|
|
563
|
+
|
|
564
|
+
async getMigrationStatus(): Promise<void> {
|
|
565
|
+
const migrations = await this.migrationService.getExecutedMigrations();
|
|
566
|
+
|
|
567
|
+
const completed = migrations.filter((m) => m.status === EConfigMigrationStatus.COMPLETED);
|
|
568
|
+
const failed = migrations.filter((m) => m.status === EConfigMigrationStatus.FAILED);
|
|
569
|
+
const stuck = migrations.filter((m) => m.status === EConfigMigrationStatus.STUCK);
|
|
570
|
+
|
|
571
|
+
console.log(
|
|
572
|
+
`Migrations - Completed: ${completed.length}, Failed: ${failed.length}, Stuck: ${stuck.length}`,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async getFailedMigrations(): Promise<IConfigMigration[]> {
|
|
577
|
+
const migrations = await this.migrationService.getExecutedMigrations();
|
|
578
|
+
return migrations.filter((m) => m.status === EConfigMigrationStatus.FAILED);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Testing Migrations
|
|
584
|
+
|
|
585
|
+
Test your migrations in different environments:
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
describe("Initial App Config Migration", () => {
|
|
589
|
+
let configService: CrudConfigService;
|
|
590
|
+
let migration: IConfigMigrationDefinition;
|
|
591
|
+
|
|
592
|
+
beforeEach(async () => {
|
|
593
|
+
// Setup test environment
|
|
594
|
+
migration = initialAppConfigMigration;
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it("should create required configurations", async () => {
|
|
598
|
+
await migration.up(configService);
|
|
599
|
+
|
|
600
|
+
const appName = await configService.get({
|
|
601
|
+
section: "app-settings",
|
|
602
|
+
name: "APP_NAME",
|
|
603
|
+
environment: "default",
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
expect(appName.value).toBe("My Application");
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it("should rollback configurations", async () => {
|
|
610
|
+
await migration.up(configService);
|
|
611
|
+
await migration.down(configService);
|
|
612
|
+
|
|
613
|
+
await expect(
|
|
614
|
+
configService.get({
|
|
615
|
+
section: "app-settings",
|
|
616
|
+
name: "APP_NAME",
|
|
617
|
+
environment: "default",
|
|
618
|
+
}),
|
|
619
|
+
).rejects.toThrow();
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
```
|
|
623
|
+
|
|
274
624
|
## 📚 API Documentation
|
|
275
625
|
|
|
276
626
|
The module automatically generates REST API endpoints with customizable paths:
|
|
@@ -284,10 +634,11 @@ The module automatically generates REST API endpoints with customizable paths:
|
|
|
284
634
|
- `DELETE /config/section/:id` - Delete section
|
|
285
635
|
|
|
286
636
|
**Request Body for POST/PUT:**
|
|
637
|
+
|
|
287
638
|
```json
|
|
288
639
|
{
|
|
289
|
-
|
|
290
|
-
|
|
640
|
+
"name": "api-settings",
|
|
641
|
+
"description": "API configuration settings"
|
|
291
642
|
}
|
|
292
643
|
```
|
|
293
644
|
|
|
@@ -300,14 +651,15 @@ The module automatically generates REST API endpoints with customizable paths:
|
|
|
300
651
|
- `DELETE /config/data/:id` - Delete configuration
|
|
301
652
|
|
|
302
653
|
**Request Body for POST/PUT:**
|
|
654
|
+
|
|
303
655
|
```json
|
|
304
656
|
{
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
657
|
+
"name": "API_KEY",
|
|
658
|
+
"value": "your-secret-key",
|
|
659
|
+
"environment": "production",
|
|
660
|
+
"description": "Production API key",
|
|
661
|
+
"isEncrypted": true,
|
|
662
|
+
"section": { "id": "section-uuid-here" }
|
|
311
663
|
}
|
|
312
664
|
```
|
|
313
665
|
|
|
@@ -315,26 +667,26 @@ The module automatically generates REST API endpoints with customizable paths:
|
|
|
315
667
|
|
|
316
668
|
```typescript
|
|
317
669
|
CrudConfigModule.register({
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
},
|
|
326
|
-
},
|
|
670
|
+
controllersOptions: {
|
|
671
|
+
section: {
|
|
672
|
+
properties: {
|
|
673
|
+
path: "api/v1/settings/sections",
|
|
674
|
+
name: "CustomSectionController",
|
|
675
|
+
swagger: {
|
|
676
|
+
tags: ["Configuration Sections"],
|
|
327
677
|
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
data: {
|
|
681
|
+
properties: {
|
|
682
|
+
path: "api/v1/settings/data",
|
|
683
|
+
routes: {
|
|
684
|
+
DELETE: { isEnabled: false }, // Disable deletion
|
|
335
685
|
},
|
|
686
|
+
},
|
|
336
687
|
},
|
|
337
|
-
}
|
|
688
|
+
},
|
|
689
|
+
});
|
|
338
690
|
```
|
|
339
691
|
|
|
340
692
|
## 🗄️ Database Schema
|
|
@@ -342,6 +694,7 @@ CrudConfigModule.register({
|
|
|
342
694
|
The module creates two main tables with the following default structure:
|
|
343
695
|
|
|
344
696
|
### Configuration Sections Table (`config_section`)
|
|
697
|
+
|
|
345
698
|
```sql
|
|
346
699
|
CREATE TABLE config_section (
|
|
347
700
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
@@ -353,6 +706,7 @@ CREATE TABLE config_section (
|
|
|
353
706
|
```
|
|
354
707
|
|
|
355
708
|
### Configuration Data Table (`config_data`)
|
|
709
|
+
|
|
356
710
|
```sql
|
|
357
711
|
CREATE TABLE config_data (
|
|
358
712
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
@@ -377,23 +731,23 @@ The module provides built-in AES-256-GCM encryption for sensitive configuration
|
|
|
377
731
|
```typescript
|
|
378
732
|
// Enable encryption globally
|
|
379
733
|
CrudConfigModule.register({
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
})
|
|
734
|
+
encryptionOptions: {
|
|
735
|
+
isEnabled: true,
|
|
736
|
+
encryptionKey: process.env.CONFIG_ENCRYPTION_KEY, // 32+ character key
|
|
737
|
+
},
|
|
738
|
+
});
|
|
385
739
|
|
|
386
740
|
// When encryption is enabled, all values are encrypted automatically
|
|
387
741
|
await configService.set({
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
742
|
+
section: "database",
|
|
743
|
+
name: "DB_PASSWORD",
|
|
744
|
+
value: "my-secret-password",
|
|
391
745
|
});
|
|
392
746
|
|
|
393
747
|
// Retrieve and decrypt automatically
|
|
394
748
|
const config = await configService.get({
|
|
395
|
-
|
|
396
|
-
|
|
749
|
+
section: "database",
|
|
750
|
+
name: "DB_PASSWORD",
|
|
397
751
|
});
|
|
398
752
|
|
|
399
753
|
console.log(config.value); // Automatically decrypted value
|
|
@@ -408,24 +762,24 @@ The module includes comprehensive event handling with both listeners and TypeORM
|
|
|
408
762
|
// Event listener example
|
|
409
763
|
@Injectable()
|
|
410
764
|
export class ConfigDataBeforeInsertListener {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
765
|
+
@OnEvent("config-data.beforeInsert")
|
|
766
|
+
async handleBeforeInsert(payload: ConfigDataEventBeforeInsert) {
|
|
767
|
+
// Custom validation logic
|
|
768
|
+
const entity = payload.item;
|
|
769
|
+
const entityManager = payload.eventManager;
|
|
770
|
+
|
|
771
|
+
// Perform custom checks
|
|
772
|
+
return { isSuccess: true };
|
|
773
|
+
}
|
|
420
774
|
}
|
|
421
775
|
|
|
422
776
|
// TypeORM subscriber for database-level events
|
|
423
777
|
@EventSubscriber()
|
|
424
778
|
export class ConfigDataBeforeInsertSubscriber {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
779
|
+
beforeInsert(event: InsertEvent<IConfigData>) {
|
|
780
|
+
// Database-level validation
|
|
781
|
+
return Promise.resolve(true);
|
|
782
|
+
}
|
|
429
783
|
}
|
|
430
784
|
```
|
|
431
785
|
|
|
@@ -433,14 +787,15 @@ export class ConfigDataBeforeInsertSubscriber {
|
|
|
433
787
|
|
|
434
788
|
```typescript
|
|
435
789
|
CrudConfigModule.register({
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
})
|
|
790
|
+
controllersOptions: {
|
|
791
|
+
section: { isEnabled: false },
|
|
792
|
+
data: { isEnabled: false },
|
|
793
|
+
},
|
|
794
|
+
});
|
|
441
795
|
```
|
|
442
796
|
|
|
443
797
|
This is perfect for:
|
|
798
|
+
|
|
444
799
|
- Background services that only need programmatic access
|
|
445
800
|
- Microservices that manage config through message queues
|
|
446
801
|
- Applications with custom GraphQL or gRPC interfaces
|
|
@@ -451,62 +806,61 @@ This is perfect for:
|
|
|
451
806
|
// multi-env.service.ts
|
|
452
807
|
@Injectable()
|
|
453
808
|
export class MultiEnvironmentService {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
description: `Database URL for ${env} environment`,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
809
|
+
constructor(private readonly configService: CrudConfigService) {}
|
|
810
|
+
|
|
811
|
+
async setupEnvironmentConfigs() {
|
|
812
|
+
const environments = ["development", "staging", "production"];
|
|
813
|
+
|
|
814
|
+
for (const env of environments) {
|
|
815
|
+
await this.configService.set({
|
|
816
|
+
section: "database",
|
|
817
|
+
name: "DATABASE_URL",
|
|
818
|
+
environment: env,
|
|
819
|
+
value: `postgres://localhost:5432/${env}_db`,
|
|
820
|
+
description: `Database URL for ${env} environment`,
|
|
821
|
+
});
|
|
470
822
|
}
|
|
823
|
+
}
|
|
471
824
|
}
|
|
472
825
|
```
|
|
473
826
|
|
|
474
827
|
## 🛣 Roadmap
|
|
475
828
|
|
|
476
|
-
| Task / Feature
|
|
477
|
-
|
|
478
|
-
| Core dynamic entity system
|
|
479
|
-
| TypeORM integration with all databases
|
|
480
|
-
| Hierarchical configuration (sections/data)
|
|
481
|
-
| Full CRUD operations with REST API
|
|
482
|
-
| Swagger/OpenAPI documentation
|
|
483
|
-
| Multi-environment support
|
|
484
|
-
| Event-driven architecture with hooks
|
|
485
|
-
| Caching system with TTL
|
|
486
|
-
| Custom table names and prefixes
|
|
487
|
-
| Validation and constraints
|
|
488
|
-
| NestJS CRUD Automator integration
|
|
489
|
-
| TypeScript interfaces and types
|
|
490
|
-
| AES-256-GCM encryption support
|
|
491
|
-
| Async module registration
|
|
492
|
-
| Auto-section creation
|
|
493
|
-
| GraphQL API endpoints
|
|
494
|
-
| Configuration versioning and history
|
|
495
|
-
| Role-based access control (RBAC)
|
|
496
|
-
| Configuration templates and inheritance
|
|
829
|
+
| Task / Feature | Status |
|
|
830
|
+
| --------------------------------------------- | -------------- |
|
|
831
|
+
| Core dynamic entity system | ✅ Done |
|
|
832
|
+
| TypeORM integration with all databases | ✅ Done |
|
|
833
|
+
| Hierarchical configuration (sections/data) | ✅ Done |
|
|
834
|
+
| Full CRUD operations with REST API | ✅ Done |
|
|
835
|
+
| Swagger/OpenAPI documentation | ✅ Done |
|
|
836
|
+
| Multi-environment support | ✅ Done |
|
|
837
|
+
| Event-driven architecture with hooks | ✅ Done |
|
|
838
|
+
| Caching system with TTL | ✅ Done |
|
|
839
|
+
| Custom table names and prefixes | ✅ Done |
|
|
840
|
+
| Validation and constraints | ✅ Done |
|
|
841
|
+
| NestJS CRUD Automator integration | ✅ Done |
|
|
842
|
+
| TypeScript interfaces and types | ✅ Done |
|
|
843
|
+
| AES-256-GCM encryption support | ✅ Done |
|
|
844
|
+
| Async module registration | ✅ Done |
|
|
845
|
+
| Auto-section creation | ✅ Done |
|
|
846
|
+
| GraphQL API endpoints | 🚧 In Progress |
|
|
847
|
+
| Configuration versioning and history | 🚧 In Progress |
|
|
848
|
+
| Role-based access control (RBAC) | 🚧 In Progress |
|
|
849
|
+
| Configuration templates and inheritance | 🚧 In Progress |
|
|
497
850
|
| Real-time configuration updates via WebSocket | 🚧 In Progress |
|
|
498
|
-
| Configuration validation schemas
|
|
499
|
-
| Bulk import/export functionality
|
|
500
|
-
| Configuration diff and merge tools
|
|
501
|
-
| Audit logging and change tracking
|
|
502
|
-
| Configuration backup and restore
|
|
503
|
-
| Integration with external secret managers
|
|
851
|
+
| Configuration validation schemas | 🚧 In Progress |
|
|
852
|
+
| Bulk import/export functionality | 🚧 In Progress |
|
|
853
|
+
| Configuration diff and merge tools | 🚧 In Progress |
|
|
854
|
+
| Audit logging and change tracking | 🚧 In Progress |
|
|
855
|
+
| Configuration backup and restore | 🚧 In Progress |
|
|
856
|
+
| Integration with external secret managers | 🚧 In Progress |
|
|
504
857
|
|
|
505
858
|
## ❓ FAQ
|
|
506
859
|
|
|
507
860
|
### What databases are supported?
|
|
508
861
|
|
|
509
862
|
The module supports any database that TypeORM supports, including:
|
|
863
|
+
|
|
510
864
|
- **PostgreSQL** - Recommended for production
|
|
511
865
|
- **MySQL/MariaDB** - Popular choice for web applications
|
|
512
866
|
- **SQLite** - Perfect for development and testing
|
|
@@ -518,6 +872,7 @@ The module supports any database that TypeORM supports, including:
|
|
|
518
872
|
### How does this compare to environment variables?
|
|
519
873
|
|
|
520
874
|
While environment variables are great for simple configurations, this module provides:
|
|
875
|
+
|
|
521
876
|
- **Database persistence** - Configurations survive container restarts
|
|
522
877
|
- **Runtime updates** - Change configurations without redeployment
|
|
523
878
|
- **Hierarchical organization** - Group related configurations
|
|
@@ -532,15 +887,15 @@ Yes! You can easily migrate by programmatically setting configurations during ap
|
|
|
532
887
|
|
|
533
888
|
```typescript
|
|
534
889
|
async function migrateFromEnvVars() {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
890
|
+
const configs = [
|
|
891
|
+
{ section: "database", name: "DATABASE_URL", value: process.env.DATABASE_URL },
|
|
892
|
+
{ section: "api", name: "API_KEY", value: process.env.API_KEY },
|
|
893
|
+
// ... more configurations
|
|
894
|
+
];
|
|
895
|
+
|
|
896
|
+
for (const config of configs) {
|
|
897
|
+
await configService.set(config);
|
|
898
|
+
}
|
|
544
899
|
}
|
|
545
900
|
```
|
|
546
901
|
|
|
@@ -550,11 +905,11 @@ The module provides built-in AES-256-GCM encryption support:
|
|
|
550
905
|
|
|
551
906
|
```typescript
|
|
552
907
|
CrudConfigModule.register({
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
})
|
|
908
|
+
encryptionOptions: {
|
|
909
|
+
isEnabled: true,
|
|
910
|
+
encryptionKey: process.env.CONFIG_ENCRYPTION_KEY,
|
|
911
|
+
},
|
|
912
|
+
});
|
|
558
913
|
```
|
|
559
914
|
|
|
560
915
|
Sensitive values are automatically encrypted before storage and decrypted when retrieved.
|
|
@@ -562,6 +917,7 @@ Sensitive values are automatically encrypted before storage and decrypted when r
|
|
|
562
917
|
### Can I use this with microservices?
|
|
563
918
|
|
|
564
919
|
Absolutely! The module is perfect for microservice architectures:
|
|
920
|
+
|
|
565
921
|
- **Centralized configuration** - All services can share the same config database
|
|
566
922
|
- **Service-specific sections** - Organize configurations by service
|
|
567
923
|
- **Environment isolation** - Separate dev/staging/prod configurations
|
|
@@ -573,27 +929,28 @@ The module provides extensive customization options:
|
|
|
573
929
|
|
|
574
930
|
```typescript
|
|
575
931
|
CrudConfigModule.register({
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
},
|
|
583
|
-
configData: {
|
|
584
|
-
tableName: 'configuration_values',
|
|
585
|
-
maxValueLength: 16384, // 16KB values
|
|
586
|
-
maxEnvironmentLength: 128,
|
|
587
|
-
maxNameLength: 256,
|
|
588
|
-
maxDescriptionLength: 1024,
|
|
589
|
-
},
|
|
932
|
+
entityOptions: {
|
|
933
|
+
tablePrefix: "myapp_",
|
|
934
|
+
configSection: {
|
|
935
|
+
tableName: "configuration_sections",
|
|
936
|
+
maxNameLength: 256,
|
|
937
|
+
maxDescriptionLength: 1024,
|
|
590
938
|
},
|
|
591
|
-
|
|
939
|
+
configData: {
|
|
940
|
+
tableName: "configuration_values",
|
|
941
|
+
maxValueLength: 16384, // 16KB values
|
|
942
|
+
maxEnvironmentLength: 128,
|
|
943
|
+
maxNameLength: 256,
|
|
944
|
+
maxDescriptionLength: 1024,
|
|
945
|
+
},
|
|
946
|
+
},
|
|
947
|
+
});
|
|
592
948
|
```
|
|
593
949
|
|
|
594
950
|
### What happens if the database is unavailable?
|
|
595
951
|
|
|
596
952
|
The module includes caching to handle temporary database outages:
|
|
953
|
+
|
|
597
954
|
- **In-memory cache** - Recently accessed configurations are cached
|
|
598
955
|
- **Configurable TTL** - Control how long configurations are cached
|
|
599
956
|
- **Graceful degradation** - Falls back to cached values when database is unavailable
|
|
@@ -604,16 +961,17 @@ Yes! The module supports "headless mode" where controllers are disabled:
|
|
|
604
961
|
|
|
605
962
|
```typescript
|
|
606
963
|
CrudConfigModule.register({
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
})
|
|
964
|
+
controllersOptions: {
|
|
965
|
+
section: { isEnabled: false },
|
|
966
|
+
data: { isEnabled: false },
|
|
967
|
+
},
|
|
968
|
+
});
|
|
612
969
|
```
|
|
613
970
|
|
|
614
971
|
### Is this production-ready?
|
|
615
972
|
|
|
616
973
|
Yes! The module is built with production use in mind:
|
|
974
|
+
|
|
617
975
|
- **Type-safe** - Full TypeScript support prevents runtime errors
|
|
618
976
|
- **Battle-tested** - Built on proven technologies (NestJS, TypeORM)
|
|
619
977
|
- **Scalable** - Works with enterprise databases
|