@nest-omni/core 4.1.3-10 → 4.1.3-12
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.js +42 -2
- package/audit/controllers/audit.controller.d.ts +64 -0
- package/audit/controllers/audit.controller.js +50 -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/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/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 +8 -0
- package/audit/entities/entity-audit-log.entity.js +54 -2
- 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.js +8 -1
- package/audit/enums/audit.enums.d.ts +22 -6
- package/audit/enums/audit.enums.js +27 -9
- package/audit/index.d.ts +4 -1
- package/audit/index.js +25 -2
- package/audit/interceptors/audit-action.interceptor.d.ts +38 -0
- package/audit/interceptors/audit-action.interceptor.js +215 -0
- package/audit/interceptors/index.d.ts +1 -0
- package/audit/interceptors/index.js +1 -0
- package/audit/interfaces/audit.interfaces.d.ts +145 -2
- package/audit/services/audit-action.service.d.ts +141 -0
- package/audit/services/audit-action.service.js +244 -0
- package/audit/services/audit-context.service.d.ts +82 -0
- package/audit/services/audit-context.service.js +170 -0
- package/audit/services/entity-audit.service.d.ts +174 -4
- package/audit/services/entity-audit.service.js +515 -14
- package/audit/services/index.d.ts +3 -0
- package/audit/services/index.js +3 -0
- package/audit/services/manual-audit-log.service.d.ts +24 -23
- package/audit/services/manual-audit-log.service.js +32 -53
- package/audit/services/operation-description.service.d.ts +13 -3
- package/audit/services/operation-description.service.js +161 -24
- package/audit/services/transaction-audit.service.js +3 -3
- package/audit/subscribers/entity-audit.subscriber.d.ts +4 -0
- package/audit/subscribers/entity-audit.subscriber.js +47 -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/csv-data.decorator.d.ts +44 -0
- package/file-upload/decorators/csv-data.decorator.js +131 -0
- package/file-upload/decorators/excel-data.decorator.d.ts +44 -0
- package/file-upload/decorators/excel-data.decorator.js +125 -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 +4 -0
- package/file-upload/decorators/index.js +20 -0
- package/file-upload/decorators/process.decorator.d.ts +40 -0
- package/file-upload/decorators/process.decorator.js +52 -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 +16 -0
- package/file-upload/dto/update-file.dto.js +71 -0
- package/file-upload/entities/file-metadata.entity.d.ts +22 -0
- package/file-upload/entities/file-metadata.entity.js +84 -0
- package/file-upload/entities/file.entity.d.ts +129 -0
- package/file-upload/entities/file.entity.js +384 -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 +264 -0
- package/file-upload/index.d.ts +26 -0
- package/file-upload/index.js +59 -0
- package/file-upload/interceptors/file-upload.interceptor.d.ts +48 -0
- package/file-upload/interceptors/file-upload.interceptor.js +434 -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-upload-options.interface.d.ts +117 -0
- package/file-upload/interfaces/file-upload-options.interface.js +2 -0
- package/file-upload/interfaces/index.d.ts +7 -0
- package/file-upload/interfaces/index.js +24 -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/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 +190 -0
- package/file-upload/services/file.service.js +609 -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 +274 -0
- package/file-upload/services/malicious-file-detector.service.js +1035 -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 +50 -0
- package/file-upload/utils/dynamic-import.util.js +144 -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/http-client/http-client.module.js +1 -5
- package/index.d.ts +3 -1
- package/index.js +4 -1
- package/package.json +4 -5
- package/redis-lock/lock-heartbeat.service.d.ts +2 -2
- package/redis-lock/lock-heartbeat.service.js +4 -4
- package/redis-lock/redis-lock.service.d.ts +18 -0
- package/redis-lock/redis-lock.service.js +38 -8
- package/setup/bootstrap.setup.d.ts +1 -0
- package/setup/bootstrap.setup.js +1 -0
- package/setup/schedule.decorator.js +18 -8
- package/shared/index.d.ts +1 -1
- package/shared/index.js +1 -1
- package/shared/{serviceRegistryModule.js → service-registry.module.js} +9 -16
- package/shared/services/index.d.ts +0 -1
- package/shared/services/index.js +0 -1
- package/transaction/__tests__/mocks.d.ts +9 -0
- package/transaction/__tests__/mocks.js +33 -0
- package/transaction/base-service-transaction.d.ts +99 -0
- package/transaction/base-service-transaction.js +286 -0
- package/transaction/cls-compatibility.service.d.ts +55 -0
- package/transaction/cls-compatibility.service.js +127 -0
- package/transaction/data-source-registry.d.ts +91 -0
- package/transaction/data-source-registry.js +349 -0
- package/transaction/database-adapter.d.ts +44 -0
- package/transaction/database-adapter.js +240 -0
- package/transaction/decorators/entity-datasource.decorator.d.ts +62 -0
- package/transaction/decorators/entity-datasource.decorator.js +105 -0
- package/transaction/index.d.ts +14 -0
- package/transaction/index.js +57 -0
- package/transaction/logging-transactional.interceptor.d.ts +18 -0
- package/transaction/logging-transactional.interceptor.js +163 -0
- package/transaction/transaction-context.service.d.ts +137 -0
- package/transaction/transaction-context.service.js +411 -0
- package/transaction/transaction-manager.d.ts +230 -0
- package/transaction/transaction-manager.js +1001 -0
- package/transaction/transaction-synchronization.d.ts +171 -0
- package/transaction/transaction-synchronization.js +380 -0
- package/transaction/transaction.errors.d.ts +91 -0
- package/transaction/transaction.errors.js +206 -0
- package/transaction/transaction.module.d.ts +30 -0
- package/transaction/transaction.module.js +98 -0
- package/transaction/transactional.decorator.d.ts +82 -0
- package/transaction/transactional.decorator.js +319 -0
- package/transaction/typeorm-module-wrapper.d.ts +96 -0
- package/transaction/typeorm-module-wrapper.js +197 -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 +2 -5
- package/validators/is-exists.validator.js +4 -6
- package/validators/is-unique.validator.d.ts +2 -5
- package/validators/is-unique.validator.js +6 -11
- 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);
|
|
@@ -18,7 +18,6 @@ var HttpClientModule_1;
|
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.HttpClientModule = void 0;
|
|
20
20
|
const common_1 = require("@nestjs/common");
|
|
21
|
-
const schedule_1 = require("@nestjs/schedule");
|
|
22
21
|
const config_1 = require("@nestjs/config");
|
|
23
22
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
24
23
|
const cache_service_1 = require("../cache/cache.service");
|
|
@@ -41,7 +40,7 @@ let HttpClientModule = HttpClientModule_1 = class HttpClientModule {
|
|
|
41
40
|
* 动态注册HTTP客户端模块
|
|
42
41
|
*/
|
|
43
42
|
static forRoot(config = {}) {
|
|
44
|
-
var _a, _b
|
|
43
|
+
var _a, _b;
|
|
45
44
|
// 基础服务提供者
|
|
46
45
|
const baseProviders = [
|
|
47
46
|
{
|
|
@@ -87,9 +86,6 @@ let HttpClientModule = HttpClientModule_1 = class HttpClientModule {
|
|
|
87
86
|
if ((_b = (_a = config.logging) === null || _a === void 0 ? void 0 : _a.databaseLogging) === null || _b === void 0 ? void 0 : _b.enabled) {
|
|
88
87
|
dynamicImports.push(typeorm_1.TypeOrmModule.forFeature([entities_1.HttpLogEntity]));
|
|
89
88
|
}
|
|
90
|
-
if ((_c = config.logCleanup) === null || _c === void 0 ? void 0 : _c.enabled) {
|
|
91
|
-
dynamicImports.push(schedule_1.ScheduleModule.forRoot());
|
|
92
|
-
}
|
|
93
89
|
return {
|
|
94
90
|
module: HttpClientModule_1,
|
|
95
91
|
imports: dynamicImports,
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './common';
|
|
1
2
|
export * from './constants';
|
|
2
3
|
export * from './decorators';
|
|
3
4
|
export * from './exceptions';
|
|
@@ -7,10 +8,10 @@ export * from './interceptors';
|
|
|
7
8
|
export * from './shared';
|
|
8
9
|
export * from './middlewares';
|
|
9
10
|
export * from './validators';
|
|
10
|
-
export * from './common';
|
|
11
11
|
export * from './validator-json';
|
|
12
12
|
export * from './helpers';
|
|
13
13
|
export * from './providers';
|
|
14
|
+
export * from './transaction';
|
|
14
15
|
export * from './redis-lock';
|
|
15
16
|
export * from './cache';
|
|
16
17
|
export * from './http-client';
|
|
@@ -18,3 +19,4 @@ export * from './vault';
|
|
|
18
19
|
export * from './setup';
|
|
19
20
|
export * from './health-checker';
|
|
20
21
|
export * from './audit';
|
|
22
|
+
export * from './file-upload';
|
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
// Core modules
|
|
18
|
+
__exportStar(require("./common"), exports);
|
|
18
19
|
__exportStar(require("./constants"), exports);
|
|
19
20
|
__exportStar(require("./decorators"), exports);
|
|
20
21
|
__exportStar(require("./exceptions"), exports);
|
|
@@ -24,10 +25,10 @@ __exportStar(require("./interceptors"), exports);
|
|
|
24
25
|
__exportStar(require("./shared"), exports);
|
|
25
26
|
__exportStar(require("./middlewares"), exports);
|
|
26
27
|
__exportStar(require("./validators"), exports);
|
|
27
|
-
__exportStar(require("./common"), exports);
|
|
28
28
|
__exportStar(require("./validator-json"), exports);
|
|
29
29
|
__exportStar(require("./helpers"), exports);
|
|
30
30
|
__exportStar(require("./providers"), exports);
|
|
31
|
+
__exportStar(require("./transaction"), exports);
|
|
31
32
|
// Lock module
|
|
32
33
|
__exportStar(require("./redis-lock"), exports);
|
|
33
34
|
// Cache module
|
|
@@ -42,3 +43,5 @@ __exportStar(require("./setup"), exports);
|
|
|
42
43
|
__exportStar(require("./health-checker"), exports);
|
|
43
44
|
// Audit module
|
|
44
45
|
__exportStar(require("./audit"), exports);
|
|
46
|
+
// File upload module
|
|
47
|
+
__exportStar(require("./file-upload"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nest-omni/core",
|
|
3
|
-
"version": "4.1.3-
|
|
3
|
+
"version": "4.1.3-12",
|
|
4
4
|
"description": "A comprehensive NestJS framework for building enterprise-grade applications with best practices",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -47,13 +47,12 @@
|
|
|
47
47
|
"@types/node": "^24.9.1",
|
|
48
48
|
"@types/sprintf-js": "^1.1.4",
|
|
49
49
|
"@types/uuid": "^11.0.0",
|
|
50
|
+
"sqlite3": "^5.1.7",
|
|
50
51
|
"typescript": "^5.9.3"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
54
|
"@dataui/crud": "^5.3.4",
|
|
54
55
|
"@dataui/crud-typeorm": "^5.3.4",
|
|
55
|
-
"@nestjs-cls/transactional": "^3.1.0",
|
|
56
|
-
"@nestjs-cls/transactional-adapter-typeorm": "^1.3.0",
|
|
57
56
|
"@nestjs/bull": "^11.0.4",
|
|
58
57
|
"@nestjs/common": "^11.1.7",
|
|
59
58
|
"@nestjs/config": "^4.0.2",
|
|
@@ -89,6 +88,7 @@
|
|
|
89
88
|
"nestjs-cls": "^6.0.1",
|
|
90
89
|
"nestjs-i18n": "^10.5.1",
|
|
91
90
|
"nestjs-pino": "^4.4.1",
|
|
91
|
+
"node-vault": "^0.10.9",
|
|
92
92
|
"pino-http": "^11.0.0",
|
|
93
93
|
"pino-pretty": "^13.1.2",
|
|
94
94
|
"reflect-metadata": "^0.2.2",
|
|
@@ -96,7 +96,6 @@
|
|
|
96
96
|
"source-map-support": "^0.5.21",
|
|
97
97
|
"sprintf-js": "^1.1.3",
|
|
98
98
|
"typeorm": "^0.3.27",
|
|
99
|
-
"uuid": "^13.0.0"
|
|
100
|
-
"node-vault": "^0.10.9"
|
|
99
|
+
"uuid": "^13.0.0"
|
|
101
100
|
}
|
|
102
101
|
}
|
|
@@ -23,12 +23,12 @@ export declare class LockHeartbeatService implements OnModuleInit, OnModuleDestr
|
|
|
23
23
|
private heartbeatTimer;
|
|
24
24
|
/**
|
|
25
25
|
* Heartbeat interval in milliseconds
|
|
26
|
-
* @default
|
|
26
|
+
* @default 5000 (5 seconds) - reduced for faster failure detection
|
|
27
27
|
*/
|
|
28
28
|
private readonly heartbeatInterval;
|
|
29
29
|
/**
|
|
30
30
|
* Heartbeat TTL in seconds (3x interval for safety)
|
|
31
|
-
* @default
|
|
31
|
+
* @default 15 (15 seconds) - adjusted to match new interval
|
|
32
32
|
*/
|
|
33
33
|
private readonly heartbeatTtl;
|
|
34
34
|
constructor();
|
|
@@ -44,14 +44,14 @@ let LockHeartbeatService = LockHeartbeatService_1 = class LockHeartbeatService {
|
|
|
44
44
|
this.heartbeatTimer = null;
|
|
45
45
|
/**
|
|
46
46
|
* Heartbeat interval in milliseconds
|
|
47
|
-
* @default
|
|
47
|
+
* @default 5000 (5 seconds) - reduced for faster failure detection
|
|
48
48
|
*/
|
|
49
|
-
this.heartbeatInterval =
|
|
49
|
+
this.heartbeatInterval = 5000;
|
|
50
50
|
/**
|
|
51
51
|
* Heartbeat TTL in seconds (3x interval for safety)
|
|
52
|
-
* @default
|
|
52
|
+
* @default 15 (15 seconds) - adjusted to match new interval
|
|
53
53
|
*/
|
|
54
|
-
this.heartbeatTtl =
|
|
54
|
+
this.heartbeatTtl = 15;
|
|
55
55
|
this.instanceId = this.getInstanceId();
|
|
56
56
|
}
|
|
57
57
|
onModuleInit() {
|
|
@@ -69,6 +69,24 @@ export interface LockOptions {
|
|
|
69
69
|
* @default 0 (no automatic extension)
|
|
70
70
|
*/
|
|
71
71
|
autoExtend?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Whether to use exponential backoff for retry attempts
|
|
74
|
+
* If true, retry delay will increase exponentially: retryDelay * 2^(attempt-1)
|
|
75
|
+
* @default false
|
|
76
|
+
*/
|
|
77
|
+
useExponentialBackoff?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Maximum retry delay in milliseconds when using exponential backoff
|
|
80
|
+
* Ensures retry delay doesn't grow indefinitely
|
|
81
|
+
* @default 60000 (60 seconds)
|
|
82
|
+
*/
|
|
83
|
+
maxRetryDelay?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Random jitter to add to retry delay in milliseconds
|
|
86
|
+
* Helps prevent synchronized retries across multiple instances
|
|
87
|
+
* @default 0 (no jitter)
|
|
88
|
+
*/
|
|
89
|
+
retryJitter?: number;
|
|
72
90
|
}
|
|
73
91
|
export interface LockResult {
|
|
74
92
|
/**
|