@nest-omni/core 4.1.3-11 → 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.
Files changed (175) hide show
  1. package/audit/audit.module.js +17 -0
  2. package/audit/controllers/audit.controller.d.ts +64 -0
  3. package/audit/controllers/audit.controller.js +50 -0
  4. package/audit/decorators/audit-action.decorator.d.ts +74 -0
  5. package/audit/decorators/audit-action.decorator.js +42 -0
  6. package/audit/decorators/entity-audit.decorator.d.ts +10 -1
  7. package/audit/decorators/entity-audit.decorator.js +34 -16
  8. package/audit/decorators/index.d.ts +1 -0
  9. package/audit/decorators/index.js +1 -0
  10. package/audit/entities/audit-action-summary.entity.d.ts +23 -0
  11. package/audit/entities/audit-action-summary.entity.js +101 -0
  12. package/audit/entities/entity-audit-log.entity.d.ts +3 -0
  13. package/audit/entities/entity-audit-log.entity.js +25 -2
  14. package/audit/entities/entity-transaction.entity.d.ts +3 -4
  15. package/audit/entities/entity-transaction.entity.js +10 -3
  16. package/audit/entities/index.d.ts +1 -0
  17. package/audit/entities/index.js +1 -0
  18. package/audit/entities/manual-operation-log.entity.js +8 -1
  19. package/audit/enums/audit.enums.d.ts +1 -10
  20. package/audit/enums/audit.enums.js +7 -17
  21. package/audit/index.d.ts +2 -1
  22. package/audit/index.js +5 -1
  23. package/audit/interceptors/audit-action.interceptor.d.ts +38 -0
  24. package/audit/interceptors/audit-action.interceptor.js +215 -0
  25. package/audit/interceptors/index.d.ts +1 -0
  26. package/audit/interceptors/index.js +1 -0
  27. package/audit/interfaces/audit.interfaces.d.ts +10 -5
  28. package/audit/services/audit-action.service.d.ts +141 -0
  29. package/audit/services/audit-action.service.js +244 -0
  30. package/audit/services/audit-context.service.d.ts +82 -0
  31. package/audit/services/audit-context.service.js +170 -0
  32. package/audit/services/entity-audit.service.d.ts +104 -3
  33. package/audit/services/entity-audit.service.js +306 -9
  34. package/audit/services/index.d.ts +1 -0
  35. package/audit/services/index.js +1 -0
  36. package/audit/services/manual-audit-log.service.d.ts +24 -23
  37. package/audit/services/manual-audit-log.service.js +32 -53
  38. package/audit/services/operation-description.service.d.ts +13 -3
  39. package/audit/services/operation-description.service.js +161 -24
  40. package/audit/services/transaction-audit.service.js +3 -3
  41. package/audit/subscribers/entity-audit.subscriber.d.ts +4 -0
  42. package/audit/subscribers/entity-audit.subscriber.js +47 -0
  43. package/file-upload/controllers/file-access.controller.d.ts +23 -0
  44. package/file-upload/controllers/file-access.controller.js +128 -0
  45. package/file-upload/decorators/csv-data.decorator.d.ts +44 -0
  46. package/file-upload/decorators/csv-data.decorator.js +131 -0
  47. package/file-upload/decorators/excel-data.decorator.d.ts +44 -0
  48. package/file-upload/decorators/excel-data.decorator.js +125 -0
  49. package/file-upload/decorators/file-upload.decorator.d.ts +83 -0
  50. package/file-upload/decorators/file-upload.decorator.js +172 -0
  51. package/file-upload/decorators/index.d.ts +4 -0
  52. package/file-upload/decorators/index.js +20 -0
  53. package/file-upload/decorators/process.decorator.d.ts +40 -0
  54. package/file-upload/decorators/process.decorator.js +52 -0
  55. package/file-upload/dto/create-file.dto.d.ts +24 -0
  56. package/file-upload/dto/create-file.dto.js +112 -0
  57. package/file-upload/dto/find-files.dto.d.ts +15 -0
  58. package/file-upload/dto/find-files.dto.js +76 -0
  59. package/file-upload/dto/index.d.ts +4 -0
  60. package/file-upload/dto/index.js +20 -0
  61. package/file-upload/dto/pagination.dto.d.ts +7 -0
  62. package/file-upload/dto/pagination.dto.js +39 -0
  63. package/file-upload/dto/update-file.dto.d.ts +16 -0
  64. package/file-upload/dto/update-file.dto.js +71 -0
  65. package/file-upload/entities/file-metadata.entity.d.ts +22 -0
  66. package/file-upload/entities/file-metadata.entity.js +84 -0
  67. package/file-upload/entities/file.entity.d.ts +129 -0
  68. package/file-upload/entities/file.entity.js +384 -0
  69. package/file-upload/entities/index.d.ts +2 -0
  70. package/file-upload/entities/index.js +18 -0
  71. package/file-upload/enums/file-type.enum.d.ts +72 -0
  72. package/file-upload/enums/file-type.enum.js +212 -0
  73. package/file-upload/exceptions/file-upload.exception.d.ts +57 -0
  74. package/file-upload/exceptions/file-upload.exception.js +120 -0
  75. package/file-upload/exceptions/index.d.ts +1 -0
  76. package/file-upload/exceptions/index.js +17 -0
  77. package/file-upload/file-upload.module.d.ts +89 -0
  78. package/file-upload/file-upload.module.js +264 -0
  79. package/file-upload/index.d.ts +26 -0
  80. package/file-upload/index.js +59 -0
  81. package/file-upload/interceptors/file-upload.interceptor.d.ts +48 -0
  82. package/file-upload/interceptors/file-upload.interceptor.js +434 -0
  83. package/file-upload/interceptors/index.d.ts +1 -0
  84. package/file-upload/interceptors/index.js +17 -0
  85. package/file-upload/interfaces/custom-file-type.interface.d.ts +72 -0
  86. package/file-upload/interfaces/custom-file-type.interface.js +2 -0
  87. package/file-upload/interfaces/file-buffer.interface.d.ts +72 -0
  88. package/file-upload/interfaces/file-buffer.interface.js +2 -0
  89. package/file-upload/interfaces/file-entity.interface.d.ts +142 -0
  90. package/file-upload/interfaces/file-entity.interface.js +28 -0
  91. package/file-upload/interfaces/file-metadata.interface.d.ts +21 -0
  92. package/file-upload/interfaces/file-metadata.interface.js +2 -0
  93. package/file-upload/interfaces/file-upload-options.interface.d.ts +117 -0
  94. package/file-upload/interfaces/file-upload-options.interface.js +2 -0
  95. package/file-upload/interfaces/index.d.ts +7 -0
  96. package/file-upload/interfaces/index.js +24 -0
  97. package/file-upload/interfaces/storage-provider.interface.d.ts +239 -0
  98. package/file-upload/interfaces/storage-provider.interface.js +2 -0
  99. package/file-upload/interfaces/upload-options.interface.d.ts +19 -0
  100. package/file-upload/interfaces/upload-options.interface.js +2 -0
  101. package/file-upload/providers/index.d.ts +2 -0
  102. package/file-upload/providers/index.js +18 -0
  103. package/file-upload/providers/local-storage.provider.d.ts +98 -0
  104. package/file-upload/providers/local-storage.provider.js +484 -0
  105. package/file-upload/providers/s3-storage.provider.d.ts +87 -0
  106. package/file-upload/providers/s3-storage.provider.js +455 -0
  107. package/file-upload/services/file-signature-validator.service.d.ts +118 -0
  108. package/file-upload/services/file-signature-validator.service.js +376 -0
  109. package/file-upload/services/file.service.d.ts +190 -0
  110. package/file-upload/services/file.service.js +609 -0
  111. package/file-upload/services/index.d.ts +4 -0
  112. package/file-upload/services/index.js +20 -0
  113. package/file-upload/services/malicious-file-detector.service.d.ts +274 -0
  114. package/file-upload/services/malicious-file-detector.service.js +1035 -0
  115. package/file-upload/services/mime-registry.service.d.ts +47 -0
  116. package/file-upload/services/mime-registry.service.js +167 -0
  117. package/file-upload/utils/checksum.util.d.ts +28 -0
  118. package/file-upload/utils/checksum.util.js +65 -0
  119. package/file-upload/utils/dynamic-import.util.d.ts +50 -0
  120. package/file-upload/utils/dynamic-import.util.js +144 -0
  121. package/file-upload/utils/filename.util.d.ts +59 -0
  122. package/file-upload/utils/filename.util.js +184 -0
  123. package/file-upload/utils/filepath.util.d.ts +70 -0
  124. package/file-upload/utils/filepath.util.js +152 -0
  125. package/file-upload/utils/index.d.ts +4 -0
  126. package/file-upload/utils/index.js +20 -0
  127. package/index.d.ts +3 -1
  128. package/index.js +4 -1
  129. package/package.json +4 -5
  130. package/setup/bootstrap.setup.d.ts +1 -0
  131. package/setup/bootstrap.setup.js +1 -0
  132. package/shared/index.d.ts +1 -1
  133. package/shared/index.js +1 -1
  134. package/shared/{serviceRegistryModule.js → service-registry.module.js} +0 -12
  135. package/shared/services/index.d.ts +0 -1
  136. package/shared/services/index.js +0 -1
  137. package/transaction/__tests__/mocks.d.ts +9 -0
  138. package/transaction/__tests__/mocks.js +33 -0
  139. package/transaction/base-service-transaction.d.ts +99 -0
  140. package/transaction/base-service-transaction.js +286 -0
  141. package/transaction/cls-compatibility.service.d.ts +55 -0
  142. package/transaction/cls-compatibility.service.js +127 -0
  143. package/transaction/data-source-registry.d.ts +91 -0
  144. package/transaction/data-source-registry.js +349 -0
  145. package/transaction/database-adapter.d.ts +44 -0
  146. package/transaction/database-adapter.js +240 -0
  147. package/transaction/decorators/entity-datasource.decorator.d.ts +62 -0
  148. package/transaction/decorators/entity-datasource.decorator.js +105 -0
  149. package/transaction/index.d.ts +14 -0
  150. package/transaction/index.js +57 -0
  151. package/transaction/logging-transactional.interceptor.d.ts +18 -0
  152. package/transaction/logging-transactional.interceptor.js +163 -0
  153. package/transaction/transaction-context.service.d.ts +137 -0
  154. package/transaction/transaction-context.service.js +411 -0
  155. package/transaction/transaction-manager.d.ts +230 -0
  156. package/transaction/transaction-manager.js +1001 -0
  157. package/transaction/transaction-synchronization.d.ts +171 -0
  158. package/transaction/transaction-synchronization.js +380 -0
  159. package/transaction/transaction.errors.d.ts +91 -0
  160. package/transaction/transaction.errors.js +206 -0
  161. package/transaction/transaction.module.d.ts +30 -0
  162. package/transaction/transaction.module.js +98 -0
  163. package/transaction/transactional.decorator.d.ts +82 -0
  164. package/transaction/transactional.decorator.js +319 -0
  165. package/transaction/typeorm-module-wrapper.d.ts +96 -0
  166. package/transaction/typeorm-module-wrapper.js +197 -0
  167. package/validators/file-mimetype.validator.d.ts +0 -2
  168. package/validators/file-mimetype.validator.js +4 -6
  169. package/validators/is-exists.validator.d.ts +2 -5
  170. package/validators/is-exists.validator.js +4 -6
  171. package/validators/is-unique.validator.d.ts +2 -5
  172. package/validators/is-unique.validator.js +6 -11
  173. package/shared/services/validator.service.d.ts +0 -3
  174. package/shared/services/validator.service.js +0 -20
  175. /package/shared/{serviceRegistryModule.d.ts → service-registry.module.d.ts} +0 -0
