@lenne.tech/nest-server 11.7.2 → 11.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/common/interfaces/server-options.interface.d.ts +22 -0
- package/dist/core/modules/auth/guards/roles.guard.d.ts +12 -2
- package/dist/core/modules/auth/guards/roles.guard.js +192 -5
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/file/core-file.controller.d.ts +1 -0
- package/dist/core/modules/file/core-file.controller.js +22 -0
- package/dist/core/modules/file/core-file.controller.js.map +1 -1
- package/dist/core/modules/tus/core-tus.controller.d.ts +9 -0
- package/dist/core/modules/tus/core-tus.controller.js +85 -0
- package/dist/core/modules/tus/core-tus.controller.js.map +1 -0
- package/dist/core/modules/tus/core-tus.service.d.ts +30 -0
- package/dist/core/modules/tus/core-tus.service.js +284 -0
- package/dist/core/modules/tus/core-tus.service.js.map +1 -0
- package/dist/core/modules/tus/index.d.ts +4 -0
- package/dist/core/modules/tus/index.js +21 -0
- package/dist/core/modules/tus/index.js.map +1 -0
- package/dist/core/modules/tus/interfaces/tus-config.interface.d.ts +10 -0
- package/dist/core/modules/tus/interfaces/tus-config.interface.js +59 -0
- package/dist/core/modules/tus/interfaces/tus-config.interface.js.map +1 -0
- package/dist/core/modules/tus/tus.module.d.ts +21 -0
- package/dist/core/modules/tus/tus.module.js +99 -0
- package/dist/core/modules/tus/tus.module.js.map +1 -0
- package/dist/core/modules/user/core-user.service.d.ts +1 -0
- package/dist/core/modules/user/core-user.service.js +12 -0
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/file/file.controller.d.ts +5 -7
- package/dist/server/modules/file/file.controller.js +3 -31
- package/dist/server/modules/file/file.controller.js.map +1 -1
- package/dist/server/server.module.js +3 -1
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -1
- package/src/core/common/interfaces/server-options.interface.ts +154 -0
- package/src/core/modules/auth/guards/roles.guard.ts +298 -5
- package/src/core/modules/file/README.md +165 -0
- package/src/core/modules/file/core-file.controller.ts +27 -1
- package/src/core/modules/tus/INTEGRATION-CHECKLIST.md +176 -0
- package/src/core/modules/tus/README.md +439 -0
- package/src/core/modules/tus/core-tus.controller.ts +88 -0
- package/src/core/modules/tus/core-tus.service.ts +424 -0
- package/src/core/modules/tus/index.ts +5 -0
- package/src/core/modules/tus/interfaces/tus-config.interface.ts +107 -0
- package/src/core/modules/tus/tus.module.ts +187 -0
- package/src/core/modules/user/core-user.service.ts +27 -0
- package/src/index.ts +6 -0
- package/src/server/modules/file/file.controller.ts +14 -34
- package/src/server/server.module.ts +5 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { DynamicModule, Global, Logger, Module, OnModuleInit, Type } from '@nestjs/common';
|
|
2
|
+
import { getConnectionToken } from '@nestjs/mongoose';
|
|
3
|
+
import { Connection } from 'mongoose';
|
|
4
|
+
|
|
5
|
+
import { ITusConfig } from '../../common/interfaces/server-options.interface';
|
|
6
|
+
import { CoreTusController } from './core-tus.controller';
|
|
7
|
+
import { CoreTusService } from './core-tus.service';
|
|
8
|
+
import { normalizeTusConfig } from './interfaces/tus-config.interface';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Token for injecting the TUS configuration
|
|
12
|
+
*/
|
|
13
|
+
export const TUS_CONFIG = 'TUS_CONFIG';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for TusModule.forRoot()
|
|
17
|
+
*/
|
|
18
|
+
export interface TusModuleOptions {
|
|
19
|
+
/**
|
|
20
|
+
* TUS configuration.
|
|
21
|
+
* Accepts:
|
|
22
|
+
* - `true` or `undefined`: Enable with defaults (enabled by default)
|
|
23
|
+
* - `false`: Disable TUS uploads
|
|
24
|
+
* - `{ ... }`: Enable with custom configuration
|
|
25
|
+
*/
|
|
26
|
+
config?: boolean | ITusConfig;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Custom controller class to use instead of CoreTusController.
|
|
30
|
+
* The class must extend CoreTusController.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* @Controller('tus')
|
|
35
|
+
* @Roles(RoleEnum.S_USER) // Require authentication
|
|
36
|
+
* export class TusController extends CoreTusController {
|
|
37
|
+
* override async handleTus(...) {
|
|
38
|
+
* // Custom logic
|
|
39
|
+
* return super.handleTus(...);
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* TusModule.forRoot({
|
|
44
|
+
* controller: TusController,
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
controller?: Type<CoreTusController>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* TUS Module for resumable file uploads
|
|
53
|
+
*
|
|
54
|
+
* This module provides integration with the tus.io protocol via @tus/server.
|
|
55
|
+
* It is enabled by default with sensible defaults - no configuration required.
|
|
56
|
+
*
|
|
57
|
+
* Features:
|
|
58
|
+
* - Resumable uploads via tus.io protocol
|
|
59
|
+
* - Automatic migration to GridFS after upload completion
|
|
60
|
+
* - Configurable extensions (creation, termination, expiration, etc.)
|
|
61
|
+
* - Module Inheritance Pattern for customization
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Default usage - enabled with all defaults
|
|
66
|
+
* @Module({
|
|
67
|
+
* imports: [
|
|
68
|
+
* CoreModule.forRoot(envConfig),
|
|
69
|
+
* TusModule.forRoot(), // No config needed
|
|
70
|
+
* ],
|
|
71
|
+
* })
|
|
72
|
+
* export class AppModule {}
|
|
73
|
+
*
|
|
74
|
+
* // Custom configuration
|
|
75
|
+
* TusModule.forRoot({
|
|
76
|
+
* config: {
|
|
77
|
+
* maxSize: 100 * 1024 * 1024, // 100 MB
|
|
78
|
+
* path: '/uploads',
|
|
79
|
+
* },
|
|
80
|
+
* })
|
|
81
|
+
*
|
|
82
|
+
* // Disable TUS
|
|
83
|
+
* TusModule.forRoot({ config: false })
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
@Global()
|
|
87
|
+
@Module({})
|
|
88
|
+
export class TusModule implements OnModuleInit {
|
|
89
|
+
private static logger = new Logger(TusModule.name);
|
|
90
|
+
private static tusEnabled = false;
|
|
91
|
+
private static currentConfig: ITusConfig | null = null;
|
|
92
|
+
private static customController: null | Type<CoreTusController> = null;
|
|
93
|
+
|
|
94
|
+
constructor(private readonly tusService?: CoreTusService) {}
|
|
95
|
+
|
|
96
|
+
async onModuleInit(): Promise<void> {
|
|
97
|
+
if (TusModule.tusEnabled && this.tusService?.isEnabled()) {
|
|
98
|
+
TusModule.logger.log('TusModule ready');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Gets the controller class to use (custom or default)
|
|
104
|
+
*/
|
|
105
|
+
private static getControllerClass(): Type<CoreTusController> {
|
|
106
|
+
return this.customController || CoreTusController;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Creates a dynamic module for TUS uploads
|
|
111
|
+
*
|
|
112
|
+
* @param options - Configuration options (optional)
|
|
113
|
+
* @returns Dynamic module configuration
|
|
114
|
+
*/
|
|
115
|
+
static forRoot(options: TusModuleOptions = {}): DynamicModule {
|
|
116
|
+
const { config: rawConfig, controller } = options;
|
|
117
|
+
|
|
118
|
+
// Normalize config: undefined/true → enabled with defaults, false → disabled
|
|
119
|
+
const config = normalizeTusConfig(rawConfig);
|
|
120
|
+
|
|
121
|
+
// Store config for service configuration
|
|
122
|
+
this.currentConfig = config;
|
|
123
|
+
// Store custom controller if provided
|
|
124
|
+
this.customController = controller || null;
|
|
125
|
+
|
|
126
|
+
// If TUS is disabled, return minimal module
|
|
127
|
+
if (config === null) {
|
|
128
|
+
this.logger.debug('TUS uploads disabled');
|
|
129
|
+
this.tusEnabled = false;
|
|
130
|
+
return {
|
|
131
|
+
exports: [TUS_CONFIG, CoreTusService],
|
|
132
|
+
module: TusModule,
|
|
133
|
+
providers: [
|
|
134
|
+
{
|
|
135
|
+
provide: TUS_CONFIG,
|
|
136
|
+
useValue: null,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
inject: [getConnectionToken()],
|
|
140
|
+
provide: CoreTusService,
|
|
141
|
+
useFactory: (connection: Connection) => {
|
|
142
|
+
const service = new CoreTusService(connection);
|
|
143
|
+
service.configure(false);
|
|
144
|
+
return service;
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Enable TUS
|
|
152
|
+
this.tusEnabled = true;
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
controllers: [this.getControllerClass()],
|
|
156
|
+
exports: [TUS_CONFIG, CoreTusService],
|
|
157
|
+
module: TusModule,
|
|
158
|
+
providers: [
|
|
159
|
+
{
|
|
160
|
+
provide: TUS_CONFIG,
|
|
161
|
+
useValue: config,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
inject: [getConnectionToken(), TUS_CONFIG],
|
|
165
|
+
provide: CoreTusService,
|
|
166
|
+
useFactory: async (connection: Connection, tusConfig: ITusConfig) => {
|
|
167
|
+
const service = new CoreTusService(connection);
|
|
168
|
+
service.configure(tusConfig);
|
|
169
|
+
// Manually call onModuleInit since useFactory bypasses lifecycle hooks
|
|
170
|
+
await service.onModuleInit();
|
|
171
|
+
return service;
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Resets the static state of TusModule
|
|
180
|
+
* Useful for testing
|
|
181
|
+
*/
|
|
182
|
+
static reset(): void {
|
|
183
|
+
this.tusEnabled = false;
|
|
184
|
+
this.currentConfig = null;
|
|
185
|
+
this.customController = null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -91,6 +91,33 @@ export abstract class CoreUserService<
|
|
|
91
91
|
return this.process(async () => dbObject, { dbObject, serviceOptions });
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Get user by MongoDB ID or BetterAuth IAM ID
|
|
96
|
+
*
|
|
97
|
+
* This method is used by RolesGuard to resolve users from BetterAuth JWT tokens.
|
|
98
|
+
* The sub claim in BetterAuth JWTs can contain either:
|
|
99
|
+
* - The MongoDB _id of the user
|
|
100
|
+
* - The BetterAuth iamId
|
|
101
|
+
*
|
|
102
|
+
* @param idOrIamId - MongoDB _id or BetterAuth iamId
|
|
103
|
+
* @returns User object or null if not found
|
|
104
|
+
*/
|
|
105
|
+
async getByIdOrIamId(idOrIamId: string): Promise<null | TUser> {
|
|
106
|
+
try {
|
|
107
|
+
// First, try to find by MongoDB _id
|
|
108
|
+
const byId = await this.mainDbModel.findById(idOrIamId).exec();
|
|
109
|
+
if (byId) {
|
|
110
|
+
return byId as TUser;
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// Invalid ObjectId format - try iamId instead
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Try to find by iamId
|
|
117
|
+
const byIamId = await this.mainDbModel.findOne({ iamId: idOrIamId }).exec();
|
|
118
|
+
return byIamId as null | TUser;
|
|
119
|
+
}
|
|
120
|
+
|
|
94
121
|
/**
|
|
95
122
|
* Get verified state of user by token
|
|
96
123
|
*/
|
package/src/index.ts
CHANGED
|
@@ -158,6 +158,12 @@ export * from './core/modules/health-check/core-health-check.service';
|
|
|
158
158
|
|
|
159
159
|
export * from './core/modules/migrate';
|
|
160
160
|
|
|
161
|
+
// =====================================================================================================================
|
|
162
|
+
// Core - Modules - Tus
|
|
163
|
+
// =====================================================================================================================
|
|
164
|
+
|
|
165
|
+
export * from './core/modules/tus';
|
|
166
|
+
|
|
161
167
|
// =====================================================================================================================
|
|
162
168
|
// Core - Modules - User
|
|
163
169
|
// =====================================================================================================================
|
|
@@ -3,33 +3,41 @@ import {
|
|
|
3
3
|
Controller,
|
|
4
4
|
Delete,
|
|
5
5
|
Get,
|
|
6
|
-
NotFoundException,
|
|
7
6
|
Param,
|
|
8
7
|
Post,
|
|
9
|
-
Res,
|
|
10
8
|
UploadedFile,
|
|
11
9
|
UseInterceptors,
|
|
12
10
|
} from '@nestjs/common';
|
|
13
11
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
14
|
-
import { Response } from 'express';
|
|
15
12
|
import { Readable } from 'stream';
|
|
16
13
|
|
|
17
14
|
import { Roles } from '../../../core/common/decorators/roles.decorator';
|
|
18
15
|
import { RoleEnum } from '../../../core/common/enums/role.enum';
|
|
19
|
-
import {
|
|
16
|
+
import { CoreFileController } from '../../../core/modules/file/core-file.controller';
|
|
20
17
|
import { FileUpload } from '../../../core/modules/file/interfaces/file-upload.interface';
|
|
21
18
|
import { FileService } from './file.service';
|
|
22
19
|
|
|
23
20
|
/**
|
|
24
21
|
* File controller
|
|
22
|
+
*
|
|
23
|
+
* Extends CoreFileController to provide public download endpoints:
|
|
24
|
+
* - GET /files/id/:id - Download file by ID (public)
|
|
25
|
+
* - GET /files/:filename - Download file by filename (public)
|
|
26
|
+
*
|
|
27
|
+
* Adds admin-only endpoints:
|
|
28
|
+
* - POST /files/upload - Upload file (admin)
|
|
29
|
+
* - GET /files/info/:id - Get file info (admin)
|
|
30
|
+
* - DELETE /files/:id - Delete file (admin)
|
|
25
31
|
*/
|
|
26
32
|
@Controller('files')
|
|
27
33
|
@Roles(RoleEnum.ADMIN)
|
|
28
|
-
export class FileController {
|
|
34
|
+
export class FileController extends CoreFileController {
|
|
29
35
|
/**
|
|
30
36
|
* Import services
|
|
31
37
|
*/
|
|
32
|
-
constructor(
|
|
38
|
+
constructor(protected override readonly fileService: FileService) {
|
|
39
|
+
super(fileService);
|
|
40
|
+
}
|
|
33
41
|
|
|
34
42
|
/**
|
|
35
43
|
* Upload file via HTTP
|
|
@@ -54,34 +62,6 @@ export class FileController {
|
|
|
54
62
|
return await this.fileService.createFile(fileUpload);
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
/**
|
|
58
|
-
* Download file
|
|
59
|
-
*/
|
|
60
|
-
@Get(':id')
|
|
61
|
-
@Roles(RoleEnum.ADMIN)
|
|
62
|
-
async getFile(@Param('id') id: string, @Res() res: Response) {
|
|
63
|
-
if (!id) {
|
|
64
|
-
throw new BadRequestException('Missing ID');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let file: CoreFileInfo | null;
|
|
68
|
-
try {
|
|
69
|
-
file = await this.fileService.getFileInfo(id);
|
|
70
|
-
} catch (e) {
|
|
71
|
-
console.error(e);
|
|
72
|
-
file = null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (!file) {
|
|
76
|
-
throw new NotFoundException('File not found');
|
|
77
|
-
}
|
|
78
|
-
const filestream = await this.fileService.getFileStream(id);
|
|
79
|
-
res.header('Content-Type', file.contentType || 'application/octet-stream');
|
|
80
|
-
res.header('Content-Disposition', `attachment; filename=${file.filename}`);
|
|
81
|
-
res.header('Cache-Control', 'max-age=31536000');
|
|
82
|
-
return filestream.pipe(res);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
65
|
/**
|
|
86
66
|
* Get file information
|
|
87
67
|
*/
|
|
@@ -7,6 +7,7 @@ import { Any } from '../core/common/scalars/any.scalar';
|
|
|
7
7
|
import { DateScalar } from '../core/common/scalars/date.scalar';
|
|
8
8
|
import { JSON } from '../core/common/scalars/json.scalar';
|
|
9
9
|
import { CoreAuthService } from '../core/modules/auth/services/core-auth.service';
|
|
10
|
+
import { TusModule } from '../core/modules/tus';
|
|
10
11
|
import { CronJobs } from './common/services/cron-jobs.service';
|
|
11
12
|
import { AuthController } from './modules/auth/auth.controller';
|
|
12
13
|
import { AuthModule } from './modules/auth/auth.module';
|
|
@@ -25,7 +26,7 @@ import { ServerController } from './server.controller';
|
|
|
25
26
|
controllers: [ServerController, AuthController],
|
|
26
27
|
|
|
27
28
|
// Export modules for reuse in other modules
|
|
28
|
-
exports: [CoreModule, AuthModule, BetterAuthModule, FileModule],
|
|
29
|
+
exports: [CoreModule, AuthModule, BetterAuthModule, FileModule, TusModule],
|
|
29
30
|
|
|
30
31
|
// Include modules
|
|
31
32
|
imports: [
|
|
@@ -49,6 +50,9 @@ import { ServerController } from './server.controller';
|
|
|
49
50
|
|
|
50
51
|
// Include FileModule for file handling
|
|
51
52
|
FileModule,
|
|
53
|
+
|
|
54
|
+
// Include TusModule for resumable file uploads
|
|
55
|
+
TusModule.forRoot(),
|
|
52
56
|
],
|
|
53
57
|
|
|
54
58
|
providers: [Any, CronJobs, DateScalar, JSON],
|