@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,176 @@
1
+ # TUS Integration Checklist
2
+
3
+ **For customizing TUS uploads in projects using `@lenne.tech/nest-server`.**
4
+
5
+ > **Note:** TUS is **enabled by default** with no configuration needed. This checklist is only for projects that need to customize behavior (e.g., require authentication).
6
+
7
+ ---
8
+
9
+ ## Do You Need This Checklist?
10
+
11
+ | Scenario | Checklist Needed? |
12
+ |----------|-------------------|
13
+ | Use TUS with defaults (everyone can upload) | No - works automatically |
14
+ | Require authentication for uploads | Yes - Step 1 |
15
+ | Custom upload handling (notifications, etc.) | Yes - Step 2 |
16
+ | Disable TUS completely | No - just use `TusModule.forRoot({ config: false })` |
17
+
18
+ ---
19
+
20
+ ## Reference Implementation
21
+
22
+ **Local (in your node_modules):**
23
+ ```
24
+ node_modules/@lenne.tech/nest-server/src/server/server.module.ts
25
+ ```
26
+
27
+ **GitHub:**
28
+ https://github.com/lenneTech/nest-server/tree/develop/src/server
29
+
30
+ ---
31
+
32
+ ## Step 1: Custom Controller (Require Authentication)
33
+
34
+ **Create:** `src/server/modules/tus/tus.controller.ts`
35
+
36
+ ```typescript
37
+ import { Controller } from '@nestjs/common';
38
+ import { CoreTusController, Roles, RoleEnum } from '@lenne.tech/nest-server';
39
+
40
+ @Controller('tus')
41
+ @Roles(RoleEnum.S_USER) // Require authenticated user
42
+ export class TusController extends CoreTusController {
43
+ // All methods inherit the S_USER requirement
44
+ // Override methods here for custom logic
45
+ }
46
+ ```
47
+
48
+ **Update ServerModule:**
49
+
50
+ ```typescript
51
+ // src/server/server.module.ts
52
+ import { TusModule } from '@lenne.tech/nest-server';
53
+ import { TusController } from './modules/tus/tus.controller';
54
+
55
+ @Module({
56
+ imports: [
57
+ // ... other imports
58
+ TusModule.forRoot({
59
+ controller: TusController, // Use custom controller
60
+ }),
61
+ ],
62
+ })
63
+ export class ServerModule {}
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Step 2: Custom Service (Custom Upload Handling)
69
+
70
+ **Create:** `src/server/modules/tus/tus.service.ts`
71
+
72
+ ```typescript
73
+ import { Injectable } from '@nestjs/common';
74
+ import { CoreTusService } from '@lenne.tech/nest-server';
75
+ import { Upload } from '@tus/server';
76
+
77
+ @Injectable()
78
+ export class TusService extends CoreTusService {
79
+ protected override async onUploadComplete(upload: Upload): Promise<void> {
80
+ // Call parent to handle GridFS migration
81
+ await super.onUploadComplete(upload);
82
+
83
+ // Add custom logic
84
+ const metadata = upload.metadata;
85
+ console.log(`Upload complete: ${metadata.filename}`);
86
+ // await this.notificationService.sendUploadComplete(...);
87
+ }
88
+ }
89
+ ```
90
+
91
+ **Note:** To use a custom service, you'll need to create a custom TusModule that provides your service instead of CoreTusService.
92
+
93
+ ---
94
+
95
+ ## Configuration Options
96
+
97
+ ### Default Configuration (No Changes Needed)
98
+
99
+ ```typescript
100
+ // TUS works with these defaults:
101
+ {
102
+ enabled: true,
103
+ path: '/tus',
104
+ maxSize: 50 * 1024 * 1024 * 1024, // 50 GB
105
+ expiration: { expiresIn: '24h' },
106
+ }
107
+ ```
108
+
109
+ ### Custom Configuration
110
+
111
+ ```typescript
112
+ // server.module.ts
113
+ TusModule.forRoot({
114
+ config: {
115
+ maxSize: 100 * 1024 * 1024, // 100 MB
116
+ path: '/uploads',
117
+ expiration: { expiresIn: '12h' },
118
+ },
119
+ })
120
+ ```
121
+
122
+ ### Disable TUS
123
+
124
+ ```typescript
125
+ TusModule.forRoot({ config: false })
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Verification Checklist
131
+
132
+ - [ ] `npm run build` succeeds
133
+ - [ ] `npm test` passes
134
+ - [ ] `OPTIONS /tus` returns TUS capabilities
135
+ - [ ] Upload via tus-js-client works
136
+ - [ ] File appears in GridFS after upload completion
137
+ - [ ] (If customized) Authentication is required for uploads
138
+
139
+ ---
140
+
141
+ ## Common Mistakes
142
+
143
+ | Mistake | Symptom | Fix |
144
+ |---------|---------|-----|
145
+ | Forgot to register custom controller | Default S_EVERYONE permissions | Add `controller: TusController` to forRoot() |
146
+ | Custom controller missing @Roles | No authentication required | Add `@Roles(RoleEnum.S_USER)` to controller class |
147
+ | Using wrong endpoint path | 404 on upload | Ensure client uses same path as config |
148
+
149
+ ---
150
+
151
+ ## Client Configuration
152
+
153
+ ```typescript
154
+ import { Upload } from 'tus-js-client';
155
+
156
+ const upload = new Upload(file, {
157
+ endpoint: 'http://localhost:3000/tus',
158
+ headers: {
159
+ Authorization: `Bearer ${token}`, // If authentication required
160
+ },
161
+ metadata: {
162
+ filename: file.name,
163
+ filetype: file.type,
164
+ },
165
+ onSuccess: () => console.log('Upload complete!'),
166
+ });
167
+
168
+ upload.start();
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Detailed Documentation
174
+
175
+ - **README.md:** `node_modules/@lenne.tech/nest-server/src/core/modules/tus/README.md`
176
+ - **GitHub:** https://github.com/lenneTech/nest-server/blob/develop/src/core/modules/tus/README.md
@@ -0,0 +1,439 @@
1
+ # TUS Module
2
+
3
+ Integration of the [tus.io](https://tus.io) resumable upload protocol with @lenne.tech/nest-server via [@tus/server](https://github.com/tus/tus-node-server).
4
+
5
+ ## TL;DR
6
+
7
+ ```typescript
8
+ // TUS is ENABLED BY DEFAULT - no configuration needed!
9
+ // Just update to the latest @lenne.tech/nest-server version
10
+
11
+ // To customize:
12
+ TusModule.forRoot({
13
+ config: {
14
+ maxSize: 100 * 1024 * 1024, // 100 MB instead of 50 GB default
15
+ path: '/uploads', // Custom path instead of /tus
16
+ },
17
+ })
18
+
19
+ // To disable:
20
+ TusModule.forRoot({ config: false })
21
+ ```
22
+
23
+ **Quick Links:** [Integration Checklist](./INTEGRATION-CHECKLIST.md) | [Endpoints](#endpoints) | [Configuration](#configuration) | [Client Usage](#client-usage)
24
+
25
+ ---
26
+
27
+ ## Table of Contents
28
+
29
+ - [Features](#features)
30
+ - [Default Behavior](#default-behavior)
31
+ - [Endpoints](#endpoints)
32
+ - [Configuration](#configuration)
33
+ - [Client Usage](#client-usage)
34
+ - [Customization](#customization)
35
+ - [Integration with FileModule](#integration-with-filemodule)
36
+ - [Troubleshooting](#troubleshooting)
37
+
38
+ ---
39
+
40
+ ## Features
41
+
42
+ - **Resumable Uploads** - Upload large files with automatic resume on connection loss
43
+ - **Enabled by Default** - Works out of the box without configuration
44
+ - **GridFS Integration** - Completed uploads are automatically migrated to GridFS
45
+ - **Module Inheritance Pattern** - Customize permissions via controller extension
46
+ - **All TUS Extensions** - Creation, termination, expiration, checksum, concatenation
47
+
48
+ ### TUS Protocol Extensions (All Enabled by Default)
49
+
50
+ | Extension | Description |
51
+ |-----------|-------------|
52
+ | **creation** | Create new uploads via POST |
53
+ | **creation-with-upload** | Include data in creation request |
54
+ | **termination** | Delete incomplete uploads |
55
+ | **expiration** | Auto-cleanup of abandoned uploads |
56
+ | **checksum** | Verify data integrity |
57
+ | **concatenation** | Combine multiple uploads |
58
+
59
+ ---
60
+
61
+ ## Default Behavior
62
+
63
+ TUS is **enabled by default** with the following configuration:
64
+
65
+ ```typescript
66
+ {
67
+ enabled: true,
68
+ path: '/tus',
69
+ maxSize: 50 * 1024 * 1024 * 1024, // 50 GB
70
+ allowedTypes: undefined, // All types allowed
71
+ allowedHeaders: [], // Additional custom headers (TUS headers already included)
72
+ uploadDir: 'uploads/tus',
73
+ creation: true,
74
+ creationWithUpload: true,
75
+ termination: true,
76
+ expiration: { enabled: true, expiresIn: '24h' },
77
+ checksum: true,
78
+ concatenation: true,
79
+ }
80
+ ```
81
+
82
+ **No configuration required** - TUS works immediately after updating @lenne.tech/nest-server.
83
+
84
+ ---
85
+
86
+ ## Endpoints
87
+
88
+ All endpoints are handled by the TUS protocol via `@tus/server`:
89
+
90
+ | Method | Endpoint | Description |
91
+ |--------|----------|-------------|
92
+ | OPTIONS | `/tus` | Get server capabilities |
93
+ | POST | `/tus` | Create new upload |
94
+ | HEAD | `/tus/:id` | Get upload status/offset |
95
+ | PATCH | `/tus/:id` | Continue upload |
96
+ | DELETE | `/tus/:id` | Terminate upload |
97
+
98
+ ### CORS Headers
99
+
100
+ The TUS server automatically handles CORS headers for browser-based clients:
101
+
102
+ - `Tus-Resumable`
103
+ - `Tus-Version`
104
+ - `Tus-Extension`
105
+ - `Tus-Max-Size`
106
+ - `Upload-Length`
107
+ - `Upload-Offset`
108
+ - `Upload-Metadata`
109
+
110
+ ---
111
+
112
+ ## Configuration
113
+
114
+ ### Disable TUS
115
+
116
+ ```typescript
117
+ // In server.module.ts
118
+ TusModule.forRoot({ config: false })
119
+
120
+ // Or via environment config
121
+ tus: false
122
+ ```
123
+
124
+ ### Custom Configuration
125
+
126
+ ```typescript
127
+ TusModule.forRoot({
128
+ config: {
129
+ // Custom endpoint path
130
+ path: '/uploads',
131
+
132
+ // Limit file size (default: 50 GB)
133
+ maxSize: 100 * 1024 * 1024, // 100 MB
134
+
135
+ // Restrict allowed file types
136
+ allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
137
+
138
+ // Custom upload directory for temporary files
139
+ uploadDir: 'temp/uploads',
140
+
141
+ // Disable specific extensions
142
+ termination: false,
143
+ concatenation: false,
144
+
145
+ // Custom expiration
146
+ expiration: {
147
+ enabled: true,
148
+ expiresIn: '12h', // Cleanup after 12 hours
149
+ },
150
+ },
151
+ })
152
+ ```
153
+
154
+ ### Configuration Options
155
+
156
+ | Option | Type | Default | Description |
157
+ |--------|------|---------|-------------|
158
+ | `enabled` | boolean | `true` | Enable/disable TUS |
159
+ | `path` | string | `/tus` | Endpoint path |
160
+ | `maxSize` | number | 50 GB | Maximum file size in bytes |
161
+ | `allowedTypes` | string[] | undefined | Allowed MIME types (all if undefined) |
162
+ | `allowedHeaders` | string[] | `[]` | Additional custom headers (TUS headers already included) |
163
+ | `uploadDir` | string | `uploads/tus` | Temporary upload directory |
164
+ | `creation` | boolean | `true` | Enable creation extension |
165
+ | `creationWithUpload` | boolean | `true` | Enable creation-with-upload extension |
166
+ | `termination` | boolean | `true` | Enable termination extension |
167
+ | `expiration` | boolean \| object | `{ expiresIn: '24h' }` | Expiration configuration |
168
+ | `checksum` | boolean | `true` | Enable checksum extension |
169
+ | `concatenation` | boolean | `true` | Enable concatenation extension |
170
+
171
+ **Note on `allowedHeaders`:**
172
+
173
+ `@tus/server` already includes all TUS protocol headers by default:
174
+ - Authorization, Content-Type, Location, Tus-Extension, Tus-Max-Size
175
+ - Tus-Resumable, Tus-Version, Upload-Concat, Upload-Defer-Length
176
+ - Upload-Length, Upload-Metadata, Upload-Offset, X-HTTP-Method-Override
177
+ - X-Requested-With, X-Forwarded-Host, X-Forwarded-Proto, Forwarded
178
+
179
+ The `allowedHeaders` option is only for **project-specific custom headers**.
180
+
181
+ ### Expiration Configuration
182
+
183
+ ```typescript
184
+ // Boolean shorthand
185
+ expiration: true // Enabled with 24h default
186
+ expiration: false // Disabled
187
+
188
+ // Object configuration
189
+ expiration: {
190
+ enabled: true,
191
+ expiresIn: '12h', // Supports: '24h', '1d', '30m', '3600s'
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Client Usage
198
+
199
+ ### Using tus-js-client
200
+
201
+ ```typescript
202
+ import { Upload } from 'tus-js-client';
203
+
204
+ const file = document.querySelector('input[type=file]').files[0];
205
+
206
+ const upload = new Upload(file, {
207
+ endpoint: 'http://localhost:3000/tus',
208
+ retryDelays: [0, 3000, 5000, 10000, 20000],
209
+ metadata: {
210
+ filename: file.name,
211
+ filetype: file.type,
212
+ },
213
+ onError: (error) => {
214
+ console.log('Upload failed:', error);
215
+ },
216
+ onProgress: (bytesUploaded, bytesTotal) => {
217
+ const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
218
+ console.log(`${percentage}%`);
219
+ },
220
+ onSuccess: () => {
221
+ console.log('Upload complete!');
222
+ console.log('File URL:', upload.url);
223
+ },
224
+ });
225
+
226
+ // Start or resume upload
227
+ upload.start();
228
+ ```
229
+
230
+ ### With Authentication
231
+
232
+ ```typescript
233
+ const upload = new Upload(file, {
234
+ endpoint: 'http://localhost:3000/tus',
235
+ headers: {
236
+ Authorization: `Bearer ${token}`,
237
+ },
238
+ // ... other options
239
+ });
240
+ ```
241
+
242
+ ### Resume Interrupted Upload
243
+
244
+ ```typescript
245
+ // Store upload URL for resumption
246
+ localStorage.setItem('uploadUrl', upload.url);
247
+
248
+ // Later, resume with stored URL
249
+ const upload = new Upload(file, {
250
+ endpoint: 'http://localhost:3000/tus',
251
+ uploadUrl: localStorage.getItem('uploadUrl'),
252
+ // ... other options
253
+ });
254
+
255
+ upload.start(); // Resumes from where it left off
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Customization
261
+
262
+ ### Require Authentication
263
+
264
+ By default, TUS allows everyone (`S_EVERYONE`) to upload. To require authentication, create a custom controller:
265
+
266
+ ```typescript
267
+ // src/server/modules/tus/tus.controller.ts
268
+ import { Controller } from '@nestjs/common';
269
+ import { CoreTusController, Roles, RoleEnum } from '@lenne.tech/nest-server';
270
+
271
+ @Controller('tus')
272
+ @Roles(RoleEnum.S_USER) // Require authenticated user
273
+ export class TusController extends CoreTusController {
274
+ // All methods inherit S_USER requirement
275
+ }
276
+ ```
277
+
278
+ Then register with custom controller:
279
+
280
+ ```typescript
281
+ // server.module.ts
282
+ TusModule.forRoot({
283
+ controller: TusController,
284
+ })
285
+ ```
286
+
287
+ ### Custom Upload Handler
288
+
289
+ Override `onUploadComplete` to customize what happens after upload:
290
+
291
+ ```typescript
292
+ // src/server/modules/tus/tus.service.ts
293
+ import { Injectable } from '@nestjs/common';
294
+ import { CoreTusService } from '@lenne.tech/nest-server';
295
+ import { Upload } from '@tus/server';
296
+
297
+ @Injectable()
298
+ export class TusService extends CoreTusService {
299
+ protected override async onUploadComplete(upload: Upload): Promise<void> {
300
+ // Call parent to migrate to GridFS
301
+ await super.onUploadComplete(upload);
302
+
303
+ // Custom logic after upload
304
+ const metadata = upload.metadata;
305
+ await this.notificationService.sendUploadComplete(metadata.filename);
306
+ await this.analyticsService.trackUpload(upload.id, upload.size);
307
+ }
308
+ }
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Integration with FileModule
314
+
315
+ After a TUS upload completes, the file is automatically:
316
+
317
+ 1. **Migrated to GridFS** - The temporary file is uploaded to MongoDB GridFS
318
+ 2. **Metadata preserved** - Filename, content type, and TUS metadata are stored
319
+ 3. **Temporary file deleted** - The local temporary file is removed
320
+
321
+ ### Accessing Uploaded Files
322
+
323
+ Use the existing FileModule to access uploaded files:
324
+
325
+ ```bash
326
+ # Via REST - by ID (recommended for TUS uploads)
327
+ GET /files/id/:id
328
+
329
+ # Via REST - by filename
330
+ GET /files/:filename
331
+
332
+ # Via GraphQL
333
+ query {
334
+ file(id: "...") {
335
+ id
336
+ filename
337
+ contentType
338
+ length
339
+ }
340
+ }
341
+ ```
342
+
343
+ **Recommendation:** Use the ID-based endpoint (`/files/id/:id`) for TUS uploads as filenames may not be unique.
344
+
345
+ ### File Metadata
346
+
347
+ The following metadata is stored with each GridFS file:
348
+
349
+ | Field | Source |
350
+ |-------|--------|
351
+ | `filename` | From TUS `Upload-Metadata` header |
352
+ | `contentType` | From TUS `filetype` metadata |
353
+ | `tusUploadId` | Original TUS upload ID |
354
+ | `originalMetadata` | All TUS metadata |
355
+ | `uploadedAt` | Completion timestamp |
356
+
357
+ ---
358
+
359
+ ## Troubleshooting
360
+
361
+ ### Upload returns 503 "TUS uploads not available"
362
+
363
+ **Cause:** TUS server not initialized
364
+
365
+ **Solutions:**
366
+ 1. Check if TUS is disabled in config (`tus: false`)
367
+ 2. Verify MongoDB connection is established
368
+ 3. Check server logs for initialization errors
369
+
370
+ ### Upload stalls or fails to resume
371
+
372
+ **Cause:** Upload expired or server restarted
373
+
374
+ **Solutions:**
375
+ 1. Check expiration configuration (default: 24h)
376
+ 2. Increase `expiration.expiresIn` if needed
377
+ 3. Client should handle `onError` and create new upload
378
+
379
+ ### CORS errors in browser
380
+
381
+ **Cause:** Missing or incorrect CORS configuration
382
+
383
+ **Solutions:**
384
+ 1. Verify client sends correct headers
385
+ 2. Check that `Tus-Resumable` header is included
386
+ 3. Ensure server CORS allows TUS headers
387
+
388
+ ### File not appearing in GridFS after upload
389
+
390
+ **Cause:** Upload incomplete or migration failed
391
+
392
+ **Solutions:**
393
+ 1. Verify upload completed (check `onSuccess` callback)
394
+ 2. Check server logs for migration errors
395
+ 3. Verify MongoDB GridFS bucket exists (`fs.files`, `fs.chunks`)
396
+
397
+ ### Large uploads failing
398
+
399
+ **Cause:** File exceeds `maxSize` limit
400
+
401
+ **Solutions:**
402
+ 1. Increase `maxSize` in configuration
403
+ 2. Check for proxy/nginx upload limits
404
+ 3. Verify client `chunkSize` is reasonable
405
+
406
+ ---
407
+
408
+ ## Technical Details
409
+
410
+ ### Dependencies
411
+
412
+ - `@tus/server` ^2.3.0 - TUS protocol server implementation
413
+ - `@tus/file-store` ^2.0.0 - File system storage for uploads
414
+
415
+ ### Upload Flow
416
+
417
+ ```
418
+ 1. Client: POST /tus (create upload)
419
+ 2. Server: Returns Upload-Location header
420
+ 3. Client: PATCH /tus/:id (send chunks)
421
+ 4. Server: Returns Upload-Offset
422
+ 5. Client: Repeat PATCH until complete
423
+ 6. Server: Migrate to GridFS, cleanup temp file
424
+ ```
425
+
426
+ ### File Storage
427
+
428
+ - **During upload:** Files stored in `uploadDir` (default: `uploads/tus`)
429
+ - **After completion:** Files migrated to MongoDB GridFS
430
+ - **Expiration:** Incomplete uploads cleaned up after `expiresIn` (default: 24h)
431
+
432
+ ---
433
+
434
+ ## Related Documentation
435
+
436
+ - [tus.io Protocol](https://tus.io/protocols/resumable-upload)
437
+ - [tus-js-client](https://github.com/tus/tus-js-client)
438
+ - [@tus/server](https://github.com/tus/tus-node-server)
439
+ - [FileModule Documentation](../file/README.md)
@@ -0,0 +1,88 @@
1
+ import { All, Controller, Logger, Req, Res } from '@nestjs/common';
2
+ import { Request, Response } from 'express';
3
+
4
+ import { Roles } from '../../common/decorators/roles.decorator';
5
+ import { RoleEnum } from '../../common/enums/role.enum';
6
+ import { CoreTusService } from './core-tus.service';
7
+
8
+ /**
9
+ * Core TUS Controller
10
+ *
11
+ * Handles all TUS protocol requests and delegates to the @tus/server handler.
12
+ * This controller uses S_EVERYONE by default, allowing all users to upload.
13
+ *
14
+ * Projects can extend this controller to add authentication/authorization:
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * @Controller('tus')
19
+ * @Roles(RoleEnum.S_USER) // Require authentication
20
+ * export class TusController extends CoreTusController {
21
+ * // Customize as needed
22
+ * }
23
+ * ```
24
+ */
25
+ @Controller('tus')
26
+ @Roles(RoleEnum.S_EVERYONE)
27
+ export class CoreTusController {
28
+ private readonly logger = new Logger(CoreTusController.name);
29
+
30
+ constructor(protected readonly tusService: CoreTusService) {}
31
+
32
+ /**
33
+ * Handle all TUS protocol requests
34
+ *
35
+ * The @tus/server handles:
36
+ * - OPTIONS: Return server capabilities
37
+ * - POST: Create new upload
38
+ * - HEAD: Get upload status/offset
39
+ * - PATCH: Continue upload
40
+ * - DELETE: Terminate upload (if termination extension enabled)
41
+ */
42
+ @All()
43
+ @Roles(RoleEnum.S_EVERYONE)
44
+ async handleTus(@Req() req: Request, @Res() res: Response): Promise<void> {
45
+ const server = this.tusService.getServer();
46
+
47
+ if (!server) {
48
+ this.logger.warn('TUS server not initialized');
49
+ res.status(503).json({ message: 'TUS uploads not available' });
50
+ return;
51
+ }
52
+
53
+ try {
54
+ await server.handle(req, res);
55
+ } catch (error) {
56
+ this.logger.error(`TUS request error: ${error.message}`);
57
+ if (!res.headersSent) {
58
+ res.status(500).json({ message: 'Upload error' });
59
+ }
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Handle requests with upload ID parameter
65
+ *
66
+ * Routes like /tus/:id for HEAD, PATCH, DELETE
67
+ */
68
+ @All(':id')
69
+ @Roles(RoleEnum.S_EVERYONE)
70
+ async handleTusWithId(@Req() req: Request, @Res() res: Response): Promise<void> {
71
+ const server = this.tusService.getServer();
72
+
73
+ if (!server) {
74
+ this.logger.warn('TUS server not initialized');
75
+ res.status(503).json({ message: 'TUS uploads not available' });
76
+ return;
77
+ }
78
+
79
+ try {
80
+ await server.handle(req, res);
81
+ } catch (error) {
82
+ this.logger.error(`TUS request error for upload ${req.params.id}: ${error.message}`);
83
+ if (!res.headersSent) {
84
+ res.status(500).json({ message: 'Upload error' });
85
+ }
86
+ }
87
+ }
88
+ }