sa2kit 1.6.44 → 1.6.45

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 (267) hide show
  1. package/dist/AliyunOSSProvider-2FARPAQD.js +15 -0
  2. package/dist/{AliyunOSSProvider-HCNGDJL7.js.map → AliyunOSSProvider-2FARPAQD.js.map} +1 -1
  3. package/dist/AliyunOSSProvider-UMVGVBDJ.mjs +9 -0
  4. package/dist/{AliyunOSSProvider-4W47OFEK.mjs.map → AliyunOSSProvider-UMVGVBDJ.mjs.map} +1 -1
  5. package/dist/CollisionBalls-BpHufX3H.d.mts +41 -0
  6. package/dist/CollisionBalls-BpHufX3H.d.ts +41 -0
  7. package/dist/ConfigService-QR67WYNK.mjs +4 -0
  8. package/dist/{ConfigService-V6ZK273Z.mjs.map → ConfigService-QR67WYNK.mjs.map} +1 -1
  9. package/dist/{ConfigService-3DIC6C3Q.js → ConfigService-UYC6ZTCM.js} +3 -3
  10. package/dist/{ConfigService-3DIC6C3Q.js.map → ConfigService-UYC6ZTCM.js.map} +1 -1
  11. package/dist/GenericOrderManager-e4WizpNf.d.mts +28 -0
  12. package/dist/GenericOrderManager-e4WizpNf.d.ts +28 -0
  13. package/dist/LocalStorageProvider-JQF5WK5H.js +15 -0
  14. package/dist/{LocalStorageProvider-PP7MA5OT.js.map → LocalStorageProvider-JQF5WK5H.js.map} +1 -1
  15. package/dist/LocalStorageProvider-PYOHETJV.mjs +9 -0
  16. package/dist/{LocalStorageProvider-3RVPCQB3.mjs.map → LocalStorageProvider-PYOHETJV.mjs.map} +1 -1
  17. package/dist/PMXParser-RLBDO7YK.mjs +4 -0
  18. package/dist/{PMXParser-RNVQL76A.mjs.map → PMXParser-RLBDO7YK.mjs.map} +1 -1
  19. package/dist/{PMXParser-2VTA737I.js → PMXParser-XHNO2KNI.js} +3 -3
  20. package/dist/{PMXParser-2VTA737I.js.map → PMXParser-XHNO2KNI.js.map} +1 -1
  21. package/dist/UniversalFileService-RBV6EN5J.js +15 -0
  22. package/dist/UniversalFileService-RBV6EN5J.js.map +1 -0
  23. package/dist/UniversalFileService-TNYKO6JN.mjs +9 -0
  24. package/dist/UniversalFileService-TNYKO6JN.mjs.map +1 -0
  25. package/dist/analytics/index.js +1 -1
  26. package/dist/analytics/index.mjs +1 -1
  27. package/dist/analytics/server/index.js +1 -1
  28. package/dist/analytics/server/index.mjs +1 -1
  29. package/dist/api/index.js +1 -1
  30. package/dist/api/index.mjs +1 -1
  31. package/dist/audioDetection/index.js +1 -1
  32. package/dist/audioDetection/index.mjs +1 -1
  33. package/dist/auth/client/index.js +1 -1
  34. package/dist/auth/client/index.mjs +1 -1
  35. package/dist/auth/components/index.js +1 -1
  36. package/dist/auth/components/index.mjs +1 -1
  37. package/dist/auth/hooks/index.js +1 -1
  38. package/dist/auth/hooks/index.mjs +1 -1
  39. package/dist/auth/index.js +1 -1
  40. package/dist/auth/index.mjs +1 -1
  41. package/dist/auth/middleware/index.js +1 -1
  42. package/dist/auth/middleware/index.mjs +1 -1
  43. package/dist/auth/routes/index.js +1 -1
  44. package/dist/auth/routes/index.mjs +1 -1
  45. package/dist/auth/schema/index.js +1 -1
  46. package/dist/auth/schema/index.mjs +1 -1
  47. package/dist/auth/services/index.js +1 -1
  48. package/dist/auth/services/index.mjs +1 -1
  49. package/dist/calendar/index.js +12 -12
  50. package/dist/calendar/index.mjs +5 -5
  51. package/dist/calendar/routes/index.js +1 -1
  52. package/dist/calendar/routes/index.mjs +1 -1
  53. package/dist/calendar/server.js +1 -1
  54. package/dist/calendar/server.mjs +1 -1
  55. package/dist/chunk-2PS5PIXV.mjs +443 -0
  56. package/dist/chunk-2PS5PIXV.mjs.map +1 -0
  57. package/dist/chunk-35CXIK5Y.js +277 -0
  58. package/dist/chunk-35CXIK5Y.js.map +1 -0
  59. package/dist/{chunk-GMIUSZXC.mjs → chunk-3JW4X3AC.mjs} +3 -3
  60. package/dist/{chunk-GMIUSZXC.mjs.map → chunk-3JW4X3AC.mjs.map} +1 -1
  61. package/dist/chunk-3M6T5KVD.js +453 -0
  62. package/dist/chunk-3M6T5KVD.js.map +1 -0
  63. package/dist/chunk-3TNR6IMC.js +168 -0
  64. package/dist/chunk-3TNR6IMC.js.map +1 -0
  65. package/dist/chunk-4NFOSCM6.js +34 -0
  66. package/dist/chunk-4NFOSCM6.js.map +1 -0
  67. package/dist/{chunk-3NHAT7D4.mjs → chunk-4VJQZSPU.mjs} +4 -3
  68. package/dist/chunk-4VJQZSPU.mjs.map +1 -0
  69. package/dist/{chunk-SCDDMIF6.js → chunk-4XXIBWCO.js} +66 -66
  70. package/dist/{chunk-SCDDMIF6.js.map → chunk-4XXIBWCO.js.map} +1 -1
  71. package/dist/chunk-6AHYPPUP.js +344 -0
  72. package/dist/chunk-6AHYPPUP.js.map +1 -0
  73. package/dist/{chunk-EGJPS7OL.mjs → chunk-6BJ76BYC.mjs} +3 -3
  74. package/dist/{chunk-EGJPS7OL.mjs.map → chunk-6BJ76BYC.mjs.map} +1 -1
  75. package/dist/chunk-76V7EKBX.mjs +796 -0
  76. package/dist/chunk-76V7EKBX.mjs.map +1 -0
  77. package/dist/chunk-ACLOJXXE.js +195 -0
  78. package/dist/chunk-ACLOJXXE.js.map +1 -0
  79. package/dist/chunk-AEXPAH7Z.mjs +32 -0
  80. package/dist/chunk-AEXPAH7Z.mjs.map +1 -0
  81. package/dist/chunk-CFGX3EKK.js +560 -0
  82. package/dist/chunk-CFGX3EKK.js.map +1 -0
  83. package/dist/chunk-D2HXMGXS.js +46 -0
  84. package/dist/chunk-D2HXMGXS.js.map +1 -0
  85. package/dist/chunk-DM2GUVUH.js +1201 -0
  86. package/dist/chunk-DM2GUVUH.js.map +1 -0
  87. package/dist/{chunk-ZWQJSZEY.js → chunk-DVENFCQY.js} +5 -4
  88. package/dist/chunk-DVENFCQY.js.map +1 -0
  89. package/dist/chunk-EONPKLEJ.mjs +163 -0
  90. package/dist/chunk-EONPKLEJ.mjs.map +1 -0
  91. package/dist/{chunk-L47ZOYHL.js → chunk-EUIXQPPU.js} +4 -4
  92. package/dist/{chunk-L47ZOYHL.js.map → chunk-EUIXQPPU.js.map} +1 -1
  93. package/dist/chunk-K7WNCB4V.mjs +554 -0
  94. package/dist/chunk-K7WNCB4V.mjs.map +1 -0
  95. package/dist/chunk-L4ZYBFB2.mjs +44 -0
  96. package/dist/chunk-L4ZYBFB2.mjs.map +1 -0
  97. package/dist/chunk-M4HGHTIC.js +820 -0
  98. package/dist/chunk-M4HGHTIC.js.map +1 -0
  99. package/dist/{chunk-HHVDOIPV.js → chunk-MZKATHB7.js} +4 -4
  100. package/dist/{chunk-HHVDOIPV.js.map → chunk-MZKATHB7.js.map} +1 -1
  101. package/dist/{chunk-UKT3PLON.mjs → chunk-NXQVTAOP.mjs} +3 -3
  102. package/dist/{chunk-UKT3PLON.mjs.map → chunk-NXQVTAOP.mjs.map} +1 -1
  103. package/dist/chunk-OBIPI4GU.mjs +266 -0
  104. package/dist/chunk-OBIPI4GU.mjs.map +1 -0
  105. package/dist/chunk-PAX4S7QM.mjs +94 -0
  106. package/dist/chunk-PAX4S7QM.mjs.map +1 -0
  107. package/dist/chunk-PXWDQFWV.mjs +192 -0
  108. package/dist/chunk-PXWDQFWV.mjs.map +1 -0
  109. package/dist/chunk-QROLPPXP.mjs +5797 -0
  110. package/dist/chunk-QROLPPXP.mjs.map +1 -0
  111. package/dist/chunk-TGNUEULF.mjs +1158 -0
  112. package/dist/chunk-TGNUEULF.mjs.map +1 -0
  113. package/dist/chunk-VBQFVXOW.mjs +2772 -0
  114. package/dist/chunk-VBQFVXOW.mjs.map +1 -0
  115. package/dist/chunk-VLZ5N6XZ.js +5888 -0
  116. package/dist/chunk-VLZ5N6XZ.js.map +1 -0
  117. package/dist/chunk-VTGPHE4Z.mjs +322 -0
  118. package/dist/chunk-VTGPHE4Z.mjs.map +1 -0
  119. package/dist/chunk-WMJKH4XE.mjs +30 -0
  120. package/dist/{chunk-BJTO5JO5.mjs.map → chunk-WMJKH4XE.mjs.map} +1 -1
  121. package/dist/chunk-XYQMAF7H.js +96 -0
  122. package/dist/chunk-XYQMAF7H.js.map +1 -0
  123. package/dist/chunk-Z3G3IXEF.js +2814 -0
  124. package/dist/chunk-Z3G3IXEF.js.map +1 -0
  125. package/dist/chunk-Z6ZWNWWR.js +35 -0
  126. package/dist/{chunk-DGUM43GV.js.map → chunk-Z6ZWNWWR.js.map} +1 -1
  127. package/dist/components/index.d.mts +378 -0
  128. package/dist/components/index.d.ts +378 -0
  129. package/dist/components/index.js +414 -0
  130. package/dist/components/index.js.map +1 -0
  131. package/dist/components/index.mjs +9 -0
  132. package/dist/components/index.mjs.map +1 -0
  133. package/dist/config/index.js +1 -1
  134. package/dist/config/index.mjs +1 -1
  135. package/dist/config/server/index.js +1 -1
  136. package/dist/config/server/index.mjs +1 -1
  137. package/dist/fileService-O3W6YXCI.mjs +4 -0
  138. package/dist/fileService-O3W6YXCI.mjs.map +1 -0
  139. package/dist/fileService-YUDIYOAS.js +13 -0
  140. package/dist/fileService-YUDIYOAS.js.map +1 -0
  141. package/dist/i18n/index.js +7 -7
  142. package/dist/i18n/index.mjs +1 -1
  143. package/dist/imageCrop/index.js +1 -1
  144. package/dist/imageCrop/index.mjs +1 -1
  145. package/dist/index.d.mts +9 -580
  146. package/dist/index.d.ts +9 -580
  147. package/dist/index.js +297 -956
  148. package/dist/index.js.map +1 -1
  149. package/dist/index.mjs +103 -789
  150. package/dist/index.mjs.map +1 -1
  151. package/dist/logger/index.js +8 -8
  152. package/dist/logger/index.mjs +5 -2
  153. package/dist/mikuFireworks3D/index.js +1 -1
  154. package/dist/mikuFireworks3D/index.mjs +1 -1
  155. package/dist/mikuFireworks3D/server/index.js +1 -1
  156. package/dist/mikuFireworks3D/server/index.mjs +1 -1
  157. package/dist/mikuFusionGame/index.js +5 -5
  158. package/dist/mikuFusionGame/index.mjs +4 -4
  159. package/dist/mmd/admin/index.js +1 -1
  160. package/dist/mmd/admin/index.mjs +1 -1
  161. package/dist/mmd/index.js +2 -2
  162. package/dist/mmd/index.mjs +2 -2
  163. package/dist/mmd/server/index.js +1 -1
  164. package/dist/mmd/server/index.mjs +1 -1
  165. package/dist/music/index.js +1 -1
  166. package/dist/music/index.mjs +1 -1
  167. package/dist/music/server/index.js +1 -1
  168. package/dist/music/server/index.mjs +1 -1
  169. package/dist/navigation/index.d.mts +93 -0
  170. package/dist/navigation/index.d.ts +93 -0
  171. package/dist/navigation/index.js +29 -0
  172. package/dist/navigation/index.js.map +1 -0
  173. package/dist/navigation/index.mjs +4 -0
  174. package/dist/navigation/index.mjs.map +1 -0
  175. package/dist/popupConfig-BznThU1O.d.mts +330 -0
  176. package/dist/popupConfig-BznThU1O.d.ts +330 -0
  177. package/dist/portfolio/index.d.mts +57 -0
  178. package/dist/portfolio/index.d.ts +57 -0
  179. package/dist/portfolio/index.js +35 -0
  180. package/dist/portfolio/index.js.map +1 -0
  181. package/dist/portfolio/index.mjs +10 -0
  182. package/dist/portfolio/index.mjs.map +1 -0
  183. package/dist/request/index.js +1 -1
  184. package/dist/request/index.mjs +1 -1
  185. package/dist/showmasterpiece/index.d.mts +2524 -0
  186. package/dist/showmasterpiece/index.d.ts +2524 -0
  187. package/dist/showmasterpiece/index.js +9681 -0
  188. package/dist/showmasterpiece/index.js.map +1 -0
  189. package/dist/showmasterpiece/index.mjs +9631 -0
  190. package/dist/showmasterpiece/index.mjs.map +1 -0
  191. package/dist/showmasterpiece/migration/index.d.mts +120 -0
  192. package/dist/showmasterpiece/migration/index.d.ts +120 -0
  193. package/dist/showmasterpiece/migration/index.js +595 -0
  194. package/dist/showmasterpiece/migration/index.js.map +1 -0
  195. package/dist/showmasterpiece/migration/index.mjs +589 -0
  196. package/dist/showmasterpiece/migration/index.mjs.map +1 -0
  197. package/dist/showmasterpiece/scripts/index.d.mts +28 -0
  198. package/dist/showmasterpiece/scripts/index.d.ts +28 -0
  199. package/dist/showmasterpiece/scripts/index.js +327 -0
  200. package/dist/showmasterpiece/scripts/index.js.map +1 -0
  201. package/dist/showmasterpiece/scripts/index.mjs +325 -0
  202. package/dist/showmasterpiece/scripts/index.mjs.map +1 -0
  203. package/dist/showmasterpiece/server/index.d.mts +2698 -0
  204. package/dist/showmasterpiece/server/index.d.ts +2698 -0
  205. package/dist/showmasterpiece/server/index.js +179 -0
  206. package/dist/showmasterpiece/server/index.js.map +1 -0
  207. package/dist/showmasterpiece/server/index.mjs +6 -0
  208. package/dist/showmasterpiece/server/index.mjs.map +1 -0
  209. package/dist/storage/index.js +8 -8
  210. package/dist/storage/index.mjs +2 -2
  211. package/dist/testYourself/admin/index.js +1 -1
  212. package/dist/testYourself/admin/index.mjs +1 -1
  213. package/dist/testYourself/index.js +2 -2
  214. package/dist/testYourself/index.mjs +2 -2
  215. package/dist/testYourself/server/index.js +1 -1
  216. package/dist/testYourself/server/index.mjs +1 -1
  217. package/dist/universalExport/index.js +154 -1195
  218. package/dist/universalExport/index.js.map +1 -1
  219. package/dist/universalExport/index.mjs +2 -1157
  220. package/dist/universalExport/index.mjs.map +1 -1
  221. package/dist/universalExport/server/index.js +5 -3
  222. package/dist/universalExport/server/index.js.map +1 -1
  223. package/dist/universalExport/server/index.mjs +4 -2
  224. package/dist/universalExport/server/index.mjs.map +1 -1
  225. package/dist/universalFile/index.js +10 -7
  226. package/dist/universalFile/index.js.map +1 -1
  227. package/dist/universalFile/index.mjs +6 -2
  228. package/dist/universalFile/index.mjs.map +1 -1
  229. package/dist/universalFile/server/index.d.mts +592 -265
  230. package/dist/universalFile/server/index.d.ts +592 -265
  231. package/dist/universalFile/server/index.js +298 -5637
  232. package/dist/universalFile/server/index.js.map +1 -1
  233. package/dist/universalFile/server/index.mjs +8 -5559
  234. package/dist/universalFile/server/index.mjs.map +1 -1
  235. package/dist/utils/index.js +12 -12
  236. package/dist/utils/index.mjs +3 -3
  237. package/package.json +36 -1
  238. package/dist/AliyunOSSProvider-4W47OFEK.mjs +0 -6
  239. package/dist/AliyunOSSProvider-HCNGDJL7.js +0 -15
  240. package/dist/ConfigService-V6ZK273Z.mjs +0 -4
  241. package/dist/LocalStorageProvider-3RVPCQB3.mjs +0 -6
  242. package/dist/LocalStorageProvider-PP7MA5OT.js +0 -15
  243. package/dist/PMXParser-RNVQL76A.mjs +0 -4
  244. package/dist/chunk-25OFOKNF.js +0 -171
  245. package/dist/chunk-25OFOKNF.js.map +0 -1
  246. package/dist/chunk-3DXPQ4YV.mjs +0 -165
  247. package/dist/chunk-3DXPQ4YV.mjs.map +0 -1
  248. package/dist/chunk-3NHAT7D4.mjs.map +0 -1
  249. package/dist/chunk-BJTO5JO5.mjs +0 -10
  250. package/dist/chunk-CIVO4R6N.mjs +0 -37
  251. package/dist/chunk-CIVO4R6N.mjs.map +0 -1
  252. package/dist/chunk-DGUM43GV.js +0 -12
  253. package/dist/chunk-HDMIOOZY.mjs +0 -546
  254. package/dist/chunk-HDMIOOZY.mjs.map +0 -1
  255. package/dist/chunk-HJ6MH7J7.js +0 -552
  256. package/dist/chunk-HJ6MH7J7.js.map +0 -1
  257. package/dist/chunk-KH6RQ4J5.js +0 -28
  258. package/dist/chunk-KH6RQ4J5.js.map +0 -1
  259. package/dist/chunk-Q5EDCKQA.js +0 -336
  260. package/dist/chunk-Q5EDCKQA.js.map +0 -1
  261. package/dist/chunk-YOTQG4NP.mjs +0 -314
  262. package/dist/chunk-YOTQG4NP.mjs.map +0 -1
  263. package/dist/chunk-ZGVB35L2.mjs +0 -25
  264. package/dist/chunk-ZGVB35L2.mjs.map +0 -1
  265. package/dist/chunk-ZRAW3HXA.js +0 -43
  266. package/dist/chunk-ZRAW3HXA.js.map +0 -1
  267. package/dist/chunk-ZWQJSZEY.js.map +0 -1
