@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.
Files changed (50) hide show
  1. package/dist/core/common/interfaces/server-options.interface.d.ts +22 -0
  2. package/dist/core/modules/auth/guards/roles.guard.d.ts +12 -2
  3. package/dist/core/modules/auth/guards/roles.guard.js +192 -5
  4. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  5. package/dist/core/modules/file/core-file.controller.d.ts +1 -0
  6. package/dist/core/modules/file/core-file.controller.js +22 -0
  7. package/dist/core/modules/file/core-file.controller.js.map +1 -1
  8. package/dist/core/modules/tus/core-tus.controller.d.ts +9 -0
  9. package/dist/core/modules/tus/core-tus.controller.js +85 -0
  10. package/dist/core/modules/tus/core-tus.controller.js.map +1 -0
  11. package/dist/core/modules/tus/core-tus.service.d.ts +30 -0
  12. package/dist/core/modules/tus/core-tus.service.js +284 -0
  13. package/dist/core/modules/tus/core-tus.service.js.map +1 -0
  14. package/dist/core/modules/tus/index.d.ts +4 -0
  15. package/dist/core/modules/tus/index.js +21 -0
  16. package/dist/core/modules/tus/index.js.map +1 -0
  17. package/dist/core/modules/tus/interfaces/tus-config.interface.d.ts +10 -0
  18. package/dist/core/modules/tus/interfaces/tus-config.interface.js +59 -0
  19. package/dist/core/modules/tus/interfaces/tus-config.interface.js.map +1 -0
  20. package/dist/core/modules/tus/tus.module.d.ts +21 -0
  21. package/dist/core/modules/tus/tus.module.js +99 -0
  22. package/dist/core/modules/tus/tus.module.js.map +1 -0
  23. package/dist/core/modules/user/core-user.service.d.ts +1 -0
  24. package/dist/core/modules/user/core-user.service.js +12 -0
  25. package/dist/core/modules/user/core-user.service.js.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +1 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/server/modules/file/file.controller.d.ts +5 -7
  30. package/dist/server/modules/file/file.controller.js +3 -31
  31. package/dist/server/modules/file/file.controller.js.map +1 -1
  32. package/dist/server/server.module.js +3 -1
  33. package/dist/server/server.module.js.map +1 -1
  34. package/dist/tsconfig.build.tsbuildinfo +1 -1
  35. package/package.json +4 -1
  36. package/src/core/common/interfaces/server-options.interface.ts +154 -0
  37. package/src/core/modules/auth/guards/roles.guard.ts +298 -5
  38. package/src/core/modules/file/README.md +165 -0
  39. package/src/core/modules/file/core-file.controller.ts +27 -1
  40. package/src/core/modules/tus/INTEGRATION-CHECKLIST.md +176 -0
  41. package/src/core/modules/tus/README.md +439 -0
  42. package/src/core/modules/tus/core-tus.controller.ts +88 -0
  43. package/src/core/modules/tus/core-tus.service.ts +424 -0
  44. package/src/core/modules/tus/index.ts +5 -0
  45. package/src/core/modules/tus/interfaces/tus-config.interface.ts +107 -0
  46. package/src/core/modules/tus/tus.module.ts +187 -0
  47. package/src/core/modules/user/core-user.service.ts +27 -0
  48. package/src/index.ts +6 -0
  49. package/src/server/modules/file/file.controller.ts +14 -34
  50. 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 { CoreFileInfo } from '../../../core/modules/file/core-file-info.model';
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(private readonly fileService: FileService) {}
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],