@@ -0,0 +1,376 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
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
+ };
14
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
+ return new (P || (P = Promise))(function (resolve, reject) {
17
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
21
+ });
22
+ };
23
+ var FileSignatureValidator_1;
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.FileSignatureValidator = void 0;
26
+ const common_1 = require("@nestjs/common");
27
+ const mime_registry_service_1 = require("./mime-registry.service");
28
+ const fs = require("fs-extra");
29
+ /**
30
+ * 文件签名验证服务
31
+ */
32
+ let FileSignatureValidator = FileSignatureValidator_1 = class FileSignatureValidator {
33
+ constructor(mimeRegistry, customTypes) {
34
+ this.mimeRegistry = mimeRegistry;
35
+ this.logger = new common_1.Logger(FileSignatureValidator_1.name);
36
+ this.defaultSignatures = new Map();
37
+ this.initializeDefaultSignatures();
38
+ if (customTypes) {
39
+ this.registerCustomSignatures(customTypes);
40
+ }
41
+ }
42
+ /**
43
+ * 验证文件签名
44
+ */
45
+ validateFileSignature(filePath, expectedTypes, options) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const maxBytes = (options === null || options === void 0 ? void 0 : options.maxReadBytes) || 2048;
48
+ // 读取文件头
49
+ const buffer = yield this.readFileBuffer(filePath, maxBytes);
50
+ // 检测实际文件类型
51
+ const detectedTypes = yield this.detectFileTypes(buffer);
52
+ // 验证期望的类型
53
+ const isValid = expectedTypes.some((type) => this.matchesType(buffer, type));
54
+ // 检查文件完整性
55
+ const integrity = yield this.checkFileIntegrity(filePath, buffer);
56
+ // 获取文件大小
57
+ const stats = yield fs.stat(filePath);
58
+ return {
59
+ valid: isValid && integrity.valid,
60
+ detectedTypes,
61
+ expectedTypes,
62
+ errors: [
63
+ ...(!isValid
64
+ ? this.getValidationError(detectedTypes, expectedTypes)
65
+ : []),
66
+ ...(!integrity.valid ? integrity.errors : []),
67
+ ],
68
+ metadata: {
69
+ fileSize: stats.size,
70
+ confidence: this.calculateConfidence(buffer, detectedTypes),
71
+ signatures: detectedTypes.map((t) => ({
72
+ type: t.type,
73
+ signature: t.signature,
74
+ })),
75
+ },
76
+ };
77
+ });
78
+ }
79
+ /**
80
+ * 检测文件类型
81
+ */
82
+ detectFileTypes(buffer) {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ const detected = [];
85
+ // 检查内置签名
86
+ for (const [mimeType, signatures] of this.defaultSignatures.entries()) {
87
+ for (const signature of signatures) {
88
+ if (this.matchesSignature(buffer, signature)) {
89
+ detected.push({
90
+ type: mimeType,
91
+ signature,
92
+ confidence: this.calculateSignatureConfidence(signature),
93
+ });
94
+ break;
95
+ }
96
+ }
97
+ }
98
+ // 检查自定义签名
99
+ const registeredTypes = this.mimeRegistry.getAllRegisteredTypes();
100
+ for (const typeName of registeredTypes) {
101
+ const config = this.mimeRegistry.getMimeConfig(typeName);
102
+ if (config === null || config === void 0 ? void 0 : config.signatures) {
103
+ const signatures = Array.isArray(config.signatures)
104
+ ? config.signatures
105
+ : [config.signatures];
106
+ for (const signature of signatures) {
107
+ if (this.matchesSignature(buffer, signature)) {
108
+ detected.push({
109
+ type: typeName,
110
+ signature,
111
+ confidence: this.calculateSignatureConfidence(signature),
112
+ isCustom: true,
113
+ });
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ // 按置信度排序
120
+ return detected.sort((a, b) => b.confidence - a.confidence);
121
+ });
122
+ }
123
+ /**
124
+ * 注册自定义签名
125
+ */
126
+ registerCustomSignatures(customTypes) {
127
+ if (!customTypes || typeof customTypes !== 'object') {
128
+ this.logger.warn('Invalid custom types provided to registerCustomSignatures');
129
+ return;
130
+ }
131
+ Object.entries(customTypes).forEach(([typeName, config]) => {
132
+ if (config === null || config === void 0 ? void 0 : config.signature) {
133
+ const signatures = Array.isArray(config.signature)
134
+ ? config.signature
135
+ : [config.signature];
136
+ this.defaultSignatures.set(typeName, signatures);
137
+ this.logger.debug(`Registered custom signatures for: ${typeName}`);
138
+ }
139
+ });
140
+ }
141
+ /**
142
+ * 检查签名是否匹配
143
+ */
144
+ matchesSignature(buffer, signature) {
145
+ const offset = signature.offset || 0;
146
+ const bytes = this.normalizeSignatureBytes(signature.bytes);
147
+ const length = signature.length || bytes.length;
148
+ // 检查边界
149
+ if (offset + length > buffer.length) {
150
+ return false;
151
+ }
152
+ const bufferSlice = buffer.slice(offset, offset + length);
153
+ // 应用掩码
154
+ if (signature.mask) {
155
+ const mask = this.normalizeSignatureBytes(signature.mask);
156
+ for (let i = 0; i < length; i++) {
157
+ if ((bufferSlice[i] & mask[i]) !== (bytes[i] & mask[i])) {
158
+ return false;
159
+ }
160
+ }
161
+ return true;
162
+ }
163
+ // 字符串比较
164
+ if (typeof signature.bytes === 'string') {
165
+ const bufferStr = bufferSlice.toString('ascii');
166
+ if (signature.exact === false) {
167
+ return bufferStr.startsWith(signature.bytes);
168
+ }
169
+ return bufferStr === signature.bytes;
170
+ }
171
+ // 字节数组比较
172
+ return bufferSlice.equals(bytes);
173
+ }
174
+ /**
175
+ * 检查类型是否匹配
176
+ */
177
+ matchesType(buffer, type) {
178
+ const config = this.mimeRegistry.getMimeConfig(type);
179
+ if (!(config === null || config === void 0 ? void 0 : config.signatures)) {
180
+ return true; // 无签名配置,默认通过
181
+ }
182
+ const signatures = Array.isArray(config.signatures)
183
+ ? config.signatures
184
+ : [config.signatures];
185
+ return signatures.some((sig) => this.matchesSignature(buffer, sig));
186
+ }
187
+ /**
188
+ * 规范化签名字节
189
+ */
190
+ normalizeSignatureBytes(bytes) {
191
+ if (Buffer.isBuffer(bytes)) {
192
+ return bytes;
193
+ }
194
+ if (typeof bytes === 'string') {
195
+ return Buffer.from(bytes, 'ascii');
196
+ }
197
+ return Buffer.from(bytes);
198
+ }
199
+ /**
200
+ * 读取文件缓冲区
201
+ */
202
+ readFileBuffer(filePath, size) {
203
+ return __awaiter(this, void 0, void 0, function* () {
204
+ let fileHandle;
205
+ try {
206
+ fileHandle = yield fs.open(filePath, 'r');
207
+ // 使用Buffer.alloc代替allocUnsafe以避免安全风险
208
+ const buffer = Buffer.alloc(size);
209
+ const { bytesRead } = yield fileHandle.read(buffer, 0, size, 0);
210
+ return buffer.slice(0, bytesRead);
211
+ }
212
+ finally {
213
+ if (fileHandle) {
214
+ try {
215
+ yield fileHandle.close();
216
+ }
217
+ catch (closeError) {
218
+ this.logger.error(`Failed to close file handle: ${closeError.message}`);
219
+ }
220
+ }
221
+ }
222
+ });
223
+ }
224
+ /**
225
+ * 检查文件完整性
226
+ */
227
+ checkFileIntegrity(filePath, header) {
228
+ return __awaiter(this, void 0, void 0, function* () {
229
+ const errors = [];
230
+ const stats = yield fs.stat(filePath);
231
+ // 检查文件是否为空
232
+ if (stats.size === 0) {
233
+ errors.push('File is empty');
234
+ }
235
+ // 检查常见损坏标记(大量null字节)
236
+ const nullBytes = header.filter((byte) => byte === 0x00).length;
237
+ if (nullBytes > header.length * 0.8) {
238
+ errors.push('File may be corrupted (excessive null bytes found)');
239
+ }
240
+ // 特定格式完整性检查
241
+ const detectedTypes = yield this.detectFileTypes(header);
242
+ for (const detection of detectedTypes) {
243
+ if (detection.type.includes('pdf')) {
244
+ // 检查 PDF 结尾标记
245
+ const tail = yield this.readFileTail(filePath, 1024);
246
+ if (!tail.includes(Buffer.from('%%EOF'))) {
247
+ errors.push('PDF file missing EOF marker');
248
+ }
249
+ }
250
+ }
251
+ return {
252
+ valid: errors.length === 0,
253
+ errors,
254
+ };
255
+ });
256
+ }
257
+ /**
258
+ * 读取文件尾部
259
+ */
260
+ readFileTail(filePath, size) {
261
+ return __awaiter(this, void 0, void 0, function* () {
262
+ const stats = yield fs.stat(filePath);
263
+ const start = Math.max(0, stats.size - size);
264
+ const actualSize = Math.min(size, stats.size);
265
+ const buffer = Buffer.alloc(actualSize);
266
+ const fileHandle = yield fs.open(filePath, 'r');
267
+ try {
268
+ yield fileHandle.read(buffer, 0, actualSize, start);
269
+ return buffer;
270
+ }
271
+ finally {
272
+ yield fileHandle.close();
273
+ }
274
+ });
275
+ }
276
+ /**
277
+ * 计算签名置信度
278
+ */
279
+ calculateSignatureConfidence(signature) {
280
+ const bytes = typeof signature.bytes === 'string'
281
+ ? signature.bytes.length
282
+ : signature.bytes.length;
283
+ // 签名越长,置信度越高
284
+ let confidence = Math.min(bytes / 8, 1);
285
+ // 精确匹配提高置信度
286
+ if (signature.exact !== false) {
287
+ confidence *= 1.2;
288
+ }
289
+ // 有掩码降低置信度
290
+ if (signature.mask) {
291
+ confidence *= 0.9;
292
+ }
293
+ return Math.min(confidence, 1);
294
+ }
295
+ /**
296
+ * 计算总体置信度
297
+ */
298
+ calculateConfidence(buffer, detected) {
299
+ if (detected.length === 0) {
300
+ return 0;
301
+ }
302
+ // 取最高置信度
303
+ return detected[0].confidence;
304
+ }
305
+ /**
306
+ * 获取验证错误信息
307
+ */
308
+ getValidationError(detected, expected) {
309
+ if (detected.length === 0) {
310
+ return ['Unable to detect file type'];
311
+ }
312
+ const detectedTypes = detected.map((d) => d.type).join(', ');
313
+ return [
314
+ `File signature mismatch. Expected: ${expected.join(', ')}, Detected: ${detectedTypes}`,
315
+ ];
316
+ }
317
+ /**
318
+ * 初始化默认签名
319
+ */
320
+ initializeDefaultSignatures() {
321
+ // 图片格式
322
+ this.defaultSignatures.set('image/jpeg', [
323
+ { bytes: [0xff, 0xd8, 0xff], offset: 0 },
324
+ ]);
325
+ this.defaultSignatures.set('image/png', [
326
+ { bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], offset: 0 },
327
+ ]);
328
+ this.defaultSignatures.set('image/gif', [
329
+ { bytes: 'GIF87a', offset: 0 },
330
+ { bytes: 'GIF89a', offset: 0 },
331
+ ]);
332
+ this.defaultSignatures.set('image/webp', [{ bytes: 'RIFF', offset: 0 }]);
333
+ this.defaultSignatures.set('image/bmp', [
334
+ { bytes: [0x42, 0x4d], offset: 0 },
335
+ ]);
336
+ // PDF
337
+ this.defaultSignatures.set('application/pdf', [
338
+ { bytes: '%PDF-', exact: false, offset: 0 },
339
+ ]);
340
+ // ZIP 容器(DOCX, XLSX 等)
341
+ this.defaultSignatures.set('application/zip', [
342
+ { bytes: [0x50, 0x4b, 0x03, 0x04], offset: 0 },
343
+ { bytes: [0x50, 0x4b, 0x05, 0x06], offset: 0 },
344
+ { bytes: [0x50, 0x4b, 0x07, 0x08], offset: 0 },
345
+ ]);
346
+ // Office 文档 (旧格式)
347
+ this.defaultSignatures.set('application/msword', [
348
+ { bytes: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1], offset: 0 },
349
+ ]);
350
+ // 可执行文件 (用于检测)
351
+ this.defaultSignatures.set('application/x-executable', [
352
+ { bytes: [0x4d, 0x5a], offset: 0 }, // PE (Windows)
353
+ { bytes: [0x7f, 0x45, 0x4c, 0x46], offset: 0 }, // ELF (Linux)
354
+ { bytes: [0xfe, 0xed, 0xfa, 0xce], offset: 0 }, // Mach-O (macOS)
355
+ { bytes: [0xfe, 0xed, 0xfa, 0xcf], offset: 0 }, // Mach-O 64-bit
356
+ ]);
357
+ // 压缩格式
358
+ this.defaultSignatures.set('application/x-rar', [
359
+ { bytes: 'Rar!', offset: 0 },
360
+ ]);
361
+ this.defaultSignatures.set('application/x-7z-compressed', [
362
+ { bytes: [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c], offset: 0 },
363
+ ]);
364
+ this.defaultSignatures.set('application/gzip', [
365
+ { bytes: [0x1f, 0x8b], offset: 0 },
366
+ ]);
367
+ this.logger.log(`Initialized ${this.defaultSignatures.size} default file signatures`);
368
+ }
369
+ };
370
+ exports.FileSignatureValidator = FileSignatureValidator;
371
+ exports.FileSignatureValidator = FileSignatureValidator = FileSignatureValidator_1 = __decorate([
372
+ (0, common_1.Injectable)(),
373
+ __param(1, (0, common_1.Optional)()),
374
+ __param(1, (0, common_1.Inject)('CUSTOM_FILE_TYPES')),
375
+ __metadata("design:paramtypes", [mime_registry_service_1.MimeRegistryService, Object])
376
+ ], FileSignatureValidator);
@@ -0,0 +1,190 @@
1
+ import { Repository } from 'typeorm';
2
+ import { FileEntity } from '../entities/file.entity';
3
+ import { FileMetadataEntity } from '../entities/file-metadata.entity';
4
+ import { CreateFileDto } from '../dto/create-file.dto';
5
+ import { UpdateFileDto } from '../dto/update-file.dto';
6
+ import { FindFilesDto } from '../dto/find-files.dto';
7
+ import { PaginationDto } from '../dto/pagination.dto';
8
+ import { IStorageProvider } from '../interfaces/storage-provider.interface';
9
+ import { FileBuffer } from '../interfaces/file-buffer.interface';
10
+ import { Readable } from 'stream';
11
+ /**
12
+ * 存储统计信息
13
+ */
14
+ export interface StorageStats {
15
+ totalFiles: number;
16
+ totalSize: number;
17
+ providers: Record<string, {
18
+ count: number;
19
+ size: number;
20
+ }>;
21
+ }
22
+ /**
23
+ * 文件查询结果
24
+ */
25
+ export interface FileQueryResult {
26
+ files: FileEntity[];
27
+ total: number;
28
+ page: number;
29
+ limit: number;
30
+ totalPages: number;
31
+ }
32
+ /**
33
+ * 文件管理服务
34
+ */
35
+ export declare class FileService {
36
+ private readonly fileRepository;
37
+ private readonly metadataRepository;
38
+ private readonly storageProvider?;
39
+ private readonly localStorageProvider?;
40
+ private readonly s3StorageProvider?;
41
+ private readonly logger;
42
+ constructor(fileRepository: Repository<FileEntity>, metadataRepository: Repository<FileMetadataEntity>, storageProvider?: IStorageProvider, localStorageProvider?: IStorageProvider, s3StorageProvider?: IStorageProvider);
43
+ /**
44
+ * 创建文件记录
45
+ */
46
+ create(createFileDto: CreateFileDto): Promise<FileEntity>;
47
+ /**
48
+ * 根据ID查找文件
49
+ */
50
+ findById(id: string, includeDeleted?: boolean): Promise<FileEntity | null>;
51
+ /**
52
+ * 根据uploadId查找文件
53
+ */
54
+ findByUploadId(uploadId: string): Promise<FileEntity[]>;
55
+ /**
56
+ * 查找文件列表
57
+ */
58
+ findFiles(query: FindFilesDto, pagination: PaginationDto): Promise<FileQueryResult>;
59
+ /**
60
+ * 更新文件记录
61
+ */
62
+ update(id: string, updateFileDto: UpdateFileDto): Promise<FileEntity>;
63
+ /**
64
+ * 软删除文件
65
+ * @param id 文件ID
66
+ * @param deletePhysicalFile 是否同时删除物理文件,默认 false
67
+ */
68
+ softDelete(id: string, deletePhysicalFile?: boolean): Promise<void>;
69
+ /**
70
+ * 硬删除文件
71
+ * @param id 文件ID
72
+ * @param deletePhysicalFile 是否同时删除物理文件,默认 true
73
+ */
74
+ hardDelete(id: string, deletePhysicalFile?: boolean): Promise<void>;
75
+ /**
76
+ * 添加元数据
77
+ */
78
+ addMetadata(fileId: string, key: string, value: any, isPublic?: boolean): Promise<FileMetadataEntity>;
79
+ /**
80
+ * 获取元数据
81
+ */
82
+ getMetadata(fileId: string, keys?: string[]): Promise<Map<string, any>>;
83
+ /**
84
+ * 获取存储统计
85
+ */
86
+ getStorageStats(userId?: string): Promise<StorageStats>;
87
+ /**
88
+ * 更新处理状态
89
+ */
90
+ updateProcessingStatus(fileId: string, processor: string, status: 'pending' | 'completed' | 'failed', result?: any, error?: string): Promise<void>;
91
+ /**
92
+ * 清理临时文件(定时任务)
93
+ */
94
+ cleanupTempFiles(olderThanHours?: number): Promise<{
95
+ deleted: number;
96
+ }>;
97
+ /**
98
+ * 获取文件内容(二进制)
99
+ * 适用于服务间调用,获取完整文件内容
100
+ *
101
+ * @param fileId 文件ID
102
+ * @returns 文件缓冲区对象
103
+ * @throws NotFoundException 文件不存在
104
+ * @throws BadRequestException 文件未完成上传
105
+ */
106
+ getFileContent(fileId: string): Promise<FileBuffer>;
107
+ /**
108
+ * 获取文件流(用于大文件)
109
+ * 适用于流式处理,避免一次性加载到内存
110
+ * 注意:仅支持本地存储,对象存储请使用 getSignedUrl
111
+ *
112
+ * @param fileId 文件ID
113
+ * @returns 可读流
114
+ * @throws NotFoundException 文件不存在或物理文件缺失
115
+ * @throws BadRequestException 文件未完成或存储类型不支持
116
+ */
117
+ getFileStream(fileId: string): Promise<Readable>;
118
+ /**
119
+ * 获取预签名 URL(对象存储)
120
+ * 适用于客户端直接下载,或临时授权访问
121
+ *
122
+ * @param fileId 文件ID
123
+ * @param expiresIn 过期时间(秒),默认 3600 秒(1小时)
124
+ * @returns 预签名 URL
125
+ * @throws NotFoundException 文件不存在
126
+ */
127
+ getSignedUrl(fileId: string, expiresIn?: number): Promise<string>;
128
+ /**
129
+ * 批量获取文件内容
130
+ * 适用于需要同时处理多个文件的场景
131
+ * 失败的文件会被跳过,不会中断整个流程
132
+ *
133
+ * @param fileIds 文件ID列表
134
+ * @returns Map<文件ID, 文件内容>
135
+ */
136
+ getFilesContent(fileIds: string[]): Promise<Map<string, FileBuffer>>;
137
+ /**
138
+ * 获取文件访问 URL
139
+ * 根据存储类型返回不同的 URL
140
+ *
141
+ * @param fileId 文件ID
142
+ * @param baseUrl 基础 URL(可选,用于本地存储)
143
+ * @returns 文件访问 URL
144
+ */
145
+ getFileUrl(fileId: string, baseUrl?: string): Promise<string>;
146
+ /**
147
+ * 批量删除文件
148
+ * @param fileIds 文件ID列表
149
+ * @param deletePhysicalFile 是否同时删除物理文件,默认 true
150
+ * @returns 删除结果统计
151
+ */
152
+ batchDelete(fileIds: string[], deletePhysicalFile?: boolean): Promise<{
153
+ success: string[];
154
+ failed: Array<{
155
+ id: string;
156
+ error: string;
157
+ }>;
158
+ }>;
159
+ /**
160
+ * 复制文件(创建新记录)
161
+ * @param sourceFileId 源文件ID
162
+ * @param userId 新文件的用户ID(可选)
163
+ * @returns 新的文件实体
164
+ */
165
+ copyFile(sourceFileId: string, userId?: string): Promise<FileEntity>;
166
+ /**
167
+ * 移动文件到新路径
168
+ * @param fileId 文件ID
169
+ * @param newPath 新路径
170
+ * @returns 更新后的文件实体
171
+ */
172
+ moveFile(fileId: string, newPath: string): Promise<FileEntity>;
173
+ /**
174
+ * 删除物理文件
175
+ * @private
176
+ */
177
+ private deletePhysicalFile;
178
+ /**
179
+ * 根据存储类型获取存储提供者
180
+ *
181
+ * @param storageType 存储类型
182
+ * @returns 存储提供者实例
183
+ * @throws Error 存储提供者未配置或不支持
184
+ */
185
+ private getStorageProvider;
186
+ /**
187
+ * 转义 LIKE 操作符中的特殊字符
188
+ */
189
+ private escapeLikeString;
190
+ }