@nest-omni/core 4.1.3-3 → 4.1.3-31
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/audit/audit.module.d.ts +1 -0
- package/audit/audit.module.js +51 -2
- package/audit/controllers/audit.controller.d.ts +57 -1
- package/audit/controllers/audit.controller.js +43 -0
- package/audit/decorators/audit-action.decorator.d.ts +74 -0
- package/audit/decorators/audit-action.decorator.js +42 -0
- package/audit/decorators/audit-controller.decorator.d.ts +1 -1
- package/audit/decorators/audit-controller.decorator.js +2 -2
- package/audit/decorators/audit-operation.decorator.d.ts +0 -7
- package/audit/decorators/audit-operation.decorator.js +0 -7
- package/audit/decorators/entity-audit.decorator.d.ts +78 -2
- package/audit/decorators/entity-audit.decorator.js +145 -4
- package/audit/decorators/index.d.ts +2 -0
- package/audit/decorators/index.js +2 -0
- package/audit/dto/audit-action-query.dto.d.ts +13 -0
- package/audit/dto/audit-action-query.dto.js +77 -0
- package/audit/dto/index.d.ts +1 -0
- package/audit/dto/index.js +1 -0
- package/audit/entities/audit-action-summary.entity.d.ts +23 -0
- package/audit/entities/audit-action-summary.entity.js +101 -0
- package/audit/entities/entity-audit-log.entity.d.ts +7 -2
- package/audit/entities/entity-audit-log.entity.js +45 -9
- package/audit/entities/entity-transaction.entity.d.ts +8 -2
- package/audit/entities/entity-transaction.entity.js +39 -3
- package/audit/entities/index.d.ts +3 -0
- package/audit/entities/index.js +3 -0
- package/audit/entities/manual-operation-log.entity.d.ts +0 -2
- package/audit/entities/manual-operation-log.entity.js +8 -9
- package/audit/enums/audit.enums.d.ts +14 -6
- package/audit/enums/audit.enums.js +18 -9
- package/audit/examples/decorator-value-mapping.example.d.ts +70 -0
- package/audit/examples/decorator-value-mapping.example.js +414 -0
- package/audit/index.d.ts +5 -1
- package/audit/index.js +29 -2
- package/audit/interceptors/audit-action.interceptor.d.ts +39 -0
- package/audit/interceptors/audit-action.interceptor.js +217 -0
- package/audit/interceptors/audit.interceptor.d.ts +1 -0
- package/audit/interceptors/audit.interceptor.js +19 -11
- package/audit/interceptors/index.d.ts +1 -0
- package/audit/interceptors/index.js +1 -0
- package/audit/interfaces/audit.interfaces.d.ts +132 -4
- package/audit/services/audit-action.service.d.ts +142 -0
- package/audit/services/audit-action.service.js +246 -0
- package/audit/services/audit-context.service.d.ts +91 -0
- package/audit/services/audit-context.service.js +170 -0
- package/audit/services/entity-audit.service.d.ts +220 -9
- package/audit/services/entity-audit.service.js +761 -72
- package/audit/services/index.d.ts +3 -0
- package/audit/services/index.js +3 -0
- package/audit/services/manual-audit-log.service.d.ts +23 -23
- package/audit/services/manual-audit-log.service.js +34 -57
- package/audit/services/multi-database.service.d.ts +0 -5
- package/audit/services/multi-database.service.js +0 -24
- package/audit/services/operation-description.service.d.ts +14 -3
- package/audit/services/operation-description.service.js +165 -26
- package/audit/services/transaction-audit.service.d.ts +1 -0
- package/audit/services/transaction-audit.service.js +12 -9
- package/audit/subscribers/entity-audit.subscriber.d.ts +5 -0
- package/audit/subscribers/entity-audit.subscriber.js +69 -5
- package/cache/cache.module.d.ts +7 -8
- package/cache/cache.module.js +15 -13
- package/cache/cache.service.d.ts +6 -4
- package/cache/cache.service.js +24 -12
- package/cache/decorators/cache-put.decorator.js +5 -4
- package/cache/dependencies/callback.dependency.js +9 -0
- package/cache/dependencies/db.dependency.d.ts +43 -12
- package/cache/dependencies/db.dependency.js +46 -18
- package/cache/dependencies/tag.dependency.d.ts +31 -4
- package/cache/dependencies/tag.dependency.js +100 -11
- package/cache/entities/index.d.ts +1 -0
- package/cache/entities/index.js +17 -0
- package/cache/entities/typeorm-cache.entity.d.ts +71 -0
- package/cache/entities/typeorm-cache.entity.js +110 -0
- package/cache/index.d.ts +2 -1
- package/cache/index.js +19 -2
- package/cache/interfaces/cache-options.interface.d.ts +8 -0
- package/cache/providers/index.d.ts +2 -1
- package/cache/providers/index.js +2 -1
- package/cache/providers/lrucache.provider.d.ts +77 -0
- package/cache/providers/lrucache.provider.js +228 -0
- package/cache/providers/redis-cache.provider.d.ts +1 -0
- package/cache/providers/redis-cache.provider.js +8 -6
- package/cache/providers/typeorm-cache.provider.d.ts +211 -0
- package/cache/providers/typeorm-cache.provider.js +483 -0
- package/common/boilerplate.polyfill.d.ts +1 -0
- package/common/boilerplate.polyfill.js +18 -1
- package/common/helpers/validation-metadata-helper.d.ts +112 -0
- package/common/helpers/validation-metadata-helper.js +164 -0
- package/common/index.d.ts +1 -0
- package/common/index.js +4 -0
- package/decorators/examples/field-i18n.example.d.ts +294 -0
- package/decorators/examples/field-i18n.example.js +478 -0
- package/decorators/field.decorators.d.ts +95 -3
- package/decorators/field.decorators.js +152 -18
- package/decorators/transform.decorators.d.ts +0 -2
- package/decorators/transform.decorators.js +0 -23
- package/decorators/translate.decorator.d.ts +26 -0
- package/decorators/translate.decorator.js +26 -1
- package/email-log/email-log.constants.d.ts +8 -0
- package/email-log/email-log.constants.js +11 -0
- package/email-log/email-log.module.d.ts +47 -0
- package/email-log/email-log.module.js +140 -0
- package/email-log/index.d.ts +11 -0
- package/email-log/index.js +48 -0
- package/email-log/interfaces/email-log-options.interface.d.ts +61 -0
- package/email-log/interfaces/email-log-options.interface.js +134 -0
- package/email-log/interfaces/email-log-transport.interface.d.ts +20 -0
- package/email-log/interfaces/email-log-transport.interface.js +2 -0
- package/email-log/interfaces/index.d.ts +2 -0
- package/email-log/interfaces/index.js +18 -0
- package/email-log/providers/email-provider.d.ts +42 -0
- package/email-log/providers/email-provider.js +127 -0
- package/email-log/providers/index.d.ts +1 -0
- package/email-log/providers/index.js +17 -0
- package/email-log/services/email-log-alert.service.d.ts +46 -0
- package/email-log/services/email-log-alert.service.js +162 -0
- package/email-log/services/email-log-formatter.service.d.ts +78 -0
- package/email-log/services/email-log-formatter.service.js +442 -0
- package/email-log/services/email-log-logger.service.d.ts +85 -0
- package/email-log/services/email-log-logger.service.js +168 -0
- package/email-log/services/email-log-rate-limiter.service.d.ts +42 -0
- package/email-log/services/email-log-rate-limiter.service.js +110 -0
- package/email-log/services/email-log-transport.service.d.ts +80 -0
- package/email-log/services/email-log-transport.service.js +271 -0
- package/email-log/services/index.d.ts +5 -0
- package/email-log/services/index.js +21 -0
- package/email-log/transports/index.d.ts +1 -0
- package/email-log/transports/index.js +17 -0
- package/email-log/transports/pino-email.transport.d.ts +56 -0
- package/email-log/transports/pino-email.transport.js +188 -0
- package/email-log/utils/index.d.ts +2 -0
- package/email-log/utils/index.js +18 -0
- package/email-log/utils/log-level.helper.d.ts +46 -0
- package/email-log/utils/log-level.helper.js +74 -0
- package/email-log/utils/pino-transport.utils.d.ts +135 -0
- package/email-log/utils/pino-transport.utils.js +238 -0
- package/file-upload/controllers/file-access.controller.d.ts +23 -0
- package/file-upload/controllers/file-access.controller.js +128 -0
- package/file-upload/decorators/column.decorator.d.ts +151 -0
- package/file-upload/decorators/column.decorator.js +273 -0
- package/file-upload/decorators/csv-data.decorator.d.ts +30 -0
- package/file-upload/decorators/csv-data.decorator.js +85 -0
- package/file-upload/decorators/csv-import.decorator.d.ts +34 -0
- package/file-upload/decorators/csv-import.decorator.js +24 -0
- package/file-upload/decorators/examples/column-mapping.example.d.ts +76 -0
- package/file-upload/decorators/examples/column-mapping.example.js +122 -0
- package/file-upload/decorators/excel-data.decorator.d.ts +30 -0
- package/file-upload/decorators/excel-data.decorator.js +85 -0
- package/file-upload/decorators/file-upload.decorator.d.ts +83 -0
- package/file-upload/decorators/file-upload.decorator.js +172 -0
- package/file-upload/decorators/index.d.ts +5 -0
- package/file-upload/decorators/index.js +38 -0
- package/file-upload/decorators/process.decorator.d.ts +40 -0
- package/file-upload/decorators/process.decorator.js +52 -0
- package/file-upload/decorators/validate-data.decorator.d.ts +91 -0
- package/file-upload/decorators/validate-data.decorator.js +39 -0
- package/file-upload/dto/create-file.dto.d.ts +24 -0
- package/file-upload/dto/create-file.dto.js +112 -0
- package/file-upload/dto/find-files.dto.d.ts +15 -0
- package/file-upload/dto/find-files.dto.js +76 -0
- package/file-upload/dto/index.d.ts +4 -0
- package/file-upload/dto/index.js +20 -0
- package/file-upload/dto/pagination.dto.d.ts +7 -0
- package/file-upload/dto/pagination.dto.js +39 -0
- package/file-upload/dto/update-file.dto.d.ts +15 -0
- package/file-upload/dto/update-file.dto.js +67 -0
- package/file-upload/entities/file-metadata.entity.d.ts +25 -0
- package/file-upload/entities/file-metadata.entity.js +76 -0
- package/file-upload/entities/file.entity.d.ts +114 -0
- package/file-upload/entities/file.entity.js +350 -0
- package/file-upload/entities/index.d.ts +2 -0
- package/file-upload/entities/index.js +18 -0
- package/file-upload/enums/file-type.enum.d.ts +72 -0
- package/file-upload/enums/file-type.enum.js +212 -0
- package/file-upload/exceptions/file-upload.exception.d.ts +57 -0
- package/file-upload/exceptions/file-upload.exception.js +120 -0
- package/file-upload/exceptions/index.d.ts +1 -0
- package/file-upload/exceptions/index.js +17 -0
- package/file-upload/file-upload.module.d.ts +89 -0
- package/file-upload/file-upload.module.js +292 -0
- package/file-upload/index.d.ts +37 -0
- package/file-upload/index.js +77 -0
- package/file-upload/interceptors/file-upload.interceptor.d.ts +101 -0
- package/file-upload/interceptors/file-upload.interceptor.js +594 -0
- package/file-upload/interceptors/index.d.ts +1 -0
- package/file-upload/interceptors/index.js +17 -0
- package/file-upload/interfaces/custom-file-type.interface.d.ts +72 -0
- package/file-upload/interfaces/custom-file-type.interface.js +2 -0
- package/file-upload/interfaces/file-buffer.interface.d.ts +72 -0
- package/file-upload/interfaces/file-buffer.interface.js +2 -0
- package/file-upload/interfaces/file-entity.interface.d.ts +142 -0
- package/file-upload/interfaces/file-entity.interface.js +28 -0
- package/file-upload/interfaces/file-metadata.interface.d.ts +21 -0
- package/file-upload/interfaces/file-metadata.interface.js +2 -0
- package/file-upload/interfaces/file-processor.interface.d.ts +93 -0
- package/file-upload/interfaces/file-processor.interface.js +2 -0
- package/file-upload/interfaces/file-upload-options.interface.d.ts +74 -0
- package/file-upload/interfaces/file-upload-options.interface.js +5 -0
- package/file-upload/interfaces/index.d.ts +7 -0
- package/file-upload/interfaces/index.js +24 -0
- package/file-upload/interfaces/processor-options.interface.d.ts +102 -0
- package/file-upload/interfaces/processor-options.interface.js +2 -0
- package/file-upload/interfaces/storage-provider.interface.d.ts +239 -0
- package/file-upload/interfaces/storage-provider.interface.js +2 -0
- package/file-upload/interfaces/upload-options.interface.d.ts +19 -0
- package/file-upload/interfaces/upload-options.interface.js +2 -0
- package/file-upload/processors/csv.processor.d.ts +98 -0
- package/file-upload/processors/csv.processor.js +391 -0
- package/file-upload/processors/excel.processor.d.ts +130 -0
- package/file-upload/processors/excel.processor.js +547 -0
- package/file-upload/processors/image.processor.d.ts +199 -0
- package/file-upload/processors/image.processor.js +377 -0
- package/file-upload/providers/index.d.ts +2 -0
- package/file-upload/providers/index.js +18 -0
- package/file-upload/providers/local-storage.provider.d.ts +98 -0
- package/file-upload/providers/local-storage.provider.js +484 -0
- package/file-upload/providers/s3-storage.provider.d.ts +87 -0
- package/file-upload/providers/s3-storage.provider.js +455 -0
- package/file-upload/services/file-signature-validator.service.d.ts +118 -0
- package/file-upload/services/file-signature-validator.service.js +376 -0
- package/file-upload/services/file.service.d.ts +193 -0
- package/file-upload/services/file.service.js +638 -0
- package/file-upload/services/index.d.ts +4 -0
- package/file-upload/services/index.js +20 -0
- package/file-upload/services/malicious-file-detector.service.d.ts +300 -0
- package/file-upload/services/malicious-file-detector.service.js +1234 -0
- package/file-upload/services/mime-registry.service.d.ts +47 -0
- package/file-upload/services/mime-registry.service.js +167 -0
- package/file-upload/utils/checksum.util.d.ts +28 -0
- package/file-upload/utils/checksum.util.js +65 -0
- package/file-upload/utils/dynamic-import.util.d.ts +54 -0
- package/file-upload/utils/dynamic-import.util.js +156 -0
- package/file-upload/utils/filename.util.d.ts +59 -0
- package/file-upload/utils/filename.util.js +184 -0
- package/file-upload/utils/filepath.util.d.ts +70 -0
- package/file-upload/utils/filepath.util.js +152 -0
- package/file-upload/utils/index.d.ts +4 -0
- package/file-upload/utils/index.js +20 -0
- package/filters/bad-request.filter.d.ts +9 -0
- package/filters/bad-request.filter.js +57 -16
- package/http-client/config/http-client.config.d.ts +5 -0
- package/http-client/config/http-client.config.js +27 -14
- package/http-client/decorators/http-client.decorators.d.ts +7 -28
- package/http-client/decorators/http-client.decorators.js +124 -99
- package/http-client/entities/http-log.entity.d.ts +0 -20
- package/http-client/entities/http-log.entity.js +1 -21
- package/http-client/examples/advanced-usage.example.d.ts +4 -5
- package/http-client/examples/advanced-usage.example.js +7 -59
- package/http-client/examples/axios-config-extended.example.d.ts +17 -0
- package/http-client/examples/axios-config-extended.example.js +311 -0
- package/http-client/examples/flexible-response-example.d.ts +28 -0
- package/http-client/examples/flexible-response-example.js +120 -0
- package/http-client/examples/index.d.ts +2 -0
- package/http-client/examples/index.js +2 -0
- package/http-client/examples/proxy-from-environment.example.d.ts +133 -0
- package/http-client/examples/proxy-from-environment.example.js +409 -0
- package/http-client/examples/ssl-certificate.example.d.ts +47 -0
- package/http-client/examples/ssl-certificate.example.js +432 -0
- package/http-client/http-client.module.d.ts +43 -2
- package/http-client/http-client.module.js +150 -90
- package/http-client/index.d.ts +1 -1
- package/http-client/interfaces/api-client-config.interface.d.ts +24 -103
- package/http-client/interfaces/http-client-config.interface.d.ts +137 -62
- package/http-client/services/api-client-registry.service.d.ts +8 -21
- package/http-client/services/api-client-registry.service.js +31 -282
- package/http-client/services/circuit-breaker.service.d.ts +69 -2
- package/http-client/services/circuit-breaker.service.js +185 -7
- package/http-client/services/http-client.service.d.ts +85 -23
- package/http-client/services/http-client.service.js +512 -168
- package/http-client/services/http-log-query.service.js +0 -13
- package/http-client/services/index.d.ts +0 -1
- package/http-client/services/index.js +0 -1
- package/http-client/services/logging.service.d.ts +69 -16
- package/http-client/services/logging.service.js +290 -170
- package/http-client/utils/call-stack-extractor.util.d.ts +26 -0
- package/http-client/utils/call-stack-extractor.util.js +35 -0
- package/http-client/utils/context-extractor.util.d.ts +2 -0
- package/http-client/utils/context-extractor.util.js +17 -3
- package/http-client/utils/curl-generator.util.js +2 -5
- package/http-client/utils/index.d.ts +2 -0
- package/http-client/utils/index.js +2 -0
- package/http-client/utils/proxy-environment.util.d.ts +42 -0
- package/http-client/utils/proxy-environment.util.js +154 -0
- package/http-client/utils/retry-recorder.util.d.ts +0 -4
- package/http-client/utils/retry-recorder.util.js +2 -27
- package/http-client/utils/sanitize.util.d.ts +58 -0
- package/http-client/utils/sanitize.util.js +188 -0
- package/http-client/utils/security-validator.util.d.ts +118 -0
- package/http-client/utils/security-validator.util.js +354 -0
- package/index.d.ts +4 -1
- package/index.js +6 -1
- package/interceptors/translation-interceptor.service.d.ts +7 -0
- package/interceptors/translation-interceptor.service.js +40 -8
- package/ip-filter/constants.d.ts +21 -0
- package/ip-filter/constants.js +24 -0
- package/ip-filter/decorators/index.d.ts +1 -0
- package/ip-filter/decorators/index.js +17 -0
- package/ip-filter/decorators/ip-filter.decorator.d.ts +58 -0
- package/ip-filter/decorators/ip-filter.decorator.js +79 -0
- package/ip-filter/guards/index.d.ts +1 -0
- package/ip-filter/guards/index.js +17 -0
- package/ip-filter/guards/ip-filter.guard.d.ts +62 -0
- package/ip-filter/guards/ip-filter.guard.js +174 -0
- package/ip-filter/index.d.ts +7 -0
- package/ip-filter/index.js +23 -0
- package/ip-filter/interfaces/index.d.ts +4 -0
- package/ip-filter/interfaces/index.js +20 -0
- package/ip-filter/interfaces/ip-filter-async-options.interface.d.ts +15 -0
- package/ip-filter/interfaces/ip-filter-async-options.interface.js +2 -0
- package/ip-filter/interfaces/ip-filter-metadata.interface.d.ts +26 -0
- package/ip-filter/interfaces/ip-filter-metadata.interface.js +2 -0
- package/ip-filter/interfaces/ip-filter-options.interface.d.ts +34 -0
- package/ip-filter/interfaces/ip-filter-options.interface.js +2 -0
- package/ip-filter/interfaces/ip-rule.interface.d.ts +36 -0
- package/ip-filter/interfaces/ip-rule.interface.js +2 -0
- package/ip-filter/ip-filter.module.d.ts +55 -0
- package/ip-filter/ip-filter.module.js +105 -0
- package/ip-filter/services/index.d.ts +1 -0
- package/ip-filter/services/index.js +17 -0
- package/ip-filter/services/ip-filter.service.d.ts +92 -0
- package/ip-filter/services/ip-filter.service.js +238 -0
- package/ip-filter/utils/index.d.ts +1 -0
- package/ip-filter/utils/index.js +17 -0
- package/ip-filter/utils/ip-utils.d.ts +61 -0
- package/ip-filter/utils/ip-utils.js +162 -0
- package/package.json +34 -29
- package/providers/context.provider.d.ts +9 -0
- package/providers/context.provider.js +13 -0
- package/redis-lock/comprehensive-lock-cleanup.service.d.ts +94 -0
- package/redis-lock/comprehensive-lock-cleanup.service.js +253 -0
- package/redis-lock/index.d.ts +2 -0
- package/redis-lock/index.js +5 -1
- package/redis-lock/lock-heartbeat.service.d.ts +80 -0
- package/redis-lock/lock-heartbeat.service.js +232 -0
- package/redis-lock/redis-lock.module.d.ts +6 -0
- package/redis-lock/redis-lock.module.js +136 -77
- package/redis-lock/redis-lock.service.d.ts +31 -0
- package/redis-lock/redis-lock.service.js +124 -17
- package/setup/bootstrap.setup.d.ts +2 -1
- package/setup/bootstrap.setup.js +3 -2
- package/setup/index.d.ts +1 -0
- package/setup/index.js +1 -0
- package/setup/run-in-mode.decorator.d.ts +56 -0
- package/setup/run-in-mode.decorator.js +92 -0
- package/setup/schedule.decorator.d.ts +1 -0
- package/setup/schedule.decorator.js +28 -13
- package/setup/worker.decorator.js +10 -1
- package/shared/index.d.ts +1 -1
- package/shared/index.js +1 -1
- package/shared/{serviceRegistryModule.js → service-registry.module.js} +28 -17
- package/shared/services/api-config.service.d.ts +41 -0
- package/shared/services/api-config.service.js +175 -8
- package/shared/services/index.d.ts +0 -1
- package/shared/services/index.js +0 -1
- package/validators/custom-validate.validator.d.ts +1 -0
- package/validators/custom-validate.validator.js +1 -0
- package/validators/file-mimetype.validator.d.ts +0 -2
- package/validators/file-mimetype.validator.js +4 -6
- package/validators/is-exists.validator.d.ts +15 -6
- package/validators/is-exists.validator.js +8 -7
- package/validators/is-unique.validator.d.ts +22 -7
- package/validators/is-unique.validator.js +41 -17
- package/vault/vault-config.service.js +1 -1
- package/cache/providers/memory-cache.provider.d.ts +0 -49
- package/cache/providers/memory-cache.provider.js +0 -197
- package/http-client/services/cache.service.d.ts +0 -76
- package/http-client/services/cache.service.js +0 -333
- package/shared/services/validator.service.d.ts +0 -3
- package/shared/services/validator.service.js +0 -20
- /package/shared/{serviceRegistryModule.d.ts → service-registry.module.d.ts} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileNameUtil = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
/**
|
|
6
|
+
* 文件名处理工具类
|
|
7
|
+
* 统一管理文件名的编码、清理、截取等操作
|
|
8
|
+
*/
|
|
9
|
+
class FileNameUtil {
|
|
10
|
+
/**
|
|
11
|
+
* 解码并标准化文件名
|
|
12
|
+
* 1. Multer 默认使用 Latin1 解码,需要转换为 UTF-8
|
|
13
|
+
* 2. 移除特殊字符和控制字符
|
|
14
|
+
* 3. 截取超长文件名到指定长度
|
|
15
|
+
*
|
|
16
|
+
* @param filename 原始文件名
|
|
17
|
+
* @param maxLength 最大长度(默认 100 字符)
|
|
18
|
+
* @returns 处理后的文件名
|
|
19
|
+
*/
|
|
20
|
+
static decodeAndNormalize(filename, maxLength = 100) {
|
|
21
|
+
if (!filename) {
|
|
22
|
+
return 'unknown';
|
|
23
|
+
}
|
|
24
|
+
let decoded;
|
|
25
|
+
// 尝试从 Latin1 转换(这是 Multer 的常见情况)
|
|
26
|
+
try {
|
|
27
|
+
const buffer = Buffer.from(filename, 'latin1');
|
|
28
|
+
decoded = buffer.toString('utf8');
|
|
29
|
+
// 检查转换结果是否包含 UTF-8 替换字符(表示可能有乱码)
|
|
30
|
+
if (!decoded.includes('\uFFFD') && this.hasValidCharacters(decoded)) {
|
|
31
|
+
// 转换成功且看起来有效
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// 可能不是 Latin1 编码,保持原始字符串
|
|
35
|
+
decoded = filename;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (_a) {
|
|
39
|
+
// 转换失败,保持原始字符串
|
|
40
|
+
decoded = filename;
|
|
41
|
+
}
|
|
42
|
+
// 移除不可见控制字符(但保留换行符)
|
|
43
|
+
decoded = decoded.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F]/g, '');
|
|
44
|
+
// 移除常见乱码字符(替换符)
|
|
45
|
+
decoded = decoded.replace(/\uFFFD/g, '');
|
|
46
|
+
// 移除前后空格
|
|
47
|
+
decoded = decoded.trim();
|
|
48
|
+
// 截取长度(按字符数,不是字节数)
|
|
49
|
+
if (decoded.length > maxLength) {
|
|
50
|
+
return this.truncateWithExtension(decoded, maxLength);
|
|
51
|
+
}
|
|
52
|
+
return decoded;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 智能截取文件名(保留扩展名)
|
|
56
|
+
*
|
|
57
|
+
* @param filename 文件名
|
|
58
|
+
* @param maxLength 最大长度
|
|
59
|
+
* @returns 截取后的文件名
|
|
60
|
+
*/
|
|
61
|
+
static truncateWithExtension(filename, maxLength) {
|
|
62
|
+
// 如果文件名长度不超过限制,直接返回
|
|
63
|
+
if (filename.length <= maxLength) {
|
|
64
|
+
return filename;
|
|
65
|
+
}
|
|
66
|
+
// 保留文件扩展名
|
|
67
|
+
const extMatch = filename.match(/\.[^.]+$/);
|
|
68
|
+
const ext = extMatch ? extMatch[0] : '';
|
|
69
|
+
const nameWithoutExt = ext ? filename.slice(0, -ext.length) : filename;
|
|
70
|
+
// 如果没有扩展名,直接截断到指定长度
|
|
71
|
+
if (!ext) {
|
|
72
|
+
return filename.slice(0, maxLength);
|
|
73
|
+
}
|
|
74
|
+
// 计算可用的文件名长度
|
|
75
|
+
const availableLength = maxLength - ext.length - 3; // 预留 3 个字符用于 "..."
|
|
76
|
+
if (availableLength > 0) {
|
|
77
|
+
return nameWithoutExt.slice(0, availableLength) + '...' + ext;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// 如果扩展名本身就超过限制,直接截取
|
|
81
|
+
return filename.slice(0, maxLength);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 清理文件名(移除危险字符)
|
|
86
|
+
* 移除路径遍历、路径分隔符、控制字符等
|
|
87
|
+
*
|
|
88
|
+
* @param filename 文件名
|
|
89
|
+
* @returns 清理后的文件名
|
|
90
|
+
*/
|
|
91
|
+
static sanitize(filename) {
|
|
92
|
+
// 移除路径遍历字符
|
|
93
|
+
let sanitized = filename.replace(/\.\./g, '');
|
|
94
|
+
// 移除路径分隔符
|
|
95
|
+
sanitized = sanitized.replace(/[\/\\]/g, '');
|
|
96
|
+
// 移除控制字符
|
|
97
|
+
sanitized = sanitized.replace(/[\x00-\x1f\x80-\x9f]/g, '');
|
|
98
|
+
// 保留空格,只替换其他特殊字符
|
|
99
|
+
sanitized = sanitized.replace(/[^a-zA-Z0-9.\-_\s\u4e00-\u9fa5]/g, '_');
|
|
100
|
+
// 移除前导/尾随空格和点
|
|
101
|
+
sanitized = sanitized
|
|
102
|
+
.trim()
|
|
103
|
+
.replace(/^\.+|\.+$/g, '')
|
|
104
|
+
.replace(/^_+|_+$/g, '');
|
|
105
|
+
return sanitized;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 清理并保留原始文件名结构
|
|
109
|
+
* 先解码 UTF-8,再清理危险字符
|
|
110
|
+
*
|
|
111
|
+
* @param filename 文件名
|
|
112
|
+
* @returns 清理后的文件名
|
|
113
|
+
*/
|
|
114
|
+
static sanitizeOriginal(filename) {
|
|
115
|
+
// 1. 解码 UTF-8
|
|
116
|
+
const decoded = this.decodeAndNormalize(filename);
|
|
117
|
+
// 2. 移除路径遍历字符
|
|
118
|
+
let sanitized = decoded.replace(/\.\./g, '');
|
|
119
|
+
sanitized = sanitized.replace(/[\/\\]/g, '');
|
|
120
|
+
// 3. 移除控制字符
|
|
121
|
+
sanitized = sanitized.replace(/[\x00-\x1f\x80-\x9f]/g, '');
|
|
122
|
+
// 4. 移除前导/尾随空格和点
|
|
123
|
+
sanitized = sanitized.trim().replace(/^\.+|\.+$/g, '');
|
|
124
|
+
return sanitized;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 提取文件扩展名
|
|
128
|
+
*
|
|
129
|
+
* @param filename 文件名
|
|
130
|
+
* @returns 扩展名(包含点)或空字符串
|
|
131
|
+
*/
|
|
132
|
+
static extractExtension(filename) {
|
|
133
|
+
const lastDotIndex = filename.lastIndexOf('.');
|
|
134
|
+
// 如果没有点、点是第一个字符(隐藏文件)、或点是最后一个字符
|
|
135
|
+
if (lastDotIndex === -1 ||
|
|
136
|
+
lastDotIndex === 0 ||
|
|
137
|
+
lastDotIndex === filename.length - 1) {
|
|
138
|
+
return '';
|
|
139
|
+
}
|
|
140
|
+
return filename.substring(lastDotIndex);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 获取不包含扩展名的文件名
|
|
144
|
+
*
|
|
145
|
+
* @param filename 文件名
|
|
146
|
+
* @returns 不包含扩展名的文件名
|
|
147
|
+
*/
|
|
148
|
+
static getBaseName(filename) {
|
|
149
|
+
return path.basename(filename, this.extractExtension(filename));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 检查字符串是否包含有效的字符
|
|
153
|
+
*/
|
|
154
|
+
static hasValidCharacters(str) {
|
|
155
|
+
// 如果字符串太短,无法判断
|
|
156
|
+
if (str.length === 0)
|
|
157
|
+
return true;
|
|
158
|
+
// 计算有效字符的比例
|
|
159
|
+
let validCount = 0;
|
|
160
|
+
for (let i = 0; i < str.length; i++) {
|
|
161
|
+
const char = str[i];
|
|
162
|
+
const code = char.charCodeAt(0);
|
|
163
|
+
// ASCII 可打印字符
|
|
164
|
+
if (code >= 0x20 && code <= 0x7e) {
|
|
165
|
+
validCount++;
|
|
166
|
+
}
|
|
167
|
+
// 基本拉丁字母扩展
|
|
168
|
+
else if (code >= 0xa0 && code <= 0xff) {
|
|
169
|
+
validCount++;
|
|
170
|
+
}
|
|
171
|
+
// 中文、日文、韩文等 CJK 字符
|
|
172
|
+
else if (code >= 0x4e00 && code <= 0x9fff) {
|
|
173
|
+
validCount++;
|
|
174
|
+
}
|
|
175
|
+
// 其他 Unicode 字符
|
|
176
|
+
else if (code > 0xff) {
|
|
177
|
+
validCount++;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// 如果超过 70% 的字符是有效的,认为是有效的字符串
|
|
181
|
+
return validCount / str.length > 0.7;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.FileNameUtil = FileNameUtil;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 文件路径处理工具类
|
|
3
|
+
* 统一管理文件上传路径、相对路径转换等操作
|
|
4
|
+
*/
|
|
5
|
+
export declare class FilePathUtil {
|
|
6
|
+
/**
|
|
7
|
+
* 获取上传根目录
|
|
8
|
+
* 优先使用环境变量 FILE_UPLOAD_PATH,否则使用 process.cwd()/uploads
|
|
9
|
+
*
|
|
10
|
+
* @returns 上传根目录的绝对路径
|
|
11
|
+
*/
|
|
12
|
+
static getUploadDir(): string;
|
|
13
|
+
/**
|
|
14
|
+
* 生成日期子目录格式 (YYYY/MM/DD)
|
|
15
|
+
*
|
|
16
|
+
* @returns 日期子目录路径
|
|
17
|
+
*/
|
|
18
|
+
static getDateSubdirectory(): string;
|
|
19
|
+
/**
|
|
20
|
+
* 获取完整的上传目录(包含日期子目录)
|
|
21
|
+
*
|
|
22
|
+
* @returns 完整的上传目录路径
|
|
23
|
+
*/
|
|
24
|
+
static getFullUploadPath(): string;
|
|
25
|
+
/**
|
|
26
|
+
* 将绝对路径转换为相对路径(相对于上传根目录)
|
|
27
|
+
* 例如: /Users/.../uploads/2025/11/30/xxx.txt -> 2025/11/30/xxx.txt
|
|
28
|
+
*
|
|
29
|
+
* @param absolutePath 绝对路径
|
|
30
|
+
* @returns 相对路径
|
|
31
|
+
*/
|
|
32
|
+
static toRelativePath(absolutePath: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* 将相对路径转换为绝对路径(物理路径)
|
|
35
|
+
* 例如: 2025/11/30/xxx.txt -> /Users/.../uploads/2025/11/30/xxx.txt
|
|
36
|
+
*
|
|
37
|
+
* @param relativePath 相对路径
|
|
38
|
+
* @returns 绝对路径
|
|
39
|
+
*/
|
|
40
|
+
static toAbsolutePath(relativePath: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* 规范化路径(统一使用正斜杠)
|
|
43
|
+
*
|
|
44
|
+
* @param filePath 文件路径
|
|
45
|
+
* @returns 规范化后的路径
|
|
46
|
+
*/
|
|
47
|
+
static normalize(filePath: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* 检查路径是否为绝对路径
|
|
50
|
+
*
|
|
51
|
+
* @param filePath 文件路径
|
|
52
|
+
* @returns 是否为绝对路径
|
|
53
|
+
*/
|
|
54
|
+
static isAbsolute(filePath: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* 检查文件是否存在
|
|
57
|
+
*
|
|
58
|
+
* @param filePath 文件路径(绝对或相对)
|
|
59
|
+
* @returns 文件是否存在
|
|
60
|
+
*/
|
|
61
|
+
static exists(filePath: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* 获取文件的物理路径(用于文件系统访问)
|
|
64
|
+
*
|
|
65
|
+
* @param relativePath 相对路径
|
|
66
|
+
* @param storageProvider 存储提供商
|
|
67
|
+
* @returns 物理路径或相对路径(对象存储)
|
|
68
|
+
*/
|
|
69
|
+
static getPhysicalPath(relativePath: string, storageProvider?: string): string;
|
|
70
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FilePathUtil = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
/**
|
|
7
|
+
* 文件路径处理工具类
|
|
8
|
+
* 统一管理文件上传路径、相对路径转换等操作
|
|
9
|
+
*/
|
|
10
|
+
class FilePathUtil {
|
|
11
|
+
/**
|
|
12
|
+
* 获取上传根目录
|
|
13
|
+
* 优先使用环境变量 FILE_UPLOAD_PATH,否则使用 process.cwd()/uploads
|
|
14
|
+
*
|
|
15
|
+
* @returns 上传根目录的绝对路径
|
|
16
|
+
*/
|
|
17
|
+
static getUploadDir() {
|
|
18
|
+
const uploadPath = process.env.FILE_UPLOAD_PATH || path.join(process.cwd(), 'uploads');
|
|
19
|
+
// 确保目录存在
|
|
20
|
+
if (!fs.existsSync(uploadPath)) {
|
|
21
|
+
fs.mkdirSync(uploadPath, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
return uploadPath;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 生成日期子目录格式 (YYYY/MM/DD)
|
|
27
|
+
*
|
|
28
|
+
* @returns 日期子目录路径
|
|
29
|
+
*/
|
|
30
|
+
static getDateSubdirectory() {
|
|
31
|
+
const now = new Date();
|
|
32
|
+
const year = now.getFullYear();
|
|
33
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
34
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
35
|
+
return path.join(String(year), month, day);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 获取完整的上传目录(包含日期子目录)
|
|
39
|
+
*
|
|
40
|
+
* @returns 完整的上传目录路径
|
|
41
|
+
*/
|
|
42
|
+
static getFullUploadPath() {
|
|
43
|
+
const basePath = this.getUploadDir();
|
|
44
|
+
const subDir = this.getDateSubdirectory();
|
|
45
|
+
const fullPath = path.join(basePath, subDir);
|
|
46
|
+
// 确保目录存在
|
|
47
|
+
if (!fs.existsSync(fullPath)) {
|
|
48
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
return fullPath;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 将绝对路径转换为相对路径(相对于上传根目录)
|
|
54
|
+
* 例如: /Users/.../uploads/2025/11/30/xxx.txt -> 2025/11/30/xxx.txt
|
|
55
|
+
*
|
|
56
|
+
* @param absolutePath 绝对路径
|
|
57
|
+
* @returns 相对路径
|
|
58
|
+
*/
|
|
59
|
+
static toRelativePath(absolutePath) {
|
|
60
|
+
if (!absolutePath) {
|
|
61
|
+
return '';
|
|
62
|
+
}
|
|
63
|
+
const uploadDir = this.getUploadDir();
|
|
64
|
+
// 方法1: 使用 startsWith 精确匹配
|
|
65
|
+
if (absolutePath.startsWith(uploadDir)) {
|
|
66
|
+
return absolutePath.substring(uploadDir.length).replace(/^[\/\\]+/, '');
|
|
67
|
+
}
|
|
68
|
+
// 方法2: 兼容处理 - 如果包含 /uploads/,从该位置开始截取
|
|
69
|
+
if (absolutePath.includes('/uploads/')) {
|
|
70
|
+
const uploadIndex = absolutePath.indexOf('/uploads/');
|
|
71
|
+
return absolutePath.substring(uploadIndex + '/uploads/'.length);
|
|
72
|
+
}
|
|
73
|
+
// 方法3: 兼容 Windows 路径
|
|
74
|
+
if (absolutePath.includes('\\uploads\\')) {
|
|
75
|
+
const uploadIndex = absolutePath.indexOf('\\uploads\\');
|
|
76
|
+
return absolutePath
|
|
77
|
+
.substring(uploadIndex + '\\uploads\\'.length)
|
|
78
|
+
.replace(/\\/g, '/');
|
|
79
|
+
}
|
|
80
|
+
// 如果都不匹配,返回原路径
|
|
81
|
+
return absolutePath;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 将相对路径转换为绝对路径(物理路径)
|
|
85
|
+
* 例如: 2025/11/30/xxx.txt -> /Users/.../uploads/2025/11/30/xxx.txt
|
|
86
|
+
*
|
|
87
|
+
* @param relativePath 相对路径
|
|
88
|
+
* @returns 绝对路径
|
|
89
|
+
*/
|
|
90
|
+
static toAbsolutePath(relativePath) {
|
|
91
|
+
if (!relativePath) {
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
const uploadDir = this.getUploadDir();
|
|
95
|
+
return path.join(uploadDir, relativePath);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 规范化路径(统一使用正斜杠)
|
|
99
|
+
*
|
|
100
|
+
* @param filePath 文件路径
|
|
101
|
+
* @returns 规范化后的路径
|
|
102
|
+
*/
|
|
103
|
+
static normalize(filePath) {
|
|
104
|
+
return filePath.replace(/\\/g, '/');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 检查路径是否为绝对路径
|
|
108
|
+
*
|
|
109
|
+
* @param filePath 文件路径
|
|
110
|
+
* @returns 是否为绝对路径
|
|
111
|
+
*/
|
|
112
|
+
static isAbsolute(filePath) {
|
|
113
|
+
return path.isAbsolute(filePath);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 检查文件是否存在
|
|
117
|
+
*
|
|
118
|
+
* @param filePath 文件路径(绝对或相对)
|
|
119
|
+
* @returns 文件是否存在
|
|
120
|
+
*/
|
|
121
|
+
static exists(filePath) {
|
|
122
|
+
const absolutePath = this.isAbsolute(filePath)
|
|
123
|
+
? filePath
|
|
124
|
+
: this.toAbsolutePath(filePath);
|
|
125
|
+
return fs.existsSync(absolutePath);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 获取文件的物理路径(用于文件系统访问)
|
|
129
|
+
*
|
|
130
|
+
* @param relativePath 相对路径
|
|
131
|
+
* @param storageProvider 存储提供商
|
|
132
|
+
* @returns 物理路径或相对路径(对象存储)
|
|
133
|
+
*/
|
|
134
|
+
static getPhysicalPath(relativePath, storageProvider = 'local') {
|
|
135
|
+
if (!relativePath) {
|
|
136
|
+
return '';
|
|
137
|
+
}
|
|
138
|
+
switch (storageProvider) {
|
|
139
|
+
case 'local':
|
|
140
|
+
return this.toAbsolutePath(relativePath);
|
|
141
|
+
case 's3':
|
|
142
|
+
case 'oss':
|
|
143
|
+
case 'minio':
|
|
144
|
+
case 'cdn':
|
|
145
|
+
// 对象存储返回相对路径,由存储服务处理
|
|
146
|
+
return relativePath;
|
|
147
|
+
default:
|
|
148
|
+
return relativePath;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.FilePathUtil = FilePathUtil;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./dynamic-import.util"), exports);
|
|
18
|
+
__exportStar(require("./checksum.util"), exports);
|
|
19
|
+
__exportStar(require("./filename.util"), exports);
|
|
20
|
+
__exportStar(require("./filepath.util"), exports);
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
|
|
2
2
|
import { Response } from 'express';
|
|
3
|
+
import type { Logger } from 'nestjs-pino';
|
|
4
|
+
/**
|
|
5
|
+
* HTTP Exception Filter
|
|
6
|
+
*
|
|
7
|
+
* Catches all exceptions and formats the response
|
|
8
|
+
* For 500 errors, logs via Pino Logger which will automatically send email alerts if EMAIL_LOG_ENABLED=true
|
|
9
|
+
*/
|
|
3
10
|
export declare class HttpExceptionFilter implements ExceptionFilter {
|
|
11
|
+
private readonly logger?;
|
|
12
|
+
constructor(logger?: Logger);
|
|
4
13
|
catch(exception: any, host: ArgumentsHost): Response<any, Record<string, any>>;
|
|
5
14
|
}
|
|
@@ -5,14 +5,30 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
8
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
15
|
exports.HttpExceptionFilter = void 0;
|
|
10
16
|
const common_1 = require("@nestjs/common");
|
|
11
17
|
const typeorm_1 = require("typeorm");
|
|
12
18
|
const nestjs_i18n_1 = require("nestjs-i18n");
|
|
19
|
+
const validation_metadata_helper_1 = require("../common/helpers/validation-metadata-helper");
|
|
20
|
+
/**
|
|
21
|
+
* HTTP Exception Filter
|
|
22
|
+
*
|
|
23
|
+
* Catches all exceptions and formats the response
|
|
24
|
+
* For 500 errors, logs via Pino Logger which will automatically send email alerts if EMAIL_LOG_ENABLED=true
|
|
25
|
+
*/
|
|
13
26
|
let HttpExceptionFilter = class HttpExceptionFilter {
|
|
27
|
+
constructor(logger) {
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
}
|
|
14
30
|
catch(exception, host) {
|
|
15
|
-
var _a, _b, _c, _d;
|
|
31
|
+
var _a, _b, _c, _d, _e;
|
|
16
32
|
const i18n = nestjs_i18n_1.I18nContext.current(host);
|
|
17
33
|
const ctx = host.switchToHttp();
|
|
18
34
|
const response = ctx.getResponse();
|
|
@@ -26,17 +42,25 @@ let HttpExceptionFilter = class HttpExceptionFilter {
|
|
|
26
42
|
error = 'Entity not found';
|
|
27
43
|
}
|
|
28
44
|
if (statusCode === 500) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
const errorObj = exception instanceof Error ? exception : new Error(String(exception));
|
|
46
|
+
// Use Pino Logger if available (will send email alerts when EMAIL_LOG_ENABLED=true)
|
|
47
|
+
// Otherwise fallback to console.error
|
|
48
|
+
if (this.logger) {
|
|
49
|
+
this.logger.error(errorObj.message, errorObj.stack);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.error({
|
|
53
|
+
message: errorObj.message,
|
|
54
|
+
stack: errorObj.stack,
|
|
55
|
+
connection: {
|
|
56
|
+
localAddress: (_a = request.socket) === null || _a === void 0 ? void 0 : _a.localAddress,
|
|
57
|
+
remoteAddress: (_b = request.socket) === null || _b === void 0 ? void 0 : _b.remoteAddress,
|
|
58
|
+
bytesRead: (_c = request.socket) === null || _c === void 0 ? void 0 : _c.bytesRead,
|
|
59
|
+
bytesWritten: (_d = request.socket) === null || _d === void 0 ? void 0 : _d.bytesWritten,
|
|
60
|
+
},
|
|
61
|
+
headers: request.headers,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
40
64
|
}
|
|
41
65
|
const getFirstError = (error, property = '') => {
|
|
42
66
|
var _a;
|
|
@@ -48,12 +72,26 @@ let HttpExceptionFilter = class HttpExceptionFilter {
|
|
|
48
72
|
return error;
|
|
49
73
|
};
|
|
50
74
|
if (exception instanceof nestjs_i18n_1.I18nValidationException) {
|
|
51
|
-
const
|
|
52
|
-
const constraint = Object.values(
|
|
75
|
+
const firstError = getFirstError(exception.errors[0], exception.errors[0].property);
|
|
76
|
+
const constraint = Object.values(firstError.constraints)[0];
|
|
53
77
|
const [translationKey, argsString] = constraint.split('|');
|
|
54
78
|
const args = !!argsString ? JSON.parse(argsString) : {};
|
|
79
|
+
// 获取当前语言
|
|
80
|
+
const lang = i18n.lang || 'zh';
|
|
81
|
+
// 在 stringify 之前获取 target
|
|
82
|
+
// firstError.target 可能是实例而不是构造函数,所以需要获取 constructor
|
|
83
|
+
let target = ((_e = firstError.target) === null || _e === void 0 ? void 0 : _e.constructor) || firstError.target;
|
|
84
|
+
if (!target && (firstError === null || firstError === void 0 ? void 0 : firstError.object)) {
|
|
85
|
+
target = firstError.object.constructor;
|
|
86
|
+
}
|
|
87
|
+
// 尝试从验证元数据中获取字段标签
|
|
88
|
+
const metadata = target ? (0, validation_metadata_helper_1.getValidationMetadata)(target, firstError.property) : undefined;
|
|
89
|
+
const fieldLabel = metadata
|
|
90
|
+
? (0, validation_metadata_helper_1.getFieldLabelForValidation)(metadata, lang)
|
|
91
|
+
: firstError.property;
|
|
92
|
+
// 使用字段标签作为 property 参数
|
|
55
93
|
error = i18n.translate(translationKey, {
|
|
56
|
-
args: Object.assign({ property:
|
|
94
|
+
args: Object.assign({ field: fieldLabel, property: fieldLabel, fieldName: fieldLabel, value: firstError.value, constraints: firstError.constraints }, args),
|
|
57
95
|
});
|
|
58
96
|
}
|
|
59
97
|
const parseJson = {
|
|
@@ -67,5 +105,8 @@ let HttpExceptionFilter = class HttpExceptionFilter {
|
|
|
67
105
|
};
|
|
68
106
|
exports.HttpExceptionFilter = HttpExceptionFilter;
|
|
69
107
|
exports.HttpExceptionFilter = HttpExceptionFilter = __decorate([
|
|
70
|
-
(0, common_1.Catch)()
|
|
108
|
+
(0, common_1.Catch)(),
|
|
109
|
+
(0, common_1.Injectable)(),
|
|
110
|
+
__param(0, (0, common_1.Optional)()),
|
|
111
|
+
__metadata("design:paramtypes", [Function])
|
|
71
112
|
], HttpExceptionFilter);
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { HttpClientConfig } from '../interfaces/http-client-config.interface';
|
|
2
|
+
/**
|
|
3
|
+
* 深度合并配置
|
|
4
|
+
* 将用户配置与默认配置合并,用户配置会覆盖默认配置
|
|
5
|
+
*/
|
|
6
|
+
export declare function mergeHttpClientConfig(userConfig?: Partial<HttpClientConfig>, defaultConfig?: Partial<HttpClientConfig>): HttpClientConfig;
|
|
2
7
|
/**
|
|
3
8
|
* HTTP客户端默认配置
|
|
4
9
|
*/
|
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TEST_HTTP_CLIENT_CONFIG = exports.PRODUCTION_HTTP_CLIENT_CONFIG = exports.DEVELOPMENT_HTTP_CLIENT_CONFIG = exports.DEFAULT_HTTP_CLIENT_CONFIG = void 0;
|
|
4
|
+
exports.mergeHttpClientConfig = mergeHttpClientConfig;
|
|
4
5
|
exports.getHttpClientConfig = getHttpClientConfig;
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* 深度合并对象
|
|
8
|
+
* 用于合并用户配置和默认配置
|
|
9
|
+
*/
|
|
10
|
+
function deepMerge(target, source) {
|
|
11
|
+
const result = Object.assign({}, target);
|
|
12
|
+
for (const key in source) {
|
|
13
|
+
if (source[key] instanceof Object &&
|
|
14
|
+
key in target &&
|
|
15
|
+
!Array.isArray(source[key])) {
|
|
16
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
result[key] = source[key];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 深度合并配置
|
|
26
|
+
* 将用户配置与默认配置合并,用户配置会覆盖默认配置
|
|
27
|
+
*/
|
|
28
|
+
function mergeHttpClientConfig(userConfig = {}, defaultConfig = exports.DEFAULT_HTTP_CLIENT_CONFIG) {
|
|
29
|
+
return deepMerge(defaultConfig, userConfig);
|
|
30
|
+
}
|
|
6
31
|
/**
|
|
7
32
|
* HTTP客户端默认配置
|
|
8
33
|
*/
|
|
@@ -29,15 +54,6 @@ exports.DEFAULT_HTTP_CLIENT_CONFIG = {
|
|
|
29
54
|
minimumThroughputThreshold: 10,
|
|
30
55
|
countHalfOpenCalls: false,
|
|
31
56
|
},
|
|
32
|
-
cache: {
|
|
33
|
-
enabled: true,
|
|
34
|
-
defaultTtl: 300000, // 5分钟
|
|
35
|
-
cacheableMethods: ['GET', 'HEAD'],
|
|
36
|
-
cacheableStatusCodes: [200, 201, 304],
|
|
37
|
-
options: {
|
|
38
|
-
layers: [cache_options_interface_1.CacheLayer.CLS, cache_options_interface_1.CacheLayer.MEMORY, cache_options_interface_1.CacheLayer.REDIS],
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
57
|
logging: {
|
|
42
58
|
enabled: true,
|
|
43
59
|
logRequests: true,
|
|
@@ -46,12 +62,11 @@ exports.DEFAULT_HTTP_CLIENT_CONFIG = {
|
|
|
46
62
|
logHeaders: false, // 默认不记录头信息(安全考虑)
|
|
47
63
|
logBody: true,
|
|
48
64
|
maxBodyLength: 1000,
|
|
49
|
-
|
|
65
|
+
sanitize: ['password', 'secret', 'token', 'apiKey', 'authorization', 'cookie'],
|
|
50
66
|
logLevel: 'info',
|
|
51
67
|
databaseLogging: {
|
|
52
68
|
enabled: false, // 默认不启用数据库日志
|
|
53
69
|
dataSource: 'default',
|
|
54
|
-
tableName: 'http_logs',
|
|
55
70
|
},
|
|
56
71
|
},
|
|
57
72
|
logCleanup: {
|
|
@@ -69,7 +84,6 @@ exports.DEFAULT_HTTP_CLIENT_CONFIG = {
|
|
|
69
84
|
exports.DEVELOPMENT_HTTP_CLIENT_CONFIG = Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG), { logging: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logging), { enabled: true, logHeaders: true, logBody: true, logLevel: 'debug', databaseLogging: {
|
|
70
85
|
enabled: true, // 开发环境启用数据库日志
|
|
71
86
|
dataSource: 'default',
|
|
72
|
-
tableName: 'http_logs',
|
|
73
87
|
} }), logCleanup: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logCleanup), { enabled: false }) });
|
|
74
88
|
/**
|
|
75
89
|
* 生产环境配置
|
|
@@ -77,7 +91,6 @@ exports.DEVELOPMENT_HTTP_CLIENT_CONFIG = Object.assign(Object.assign({}, exports
|
|
|
77
91
|
exports.PRODUCTION_HTTP_CLIENT_CONFIG = Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG), { timeout: 60000, retry: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.retry), { retries: 5 }), circuitBreaker: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.circuitBreaker), { failureThreshold: 3, recoveryTimeoutMs: 120000 }), logging: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logging), { enabled: true, logHeaders: false, logBody: false, logLevel: 'warn', databaseLogging: {
|
|
78
92
|
enabled: true, // 生产环境启用数据库日志
|
|
79
93
|
dataSource: 'default',
|
|
80
|
-
tableName: 'http_logs',
|
|
81
94
|
} }), logCleanup: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logCleanup), { enabled: true, retentionDays: 7, maxRecords: 50000, schedule: '0 3 * * *' }) });
|
|
82
95
|
/**
|
|
83
96
|
* 测试环境配置
|