@lenne.tech/nest-server 11.32.3 → 11.33.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/.claude/rules/architecture.md +16 -0
- package/.claude/rules/configurable-features.md +11 -3
- package/.claude/rules/testing.md +101 -1
- package/CLAUDE.md +5 -0
- package/FRAMEWORK-API.md +58 -3
- package/bin/migrate.js +13 -3
- package/dist/core/common/helpers/content-disposition.helper.d.ts +1 -0
- package/dist/core/common/helpers/content-disposition.helper.js +14 -0
- package/dist/core/common/helpers/content-disposition.helper.js.map +1 -0
- package/dist/core/common/helpers/file.helper.d.ts +21 -2
- package/dist/core/common/helpers/file.helper.js +69 -13
- package/dist/core/common/helpers/file.helper.js.map +1 -1
- package/dist/core/common/helpers/graceful-shutdown.helper.d.ts +2 -0
- package/dist/core/common/helpers/graceful-shutdown.helper.js +55 -0
- package/dist/core/common/helpers/graceful-shutdown.helper.js.map +1 -0
- package/dist/core/common/helpers/project-name.helper.d.ts +2 -0
- package/dist/core/common/helpers/project-name.helper.js +38 -0
- package/dist/core/common/helpers/project-name.helper.js.map +1 -0
- package/dist/core/common/interfaces/cron-job-config.interface.d.ts +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +39 -0
- package/dist/core/common/services/core-cron-jobs.initializer.d.ts +10 -0
- package/dist/core/common/services/core-cron-jobs.initializer.js +46 -0
- package/dist/core/common/services/core-cron-jobs.initializer.js.map +1 -0
- package/dist/core/common/services/core-cron-jobs.registry.d.ts +8 -0
- package/dist/core/common/services/core-cron-jobs.registry.js +12 -0
- package/dist/core/common/services/core-cron-jobs.registry.js.map +1 -0
- package/dist/core/common/services/core-cron-jobs.service.d.ts +37 -7
- package/dist/core/common/services/core-cron-jobs.service.js +278 -27
- package/dist/core/common/services/core-cron-jobs.service.js.map +1 -1
- package/dist/core/common/services/core-redis-pubsub.d.ts +20 -0
- package/dist/core/common/services/core-redis-pubsub.js +112 -0
- package/dist/core/common/services/core-redis-pubsub.js.map +1 -0
- package/dist/core/common/services/core-redis.service.d.ts +25 -0
- package/dist/core/common/services/core-redis.service.js +154 -0
- package/dist/core/common/services/core-redis.service.js.map +1 -0
- package/dist/core/common/services/core-s3.service.d.ts +51 -0
- package/dist/core/common/services/core-s3.service.js +261 -0
- package/dist/core/common/services/core-s3.service.js.map +1 -0
- package/dist/core/common/services/core-trust-proxy.initializer.d.ts +16 -0
- package/dist/core/common/services/core-trust-proxy.initializer.js +77 -0
- package/dist/core/common/services/core-trust-proxy.initializer.js.map +1 -0
- package/dist/core/common/services/rate-limit-store.d.ts +61 -0
- package/dist/core/common/services/rate-limit-store.js +235 -0
- package/dist/core/common/services/rate-limit-store.js.map +1 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.d.ts +19 -3
- package/dist/core/modules/ai/core-ai-mcp.controller.js +148 -18
- package/dist/core/modules/ai/core-ai-mcp.controller.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai.service.d.ts +7 -2
- package/dist/core/modules/ai/services/core-ai.service.js +24 -20
- package/dist/core/modules/ai/services/core-ai.service.js.map +1 -1
- package/dist/core/modules/auth/core-auth.module.js +4 -1
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.d.ts +1 -1
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js +3 -14
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js.map +1 -1
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.d.ts +12 -10
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js +30 -59
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.d.ts +8 -2
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js +118 -45
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.d.ts +1 -1
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js +2 -11
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.d.ts +12 -10
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js +30 -85
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +0 -1
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
- package/dist/core/modules/file/core-file.controller.d.ts +15 -3
- package/dist/core/modules/file/core-file.controller.js +90 -20
- package/dist/core/modules/file/core-file.controller.js.map +1 -1
- package/dist/core/modules/file/core-file.resolver.d.ts +4 -4
- package/dist/core/modules/file/core-file.resolver.js +23 -16
- package/dist/core/modules/file/core-file.resolver.js.map +1 -1
- package/dist/core/modules/file/core-file.service.d.ts +41 -7
- package/dist/core/modules/file/core-file.service.js +227 -7
- package/dist/core/modules/file/core-file.service.js.map +1 -1
- package/dist/core/modules/file/file-metadata.helper.d.ts +16 -0
- package/dist/core/modules/file/file-metadata.helper.js +33 -0
- package/dist/core/modules/file/file-metadata.helper.js.map +1 -0
- package/dist/core/modules/file/file-roles.helper.d.ts +4 -0
- package/dist/core/modules/file/file-roles.helper.js +53 -0
- package/dist/core/modules/file/file-roles.helper.js.map +1 -0
- package/dist/core/modules/file/file-storage.helper.d.ts +13 -0
- package/dist/core/modules/file/file-storage.helper.js +65 -0
- package/dist/core/modules/file/file-storage.helper.js.map +1 -0
- package/dist/core/modules/file/filesystem-file.helper.d.ts +22 -0
- package/dist/core/modules/file/filesystem-file.helper.js +77 -0
- package/dist/core/modules/file/filesystem-file.helper.js.map +1 -0
- package/dist/core/modules/file/interfaces/file-service-options.interface.d.ts +1 -0
- package/dist/core/modules/file/interfaces/file-upload.interface.d.ts +7 -1
- package/dist/core/modules/file/s3-file.helper.d.ts +37 -0
- package/dist/core/modules/file/s3-file.helper.js +81 -0
- package/dist/core/modules/file/s3-file.helper.js.map +1 -0
- package/dist/core/modules/hub/core-hub.controller.d.ts +8 -8
- package/dist/core/modules/hub/core-hub.controller.js +24 -22
- package/dist/core/modules/hub/core-hub.controller.js.map +1 -1
- package/dist/core/modules/hub/helpers/hub-mask.helper.js +2 -2
- package/dist/core/modules/hub/helpers/hub-mask.helper.js.map +1 -1
- package/dist/core/modules/hub/hub-buffer.d.ts +23 -0
- package/dist/core/modules/hub/hub-buffer.js +101 -0
- package/dist/core/modules/hub/hub-buffer.js.map +1 -0
- package/dist/core/modules/hub/services/core-hub-mailbox.service.d.ts +7 -5
- package/dist/core/modules/hub/services/core-hub-mailbox.service.js +14 -10
- package/dist/core/modules/hub/services/core-hub-mailbox.service.js.map +1 -1
- package/dist/core/modules/hub/services/hub-log-buffer.service.d.ts +5 -2
- package/dist/core/modules/hub/services/hub-log-buffer.service.js +17 -8
- package/dist/core/modules/hub/services/hub-log-buffer.service.js.map +1 -1
- package/dist/core/modules/hub/services/hub-query-profiler.service.d.ts +4 -2
- package/dist/core/modules/hub/services/hub-query-profiler.service.js +13 -7
- package/dist/core/modules/hub/services/hub-query-profiler.service.js.map +1 -1
- package/dist/core/modules/hub/services/hub-trace-buffer.service.d.ts +4 -2
- package/dist/core/modules/hub/services/hub-trace-buffer.service.js +13 -8
- package/dist/core/modules/hub/services/hub-trace-buffer.service.js.map +1 -1
- package/dist/core/modules/migrate/cli/migrate-cli.d.ts +3 -1
- package/dist/core/modules/migrate/cli/migrate-cli.js +29 -4
- package/dist/core/modules/migrate/cli/migrate-cli.js.map +1 -1
- package/dist/core/modules/migrate/helpers/migration.helper.d.ts +3 -6
- package/dist/core/modules/migrate/helpers/migration.helper.js +55 -8
- package/dist/core/modules/migrate/helpers/migration.helper.js.map +1 -1
- package/dist/core/modules/migrate/migration-runner.d.ts +1 -0
- package/dist/core/modules/migrate/migration-runner.js +4 -0
- package/dist/core/modules/migrate/migration-runner.js.map +1 -1
- package/dist/core/modules/migrate/mongo-state-store.d.ts +1 -0
- package/dist/core/modules/migrate/mongo-state-store.js +98 -9
- package/dist/core/modules/migrate/mongo-state-store.js.map +1 -1
- package/dist/core/modules/system-setup/core-system-setup.service.d.ts +2 -0
- package/dist/core/modules/system-setup/core-system-setup.service.js +31 -1
- package/dist/core/modules/system-setup/core-system-setup.service.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.guard.d.ts +17 -3
- package/dist/core/modules/tenant/core-tenant.guard.js +92 -15
- package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.module.js +3 -4
- package/dist/core/modules/tenant/core-tenant.module.js.map +1 -1
- package/dist/core/modules/tus/core-tus.controller.d.ts +2 -0
- package/dist/core/modules/tus/core-tus.controller.js +29 -3
- package/dist/core/modules/tus/core-tus.controller.js.map +1 -1
- package/dist/core/modules/tus/core-tus.service.d.ts +24 -1
- package/dist/core/modules/tus/core-tus.service.js +182 -28
- package/dist/core/modules/tus/core-tus.service.js.map +1 -1
- package/dist/core/modules/tus/interfaces/tus-config.interface.js +3 -0
- package/dist/core/modules/tus/interfaces/tus-config.interface.js.map +1 -1
- package/dist/core/modules/tus/tus-redis-locker.d.ts +25 -0
- package/dist/core/modules/tus/tus-redis-locker.js +87 -0
- package/dist/core/modules/tus/tus-redis-locker.js.map +1 -0
- package/dist/core/modules/tus/tus.module.d.ts +1 -0
- package/dist/core/modules/tus/tus.module.js +30 -4
- package/dist/core/modules/tus/tus.module.js.map +1 -1
- package/dist/core.module.js +18 -1
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/main.js +2 -1
- package/dist/main.js.map +1 -1
- package/dist/server/modules/file/file.controller.js.map +1 -1
- package/dist/server/modules/file/file.resolver.js +1 -12
- package/dist/server/modules/file/file.resolver.js.map +1 -1
- package/dist/server/modules/file/file.service.d.ts +5 -1
- package/dist/server/modules/file/file.service.js +12 -3
- package/dist/server/modules/file/file.service.js.map +1 -1
- package/dist/server/modules/user/avatar.controller.d.ts +5 -1
- package/dist/server/modules/user/avatar.controller.js +26 -10
- package/dist/server/modules/user/avatar.controller.js.map +1 -1
- package/dist/server/modules/user/user.model.js +3 -2
- package/dist/server/modules/user/user.model.js.map +1 -1
- package/dist/server/modules/user/user.module.js +6 -2
- package/dist/server/modules/user/user.module.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +1 -1
- package/dist/server/modules/user/user.service.js +6 -13
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +57 -8
- package/docs/security-overrides.md +30 -5
- package/migration-guides/11.32.3-to-11.32.4.md +335 -0
- package/migration-guides/11.32.x-to-11.33.x.md +1551 -0
- package/package.json +74 -42
- package/src/core/common/helpers/content-disposition.helper.ts +90 -0
- package/src/core/common/helpers/file.helper.ts +201 -20
- package/src/core/common/helpers/graceful-shutdown.helper.ts +116 -0
- package/src/core/common/helpers/project-name.helper.ts +71 -0
- package/src/core/common/interfaces/cron-job-config.interface.ts +16 -0
- package/src/core/common/interfaces/server-options.interface.ts +439 -2
- package/src/core/common/services/core-cron-jobs.initializer.ts +39 -0
- package/src/core/common/services/core-cron-jobs.registry.ts +45 -0
- package/src/core/common/services/core-cron-jobs.service.ts +589 -47
- package/src/core/common/services/core-redis-pubsub.ts +183 -0
- package/src/core/common/services/core-redis.service.ts +249 -0
- package/src/core/common/services/core-s3.service.ts +465 -0
- package/src/core/common/services/core-trust-proxy.initializer.ts +112 -0
- package/src/core/common/services/rate-limit-store.ts +492 -0
- package/src/core/modules/ai/README.md +25 -0
- package/src/core/modules/ai/core-ai-mcp.controller.ts +253 -22
- package/src/core/modules/ai/services/core-ai.service.ts +33 -24
- package/src/core/modules/auth/core-auth.module.ts +7 -1
- package/src/core/modules/auth/guards/legacy-auth-rate-limit.guard.ts +12 -21
- package/src/core/modules/auth/services/legacy-auth-rate-limiter.service.ts +49 -93
- package/src/core/modules/better-auth/core-better-auth-email-verification.service.ts +202 -66
- package/src/core/modules/better-auth/core-better-auth-rate-limit.middleware.ts +11 -18
- package/src/core/modules/better-auth/core-better-auth-rate-limiter.service.ts +51 -132
- package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +7 -1
- package/src/core/modules/file/INTEGRATION-CHECKLIST.md +108 -0
- package/src/core/modules/file/README.md +324 -41
- package/src/core/modules/file/core-file.controller.ts +267 -19
- package/src/core/modules/file/core-file.resolver.ts +46 -12
- package/src/core/modules/file/core-file.service.ts +482 -16
- package/src/core/modules/file/file-metadata.helper.ts +142 -0
- package/src/core/modules/file/file-roles.helper.ts +110 -0
- package/src/core/modules/file/file-storage.helper.ts +163 -0
- package/src/core/modules/file/filesystem-file.helper.ts +184 -0
- package/src/core/modules/file/interfaces/file-service-options.interface.ts +14 -0
- package/src/core/modules/file/interfaces/file-upload.interface.ts +17 -1
- package/src/core/modules/file/s3-file.helper.ts +209 -0
- package/src/core/modules/hub/README.md +48 -0
- package/src/core/modules/hub/core-hub.controller.ts +18 -16
- package/src/core/modules/hub/helpers/hub-mask.helper.ts +21 -4
- package/src/core/modules/hub/hub-buffer.ts +198 -0
- package/src/core/modules/hub/services/core-hub-mailbox.service.ts +22 -12
- package/src/core/modules/hub/services/hub-log-buffer.service.ts +24 -8
- package/src/core/modules/hub/services/hub-query-profiler.service.ts +9 -6
- package/src/core/modules/hub/services/hub-trace-buffer.service.ts +14 -9
- package/src/core/modules/migrate/README.md +68 -6
- package/src/core/modules/migrate/cli/migrate-cli.ts +69 -6
- package/src/core/modules/migrate/helpers/migration.helper.ts +144 -10
- package/src/core/modules/migrate/migration-runner.ts +10 -1
- package/src/core/modules/migrate/mongo-state-store.ts +179 -11
- package/src/core/modules/system-setup/README.md +7 -2
- package/src/core/modules/system-setup/core-system-setup.service.ts +95 -1
- package/src/core/modules/tenant/README.md +21 -1
- package/src/core/modules/tenant/core-tenant.guard.ts +135 -14
- package/src/core/modules/tenant/core-tenant.module.ts +8 -4
- package/src/core/modules/tus/INTEGRATION-CHECKLIST.md +93 -21
- package/src/core/modules/tus/README.md +35 -2
- package/src/core/modules/tus/core-tus.controller.ts +64 -12
- package/src/core/modules/tus/core-tus.service.ts +358 -34
- package/src/core/modules/tus/interfaces/tus-config.interface.ts +9 -0
- package/src/core/modules/tus/tus-redis-locker.ts +139 -0
- package/src/core/modules/tus/tus.module.ts +78 -6
- package/src/core.module.ts +26 -1
- package/src/index.ts +10 -0
- package/src/main.ts +7 -1
- package/src/server/modules/file/file.controller.ts +7 -3
- package/src/server/modules/file/file.resolver.ts +5 -17
- package/src/server/modules/file/file.service.ts +42 -3
- package/src/server/modules/user/avatar.controller.ts +39 -9
- package/src/server/modules/user/user.model.ts +9 -3
- package/src/server/modules/user/user.module.ts +12 -3
- package/src/server/modules/user/user.service.ts +20 -17
- package/src/test/README.md +7 -2
- package/src/core/common/helpers/logging.helper.spec.ts +0 -61
- package/src/core/common/helpers/process-diagnostics.helper.spec.ts +0 -310
- package/src/core/common/services/brevo.service.spec.ts +0 -266
- package/src/core/modules/hub/core-hub.module.spec.ts +0 -108
- package/src/core/modules/hub/helpers/hub-command-shape.helper.spec.ts +0 -48
- package/src/core/modules/hub/helpers/hub-mask.helper.spec.ts +0 -67
- package/src/core/modules/hub/helpers/hub-mermaid.helper.spec.ts +0 -61
- package/src/core/modules/hub/helpers/hub-shell.helper.spec.ts +0 -106
- package/src/core/modules/hub/hub-config.helper.spec.ts +0 -108
- package/src/core/modules/hub/hub-ring-buffer.spec.ts +0 -95
- package/src/core/modules/hub/services/core-hub-mailbox.service.spec.ts +0 -116
- package/src/core/modules/hub/services/hub-trace-buffer.service.spec.ts +0 -112
|
@@ -75,10 +75,14 @@ export class CoreTenantModule {
|
|
|
75
75
|
provide: CoreTenantService,
|
|
76
76
|
useClass: Service,
|
|
77
77
|
},
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
// Registered under its OWN token first, then aliased to APP_GUARD — the pattern
|
|
79
|
+
// CoreAuthModule uses for RolesGuard. A bare `{ provide: APP_GUARD, useClass: Guard }`
|
|
80
|
+
// makes Nest mint an internal token for the instance, so `@Optional() CoreTenantGuard`
|
|
81
|
+
// in CoreTenantService resolves to undefined and every invalidateUser()/invalidateAll()
|
|
82
|
+
// — including the cross-replica Redis broadcast — silently did nothing.
|
|
83
|
+
Guard,
|
|
84
|
+
...(Guard === CoreTenantGuard ? [] : [{ provide: CoreTenantGuard, useExisting: Guard }]),
|
|
85
|
+
{ provide: APP_GUARD, useExisting: Guard },
|
|
82
86
|
];
|
|
83
87
|
|
|
84
88
|
// When a custom model name is used, alias the default injection token to the custom model.
|
|
@@ -2,18 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
**For customizing TUS uploads in projects using `@lenne.tech/nest-server`.**
|
|
4
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.,
|
|
5
|
+
> **Note:** TUS is **enabled by default** with no configuration needed. This checklist is only for projects that need to customize behavior (e.g., widen or narrow who may upload).
|
|
6
|
+
|
|
7
|
+
> **Since 11.33.0 uploads require a signed-in user.** `tus.roles` defaults to `[RoleEnum.S_USER]`; it used to be `S_EVERYONE`. If your project accepts attachments on a public form, you must opt back in explicitly — see Step 0.
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
9
11
|
## Do You Need This Checklist?
|
|
10
12
|
|
|
11
|
-
| Scenario
|
|
12
|
-
|
|
|
13
|
-
| Use TUS with defaults (
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
13
|
+
| Scenario | Checklist Needed? |
|
|
14
|
+
| -------------------------------------------------- | ---------------------------------------------------- |
|
|
15
|
+
| Use TUS with defaults (signed-in users may upload) | No - works automatically |
|
|
16
|
+
| **Allow anonymous uploads** (public form) | **Yes - Step 0** (the default no longer allows this) |
|
|
17
|
+
| Restrict uploads to specific roles | Yes - Step 0 |
|
|
18
|
+
| Custom upload handling (notifications, etc.) | Yes - Step 2 |
|
|
19
|
+
| Hard-code a policy that config must NOT change | Yes - Step 1 |
|
|
20
|
+
| Disable TUS completely | No - just use `TusModule.forRoot({ config: false })` |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Step 0: Configure `tus.roles` (Who May Upload)
|
|
25
|
+
|
|
26
|
+
This is the normal way to set TUS permissions — no controller subclass needed.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// src/config.env.ts
|
|
30
|
+
tus: {
|
|
31
|
+
roles: [RoleEnum.S_USER], // default since 11.33.0
|
|
32
|
+
// roles: [RoleEnum.S_EVERYONE], // opt back in to anonymous uploads (public form)
|
|
33
|
+
// roles: ['editor', 'contributor'], // project-specific roles work too
|
|
34
|
+
},
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Plain role **strings**, exactly like a hand-written `@Roles()` call, so project roles work alongside
|
|
38
|
+
the `RoleEnum` system roles.
|
|
39
|
+
|
|
40
|
+
**Three properties worth knowing:**
|
|
41
|
+
|
|
42
|
+
1. **`roles: []` is rejected**, with a warning, and the default applies. An all-empty role set reads
|
|
43
|
+
to the guards as "no roles required" and returns `true` — honouring it literally would _open_ the
|
|
44
|
+
endpoints, the exact opposite of what someone writing `[]` intends. Same for a non-array or an
|
|
45
|
+
array holding a non-string.
|
|
46
|
+
|
|
47
|
+
2. **`OPTIONS` stays public regardless.** `handleTusOptions` / `handleTusOptionsWithId` keep their
|
|
48
|
+
own `@Roles(RoleEnum.S_EVERYONE)`, because that is the CORS preflight — browsers send it
|
|
49
|
+
**without credentials**, and it returns server capabilities only. Gating it would make every
|
|
50
|
+
browser upload fail before the first byte.
|
|
51
|
+
|
|
52
|
+
3. **Pair it with the file roles.** A TUS upload lands in the same store `file.downloadRoles`
|
|
53
|
+
guards (default `[RoleEnum.ADMIN]`), so with both at their defaults a signed-in user may upload
|
|
54
|
+
but cannot read the result back. Either widen `file.downloadRoles`, or write an owner into the
|
|
55
|
+
metadata at upload time and authorize per file in `CoreFileService.checkRights()`. TUS uploads
|
|
56
|
+
already carry `metadata.tusUploadId` plus the original TUS metadata.
|
|
57
|
+
|
|
58
|
+
**Verify:** `curl -X OPTIONS <baseUrl>/tus` still answers `204` with the TUS capability headers,
|
|
59
|
+
while `POST /tus` without a token answers `401`.
|
|
17
60
|
|
|
18
61
|
---
|
|
19
62
|
|
|
@@ -30,22 +73,42 @@ https://github.com/lenneTech/nest-server/tree/develop/src/server
|
|
|
30
73
|
|
|
31
74
|
---
|
|
32
75
|
|
|
33
|
-
## Step 1: Custom Controller (
|
|
76
|
+
## Step 1: Custom Controller (Custom Upload Logic)
|
|
77
|
+
|
|
78
|
+
> Only needed for custom handler behaviour. For "who may upload", use Step 0 — it needs no subclass.
|
|
34
79
|
|
|
35
80
|
**Create:** `src/server/modules/tus/tus.controller.ts`
|
|
36
81
|
|
|
37
82
|
```typescript
|
|
38
83
|
import { Controller } from '@nestjs/common';
|
|
39
|
-
import { CoreTusController
|
|
84
|
+
import { CoreTusController } from '@lenne.tech/nest-server';
|
|
40
85
|
|
|
41
86
|
@Controller('tus')
|
|
42
|
-
@Roles(RoleEnum.S_USER) // Require authenticated user
|
|
43
87
|
export class TusController extends CoreTusController {
|
|
44
|
-
//
|
|
45
|
-
// Override methods here for custom logic
|
|
88
|
+
// Inherits the handlers; `tus.roles` from config applies to them.
|
|
89
|
+
// Override methods here for custom logic.
|
|
46
90
|
}
|
|
47
91
|
```
|
|
48
92
|
|
|
93
|
+
**How `tus.roles` reaches a custom controller — and why you should not fight it with `@Roles()`:**
|
|
94
|
+
|
|
95
|
+
`TusModule.forRoot()` calls `applyRoles()` on the class it actually registers, writing the configured
|
|
96
|
+
roles onto that class **and** onto its `handleTus` / `handleTusWithId`, resolved through the
|
|
97
|
+
prototype chain. `forRoot()` runs after the controller's decorators have been evaluated, so it wins:
|
|
98
|
+
|
|
99
|
+
| You write on the subclass | Effective at runtime |
|
|
100
|
+
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
101
|
+
| Nothing | `tus.roles` (config) |
|
|
102
|
+
| A class-level `@Roles(...)` | `tus.roles` — overwritten by `applyRoles()` |
|
|
103
|
+
| A re-declared `handleTus` with its own `@Roles(...)` | `tus.roles` — overwritten too, because `applyRoles()` writes onto whichever function `prototype.handleTus` resolves to |
|
|
104
|
+
|
|
105
|
+
So `tus.roles` is the single source of truth for TUS permissions. If you need a policy that
|
|
106
|
+
configuration cannot change, either give your handler a **different method name** (`applyRoles()`
|
|
107
|
+
only targets `handleTus` / `handleTusWithId`), or register the controller outside `TusModule`.
|
|
108
|
+
|
|
109
|
+
> This differs from the file module, where `applyFileRoles()` targets the **base** class methods by
|
|
110
|
+
> name — there an override genuinely does opt out.
|
|
111
|
+
|
|
49
112
|
**Update ServerModule:**
|
|
50
113
|
|
|
51
114
|
```typescript
|
|
@@ -104,6 +167,10 @@ export class TusService extends CoreTusService {
|
|
|
104
167
|
path: '/tus',
|
|
105
168
|
maxSize: 50 * 1024 * 1024 * 1024, // 50 GB
|
|
106
169
|
expiration: { expiresIn: '24h' },
|
|
170
|
+
roles: [RoleEnum.S_USER], // since 11.33.0 (was S_EVERYONE)
|
|
171
|
+
// s3Staging: true when `s3` is configured — stages in-progress uploads in
|
|
172
|
+
// s3.stagingBucket instead of on pod-local disk, so resumable uploads survive
|
|
173
|
+
// replica restarts and need no sticky sessions. Set false to force local disk.
|
|
107
174
|
}
|
|
108
175
|
```
|
|
109
176
|
|
|
@@ -132,20 +199,25 @@ TusModule.forRoot({ config: false });
|
|
|
132
199
|
|
|
133
200
|
- [ ] `npm run build` succeeds
|
|
134
201
|
- [ ] `npm test` passes
|
|
135
|
-
- [ ] `OPTIONS /tus` returns TUS capabilities
|
|
136
|
-
- [ ]
|
|
137
|
-
- [ ]
|
|
138
|
-
- [ ] (
|
|
202
|
+
- [ ] `OPTIONS /tus` returns TUS capabilities **without a token** (the CORS preflight must stay public)
|
|
203
|
+
- [ ] `POST /tus` **without** a token answers `401` (or `204` if you deliberately set `roles: [S_EVERYONE]`)
|
|
204
|
+
- [ ] Upload via tus-js-client works for a user holding a role from `tus.roles`
|
|
205
|
+
- [ ] File appears in the configured file store after upload completion (GridFS by default; S3 or filesystem per `file.storage`)
|
|
206
|
+
- [ ] The uploaded file can also be **downloaded** by its intended audience — check `file.downloadRoles` too
|
|
139
207
|
|
|
140
208
|
---
|
|
141
209
|
|
|
142
210
|
## Common Mistakes
|
|
143
211
|
|
|
144
|
-
| Mistake
|
|
145
|
-
|
|
|
146
|
-
|
|
|
147
|
-
|
|
|
148
|
-
|
|
|
212
|
+
| Mistake | Symptom | Fix |
|
|
213
|
+
| -------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
214
|
+
| Assuming uploads are still public (pre-11.33.0) | `401` on the very first `POST /tus` from a public form | `tus: { roles: [RoleEnum.S_EVERYONE] }` — Step 0 |
|
|
215
|
+
| `tus: { roles: [] }` meaning "nobody" | Warning in the log, default `[S_USER]` applied instead | An empty role set would OPEN the routes; name the roles explicitly — Step 0 |
|
|
216
|
+
| Gating `OPTIONS` in a custom controller | Every browser upload fails before the first byte | Leave `handleTusOptions*` on `@Roles(RoleEnum.S_EVERYONE)` — the preflight sends no credentials |
|
|
217
|
+
| Expecting a subclass `@Roles()` to win over config | Roles look ignored | `applyRoles()` overwrites them — Step 1 table |
|
|
218
|
+
| Forgot to register custom controller | Custom handler logic never runs (core controller is used) | Add `controller: TusController` to `forRoot()` |
|
|
219
|
+
| Upload succeeds but the file cannot be downloaded | `403` on `GET /files/id/:id` | `file.downloadRoles` defaults to `[ADMIN]` — widen it, or authorize per file in `checkRights()` |
|
|
220
|
+
| Using wrong endpoint path | 404 on upload | Ensure client uses same path as config |
|
|
149
221
|
|
|
150
222
|
---
|
|
151
223
|
|
|
@@ -262,7 +262,31 @@ upload.start(); // Resumes from where it left off
|
|
|
262
262
|
|
|
263
263
|
### Require Authentication
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
Since 11.33.0 TUS requires a signed-in caller by default (`tus.roles`, default `[S_USER]`). The
|
|
266
|
+
previous default was `S_EVERYONE`, which let anonymous callers write into — and, with the
|
|
267
|
+
termination extension, delete from — the same GridFS bucket the download routes guard.
|
|
268
|
+
|
|
269
|
+
Set it in `config.env.ts`:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
tus: {
|
|
273
|
+
roles: [RoleEnum.S_USER];
|
|
274
|
+
} // default
|
|
275
|
+
tus: {
|
|
276
|
+
roles: [RoleEnum.S_EVERYONE];
|
|
277
|
+
} // opt back in to anonymous uploads
|
|
278
|
+
tus: {
|
|
279
|
+
roles: ['editor', 'contributor'];
|
|
280
|
+
} // project-specific roles work too
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
`roles: []` is rejected with a warning rather than honoured — an all-empty role set reads to the
|
|
284
|
+
guards as "no roles required" and would open the endpoints instead of closing them.
|
|
285
|
+
|
|
286
|
+
`OPTIONS` stays public regardless: it is the CORS preflight, which browsers send without
|
|
287
|
+
credentials, and it discloses only server capabilities.
|
|
288
|
+
|
|
289
|
+
Alternatively, create a custom controller:
|
|
266
290
|
|
|
267
291
|
```typescript
|
|
268
292
|
// src/server/modules/tus/tus.controller.ts
|
|
@@ -332,7 +356,7 @@ GET /files/:filename
|
|
|
332
356
|
|
|
333
357
|
# Via GraphQL
|
|
334
358
|
query {
|
|
335
|
-
|
|
359
|
+
getFileInfo(filename: "...") {
|
|
336
360
|
id
|
|
337
361
|
filename
|
|
338
362
|
contentType
|
|
@@ -343,6 +367,15 @@ query {
|
|
|
343
367
|
|
|
344
368
|
**Recommendation:** Use the ID-based endpoint (`/files/id/:id`) for TUS uploads as filenames may not be unique.
|
|
345
369
|
|
|
370
|
+
> **These download routes are gated.** They require `file.downloadRoles`, which defaults to
|
|
371
|
+
> `[RoleEnum.ADMIN]` — while uploading here only requires `tus.roles` (default `S_USER`). So out of
|
|
372
|
+
> the box a signed-in user can upload but cannot read their own file back.
|
|
373
|
+
>
|
|
374
|
+
> Do not fix that by widening `downloadRoles` to `S_USER`: that would let every signed-in user read
|
|
375
|
+
> _every_ file in the shared bucket. Write an owner into the file metadata at upload time and
|
|
376
|
+
> authorize per file in `CoreFileService.checkRights()` — see the File module's README, section
|
|
377
|
+
> "Access control".
|
|
378
|
+
|
|
346
379
|
### File Metadata
|
|
347
380
|
|
|
348
381
|
The following metadata is stored with each GridFS file:
|
|
@@ -1,46 +1,98 @@
|
|
|
1
|
-
import { All, Controller, Logger, Req, Res } from '@nestjs/common';
|
|
1
|
+
import { All, Controller, Logger, Options, Req, Res } from '@nestjs/common';
|
|
2
2
|
import { Request, Response } from 'express';
|
|
3
3
|
|
|
4
4
|
import { Roles } from '../../common/decorators/roles.decorator';
|
|
5
5
|
import { RoleEnum } from '../../common/enums/role.enum';
|
|
6
|
+
import { SkipTenantCheck } from '../tenant/core-tenant.decorators';
|
|
6
7
|
import { CoreTusService } from './core-tus.service';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Core TUS Controller
|
|
10
11
|
*
|
|
11
12
|
* 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
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* SECURITY: requires a signed-in caller (`S_USER`) by default. Configure via
|
|
15
|
+
* `tus: { roles: [...] }` — `TusModule.forRoot()` writes the configured roles
|
|
16
|
+
* onto these handlers at module-build time, so the decorators below are the
|
|
17
|
+
* fallback for a controller built outside that path, not the effective value.
|
|
18
|
+
*
|
|
19
|
+
* The default used to be `S_EVERYONE`, which the roles guard turns into an
|
|
20
|
+
* unconditional `return true` WITHOUT authenticating. Since a tus upload writes
|
|
21
|
+
* into the same shared GridFS bucket the file download routes read from, and
|
|
22
|
+
* the termination extension is on by default, that meant anonymous callers
|
|
23
|
+
* could fill — and delete from — a store only privileged callers may read.
|
|
24
|
+
*
|
|
25
|
+
* Projects that genuinely need anonymous uploads should say so explicitly:
|
|
15
26
|
*
|
|
16
27
|
* @example
|
|
17
28
|
* ```typescript
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* export class TusController extends CoreTusController {
|
|
21
|
-
* // Customize as needed
|
|
22
|
-
* }
|
|
29
|
+
* // config.env.ts
|
|
30
|
+
* tus: { roles: [RoleEnum.S_EVERYONE] }
|
|
23
31
|
* ```
|
|
24
32
|
*/
|
|
33
|
+
/**
|
|
34
|
+
* TENANT SCOPING: the same reasoning as `CoreFileController` — a tus upload lands in
|
|
35
|
+
* the SAME store the file routes read, and that store is reached through the native
|
|
36
|
+
* driver, so `mongooseTenantPlugin` never scopes it. Without this decorator a
|
|
37
|
+
* configured non-system `tus.roles` (e.g. `['admin']`) is resolved against
|
|
38
|
+
* `membership.role`, which would let a workspace "admin" of ANY tenant write into —
|
|
39
|
+
* and, via the termination extension, delete from — a store that has no tenant
|
|
40
|
+
* boundary at all.
|
|
41
|
+
*/
|
|
25
42
|
@Controller('tus')
|
|
26
|
-
@Roles(RoleEnum.
|
|
43
|
+
@Roles(RoleEnum.S_USER)
|
|
44
|
+
@SkipTenantCheck()
|
|
27
45
|
export class CoreTusController {
|
|
28
46
|
private readonly logger = new Logger(CoreTusController.name);
|
|
29
47
|
|
|
30
48
|
constructor(protected readonly tusService: CoreTusService) {}
|
|
31
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Answer the tus discovery / CORS preflight request.
|
|
52
|
+
*
|
|
53
|
+
* DELIBERATELY UNAUTHENTICATED, and it must stay that way: a browser sends the
|
|
54
|
+
* CORS preflight WITHOUT credentials, by specification. Gating OPTIONS behind
|
|
55
|
+
* `tus.roles` would make every browser upload fail at the preflight — the
|
|
56
|
+
* endpoint would be unreachable from a web client no matter how valid the
|
|
57
|
+
* caller's session is.
|
|
58
|
+
*
|
|
59
|
+
* Nothing is disclosed: the response carries only server capabilities
|
|
60
|
+
* (`Tus-Version`, `Tus-Extension`, `Tus-Max-Size`), never upload data or the
|
|
61
|
+
* existence of any particular upload. `roles` is not applied to this handler.
|
|
62
|
+
*
|
|
63
|
+
* Declared before the `@All()` handlers so it wins the route match.
|
|
64
|
+
*/
|
|
65
|
+
@Options()
|
|
66
|
+
@Roles(RoleEnum.S_EVERYONE)
|
|
67
|
+
async handleTusOptions(@Req() req: Request, @Res() res: Response): Promise<void> {
|
|
68
|
+
return this.handleTus(req, res);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Answer the tus discovery / CORS preflight for an upload-specific URL.
|
|
73
|
+
*
|
|
74
|
+
* Same reasoning as {@link handleTusOptions}. `@tus/server` answers OPTIONS
|
|
75
|
+
* identically regardless of the id, so this leaks no information about whether
|
|
76
|
+
* the upload exists.
|
|
77
|
+
*/
|
|
78
|
+
@Options(':id')
|
|
79
|
+
@Roles(RoleEnum.S_EVERYONE)
|
|
80
|
+
async handleTusOptionsWithId(@Req() req: Request, @Res() res: Response): Promise<void> {
|
|
81
|
+
return this.handleTus(req, res);
|
|
82
|
+
}
|
|
83
|
+
|
|
32
84
|
/**
|
|
33
85
|
* Handle all TUS protocol requests
|
|
34
86
|
*
|
|
35
87
|
* The @tus/server handles:
|
|
36
|
-
* - OPTIONS: Return server capabilities
|
|
88
|
+
* - OPTIONS: Return server capabilities (see handleTusOptions — routed separately)
|
|
37
89
|
* - POST: Create new upload
|
|
38
90
|
* - HEAD: Get upload status/offset
|
|
39
91
|
* - PATCH: Continue upload
|
|
40
92
|
* - DELETE: Terminate upload (if termination extension enabled)
|
|
41
93
|
*/
|
|
42
94
|
@All()
|
|
43
|
-
@Roles(RoleEnum.
|
|
95
|
+
@Roles(RoleEnum.S_USER)
|
|
44
96
|
async handleTus(@Req() req: Request, @Res() res: Response): Promise<void> {
|
|
45
97
|
const server = this.tusService.getServer();
|
|
46
98
|
|
|
@@ -66,7 +118,7 @@ export class CoreTusController {
|
|
|
66
118
|
* Routes like /tus/:id for HEAD, PATCH, DELETE
|
|
67
119
|
*/
|
|
68
120
|
@All(':id')
|
|
69
|
-
@Roles(RoleEnum.
|
|
121
|
+
@Roles(RoleEnum.S_USER)
|
|
70
122
|
async handleTusWithId(@Req() req: Request, @Res() res: Response): Promise<void> {
|
|
71
123
|
const server = this.tusService.getServer();
|
|
72
124
|
|