@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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { INestApplication, Logger } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { ConfigService } from '../services/config.service';
|
|
4
|
+
|
|
5
|
+
/** Signals an orchestrator uses to ask a container to stop */
|
|
6
|
+
const SHUTDOWN_SIGNALS: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A delay this long is almost certainly a typo (seconds written as milliseconds, or the other way
|
|
10
|
+
* round). Orchestrators SIGKILL after their grace period anyway — typically 10-30s — so a longer
|
|
11
|
+
* wait cannot be honored, it only guarantees the ungraceful ending it was meant to avoid.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_SHUTDOWN_DELAY_MS = 60_000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Above this, the delay is very likely longer than what actually gets to run.
|
|
17
|
+
*
|
|
18
|
+
* The cap above catches typos; it does NOT describe the real ceiling, and the gap between the two
|
|
19
|
+
* is where this silently breaks. Three separate limits sit BELOW the 60s cap:
|
|
20
|
+
* Docker/Compose `stop_grace_period` defaults to 10s, Kubernetes `terminationGracePeriodSeconds`
|
|
21
|
+
* to 30s, and this framework's own `installProcessDiagnostics()` arms a 30s watchdog that calls
|
|
22
|
+
* `exit(1)`. A delay of, say, 45s passes the cap without a word and is then force-killed mid-wait:
|
|
23
|
+
* no drain, and not one `onModuleDestroy` / `onApplicationShutdown` hook runs — precisely the
|
|
24
|
+
* ending the delay exists to prevent. Warning here means the dangerous band is no longer silent.
|
|
25
|
+
*
|
|
26
|
+
* The delay is only part of the budget: whatever `app.close()` needs to drain comes after it.
|
|
27
|
+
*/
|
|
28
|
+
const SHUTDOWN_DELAY_ADVISORY_MS = 10_000;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Install signal handling that keeps the app healthy for `IServerOptions.shutdownDelayMs`
|
|
32
|
+
* before shutting it down, then closes it cleanly.
|
|
33
|
+
*
|
|
34
|
+
* **Why this is not a NestJS lifecycle hook.** The obvious place looks like
|
|
35
|
+
* `beforeApplicationShutdown`, but Nest's `close()` runs
|
|
36
|
+
* `onModuleDestroy` → `beforeApplicationShutdown` → dispose (close the HTTP server) →
|
|
37
|
+
* `onApplicationShutdown`. A delay in that hook therefore waits AFTER every module has been
|
|
38
|
+
* destroyed while the socket is still accepting — so the instance keeps taking traffic with its
|
|
39
|
+
* services already torn down. That is worse than not waiting at all, which is why the wait has to
|
|
40
|
+
* happen before `close()` is entered, i.e. in the signal handler.
|
|
41
|
+
*
|
|
42
|
+
* **What the delay is for.** An orchestrator sends SIGTERM and deregisters the instance from the
|
|
43
|
+
* load balancer at the same moment, but deregistration propagates asynchronously — for a beat,
|
|
44
|
+
* requests still arrive. Staying fully healthy through that beat is what lets them be answered
|
|
45
|
+
* instead of dropped.
|
|
46
|
+
*
|
|
47
|
+
* Without a configured delay this is exactly `app.enableShutdownHooks()`, which is what the
|
|
48
|
+
* framework did before.
|
|
49
|
+
*
|
|
50
|
+
* @param app the Nest application to shut down
|
|
51
|
+
* @returns the same app, so it can be chained
|
|
52
|
+
*/
|
|
53
|
+
export function installGracefulShutdown<T extends INestApplication>(app: T): T {
|
|
54
|
+
const logger = new Logger('GracefulShutdown');
|
|
55
|
+
const configured = ConfigService.getFastButReadOnly<number | undefined>('shutdownDelayMs');
|
|
56
|
+
const delayMs = typeof configured === 'number' && configured > 0 ? configured : 0;
|
|
57
|
+
|
|
58
|
+
if (!delayMs) {
|
|
59
|
+
// Nest's own handling: close immediately on a signal, running every lifecycle hook.
|
|
60
|
+
app.enableShutdownHooks();
|
|
61
|
+
return app;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (delayMs > MAX_SHUTDOWN_DELAY_MS) {
|
|
65
|
+
logger.warn(
|
|
66
|
+
`shutdownDelayMs is ${delayMs}ms, which exceeds the ${MAX_SHUTDOWN_DELAY_MS}ms cap and is longer than a typical ` +
|
|
67
|
+
`orchestrator grace period — capping it, since a longer wait only ends in SIGKILL.`,
|
|
68
|
+
);
|
|
69
|
+
} else if (delayMs > SHUTDOWN_DELAY_ADVISORY_MS) {
|
|
70
|
+
logger.warn(
|
|
71
|
+
`shutdownDelayMs is ${delayMs}ms. Keep it well below your orchestrator's grace period AND leave room for the ` +
|
|
72
|
+
'drain that follows it — Docker/Compose stop_grace_period defaults to 10s, Kubernetes ' +
|
|
73
|
+
'terminationGracePeriodSeconds to 30s, and installProcessDiagnostics() force-exits after 30s. Exceeding any ' +
|
|
74
|
+
'of them means the process is killed mid-wait and no shutdown hook runs at all.',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const effectiveDelayMs = Math.min(delayMs, MAX_SHUTDOWN_DELAY_MS);
|
|
78
|
+
|
|
79
|
+
// Deliberately NOT enableShutdownHooks(): Nest would install its own listener for the same
|
|
80
|
+
// signals and close the app immediately, in parallel with the wait below.
|
|
81
|
+
let shuttingDown = false;
|
|
82
|
+
// Hoisted out of the handler so a second signal can CANCEL the pending wait. Left inside, the
|
|
83
|
+
// timer stayed armed and fired after the impatient close had already run, calling app.close()
|
|
84
|
+
// a second time and re-running every onApplicationShutdown hook against a closed app.
|
|
85
|
+
let timer: NodeJS.Timeout | undefined;
|
|
86
|
+
|
|
87
|
+
const handle = (signal: NodeJS.Signals): void => {
|
|
88
|
+
if (shuttingDown) {
|
|
89
|
+
// A second signal means someone is impatient. Stop waiting and close now.
|
|
90
|
+
logger.warn(`${signal} received again — closing immediately`);
|
|
91
|
+
if (timer) {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
timer = undefined;
|
|
94
|
+
}
|
|
95
|
+
void app.close();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
shuttingDown = true;
|
|
99
|
+
logger.log(`${signal} received — staying healthy for ${effectiveDelayMs}ms so the load balancer can deregister`);
|
|
100
|
+
|
|
101
|
+
timer = setTimeout(() => {
|
|
102
|
+
app
|
|
103
|
+
.close()
|
|
104
|
+
.then(() => logger.log('Shutdown complete'))
|
|
105
|
+
.catch((error) => logger.error(`Shutdown failed: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
106
|
+
}, effectiveDelayMs);
|
|
107
|
+
// The listening server keeps the loop alive on its own; this timer must not be what holds it.
|
|
108
|
+
timer.unref?.();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
for (const signal of SHUTDOWN_SIGNALS) {
|
|
112
|
+
process.on(signal, handle);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return app;
|
|
116
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Cached slug for the current process — package.json is read at most once.
|
|
6
|
+
*
|
|
7
|
+
* `undefined` means "not read yet"; a string (including the fallback) means "read".
|
|
8
|
+
*/
|
|
9
|
+
let cachedProjectSlug: string | undefined;
|
|
10
|
+
|
|
11
|
+
/** Used when package.json is unreadable or carries no usable name */
|
|
12
|
+
const FALLBACK_SLUG = 'nest-server';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The project's package name, reduced to a form that is safe as a key namespace.
|
|
16
|
+
*
|
|
17
|
+
* This exists so framework-managed keys in SHARED infrastructure (Redis today) are namespaced per
|
|
18
|
+
* application instead of per framework. A constant default is fine until two applications point at
|
|
19
|
+
* one Redis — a normal staging setup — at which point identically-named cron jobs, rate-limit
|
|
20
|
+
* counters and diagnostic buffers of different applications land on the same keys. That failure is
|
|
21
|
+
* silent: one app's scheduler consumes the other's jobs, and the job simply never runs where it was
|
|
22
|
+
* defined.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately NOT the display name from `getProjectAppName()` in the better-auth config: that one
|
|
25
|
+
* is Title Case with an environment suffix ("My App (Local)"), which is right for a 2FA issuer and
|
|
26
|
+
* wrong for a key prefix.
|
|
27
|
+
*
|
|
28
|
+
* @returns lower-case slug, scope stripped (`@acme/api` → `acme-api`), or `nest-server` as fallback
|
|
29
|
+
*/
|
|
30
|
+
export function getProjectSlug(): string {
|
|
31
|
+
if (cachedProjectSlug !== undefined) {
|
|
32
|
+
return cachedProjectSlug;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
cachedProjectSlug = FALLBACK_SLUG;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8'));
|
|
39
|
+
if (raw?.name && typeof raw.name === 'string') {
|
|
40
|
+
const slug = slugify(raw.name);
|
|
41
|
+
if (slug) {
|
|
42
|
+
cachedProjectSlug = slug;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
// Unreadable package.json is not worth failing a boot over — the fallback keeps the previous
|
|
47
|
+
// behavior, which is exactly what a consumer without a readable manifest had before.
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return cachedProjectSlug;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Reset the cache — tests only, so a fixture can vary the detected name */
|
|
54
|
+
export function resetProjectSlugCache(): void {
|
|
55
|
+
cachedProjectSlug = undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Reduce a package name to a key-safe slug.
|
|
60
|
+
*
|
|
61
|
+
* Colons are stripped rather than replaced because the framework uses `:` as its own key separator —
|
|
62
|
+
* a name containing one would otherwise invent a namespace level.
|
|
63
|
+
*/
|
|
64
|
+
function slugify(name: string): string {
|
|
65
|
+
return name
|
|
66
|
+
.trim()
|
|
67
|
+
.toLowerCase()
|
|
68
|
+
.replace(/^@/, '')
|
|
69
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
70
|
+
.replace(/^-+|-+$/g, '');
|
|
71
|
+
}
|
|
@@ -23,6 +23,22 @@ export interface CronJobConfig<OC extends CronOnCompleteCommand | null = null, C
|
|
|
23
23
|
*/
|
|
24
24
|
disabled?: boolean;
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Whether the job is deduplicated across replicas in multi-replica deployments.
|
|
28
|
+
*
|
|
29
|
+
* When enabled, only ONE replica executes each tick: via BullMQ job schedulers when
|
|
30
|
+
* Redis is configured, otherwise via a MongoDB lease per tick. The lease is BEST EFFORT —
|
|
31
|
+
* an unreachable lease store runs the tick everywhere rather than stopping all scheduled
|
|
32
|
+
* work, because a silent fleet-wide outage of every cron job is the worse failure.
|
|
33
|
+
*
|
|
34
|
+
* Set to `false` for jobs that must deliberately run on EVERY replica (e.g. refreshing a
|
|
35
|
+
* process-local cache), or to `true` to deduplicate via the MongoDB lease in a
|
|
36
|
+
* multi-replica deployment that runs without Redis.
|
|
37
|
+
*
|
|
38
|
+
* @default true when `redis` is configured, otherwise false (unchanged single-replica behavior)
|
|
39
|
+
*/
|
|
40
|
+
distributed?: boolean;
|
|
41
|
+
|
|
26
42
|
/**
|
|
27
43
|
* A function that will fire when the job is complete, when it is stopped.
|
|
28
44
|
*/
|