@flusys/nestjs-core 1.1.0-beta → 1.2.0-rc.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 +136 -162
- package/cjs/config/env-config.service.js +14 -10
- package/cjs/docs/docs.config.js +99 -15
- package/cjs/docs/index.js +0 -1
- package/cjs/index.js +0 -1
- package/cjs/interfaces/base-entity.interface.js +5 -3
- package/cjs/interfaces/database.interface.js +1 -3
- package/cjs/migration/datasource.factory.js +1 -3
- package/cjs/migration/index.js +0 -12
- package/cjs/migration/migration.cli.js +1 -17
- package/cjs/migration/migration.runner.js +37 -65
- package/cjs/utils/datasource-config.builder.js +2 -14
- package/config/env-config.service.d.ts +3 -1
- package/docs/docs.config.d.ts +8 -0
- package/docs/index.d.ts +0 -1
- package/fesm/config/env-config.service.js +14 -10
- package/fesm/docs/docs.config.js +89 -15
- package/fesm/docs/index.js +0 -1
- package/fesm/index.js +0 -2
- package/fesm/interfaces/app-config.interfaces.js +1 -3
- package/fesm/interfaces/base-entity.interface.js +5 -5
- package/fesm/interfaces/database.interface.js +1 -5
- package/fesm/migration/cli.js +1 -20
- package/fesm/migration/datasource.factory.js +3 -20
- package/fesm/migration/index.js +0 -14
- package/fesm/migration/migration.cli.js +1 -17
- package/fesm/migration/migration.runner.js +43 -132
- package/fesm/utils/datasource-config.builder.js +2 -13
- package/index.d.ts +0 -1
- package/interfaces/app-config.interfaces.d.ts +1 -0
- package/interfaces/base-entity.interface.d.ts +2 -2
- package/interfaces/database.interface.d.ts +2 -1
- package/package.json +9 -9
- package/utils/datasource-config.builder.d.ts +0 -1
- package/cjs/docs/docs.setup.js +0 -14
- package/cjs/seeders/base-seeder.js +0 -95
- package/cjs/seeders/cli.js +0 -398
- package/cjs/seeders/data-generator.js +0 -247
- package/cjs/seeders/entity-reader.js +0 -168
- package/cjs/seeders/index.js +0 -77
- package/cjs/seeders/seed-config.js +0 -101
- package/cjs/seeders/seed-runner.js +0 -320
- package/cjs/seeders/template-generator.js +0 -297
- package/docs/docs.setup.d.ts +0 -3
- package/fesm/docs/docs.setup.js +0 -4
- package/fesm/seeders/base-seeder.js +0 -110
- package/fesm/seeders/cli.js +0 -366
- package/fesm/seeders/data-generator.js +0 -249
- package/fesm/seeders/entity-reader.js +0 -170
- package/fesm/seeders/index.js +0 -11
- package/fesm/seeders/seed-config.js +0 -85
- package/fesm/seeders/seed-runner.js +0 -323
- package/fesm/seeders/template-generator.js +0 -257
- package/seeders/base-seeder.d.ts +0 -14
- package/seeders/cli.d.ts +0 -3
- package/seeders/data-generator.d.ts +0 -15
- package/seeders/entity-reader.d.ts +0 -45
- package/seeders/index.d.ts +0 -7
- package/seeders/seed-config.d.ts +0 -11
- package/seeders/seed-runner.d.ts +0 -47
- package/seeders/template-generator.d.ts +0 -16
package/README.md
CHANGED
|
@@ -1,197 +1,171 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @flusys/nestjs-core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> **Type:** Pure TypeScript utilities (Zero NestJS dependencies)
|
|
3
|
+
Foundation infrastructure for NestJS applications — environment config, TypeORM migrations, and Swagger setup.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/nestjs-core)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
---
|
|
9
9
|
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @flusys/nestjs-core
|
|
14
|
+
npm install typeorm typeorm-naming-strategies dotenv
|
|
15
|
+
npm install -D ts-node tsconfig-paths
|
|
10
16
|
```
|
|
11
|
-
nestjs-core/src/
|
|
12
|
-
├── config/
|
|
13
|
-
│ └── env-config.service.ts # Environment configuration singleton
|
|
14
|
-
├── constants/
|
|
15
|
-
│ └── database.constants.ts # MODULE_OPTIONS, DEFAULT_TENANT_HEADER
|
|
16
|
-
├── interfaces/
|
|
17
|
-
│ ├── app-config.interfaces.ts # IBootstrapAppConfig, IDynamicModuleConfig
|
|
18
|
-
│ ├── base-entity.interface.ts # IBaseEntity, ISoftDeletable, ITimestampable
|
|
19
|
-
│ ├── database.interface.ts # IDatabaseConfig, ITenantDatabaseConfig
|
|
20
|
-
│ └── migration.interface.ts # IMigrationConfig, IMigrationResult
|
|
21
|
-
├── utils/
|
|
22
|
-
│ └── datasource-config.builder.ts
|
|
23
|
-
├── migration/
|
|
24
|
-
│ ├── cli.ts, migration.cli.ts, datasource.factory.ts, migration.runner.ts
|
|
25
|
-
├── seeders/
|
|
26
|
-
│ ├── cli.ts, base-seeder.ts, seed-runner.ts, seed-config.ts
|
|
27
|
-
│ ├── entity-reader.ts, data-generator.ts, template-generator.ts
|
|
28
|
-
├── docs/
|
|
29
|
-
│ ├── docs.config.ts, docs.setup.ts
|
|
30
|
-
└── index.ts
|
|
31
|
-
```
|
|
32
17
|
|
|
33
|
-
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Environment Config
|
|
21
|
+
|
|
22
|
+
Access typed environment variables via the `envConfig` singleton — no class instantiation needed.
|
|
34
23
|
|
|
35
24
|
```typescript
|
|
36
|
-
import { envConfig } from '@flusys/nestjs-core
|
|
25
|
+
import { envConfig } from '@flusys/nestjs-core';
|
|
26
|
+
|
|
27
|
+
const port = envConfig.getPort(); // number
|
|
28
|
+
const db = envConfig.getTypeOrmConfig(); // { host, port, username, password, database }
|
|
29
|
+
const jwt = envConfig.getJwtConfig(); // { secret, expiration, refreshSecret, ... }
|
|
30
|
+
const origins = envConfig.getOrigins(); // string[]
|
|
31
|
+
const isProd = envConfig.isProduction(); // boolean
|
|
32
|
+
public tryGetValue(key: string, throwOnMissing = false): string // get env value
|
|
37
33
|
```
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
| `getPort()` | Get PORT (default: 3000) |
|
|
45
|
-
| `isProduction()` | Check if MODE !== 'DEV' |
|
|
46
|
-
| `getFrontendUrl()` | Get FRONTEND_URL |
|
|
47
|
-
| `getOrigins()` | Get ALLOW_ORIGINS as array |
|
|
48
|
-
| `getTypeOrmConfig()` | Get database config |
|
|
49
|
-
| `getJwtConfig()` | Get JWT config |
|
|
50
|
-
| `getRedisUrl()` | Get Redis connection URL |
|
|
51
|
-
| `getMailConfig()` | Get mail configuration |
|
|
52
|
-
| `getTenantId()` | Get TENANT_ID |
|
|
53
|
-
| `useTenantMode()` | Check USE_TENANT_MODE |
|
|
54
|
-
| `getLogConfig()` | Get logging configuration |
|
|
55
|
-
| `getEnv()` | Get all env vars as Record |
|
|
56
|
-
|
|
57
|
-
## Database Interfaces
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Migrations
|
|
38
|
+
|
|
39
|
+
### Create `src/persistence/migration.config.ts`
|
|
58
40
|
|
|
59
41
|
```typescript
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
42
|
+
import { createMigrationDataSource, IMigrationConfig } from '@flusys/nestjs-core';
|
|
43
|
+
import { envConfig } from '@flusys/nestjs-core/config';
|
|
44
|
+
import { getAuthEntitiesByConfig } from '@flusys/nestjs-auth';
|
|
45
|
+
import { getIAMEntitiesByConfig } from '@flusys/nestjs-iam';
|
|
46
|
+
|
|
47
|
+
const bootstrapConfig = {
|
|
48
|
+
databaseMode: 'single' as const,
|
|
49
|
+
enableCompanyFeature: true,
|
|
50
|
+
permissionMode: 'FULL' as const,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const migrationConfig: IMigrationConfig = {
|
|
54
|
+
defaultDatabaseConfig: envConfig.getTypeOrmConfig(),
|
|
55
|
+
bootstrapAppConfig: bootstrapConfig,
|
|
56
|
+
migrationsPath: `${__dirname}/migrations`,
|
|
57
|
+
migrationsTableName: 'migrations',
|
|
58
|
+
entities: [
|
|
59
|
+
...getAuthEntitiesByConfig(bootstrapConfig),
|
|
60
|
+
...getIAMEntitiesByConfig(bootstrapConfig),
|
|
61
|
+
],
|
|
62
|
+
};
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
// Required for TypeORM CLI
|
|
65
|
+
export default createMigrationDataSource({
|
|
66
|
+
config: migrationConfig,
|
|
67
|
+
tenantId: process.env.TENANT_ID,
|
|
68
|
+
});
|
|
69
|
+
```
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
### Add scripts to `package.json`
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"scripts": {
|
|
76
|
+
"migration": "ts-node -r tsconfig-paths/register ./node_modules/@flusys/nestjs-core/migration/cli --config=src/persistence/migration.config.ts",
|
|
77
|
+
"migration:generate": "npm run migration -- generate",
|
|
78
|
+
"migration:run": "npm run migration -- run",
|
|
79
|
+
"migration:revert": "npm run migration -- revert",
|
|
80
|
+
"migration:status": "npm run migration -- status",
|
|
81
|
+
"migration:run:all": "npm run migration -- run:all",
|
|
82
|
+
"migration:revert:all": "npm run migration -- revert:all",
|
|
83
|
+
"migration:status:all": "npm run migration -- status:all"
|
|
84
|
+
}
|
|
76
85
|
}
|
|
77
86
|
```
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
### Run
|
|
80
89
|
|
|
81
90
|
```bash
|
|
82
|
-
|
|
91
|
+
# Single database
|
|
92
|
+
npm run migration:generate -- --name=CreateUsersTable
|
|
83
93
|
npm run migration:run
|
|
84
94
|
npm run migration:revert
|
|
85
95
|
npm run migration:status
|
|
86
|
-
TENANT_ID=tenant1 npm run migration:run # specific tenant
|
|
87
|
-
npm run migration:run:all # all tenants
|
|
88
|
-
```
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
# Tenant-specific (prefix with TENANT_ID)
|
|
98
|
+
TENANT_ID=tenant-1 npm run migration:generate -- --name=CreateUsersTable
|
|
99
|
+
TENANT_ID=tenant-1 npm run migration:run
|
|
100
|
+
TENANT_ID=tenant-1 npm run migration:revert
|
|
101
|
+
TENANT_ID=tenant-1 npm run migration:status
|
|
93
102
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
npm run
|
|
98
|
-
npm run seed:run -- --entity=User --count=100
|
|
99
|
-
npm run seed:clear
|
|
100
|
-
npm run seed:status
|
|
103
|
+
# All tenants at once
|
|
104
|
+
npm run migration:run:all
|
|
105
|
+
npm run migration:revert:all
|
|
106
|
+
npm run migration:status:all
|
|
101
107
|
```
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
import { BaseSeeder, SeedRunner } from '@flusys/nestjs-core/seeders';
|
|
105
|
-
|
|
106
|
-
class UserSeeder extends BaseSeeder<User> {
|
|
107
|
-
constructor(ds: DataSource) { super(ds, User); }
|
|
108
|
-
async generate(count: number): Promise<User[]> {
|
|
109
|
-
const users = [];
|
|
110
|
-
for (let i = 0; i < count; i++) {
|
|
111
|
-
users.push(this.repository.create({ email: faker.internet.email() }));
|
|
112
|
-
}
|
|
113
|
-
return this.repository.save(users);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
109
|
+
---
|
|
116
110
|
|
|
117
|
-
|
|
118
|
-
runner.registerCustomSeeder('User', new UserSeeder(dataSource));
|
|
119
|
-
await runner.runAll({ count: 50, clear: true });
|
|
120
|
-
```
|
|
111
|
+
## 3. Swagger
|
|
121
112
|
|
|
122
|
-
|
|
113
|
+
`setupSwaggerDocs` and `setupModuleSwaggerDocs` accept the same `IModuleSwaggerOptions` shape.
|
|
123
114
|
|
|
124
115
|
```typescript
|
|
116
|
+
import { NestFactory } from '@nestjs/core';
|
|
125
117
|
import { setupSwaggerDocs, setupModuleSwaggerDocs } from '@flusys/nestjs-core/docs';
|
|
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
|
-
// Seeders
|
|
175
|
-
import {
|
|
176
|
-
BaseSeeder, SeedRunner, EntityReader, DataGenerator,
|
|
177
|
-
seedConfig, runSeedCli,
|
|
178
|
-
} from '@flusys/nestjs-core/seeders';
|
|
179
|
-
|
|
180
|
-
// Swagger
|
|
181
|
-
import {
|
|
182
|
-
setupSwaggerDocs, setupModuleSwaggerDocs,
|
|
183
|
-
IModuleSwaggerOptions, ISchemaPropertyExclusion,
|
|
184
|
-
} from '@flusys/nestjs-core/docs';
|
|
118
|
+
import { AuthModule } from '@flusys/nestjs-auth';
|
|
119
|
+
|
|
120
|
+
async function bootstrap() {
|
|
121
|
+
const app = await NestFactory.create(AppModule);
|
|
122
|
+
|
|
123
|
+
// All available options (one entry showing every field)
|
|
124
|
+
setupModuleSwaggerDocs(app, [
|
|
125
|
+
{
|
|
126
|
+
title: 'Auth API', // required
|
|
127
|
+
description: 'Auth endpoints', // required
|
|
128
|
+
version: '1.0', // optional, default '1.0'
|
|
129
|
+
path: 'api/docs/auth', // required — URL where docs are served
|
|
130
|
+
bearerAuth: true, // adds Authorization header to Swagger UI
|
|
131
|
+
modules: [AuthModule], // scope to specific NestJS modules only
|
|
132
|
+
|
|
133
|
+
// Exclude controllers by @ApiTags name
|
|
134
|
+
excludeTags: ['Internal', 'Health'],
|
|
135
|
+
|
|
136
|
+
// Exclude specific paths (supports * and ** wildcards)
|
|
137
|
+
excludePaths: ['/api/auth/internal/*', '/api/health'],
|
|
138
|
+
|
|
139
|
+
// Exclude properties from a DTO schema
|
|
140
|
+
excludeSchemaProperties: [
|
|
141
|
+
{ schemaName: 'CreateUserDto', properties: ['passwordHash', 'salt'] },
|
|
142
|
+
],
|
|
143
|
+
|
|
144
|
+
// Exclude query parameters from specific endpoints
|
|
145
|
+
excludeQueryParameters: [
|
|
146
|
+
{ pathPattern: '/api/auth/*', method: 'post', parameters: ['debug'] },
|
|
147
|
+
],
|
|
148
|
+
|
|
149
|
+
// Exclude named examples from responses
|
|
150
|
+
excludeExamples: [
|
|
151
|
+
{ pathPattern: '/api/auth/login', examples: ['AdminExample'] },
|
|
152
|
+
],
|
|
153
|
+
|
|
154
|
+
// Add a custom header to every request in Swagger UI
|
|
155
|
+
globalHeaders: [
|
|
156
|
+
{ name: 'x-tenant-id', description: 'Tenant identifier', required: false, example: 'tenant-1' },
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// Minimal entry
|
|
161
|
+
{ title: 'IAM API', description: 'Roles and permissions', path: 'api/docs/iam' },
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
await app.listen(3000);
|
|
165
|
+
}
|
|
185
166
|
```
|
|
186
167
|
|
|
187
|
-
## Dependencies
|
|
188
|
-
|
|
189
|
-
- `dotenv` - Environment variable loading
|
|
190
|
-
- `typeorm` - Migration/seeder utilities
|
|
191
|
-
- `@faker-js/faker` - Seed data generation
|
|
192
168
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
---
|
|
169
|
+
## License
|
|
196
170
|
|
|
197
|
-
|
|
171
|
+
MIT © FLUSYS
|
|
@@ -85,24 +85,25 @@ let EnvConfigService = class EnvConfigService {
|
|
|
85
85
|
return this.getValue(key, throwOnMissing).toLowerCase() === 'true';
|
|
86
86
|
}
|
|
87
87
|
getPort() {
|
|
88
|
-
return this.getNumber('PORT', false);
|
|
88
|
+
return this.getNumber('PORT', false) || 3000;
|
|
89
89
|
}
|
|
90
90
|
isProduction() {
|
|
91
91
|
return this.getValue('MODE', false).toUpperCase() !== 'DEV';
|
|
92
92
|
}
|
|
93
|
-
getFrontendUrl() {
|
|
94
|
-
return this.getValue('FRONTEND_URL');
|
|
95
|
-
}
|
|
96
93
|
getOrigins() {
|
|
97
94
|
const origins = this.getValue('ALLOW_ORIGINS');
|
|
98
95
|
return origins ? origins.split(',').map((o)=>o.trim()) : [];
|
|
99
96
|
}
|
|
100
97
|
getTypeOrmConfig() {
|
|
98
|
+
const type = this.getValue('DB_TYPE', false) || 'mysql';
|
|
99
|
+
const defaultPort = type === 'postgres' ? 5432 : 3306;
|
|
101
100
|
return {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
type,
|
|
102
|
+
host: this.getValue('DB_HOST', false) || 'localhost',
|
|
103
|
+
port: this.getNumber('DB_PORT', false) || defaultPort,
|
|
104
|
+
username: this.getValue('DB_USER', false) || 'root',
|
|
105
|
+
password: this.getValue('DB_PASSWORD', false) || '',
|
|
106
|
+
database: this.getValue('DB_NAME', false) || 'flusys_main'
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
getJwtConfig() {
|
|
@@ -133,7 +134,8 @@ let EnvConfigService = class EnvConfigService {
|
|
|
133
134
|
dir: this.getValue('LOG_DIR', false) || 'logs',
|
|
134
135
|
level: this.getValue('LOG_LEVEL', false) || (this.isProduction() ? 'info' : 'debug'),
|
|
135
136
|
maxSize: this.getValue('LOG_MAX_SIZE', false) || '20m',
|
|
136
|
-
maxFiles: this.getValue('LOG_MAX_FILES', false) || '14d'
|
|
137
|
+
maxFiles: this.getValue('LOG_MAX_FILES', false) || '14d',
|
|
138
|
+
disableHttpLogging: this.getValue('DISABLE_HTTP_LOGGING', false).toLowerCase() === 'true'
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
141
|
getEnv() {
|
|
@@ -148,10 +150,12 @@ let EnvConfigService = class EnvConfigService {
|
|
|
148
150
|
ALLOW_ORIGINS: 'getOrigins',
|
|
149
151
|
PORT: 'getPort',
|
|
150
152
|
MODE: 'isProduction',
|
|
153
|
+
DB_TYPE: 'getTypeOrmConfig',
|
|
151
154
|
DB_HOST: 'getTypeOrmConfig',
|
|
152
155
|
DB_PORT: 'getTypeOrmConfig',
|
|
153
156
|
DB_USER: 'getTypeOrmConfig',
|
|
154
|
-
DB_PASSWORD: 'getTypeOrmConfig'
|
|
157
|
+
DB_PASSWORD: 'getTypeOrmConfig',
|
|
158
|
+
DB_NAME: 'getTypeOrmConfig'
|
|
155
159
|
};
|
|
156
160
|
}
|
|
157
161
|
};
|
package/cjs/docs/docs.config.js
CHANGED
|
@@ -2,16 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get setupModuleSwaggerDocs () {
|
|
8
13
|
return setupModuleSwaggerDocs;
|
|
14
|
+
},
|
|
15
|
+
get setupSwaggerDocs () {
|
|
16
|
+
return setupSwaggerDocs;
|
|
9
17
|
}
|
|
10
18
|
});
|
|
11
19
|
const _swagger = require("@nestjs/swagger");
|
|
12
|
-
|
|
13
|
-
* Filter OpenAPI document to exclude schema properties
|
|
14
|
-
*/ function filterSchemaProperties(document, exclusions) {
|
|
20
|
+
function filterSchemaProperties(document, exclusions) {
|
|
15
21
|
if (!exclusions?.length || !document.components?.schemas) {
|
|
16
22
|
return document;
|
|
17
23
|
}
|
|
@@ -46,9 +52,7 @@ const _swagger = require("@nestjs/swagger");
|
|
|
46
52
|
}
|
|
47
53
|
};
|
|
48
54
|
}
|
|
49
|
-
|
|
50
|
-
* Filter OpenAPI document to exclude query parameters from specific endpoints
|
|
51
|
-
*/ function filterQueryParameters(document, exclusions) {
|
|
55
|
+
function filterQueryParameters(document, exclusions) {
|
|
52
56
|
if (!exclusions?.length || !document.paths) {
|
|
53
57
|
return document;
|
|
54
58
|
}
|
|
@@ -94,18 +98,87 @@ const _swagger = require("@nestjs/swagger");
|
|
|
94
98
|
paths: filteredPaths
|
|
95
99
|
};
|
|
96
100
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
function filterExamples(document, exclusions) {
|
|
102
|
+
if (!exclusions?.length || !document.paths) {
|
|
103
|
+
return document;
|
|
104
|
+
}
|
|
105
|
+
const filteredPaths = {};
|
|
106
|
+
for (const [path, pathItem] of Object.entries(document.paths)){
|
|
107
|
+
const filteredPathItem = {};
|
|
108
|
+
for (const [method, operation] of Object.entries(pathItem)){
|
|
109
|
+
// Skip non-operation properties
|
|
110
|
+
if (!operation || typeof operation !== 'object') {
|
|
111
|
+
filteredPathItem[method] = operation;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// Check if this endpoint matches any exclusion pattern
|
|
115
|
+
const matchingExclusions = exclusions.filter((exclusion)=>{
|
|
116
|
+
const pathMatches = pathMatchesPattern(path, exclusion.pathPattern);
|
|
117
|
+
const methodMatches = !exclusion.method || exclusion.method === method;
|
|
118
|
+
return pathMatches && methodMatches;
|
|
119
|
+
});
|
|
120
|
+
if (matchingExclusions.length > 0) {
|
|
121
|
+
// Collect all examples to exclude
|
|
122
|
+
const examplesToExclude = new Set();
|
|
123
|
+
matchingExclusions.forEach((exclusion)=>{
|
|
124
|
+
exclusion.examples.forEach((example)=>examplesToExclude.add(example));
|
|
125
|
+
});
|
|
126
|
+
// Deep clone the operation to avoid mutating original
|
|
127
|
+
const filteredOperation = JSON.parse(JSON.stringify(operation));
|
|
128
|
+
// Filter examples from responses
|
|
129
|
+
if (filteredOperation.responses) {
|
|
130
|
+
for (const [statusCode, response] of Object.entries(filteredOperation.responses)){
|
|
131
|
+
const responseObj = response;
|
|
132
|
+
if (responseObj?.content) {
|
|
133
|
+
const content = responseObj.content;
|
|
134
|
+
for (const [mediaType, mediaTypeObj] of Object.entries(content)){
|
|
135
|
+
const media = mediaTypeObj;
|
|
136
|
+
if (media?.examples && typeof media.examples === 'object') {
|
|
137
|
+
const examples = media.examples;
|
|
138
|
+
for (const exampleName of examplesToExclude){
|
|
139
|
+
delete examples[exampleName];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
filteredPathItem[method] = filteredOperation;
|
|
147
|
+
} else {
|
|
148
|
+
filteredPathItem[method] = operation;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
filteredPaths[path] = filteredPathItem;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
...document,
|
|
155
|
+
paths: filteredPaths
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function pathMatchesPattern(path, pattern) {
|
|
100
159
|
// Convert wildcard pattern to regex
|
|
101
160
|
const regexPattern = pattern.replace(/\*/g, '[^/]+') // * matches any characters except /
|
|
102
161
|
.replace(/\*\*/g, '.*'); // ** matches any characters including /
|
|
103
162
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
104
163
|
return regex.test(path);
|
|
105
164
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
165
|
+
function filterPaths(document, excludePaths) {
|
|
166
|
+
if (!excludePaths?.length || !document.paths) {
|
|
167
|
+
return document;
|
|
168
|
+
}
|
|
169
|
+
const filteredPaths = {};
|
|
170
|
+
for (const [path, pathItem] of Object.entries(document.paths)){
|
|
171
|
+
const isExcluded = excludePaths.some((pattern)=>pathMatchesPattern(path, pattern));
|
|
172
|
+
if (!isExcluded) {
|
|
173
|
+
filteredPaths[path] = pathItem;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
...document,
|
|
178
|
+
paths: filteredPaths
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function filterDocumentByTags(document, excludeTags) {
|
|
109
182
|
if (!excludeTags?.length) {
|
|
110
183
|
return document;
|
|
111
184
|
}
|
|
@@ -175,6 +248,10 @@ function setupModuleSwaggerDocs(app, configs) {
|
|
|
175
248
|
include: config.modules
|
|
176
249
|
} : {};
|
|
177
250
|
let document = _swagger.SwaggerModule.createDocument(app, builder.build(), createDocOptions);
|
|
251
|
+
// Filter out excluded paths
|
|
252
|
+
if (config.excludePaths?.length) {
|
|
253
|
+
document = filterPaths(document, config.excludePaths);
|
|
254
|
+
}
|
|
178
255
|
// Filter out excluded tags
|
|
179
256
|
if (config.excludeTags?.length) {
|
|
180
257
|
document = filterDocumentByTags(document, config.excludeTags);
|
|
@@ -187,6 +264,13 @@ function setupModuleSwaggerDocs(app, configs) {
|
|
|
187
264
|
if (config.excludeQueryParameters?.length) {
|
|
188
265
|
document = filterQueryParameters(document, config.excludeQueryParameters);
|
|
189
266
|
}
|
|
267
|
+
// Filter out excluded examples
|
|
268
|
+
if (config.excludeExamples?.length) {
|
|
269
|
+
document = filterExamples(document, config.excludeExamples);
|
|
270
|
+
}
|
|
190
271
|
_swagger.SwaggerModule.setup(config.path, app, document);
|
|
191
272
|
});
|
|
192
273
|
}
|
|
274
|
+
function setupSwaggerDocs(app, ...modules) {
|
|
275
|
+
setupModuleSwaggerDocs(app, modules);
|
|
276
|
+
}
|
package/cjs/docs/index.js
CHANGED
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
_export_star(require("./docs.config"), exports);
|
|
6
|
-
_export_star(require("./docs.setup"), exports);
|
|
7
6
|
function _export_star(from, to) {
|
|
8
7
|
Object.keys(from).forEach(function(k) {
|
|
9
8
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
package/cjs/index.js
CHANGED
|
@@ -7,7 +7,6 @@ _export_star(require("./interfaces"), exports);
|
|
|
7
7
|
_export_star(require("./constants"), exports);
|
|
8
8
|
_export_star(require("./utils"), exports);
|
|
9
9
|
_export_star(require("./migration"), exports);
|
|
10
|
-
_export_star(require("./seeders"), exports);
|
|
11
10
|
_export_star(require("./config"), exports);
|
|
12
11
|
function _export_star(from, to) {
|
|
13
12
|
Object.keys(from).forEach(function(k) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
2
|
+
* Entity Contract Interfaces
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the standard fields that entities should have.
|
|
5
|
+
* Use them as contracts when implementing new entities.
|
|
6
|
+
*/ /** Base entity with UUID, timestamps, and soft delete */ "use strict";
|
|
5
7
|
Object.defineProperty(exports, "__esModule", {
|
|
6
8
|
value: true
|
|
7
9
|
});
|
|
@@ -22,10 +22,9 @@ const _datasourceconfigbuilder = require("../utils/datasource-config.builder");
|
|
|
22
22
|
function createMigrationDataSource(options) {
|
|
23
23
|
const { config, tenantId } = options;
|
|
24
24
|
const { database, tenant, tenantConfig } = (0, _datasourceconfigbuilder.getDatabaseForTenant)(config, tenantId);
|
|
25
|
-
// Log target info
|
|
26
25
|
console.log(`\n📦 Database: ${database}${tenant ? ` [${tenant.name}]` : ' [default]'}`);
|
|
27
26
|
console.log(`📁 Migrations: migrations/${tenantId || 'default'}/`);
|
|
28
|
-
if (config.bootstrapAppConfig?.databaseMode
|
|
27
|
+
if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
|
|
29
28
|
console.log(`\n💡 Multi-tenant mode. Available tenants:`);
|
|
30
29
|
config.tenants.forEach((t)=>console.log(` - TENANT_ID=${t.id} → ${t.database}`));
|
|
31
30
|
}
|
|
@@ -40,7 +39,6 @@ function createMigrationDataSource(options) {
|
|
|
40
39
|
...config.defaultDatabaseConfig,
|
|
41
40
|
database
|
|
42
41
|
};
|
|
43
|
-
// Resolve entities based on tenant config
|
|
44
42
|
const entities = (0, _datasourceconfigbuilder.resolveEntities)(config, tenantConfig);
|
|
45
43
|
const dsOptions = (0, _datasourceconfigbuilder.buildDataSourceOptions)(dbConfig, entities, (0, _datasourceconfigbuilder.getMigrationsGlobPattern)(config.migrationsPath, tenantId), config.migrationsTableName);
|
|
46
44
|
return new _typeorm.DataSource({
|
package/cjs/migration/index.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migration Module Exports
|
|
3
|
-
*
|
|
4
|
-
* Public API for TypeORM migration management in single and multi-tenant databases.
|
|
5
|
-
*
|
|
6
|
-
* Main exports:
|
|
7
|
-
* - createMigrationDataSource: Create DataSource for migrations
|
|
8
|
-
* - initializeDataSource: Create and initialize DataSource
|
|
9
|
-
* - runMigrationCli: Programmatic CLI execution
|
|
10
|
-
*
|
|
11
|
-
* The CLI entry point is at ./cli.ts (used by npm scripts)
|
|
12
|
-
*/ // DataSource factory for migration config files
|
|
13
1
|
"use strict";
|
|
14
2
|
Object.defineProperty(exports, "__esModule", {
|
|
15
3
|
value: true
|
|
@@ -53,23 +53,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
53
53
|
}
|
|
54
54
|
return newObj;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
* Migration CLI Service
|
|
58
|
-
*
|
|
59
|
-
* Handles migration command execution for single and multi-tenant databases.
|
|
60
|
-
*
|
|
61
|
-
* Commands:
|
|
62
|
-
* - generate: Create new migration
|
|
63
|
-
* - run: Execute pending migrations
|
|
64
|
-
* - revert: Rollback last migration
|
|
65
|
-
* - status: Check migration status
|
|
66
|
-
* - run:all/revert:all/status:all: Batch operations for all tenants
|
|
67
|
-
*
|
|
68
|
-
* Configuration:
|
|
69
|
-
* - TENANT_ID: Target tenant (optional, defaults to default database)
|
|
70
|
-
* - --config=<path>: Migration config file path
|
|
71
|
-
* - --datasource=<path>: DataSource file path for TypeORM CLI
|
|
72
|
-
*/ function showHelp() {
|
|
56
|
+
function showHelp() {
|
|
73
57
|
console.log(`
|
|
74
58
|
Migration CLI
|
|
75
59
|
|