@@ -0,0 +1,796 @@
1
+ import { init_logger, createLogger } from './chunk-PXWDQFWV.mjs';
2
+ import { init_types, StorageProviderError, FileUploadError, FileProcessingError } from './chunk-L4ZYBFB2.mjs';
3
+ import { __esm } from './chunk-WMJKH4XE.mjs';
4
+ import * as path2 from 'path';
5
+ import { EventEmitter } from 'events';
6
+ import { v4 } from 'uuid';
7
+ import { createHash } from 'crypto';
8
+
9
+ function getMimeType(filename) {
10
+ const ext = path2.extname(filename).toLowerCase();
11
+ return MIME_TYPES[ext] || "application/octet-stream";
12
+ }
13
+ var MIME_TYPES;
14
+ var init_mime = __esm({
15
+ "src/universalFile/server/utils/mime.ts"() {
16
+ MIME_TYPES = {
17
+ // 图片
18
+ ".jpg": "image/jpeg",
19
+ ".jpeg": "image/jpeg",
20
+ ".png": "image/png",
21
+ ".gif": "image/gif",
22
+ ".webp": "image/webp",
23
+ ".svg": "image/svg+xml",
24
+ ".bmp": "image/bmp",
25
+ ".ico": "image/x-icon",
26
+ // 视频
27
+ ".mp4": "video/mp4",
28
+ ".avi": "video/x-msvideo",
29
+ ".mov": "video/quicktime",
30
+ ".wmv": "video/x-ms-wmv",
31
+ ".flv": "video/x-flv",
32
+ ".mkv": "video/x-matroska",
33
+ ".webm": "video/webm",
34
+ // 音频
35
+ ".mp3": "audio/mpeg",
36
+ ".wav": "audio/wav",
37
+ ".ogg": "audio/ogg",
38
+ ".m4a": "audio/mp4",
39
+ ".flac": "audio/flac",
40
+ ".aac": "audio/aac",
41
+ // 文档
42
+ ".pdf": "application/pdf",
43
+ ".doc": "application/msword",
44
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
45
+ ".xls": "application/vnd.ms-excel",
46
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
47
+ ".ppt": "application/vnd.ms-powerpoint",
48
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
49
+ ".txt": "text/plain",
50
+ ".rtf": "application/rtf",
51
+ // 压缩文件
52
+ ".zip": "application/zip",
53
+ ".rar": "application/x-rar-compressed",
54
+ ".7z": "application/x-7z-compressed",
55
+ ".tar": "application/x-tar",
56
+ ".gz": "application/gzip",
57
+ // 代码文件
58
+ ".js": "application/javascript",
59
+ ".json": "application/json",
60
+ ".xml": "application/xml",
61
+ ".html": "text/html",
62
+ ".css": "text/css",
63
+ ".ts": "application/typescript",
64
+ // 其他
65
+ ".csv": "text/csv",
66
+ ".md": "text/markdown"
67
+ };
68
+ }
69
+ });
70
+ var logger, UniversalFileService;
71
+ var init_UniversalFileService = __esm({
72
+ "src/universalFile/server/UniversalFileService.ts"() {
73
+ init_logger();
74
+ init_mime();
75
+ init_types();
76
+ logger = createLogger("UniversalFileService");
77
+ UniversalFileService = class extends EventEmitter {
78
+ constructor(config) {
79
+ super();
80
+ this.storageProviders = /* @__PURE__ */ new Map();
81
+ this.cdnProviders = /* @__PURE__ */ new Map();
82
+ this.fileProcessors = /* @__PURE__ */ new Map();
83
+ this.uploadProgressMap = /* @__PURE__ */ new Map();
84
+ this.metadataCache = /* @__PURE__ */ new Map();
85
+ this.urlCache = /* @__PURE__ */ new Map();
86
+ this.processingQueue = [];
87
+ this.isProcessingQueueRunning = false;
88
+ this.config = config;
89
+ if (this.config.persistence?.enabled && this.config.persistence.repository) {
90
+ this.setupPersistenceListeners();
91
+ }
92
+ }
93
+ // ============= 持久化设置 =============
94
+ /**
95
+ * 设置数据库持久化监听器
96
+ *
97
+ * 当文件上传完成或删除时,自动触发数据库操作
98
+ */
99
+ setupPersistenceListeners() {
100
+ const { repository, autoPersist = true } = this.config.persistence;
101
+ if (!autoPersist) {
102
+ logger.info("\u2699\uFE0F [UniversalFileService] \u81EA\u52A8\u6301\u4E45\u5316\u5DF2\u7981\u7528");
103
+ return;
104
+ }
105
+ logger.info("\u2705 [UniversalFileService] \u5DF2\u542F\u7528\u6570\u636E\u5E93\u6301\u4E45\u5316\uFF0C\u81EA\u52A8\u76D1\u542C\u6587\u4EF6\u4E8B\u4EF6");
106
+ this.on("upload:complete", async (fileId, data) => {
107
+ try {
108
+ const metadata = data.metadata || data;
109
+ await repository.save(metadata);
110
+ logger.info("\u{1F4BE} [Persistence] \u6587\u4EF6\u5143\u6570\u636E\u5DF2\u81EA\u52A8\u4FDD\u5B58: " + fileId);
111
+ } catch (error) {
112
+ logger.error("\u274C [Persistence] \u4FDD\u5B58\u5931\u8D25: " + fileId, error);
113
+ }
114
+ });
115
+ this.on("file:deleted", async (fileId) => {
116
+ try {
117
+ await repository.delete(fileId);
118
+ logger.info("\u{1F5D1}\uFE0F [Persistence] \u6587\u4EF6\u5143\u6570\u636E\u5DF2\u81EA\u52A8\u5220\u9664: " + fileId);
119
+ } catch (error) {
120
+ logger.error("\u274C [Persistence] \u5220\u9664\u5931\u8D25: " + fileId, error);
121
+ }
122
+ });
123
+ this.on("files:batch-deleted", async (fileIds) => {
124
+ try {
125
+ await repository.batchDelete(fileIds);
126
+ logger.info("\u{1F5D1}\uFE0F [Persistence] \u6279\u91CF\u5220\u9664\u5143\u6570\u636E: " + fileIds.length + " \u4E2A\u6587\u4EF6");
127
+ } catch (error) {
128
+ logger.error(`\u274C [Persistence] \u6279\u91CF\u5220\u9664\u5931\u8D25`, error);
129
+ }
130
+ });
131
+ }
132
+ // ============= 初始化方法 =============
133
+ /**
134
+ * 初始化文件服务
135
+ */
136
+ async initialize() {
137
+ logger.info("\u{1F680} [UniversalFileService] \u5F00\u59CB\u521D\u59CB\u5316\u6587\u4EF6\u670D\u52A1...");
138
+ try {
139
+ await this.validateConfiguration();
140
+ await this.initializeStorageProviders();
141
+ await this.initializeCDNProviders();
142
+ await this.initializeFileProcessors();
143
+ logger.info("\u2705 [UniversalFileService] \u6587\u4EF6\u670D\u52A1\u521D\u59CB\u5316\u5B8C\u6210");
144
+ } catch (error) {
145
+ console.error("\u274C [UniversalFileService] \u6587\u4EF6\u670D\u52A1\u521D\u59CB\u5316\u5931\u8D25:", error);
146
+ throw error;
147
+ }
148
+ }
149
+ /**
150
+ * 重新初始化存储提供者(支持配置热更新)
151
+ */
152
+ async reinitializeStorageProviders() {
153
+ logger.info("\u{1F504} [UniversalFileService] \u91CD\u65B0\u521D\u59CB\u5316\u5B58\u50A8\u63D0\u4F9B\u8005...");
154
+ try {
155
+ for (const [type, provider] of this.storageProviders) {
156
+ if ("reinitialize" in provider) {
157
+ try {
158
+ logger.info("\u{1F504} [UniversalFileService] \u91CD\u65B0\u521D\u59CB\u5316\u5B58\u50A8\u63D0\u4F9B\u8005: " + type + "...");
159
+ const config = this.config.storageProviders?.[type];
160
+ if (config) {
161
+ await provider.reinitialize(config);
162
+ logger.info("\u2705 [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u91CD\u65B0\u521D\u59CB\u5316\u5B8C\u6210: " + type);
163
+ } else {
164
+ logger.warn("\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u914D\u7F6E\u4E0D\u5B58\u5728: " + type);
165
+ }
166
+ } catch (error) {
167
+ logger.error("\u274C [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u91CD\u65B0\u521D\u59CB\u5316\u5931\u8D25: " + type, error);
168
+ }
169
+ }
170
+ }
171
+ logger.info("\u2705 [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u91CD\u65B0\u521D\u59CB\u5316\u5B8C\u6210");
172
+ } catch (error) {
173
+ console.error("\u274C [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u91CD\u65B0\u521D\u59CB\u5316\u5931\u8D25:", error);
174
+ throw error;
175
+ }
176
+ }
177
+ /**
178
+ * 注册存储提供者
179
+ */
180
+ registerStorageProvider(provider) {
181
+ this.storageProviders.set(provider.type, provider);
182
+ logger.info("\u{1F4E6} [UniversalFileService] \u6CE8\u518C\u5B58\u50A8\u63D0\u4F9B\u8005: " + provider.type);
183
+ }
184
+ /**
185
+ * 注册CDN提供者
186
+ */
187
+ registerCDNProvider(provider) {
188
+ this.cdnProviders.set(provider.type, provider);
189
+ logger.info("\u{1F310} [UniversalFileService] \u6CE8\u518CCDN\u63D0\u4F9B\u8005: " + provider.type);
190
+ }
191
+ /**
192
+ * 注册文件处理器
193
+ */
194
+ registerFileProcessor(processor) {
195
+ this.fileProcessors.set(processor.type, processor);
196
+ logger.info("\u2699\uFE0F [UniversalFileService] \u6CE8\u518C\u6587\u4EF6\u5904\u7406\u5668: " + processor.type);
197
+ }
198
+ // ============= 核心文件操作方法 =============
199
+ /**
200
+ * 上传文件
201
+ */
202
+ async uploadFile(fileInfo, storageType, onProgress) {
203
+ const fileId = v4();
204
+ const startTime = Date.now();
205
+ logger.info("\u{1F4E4} [UniversalFileService] \u5F00\u59CB\u4E0A\u4F20\u6587\u4EF6: " + fileInfo.file.name + ", ID: " + fileId);
206
+ try {
207
+ await this.validateFile(fileInfo.file);
208
+ const progress = {
209
+ fileId,
210
+ status: "pending",
211
+ progress: 0,
212
+ uploadedBytes: 0,
213
+ totalBytes: fileInfo.file.size,
214
+ speed: 0,
215
+ remainingTime: 0
216
+ };
217
+ this.uploadProgressMap.set(fileId, progress);
218
+ this.emitFileEvent("upload:start", fileId, { fileName: fileInfo.file.name });
219
+ const metadata = await this.generateFileMetadata(fileId, fileInfo);
220
+ const selectedStorageType = storageType || this.config.defaultStorage;
221
+ let storageProvider = this.storageProviders.get(selectedStorageType);
222
+ if (!storageProvider) {
223
+ logger.info(
224
+ "\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005 " + selectedStorageType + " \u4E0D\u53EF\u7528\uFF0C\u5C1D\u8BD5\u4F7F\u7528OSS"
225
+ );
226
+ storageProvider = this.storageProviders.get("aliyun-oss");
227
+ if (!storageProvider) {
228
+ logger.info(`\u26A0\uFE0F [UniversalFileService] OSS\u4E0D\u53EF\u7528\uFF0C\u56DE\u9000\u5230\u672C\u5730\u5B58\u50A8`);
229
+ storageProvider = this.storageProviders.get("local");
230
+ }
231
+ }
232
+ if (!storageProvider) {
233
+ throw new StorageProviderError(`\u6CA1\u6709\u53EF\u7528\u7684\u5B58\u50A8\u63D0\u4F9B\u8005`);
234
+ }
235
+ const storagePath = this.generateStoragePath(metadata);
236
+ progress.status = "uploading";
237
+ progress.progress = 10;
238
+ this.uploadProgressMap.set(fileId, progress);
239
+ onProgress?.(progress);
240
+ this.emitFileEvent("upload:progress", fileId, { progress: progress.progress });
241
+ const uploadResult = await storageProvider.upload(fileInfo, storagePath);
242
+ if (!uploadResult.success) {
243
+ throw new FileUploadError("\u4E0A\u4F20\u5931\u8D25: " + uploadResult.error);
244
+ }
245
+ metadata.storagePath = uploadResult.path || storagePath;
246
+ metadata.storageProvider = selectedStorageType;
247
+ if (this.config.defaultCDN !== "none") {
248
+ const cdnProvider = this.cdnProviders.get(this.config.defaultCDN);
249
+ if (cdnProvider && uploadResult.url) {
250
+ metadata.cdnUrl = await cdnProvider.generateUrl(uploadResult.url);
251
+ }
252
+ }
253
+ progress.status = fileInfo.needsProcessing ? "processing" : "completed";
254
+ progress.progress = fileInfo.needsProcessing ? 70 : 100;
255
+ this.uploadProgressMap.set(fileId, progress);
256
+ onProgress?.(progress);
257
+ if (fileInfo.needsProcessing && fileInfo.processingOptions) {
258
+ await this.queueFileProcessing(metadata, fileInfo.processingOptions);
259
+ }
260
+ this.cacheMetadata(metadata);
261
+ progress.status = "completed";
262
+ progress.progress = 100;
263
+ this.uploadProgressMap.set(fileId, progress);
264
+ onProgress?.(progress);
265
+ const uploadTime = Date.now() - startTime;
266
+ logger.info("\u2705 [UniversalFileService] \u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210: " + fileId + ", \u8017\u65F6: " + uploadTime + "ms");
267
+ this.emitFileEvent("upload:complete", fileId, {
268
+ fileName: fileInfo.file.name,
269
+ size: fileInfo.file.size,
270
+ uploadTime
271
+ });
272
+ return metadata;
273
+ } catch (error) {
274
+ console.error("\u274C [UniversalFileService] \u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: " + fileId + ":", error);
275
+ const progress = this.uploadProgressMap.get(fileId);
276
+ if (progress) {
277
+ progress.status = "failed";
278
+ progress.error = error instanceof Error ? error.message : "\u4E0A\u4F20\u5931\u8D25";
279
+ this.uploadProgressMap.set(fileId, progress);
280
+ onProgress?.(progress);
281
+ }
282
+ this.emitFileEvent(
283
+ "upload:error",
284
+ fileId,
285
+ void 0,
286
+ error instanceof Error ? error.message : "\u4E0A\u4F20\u5931\u8D25"
287
+ );
288
+ throw error;
289
+ } finally {
290
+ setTimeout(
291
+ () => {
292
+ this.uploadProgressMap.delete(fileId);
293
+ },
294
+ 5 * 60 * 1e3
295
+ );
296
+ }
297
+ }
298
+ /**
299
+ * 下载文件
300
+ */
301
+ async downloadFile(fileId, userId) {
302
+ logger.info("\u{1F4E5} [UniversalFileService] \u5F00\u59CB\u4E0B\u8F7D\u6587\u4EF6: " + fileId);
303
+ try {
304
+ this.emitFileEvent("download:start", fileId);
305
+ const metadata = await this.getFileMetadata(fileId);
306
+ if (!metadata) {
307
+ throw new FileUploadError("\u6587\u4EF6\u4E0D\u5B58\u5728: " + fileId);
308
+ }
309
+ await this.checkFileAccess(metadata, userId);
310
+ const storageProvider = this.storageProviders.get(metadata.storageProvider);
311
+ if (!storageProvider) {
312
+ throw new StorageProviderError("\u5B58\u50A8\u63D0\u4F9B\u8005\u4E0D\u5B58\u5728: " + metadata.storageProvider);
313
+ }
314
+ const fileBuffer = await storageProvider.download(metadata.storagePath);
315
+ if (this.config.persistence?.enabled) {
316
+ await this.updateAccessStats(fileId);
317
+ }
318
+ logger.info("\u2705 [UniversalFileService] \u6587\u4EF6\u4E0B\u8F7D\u5B8C\u6210: " + fileId);
319
+ this.emitFileEvent("download:complete", fileId, { size: fileBuffer.length });
320
+ return fileBuffer;
321
+ } catch (error) {
322
+ console.error("\u274C [UniversalFileService] \u6587\u4EF6\u4E0B\u8F7D\u5931\u8D25: " + fileId + ":", error);
323
+ this.emitFileEvent("download:error", fileId, void 0, error instanceof Error ? error.message : "\u4E0B\u8F7D\u5931\u8D25");
324
+ throw error;
325
+ }
326
+ }
327
+ /**
328
+ * 删除文件
329
+ */
330
+ async deleteFile(fileId, userId) {
331
+ logger.info("\u{1F5D1}\uFE0F [UniversalFileService] \u5F00\u59CB\u5220\u9664\u6587\u4EF6: " + fileId);
332
+ try {
333
+ const metadata = await this.getFileMetadata(fileId);
334
+ if (!metadata) {
335
+ throw new FileUploadError("\u6587\u4EF6\u4E0D\u5B58\u5728: " + fileId);
336
+ }
337
+ await this.checkFileDeleteAccess(metadata, userId);
338
+ const storageProvider = this.storageProviders.get(metadata.storageProvider);
339
+ if (!storageProvider) {
340
+ throw new StorageProviderError("\u5B58\u50A8\u63D0\u4F9B\u8005\u4E0D\u5B58\u5728: " + metadata.storageProvider);
341
+ }
342
+ const deleteResult = await storageProvider.delete(metadata.storagePath);
343
+ if (!deleteResult.success) {
344
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u6587\u4EF6\u5220\u9664\u5931\u8D25: " + deleteResult.error);
345
+ }
346
+ if (this.config.persistence?.enabled) {
347
+ await this.deleteFileMetadata(fileId);
348
+ }
349
+ this.clearMetadataCache(fileId);
350
+ logger.info("\u2705 [UniversalFileService] \u6587\u4EF6\u5220\u9664\u5B8C\u6210: " + fileId);
351
+ this.emitFileEvent("delete:complete", fileId);
352
+ this.emit("file:deleted", fileId);
353
+ } catch (error) {
354
+ console.error("\u274C [UniversalFileService] \u6587\u4EF6\u5220\u9664\u5931\u8D25: " + fileId + ":", error);
355
+ this.emitFileEvent("delete:error", fileId, void 0, error instanceof Error ? error.message : "\u5220\u9664\u5931\u8D25");
356
+ throw error;
357
+ }
358
+ }
359
+ /**
360
+ * 获取文件访问URL
361
+ */
362
+ async getFileUrl(fileId, userId, expiresIn) {
363
+ const cacheKey = fileId + "_" + (userId || "public") + "_" + (expiresIn || 0);
364
+ const cached = this.urlCache.get(cacheKey);
365
+ if (cached && cached.expires > Date.now()) {
366
+ return cached.url;
367
+ }
368
+ const metadata = await this.getFileMetadata(fileId);
369
+ if (!metadata) {
370
+ throw new FileUploadError("\u6587\u4EF6\u4E0D\u5B58\u5728: " + fileId);
371
+ }
372
+ await this.checkFileAccess(metadata, userId);
373
+ let url;
374
+ if (metadata.cdnUrl) {
375
+ url = metadata.cdnUrl;
376
+ } else {
377
+ const storageProvider = this.storageProviders.get(metadata.storageProvider);
378
+ if (!storageProvider) {
379
+ throw new StorageProviderError("\u5B58\u50A8\u63D0\u4F9B\u8005\u4E0D\u5B58\u5728: " + metadata.storageProvider);
380
+ }
381
+ url = await storageProvider.getAccessUrl(metadata.storagePath, expiresIn);
382
+ }
383
+ const cacheExpires = Date.now() + (this.config.cache?.urlTTL || 1800) * 1e3;
384
+ this.urlCache.set(cacheKey, { url, expires: cacheExpires });
385
+ return url;
386
+ }
387
+ /**
388
+ * 获取文件元数据
389
+ */
390
+ async getFileMetadata(fileId) {
391
+ const cached = this.metadataCache.get(fileId);
392
+ if (cached && cached.expires > Date.now()) {
393
+ return cached.data;
394
+ }
395
+ if (this.config.persistence?.enabled && this.config.persistence.repository) {
396
+ try {
397
+ const metadata = await this.config.persistence.repository.get(fileId);
398
+ if (metadata) {
399
+ this.cacheMetadata(metadata);
400
+ }
401
+ return metadata;
402
+ } catch (error) {
403
+ console.error("\u274C [UniversalFileService] \u67E5\u8BE2\u6587\u4EF6\u5143\u6570\u636E\u5931\u8D25:", error);
404
+ return null;
405
+ }
406
+ }
407
+ logger.warn("\u26A0\uFE0F [UniversalFileService] \u6301\u4E45\u5316\u672A\u542F\u7528,\u65E0\u6CD5\u67E5\u8BE2\u6587\u4EF6\u5143\u6570\u636E: " + fileId);
408
+ return null;
409
+ }
410
+ /**
411
+ * 查询文件列表
412
+ */
413
+ async queryFiles(options) {
414
+ if (!this.config.persistence?.enabled || !this.config.persistence.repository) {
415
+ logger.warn("\u26A0\uFE0F [UniversalFileService] \u6301\u4E45\u5316\u672A\u542F\u7528,\u65E0\u6CD5\u67E5\u8BE2\u6587\u4EF6\u5217\u8868");
416
+ return {
417
+ items: [],
418
+ total: 0,
419
+ page: options.page || 1,
420
+ pageSize: options.pageSize || 20,
421
+ totalPages: 0,
422
+ hasNext: false,
423
+ hasPrev: false
424
+ };
425
+ }
426
+ try {
427
+ const result = await this.config.persistence.repository.query(options);
428
+ const hasNext = result.page < result.totalPages;
429
+ const hasPrev = result.page > 1;
430
+ return {
431
+ ...result,
432
+ hasNext,
433
+ hasPrev
434
+ };
435
+ } catch (error) {
436
+ console.error("\u274C [UniversalFileService] \u67E5\u8BE2\u6587\u4EF6\u5217\u8868\u5931\u8D25:", error);
437
+ throw error;
438
+ }
439
+ }
440
+ /**
441
+ * 批量删除文件
442
+ */
443
+ async batchDeleteFiles(fileIds, userId) {
444
+ const result = {
445
+ successCount: 0,
446
+ failureCount: 0,
447
+ failures: []
448
+ };
449
+ for (const fileId of fileIds) {
450
+ try {
451
+ await this.deleteFile(fileId, userId);
452
+ result.successCount++;
453
+ } catch (error) {
454
+ result.failureCount++;
455
+ result.failures.push({
456
+ fileId,
457
+ error: error instanceof Error ? error.message : "\u5220\u9664\u5931\u8D25"
458
+ });
459
+ }
460
+ }
461
+ if (result.successCount > 0) {
462
+ this.emit("files:batch-deleted", fileIds.filter((_, i) => i < result.successCount));
463
+ }
464
+ return result;
465
+ }
466
+ /**
467
+ * 获取上传进度
468
+ */
469
+ getUploadProgress(fileId) {
470
+ return this.uploadProgressMap.get(fileId);
471
+ }
472
+ // ============= 事件处理方法 =============
473
+ /**
474
+ * 监听文件事件
475
+ */
476
+ onFileEvent(eventType, listener) {
477
+ this.on(eventType, listener);
478
+ }
479
+ /**
480
+ * 移除文件事件监听器
481
+ */
482
+ offFileEvent(eventType, listener) {
483
+ this.off(eventType, listener);
484
+ }
485
+ // ============= 私有方法 =============
486
+ /**
487
+ * 验证配置是否完整
488
+ */
489
+ async validateConfiguration() {
490
+ logger.info("\u{1F50D} [UniversalFileService] \u9A8C\u8BC1\u914D\u7F6E\u6587\u4EF6...");
491
+ if (!this.config) {
492
+ throw new Error("\u6587\u4EF6\u670D\u52A1\u914D\u7F6E\u4E3A\u7A7A");
493
+ }
494
+ if (!this.config.storage) {
495
+ throw new Error("\u5B58\u50A8\u914D\u7F6E\u4E3A\u7A7A");
496
+ }
497
+ if (this.config.storage.type === "aliyun-oss") {
498
+ const ossConfig = this.config.storage;
499
+ if (!ossConfig.accessKeyId || !ossConfig.accessKeySecret || !ossConfig.bucket || !ossConfig.region) {
500
+ logger.warn("\u26A0\uFE0F [UniversalFileService] OSS\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF0C\u7B49\u5F85\u914D\u7F6E\u52A0\u8F7D...");
501
+ const maxRetries = 30;
502
+ const retryInterval = 1e3;
503
+ for (let i = 0; i < maxRetries; i++) {
504
+ await new Promise((resolve) => setTimeout(resolve, retryInterval));
505
+ const updatedConfig = this.config.storage;
506
+ if (updatedConfig.accessKeyId && updatedConfig.accessKeySecret && updatedConfig.bucket && updatedConfig.region) {
507
+ logger.info("\u2705 [UniversalFileService] OSS\u914D\u7F6E\u52A0\u8F7D\u5B8C\u6210");
508
+ break;
509
+ }
510
+ if (i === maxRetries - 1) {
511
+ throw new Error("OSS\u914D\u7F6E\u52A0\u8F7D\u8D85\u65F6\uFF1A\u7F3A\u5C11\u5FC5\u9700\u7684\u914D\u7F6E\u9879 (accessKeyId, accessKeySecret, bucket, region)");
512
+ }
513
+ logger.debug("\u7B49\u5F85OSS\u914D\u7F6E\u52A0\u8F7D\u4E2D... (" + (i + 1) + "/" + maxRetries + ")");
514
+ }
515
+ }
516
+ }
517
+ if (!this.config.defaultStorage) {
518
+ this.config.defaultStorage = this.config.storage.type;
519
+ }
520
+ logger.info("\u2705 [UniversalFileService] \u914D\u7F6E\u9A8C\u8BC1\u5B8C\u6210");
521
+ }
522
+ async initializeStorageProviders() {
523
+ logger.info("\u{1F4E6} [UniversalFileService] \u5F00\u59CB\u521D\u59CB\u5316\u5B58\u50A8\u63D0\u4F9B\u8005...");
524
+ if (this.storageProviders.size === 0) {
525
+ await this.registerDefaultStorageProviders();
526
+ }
527
+ if (this.config.storageProviders) {
528
+ for (const [type, config] of Object.entries(this.config.storageProviders)) {
529
+ if (config && config.enabled) {
530
+ const provider = this.storageProviders.get(type);
531
+ if (provider) {
532
+ try {
533
+ await provider.initialize(config);
534
+ logger.info("\u2705 [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u521D\u59CB\u5316\u5B8C\u6210: " + type);
535
+ } catch (error) {
536
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u521D\u59CB\u5316\u5931\u8D25: " + type + ":", error);
537
+ }
538
+ } else {
539
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u672A\u6CE8\u518C: " + type);
540
+ }
541
+ }
542
+ }
543
+ }
544
+ }
545
+ async registerDefaultStorageProviders() {
546
+ logger.info("\u{1F4E6} [UniversalFileService] \u6CE8\u518C\u9ED8\u8BA4\u5B58\u50A8\u63D0\u4F9B\u8005...");
547
+ try {
548
+ if (this.config.storage) {
549
+ if (this.config.storage.type === "aliyun-oss" && this.config.storage.enabled) {
550
+ const { AliyunOSSProvider } = await import('./AliyunOSSProvider-UMVGVBDJ.mjs');
551
+ const ossProvider = new AliyunOSSProvider();
552
+ this.registerStorageProvider(ossProvider);
553
+ logger.info("\u2705 [UniversalFileService] \u963F\u91CC\u4E91OSS\u63D0\u4F9B\u8005\u6CE8\u518C\u6210\u529F");
554
+ } else if (this.config.storage.type === "local" && this.config.storage.enabled) {
555
+ const { LocalStorageProvider } = await import('./LocalStorageProvider-PYOHETJV.mjs');
556
+ const localProvider = new LocalStorageProvider();
557
+ this.registerStorageProvider(localProvider);
558
+ logger.info("\u2705 [UniversalFileService] \u672C\u5730\u5B58\u50A8\u63D0\u4F9B\u8005\u6CE8\u518C\u6210\u529F");
559
+ }
560
+ }
561
+ if (this.storageProviders.size === 0) {
562
+ const { LocalStorageProvider } = await import('./LocalStorageProvider-PYOHETJV.mjs');
563
+ const fallbackProvider = new LocalStorageProvider();
564
+ this.registerStorageProvider(fallbackProvider);
565
+ logger.info("\u2705 [UniversalFileService] \u5DF2\u6CE8\u518C\u5907\u7528\u672C\u5730\u5B58\u50A8\u63D0\u4F9B\u8005");
566
+ }
567
+ } catch (error) {
568
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u6CE8\u518C\u9ED8\u8BA4\u5B58\u50A8\u63D0\u4F9B\u8005\u5931\u8D25:", error);
569
+ throw new Error("\u65E0\u6CD5\u521D\u59CB\u5316\u5B58\u50A8\u63D0\u4F9B\u8005");
570
+ }
571
+ }
572
+ async initializeCDNProviders() {
573
+ if (this.config.cdn && this.config.cdn.enabled) {
574
+ logger.info("\u2705 [UniversalFileService] CDN\u914D\u7F6E\u5DF2\u542F\u7528: " + this.config.cdn.type);
575
+ }
576
+ }
577
+ async initializeFileProcessors() {
578
+ for (const processor of Array.from(this.fileProcessors.values())) {
579
+ await processor.initialize();
580
+ logger.info("\u2705 [UniversalFileService] \u6587\u4EF6\u5904\u7406\u5668\u521D\u59CB\u5316\u5B8C\u6210: " + processor.type);
581
+ }
582
+ }
583
+ async validateFile(file) {
584
+ if (this.config.maxFileSize && file.size > this.config.maxFileSize) {
585
+ throw new FileUploadError("\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC7\u9650\u5236: " + file.size + " > " + this.config.maxFileSize);
586
+ }
587
+ const mimeType = file.type || getMimeType(file.name);
588
+ if (this.config.allowedMimeTypes && this.config.allowedMimeTypes.length > 0 && !this.config.allowedMimeTypes.includes(mimeType)) {
589
+ throw new FileUploadError("\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B: " + mimeType);
590
+ }
591
+ }
592
+ async generateFileMetadata(fileId, fileInfo) {
593
+ const now = /* @__PURE__ */ new Date();
594
+ const mimeType = fileInfo.file.type || getMimeType(fileInfo.file.name);
595
+ const extension = path2.extname(fileInfo.file.name).toLowerCase();
596
+ const hash = await this.generateFileHash(fileInfo.file);
597
+ return {
598
+ id: fileId,
599
+ originalName: fileInfo.file.name,
600
+ storageName: fileId + extension,
601
+ size: fileInfo.file.size,
602
+ mimeType,
603
+ extension,
604
+ hash,
605
+ uploadTime: now,
606
+ permission: fileInfo.permission || "public",
607
+ uploaderId: fileInfo.metadata?.uploadedBy || "system",
608
+ moduleId: fileInfo.moduleId,
609
+ businessId: fileInfo.businessId,
610
+ storageProvider: this.config.defaultStorage || "local",
611
+ storagePath: "",
612
+ accessCount: 0,
613
+ metadata: fileInfo.metadata || {}
614
+ };
615
+ }
616
+ generateStoragePath(metadata) {
617
+ const date = /* @__PURE__ */ new Date();
618
+ const year = date.getFullYear();
619
+ const month = String(date.getMonth() + 1).padStart(2, "0");
620
+ const day = String(date.getDate()).padStart(2, "0");
621
+ return metadata.moduleId + "/" + year + "/" + month + "/" + day + "/" + metadata.storageName;
622
+ }
623
+ async generateFileHash(file) {
624
+ const buffer = await file.arrayBuffer();
625
+ const hash = createHash("sha256");
626
+ hash.update(Buffer.from(buffer));
627
+ return hash.digest("hex");
628
+ }
629
+ async queueFileProcessing(metadata, options) {
630
+ if (!this.config.processors?.length || 0 > 0) {
631
+ return;
632
+ }
633
+ const processor = this.fileProcessors.get(options.type);
634
+ if (!processor) {
635
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u6587\u4EF6\u5904\u7406\u5668\u4E0D\u5B58\u5728: " + options.type);
636
+ return;
637
+ }
638
+ if (this.processingQueue.length >= 1e3) {
639
+ throw new FileProcessingError("\u5904\u7406\u961F\u5217\u5DF2\u6EE1");
640
+ }
641
+ this.processingQueue.push({
642
+ fileId: metadata.id,
643
+ processor,
644
+ inputPath: metadata.storagePath,
645
+ outputPath: this.generateProcessedPath(metadata, options),
646
+ options
647
+ });
648
+ if (!this.isProcessingQueueRunning) {
649
+ this.processFileQueue();
650
+ }
651
+ }
652
+ generateProcessedPath(metadata, options) {
653
+ const basePath = metadata.storagePath;
654
+ const extension = path2.extname(basePath);
655
+ const basename = basePath.replace(extension, "");
656
+ return basename + "_processed" + extension;
657
+ }
658
+ async processFileQueue() {
659
+ if (this.isProcessingQueueRunning || this.processingQueue.length === 0) {
660
+ return;
661
+ }
662
+ this.isProcessingQueueRunning = true;
663
+ while (this.processingQueue.length > 0) {
664
+ const task = this.processingQueue.shift();
665
+ if (!task) break;
666
+ try {
667
+ this.emitFileEvent("processing:start", task.fileId);
668
+ const result = await task.processor.process(task.inputPath, task.outputPath, task.options);
669
+ if (result.success) {
670
+ this.emitFileEvent("processing:complete", task.fileId, result);
671
+ } else {
672
+ this.emitFileEvent("processing:error", task.fileId, void 0, result.error);
673
+ }
674
+ } catch (error) {
675
+ console.error("\u274C [UniversalFileService] \u6587\u4EF6\u5904\u7406\u5931\u8D25: " + task.fileId + ":", error);
676
+ this.emitFileEvent(
677
+ "processing:error",
678
+ task.fileId,
679
+ void 0,
680
+ error instanceof Error ? error.message : "\u5904\u7406\u5931\u8D25"
681
+ );
682
+ }
683
+ }
684
+ this.isProcessingQueueRunning = false;
685
+ }
686
+ cacheMetadata(metadata) {
687
+ const expires = Date.now() + (this.config.cache?.metadataTTL || 3600) * 1e3;
688
+ this.metadataCache.set(metadata.id, { data: metadata, expires });
689
+ }
690
+ clearMetadataCache(fileId) {
691
+ this.metadataCache.delete(fileId);
692
+ }
693
+ emitFileEvent(type, fileId, data, error) {
694
+ const event = {
695
+ type,
696
+ fileId,
697
+ timestamp: /* @__PURE__ */ new Date(),
698
+ data,
699
+ error
700
+ };
701
+ this.emit(type, event);
702
+ this.emit("*", event);
703
+ }
704
+ // ============= 数据库操作私有方法 =============
705
+ /**
706
+ * 保存文件元数据到数据库 (通过持久化仓储)
707
+ */
708
+ async saveFileMetadata(metadata) {
709
+ if (!this.config.persistence?.enabled || !this.config.persistence.repository) {
710
+ logger.warn("\u26A0\uFE0F [UniversalFileService] \u6301\u4E45\u5316\u672A\u542F\u7528,\u8DF3\u8FC7\u4FDD\u5B58\u5143\u6570\u636E");
711
+ return;
712
+ }
713
+ try {
714
+ await this.config.persistence.repository.save(metadata);
715
+ logger.info("\u{1F4BE} [UniversalFileService] \u6587\u4EF6\u5143\u6570\u636E\u4FDD\u5B58\u6210\u529F:", metadata.id);
716
+ } catch (error) {
717
+ console.error("\u274C [UniversalFileService] \u4FDD\u5B58\u6587\u4EF6\u5143\u6570\u636E\u5931\u8D25:", error);
718
+ throw new FileUploadError(
719
+ "\u4FDD\u5B58\u6587\u4EF6\u5143\u6570\u636E\u5931\u8D25: " + (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF")
720
+ );
721
+ }
722
+ }
723
+ /**
724
+ * 从数据库删除文件元数据 (通过持久化仓储)
725
+ */
726
+ async deleteFileMetadata(fileId) {
727
+ if (!this.config.persistence?.enabled || !this.config.persistence.repository) {
728
+ return;
729
+ }
730
+ try {
731
+ await this.config.persistence.repository.delete(fileId);
732
+ logger.info("\u{1F5D1}\uFE0F [UniversalFileService] \u6587\u4EF6\u5143\u6570\u636E\u5220\u9664\u6210\u529F:", fileId);
733
+ } catch (error) {
734
+ console.error("\u274C [UniversalFileService] \u5220\u9664\u6587\u4EF6\u5143\u6570\u636E\u5931\u8D25:", error);
735
+ throw new FileUploadError(
736
+ "\u5220\u9664\u6587\u4EF6\u5143\u6570\u636E\u5931\u8D25: " + (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF")
737
+ );
738
+ }
739
+ }
740
+ /**
741
+ * 更新访问统计
742
+ */
743
+ async updateAccessStats(fileId) {
744
+ logger.info("\u{1F4CA} [UniversalFileService] \u9700\u8981\u66F4\u65B0\u8BBF\u95EE\u7EDF\u8BA1:", fileId);
745
+ }
746
+ /**
747
+ * 检查文件访问权限
748
+ */
749
+ async checkFileAccess(metadata, userId) {
750
+ if (metadata.permission === "public") {
751
+ return;
752
+ }
753
+ if (metadata.permission === "private" && metadata.uploaderId !== userId) {
754
+ throw new FileUploadError("\u65E0\u6743\u9650\u8BBF\u95EE\u6B64\u6587\u4EF6");
755
+ }
756
+ if (metadata.permission === "authenticated" && !userId) {
757
+ throw new FileUploadError("\u9700\u8981\u767B\u5F55\u624D\u80FD\u8BBF\u95EE\u6B64\u6587\u4EF6");
758
+ }
759
+ }
760
+ /**
761
+ * 检查文件删除权限
762
+ */
763
+ async checkFileDeleteAccess(metadata, userId) {
764
+ if (metadata.uploaderId !== userId) {
765
+ throw new FileUploadError("\u65E0\u6743\u9650\u5220\u9664\u6B64\u6587\u4EF6");
766
+ }
767
+ }
768
+ /**
769
+ * 检查服务是否完全可用(包括存储提供者)
770
+ */
771
+ isFullyInitialized() {
772
+ const hasStorageProvider = Array.from(this.storageProviders.values()).some(
773
+ (provider) => provider.type === "aliyun-oss" || provider.type === "local"
774
+ );
775
+ return hasStorageProvider;
776
+ }
777
+ /**
778
+ * 等待服务完全初始化(带超时)
779
+ */
780
+ async waitForInitialization(timeoutMs = 3e4) {
781
+ const startTime = Date.now();
782
+ while (!this.isFullyInitialized()) {
783
+ if (Date.now() - startTime > timeoutMs) {
784
+ throw new Error("\u670D\u52A1\u521D\u59CB\u5316\u8D85\u65F6 (" + timeoutMs + "ms)");
785
+ }
786
+ await new Promise((resolve) => setTimeout(resolve, 100));
787
+ }
788
+ logger.info("\u2705 [UniversalFileService] \u670D\u52A1\u5B8C\u5168\u521D\u59CB\u5316\u5C31\u7EEA");
789
+ }
790
+ };
791
+ }
792
+ });
793
+
794
+ export { UniversalFileService, getMimeType, init_UniversalFileService, init_mime };
795
+ //# sourceMappingURL=chunk-76V7EKBX.mjs.map
796
+ //# sourceMappingURL=chunk-76V7EKBX.mjs.map