sa2kit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +298 -0
  3. package/dist/AliyunOSSProvider-7JLMJDXK.js +15 -0
  4. package/dist/AliyunOSSProvider-7JLMJDXK.js.map +1 -0
  5. package/dist/AliyunOSSProvider-GQMSDJGZ.mjs +6 -0
  6. package/dist/AliyunOSSProvider-GQMSDJGZ.mjs.map +1 -0
  7. package/dist/LocalStorageProvider-FVLLHBHO.mjs +6 -0
  8. package/dist/LocalStorageProvider-FVLLHBHO.mjs.map +1 -0
  9. package/dist/LocalStorageProvider-NBNHHWLY.js +15 -0
  10. package/dist/LocalStorageProvider-NBNHHWLY.js.map +1 -0
  11. package/dist/analytics/index.d.mts +1084 -0
  12. package/dist/analytics/index.d.ts +1084 -0
  13. package/dist/analytics/index.js +2595 -0
  14. package/dist/analytics/index.js.map +1 -0
  15. package/dist/analytics/index.mjs +2518 -0
  16. package/dist/analytics/index.mjs.map +1 -0
  17. package/dist/analytics/server/index.d.mts +499 -0
  18. package/dist/analytics/server/index.d.ts +499 -0
  19. package/dist/analytics/server/index.js +529 -0
  20. package/dist/analytics/server/index.js.map +1 -0
  21. package/dist/analytics/server/index.mjs +525 -0
  22. package/dist/analytics/server/index.mjs.map +1 -0
  23. package/dist/auth/client/index.d.mts +104 -0
  24. package/dist/auth/client/index.d.ts +104 -0
  25. package/dist/auth/client/index.js +21 -0
  26. package/dist/auth/client/index.js.map +1 -0
  27. package/dist/auth/client/index.mjs +4 -0
  28. package/dist/auth/client/index.mjs.map +1 -0
  29. package/dist/auth/components/index.d.mts +82 -0
  30. package/dist/auth/components/index.d.ts +82 -0
  31. package/dist/auth/components/index.js +93 -0
  32. package/dist/auth/components/index.js.map +1 -0
  33. package/dist/auth/components/index.mjs +86 -0
  34. package/dist/auth/components/index.mjs.map +1 -0
  35. package/dist/auth/hooks/index.d.mts +2 -0
  36. package/dist/auth/hooks/index.d.ts +2 -0
  37. package/dist/auth/hooks/index.js +17 -0
  38. package/dist/auth/hooks/index.js.map +1 -0
  39. package/dist/auth/hooks/index.mjs +4 -0
  40. package/dist/auth/hooks/index.mjs.map +1 -0
  41. package/dist/auth/index.d.mts +15 -0
  42. package/dist/auth/index.d.ts +15 -0
  43. package/dist/auth/index.js +110 -0
  44. package/dist/auth/index.js.map +1 -0
  45. package/dist/auth/index.mjs +9 -0
  46. package/dist/auth/index.mjs.map +1 -0
  47. package/dist/auth/middleware/index.d.mts +75 -0
  48. package/dist/auth/middleware/index.d.ts +75 -0
  49. package/dist/auth/middleware/index.js +15 -0
  50. package/dist/auth/middleware/index.js.map +1 -0
  51. package/dist/auth/middleware/index.mjs +6 -0
  52. package/dist/auth/middleware/index.mjs.map +1 -0
  53. package/dist/auth/routes/index.d.mts +163 -0
  54. package/dist/auth/routes/index.d.ts +163 -0
  55. package/dist/auth/routes/index.js +27 -0
  56. package/dist/auth/routes/index.js.map +1 -0
  57. package/dist/auth/routes/index.mjs +6 -0
  58. package/dist/auth/routes/index.mjs.map +1 -0
  59. package/dist/auth/schema/index.d.mts +789 -0
  60. package/dist/auth/schema/index.d.ts +789 -0
  61. package/dist/auth/schema/index.js +41 -0
  62. package/dist/auth/schema/index.js.map +1 -0
  63. package/dist/auth/schema/index.mjs +4 -0
  64. package/dist/auth/schema/index.mjs.map +1 -0
  65. package/dist/auth/services/index.d.mts +47 -0
  66. package/dist/auth/services/index.d.ts +47 -0
  67. package/dist/auth/services/index.js +34 -0
  68. package/dist/auth/services/index.js.map +1 -0
  69. package/dist/auth/services/index.mjs +5 -0
  70. package/dist/auth/services/index.mjs.map +1 -0
  71. package/dist/chunk-3RFBUDRA.js +507 -0
  72. package/dist/chunk-3RFBUDRA.js.map +1 -0
  73. package/dist/chunk-3XG5OHFD.mjs +37 -0
  74. package/dist/chunk-3XG5OHFD.mjs.map +1 -0
  75. package/dist/chunk-6BL3AZGD.js +285 -0
  76. package/dist/chunk-6BL3AZGD.js.map +1 -0
  77. package/dist/chunk-6FNUWAIV.js +394 -0
  78. package/dist/chunk-6FNUWAIV.js.map +1 -0
  79. package/dist/chunk-6PRFP5EG.js +171 -0
  80. package/dist/chunk-6PRFP5EG.js.map +1 -0
  81. package/dist/chunk-6VHWOPRR.mjs +90 -0
  82. package/dist/chunk-6VHWOPRR.mjs.map +1 -0
  83. package/dist/chunk-AIKEVVDR.mjs +122 -0
  84. package/dist/chunk-AIKEVVDR.mjs.map +1 -0
  85. package/dist/chunk-APY57REU.js +300 -0
  86. package/dist/chunk-APY57REU.js.map +1 -0
  87. package/dist/chunk-BJTO5JO5.mjs +10 -0
  88. package/dist/chunk-BJTO5JO5.mjs.map +1 -0
  89. package/dist/chunk-C64RY2OW.mjs +295 -0
  90. package/dist/chunk-C64RY2OW.mjs.map +1 -0
  91. package/dist/chunk-DGUM43GV.js +12 -0
  92. package/dist/chunk-DGUM43GV.js.map +1 -0
  93. package/dist/chunk-FV3FNHQY.js +92 -0
  94. package/dist/chunk-FV3FNHQY.js.map +1 -0
  95. package/dist/chunk-GSTLV3MB.mjs +316 -0
  96. package/dist/chunk-GSTLV3MB.mjs.map +1 -0
  97. package/dist/chunk-HEMA7SWK.mjs +212 -0
  98. package/dist/chunk-HEMA7SWK.mjs.map +1 -0
  99. package/dist/chunk-HWJ34NL6.js +43 -0
  100. package/dist/chunk-HWJ34NL6.js.map +1 -0
  101. package/dist/chunk-HXFFYNIF.mjs +385 -0
  102. package/dist/chunk-HXFFYNIF.mjs.map +1 -0
  103. package/dist/chunk-KGRQNEIR.mjs +183 -0
  104. package/dist/chunk-KGRQNEIR.mjs.map +1 -0
  105. package/dist/chunk-KH6RQ4J5.js +28 -0
  106. package/dist/chunk-KH6RQ4J5.js.map +1 -0
  107. package/dist/chunk-KQGP6BTS.mjs +165 -0
  108. package/dist/chunk-KQGP6BTS.mjs.map +1 -0
  109. package/dist/chunk-NMF4ANIC.js +365 -0
  110. package/dist/chunk-NMF4ANIC.js.map +1 -0
  111. package/dist/chunk-O26VCNS3.js +216 -0
  112. package/dist/chunk-O26VCNS3.js.map +1 -0
  113. package/dist/chunk-OLHGZXN3.mjs +86 -0
  114. package/dist/chunk-OLHGZXN3.mjs.map +1 -0
  115. package/dist/chunk-QU5OT4DF.js +88 -0
  116. package/dist/chunk-QU5OT4DF.js.map +1 -0
  117. package/dist/chunk-RCNNVNLT.mjs +356 -0
  118. package/dist/chunk-RCNNVNLT.mjs.map +1 -0
  119. package/dist/chunk-ROEYW4A7.js +186 -0
  120. package/dist/chunk-ROEYW4A7.js.map +1 -0
  121. package/dist/chunk-SVWQN2LR.js +131 -0
  122. package/dist/chunk-SVWQN2LR.js.map +1 -0
  123. package/dist/chunk-TKCYPDWU.js +338 -0
  124. package/dist/chunk-TKCYPDWU.js.map +1 -0
  125. package/dist/chunk-U2L6V7KD.mjs +273 -0
  126. package/dist/chunk-U2L6V7KD.mjs.map +1 -0
  127. package/dist/chunk-YVBU7QDJ.mjs +505 -0
  128. package/dist/chunk-YVBU7QDJ.mjs.map +1 -0
  129. package/dist/chunk-ZGVB35L2.mjs +25 -0
  130. package/dist/chunk-ZGVB35L2.mjs.map +1 -0
  131. package/dist/config/index.d.mts +64 -0
  132. package/dist/config/index.d.ts +64 -0
  133. package/dist/config/index.js +136 -0
  134. package/dist/config/index.js.map +1 -0
  135. package/dist/config/index.mjs +128 -0
  136. package/dist/config/index.mjs.map +1 -0
  137. package/dist/drizzle-auth-service-Bxlovhv8.d.ts +145 -0
  138. package/dist/drizzle-auth-service-DZY2F1sv.d.mts +145 -0
  139. package/dist/enums-Dume-V5Y.d.mts +16 -0
  140. package/dist/enums-Dume-V5Y.d.ts +16 -0
  141. package/dist/i18n/index.d.mts +416 -0
  142. package/dist/i18n/index.d.ts +416 -0
  143. package/dist/i18n/index.js +671 -0
  144. package/dist/i18n/index.js.map +1 -0
  145. package/dist/i18n/index.mjs +650 -0
  146. package/dist/i18n/index.mjs.map +1 -0
  147. package/dist/index-8VoHap_4.d.mts +105 -0
  148. package/dist/index-8VoHap_4.d.ts +105 -0
  149. package/dist/index.d.mts +4 -0
  150. package/dist/index.d.ts +4 -0
  151. package/dist/index.js +84 -0
  152. package/dist/index.js.map +1 -0
  153. package/dist/index.mjs +7 -0
  154. package/dist/index.mjs.map +1 -0
  155. package/dist/logger/index.d.mts +125 -0
  156. package/dist/logger/index.d.ts +125 -0
  157. package/dist/logger/index.js +29 -0
  158. package/dist/logger/index.js.map +1 -0
  159. package/dist/logger/index.mjs +4 -0
  160. package/dist/logger/index.mjs.map +1 -0
  161. package/dist/request/index.d.mts +51 -0
  162. package/dist/request/index.d.ts +51 -0
  163. package/dist/request/index.js +85 -0
  164. package/dist/request/index.js.map +1 -0
  165. package/dist/request/index.mjs +82 -0
  166. package/dist/request/index.mjs.map +1 -0
  167. package/dist/storage/index.d.mts +74 -0
  168. package/dist/storage/index.d.ts +74 -0
  169. package/dist/storage/index.js +46 -0
  170. package/dist/storage/index.js.map +1 -0
  171. package/dist/storage/index.mjs +5 -0
  172. package/dist/storage/index.mjs.map +1 -0
  173. package/dist/types-BINlP9MK.d.mts +286 -0
  174. package/dist/types-BINlP9MK.d.ts +286 -0
  175. package/dist/types-BaZccpvk.d.mts +48 -0
  176. package/dist/types-BaZccpvk.d.ts +48 -0
  177. package/dist/types-CbTsi9CZ.d.mts +31 -0
  178. package/dist/types-CbTsi9CZ.d.ts +31 -0
  179. package/dist/types-CoGG1rNV.d.mts +258 -0
  180. package/dist/types-CoGG1rNV.d.ts +258 -0
  181. package/dist/types-DAxQ1MeY.d.ts +70 -0
  182. package/dist/types-DT8LVCvE.d.mts +70 -0
  183. package/dist/types-DW9qar-w.d.mts +52 -0
  184. package/dist/types-DW9qar-w.d.ts +52 -0
  185. package/dist/universalExport/index.d.mts +235 -0
  186. package/dist/universalExport/index.d.ts +235 -0
  187. package/dist/universalExport/index.js +621 -0
  188. package/dist/universalExport/index.js.map +1 -0
  189. package/dist/universalExport/index.mjs +580 -0
  190. package/dist/universalExport/index.mjs.map +1 -0
  191. package/dist/universalExport/server/index.d.mts +429 -0
  192. package/dist/universalExport/server/index.d.ts +429 -0
  193. package/dist/universalExport/server/index.js +263 -0
  194. package/dist/universalExport/server/index.js.map +1 -0
  195. package/dist/universalExport/server/index.mjs +242 -0
  196. package/dist/universalExport/server/index.mjs.map +1 -0
  197. package/dist/universalFile/index.d.mts +310 -0
  198. package/dist/universalFile/index.d.ts +310 -0
  199. package/dist/universalFile/index.js +811 -0
  200. package/dist/universalFile/index.js.map +1 -0
  201. package/dist/universalFile/index.mjs +736 -0
  202. package/dist/universalFile/index.mjs.map +1 -0
  203. package/dist/universalFile/server/index.d.mts +2428 -0
  204. package/dist/universalFile/server/index.d.ts +2428 -0
  205. package/dist/universalFile/server/index.js +4578 -0
  206. package/dist/universalFile/server/index.js.map +1 -0
  207. package/dist/universalFile/server/index.mjs +4518 -0
  208. package/dist/universalFile/server/index.mjs.map +1 -0
  209. package/dist/useElectronStorage-Dj0rcorG.d.mts +65 -0
  210. package/dist/useElectronStorage-DwnNfIhl.d.ts +65 -0
  211. package/dist/utils/index.d.mts +188 -0
  212. package/dist/utils/index.d.ts +188 -0
  213. package/dist/utils/index.js +42 -0
  214. package/dist/utils/index.js.map +1 -0
  215. package/dist/utils/index.mjs +5 -0
  216. package/dist/utils/index.mjs.map +1 -0
  217. package/package.json +220 -0
  218. package/tailwind.animations.js +34 -0
@@ -0,0 +1,4578 @@
1
+ 'use strict';
2
+
3
+ var chunkTKCYPDWU_js = require('../../chunk-TKCYPDWU.js');
4
+ var chunk3RFBUDRA_js = require('../../chunk-3RFBUDRA.js');
5
+ var chunkKH6RQ4J5_js = require('../../chunk-KH6RQ4J5.js');
6
+ var chunk6PRFP5EG_js = require('../../chunk-6PRFP5EG.js');
7
+ var chunkHWJ34NL6_js = require('../../chunk-HWJ34NL6.js');
8
+ var chunkDGUM43GV_js = require('../../chunk-DGUM43GV.js');
9
+ var crypto = require('crypto');
10
+ var path3 = require('path');
11
+ var fs = require('fs');
12
+ var events = require('events');
13
+ var uuid = require('uuid');
14
+ var lruCache = require('lru-cache');
15
+ var server = require('next/server');
16
+ var drizzleOrm = require('drizzle-orm');
17
+
18
+ function _interopNamespace(e) {
19
+ if (e && e.__esModule) return e;
20
+ var n = Object.create(null);
21
+ if (e) {
22
+ Object.keys(e).forEach(function (k) {
23
+ if (k !== 'default') {
24
+ var d = Object.getOwnPropertyDescriptor(e, k);
25
+ Object.defineProperty(n, k, d.get ? d : {
26
+ enumerable: true,
27
+ get: function () { return e[k]; }
28
+ });
29
+ }
30
+ });
31
+ }
32
+ n.default = e;
33
+ return Object.freeze(n);
34
+ }
35
+
36
+ var path3__namespace = /*#__PURE__*/_interopNamespace(path3);
37
+
38
+ // src/universalFile/server/factory.ts
39
+ function createFileServiceConfig(options) {
40
+ let storage;
41
+ if (options.storage === "local") {
42
+ storage = {
43
+ type: "local",
44
+ enabled: true,
45
+ rootPath: process.env.UPLOAD_DIR || "./uploads",
46
+ baseUrl: process.env.BASE_URL || "http://localhost:3000"
47
+ };
48
+ } else {
49
+ storage = options.storage;
50
+ }
51
+ return {
52
+ storage,
53
+ cdn: options.cdn,
54
+ cache: options.cache || {
55
+ enabled: false
56
+ },
57
+ processors: options.processors || [],
58
+ db: options.db,
59
+ maxFileSize: options.maxFileSize || 10 * 1024 * 1024,
60
+ // 10MB
61
+ allowedMimeTypes: options.allowedMimeTypes || [
62
+ "image/*",
63
+ "video/*",
64
+ "audio/*",
65
+ "application/pdf"
66
+ ],
67
+ enableMonitoring: options.enableMonitoring !== false
68
+ };
69
+ }
70
+ function createUniversalFileService(options) {
71
+ const config = createFileServiceConfig(options);
72
+ console.warn(
73
+ "\u26A0\uFE0F UniversalFileService \u7684\u5B8C\u6574\u5B9E\u73B0\u5C06\u5728\u540E\u7EED Phase \u4E2D\u8FC1\u79FB\u5230 Sa2kit\u3002\n \u5F53\u524D\u7248\u672C\u4EC5\u5305\u542B\u7C7B\u578B\u5B9A\u4E49\u548C\u914D\u7F6E\u5DE5\u5382\u51FD\u6570\u3002\n \u8BF7\u6682\u65F6\u7EE7\u7EED\u4ECE LyricNote \u7684 lib/universalFile \u5BFC\u5165\u670D\u52A1\u7C7B\u3002"
74
+ );
75
+ return {
76
+ config,
77
+ // 占位方法
78
+ async upload() {
79
+ throw new Error("UniversalFileService \u5C1A\u672A\u5B8C\u5168\u8FC1\u79FB\uFF0C\u8BF7\u4ECE LyricNote \u5BFC\u5165");
80
+ },
81
+ async download() {
82
+ throw new Error("UniversalFileService \u5C1A\u672A\u5B8C\u5168\u8FC1\u79FB\uFF0C\u8BF7\u4ECE LyricNote \u5BFC\u5165");
83
+ }
84
+ };
85
+ }
86
+ function createFileServiceFromEnv(db) {
87
+ const storageType = process.env.STORAGE_TYPE || "local";
88
+ let storage;
89
+ switch (storageType) {
90
+ case "local":
91
+ storage = {
92
+ type: "local",
93
+ enabled: true,
94
+ rootPath: process.env.UPLOAD_DIR || "./uploads",
95
+ baseUrl: process.env.BASE_URL || "http://localhost:3000"
96
+ };
97
+ break;
98
+ case "aliyun-oss":
99
+ if (!process.env.OSS_ACCESS_KEY_ID || !process.env.OSS_ACCESS_KEY_SECRET) {
100
+ throw new Error("Missing required OSS environment variables");
101
+ }
102
+ storage = {
103
+ type: "aliyun-oss",
104
+ enabled: true,
105
+ accessKeyId: process.env.OSS_ACCESS_KEY_ID,
106
+ accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
107
+ bucket: process.env.OSS_BUCKET,
108
+ region: process.env.OSS_REGION || "oss-cn-hangzhou"
109
+ };
110
+ break;
111
+ default:
112
+ throw new Error(`Unsupported storage type: ${storageType}`);
113
+ }
114
+ return createUniversalFileService({
115
+ storage,
116
+ db
117
+ });
118
+ }
119
+
120
+ // src/universalFile/server/presets.ts
121
+ function createLocalDevPreset(baseUrl = "http://localhost:3000") {
122
+ return {
123
+ type: "local",
124
+ enabled: true,
125
+ rootPath: "./uploads",
126
+ baseUrl
127
+ };
128
+ }
129
+ function createAliyunOSSPreset(config) {
130
+ return {
131
+ type: "aliyun-oss",
132
+ enabled: true,
133
+ accessKeyId: config.accessKeyId,
134
+ accessKeySecret: config.accessKeySecret,
135
+ bucket: config.bucket,
136
+ region: config.region || "oss-cn-hangzhou"
137
+ };
138
+ }
139
+ function createSmartPreset() {
140
+ const isProduction = process.env.NODE_ENV === "production";
141
+ if (isProduction && process.env.OSS_ACCESS_KEY_ID) {
142
+ return createAliyunOSSPreset({
143
+ accessKeyId: process.env.OSS_ACCESS_KEY_ID,
144
+ accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
145
+ bucket: process.env.OSS_BUCKET,
146
+ region: process.env.OSS_REGION
147
+ });
148
+ }
149
+ return createLocalDevPreset(process.env.BASE_URL);
150
+ }
151
+ function createImageServicePreset(storage) {
152
+ return {
153
+ storage,
154
+ maxFileSize: 5 * 1024 * 1024,
155
+ // 5MB
156
+ allowedMimeTypes: [
157
+ "image/jpeg",
158
+ "image/png",
159
+ "image/webp",
160
+ "image/gif"
161
+ ]
162
+ };
163
+ }
164
+ function createVideoServicePreset(storage) {
165
+ return {
166
+ storage,
167
+ maxFileSize: 100 * 1024 * 1024,
168
+ // 100MB
169
+ allowedMimeTypes: [
170
+ "video/mp4",
171
+ "video/webm",
172
+ "video/quicktime"
173
+ ],
174
+ enableStreaming: true
175
+ };
176
+ }
177
+ function createDocumentServicePreset(storage) {
178
+ return {
179
+ storage,
180
+ maxFileSize: 20 * 1024 * 1024,
181
+ // 20MB
182
+ allowedMimeTypes: [
183
+ "application/pdf",
184
+ "application/msword",
185
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
186
+ "application/vnd.ms-excel",
187
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
188
+ ]
189
+ };
190
+ }
191
+
192
+ // src/universalFile/server/validation.ts
193
+ var ConfigValidationError = class extends Error {
194
+ constructor(message, field) {
195
+ super(message);
196
+ this.field = field;
197
+ this.name = "ConfigValidationError";
198
+ }
199
+ };
200
+ function validateStorageConfig(storage) {
201
+ if (!storage.type) {
202
+ throw new ConfigValidationError("Storage type is required", "storage.type");
203
+ }
204
+ switch (storage.type) {
205
+ case "local": {
206
+ const config = storage;
207
+ if (!config.rootPath) {
208
+ throw new ConfigValidationError(
209
+ "rootPath is required for local storage",
210
+ "storage.rootPath"
211
+ );
212
+ }
213
+ if (!config.baseUrl) {
214
+ throw new ConfigValidationError(
215
+ "baseUrl is required for local storage",
216
+ "storage.baseUrl"
217
+ );
218
+ }
219
+ break;
220
+ }
221
+ case "aliyun-oss": {
222
+ const config = storage;
223
+ if (!config.accessKeyId) {
224
+ throw new ConfigValidationError(
225
+ "accessKeyId is required for Aliyun OSS",
226
+ "storage.accessKeyId"
227
+ );
228
+ }
229
+ if (!config.accessKeySecret) {
230
+ throw new ConfigValidationError(
231
+ "accessKeySecret is required for Aliyun OSS",
232
+ "storage.accessKeySecret"
233
+ );
234
+ }
235
+ if (!config.bucket) {
236
+ throw new ConfigValidationError(
237
+ "bucket is required for Aliyun OSS",
238
+ "storage.bucket"
239
+ );
240
+ }
241
+ break;
242
+ }
243
+ default:
244
+ throw new ConfigValidationError(
245
+ `Unsupported storage type: ${storage.type}`,
246
+ "storage.type"
247
+ );
248
+ }
249
+ }
250
+ function validateServiceConfig(config) {
251
+ if (!config.storage) {
252
+ throw new ConfigValidationError("Storage config is required", "storage");
253
+ }
254
+ validateStorageConfig(config.storage);
255
+ if (config.maxFileSize && config.maxFileSize <= 0) {
256
+ throw new ConfigValidationError(
257
+ "maxFileSize must be greater than 0",
258
+ "maxFileSize"
259
+ );
260
+ }
261
+ if (config.allowedMimeTypes && config.allowedMimeTypes.length === 0) {
262
+ throw new ConfigValidationError(
263
+ "allowedMimeTypes must not be empty",
264
+ "allowedMimeTypes"
265
+ );
266
+ }
267
+ }
268
+ function validateEnvironment(requiredVars) {
269
+ const missing = [];
270
+ for (const varName of requiredVars) {
271
+ if (!process.env[varName]) {
272
+ missing.push(varName);
273
+ }
274
+ }
275
+ if (missing.length > 0) {
276
+ throw new ConfigValidationError(
277
+ `Missing required environment variables: ${missing.join(", ")}`,
278
+ "environment"
279
+ );
280
+ }
281
+ }
282
+ function getRequiredEnvVars(storageType) {
283
+ switch (storageType) {
284
+ case "local":
285
+ return ["UPLOAD_DIR", "BASE_URL"];
286
+ case "aliyun-oss":
287
+ return [
288
+ "OSS_ACCESS_KEY_ID",
289
+ "OSS_ACCESS_KEY_SECRET",
290
+ "OSS_BUCKET",
291
+ "OSS_REGION"
292
+ ];
293
+ default:
294
+ return [];
295
+ }
296
+ }
297
+ var logger = chunk6PRFP5EG_js.createLogger("AliyunCDNProvider");
298
+ var AliyunCDNProvider = class {
299
+ constructor() {
300
+ this.type = "aliyun-cdn";
301
+ this.config = null;
302
+ this.client = null;
303
+ this.isInitialized = false;
304
+ }
305
+ /**
306
+ * 初始化CDN提供者
307
+ */
308
+ async initialize(config) {
309
+ if (config.type !== "aliyun-cdn") {
310
+ throw new chunkKH6RQ4J5_js.CDNProviderError("\u914D\u7F6E\u7C7B\u578B\u4E0D\u5339\u914D\uFF1A\u671F\u671B aliyun-cdn");
311
+ }
312
+ this.config = config;
313
+ logger.info(`\u{1F310} [AliyunCDNProvider] \u521D\u59CB\u5316\u963F\u91CC\u4E91CDN\uFF0C\u57DF\u540D: ${this.config.domain}`);
314
+ try {
315
+ this.validateConfig();
316
+ try {
317
+ const CDN = chunkDGUM43GV_js.__require("@alicloud/cdn20180510");
318
+ const OpenApi = chunkDGUM43GV_js.__require("@alicloud/openapi-client");
319
+ const cdnConfig = new OpenApi.Config({
320
+ accessKeyId: this.config.accessKeyId,
321
+ accessKeySecret: this.config.accessKeySecret,
322
+ endpoint: "cdn.aliyuncs.com"
323
+ });
324
+ this.client = new CDN.default(cdnConfig);
325
+ } catch (sdkError) {
326
+ logger.warn("\u26A0\uFE0F [AliyunCDNProvider] \u963F\u91CC\u4E91CDN SDK\u672A\u5B89\u88C5\uFF0C\u4F7F\u7528\u6A21\u62DF\u6A21\u5F0F");
327
+ this.client = this.createMockClient();
328
+ }
329
+ await this.testConnection();
330
+ this.isInitialized = true;
331
+ logger.info("\u2705 [AliyunCDNProvider] \u963F\u91CC\u4E91CDN\u521D\u59CB\u5316\u5B8C\u6210");
332
+ } catch (error) {
333
+ logger.error("\u274C [AliyunCDNProvider] \u963F\u91CC\u4E91CDN\u521D\u59CB\u5316\u5931\u8D25:", error);
334
+ throw new chunkKH6RQ4J5_js.CDNProviderError(
335
+ `\u963F\u91CC\u4E91CDN\u521D\u59CB\u5316\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
336
+ );
337
+ }
338
+ }
339
+ /**
340
+ * 生成CDN URL
341
+ */
342
+ async generateUrl(originalUrl) {
343
+ this.ensureInitialized();
344
+ logger.info(`\u{1F517} [AliyunCDNProvider] \u751F\u6210CDN URL: ${originalUrl}`);
345
+ try {
346
+ const url = new URL(originalUrl);
347
+ const cdnUrl = `${url.protocol}//${this.config.domain}${url.pathname}${url.search}${url.hash}`;
348
+ logger.info(`\u2705 [AliyunCDNProvider] CDN URL\u751F\u6210\u5B8C\u6210: ${cdnUrl}`);
349
+ return cdnUrl;
350
+ } catch (error) {
351
+ logger.error(`\u274C [AliyunCDNProvider] CDN URL\u751F\u6210\u5931\u8D25: ${originalUrl}:`, error);
352
+ throw new chunkKH6RQ4J5_js.CDNProviderError(
353
+ `CDN URL\u751F\u6210\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
354
+ );
355
+ }
356
+ }
357
+ /**
358
+ * 刷新缓存
359
+ */
360
+ async refreshCache(urls) {
361
+ this.ensureInitialized();
362
+ logger.info(`\u{1F504} [AliyunCDNProvider] \u5F00\u59CB\u5237\u65B0CDN\u7F13\u5B58\uFF0CURL\u6570\u91CF: ${urls.length}`);
363
+ try {
364
+ const cdnUrls = await Promise.all(urls.map((url) => this.generateUrl(url)));
365
+ const result = await this.client.refreshObjectCaches({
366
+ domainName: this.config.domain,
367
+ objectPath: cdnUrls.join("\n"),
368
+ objectType: "File"
369
+ // File 或 Directory
370
+ });
371
+ logger.info(
372
+ `\u2705 [AliyunCDNProvider] CDN\u7F13\u5B58\u5237\u65B0\u5B8C\u6210\uFF0C\u4EFB\u52A1ID: ${result.RefreshTaskId || "unknown"}`
373
+ );
374
+ return {
375
+ success: true,
376
+ data: {
377
+ taskId: result.RefreshTaskId,
378
+ requestId: result.RequestId,
379
+ urls: cdnUrls
380
+ }
381
+ };
382
+ } catch (error) {
383
+ logger.error(`\u274C [AliyunCDNProvider] CDN\u7F13\u5B58\u5237\u65B0\u5931\u8D25:`, error);
384
+ return {
385
+ success: false,
386
+ error: `CDN\u7F13\u5B58\u5237\u65B0\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
387
+ };
388
+ }
389
+ }
390
+ /**
391
+ * 预热缓存
392
+ */
393
+ async preheatCache(urls) {
394
+ this.ensureInitialized();
395
+ logger.info(`\u{1F525} [AliyunCDNProvider] \u5F00\u59CB\u9884\u70EDCDN\u7F13\u5B58\uFF0CURL\u6570\u91CF: ${urls.length}`);
396
+ try {
397
+ const cdnUrls = await Promise.all(urls.map((url) => this.generateUrl(url)));
398
+ const result = await this.client.pushObjectCache({
399
+ domainName: this.config.domain,
400
+ objectPath: cdnUrls.join("\n"),
401
+ area: "domestic"
402
+ // domestic, overseas, global
403
+ });
404
+ logger.info(
405
+ `\u2705 [AliyunCDNProvider] CDN\u7F13\u5B58\u9884\u70ED\u5B8C\u6210\uFF0C\u4EFB\u52A1ID: ${result.PushTaskId || "unknown"}`
406
+ );
407
+ return {
408
+ success: true,
409
+ data: {
410
+ taskId: result.PushTaskId,
411
+ requestId: result.RequestId,
412
+ urls: cdnUrls
413
+ }
414
+ };
415
+ } catch (error) {
416
+ logger.error(`\u274C [AliyunCDNProvider] CDN\u7F13\u5B58\u9884\u70ED\u5931\u8D25:`, error);
417
+ return {
418
+ success: false,
419
+ error: `CDN\u7F13\u5B58\u9884\u70ED\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
420
+ };
421
+ }
422
+ }
423
+ /**
424
+ * 获取访问统计
425
+ */
426
+ async getAccessStats(startTime, endTime) {
427
+ this.ensureInitialized();
428
+ logger.info(
429
+ `\u{1F4CA} [AliyunCDNProvider] \u83B7\u53D6CDN\u8BBF\u95EE\u7EDF\u8BA1: ${startTime.toISOString()} - ${endTime.toISOString()}`
430
+ );
431
+ try {
432
+ const formatTime = (date) => date.toISOString().slice(0, 19).replace("T", " ") + "Z";
433
+ const result = await this.client.describeDomainRealTimeData({
434
+ domainName: this.config.domain,
435
+ startTime: formatTime(startTime),
436
+ endTime: formatTime(endTime),
437
+ field: "bps,qps"
438
+ // 带宽和QPS
439
+ });
440
+ logger.info(`\u2705 [AliyunCDNProvider] CDN\u8BBF\u95EE\u7EDF\u8BA1\u83B7\u53D6\u5B8C\u6210`);
441
+ return {
442
+ success: true,
443
+ data: {
444
+ requestId: result.RequestId,
445
+ dataInterval: result.DataInterval,
446
+ realTimeData: result.RealTimeData,
447
+ domain: this.config.domain,
448
+ startTime: startTime.toISOString(),
449
+ endTime: endTime.toISOString()
450
+ }
451
+ };
452
+ } catch (error) {
453
+ logger.error(`\u274C [AliyunCDNProvider] CDN\u8BBF\u95EE\u7EDF\u8BA1\u83B7\u53D6\u5931\u8D25:`, error);
454
+ return {
455
+ success: false,
456
+ error: `CDN\u8BBF\u95EE\u7EDF\u8BA1\u83B7\u53D6\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
457
+ };
458
+ }
459
+ }
460
+ // ============= 高级功能 =============
461
+ /**
462
+ * 生成防盗链签名URL
463
+ */
464
+ async generateSignedUrl(originalUrl, expiresIn = 3600, authKey) {
465
+ this.ensureInitialized();
466
+ logger.info(`\u{1F510} [AliyunCDNProvider] \u751F\u6210\u9632\u76D7\u94FE\u7B7E\u540DURL: ${originalUrl}`);
467
+ try {
468
+ const cdnUrl = await this.generateUrl(originalUrl);
469
+ if (!authKey) {
470
+ logger.info(`\u26A0\uFE0F [AliyunCDNProvider] \u672A\u63D0\u4F9BauthKey\uFF0C\u8FD4\u56DE\u666E\u901ACDN URL`);
471
+ return cdnUrl;
472
+ }
473
+ const parsedUrl = new URL(cdnUrl);
474
+ const timestamp = Math.floor(Date.now() / 1e3) + expiresIn;
475
+ const signString = `${parsedUrl.pathname}-${timestamp}-0-0-${authKey}`;
476
+ const authValue = crypto.createHash("md5").update(signString).digest("hex");
477
+ const signedUrl = `${cdnUrl}?auth_key=${timestamp}-0-0-${authValue}`;
478
+ logger.info(`\u2705 [AliyunCDNProvider] \u9632\u76D7\u94FE\u7B7E\u540DURL\u751F\u6210\u5B8C\u6210`);
479
+ return signedUrl;
480
+ } catch (error) {
481
+ logger.error(`\u274C [AliyunCDNProvider] \u9632\u76D7\u94FE\u7B7E\u540DURL\u751F\u6210\u5931\u8D25: ${originalUrl}:`, error);
482
+ throw new chunkKH6RQ4J5_js.CDNProviderError(
483
+ `\u9632\u76D7\u94FE\u7B7E\u540DURL\u751F\u6210\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
484
+ );
485
+ }
486
+ }
487
+ /**
488
+ * 查询刷新任务状态
489
+ */
490
+ async getRefreshTaskStatus(taskId) {
491
+ this.ensureInitialized();
492
+ try {
493
+ const result = await this.client.describeRefreshTasks({
494
+ taskId
495
+ });
496
+ return {
497
+ success: true,
498
+ data: {
499
+ tasks: result.Tasks,
500
+ requestId: result.RequestId
501
+ }
502
+ };
503
+ } catch (error) {
504
+ return {
505
+ success: false,
506
+ error: `\u67E5\u8BE2\u5237\u65B0\u4EFB\u52A1\u72B6\u6001\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
507
+ };
508
+ }
509
+ }
510
+ /**
511
+ * 查询预热任务状态
512
+ */
513
+ async getPreheatTaskStatus(taskId) {
514
+ this.ensureInitialized();
515
+ try {
516
+ const result = await this.client.describePushTasks({
517
+ taskId
518
+ });
519
+ return {
520
+ success: true,
521
+ data: {
522
+ tasks: result.Tasks,
523
+ requestId: result.RequestId
524
+ }
525
+ };
526
+ } catch (error) {
527
+ return {
528
+ success: false,
529
+ error: `\u67E5\u8BE2\u9884\u70ED\u4EFB\u52A1\u72B6\u6001\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
530
+ };
531
+ }
532
+ }
533
+ /**
534
+ * 获取域名配置
535
+ */
536
+ async getDomainConfig() {
537
+ this.ensureInitialized();
538
+ try {
539
+ const result = await this.client.describeDomainConfigs({
540
+ domainName: this.config.domain
541
+ });
542
+ return {
543
+ success: true,
544
+ data: {
545
+ domainConfigs: result.DomainConfigs,
546
+ requestId: result.RequestId
547
+ }
548
+ };
549
+ } catch (error) {
550
+ return {
551
+ success: false,
552
+ error: `\u83B7\u53D6\u57DF\u540D\u914D\u7F6E\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
553
+ };
554
+ }
555
+ }
556
+ /**
557
+ * 优化URL (添加图像处理参数等)
558
+ */
559
+ async optimizeUrl(originalUrl, options = {}) {
560
+ this.ensureInitialized();
561
+ try {
562
+ const cdnUrl = await this.generateUrl(originalUrl);
563
+ const url = new URL(cdnUrl);
564
+ const params = new URLSearchParams(url.search);
565
+ if (options.imageQuality) {
566
+ params.set("x-oss-process", `image/quality,q_${options.imageQuality}`);
567
+ }
568
+ if (options.imageFormat) {
569
+ const formatParam = params.get("x-oss-process") || "image";
570
+ params.set("x-oss-process", `${formatParam}/format,${options.imageFormat}`);
571
+ }
572
+ if (options.imageResize) {
573
+ const resizeParam = params.get("x-oss-process") || "image";
574
+ let resize = "resize";
575
+ if (options.imageResize.width) resize += `,w_${options.imageResize.width}`;
576
+ if (options.imageResize.height) resize += `,h_${options.imageResize.height}`;
577
+ params.set("x-oss-process", `${resizeParam}/${resize}`);
578
+ }
579
+ url.search = params.toString();
580
+ return url.toString();
581
+ } catch (error) {
582
+ logger.error(`\u274C [AliyunCDNProvider] URL\u4F18\u5316\u5931\u8D25: ${originalUrl}:`, error);
583
+ return this.generateUrl(originalUrl);
584
+ }
585
+ }
586
+ // ============= 私有方法 =============
587
+ /**
588
+ * 确保已初始化
589
+ */
590
+ ensureInitialized() {
591
+ if (!this.isInitialized || !this.client || !this.config) {
592
+ throw new chunkKH6RQ4J5_js.CDNProviderError("CDN\u63D0\u4F9B\u8005\u672A\u521D\u59CB\u5316");
593
+ }
594
+ }
595
+ /**
596
+ * 验证配置
597
+ */
598
+ validateConfig() {
599
+ if (!this.config) {
600
+ throw new chunkKH6RQ4J5_js.CDNProviderError("CDN\u914D\u7F6E\u4E3A\u7A7A");
601
+ }
602
+ const required = ["domain", "accessKeyId", "accessKeySecret"];
603
+ const missing = required.filter((key) => !this.config[key]);
604
+ if (missing.length > 0) {
605
+ throw new chunkKH6RQ4J5_js.CDNProviderError(`CDN\u914D\u7F6E\u7F3A\u5C11\u5FC5\u9700\u9879: ${missing.join(", ")}`);
606
+ }
607
+ if (!this.isValidDomain(this.config.domain)) {
608
+ throw new chunkKH6RQ4J5_js.CDNProviderError(`\u65E0\u6548\u7684CDN\u57DF\u540D: ${this.config.domain}`);
609
+ }
610
+ }
611
+ /**
612
+ * 验证域名格式
613
+ */
614
+ isValidDomain(domain) {
615
+ const domainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
616
+ return domainRegex.test(domain);
617
+ }
618
+ /**
619
+ * 测试连接
620
+ */
621
+ async testConnection() {
622
+ try {
623
+ await this.getDomainConfig();
624
+ logger.info(`\u2705 [AliyunCDNProvider] CDN\u8FDE\u63A5\u6D4B\u8BD5\u6210\u529F`);
625
+ } catch (error) {
626
+ logger.warn(`\u26A0\uFE0F [AliyunCDNProvider] CDN\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25\uFF0C\u53EF\u80FD\u662F\u6743\u9650\u95EE\u9898:`, error);
627
+ }
628
+ }
629
+ /**
630
+ * 创建模拟客户端(用于开发测试)
631
+ */
632
+ createMockClient() {
633
+ logger.info("\u{1F9EA} [AliyunCDNProvider] \u521B\u5EFA\u6A21\u62DFCDN\u5BA2\u6237\u7AEF");
634
+ return {
635
+ async refreshObjectCaches(params) {
636
+ logger.info("\u{1F504} [MockCDN] \u6A21\u62DF\u5237\u65B0\u7F13\u5B58:", params);
637
+ return {
638
+ RefreshTaskId: `mock-refresh-${Date.now()}`,
639
+ RequestId: `mock-request-${Date.now()}`
640
+ };
641
+ },
642
+ async pushObjectCache(params) {
643
+ logger.info("\u{1F525} [MockCDN] \u6A21\u62DF\u9884\u70ED\u7F13\u5B58:", params);
644
+ return {
645
+ PushTaskId: `mock-push-${Date.now()}`,
646
+ RequestId: `mock-request-${Date.now()}`
647
+ };
648
+ },
649
+ async describeRefreshTasks(params) {
650
+ logger.info("\u{1F4CB} [MockCDN] \u6A21\u62DF\u67E5\u8BE2\u5237\u65B0\u4EFB\u52A1:", params);
651
+ return {
652
+ Tasks: {
653
+ Task: [
654
+ {
655
+ TaskId: params.taskId,
656
+ Status: "Complete",
657
+ Process: "100%",
658
+ CreateTime: (/* @__PURE__ */ new Date()).toISOString()
659
+ }
660
+ ]
661
+ },
662
+ RequestId: `mock-request-${Date.now()}`
663
+ };
664
+ },
665
+ async describePushTasks(params) {
666
+ logger.info("\u{1F4CB} [MockCDN] \u6A21\u62DF\u67E5\u8BE2\u9884\u70ED\u4EFB\u52A1:", params);
667
+ return {
668
+ Tasks: {
669
+ Task: [
670
+ {
671
+ TaskId: params.taskId,
672
+ Status: "Complete",
673
+ Process: "100%",
674
+ CreateTime: (/* @__PURE__ */ new Date()).toISOString()
675
+ }
676
+ ]
677
+ },
678
+ RequestId: `mock-request-${Date.now()}`
679
+ };
680
+ },
681
+ async describeDomainConfigs(params) {
682
+ logger.info("\u2699\uFE0F [MockCDN] \u6A21\u62DF\u83B7\u53D6\u57DF\u540D\u914D\u7F6E:", params);
683
+ return {
684
+ DomainConfigs: {
685
+ DomainConfig: [
686
+ {
687
+ FunctionName: "cache",
688
+ ConfigId: "mock-config-id",
689
+ Status: "success"
690
+ }
691
+ ]
692
+ },
693
+ RequestId: `mock-request-${Date.now()}`
694
+ };
695
+ },
696
+ async describeCdnDomainLogs(params) {
697
+ logger.info("\u{1F4CA} [MockCDN] \u6A21\u62DF\u83B7\u53D6\u65E5\u5FD7:", params);
698
+ return {
699
+ DomainLogDetails: {
700
+ DomainLogDetail: []
701
+ },
702
+ RequestId: `mock-request-${Date.now()}`
703
+ };
704
+ },
705
+ async describeDomainRealTimeData(params) {
706
+ logger.info("\u{1F4C8} [MockCDN] \u6A21\u62DF\u83B7\u53D6\u5B9E\u65F6\u6570\u636E:", params);
707
+ return {
708
+ RealTimeData: {
709
+ UsageData: [
710
+ {
711
+ TimeStamp: (/* @__PURE__ */ new Date()).toISOString(),
712
+ Value: Math.random() * 1e3
713
+ }
714
+ ]
715
+ },
716
+ DataInterval: "60",
717
+ RequestId: `mock-request-${Date.now()}`
718
+ };
719
+ }
720
+ };
721
+ }
722
+ /**
723
+ * 批量刷新缓存(带进度回调)
724
+ */
725
+ async batchRefreshCache(urls, batchSize = 20, onProgress) {
726
+ this.ensureInitialized();
727
+ logger.info(
728
+ `\u{1F504} [AliyunCDNProvider] \u5F00\u59CB\u6279\u91CF\u5237\u65B0\u7F13\u5B58\uFF0C\u603BURL\u6570: ${urls.length}\uFF0C\u6279\u6B21\u5927\u5C0F: ${batchSize}`
729
+ );
730
+ try {
731
+ const results = [];
732
+ let completed = 0;
733
+ for (let i = 0; i < urls.length; i += batchSize) {
734
+ const batch = urls.slice(i, i + batchSize);
735
+ const batchResult = await this.refreshCache(batch);
736
+ results.push(batchResult);
737
+ completed += batch.length;
738
+ if (onProgress) {
739
+ onProgress(completed, urls.length);
740
+ }
741
+ if (i + batchSize < urls.length) {
742
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
743
+ }
744
+ }
745
+ const successCount = results.filter((r) => r.success).length;
746
+ logger.info(`\u2705 [AliyunCDNProvider] \u6279\u91CF\u5237\u65B0\u5B8C\u6210\uFF0C\u6210\u529F: ${successCount}/${results.length}`);
747
+ return {
748
+ success: successCount === results.length,
749
+ data: {
750
+ totalBatches: results.length,
751
+ successBatches: successCount,
752
+ results
753
+ }
754
+ };
755
+ } catch (error) {
756
+ logger.error(`\u274C [AliyunCDNProvider] \u6279\u91CF\u5237\u65B0\u5931\u8D25:`, error);
757
+ return {
758
+ success: false,
759
+ error: `\u6279\u91CF\u5237\u65B0\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
760
+ };
761
+ }
762
+ }
763
+ };
764
+
765
+ // src/universalFile/server/providers/CdnCacheStrategy.ts
766
+ var CacheStrategyType = /* @__PURE__ */ ((CacheStrategyType3) => {
767
+ CacheStrategyType3["IMAGE"] = "image";
768
+ CacheStrategyType3["VIDEO"] = "video";
769
+ CacheStrategyType3["AUDIO"] = "audio";
770
+ CacheStrategyType3["DOCUMENT"] = "document";
771
+ CacheStrategyType3["ARCHIVE"] = "archive";
772
+ CacheStrategyType3["STATIC"] = "static";
773
+ CacheStrategyType3["OTHER"] = "other";
774
+ return CacheStrategyType3;
775
+ })(CacheStrategyType || {});
776
+ var CdnCacheStrategy = class {
777
+ constructor() {
778
+ this.strategies = /* @__PURE__ */ new Map();
779
+ this.stats = /* @__PURE__ */ new Map();
780
+ this.initializeDefaultStrategies();
781
+ }
782
+ /**
783
+ * 初始化默认缓存策略
784
+ */
785
+ initializeDefaultStrategies() {
786
+ this.strategies.set("image" /* IMAGE */, {
787
+ type: "image" /* IMAGE */,
788
+ ttl: 30 * 24 * 3600,
789
+ // 30天
790
+ browserCache: true,
791
+ browserCacheTtl: 7 * 24 * 3600,
792
+ // 7天
793
+ cdnCache: true,
794
+ cdnCacheTtl: 30 * 24 * 3600,
795
+ // 30天
796
+ enableWarmup: true,
797
+ cacheControl: "public, max-age=604800, s-maxage=2592000",
798
+ allowedMimeTypes: ["image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml"],
799
+ maxFileSize: 50 * 1024 * 1024
800
+ // 50MB
801
+ });
802
+ this.strategies.set("video" /* VIDEO */, {
803
+ type: "video" /* VIDEO */,
804
+ ttl: 7 * 24 * 3600,
805
+ // 7天
806
+ browserCache: true,
807
+ browserCacheTtl: 24 * 3600,
808
+ // 1天
809
+ cdnCache: true,
810
+ cdnCacheTtl: 7 * 24 * 3600,
811
+ // 7天
812
+ enableWarmup: false,
813
+ // 视频文件通常较大,不预热
814
+ cacheControl: "public, max-age=86400, s-maxage=604800",
815
+ allowedMimeTypes: ["video/mp4", "video/avi", "video/mov", "video/wmv", "video/webm"],
816
+ maxFileSize: 500 * 1024 * 1024
817
+ // 500MB
818
+ });
819
+ this.strategies.set("audio" /* AUDIO */, {
820
+ type: "audio" /* AUDIO */,
821
+ ttl: 14 * 24 * 3600,
822
+ // 14天
823
+ browserCache: true,
824
+ browserCacheTtl: 3 * 24 * 3600,
825
+ // 3天
826
+ cdnCache: true,
827
+ cdnCacheTtl: 14 * 24 * 3600,
828
+ // 14天
829
+ enableWarmup: true,
830
+ cacheControl: "public, max-age=259200, s-maxage=1209600",
831
+ allowedMimeTypes: ["audio/mpeg", "audio/wav", "audio/ogg", "audio/m4a", "audio/flac"],
832
+ maxFileSize: 100 * 1024 * 1024
833
+ // 100MB
834
+ });
835
+ this.strategies.set("document" /* DOCUMENT */, {
836
+ type: "document" /* DOCUMENT */,
837
+ ttl: 24 * 3600,
838
+ // 1天
839
+ browserCache: true,
840
+ browserCacheTtl: 3600,
841
+ // 1小时
842
+ cdnCache: true,
843
+ cdnCacheTtl: 24 * 3600,
844
+ // 1天
845
+ enableWarmup: false,
846
+ cacheControl: "public, max-age=3600, s-maxage=86400",
847
+ allowedMimeTypes: [
848
+ "application/pdf",
849
+ "application/msword",
850
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
851
+ "application/vnd.ms-excel",
852
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
853
+ "text/plain",
854
+ "text/csv"
855
+ ],
856
+ maxFileSize: 50 * 1024 * 1024
857
+ // 50MB
858
+ });
859
+ this.strategies.set("archive" /* ARCHIVE */, {
860
+ type: "archive" /* ARCHIVE */,
861
+ ttl: 24 * 3600,
862
+ // 1天
863
+ browserCache: true,
864
+ browserCacheTtl: 1800,
865
+ // 30分钟
866
+ cdnCache: true,
867
+ cdnCacheTtl: 24 * 3600,
868
+ // 1天
869
+ enableWarmup: false,
870
+ cacheControl: "public, max-age=1800, s-maxage=86400",
871
+ allowedMimeTypes: [
872
+ "application/zip",
873
+ "application/x-rar-compressed",
874
+ "application/x-7z-compressed",
875
+ "application/gzip",
876
+ "application/x-tar"
877
+ ],
878
+ maxFileSize: 100 * 1024 * 1024
879
+ // 100MB
880
+ });
881
+ this.strategies.set("static" /* STATIC */, {
882
+ type: "static" /* STATIC */,
883
+ ttl: 365 * 24 * 3600,
884
+ // 1年
885
+ browserCache: true,
886
+ browserCacheTtl: 30 * 24 * 3600,
887
+ // 30天
888
+ cdnCache: true,
889
+ cdnCacheTtl: 365 * 24 * 3600,
890
+ // 1年
891
+ enableWarmup: true,
892
+ cacheControl: "public, max-age=2592000, s-maxage=31536000, immutable",
893
+ allowedMimeTypes: [
894
+ "text/css",
895
+ "application/javascript",
896
+ "application/json",
897
+ "font/woff",
898
+ "font/woff2",
899
+ "font/ttf"
900
+ ],
901
+ maxFileSize: 10 * 1024 * 1024
902
+ // 10MB
903
+ });
904
+ this.strategies.set("other" /* OTHER */, {
905
+ type: "other" /* OTHER */,
906
+ ttl: 3600,
907
+ // 1小时
908
+ browserCache: true,
909
+ browserCacheTtl: 300,
910
+ // 5分钟
911
+ cdnCache: false,
912
+ cdnCacheTtl: 0,
913
+ enableWarmup: false,
914
+ cacheControl: "public, max-age=300",
915
+ allowedMimeTypes: [],
916
+ maxFileSize: 10 * 1024 * 1024
917
+ // 10MB
918
+ });
919
+ for (const type of Object.values(CacheStrategyType)) {
920
+ this.stats.set(type, {
921
+ hits: 0,
922
+ misses: 0,
923
+ hitRate: 0,
924
+ totalRequests: 0,
925
+ estimatedSize: 0,
926
+ bandwidthSaved: 0
927
+ });
928
+ }
929
+ }
930
+ /**
931
+ * 根据MIME类型获取缓存策略
932
+ */
933
+ getStrategyByMimeType(mimeType) {
934
+ for (const [_type, strategy] of this.strategies.entries()) {
935
+ if (strategy.allowedMimeTypes.includes(mimeType)) {
936
+ return strategy;
937
+ }
938
+ }
939
+ if (mimeType.startsWith("image/")) {
940
+ return this.strategies.get("image" /* IMAGE */);
941
+ } else if (mimeType.startsWith("video/")) {
942
+ return this.strategies.get("video" /* VIDEO */);
943
+ } else if (mimeType.startsWith("audio/")) {
944
+ return this.strategies.get("audio" /* AUDIO */);
945
+ } else if (mimeType.includes("pdf") || mimeType.includes("document") || mimeType.includes("word") || mimeType.includes("excel")) {
946
+ return this.strategies.get("document" /* DOCUMENT */);
947
+ } else if (mimeType.includes("zip") || mimeType.includes("compressed") || mimeType.includes("archive")) {
948
+ return this.strategies.get("archive" /* ARCHIVE */);
949
+ }
950
+ return this.strategies.get("other" /* OTHER */);
951
+ }
952
+ /**
953
+ * 生成缓存控制头
954
+ */
955
+ generateCacheHeaders(mimeType, fileSize) {
956
+ const strategy = this.getStrategyByMimeType(mimeType);
957
+ if (fileSize && strategy.maxFileSize && fileSize > strategy.maxFileSize) {
958
+ return {
959
+ "Cache-Control": "public, max-age=300",
960
+ // 5分钟
961
+ Expires: new Date(Date.now() + 300 * 1e3).toUTCString()
962
+ };
963
+ }
964
+ const headers = {
965
+ "Cache-Control": strategy.cacheControl,
966
+ Expires: new Date(Date.now() + strategy.browserCacheTtl * 1e3).toUTCString()
967
+ };
968
+ if (strategy.browserCache) {
969
+ headers["ETag"] = `"${Date.now()}"`;
970
+ }
971
+ headers["Last-Modified"] = (/* @__PURE__ */ new Date()).toUTCString();
972
+ if (strategy.type === "static" /* STATIC */) {
973
+ headers["Cache-Control"] += ", immutable";
974
+ }
975
+ return headers;
976
+ }
977
+ /**
978
+ * 检查是否需要缓存预热
979
+ */
980
+ shouldWarmupCache(mimeType) {
981
+ const strategy = this.getStrategyByMimeType(mimeType);
982
+ return strategy.enableWarmup;
983
+ }
984
+ /**
985
+ * 记录缓存命中
986
+ */
987
+ recordCacheHit(mimeType, fileSize = 0) {
988
+ const strategy = this.getStrategyByMimeType(mimeType);
989
+ const stats = this.stats.get(strategy.type);
990
+ if (stats) {
991
+ stats.hits++;
992
+ stats.totalRequests++;
993
+ stats.bandwidthSaved += fileSize;
994
+ stats.hitRate = stats.hits / stats.totalRequests * 100;
995
+ }
996
+ }
997
+ /**
998
+ * 记录缓存未命中
999
+ */
1000
+ recordCacheMiss(mimeType, fileSize = 0) {
1001
+ const strategy = this.getStrategyByMimeType(mimeType);
1002
+ const stats = this.stats.get(strategy.type);
1003
+ if (stats) {
1004
+ stats.misses++;
1005
+ stats.totalRequests++;
1006
+ stats.estimatedSize += fileSize;
1007
+ stats.hitRate = stats.hits / stats.totalRequests * 100;
1008
+ }
1009
+ }
1010
+ /**
1011
+ * 获取缓存统计信息
1012
+ */
1013
+ getCacheStats() {
1014
+ return new Map(this.stats);
1015
+ }
1016
+ /**
1017
+ * 获取总体缓存统计
1018
+ */
1019
+ getOverallStats() {
1020
+ const overall = {
1021
+ hits: 0,
1022
+ misses: 0,
1023
+ hitRate: 0,
1024
+ totalRequests: 0,
1025
+ estimatedSize: 0,
1026
+ bandwidthSaved: 0
1027
+ };
1028
+ for (const stats of this.stats.values()) {
1029
+ overall.hits += stats.hits;
1030
+ overall.misses += stats.misses;
1031
+ overall.totalRequests += stats.totalRequests;
1032
+ overall.estimatedSize += stats.estimatedSize;
1033
+ overall.bandwidthSaved += stats.bandwidthSaved;
1034
+ }
1035
+ overall.hitRate = overall.totalRequests > 0 ? overall.hits / overall.totalRequests * 100 : 0;
1036
+ return overall;
1037
+ }
1038
+ /**
1039
+ * 生成缓存优化建议
1040
+ */
1041
+ generateOptimizationSuggestions() {
1042
+ const suggestions = [];
1043
+ for (const [type, stats] of this.stats.entries()) {
1044
+ if (stats.totalRequests > 100) {
1045
+ if (stats.hitRate < 60) {
1046
+ suggestions.push({
1047
+ type,
1048
+ issue: `${type}\u7C7B\u578B\u6587\u4EF6\u7F13\u5B58\u547D\u4E2D\u7387\u8FC7\u4F4E (${stats.hitRate.toFixed(1)}%)`,
1049
+ suggestion: "\u8003\u8651\u589E\u52A0\u7F13\u5B58\u65F6\u95F4\u6216\u542F\u7528\u9884\u70ED\u673A\u5236",
1050
+ severity: stats.hitRate < 30 ? "high" : "medium"
1051
+ });
1052
+ }
1053
+ if (stats.estimatedSize > 1024 * 1024 * 1024) {
1054
+ suggestions.push({
1055
+ type,
1056
+ issue: `${type}\u7C7B\u578B\u6587\u4EF6\u7F13\u5B58\u5360\u7528\u7A7A\u95F4\u8FC7\u5927 (${(stats.estimatedSize / 1024 / 1024 / 1024).toFixed(2)}GB)`,
1057
+ suggestion: "\u8003\u8651\u51CF\u5C11\u7F13\u5B58\u65F6\u95F4\u6216\u4F18\u5316\u6587\u4EF6\u538B\u7F29",
1058
+ severity: "medium"
1059
+ });
1060
+ }
1061
+ }
1062
+ }
1063
+ return suggestions;
1064
+ }
1065
+ /**
1066
+ * 更新缓存策略
1067
+ */
1068
+ updateStrategy(type, config) {
1069
+ const currentStrategy = this.strategies.get(type);
1070
+ if (currentStrategy) {
1071
+ this.strategies.set(type, { ...currentStrategy, ...config });
1072
+ }
1073
+ }
1074
+ /**
1075
+ * 重置统计信息
1076
+ */
1077
+ resetStats() {
1078
+ for (const type of Object.values(CacheStrategyType)) {
1079
+ this.stats.set(type, {
1080
+ hits: 0,
1081
+ misses: 0,
1082
+ hitRate: 0,
1083
+ totalRequests: 0,
1084
+ estimatedSize: 0,
1085
+ bandwidthSaved: 0
1086
+ });
1087
+ }
1088
+ }
1089
+ };
1090
+ var cdnCacheStrategy = new CdnCacheStrategy();
1091
+ var logger2 = chunk6PRFP5EG_js.createLogger("ImageProcessor");
1092
+ var ImageProcessor = class {
1093
+ constructor() {
1094
+ this.type = "image";
1095
+ this.sharp = null;
1096
+ this.isInitialized = false;
1097
+ }
1098
+ /**
1099
+ * 初始化图片处理器
1100
+ */
1101
+ async initialize() {
1102
+ logger2.info("\u{1F5BC}\uFE0F [ImageProcessor] \u521D\u59CB\u5316\u56FE\u7247\u5904\u7406\u5668...");
1103
+ try {
1104
+ try {
1105
+ this.sharp = chunkDGUM43GV_js.__require("sharp");
1106
+ logger2.info("\u2705 [ImageProcessor] Sharp\u5E93\u52A0\u8F7D\u6210\u529F");
1107
+ } catch (error) {
1108
+ console.warn("\u26A0\uFE0F [ImageProcessor] Sharp\u5E93\u672A\u5B89\u88C5\uFF0C\u4F7F\u7528\u6A21\u62DF\u6A21\u5F0F");
1109
+ this.sharp = this.createMockSharp();
1110
+ }
1111
+ this.isInitialized = true;
1112
+ logger2.info("\u2705 [ImageProcessor] \u56FE\u7247\u5904\u7406\u5668\u521D\u59CB\u5316\u5B8C\u6210");
1113
+ } catch (error) {
1114
+ console.error("\u274C [ImageProcessor] \u56FE\u7247\u5904\u7406\u5668\u521D\u59CB\u5316\u5931\u8D25:", error);
1115
+ throw error;
1116
+ }
1117
+ }
1118
+ /**
1119
+ * 处理图片文件
1120
+ */
1121
+ async process(inputPath, outputPath, options) {
1122
+ this.ensureInitialized();
1123
+ if (options.type !== "image") {
1124
+ throw new Error("\u5904\u7406\u9009\u9879\u7C7B\u578B\u4E0D\u5339\u914D\uFF1A\u671F\u671B image");
1125
+ }
1126
+ const imageOptions = options;
1127
+ const startTime = Date.now();
1128
+ logger2.info(`\u{1F5BC}\uFE0F [ImageProcessor] \u5F00\u59CB\u5904\u7406\u56FE\u7247: ${inputPath}`);
1129
+ try {
1130
+ if (!fs.existsSync(inputPath)) {
1131
+ throw new Error(`\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${inputPath}`);
1132
+ }
1133
+ const outputDir = path3__namespace.dirname(outputPath);
1134
+ await fs.promises.mkdir(outputDir, { recursive: true });
1135
+ const metadata = await this.getImageMetadata(inputPath);
1136
+ logger2.info(
1137
+ `\u{1F4CA} [ImageProcessor] \u56FE\u7247\u4FE1\u606F: ${metadata.width}x${metadata.height}, \u683C\u5F0F: ${metadata.format}`
1138
+ );
1139
+ let sharpInstance = this.sharp(inputPath);
1140
+ sharpInstance = await this.applyImageOperations(sharpInstance, imageOptions);
1141
+ const outputFormat = this.determineOutputFormat(outputPath, imageOptions.format);
1142
+ sharpInstance = this.applyOutputSettings(sharpInstance, outputFormat, imageOptions.quality);
1143
+ const info = await sharpInstance.toFile(outputPath);
1144
+ let thumbnailPath;
1145
+ if (this.shouldGenerateThumbnail(imageOptions)) {
1146
+ thumbnailPath = await this.generateThumbnail(inputPath, outputPath, imageOptions);
1147
+ }
1148
+ const processingTime = Date.now() - startTime;
1149
+ logger2.info(`\u2705 [ImageProcessor] \u56FE\u7247\u5904\u7406\u5B8C\u6210: ${outputPath}, \u8017\u65F6: ${processingTime}ms`);
1150
+ return {
1151
+ success: true,
1152
+ processedPath: outputPath,
1153
+ processedSize: info.size,
1154
+ thumbnailPath,
1155
+ processingTime,
1156
+ data: {
1157
+ originalSize: (await fs.promises.stat(inputPath)).size,
1158
+ processedSize: info.size,
1159
+ compressionRatio: ((await fs.promises.stat(inputPath)).size - info.size) / (await fs.promises.stat(inputPath)).size,
1160
+ dimensions: {
1161
+ original: { width: metadata.width, height: metadata.height },
1162
+ processed: { width: info.width, height: info.height }
1163
+ },
1164
+ format: {
1165
+ original: metadata.format,
1166
+ processed: outputFormat
1167
+ }
1168
+ }
1169
+ };
1170
+ } catch (error) {
1171
+ console.error(`\u274C [ImageProcessor] \u56FE\u7247\u5904\u7406\u5931\u8D25: ${inputPath}:`, error);
1172
+ return {
1173
+ success: false,
1174
+ error: `\u56FE\u7247\u5904\u7406\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
1175
+ processingTime: Date.now() - startTime
1176
+ };
1177
+ }
1178
+ }
1179
+ /**
1180
+ * 检查文件是否支持处理
1181
+ */
1182
+ supports(mimeType) {
1183
+ const supportedTypes = [
1184
+ "image/jpeg",
1185
+ "image/jpg",
1186
+ "image/png",
1187
+ "image/webp",
1188
+ "image/avif",
1189
+ "image/gif",
1190
+ "image/tiff",
1191
+ "image/bmp"
1192
+ ];
1193
+ return supportedTypes.includes(mimeType.toLowerCase());
1194
+ }
1195
+ /**
1196
+ * 获取图片文件信息
1197
+ */
1198
+ async getFileInfo(filePath) {
1199
+ this.ensureInitialized();
1200
+ try {
1201
+ const metadata = await this.getImageMetadata(filePath);
1202
+ const stats = await fs.promises.stat(filePath);
1203
+ return {
1204
+ dimensions: {
1205
+ width: metadata.width,
1206
+ height: metadata.height
1207
+ },
1208
+ format: metadata.format,
1209
+ channels: metadata.channels,
1210
+ hasAlpha: metadata.hasAlpha,
1211
+ density: metadata.density,
1212
+ orientation: metadata.orientation,
1213
+ fileSize: stats.size,
1214
+ aspectRatio: metadata.width / metadata.height,
1215
+ megapixels: metadata.width * metadata.height / 1e6
1216
+ };
1217
+ } catch (error) {
1218
+ console.error(`\u274C [ImageProcessor] \u83B7\u53D6\u56FE\u7247\u4FE1\u606F\u5931\u8D25: ${filePath}:`, error);
1219
+ throw error;
1220
+ }
1221
+ }
1222
+ // ============= 私有方法 =============
1223
+ /**
1224
+ * 确保处理器已初始化
1225
+ */
1226
+ ensureInitialized() {
1227
+ if (!this.isInitialized || !this.sharp) {
1228
+ throw new Error("\u56FE\u7247\u5904\u7406\u5668\u672A\u521D\u59CB\u5316");
1229
+ }
1230
+ }
1231
+ /**
1232
+ * 获取图片元数据
1233
+ */
1234
+ async getImageMetadata(filePath) {
1235
+ try {
1236
+ const metadata = await this.sharp(filePath).metadata();
1237
+ return {
1238
+ format: metadata.format || "unknown",
1239
+ width: metadata.width || 0,
1240
+ height: metadata.height || 0,
1241
+ channels: metadata.channels || 3,
1242
+ density: metadata.density || 72,
1243
+ hasAlpha: metadata.hasAlpha || false,
1244
+ orientation: metadata.orientation
1245
+ };
1246
+ } catch (error) {
1247
+ console.error(`\u274C [ImageProcessor] \u83B7\u53D6\u56FE\u7247\u5143\u6570\u636E\u5931\u8D25: ${filePath}:`, error);
1248
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247\u5143\u6570\u636E: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`);
1249
+ }
1250
+ }
1251
+ /**
1252
+ * 应用图片处理操作
1253
+ */
1254
+ async applyImageOperations(sharpInstance, options) {
1255
+ if (options.width || options.height) {
1256
+ const resizeOptions = {
1257
+ width: options.width,
1258
+ height: options.height,
1259
+ fit: "inside",
1260
+ // 保持纵横比
1261
+ withoutEnlargement: true
1262
+ // 不放大小图片
1263
+ };
1264
+ sharpInstance = sharpInstance.resize(resizeOptions);
1265
+ logger2.info(
1266
+ `\u{1F527} [ImageProcessor] \u5E94\u7528\u5C3A\u5BF8\u8C03\u6574: ${options.width || "auto"}x${options.height || "auto"}`
1267
+ );
1268
+ }
1269
+ sharpInstance = sharpInstance.rotate();
1270
+ if (options.watermark && options.watermarkOptions) {
1271
+ sharpInstance = await this.applyWatermark(sharpInstance, options.watermarkOptions);
1272
+ }
1273
+ return sharpInstance;
1274
+ }
1275
+ /**
1276
+ * 应用水印
1277
+ */
1278
+ async applyWatermark(sharpInstance, watermarkOptions) {
1279
+ try {
1280
+ if (watermarkOptions.text) {
1281
+ logger2.info(`\u{1F4A7} [ImageProcessor] \u5E94\u7528\u6587\u5B57\u6C34\u5370: ${watermarkOptions.text}`);
1282
+ const textSvg = this.createTextWatermarkSvg(
1283
+ watermarkOptions.text,
1284
+ watermarkOptions.opacity || 0.5
1285
+ );
1286
+ const textBuffer = Buffer.from(textSvg);
1287
+ sharpInstance = sharpInstance.composite([
1288
+ {
1289
+ input: textBuffer,
1290
+ gravity: this.getWatermarkGravity(watermarkOptions.position || "bottom-right")
1291
+ }
1292
+ ]);
1293
+ } else if (watermarkOptions.image && fs.existsSync(watermarkOptions.image)) {
1294
+ logger2.info(`\u{1F4A7} [ImageProcessor] \u5E94\u7528\u56FE\u7247\u6C34\u5370: ${watermarkOptions.image}`);
1295
+ let watermarkBuffer = await fs.promises.readFile(watermarkOptions.image);
1296
+ if (watermarkOptions.opacity && watermarkOptions.opacity < 1) {
1297
+ const watermarkSharp = this.sharp(watermarkBuffer).png().modulate({ brightness: 1, saturation: 1, alpha: watermarkOptions.opacity });
1298
+ watermarkBuffer = await watermarkSharp.toBuffer();
1299
+ }
1300
+ sharpInstance = sharpInstance.composite([
1301
+ {
1302
+ input: watermarkBuffer,
1303
+ gravity: this.getWatermarkGravity(watermarkOptions.position || "bottom-right")
1304
+ }
1305
+ ]);
1306
+ }
1307
+ return sharpInstance;
1308
+ } catch (error) {
1309
+ console.warn(`\u26A0\uFE0F [ImageProcessor] \u6C34\u5370\u5E94\u7528\u5931\u8D25\uFF0C\u8DF3\u8FC7\u6C34\u5370:`, error);
1310
+ return sharpInstance;
1311
+ }
1312
+ }
1313
+ /**
1314
+ * 创建文字水印SVG
1315
+ */
1316
+ createTextWatermarkSvg(text, opacity) {
1317
+ const fontSize = 24;
1318
+ const padding = 10;
1319
+ return `
1320
+ <svg width="200" height="50" xmlns="http://www.w3.org/2000/svg">
1321
+ <text
1322
+ x="${padding}"
1323
+ y="${fontSize + padding}"
1324
+ font-family="Arial, sans-serif"
1325
+ font-size="${fontSize}"
1326
+ fill="white"
1327
+ fill-opacity="${opacity}"
1328
+ stroke="black"
1329
+ stroke-width="1"
1330
+ stroke-opacity="${opacity * 0.8}"
1331
+ >
1332
+ ${text}
1333
+ </text>
1334
+ </svg>
1335
+ `.trim();
1336
+ }
1337
+ /**
1338
+ * 获取水印位置对应的gravity值
1339
+ */
1340
+ getWatermarkGravity(position) {
1341
+ const gravityMap = {
1342
+ "top-left": "northwest",
1343
+ "top-right": "northeast",
1344
+ "bottom-left": "southwest",
1345
+ "bottom-right": "southeast",
1346
+ center: "center"
1347
+ };
1348
+ return gravityMap[position] || "southeast";
1349
+ }
1350
+ /**
1351
+ * 确定输出格式
1352
+ */
1353
+ determineOutputFormat(outputPath, requestedFormat) {
1354
+ if (requestedFormat) {
1355
+ return requestedFormat;
1356
+ }
1357
+ const ext = path3__namespace.extname(outputPath).toLowerCase();
1358
+ const formatMap = {
1359
+ ".jpg": "jpeg",
1360
+ ".jpeg": "jpeg",
1361
+ ".png": "png",
1362
+ ".webp": "webp",
1363
+ ".avif": "avif"
1364
+ };
1365
+ return formatMap[ext] || "jpeg";
1366
+ }
1367
+ /**
1368
+ * 应用输出设置
1369
+ */
1370
+ applyOutputSettings(sharpInstance, format, quality) {
1371
+ const defaultQuality = 80;
1372
+ const finalQuality = quality || defaultQuality;
1373
+ switch (format) {
1374
+ case "jpeg":
1375
+ return sharpInstance.jpeg({
1376
+ quality: finalQuality,
1377
+ progressive: true,
1378
+ mozjpeg: true
1379
+ });
1380
+ case "png":
1381
+ return sharpInstance.png({
1382
+ quality: finalQuality,
1383
+ progressive: true,
1384
+ compressionLevel: 6
1385
+ });
1386
+ case "webp":
1387
+ return sharpInstance.webp({
1388
+ quality: finalQuality,
1389
+ effort: 4
1390
+ });
1391
+ case "avif":
1392
+ return sharpInstance.avif({
1393
+ quality: finalQuality,
1394
+ effort: 4
1395
+ });
1396
+ default:
1397
+ return sharpInstance.jpeg({ quality: finalQuality });
1398
+ }
1399
+ }
1400
+ /**
1401
+ * 是否需要生成缩略图
1402
+ */
1403
+ shouldGenerateThumbnail(options) {
1404
+ const isSmallImage = options.width && options.width <= 300 || options.height && options.height <= 300;
1405
+ return !isSmallImage;
1406
+ }
1407
+ /**
1408
+ * 生成缩略图
1409
+ */
1410
+ async generateThumbnail(inputPath, outputPath, _options) {
1411
+ try {
1412
+ const thumbnailPath = this.getThumbnailPath(outputPath);
1413
+ const thumbnailSize = 200;
1414
+ await this.sharp(inputPath).resize({
1415
+ width: thumbnailSize,
1416
+ height: thumbnailSize,
1417
+ fit: "cover",
1418
+ position: "center"
1419
+ }).jpeg({ quality: 70 }).toFile(thumbnailPath);
1420
+ logger2.info(`\u{1F5BC}\uFE0F [ImageProcessor] \u7F29\u7565\u56FE\u751F\u6210\u5B8C\u6210: ${thumbnailPath}`);
1421
+ return thumbnailPath;
1422
+ } catch (error) {
1423
+ console.warn(`\u26A0\uFE0F [ImageProcessor] \u7F29\u7565\u56FE\u751F\u6210\u5931\u8D25:`, error);
1424
+ throw error;
1425
+ }
1426
+ }
1427
+ /**
1428
+ * 获取缩略图路径
1429
+ */
1430
+ getThumbnailPath(originalPath) {
1431
+ const ext = path3__namespace.extname(originalPath);
1432
+ const basePath = originalPath.replace(ext, "");
1433
+ return `${basePath}_thumb${ext}`;
1434
+ }
1435
+ /**
1436
+ * 创建模拟Sharp对象(开发测试用)
1437
+ */
1438
+ createMockSharp() {
1439
+ logger2.info("\u{1F9EA} [ImageProcessor] \u521B\u5EFA\u6A21\u62DFSharp\u5904\u7406\u5668");
1440
+ const mockSharp = (input) => {
1441
+ logger2.info(`\u{1F9EA} [MockSharp] \u5904\u7406\u56FE\u7247: ${input}`);
1442
+ return {
1443
+ metadata: async () => ({
1444
+ format: "jpeg",
1445
+ width: 1920,
1446
+ height: 1080,
1447
+ channels: 3,
1448
+ density: 72,
1449
+ hasAlpha: false
1450
+ }),
1451
+ resize: (options) => {
1452
+ logger2.info(`\u{1F9EA} [MockSharp] \u8C03\u6574\u5C3A\u5BF8:`, options);
1453
+ return mockSharp(input);
1454
+ },
1455
+ rotate: () => {
1456
+ logger2.info(`\u{1F9EA} [MockSharp] \u81EA\u52A8\u65CB\u8F6C`);
1457
+ return mockSharp(input);
1458
+ },
1459
+ composite: (operations) => {
1460
+ logger2.info(`\u{1F9EA} [MockSharp] \u5408\u6210\u64CD\u4F5C:`, operations.length);
1461
+ return mockSharp(input);
1462
+ },
1463
+ jpeg: (options) => {
1464
+ logger2.info(`\u{1F9EA} [MockSharp] JPEG\u8F93\u51FA:`, options);
1465
+ return mockSharp(input);
1466
+ },
1467
+ png: (options) => {
1468
+ logger2.info(`\u{1F9EA} [MockSharp] PNG\u8F93\u51FA:`, options);
1469
+ return mockSharp(input);
1470
+ },
1471
+ webp: (options) => {
1472
+ logger2.info(`\u{1F9EA} [MockSharp] WebP\u8F93\u51FA:`, options);
1473
+ return mockSharp(input);
1474
+ },
1475
+ avif: (options) => {
1476
+ logger2.info(`\u{1F9EA} [MockSharp] AVIF\u8F93\u51FA:`, options);
1477
+ return mockSharp(input);
1478
+ },
1479
+ toFile: async (outputPath) => {
1480
+ logger2.info(`\u{1F9EA} [MockSharp] \u4FDD\u5B58\u5230\u6587\u4EF6: ${outputPath}`);
1481
+ const outputDir = path3__namespace.dirname(outputPath);
1482
+ await fs.promises.mkdir(outputDir, { recursive: true });
1483
+ await fs.promises.writeFile(outputPath, `Mock processed image from ${input}`);
1484
+ return {
1485
+ format: "jpeg",
1486
+ width: 800,
1487
+ height: 600,
1488
+ channels: 3,
1489
+ premultiplied: false,
1490
+ size: 1024 * 50
1491
+ // 50KB
1492
+ };
1493
+ },
1494
+ toBuffer: async () => {
1495
+ logger2.info(`\u{1F9EA} [MockSharp] \u8F6C\u6362\u4E3ABuffer`);
1496
+ return Buffer.from("Mock image buffer");
1497
+ }
1498
+ };
1499
+ };
1500
+ return mockSharp;
1501
+ }
1502
+ /**
1503
+ * 批量图片处理
1504
+ */
1505
+ async batchProcess(inputPaths, outputDir, options, onProgress) {
1506
+ this.ensureInitialized();
1507
+ logger2.info(`\u{1F5BC}\uFE0F [ImageProcessor] \u5F00\u59CB\u6279\u91CF\u5904\u7406 ${inputPaths.length} \u5F20\u56FE\u7247`);
1508
+ const results = [];
1509
+ for (let i = 0; i < inputPaths.length; i++) {
1510
+ const inputPath = inputPaths[i];
1511
+ const fileName = path3__namespace.basename(inputPath);
1512
+ const outputPath = path3__namespace.join(outputDir, fileName);
1513
+ try {
1514
+ const result = await this.process(inputPath, outputPath, options);
1515
+ results.push(result);
1516
+ if (onProgress) {
1517
+ onProgress(i + 1, inputPaths.length);
1518
+ }
1519
+ } catch (error) {
1520
+ console.error(`\u274C [ImageProcessor] \u6279\u91CF\u5904\u7406\u5931\u8D25: ${inputPath}:`, error);
1521
+ results.push({
1522
+ success: false,
1523
+ error: `\u5904\u7406\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
1524
+ });
1525
+ }
1526
+ }
1527
+ const successCount = results.filter((r) => r.success).length;
1528
+ logger2.info(`\u2705 [ImageProcessor] \u6279\u91CF\u5904\u7406\u5B8C\u6210\uFF0C\u6210\u529F: ${successCount}/${inputPaths.length}`);
1529
+ return results;
1530
+ }
1531
+ };
1532
+ var logger3 = chunk6PRFP5EG_js.createLogger("AudioProcessor");
1533
+ var AudioProcessor = class {
1534
+ constructor() {
1535
+ this.type = "audio";
1536
+ this.ffmpeg = null;
1537
+ this.isInitialized = false;
1538
+ }
1539
+ /**
1540
+ * 初始化音频处理器
1541
+ */
1542
+ async initialize() {
1543
+ logger3.info("\u{1F3B5} [AudioProcessor] \u521D\u59CB\u5316\u97F3\u9891\u5904\u7406\u5668...");
1544
+ try {
1545
+ try {
1546
+ this.ffmpeg = chunkDGUM43GV_js.__require("fluent-ffmpeg");
1547
+ logger3.info("\u2705 [AudioProcessor] FFmpeg\u5E93\u52A0\u8F7D\u6210\u529F");
1548
+ } catch (error) {
1549
+ console.warn("\u26A0\uFE0F [AudioProcessor] FFmpeg\u5E93\u672A\u5B89\u88C5\uFF0C\u4F7F\u7528\u6A21\u62DF\u6A21\u5F0F");
1550
+ this.ffmpeg = this.createMockFFmpeg();
1551
+ }
1552
+ this.isInitialized = true;
1553
+ logger3.info("\u2705 [AudioProcessor] \u97F3\u9891\u5904\u7406\u5668\u521D\u59CB\u5316\u5B8C\u6210");
1554
+ } catch (error) {
1555
+ console.error("\u274C [AudioProcessor] \u97F3\u9891\u5904\u7406\u5668\u521D\u59CB\u5316\u5931\u8D25:", error);
1556
+ throw error;
1557
+ }
1558
+ }
1559
+ /**
1560
+ * 处理音频文件
1561
+ */
1562
+ async process(inputPath, outputPath, options) {
1563
+ this.ensureInitialized();
1564
+ if (options.type !== "audio") {
1565
+ throw new Error("\u5904\u7406\u9009\u9879\u7C7B\u578B\u4E0D\u5339\u914D\uFF1A\u671F\u671B audio");
1566
+ }
1567
+ const audioOptions = options;
1568
+ const startTime = Date.now();
1569
+ logger3.info(`\u{1F3B5} [AudioProcessor] \u5F00\u59CB\u5904\u7406\u97F3\u9891: ${inputPath}`);
1570
+ try {
1571
+ if (!fs.existsSync(inputPath)) {
1572
+ throw new Error(`\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${inputPath}`);
1573
+ }
1574
+ const outputDir = path3__namespace.dirname(outputPath);
1575
+ await fs.promises.mkdir(outputDir, { recursive: true });
1576
+ const metadata = await this.getAudioMetadata(inputPath);
1577
+ logger3.info(
1578
+ `\u{1F4CA} [AudioProcessor] \u97F3\u9891\u4FE1\u606F: ${this.formatDuration(metadata.duration)}, ${metadata.bitrate}kbps, ${metadata.sampleRate}Hz`
1579
+ );
1580
+ const outputFormat = this.determineOutputFormat(outputPath, audioOptions.format);
1581
+ await this.processAudio(inputPath, outputPath, audioOptions, outputFormat);
1582
+ const processedStats = await fs.promises.stat(outputPath);
1583
+ const processingTime = Date.now() - startTime;
1584
+ logger3.info(`\u2705 [AudioProcessor] \u97F3\u9891\u5904\u7406\u5B8C\u6210: ${outputPath}, \u8017\u65F6: ${processingTime}ms`);
1585
+ return {
1586
+ success: true,
1587
+ processedPath: outputPath,
1588
+ processedSize: processedStats.size,
1589
+ processingTime,
1590
+ data: {
1591
+ originalSize: metadata.size,
1592
+ processedSize: processedStats.size,
1593
+ compressionRatio: (metadata.size - processedStats.size) / metadata.size,
1594
+ duration: metadata.duration,
1595
+ originalFormat: metadata.format,
1596
+ processedFormat: outputFormat,
1597
+ originalBitrate: metadata.bitrate,
1598
+ processedBitrate: audioOptions.bitrate || metadata.bitrate,
1599
+ sampleRate: audioOptions.sampleRate || metadata.sampleRate,
1600
+ channels: audioOptions.channels || metadata.channels
1601
+ }
1602
+ };
1603
+ } catch (error) {
1604
+ console.error(`\u274C [AudioProcessor] \u97F3\u9891\u5904\u7406\u5931\u8D25: ${inputPath}:`, error);
1605
+ return {
1606
+ success: false,
1607
+ error: `\u97F3\u9891\u5904\u7406\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
1608
+ processingTime: Date.now() - startTime
1609
+ };
1610
+ }
1611
+ }
1612
+ /**
1613
+ * 检查文件是否支持处理
1614
+ */
1615
+ supports(mimeType) {
1616
+ const supportedTypes = [
1617
+ "audio/mpeg",
1618
+ "audio/mp3",
1619
+ "audio/wav",
1620
+ "audio/wave",
1621
+ "audio/x-wav",
1622
+ "audio/ogg",
1623
+ "audio/vorbis",
1624
+ "audio/aac",
1625
+ "audio/x-aac",
1626
+ "audio/mp4",
1627
+ "audio/m4a",
1628
+ "audio/flac",
1629
+ "audio/x-flac",
1630
+ "audio/webm",
1631
+ "audio/opus"
1632
+ ];
1633
+ return supportedTypes.includes(mimeType.toLowerCase());
1634
+ }
1635
+ /**
1636
+ * 获取音频文件信息
1637
+ */
1638
+ async getFileInfo(filePath) {
1639
+ this.ensureInitialized();
1640
+ try {
1641
+ const metadata = await this.getAudioMetadata(filePath);
1642
+ return {
1643
+ duration: metadata.duration,
1644
+ durationFormatted: this.formatDuration(metadata.duration),
1645
+ bitrate: metadata.bitrate,
1646
+ sampleRate: metadata.sampleRate,
1647
+ channels: metadata.channels,
1648
+ channelsDescription: this.getChannelsDescription(metadata.channels),
1649
+ format: metadata.format,
1650
+ codec: metadata.codec,
1651
+ fileSize: metadata.size,
1652
+ quality: this.getQualityDescription(metadata.bitrate, metadata.sampleRate)
1653
+ };
1654
+ } catch (error) {
1655
+ console.error(`\u274C [AudioProcessor] \u83B7\u53D6\u97F3\u9891\u4FE1\u606F\u5931\u8D25: ${filePath}:`, error);
1656
+ throw error;
1657
+ }
1658
+ }
1659
+ // ============= 私有方法 =============
1660
+ /**
1661
+ * 确保处理器已初始化
1662
+ */
1663
+ ensureInitialized() {
1664
+ if (!this.isInitialized || !this.ffmpeg) {
1665
+ throw new Error("\u97F3\u9891\u5904\u7406\u5668\u672A\u521D\u59CB\u5316");
1666
+ }
1667
+ }
1668
+ /**
1669
+ * 获取音频元数据
1670
+ */
1671
+ async getAudioMetadata(filePath) {
1672
+ return new Promise((resolve, reject) => {
1673
+ try {
1674
+ this.ffmpeg.ffprobe(filePath, (err, metadata) => {
1675
+ if (err) {
1676
+ console.error(`\u274C [AudioProcessor] \u83B7\u53D6\u97F3\u9891\u5143\u6570\u636E\u5931\u8D25: ${filePath}:`, err);
1677
+ reject(new Error(`\u65E0\u6CD5\u8BFB\u53D6\u97F3\u9891\u5143\u6570\u636E: ${err.message}`));
1678
+ return;
1679
+ }
1680
+ const audioStream = metadata.streams?.find(
1681
+ (stream) => stream.codec_type === "audio"
1682
+ );
1683
+ if (!audioStream) {
1684
+ reject(new Error("\u6587\u4EF6\u4E2D\u672A\u627E\u5230\u97F3\u9891\u6D41"));
1685
+ return;
1686
+ }
1687
+ const result = {
1688
+ format: metadata.format?.format_name || "unknown",
1689
+ duration: parseFloat(metadata.format?.duration || "0"),
1690
+ bitrate: parseInt(audioStream.bit_rate || "0") / 1e3,
1691
+ // 转换为kbps
1692
+ sampleRate: parseInt(audioStream.sample_rate || "0"),
1693
+ channels: parseInt(audioStream.channels || "0"),
1694
+ codec: audioStream.codec_name || "unknown",
1695
+ size: parseInt(metadata.format?.size || "0")
1696
+ };
1697
+ resolve(result);
1698
+ });
1699
+ } catch (error) {
1700
+ reject(error);
1701
+ }
1702
+ });
1703
+ }
1704
+ /**
1705
+ * 执行音频处理
1706
+ */
1707
+ async processAudio(inputPath, outputPath, options, outputFormat) {
1708
+ return new Promise((resolve, reject) => {
1709
+ try {
1710
+ let command = this.ffmpeg(inputPath);
1711
+ command = this.setAudioCodec(command, outputFormat);
1712
+ if (options.bitrate) {
1713
+ command = command.audioBitrate(options.bitrate);
1714
+ logger3.info(`\u{1F527} [AudioProcessor] \u8BBE\u7F6E\u6BD4\u7279\u7387: ${options.bitrate}kbps`);
1715
+ }
1716
+ if (options.sampleRate) {
1717
+ command = command.audioFrequency(options.sampleRate);
1718
+ logger3.info(`\u{1F527} [AudioProcessor] \u8BBE\u7F6E\u91C7\u6837\u7387: ${options.sampleRate}Hz`);
1719
+ }
1720
+ if (options.channels) {
1721
+ command = command.audioChannels(options.channels);
1722
+ logger3.info(`\u{1F527} [AudioProcessor] \u8BBE\u7F6E\u58F0\u9053\u6570: ${options.channels}`);
1723
+ }
1724
+ command = command.format(outputFormat);
1725
+ command.on("progress", (progress) => {
1726
+ if (progress.percent) {
1727
+ logger3.info(`\u{1F3B5} [AudioProcessor] \u5904\u7406\u8FDB\u5EA6: ${Math.round(progress.percent)}%`);
1728
+ }
1729
+ });
1730
+ command.on("error", (err) => {
1731
+ console.error(`\u274C [AudioProcessor] FFmpeg\u5904\u7406\u9519\u8BEF:`, err);
1732
+ reject(new Error(`\u97F3\u9891\u5904\u7406\u5931\u8D25: ${err.message}`));
1733
+ });
1734
+ command.on("end", () => {
1735
+ logger3.info(`\u2705 [AudioProcessor] FFmpeg\u5904\u7406\u5B8C\u6210: ${outputPath}`);
1736
+ resolve();
1737
+ });
1738
+ command.save(outputPath);
1739
+ } catch (error) {
1740
+ reject(error);
1741
+ }
1742
+ });
1743
+ }
1744
+ /**
1745
+ * 设置音频编解码器
1746
+ */
1747
+ setAudioCodec(command, format) {
1748
+ switch (format) {
1749
+ case "mp3":
1750
+ return command.audioCodec("libmp3lame");
1751
+ case "aac":
1752
+ return command.audioCodec("aac");
1753
+ case "ogg":
1754
+ return command.audioCodec("libvorbis");
1755
+ case "wav":
1756
+ return command.audioCodec("pcm_s16le");
1757
+ default:
1758
+ return command;
1759
+ }
1760
+ }
1761
+ /**
1762
+ * 确定输出格式
1763
+ */
1764
+ determineOutputFormat(outputPath, requestedFormat) {
1765
+ if (requestedFormat) {
1766
+ return requestedFormat;
1767
+ }
1768
+ const ext = path3__namespace.extname(outputPath).toLowerCase();
1769
+ const formatMap = {
1770
+ ".mp3": "mp3",
1771
+ ".wav": "wav",
1772
+ ".ogg": "ogg",
1773
+ ".aac": "aac",
1774
+ ".m4a": "aac"
1775
+ };
1776
+ return formatMap[ext] || "mp3";
1777
+ }
1778
+ /**
1779
+ * 格式化时长显示
1780
+ */
1781
+ formatDuration(seconds) {
1782
+ const minutes = Math.floor(seconds / 60);
1783
+ const remainingSeconds = Math.floor(seconds % 60);
1784
+ return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
1785
+ }
1786
+ /**
1787
+ * 获取声道描述
1788
+ */
1789
+ getChannelsDescription(channels) {
1790
+ switch (channels) {
1791
+ case 1:
1792
+ return "\u5355\u58F0\u9053";
1793
+ case 2:
1794
+ return "\u7ACB\u4F53\u58F0";
1795
+ case 6:
1796
+ return "5.1\u73AF\u7ED5\u58F0";
1797
+ case 8:
1798
+ return "7.1\u73AF\u7ED5\u58F0";
1799
+ default:
1800
+ return `${channels}\u58F0\u9053`;
1801
+ }
1802
+ }
1803
+ /**
1804
+ * 获取音质描述
1805
+ */
1806
+ getQualityDescription(bitrate, sampleRate) {
1807
+ if (bitrate >= 320 && sampleRate >= 44100) {
1808
+ return "\u9AD8\u97F3\u8D28";
1809
+ } else if (bitrate >= 192 && sampleRate >= 44100) {
1810
+ return "\u6807\u51C6\u97F3\u8D28";
1811
+ } else if (bitrate >= 128) {
1812
+ return "\u4E00\u822C\u97F3\u8D28";
1813
+ } else {
1814
+ return "\u4F4E\u97F3\u8D28";
1815
+ }
1816
+ }
1817
+ /**
1818
+ * 创建模拟FFmpeg对象(开发测试用)
1819
+ */
1820
+ createMockFFmpeg() {
1821
+ logger3.info("\u{1F9EA} [AudioProcessor] \u521B\u5EFA\u6A21\u62DFFFmpeg\u5904\u7406\u5668");
1822
+ const mockFFmpeg = (input) => {
1823
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u5904\u7406\u97F3\u9891: ${input}`);
1824
+ return {
1825
+ audioBitrate: (bitrate) => {
1826
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u6BD4\u7279\u7387: ${bitrate}kbps`);
1827
+ return mockFFmpeg(input);
1828
+ },
1829
+ audioFrequency: (sampleRate) => {
1830
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u91C7\u6837\u7387: ${sampleRate}Hz`);
1831
+ return mockFFmpeg(input);
1832
+ },
1833
+ audioChannels: (channels) => {
1834
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u58F0\u9053\u6570: ${channels}`);
1835
+ return mockFFmpeg(input);
1836
+ },
1837
+ audioCodec: (codec) => {
1838
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u7F16\u89E3\u7801\u5668: ${codec}`);
1839
+ return mockFFmpeg(input);
1840
+ },
1841
+ format: (format) => {
1842
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u8F93\u51FA\u683C\u5F0F: ${format}`);
1843
+ return mockFFmpeg(input);
1844
+ },
1845
+ on: (event, callback) => {
1846
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u6CE8\u518C\u4E8B\u4EF6\u76D1\u542C: ${event}`);
1847
+ if (event === "progress") {
1848
+ setTimeout(() => callback({ percent: 50 }), 100);
1849
+ setTimeout(() => callback({ percent: 100 }), 200);
1850
+ } else if (event === "end") {
1851
+ setTimeout(() => callback(), 300);
1852
+ }
1853
+ return mockFFmpeg(input);
1854
+ },
1855
+ save: async (outputPath) => {
1856
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u4FDD\u5B58\u97F3\u9891\u6587\u4EF6: ${outputPath}`);
1857
+ const outputDir = path3__namespace.dirname(outputPath);
1858
+ await fs.promises.mkdir(outputDir, { recursive: true });
1859
+ await fs.promises.writeFile(outputPath, `Mock processed audio from ${input}`);
1860
+ }
1861
+ };
1862
+ };
1863
+ mockFFmpeg.ffprobe = (filePath, callback) => {
1864
+ logger3.info(`\u{1F9EA} [MockFFmpeg] \u83B7\u53D6\u97F3\u9891\u5143\u6570\u636E: ${filePath}`);
1865
+ setTimeout(() => {
1866
+ const mockMetadata = {
1867
+ streams: [
1868
+ {
1869
+ codec_type: "audio",
1870
+ codec_name: "mp3",
1871
+ bit_rate: "128000",
1872
+ sample_rate: "44100",
1873
+ channels: "2"
1874
+ }
1875
+ ],
1876
+ format: {
1877
+ format_name: "mp3",
1878
+ duration: "180.5",
1879
+ size: "1024000"
1880
+ }
1881
+ };
1882
+ callback(null, mockMetadata);
1883
+ }, 100);
1884
+ };
1885
+ return mockFFmpeg;
1886
+ }
1887
+ /**
1888
+ * 批量音频处理
1889
+ */
1890
+ async batchProcess(inputPaths, outputDir, options, onProgress) {
1891
+ this.ensureInitialized();
1892
+ logger3.info(`\u{1F3B5} [AudioProcessor] \u5F00\u59CB\u6279\u91CF\u5904\u7406 ${inputPaths.length} \u4E2A\u97F3\u9891\u6587\u4EF6`);
1893
+ const results = [];
1894
+ for (let i = 0; i < inputPaths.length; i++) {
1895
+ const inputPath = inputPaths[i];
1896
+ const fileName = path3__namespace.basename(inputPath);
1897
+ const nameWithoutExt = path3__namespace.parse(fileName).name;
1898
+ const outputFormat = options.format || "mp3";
1899
+ const outputPath = path3__namespace.join(outputDir, `${nameWithoutExt}.${outputFormat}`);
1900
+ try {
1901
+ const result = await this.process(inputPath, outputPath, options);
1902
+ results.push(result);
1903
+ if (onProgress) {
1904
+ onProgress(i + 1, inputPaths.length);
1905
+ }
1906
+ } catch (error) {
1907
+ console.error(`\u274C [AudioProcessor] \u6279\u91CF\u5904\u7406\u5931\u8D25: ${inputPath}:`, error);
1908
+ results.push({
1909
+ success: false,
1910
+ error: `\u5904\u7406\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
1911
+ });
1912
+ }
1913
+ }
1914
+ const successCount = results.filter((r) => r.success).length;
1915
+ logger3.info(`\u2705 [AudioProcessor] \u6279\u91CF\u5904\u7406\u5B8C\u6210\uFF0C\u6210\u529F: ${successCount}/${inputPaths.length}`);
1916
+ return results;
1917
+ }
1918
+ /**
1919
+ * 提取音频封面
1920
+ */
1921
+ async extractCover(inputPath, outputPath) {
1922
+ this.ensureInitialized();
1923
+ return new Promise((resolve) => {
1924
+ try {
1925
+ this.ffmpeg(inputPath).outputOptions(["-an", "-vcodec copy"]).on("error", (err) => {
1926
+ console.warn(`\u26A0\uFE0F [AudioProcessor] \u63D0\u53D6\u5C01\u9762\u5931\u8D25: ${err.message}`);
1927
+ resolve(false);
1928
+ }).on("end", () => {
1929
+ logger3.info(`\u{1F5BC}\uFE0F [AudioProcessor] \u97F3\u9891\u5C01\u9762\u63D0\u53D6\u5B8C\u6210: ${outputPath}`);
1930
+ resolve(true);
1931
+ }).save(outputPath);
1932
+ } catch (error) {
1933
+ console.warn(`\u26A0\uFE0F [AudioProcessor] \u63D0\u53D6\u5C01\u9762\u5F02\u5E38:`, error);
1934
+ resolve(false);
1935
+ }
1936
+ });
1937
+ }
1938
+ /**
1939
+ * 音频降噪处理
1940
+ */
1941
+ async denoise(inputPath, outputPath) {
1942
+ this.ensureInitialized();
1943
+ const startTime = Date.now();
1944
+ logger3.info(`\u{1F507} [AudioProcessor] \u5F00\u59CB\u97F3\u9891\u964D\u566A: ${inputPath}`);
1945
+ return new Promise((resolve) => {
1946
+ try {
1947
+ this.ffmpeg(inputPath).audioFilters("highpass=f=200,lowpass=f=3000").on("error", (err) => {
1948
+ resolve({
1949
+ success: false,
1950
+ error: `\u964D\u566A\u5904\u7406\u5931\u8D25: ${err.message}`,
1951
+ processingTime: Date.now() - startTime
1952
+ });
1953
+ }).on("end", async () => {
1954
+ const processedStats = await fs.promises.stat(outputPath);
1955
+ resolve({
1956
+ success: true,
1957
+ processedPath: outputPath,
1958
+ processedSize: processedStats.size,
1959
+ processingTime: Date.now() - startTime,
1960
+ data: {
1961
+ operation: "denoise",
1962
+ filters: "highpass=f=200,lowpass=f=3000"
1963
+ }
1964
+ });
1965
+ }).save(outputPath);
1966
+ } catch (error) {
1967
+ resolve({
1968
+ success: false,
1969
+ error: `\u964D\u566A\u5904\u7406\u5F02\u5E38: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
1970
+ processingTime: Date.now() - startTime
1971
+ });
1972
+ }
1973
+ });
1974
+ }
1975
+ };
1976
+ var logger4 = chunk6PRFP5EG_js.createLogger("VideoProcessor");
1977
+ var VideoProcessor = class {
1978
+ constructor() {
1979
+ this.type = "video";
1980
+ this.ffmpeg = null;
1981
+ this.isInitialized = false;
1982
+ }
1983
+ /**
1984
+ * 初始化视频处理器
1985
+ */
1986
+ async initialize() {
1987
+ logger4.info("\u{1F3AC} [VideoProcessor] \u521D\u59CB\u5316\u89C6\u9891\u5904\u7406\u5668...");
1988
+ try {
1989
+ try {
1990
+ this.ffmpeg = chunkDGUM43GV_js.__require("fluent-ffmpeg");
1991
+ logger4.info("\u2705 [VideoProcessor] FFmpeg\u5E93\u52A0\u8F7D\u6210\u529F");
1992
+ } catch (error) {
1993
+ console.warn("\u26A0\uFE0F [VideoProcessor] FFmpeg\u5E93\u672A\u5B89\u88C5\uFF0C\u4F7F\u7528\u6A21\u62DF\u6A21\u5F0F");
1994
+ this.ffmpeg = this.createMockFFmpeg();
1995
+ }
1996
+ this.isInitialized = true;
1997
+ logger4.info("\u2705 [VideoProcessor] \u89C6\u9891\u5904\u7406\u5668\u521D\u59CB\u5316\u5B8C\u6210");
1998
+ } catch (error) {
1999
+ console.error("\u274C [VideoProcessor] \u89C6\u9891\u5904\u7406\u5668\u521D\u59CB\u5316\u5931\u8D25:", error);
2000
+ throw error;
2001
+ }
2002
+ }
2003
+ /**
2004
+ * 处理视频文件
2005
+ */
2006
+ async process(inputPath, outputPath, options) {
2007
+ this.ensureInitialized();
2008
+ if (options.type !== "video") {
2009
+ throw new Error("\u5904\u7406\u9009\u9879\u7C7B\u578B\u4E0D\u5339\u914D\uFF1A\u671F\u671B video");
2010
+ }
2011
+ const videoOptions = options;
2012
+ const startTime = Date.now();
2013
+ logger4.info(`\u{1F3AC} [VideoProcessor] \u5F00\u59CB\u5904\u7406\u89C6\u9891: ${inputPath}`);
2014
+ try {
2015
+ if (!fs.existsSync(inputPath)) {
2016
+ throw new Error(`\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${inputPath}`);
2017
+ }
2018
+ const outputDir = path3__namespace.dirname(outputPath);
2019
+ await fs.promises.mkdir(outputDir, { recursive: true });
2020
+ const metadata = await this.getVideoMetadata(inputPath);
2021
+ logger4.info(
2022
+ `\u{1F4CA} [VideoProcessor] \u89C6\u9891\u4FE1\u606F: ${metadata.width}x${metadata.height}, ${this.formatDuration(metadata.duration)}, ${metadata.fps}fps`
2023
+ );
2024
+ const outputFormat = this.determineOutputFormat(outputPath, videoOptions.format);
2025
+ await this.processVideo(inputPath, outputPath, videoOptions, outputFormat);
2026
+ let thumbnailPath;
2027
+ if (videoOptions.generateThumbnail !== false) {
2028
+ thumbnailPath = await this.generateThumbnail(
2029
+ inputPath,
2030
+ outputPath,
2031
+ videoOptions.thumbnailTime || 1
2032
+ );
2033
+ }
2034
+ const processedStats = await fs.promises.stat(outputPath);
2035
+ const processingTime = Date.now() - startTime;
2036
+ logger4.info(`\u2705 [VideoProcessor] \u89C6\u9891\u5904\u7406\u5B8C\u6210: ${outputPath}, \u8017\u65F6: ${processingTime}ms`);
2037
+ return {
2038
+ success: true,
2039
+ processedPath: outputPath,
2040
+ processedSize: processedStats.size,
2041
+ thumbnailPath,
2042
+ processingTime,
2043
+ data: {
2044
+ originalSize: metadata.size,
2045
+ processedSize: processedStats.size,
2046
+ compressionRatio: (metadata.size - processedStats.size) / metadata.size,
2047
+ duration: metadata.duration,
2048
+ originalFormat: metadata.format,
2049
+ processedFormat: outputFormat,
2050
+ dimensions: {
2051
+ original: { width: metadata.width, height: metadata.height },
2052
+ processed: { width: metadata.width, height: metadata.height }
2053
+ // 处理后可能会变化
2054
+ },
2055
+ fps: metadata.fps,
2056
+ bitrate: metadata.bitrate
2057
+ }
2058
+ };
2059
+ } catch (error) {
2060
+ console.error(`\u274C [VideoProcessor] \u89C6\u9891\u5904\u7406\u5931\u8D25: ${inputPath}:`, error);
2061
+ return {
2062
+ success: false,
2063
+ error: `\u89C6\u9891\u5904\u7406\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
2064
+ processingTime: Date.now() - startTime
2065
+ };
2066
+ }
2067
+ }
2068
+ /**
2069
+ * 检查文件是否支持处理
2070
+ */
2071
+ supports(mimeType) {
2072
+ const supportedTypes = [
2073
+ "video/mp4",
2074
+ "video/x-msvideo",
2075
+ // avi
2076
+ "video/quicktime",
2077
+ // mov
2078
+ "video/x-ms-wmv",
2079
+ // wmv
2080
+ "video/webm",
2081
+ "video/ogg",
2082
+ "video/3gpp",
2083
+ // 3gp
2084
+ "video/x-flv",
2085
+ // flv
2086
+ "video/x-matroska"
2087
+ // mkv
2088
+ ];
2089
+ return supportedTypes.includes(mimeType.toLowerCase());
2090
+ }
2091
+ /**
2092
+ * 获取视频文件信息
2093
+ */
2094
+ async getFileInfo(filePath) {
2095
+ this.ensureInitialized();
2096
+ try {
2097
+ const metadata = await this.getVideoMetadata(filePath);
2098
+ return {
2099
+ duration: metadata.duration,
2100
+ durationFormatted: this.formatDuration(metadata.duration),
2101
+ dimensions: {
2102
+ width: metadata.width,
2103
+ height: metadata.height
2104
+ },
2105
+ resolution: this.getResolutionDescription(metadata.width, metadata.height),
2106
+ aspectRatio: metadata.aspectRatio,
2107
+ fps: metadata.fps,
2108
+ bitrate: metadata.bitrate,
2109
+ format: metadata.format,
2110
+ codec: metadata.codec,
2111
+ fileSize: metadata.size,
2112
+ quality: this.getQualityDescription(metadata.width, metadata.height, metadata.bitrate)
2113
+ };
2114
+ } catch (error) {
2115
+ console.error(`\u274C [VideoProcessor] \u83B7\u53D6\u89C6\u9891\u4FE1\u606F\u5931\u8D25: ${filePath}:`, error);
2116
+ throw error;
2117
+ }
2118
+ }
2119
+ // ============= 私有方法 =============
2120
+ /**
2121
+ * 确保处理器已初始化
2122
+ */
2123
+ ensureInitialized() {
2124
+ if (!this.isInitialized || !this.ffmpeg) {
2125
+ throw new Error("\u89C6\u9891\u5904\u7406\u5668\u672A\u521D\u59CB\u5316");
2126
+ }
2127
+ }
2128
+ /**
2129
+ * 获取视频元数据
2130
+ */
2131
+ async getVideoMetadata(filePath) {
2132
+ return new Promise((resolve, reject) => {
2133
+ try {
2134
+ this.ffmpeg.ffprobe(filePath, (err, metadata) => {
2135
+ if (err) {
2136
+ console.error(`\u274C [VideoProcessor] \u83B7\u53D6\u89C6\u9891\u5143\u6570\u636E\u5931\u8D25: ${filePath}:`, err);
2137
+ reject(new Error(`\u65E0\u6CD5\u8BFB\u53D6\u89C6\u9891\u5143\u6570\u636E: ${err.message}`));
2138
+ return;
2139
+ }
2140
+ const videoStream = metadata.streams?.find(
2141
+ (stream) => stream.codec_type === "video"
2142
+ );
2143
+ if (!videoStream) {
2144
+ reject(new Error("\u6587\u4EF6\u4E2D\u672A\u627E\u5230\u89C6\u9891\u6D41"));
2145
+ return;
2146
+ }
2147
+ const width = parseInt(videoStream.width || "0");
2148
+ const height = parseInt(videoStream.height || "0");
2149
+ const result = {
2150
+ format: metadata.format?.format_name || "unknown",
2151
+ duration: parseFloat(metadata.format?.duration || "0"),
2152
+ bitrate: parseInt(metadata.format?.bit_rate || "0") / 1e3,
2153
+ // 转换为kbps
2154
+ width,
2155
+ height,
2156
+ fps: this.parseFPS(videoStream.r_frame_rate || "0/1"),
2157
+ codec: videoStream.codec_name || "unknown",
2158
+ size: parseInt(metadata.format?.size || "0"),
2159
+ aspectRatio: width > 0 && height > 0 ? width / height : 16 / 9
2160
+ };
2161
+ resolve(result);
2162
+ });
2163
+ } catch (error) {
2164
+ reject(error);
2165
+ }
2166
+ });
2167
+ }
2168
+ /**
2169
+ * 解析帧率
2170
+ */
2171
+ parseFPS(frameRate) {
2172
+ const parts = frameRate.split("/").map(Number);
2173
+ const numerator = parts[0] || 0;
2174
+ const denominator = parts[1] || 1;
2175
+ return denominator > 0 ? numerator / denominator : 0;
2176
+ }
2177
+ /**
2178
+ * 执行视频处理
2179
+ */
2180
+ async processVideo(inputPath, outputPath, options, outputFormat) {
2181
+ return new Promise((resolve, reject) => {
2182
+ try {
2183
+ let command = this.ffmpeg(inputPath);
2184
+ command = this.setVideoCodec(command, outputFormat);
2185
+ if (options.quality) {
2186
+ command = this.setVideoQuality(command, options.quality);
2187
+ logger4.info(`\u{1F527} [VideoProcessor] \u8BBE\u7F6E\u89C6\u9891\u8D28\u91CF: ${options.quality}`);
2188
+ }
2189
+ command = command.format(outputFormat);
2190
+ command.on("progress", (progress) => {
2191
+ if (progress.percent) {
2192
+ logger4.info(`\u{1F3AC} [VideoProcessor] \u5904\u7406\u8FDB\u5EA6: ${Math.round(progress.percent)}%`);
2193
+ }
2194
+ });
2195
+ command.on("error", (err) => {
2196
+ console.error(`\u274C [VideoProcessor] FFmpeg\u5904\u7406\u9519\u8BEF:`, err);
2197
+ reject(new Error(`\u89C6\u9891\u5904\u7406\u5931\u8D25: ${err.message}`));
2198
+ });
2199
+ command.on("end", () => {
2200
+ logger4.info(`\u2705 [VideoProcessor] FFmpeg\u5904\u7406\u5B8C\u6210: ${outputPath}`);
2201
+ resolve();
2202
+ });
2203
+ command.save(outputPath);
2204
+ } catch (error) {
2205
+ reject(error);
2206
+ }
2207
+ });
2208
+ }
2209
+ /**
2210
+ * 设置视频编解码器
2211
+ */
2212
+ setVideoCodec(command, format) {
2213
+ switch (format) {
2214
+ case "mp4":
2215
+ return command.videoCodec("libx264").audioCodec("aac");
2216
+ case "webm":
2217
+ return command.videoCodec("libvpx-vp9").audioCodec("libvorbis");
2218
+ case "avi":
2219
+ return command.videoCodec("libx264").audioCodec("mp3");
2220
+ case "mov":
2221
+ return command.videoCodec("libx264").audioCodec("aac");
2222
+ default:
2223
+ return command.videoCodec("libx264").audioCodec("aac");
2224
+ }
2225
+ }
2226
+ /**
2227
+ * 设置视频质量
2228
+ */
2229
+ setVideoQuality(command, quality) {
2230
+ const crf = Math.max(18, Math.min(51, 51 - Math.floor(quality * 0.33)));
2231
+ return command.outputOptions([`-crf ${crf}`]);
2232
+ }
2233
+ /**
2234
+ * 确定输出格式
2235
+ */
2236
+ determineOutputFormat(outputPath, requestedFormat) {
2237
+ if (requestedFormat) {
2238
+ return requestedFormat;
2239
+ }
2240
+ const ext = path3__namespace.extname(outputPath).toLowerCase();
2241
+ const formatMap = {
2242
+ ".mp4": "mp4",
2243
+ ".avi": "avi",
2244
+ ".mov": "mov",
2245
+ ".webm": "webm"
2246
+ };
2247
+ return formatMap[ext] || "mp4";
2248
+ }
2249
+ /**
2250
+ * 生成缩略图
2251
+ */
2252
+ async generateThumbnail(inputPath, outputPath, timeOffset) {
2253
+ return new Promise((resolve, reject) => {
2254
+ try {
2255
+ const thumbnailPath = this.getThumbnailPath(outputPath);
2256
+ this.ffmpeg(inputPath).seekInput(timeOffset).frames(1).size("320x240").on("error", (err) => {
2257
+ console.warn(`\u26A0\uFE0F [VideoProcessor] \u7F29\u7565\u56FE\u751F\u6210\u5931\u8D25: ${err.message}`);
2258
+ reject(err);
2259
+ }).on("end", () => {
2260
+ logger4.info(`\u{1F5BC}\uFE0F [VideoProcessor] \u89C6\u9891\u7F29\u7565\u56FE\u751F\u6210\u5B8C\u6210: ${thumbnailPath}`);
2261
+ resolve(thumbnailPath);
2262
+ }).save(thumbnailPath);
2263
+ } catch (error) {
2264
+ console.warn(`\u26A0\uFE0F [VideoProcessor] \u7F29\u7565\u56FE\u751F\u6210\u5F02\u5E38:`, error);
2265
+ reject(error);
2266
+ }
2267
+ });
2268
+ }
2269
+ /**
2270
+ * 获取缩略图路径
2271
+ */
2272
+ getThumbnailPath(originalPath) {
2273
+ const ext = path3__namespace.extname(originalPath);
2274
+ const basePath = originalPath.replace(ext, "");
2275
+ return `${basePath}_thumb.jpg`;
2276
+ }
2277
+ /**
2278
+ * 格式化时长显示
2279
+ */
2280
+ formatDuration(seconds) {
2281
+ const hours = Math.floor(seconds / 3600);
2282
+ const minutes = Math.floor(seconds % 3600 / 60);
2283
+ const remainingSeconds = Math.floor(seconds % 60);
2284
+ if (hours > 0) {
2285
+ return `${hours}:${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
2286
+ } else {
2287
+ return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
2288
+ }
2289
+ }
2290
+ /**
2291
+ * 获取分辨率描述
2292
+ */
2293
+ getResolutionDescription(width, height) {
2294
+ if (width >= 3840 && height >= 2160) {
2295
+ return "4K UHD";
2296
+ } else if (width >= 1920 && height >= 1080) {
2297
+ return "1080p Full HD";
2298
+ } else if (width >= 1280 && height >= 720) {
2299
+ return "720p HD";
2300
+ } else if (width >= 640 && height >= 480) {
2301
+ return "480p SD";
2302
+ } else {
2303
+ return `${width}x${height}`;
2304
+ }
2305
+ }
2306
+ /**
2307
+ * 获取视频质量描述
2308
+ */
2309
+ getQualityDescription(width, height, bitrate) {
2310
+ const pixels = width * height;
2311
+ const bitratePerPixel = bitrate / pixels * 1e3;
2312
+ if (pixels >= 1920 * 1080 && bitratePerPixel >= 0.1) {
2313
+ return "\u9AD8\u6E05";
2314
+ } else if (pixels >= 1280 * 720 && bitratePerPixel >= 0.05) {
2315
+ return "\u6807\u6E05";
2316
+ } else if (bitratePerPixel >= 0.02) {
2317
+ return "\u4E00\u822C";
2318
+ } else {
2319
+ return "\u4F4E\u8D28\u91CF";
2320
+ }
2321
+ }
2322
+ /**
2323
+ * 创建模拟FFmpeg对象(开发测试用)
2324
+ */
2325
+ createMockFFmpeg() {
2326
+ logger4.info("\u{1F9EA} [VideoProcessor] \u521B\u5EFA\u6A21\u62DFFFmpeg\u5904\u7406\u5668");
2327
+ const mockFFmpeg = (input) => {
2328
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u5904\u7406\u89C6\u9891: ${input}`);
2329
+ return {
2330
+ videoCodec: (codec) => {
2331
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u89C6\u9891\u7F16\u89E3\u7801\u5668: ${codec}`);
2332
+ return mockFFmpeg(input);
2333
+ },
2334
+ audioCodec: (codec) => {
2335
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u97F3\u9891\u7F16\u89E3\u7801\u5668: ${codec}`);
2336
+ return mockFFmpeg(input);
2337
+ },
2338
+ format: (format) => {
2339
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u8F93\u51FA\u683C\u5F0F: ${format}`);
2340
+ return mockFFmpeg(input);
2341
+ },
2342
+ outputOptions: (options) => {
2343
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u8F93\u51FA\u9009\u9879:`, options);
2344
+ return mockFFmpeg(input);
2345
+ },
2346
+ seekInput: (time) => {
2347
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u8DF3\u8F6C\u5230\u65F6\u95F4: ${time}s`);
2348
+ return mockFFmpeg(input);
2349
+ },
2350
+ frames: (count) => {
2351
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u63D0\u53D6\u5E27\u6570: ${count}`);
2352
+ return mockFFmpeg(input);
2353
+ },
2354
+ size: (size) => {
2355
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u8BBE\u7F6E\u5C3A\u5BF8: ${size}`);
2356
+ return mockFFmpeg(input);
2357
+ },
2358
+ on: (event, callback) => {
2359
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u6CE8\u518C\u4E8B\u4EF6\u76D1\u542C: ${event}`);
2360
+ if (event === "progress") {
2361
+ setTimeout(() => callback({ percent: 25 }), 100);
2362
+ setTimeout(() => callback({ percent: 50 }), 200);
2363
+ setTimeout(() => callback({ percent: 75 }), 300);
2364
+ setTimeout(() => callback({ percent: 100 }), 400);
2365
+ } else if (event === "end") {
2366
+ setTimeout(() => callback(), 500);
2367
+ }
2368
+ return mockFFmpeg(input);
2369
+ },
2370
+ save: async (outputPath) => {
2371
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u4FDD\u5B58\u89C6\u9891\u6587\u4EF6: ${outputPath}`);
2372
+ const outputDir = path3__namespace.dirname(outputPath);
2373
+ await fs.promises.mkdir(outputDir, { recursive: true });
2374
+ await fs.promises.writeFile(outputPath, `Mock processed video from ${input}`);
2375
+ }
2376
+ };
2377
+ };
2378
+ mockFFmpeg.ffprobe = (filePath, callback) => {
2379
+ logger4.info(`\u{1F9EA} [MockFFmpeg] \u83B7\u53D6\u89C6\u9891\u5143\u6570\u636E: ${filePath}`);
2380
+ setTimeout(() => {
2381
+ const mockMetadata = {
2382
+ streams: [
2383
+ {
2384
+ codec_type: "video",
2385
+ codec_name: "h264",
2386
+ width: "1920",
2387
+ height: "1080",
2388
+ r_frame_rate: "30/1"
2389
+ }
2390
+ ],
2391
+ format: {
2392
+ format_name: "mp4",
2393
+ duration: "300.0",
2394
+ size: "50000000",
2395
+ bit_rate: "1000000"
2396
+ }
2397
+ };
2398
+ callback(null, mockMetadata);
2399
+ }, 100);
2400
+ };
2401
+ return mockFFmpeg;
2402
+ }
2403
+ /**
2404
+ * 批量视频处理
2405
+ */
2406
+ async batchProcess(inputPaths, outputDir, options, onProgress) {
2407
+ this.ensureInitialized();
2408
+ logger4.info(`\u{1F3AC} [VideoProcessor] \u5F00\u59CB\u6279\u91CF\u5904\u7406 ${inputPaths.length} \u4E2A\u89C6\u9891\u6587\u4EF6`);
2409
+ const results = [];
2410
+ for (let i = 0; i < inputPaths.length; i++) {
2411
+ const inputPath = inputPaths[i];
2412
+ const fileName = path3__namespace.basename(inputPath);
2413
+ const nameWithoutExt = path3__namespace.parse(fileName).name;
2414
+ const outputFormat = options.format || "mp4";
2415
+ const outputPath = path3__namespace.join(outputDir, `${nameWithoutExt}.${outputFormat}`);
2416
+ try {
2417
+ const result = await this.process(inputPath, outputPath, options);
2418
+ results.push(result);
2419
+ if (onProgress) {
2420
+ onProgress(i + 1, inputPaths.length);
2421
+ }
2422
+ } catch (error) {
2423
+ console.error(`\u274C [VideoProcessor] \u6279\u91CF\u5904\u7406\u5931\u8D25: ${inputPath}:`, error);
2424
+ results.push({
2425
+ success: false,
2426
+ error: `\u5904\u7406\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
2427
+ });
2428
+ }
2429
+ }
2430
+ const successCount = results.filter((r) => r.success).length;
2431
+ logger4.info(`\u2705 [VideoProcessor] \u6279\u91CF\u5904\u7406\u5B8C\u6210\uFF0C\u6210\u529F: ${successCount}/${inputPaths.length}`);
2432
+ return results;
2433
+ }
2434
+ /**
2435
+ * 提取视频帧
2436
+ */
2437
+ async extractFrames(inputPath, outputDir, options = {}) {
2438
+ this.ensureInitialized();
2439
+ const { count = 10, interval, format = "jpg" } = options;
2440
+ logger4.info(`\u{1F5BC}\uFE0F [VideoProcessor] \u63D0\u53D6\u89C6\u9891\u5E27: ${inputPath}, \u6570\u91CF: ${count}`);
2441
+ return new Promise((resolve, reject) => {
2442
+ try {
2443
+ let command = this.ffmpeg(inputPath);
2444
+ if (interval) {
2445
+ command = command.outputOptions([`-vf fps=1/${interval}`]);
2446
+ } else {
2447
+ command = command.frames(count);
2448
+ }
2449
+ const outputPattern = path3__namespace.join(outputDir, `frame_%03d.${format}`);
2450
+ command.on("error", (err) => {
2451
+ console.error(`\u274C [VideoProcessor] \u63D0\u53D6\u5E27\u5931\u8D25:`, err);
2452
+ reject(new Error(`\u63D0\u53D6\u5E27\u5931\u8D25: ${err.message}`));
2453
+ }).on("end", async () => {
2454
+ try {
2455
+ const files = await fs.promises.readdir(outputDir);
2456
+ const frameFiles = files.filter((file) => file.startsWith("frame_") && file.endsWith(`.${format}`)).sort().map((file) => path3__namespace.join(outputDir, file));
2457
+ logger4.info(`\u2705 [VideoProcessor] \u5E27\u63D0\u53D6\u5B8C\u6210\uFF0C\u5171 ${frameFiles.length} \u5E27`);
2458
+ resolve(frameFiles);
2459
+ } catch (error) {
2460
+ reject(error);
2461
+ }
2462
+ }).save(outputPattern);
2463
+ } catch (error) {
2464
+ reject(error);
2465
+ }
2466
+ });
2467
+ }
2468
+ /**
2469
+ * 视频压缩
2470
+ */
2471
+ async compress(inputPath, outputPath, compressionLevel = "medium") {
2472
+ this.ensureInitialized();
2473
+ logger4.info(`\u{1F5DC}\uFE0F [VideoProcessor] \u5F00\u59CB\u89C6\u9891\u538B\u7F29: ${inputPath}, \u7EA7\u522B: ${compressionLevel}`);
2474
+ const options = {
2475
+ type: "video",
2476
+ quality: this.getCompressionQuality(compressionLevel),
2477
+ format: "mp4"
2478
+ };
2479
+ return this.process(inputPath, outputPath, options);
2480
+ }
2481
+ /**
2482
+ * 获取压缩质量
2483
+ */
2484
+ getCompressionQuality(level) {
2485
+ switch (level) {
2486
+ case "low":
2487
+ return 30;
2488
+ case "medium":
2489
+ return 60;
2490
+ case "high":
2491
+ return 85;
2492
+ default:
2493
+ return 60;
2494
+ }
2495
+ }
2496
+ };
2497
+ var logger5 = chunk6PRFP5EG_js.createLogger("ProcessingQueue");
2498
+ var ProcessingQueue = class extends events.EventEmitter {
2499
+ constructor(options = {}) {
2500
+ super();
2501
+ this.tasks = /* @__PURE__ */ new Map();
2502
+ this.runningTasks = /* @__PURE__ */ new Set();
2503
+ this.processors = /* @__PURE__ */ new Map();
2504
+ this.isStarted = false;
2505
+ this.processInterval = null;
2506
+ this.options = {
2507
+ maxConcurrentTasks: options.maxConcurrentTasks || 3,
2508
+ maxRetries: options.maxRetries || 2,
2509
+ retryDelay: options.retryDelay || 5e3,
2510
+ taskTimeout: options.taskTimeout || 3e5,
2511
+ // 5分钟
2512
+ autoStart: options.autoStart !== false
2513
+ };
2514
+ logger5.info("\u{1F4CB} [ProcessingQueue] \u961F\u5217\u7BA1\u7406\u5668\u5DF2\u521B\u5EFA");
2515
+ if (this.options.autoStart) {
2516
+ this.start();
2517
+ }
2518
+ }
2519
+ /**
2520
+ * 注册文件处理器
2521
+ */
2522
+ registerProcessor(processor) {
2523
+ this.processors.set(processor.type, processor);
2524
+ logger5.info(`\u{1F527} [ProcessingQueue] \u6CE8\u518C\u5904\u7406\u5668: ${processor.type}`);
2525
+ }
2526
+ /**
2527
+ * 添加任务到队列
2528
+ */
2529
+ addTask(inputPath, outputPath, options, taskOptions = {}) {
2530
+ const taskId = this.generateTaskId();
2531
+ const task = {
2532
+ id: taskId,
2533
+ inputPath,
2534
+ outputPath,
2535
+ options,
2536
+ priority: taskOptions.priority || "normal",
2537
+ status: "pending",
2538
+ retries: 0,
2539
+ maxRetries: taskOptions.maxRetries || this.options.maxRetries,
2540
+ onProgress: taskOptions.onProgress,
2541
+ onComplete: taskOptions.onComplete,
2542
+ onError: taskOptions.onError
2543
+ };
2544
+ const processor = this.processors.get(options.type);
2545
+ if (!processor) {
2546
+ throw new Error(`\u672A\u627E\u5230\u7C7B\u578B\u4E3A ${options.type} \u7684\u6587\u4EF6\u5904\u7406\u5668`);
2547
+ }
2548
+ task.processor = processor;
2549
+ this.tasks.set(taskId, task);
2550
+ logger5.info(`\u{1F4DD} [ProcessingQueue] \u6DFB\u52A0\u4EFB\u52A1: ${taskId} (${inputPath})`);
2551
+ this.emit("taskAdded", task);
2552
+ if (this.isStarted) {
2553
+ this.processNext();
2554
+ }
2555
+ return taskId;
2556
+ }
2557
+ /**
2558
+ * 启动队列处理
2559
+ */
2560
+ start() {
2561
+ if (this.isStarted) {
2562
+ console.warn("\u26A0\uFE0F [ProcessingQueue] \u961F\u5217\u5DF2\u7ECF\u542F\u52A8");
2563
+ return;
2564
+ }
2565
+ this.isStarted = true;
2566
+ logger5.info("\u25B6\uFE0F [ProcessingQueue] \u542F\u52A8\u961F\u5217\u5904\u7406");
2567
+ this.processInterval = setInterval(() => {
2568
+ this.processNext();
2569
+ }, 1e3);
2570
+ this.emit("started");
2571
+ this.processNext();
2572
+ }
2573
+ /**
2574
+ * 停止队列处理
2575
+ */
2576
+ stop() {
2577
+ if (!this.isStarted) {
2578
+ console.warn("\u26A0\uFE0F [ProcessingQueue] \u961F\u5217\u672A\u542F\u52A8");
2579
+ return;
2580
+ }
2581
+ this.isStarted = false;
2582
+ if (this.processInterval) {
2583
+ clearInterval(this.processInterval);
2584
+ this.processInterval = null;
2585
+ }
2586
+ logger5.info("\u23F9\uFE0F [ProcessingQueue] \u505C\u6B62\u961F\u5217\u5904\u7406");
2587
+ this.emit("stopped");
2588
+ }
2589
+ /**
2590
+ * 暂停任务
2591
+ */
2592
+ pauseTask(taskId) {
2593
+ const task = this.tasks.get(taskId);
2594
+ if (!task) {
2595
+ console.warn(`\u26A0\uFE0F [ProcessingQueue] \u4EFB\u52A1\u4E0D\u5B58\u5728: ${taskId}`);
2596
+ return false;
2597
+ }
2598
+ if (task.status === "running") {
2599
+ console.warn(`\u26A0\uFE0F [ProcessingQueue] \u65E0\u6CD5\u6682\u505C\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1: ${taskId}`);
2600
+ return false;
2601
+ }
2602
+ task.status = "cancelled";
2603
+ logger5.info(`\u23F8\uFE0F [ProcessingQueue] \u6682\u505C\u4EFB\u52A1: ${taskId}`);
2604
+ this.emit("taskCancelled", task);
2605
+ return true;
2606
+ }
2607
+ /**
2608
+ * 取消任务
2609
+ */
2610
+ cancelTask(taskId) {
2611
+ const task = this.tasks.get(taskId);
2612
+ if (!task) {
2613
+ console.warn(`\u26A0\uFE0F [ProcessingQueue] \u4EFB\u52A1\u4E0D\u5B58\u5728: ${taskId}`);
2614
+ return false;
2615
+ }
2616
+ if (task.status === "running") {
2617
+ console.warn(`\u26A0\uFE0F [ProcessingQueue] \u65E0\u6CD5\u53D6\u6D88\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1: ${taskId}`);
2618
+ return false;
2619
+ }
2620
+ task.status = "cancelled";
2621
+ logger5.info(`\u274C [ProcessingQueue] \u53D6\u6D88\u4EFB\u52A1: ${taskId}`);
2622
+ this.emit("taskCancelled", task);
2623
+ return true;
2624
+ }
2625
+ /**
2626
+ * 获取任务状态
2627
+ */
2628
+ getTask(taskId) {
2629
+ return this.tasks.get(taskId);
2630
+ }
2631
+ /**
2632
+ * 获取所有任务
2633
+ */
2634
+ getAllTasks() {
2635
+ return Array.from(this.tasks.values());
2636
+ }
2637
+ /**
2638
+ * 获取待处理任务
2639
+ */
2640
+ getPendingTasks() {
2641
+ return Array.from(this.tasks.values()).filter((task) => task.status === "pending");
2642
+ }
2643
+ /**
2644
+ * 获取正在运行的任务
2645
+ */
2646
+ getRunningTasks() {
2647
+ return Array.from(this.tasks.values()).filter((task) => task.status === "running");
2648
+ }
2649
+ /**
2650
+ * 获取队列统计信息
2651
+ */
2652
+ getStats() {
2653
+ const allTasks = Array.from(this.tasks.values());
2654
+ const completedTasks = allTasks.filter((task) => task.status === "completed");
2655
+ const totalProcessingTime = completedTasks.reduce((sum, task) => {
2656
+ if (task.startTime && task.endTime) {
2657
+ return sum + (task.endTime - task.startTime);
2658
+ }
2659
+ return sum;
2660
+ }, 0);
2661
+ return {
2662
+ totalTasks: allTasks.length,
2663
+ pendingTasks: allTasks.filter((task) => task.status === "pending").length,
2664
+ runningTasks: allTasks.filter((task) => task.status === "running").length,
2665
+ completedTasks: allTasks.filter((task) => task.status === "completed").length,
2666
+ failedTasks: allTasks.filter((task) => task.status === "failed").length,
2667
+ cancelledTasks: allTasks.filter((task) => task.status === "cancelled").length,
2668
+ averageProcessingTime: completedTasks.length > 0 ? totalProcessingTime / completedTasks.length : 0,
2669
+ successRate: allTasks.length > 0 ? completedTasks.length / allTasks.length : 0
2670
+ };
2671
+ }
2672
+ /**
2673
+ * 清理已完成的任务
2674
+ */
2675
+ cleanup() {
2676
+ const beforeCount = this.tasks.size;
2677
+ for (const [taskId, task] of Array.from(this.tasks.entries())) {
2678
+ if (task.status === "completed" || task.status === "failed" || task.status === "cancelled") {
2679
+ this.tasks.delete(taskId);
2680
+ }
2681
+ }
2682
+ const afterCount = this.tasks.size;
2683
+ const cleanedCount = beforeCount - afterCount;
2684
+ logger5.info(`\u{1F9F9} [ProcessingQueue] \u6E05\u7406\u5B8C\u6210\uFF0C\u79FB\u9664 ${cleanedCount} \u4E2A\u4EFB\u52A1`);
2685
+ this.emit("cleanup", { cleaned: cleanedCount, remaining: afterCount });
2686
+ }
2687
+ // ============= 私有方法 =============
2688
+ /**
2689
+ * 处理下一个任务
2690
+ */
2691
+ async processNext() {
2692
+ if (!this.isStarted) {
2693
+ return;
2694
+ }
2695
+ if (this.runningTasks.size >= this.options.maxConcurrentTasks) {
2696
+ return;
2697
+ }
2698
+ const nextTask = this.getNextTask();
2699
+ if (!nextTask) {
2700
+ return;
2701
+ }
2702
+ await this.processTask(nextTask);
2703
+ }
2704
+ /**
2705
+ * 获取下一个待处理任务(按优先级排序)
2706
+ */
2707
+ getNextTask() {
2708
+ const pendingTasks = this.getPendingTasks();
2709
+ if (pendingTasks.length === 0) {
2710
+ return null;
2711
+ }
2712
+ const priorityOrder = {
2713
+ urgent: 4,
2714
+ high: 3,
2715
+ normal: 2,
2716
+ low: 1
2717
+ };
2718
+ pendingTasks.sort((a, b) => {
2719
+ const priorityDiff = priorityOrder[b.priority] - priorityOrder[a.priority];
2720
+ if (priorityDiff !== 0) {
2721
+ return priorityDiff;
2722
+ }
2723
+ return 0;
2724
+ });
2725
+ return pendingTasks[0] || null;
2726
+ }
2727
+ /**
2728
+ * 处理单个任务
2729
+ */
2730
+ async processTask(task) {
2731
+ if (!task.processor) {
2732
+ this.failTask(task, "\u672A\u627E\u5230\u5BF9\u5E94\u7684\u6587\u4EF6\u5904\u7406\u5668");
2733
+ return;
2734
+ }
2735
+ logger5.info(`\u{1F680} [ProcessingQueue] \u5F00\u59CB\u5904\u7406\u4EFB\u52A1: ${task.id}`);
2736
+ task.status = "running";
2737
+ task.startTime = Date.now();
2738
+ this.runningTasks.add(task.id);
2739
+ this.emit("taskStarted", task);
2740
+ const timeoutId = setTimeout(() => {
2741
+ if (task.status === "running") {
2742
+ this.failTask(task, "\u4EFB\u52A1\u5904\u7406\u8D85\u65F6");
2743
+ }
2744
+ }, this.options.taskTimeout);
2745
+ try {
2746
+ const result = await task.processor.process(task.inputPath, task.outputPath, task.options);
2747
+ clearTimeout(timeoutId);
2748
+ if (result.success) {
2749
+ this.completeTask(task, result);
2750
+ } else {
2751
+ this.retryOrFailTask(task, result.error || "\u5904\u7406\u5931\u8D25");
2752
+ }
2753
+ } catch (error) {
2754
+ clearTimeout(timeoutId);
2755
+ const errorMessage = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
2756
+ this.retryOrFailTask(task, errorMessage);
2757
+ }
2758
+ }
2759
+ /**
2760
+ * 完成任务
2761
+ */
2762
+ completeTask(task, result) {
2763
+ task.status = "completed";
2764
+ task.endTime = Date.now();
2765
+ task.result = result;
2766
+ this.runningTasks.delete(task.id);
2767
+ logger5.info(`\u2705 [ProcessingQueue] \u4EFB\u52A1\u5B8C\u6210: ${task.id}`);
2768
+ if (task.onComplete) {
2769
+ try {
2770
+ task.onComplete(task, result);
2771
+ } catch (error) {
2772
+ console.error("\u274C [ProcessingQueue] \u4EFB\u52A1\u5B8C\u6210\u56DE\u8C03\u9519\u8BEF:", error);
2773
+ }
2774
+ }
2775
+ this.emit("taskCompleted", task, result);
2776
+ setTimeout(() => this.processNext(), 0);
2777
+ }
2778
+ /**
2779
+ * 重试或失败任务
2780
+ */
2781
+ retryOrFailTask(task, error) {
2782
+ this.runningTasks.delete(task.id);
2783
+ if (task.retries < task.maxRetries) {
2784
+ task.retries++;
2785
+ task.status = "pending";
2786
+ task.error = void 0;
2787
+ logger5.info(`\u{1F504} [ProcessingQueue] \u91CD\u8BD5\u4EFB\u52A1: ${task.id} (${task.retries}/${task.maxRetries})`);
2788
+ this.emit("taskRetried", task);
2789
+ setTimeout(() => {
2790
+ if (this.isStarted) {
2791
+ this.processNext();
2792
+ }
2793
+ }, this.options.retryDelay);
2794
+ } else {
2795
+ this.failTask(task, error);
2796
+ }
2797
+ }
2798
+ /**
2799
+ * 失败任务
2800
+ */
2801
+ failTask(task, error) {
2802
+ task.status = "failed";
2803
+ task.endTime = Date.now();
2804
+ task.error = error;
2805
+ this.runningTasks.delete(task.id);
2806
+ console.error(`\u274C [ProcessingQueue] \u4EFB\u52A1\u5931\u8D25: ${task.id} - ${error}`);
2807
+ if (task.onError) {
2808
+ try {
2809
+ task.onError(task, error);
2810
+ } catch (callbackError) {
2811
+ console.error("\u274C [ProcessingQueue] \u4EFB\u52A1\u5931\u8D25\u56DE\u8C03\u9519\u8BEF:", callbackError);
2812
+ }
2813
+ }
2814
+ this.emit("taskFailed", task, error);
2815
+ setTimeout(() => this.processNext(), 0);
2816
+ }
2817
+ /**
2818
+ * 生成唯一任务ID
2819
+ */
2820
+ generateTaskId() {
2821
+ return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2822
+ }
2823
+ /**
2824
+ * 批量添加任务
2825
+ */
2826
+ addBatchTasks(tasks, onBatchProgress, onBatchComplete) {
2827
+ const taskIds = [];
2828
+ const results = /* @__PURE__ */ new Map();
2829
+ let completedCount = 0;
2830
+ logger5.info(`\u{1F4E6} [ProcessingQueue] \u6279\u91CF\u6DFB\u52A0 ${tasks.length} \u4E2A\u4EFB\u52A1`);
2831
+ for (const taskSpec of tasks) {
2832
+ const taskId = this.addTask(taskSpec.inputPath, taskSpec.outputPath, taskSpec.options, {
2833
+ priority: taskSpec.priority,
2834
+ onComplete: (task, result) => {
2835
+ results.set(task.id, result);
2836
+ completedCount++;
2837
+ if (onBatchProgress) {
2838
+ onBatchProgress(completedCount, tasks.length);
2839
+ }
2840
+ if (completedCount === tasks.length && onBatchComplete) {
2841
+ onBatchComplete(results);
2842
+ }
2843
+ },
2844
+ onError: (task, error) => {
2845
+ results.set(task.id, { success: false, error });
2846
+ completedCount++;
2847
+ if (onBatchProgress) {
2848
+ onBatchProgress(completedCount, tasks.length);
2849
+ }
2850
+ if (completedCount === tasks.length && onBatchComplete) {
2851
+ onBatchComplete(results);
2852
+ }
2853
+ }
2854
+ });
2855
+ taskIds.push(taskId);
2856
+ }
2857
+ return taskIds;
2858
+ }
2859
+ /**
2860
+ * 获取队列健康状态
2861
+ */
2862
+ getHealthStatus() {
2863
+ const stats = this.getStats();
2864
+ const issues = [];
2865
+ const recommendations = [];
2866
+ if (stats.successRate < 0.8 && stats.totalTasks > 10) {
2867
+ issues.push(`\u6210\u529F\u7387\u8FC7\u4F4E: ${(stats.successRate * 100).toFixed(1)}%`);
2868
+ recommendations.push("\u68C0\u67E5\u6587\u4EF6\u5904\u7406\u5668\u914D\u7F6E\u548C\u8F93\u5165\u6587\u4EF6\u8D28\u91CF");
2869
+ }
2870
+ if (stats.pendingTasks > 50) {
2871
+ issues.push(`\u5F85\u5904\u7406\u4EFB\u52A1\u79EF\u538B: ${stats.pendingTasks} \u4E2A`);
2872
+ recommendations.push("\u8003\u8651\u589E\u52A0\u5E76\u53D1\u5904\u7406\u6570\u6216\u4F18\u5316\u5904\u7406\u6027\u80FD");
2873
+ }
2874
+ if (stats.averageProcessingTime > 6e4) {
2875
+ issues.push(`\u5E73\u5747\u5904\u7406\u65F6\u95F4\u8FC7\u957F: ${(stats.averageProcessingTime / 1e3).toFixed(1)}\u79D2`);
2876
+ recommendations.push("\u4F18\u5316\u6587\u4EF6\u5904\u7406\u903B\u8F91\u6216\u51CF\u5C11\u5904\u7406\u590D\u6742\u5EA6");
2877
+ }
2878
+ return {
2879
+ isHealthy: issues.length === 0,
2880
+ issues,
2881
+ recommendations
2882
+ };
2883
+ }
2884
+ };
2885
+ var MIME_TYPES = {
2886
+ // 图片
2887
+ ".jpg": "image/jpeg",
2888
+ ".jpeg": "image/jpeg",
2889
+ ".png": "image/png",
2890
+ ".gif": "image/gif",
2891
+ ".webp": "image/webp",
2892
+ ".svg": "image/svg+xml",
2893
+ ".bmp": "image/bmp",
2894
+ ".ico": "image/x-icon",
2895
+ // 视频
2896
+ ".mp4": "video/mp4",
2897
+ ".avi": "video/x-msvideo",
2898
+ ".mov": "video/quicktime",
2899
+ ".wmv": "video/x-ms-wmv",
2900
+ ".flv": "video/x-flv",
2901
+ ".mkv": "video/x-matroska",
2902
+ ".webm": "video/webm",
2903
+ // 音频
2904
+ ".mp3": "audio/mpeg",
2905
+ ".wav": "audio/wav",
2906
+ ".ogg": "audio/ogg",
2907
+ ".m4a": "audio/mp4",
2908
+ ".flac": "audio/flac",
2909
+ ".aac": "audio/aac",
2910
+ // 文档
2911
+ ".pdf": "application/pdf",
2912
+ ".doc": "application/msword",
2913
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
2914
+ ".xls": "application/vnd.ms-excel",
2915
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
2916
+ ".ppt": "application/vnd.ms-powerpoint",
2917
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
2918
+ ".txt": "text/plain",
2919
+ ".rtf": "application/rtf",
2920
+ // 压缩文件
2921
+ ".zip": "application/zip",
2922
+ ".rar": "application/x-rar-compressed",
2923
+ ".7z": "application/x-7z-compressed",
2924
+ ".tar": "application/x-tar",
2925
+ ".gz": "application/gzip",
2926
+ // 代码文件
2927
+ ".js": "application/javascript",
2928
+ ".json": "application/json",
2929
+ ".xml": "application/xml",
2930
+ ".html": "text/html",
2931
+ ".css": "text/css",
2932
+ ".ts": "application/typescript",
2933
+ // 其他
2934
+ ".csv": "text/csv",
2935
+ ".md": "text/markdown"
2936
+ };
2937
+ function getMimeType(filename) {
2938
+ const ext = path3__namespace.extname(filename).toLowerCase();
2939
+ return MIME_TYPES[ext] || "application/octet-stream";
2940
+ }
2941
+ var logger6 = chunk6PRFP5EG_js.createLogger("UniversalFileService");
2942
+ var UniversalFileService = class extends events.EventEmitter {
2943
+ constructor(config) {
2944
+ super();
2945
+ this.storageProviders = /* @__PURE__ */ new Map();
2946
+ this.cdnProviders = /* @__PURE__ */ new Map();
2947
+ this.fileProcessors = /* @__PURE__ */ new Map();
2948
+ this.uploadProgressMap = /* @__PURE__ */ new Map();
2949
+ this.metadataCache = /* @__PURE__ */ new Map();
2950
+ this.urlCache = /* @__PURE__ */ new Map();
2951
+ this.processingQueue = [];
2952
+ this.isProcessingQueueRunning = false;
2953
+ this.config = config;
2954
+ if (this.config.persistence?.enabled && this.config.persistence.repository) {
2955
+ this.setupPersistenceListeners();
2956
+ }
2957
+ }
2958
+ // ============= 持久化设置 =============
2959
+ /**
2960
+ * 设置数据库持久化监听器
2961
+ *
2962
+ * 当文件上传完成或删除时,自动触发数据库操作
2963
+ */
2964
+ setupPersistenceListeners() {
2965
+ const { repository, autoPersist = true } = this.config.persistence;
2966
+ if (!autoPersist) {
2967
+ logger6.info("\u2699\uFE0F [UniversalFileService] \u81EA\u52A8\u6301\u4E45\u5316\u5DF2\u7981\u7528");
2968
+ return;
2969
+ }
2970
+ logger6.info("\u2705 [UniversalFileService] \u5DF2\u542F\u7528\u6570\u636E\u5E93\u6301\u4E45\u5316\uFF0C\u81EA\u52A8\u76D1\u542C\u6587\u4EF6\u4E8B\u4EF6");
2971
+ this.on("upload:complete", async (fileId, data) => {
2972
+ try {
2973
+ const metadata = data.metadata || data;
2974
+ await repository.save(metadata);
2975
+ logger6.info(`\u{1F4BE} [Persistence] \u6587\u4EF6\u5143\u6570\u636E\u5DF2\u81EA\u52A8\u4FDD\u5B58: ${fileId}`);
2976
+ } catch (error) {
2977
+ logger6.error(`\u274C [Persistence] \u4FDD\u5B58\u5931\u8D25: ${fileId}`, error);
2978
+ }
2979
+ });
2980
+ this.on("file:deleted", async (fileId) => {
2981
+ try {
2982
+ await repository.delete(fileId);
2983
+ logger6.info(`\u{1F5D1}\uFE0F [Persistence] \u6587\u4EF6\u5143\u6570\u636E\u5DF2\u81EA\u52A8\u5220\u9664: ${fileId}`);
2984
+ } catch (error) {
2985
+ logger6.error(`\u274C [Persistence] \u5220\u9664\u5931\u8D25: ${fileId}`, error);
2986
+ }
2987
+ });
2988
+ this.on("files:batch-deleted", async (fileIds) => {
2989
+ try {
2990
+ await repository.batchDelete(fileIds);
2991
+ logger6.info(`\u{1F5D1}\uFE0F [Persistence] \u6279\u91CF\u5220\u9664\u5143\u6570\u636E: ${fileIds.length} \u4E2A\u6587\u4EF6`);
2992
+ } catch (error) {
2993
+ logger6.error(`\u274C [Persistence] \u6279\u91CF\u5220\u9664\u5931\u8D25`, error);
2994
+ }
2995
+ });
2996
+ }
2997
+ // ============= 初始化方法 =============
2998
+ /**
2999
+ * 初始化文件服务
3000
+ */
3001
+ async initialize() {
3002
+ logger6.info("\u{1F680} [UniversalFileService] \u5F00\u59CB\u521D\u59CB\u5316\u6587\u4EF6\u670D\u52A1...");
3003
+ try {
3004
+ await this.initializeStorageProviders();
3005
+ await this.initializeCDNProviders();
3006
+ await this.initializeFileProcessors();
3007
+ logger6.info("\u2705 [UniversalFileService] \u6587\u4EF6\u670D\u52A1\u521D\u59CB\u5316\u5B8C\u6210");
3008
+ } catch (error) {
3009
+ console.error("\u274C [UniversalFileService] \u6587\u4EF6\u670D\u52A1\u521D\u59CB\u5316\u5931\u8D25:", error);
3010
+ throw error;
3011
+ }
3012
+ }
3013
+ /**
3014
+ * 重新初始化存储提供者(支持配置热更新)
3015
+ */
3016
+ async reinitializeStorageProviders() {
3017
+ logger6.info("\u{1F504} [UniversalFileService] \u91CD\u65B0\u521D\u59CB\u5316\u5B58\u50A8\u63D0\u4F9B\u8005...");
3018
+ try {
3019
+ const ossConfig = this.config.storageProviders["aliyun-oss"];
3020
+ if (ossConfig && ossConfig.enabled) {
3021
+ const ossProvider = this.storageProviders.get("aliyun-oss");
3022
+ if (ossProvider && "reinitialize" in ossProvider) {
3023
+ logger6.info("\u{1F504} [UniversalFileService] \u91CD\u65B0\u521D\u59CB\u5316\u963F\u91CC\u4E91OSS\u63D0\u4F9B\u8005...");
3024
+ await ossProvider.reinitialize(ossConfig);
3025
+ }
3026
+ }
3027
+ logger6.info("\u2705 [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u91CD\u65B0\u521D\u59CB\u5316\u5B8C\u6210");
3028
+ } catch (error) {
3029
+ console.error("\u274C [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u91CD\u65B0\u521D\u59CB\u5316\u5931\u8D25:", error);
3030
+ throw error;
3031
+ }
3032
+ }
3033
+ /**
3034
+ * 注册存储提供者
3035
+ */
3036
+ registerStorageProvider(provider) {
3037
+ this.storageProviders.set(provider.type, provider);
3038
+ logger6.info(`\u{1F4E6} [UniversalFileService] \u6CE8\u518C\u5B58\u50A8\u63D0\u4F9B\u8005: ${provider.type}`);
3039
+ }
3040
+ /**
3041
+ * 注册CDN提供者
3042
+ */
3043
+ registerCDNProvider(provider) {
3044
+ this.cdnProviders.set(provider.type, provider);
3045
+ logger6.info(`\u{1F310} [UniversalFileService] \u6CE8\u518CCDN\u63D0\u4F9B\u8005: ${provider.type}`);
3046
+ }
3047
+ /**
3048
+ * 注册文件处理器
3049
+ */
3050
+ registerFileProcessor(processor) {
3051
+ this.fileProcessors.set(processor.type, processor);
3052
+ logger6.info(`\u2699\uFE0F [UniversalFileService] \u6CE8\u518C\u6587\u4EF6\u5904\u7406\u5668: ${processor.type}`);
3053
+ }
3054
+ // ============= 核心文件操作方法 =============
3055
+ /**
3056
+ * 上传文件
3057
+ */
3058
+ async uploadFile(fileInfo, storageType, onProgress) {
3059
+ const fileId = uuid.v4();
3060
+ const startTime = Date.now();
3061
+ logger6.info(`\u{1F4E4} [UniversalFileService] \u5F00\u59CB\u4E0A\u4F20\u6587\u4EF6: ${fileInfo.file.name}, ID: ${fileId}`);
3062
+ try {
3063
+ await this.validateFile(fileInfo.file);
3064
+ const progress = {
3065
+ fileId,
3066
+ status: "pending",
3067
+ progress: 0,
3068
+ uploadedBytes: 0,
3069
+ totalBytes: fileInfo.file.size,
3070
+ speed: 0,
3071
+ remainingTime: 0
3072
+ };
3073
+ this.uploadProgressMap.set(fileId, progress);
3074
+ this.emitFileEvent("upload:start", fileId, { fileName: fileInfo.file.name });
3075
+ const metadata = await this.generateFileMetadata(fileId, fileInfo);
3076
+ const selectedStorageType = storageType || this.config.defaultStorage;
3077
+ let storageProvider = this.storageProviders.get(selectedStorageType);
3078
+ if (!storageProvider) {
3079
+ logger6.info(
3080
+ `\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005 ${selectedStorageType} \u4E0D\u53EF\u7528\uFF0C\u5C1D\u8BD5\u4F7F\u7528OSS`
3081
+ );
3082
+ storageProvider = this.storageProviders.get("aliyun-oss");
3083
+ if (!storageProvider) {
3084
+ logger6.info(`\u26A0\uFE0F [UniversalFileService] OSS\u4E0D\u53EF\u7528\uFF0C\u56DE\u9000\u5230\u672C\u5730\u5B58\u50A8`);
3085
+ storageProvider = this.storageProviders.get("local");
3086
+ }
3087
+ }
3088
+ if (!storageProvider) {
3089
+ throw new chunkHWJ34NL6_js.StorageProviderError(`\u6CA1\u6709\u53EF\u7528\u7684\u5B58\u50A8\u63D0\u4F9B\u8005`);
3090
+ }
3091
+ const storagePath = this.generateStoragePath(metadata);
3092
+ progress.status = "uploading";
3093
+ progress.progress = 10;
3094
+ this.uploadProgressMap.set(fileId, progress);
3095
+ onProgress?.(progress);
3096
+ this.emitFileEvent("upload:progress", fileId, { progress: progress.progress });
3097
+ const uploadResult = await storageProvider.upload(fileInfo, storagePath);
3098
+ if (!uploadResult.success) {
3099
+ throw new chunkHWJ34NL6_js.FileUploadError(`\u4E0A\u4F20\u5931\u8D25: ${uploadResult.error}`);
3100
+ }
3101
+ metadata.storagePath = uploadResult.path || storagePath;
3102
+ metadata.storageProvider = selectedStorageType;
3103
+ if (this.config.defaultCDN !== "none") {
3104
+ const cdnProvider = this.cdnProviders.get(this.config.defaultCDN);
3105
+ if (cdnProvider && uploadResult.url) {
3106
+ metadata.cdnUrl = await cdnProvider.generateUrl(uploadResult.url);
3107
+ }
3108
+ }
3109
+ progress.status = fileInfo.needsProcessing ? "processing" : "completed";
3110
+ progress.progress = fileInfo.needsProcessing ? 70 : 100;
3111
+ this.uploadProgressMap.set(fileId, progress);
3112
+ onProgress?.(progress);
3113
+ if (fileInfo.needsProcessing && fileInfo.processingOptions) {
3114
+ await this.queueFileProcessing(metadata, fileInfo.processingOptions);
3115
+ }
3116
+ this.cacheMetadata(metadata);
3117
+ progress.status = "completed";
3118
+ progress.progress = 100;
3119
+ this.uploadProgressMap.set(fileId, progress);
3120
+ onProgress?.(progress);
3121
+ const uploadTime = Date.now() - startTime;
3122
+ logger6.info(`\u2705 [UniversalFileService] \u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210: ${fileId}, \u8017\u65F6: ${uploadTime}ms`);
3123
+ this.emitFileEvent("upload:complete", fileId, {
3124
+ fileName: fileInfo.file.name,
3125
+ size: fileInfo.file.size,
3126
+ uploadTime
3127
+ });
3128
+ return metadata;
3129
+ } catch (error) {
3130
+ console.error(`\u274C [UniversalFileService] \u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${fileId}:`, error);
3131
+ const progress = this.uploadProgressMap.get(fileId);
3132
+ if (progress) {
3133
+ progress.status = "failed";
3134
+ progress.error = error instanceof Error ? error.message : "\u4E0A\u4F20\u5931\u8D25";
3135
+ this.uploadProgressMap.set(fileId, progress);
3136
+ onProgress?.(progress);
3137
+ }
3138
+ this.emitFileEvent(
3139
+ "upload:error",
3140
+ fileId,
3141
+ void 0,
3142
+ error instanceof Error ? error.message : "\u4E0A\u4F20\u5931\u8D25"
3143
+ );
3144
+ throw error;
3145
+ } finally {
3146
+ setTimeout(
3147
+ () => {
3148
+ this.uploadProgressMap.delete(fileId);
3149
+ },
3150
+ 5 * 60 * 1e3
3151
+ );
3152
+ }
3153
+ }
3154
+ /**
3155
+ * 获取上传进度
3156
+ */
3157
+ getUploadProgress(fileId) {
3158
+ return this.uploadProgressMap.get(fileId);
3159
+ }
3160
+ // ============= 事件处理方法 =============
3161
+ /**
3162
+ * 监听文件事件
3163
+ */
3164
+ onFileEvent(eventType, listener) {
3165
+ this.on(eventType, listener);
3166
+ }
3167
+ /**
3168
+ * 移除文件事件监听器
3169
+ */
3170
+ offFileEvent(eventType, listener) {
3171
+ this.off(eventType, listener);
3172
+ }
3173
+ // ============= 私有方法 =============
3174
+ async initializeStorageProviders() {
3175
+ logger6.info("\u{1F4E6} [UniversalFileService] \u5F00\u59CB\u521D\u59CB\u5316\u5B58\u50A8\u63D0\u4F9B\u8005...");
3176
+ if (this.storageProviders.size === 0) {
3177
+ await this.registerDefaultStorageProviders();
3178
+ }
3179
+ for (const [type, config] of Object.entries(this.config.storageProviders)) {
3180
+ if (config.enabled) {
3181
+ const provider = this.storageProviders.get(type);
3182
+ if (provider) {
3183
+ try {
3184
+ await provider.initialize(config);
3185
+ logger6.info(`\u2705 [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u521D\u59CB\u5316\u5B8C\u6210: ${type}`);
3186
+ } catch (error) {
3187
+ console.warn(`\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u521D\u59CB\u5316\u5931\u8D25: ${type}:`, error);
3188
+ }
3189
+ } else {
3190
+ console.warn(`\u26A0\uFE0F [UniversalFileService] \u5B58\u50A8\u63D0\u4F9B\u8005\u672A\u6CE8\u518C: ${type}`);
3191
+ }
3192
+ }
3193
+ }
3194
+ }
3195
+ async registerDefaultStorageProviders() {
3196
+ logger6.info("\u{1F4E6} [UniversalFileService] \u6CE8\u518C\u9ED8\u8BA4\u5B58\u50A8\u63D0\u4F9B\u8005...");
3197
+ const ossConfig = this.config.storageProviders["aliyun-oss"];
3198
+ if (ossConfig && ossConfig.enabled) {
3199
+ try {
3200
+ const { AliyunOSSProvider: AliyunOSSProvider2 } = await import('../../AliyunOSSProvider-7JLMJDXK.js');
3201
+ const ossProvider = new AliyunOSSProvider2();
3202
+ this.registerStorageProvider(ossProvider);
3203
+ logger6.info("\u2705 [UniversalFileService] \u963F\u91CC\u4E91OSS\u63D0\u4F9B\u8005\u6CE8\u518C\u6210\u529F");
3204
+ } catch (error) {
3205
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u963F\u91CC\u4E91OSS\u63D0\u4F9B\u8005\u6CE8\u518C\u5931\u8D25:", error);
3206
+ }
3207
+ }
3208
+ const localConfig = this.config.storageProviders["local"];
3209
+ if (localConfig && localConfig.enabled) {
3210
+ try {
3211
+ const { LocalStorageProvider: LocalStorageProvider2 } = await import('../../LocalStorageProvider-NBNHHWLY.js');
3212
+ const localProvider = new LocalStorageProvider2();
3213
+ this.registerStorageProvider(localProvider);
3214
+ logger6.info("\u2705 [UniversalFileService] \u672C\u5730\u5B58\u50A8\u63D0\u4F9B\u8005\u6CE8\u518C\u6210\u529F");
3215
+ } catch (error) {
3216
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u672C\u5730\u5B58\u50A8\u63D0\u4F9B\u8005\u6CE8\u518C\u5931\u8D25:", error);
3217
+ }
3218
+ }
3219
+ if (this.storageProviders.size === 0) {
3220
+ console.warn("\u26A0\uFE0F [UniversalFileService] \u6CA1\u6709\u53EF\u7528\u7684\u5B58\u50A8\u63D0\u4F9B\u8005\uFF0C\u5C1D\u8BD5\u6CE8\u518C\u672C\u5730\u5B58\u50A8\u4F5C\u4E3A\u5907\u7528");
3221
+ try {
3222
+ const { LocalStorageProvider: LocalStorageProvider2 } = await import('../../LocalStorageProvider-NBNHHWLY.js');
3223
+ const localProvider = new LocalStorageProvider2();
3224
+ this.registerStorageProvider(localProvider);
3225
+ logger6.info("\u2705 [UniversalFileService] \u672C\u5730\u5B58\u50A8\u63D0\u4F9B\u8005\u6CE8\u518C\u6210\u529F\uFF08\u5907\u7528\uFF09");
3226
+ } catch (error) {
3227
+ console.error("\u274C [UniversalFileService] \u65E0\u6CD5\u6CE8\u518C\u4EFB\u4F55\u5B58\u50A8\u63D0\u4F9B\u8005:", error);
3228
+ throw new Error("\u65E0\u6CD5\u521D\u59CB\u5316\u5B58\u50A8\u63D0\u4F9B\u8005");
3229
+ }
3230
+ }
3231
+ }
3232
+ async initializeCDNProviders() {
3233
+ for (const [type, config] of Object.entries(this.config.cdnProviders)) {
3234
+ if (config.enabled) {
3235
+ const provider = this.cdnProviders.get(type);
3236
+ if (provider) {
3237
+ await provider.initialize(config);
3238
+ logger6.info(`\u2705 [UniversalFileService] CDN\u63D0\u4F9B\u8005\u521D\u59CB\u5316\u5B8C\u6210: ${type}`);
3239
+ }
3240
+ }
3241
+ }
3242
+ }
3243
+ async initializeFileProcessors() {
3244
+ for (const processor of Array.from(this.fileProcessors.values())) {
3245
+ await processor.initialize();
3246
+ logger6.info(`\u2705 [UniversalFileService] \u6587\u4EF6\u5904\u7406\u5668\u521D\u59CB\u5316\u5B8C\u6210: ${processor.type}`);
3247
+ }
3248
+ }
3249
+ async validateFile(file) {
3250
+ if (file.size > this.config.maxFileSize) {
3251
+ throw new chunkHWJ34NL6_js.FileUploadError(`\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC7\u9650\u5236: ${file.size} > ${this.config.maxFileSize}`);
3252
+ }
3253
+ const mimeType = file.type || getMimeType(file.name);
3254
+ if (this.config.allowedMimeTypes.length > 0 && !this.config.allowedMimeTypes.includes(mimeType)) {
3255
+ throw new chunkHWJ34NL6_js.FileUploadError(`\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B: ${mimeType}`);
3256
+ }
3257
+ }
3258
+ async generateFileMetadata(fileId, fileInfo) {
3259
+ const now = /* @__PURE__ */ new Date();
3260
+ const mimeType = fileInfo.file.type || getMimeType(fileInfo.file.name);
3261
+ const extension = path3__namespace.extname(fileInfo.file.name).toLowerCase();
3262
+ const hash = await this.generateFileHash(fileInfo.file);
3263
+ return {
3264
+ id: fileId,
3265
+ originalName: fileInfo.file.name,
3266
+ storageName: `${fileId}${extension}`,
3267
+ size: fileInfo.file.size,
3268
+ mimeType,
3269
+ extension,
3270
+ hash,
3271
+ uploadTime: now,
3272
+ permission: fileInfo.permission || "public",
3273
+ uploaderId: fileInfo.metadata?.uploadedBy || "system",
3274
+ moduleId: fileInfo.moduleId,
3275
+ businessId: fileInfo.businessId,
3276
+ storageProvider: this.config.defaultStorage,
3277
+ storagePath: "",
3278
+ accessCount: 0,
3279
+ metadata: fileInfo.metadata || {}
3280
+ };
3281
+ }
3282
+ generateStoragePath(metadata) {
3283
+ const date = /* @__PURE__ */ new Date();
3284
+ const year = date.getFullYear();
3285
+ const month = String(date.getMonth() + 1).padStart(2, "0");
3286
+ const day = String(date.getDate()).padStart(2, "0");
3287
+ return `${metadata.moduleId}/${year}/${month}/${day}/${metadata.storageName}`;
3288
+ }
3289
+ async generateFileHash(file) {
3290
+ const buffer = await file.arrayBuffer();
3291
+ const hash = crypto.createHash("sha256");
3292
+ hash.update(Buffer.from(buffer));
3293
+ return hash.digest("hex");
3294
+ }
3295
+ async queueFileProcessing(metadata, options) {
3296
+ if (!this.config.processors?.length || 0 > 0) {
3297
+ return;
3298
+ }
3299
+ const processor = this.fileProcessors.get(options.type);
3300
+ if (!processor) {
3301
+ console.warn(`\u26A0\uFE0F [UniversalFileService] \u6587\u4EF6\u5904\u7406\u5668\u4E0D\u5B58\u5728: ${options.type}`);
3302
+ return;
3303
+ }
3304
+ if (this.processingQueue.length >= 1e3) {
3305
+ throw new chunkHWJ34NL6_js.FileProcessingError("\u5904\u7406\u961F\u5217\u5DF2\u6EE1");
3306
+ }
3307
+ this.processingQueue.push({
3308
+ fileId: metadata.id,
3309
+ processor,
3310
+ inputPath: metadata.storagePath,
3311
+ outputPath: this.generateProcessedPath(metadata, options),
3312
+ options
3313
+ });
3314
+ if (!this.isProcessingQueueRunning) {
3315
+ this.processFileQueue();
3316
+ }
3317
+ }
3318
+ generateProcessedPath(metadata, options) {
3319
+ const basePath = metadata.storagePath;
3320
+ const extension = path3__namespace.extname(basePath);
3321
+ const basename4 = basePath.replace(extension, "");
3322
+ return `${basename4}_processed${extension}`;
3323
+ }
3324
+ async processFileQueue() {
3325
+ if (this.isProcessingQueueRunning || this.processingQueue.length === 0) {
3326
+ return;
3327
+ }
3328
+ this.isProcessingQueueRunning = true;
3329
+ while (this.processingQueue.length > 0) {
3330
+ const task = this.processingQueue.shift();
3331
+ if (!task) break;
3332
+ try {
3333
+ this.emitFileEvent("processing:start", task.fileId);
3334
+ const result = await task.processor.process(task.inputPath, task.outputPath, task.options);
3335
+ if (result.success) {
3336
+ this.emitFileEvent("processing:complete", task.fileId, result);
3337
+ } else {
3338
+ this.emitFileEvent("processing:error", task.fileId, void 0, result.error);
3339
+ }
3340
+ } catch (error) {
3341
+ console.error(`\u274C [UniversalFileService] \u6587\u4EF6\u5904\u7406\u5931\u8D25: ${task.fileId}:`, error);
3342
+ this.emitFileEvent(
3343
+ "processing:error",
3344
+ task.fileId,
3345
+ void 0,
3346
+ error instanceof Error ? error.message : "\u5904\u7406\u5931\u8D25"
3347
+ );
3348
+ }
3349
+ }
3350
+ this.isProcessingQueueRunning = false;
3351
+ }
3352
+ cacheMetadata(metadata) {
3353
+ const expires = Date.now() + (this.config.cache?.metadataTTL || 3600) * 1e3;
3354
+ this.metadataCache.set(metadata.id, { data: metadata, expires });
3355
+ }
3356
+ _clearMetadataCache2(fileId) {
3357
+ this.metadataCache.delete(fileId);
3358
+ }
3359
+ emitFileEvent(type, fileId, data, error) {
3360
+ const event = {
3361
+ type,
3362
+ fileId,
3363
+ timestamp: /* @__PURE__ */ new Date(),
3364
+ data,
3365
+ error
3366
+ };
3367
+ this.emit(type, event);
3368
+ this.emit("*", event);
3369
+ }
3370
+ };
3371
+ var logger7 = chunk6PRFP5EG_js.createLogger("CacheManager");
3372
+ var CacheManager = class {
3373
+ constructor(options = {}) {
3374
+ this.redisClient = null;
3375
+ this.options = {
3376
+ defaultTTL: options.defaultTTL || 300,
3377
+ // 5分钟
3378
+ maxMemoryItems: options.maxMemoryItems || 1e3,
3379
+ enableRedis: options.enableRedis || false,
3380
+ redisConfig: options.redisConfig || {
3381
+ host: "localhost",
3382
+ port: 6379,
3383
+ db: 0
3384
+ },
3385
+ keyPrefix: options.keyPrefix || "universal-file:"
3386
+ };
3387
+ this.memoryCache = new lruCache.LRUCache({
3388
+ max: this.options.maxMemoryItems,
3389
+ ttl: this.options.defaultTTL * 1e3,
3390
+ // LRU缓存使用毫秒
3391
+ updateAgeOnGet: true,
3392
+ allowStale: false
3393
+ });
3394
+ this.stats = {
3395
+ totalRequests: 0,
3396
+ hits: 0,
3397
+ misses: 0,
3398
+ hitRate: 0,
3399
+ memorySize: 0,
3400
+ redisConnected: false
3401
+ };
3402
+ if (this.options.enableRedis) {
3403
+ this.initRedis();
3404
+ }
3405
+ }
3406
+ /**
3407
+ * 初始化Redis连接
3408
+ */
3409
+ async initRedis() {
3410
+ try {
3411
+ logger7.info("Redis\u7F13\u5B58\u5DF2\u7981\u7528 - \u8BF7\u5B89\u88C5\u5E76\u914D\u7F6ERedis\u5BA2\u6237\u7AEF");
3412
+ this.stats.redisConnected = false;
3413
+ } catch (error) {
3414
+ console.error("Redis\u8FDE\u63A5\u5931\u8D25:", error);
3415
+ this.stats.redisConnected = false;
3416
+ }
3417
+ }
3418
+ /**
3419
+ * 生成缓存键
3420
+ */
3421
+ generateKey(key) {
3422
+ return `${this.options.keyPrefix}${key}`;
3423
+ }
3424
+ /**
3425
+ * 获取缓存数据
3426
+ */
3427
+ async get(key) {
3428
+ const cacheKey = this.generateKey(key);
3429
+ this.stats.totalRequests++;
3430
+ try {
3431
+ const memoryItem = this.memoryCache.get(cacheKey);
3432
+ if (memoryItem && memoryItem.expiresAt > Date.now()) {
3433
+ memoryItem.accessCount++;
3434
+ memoryItem.lastAccessAt = Date.now();
3435
+ this.stats.hits++;
3436
+ this.updateHitRate();
3437
+ return memoryItem.data;
3438
+ }
3439
+ if (this.redisClient && this.stats.redisConnected) {
3440
+ const redisData = await this.redisClient.get(cacheKey);
3441
+ if (redisData) {
3442
+ const parsedData = JSON.parse(redisData);
3443
+ const cacheItem = {
3444
+ data: parsedData.data,
3445
+ createdAt: parsedData.createdAt,
3446
+ expiresAt: parsedData.expiresAt,
3447
+ accessCount: parsedData.accessCount + 1,
3448
+ lastAccessAt: Date.now()
3449
+ };
3450
+ this.memoryCache.set(cacheKey, cacheItem);
3451
+ this.stats.hits++;
3452
+ this.updateHitRate();
3453
+ return parsedData.data;
3454
+ }
3455
+ }
3456
+ this.stats.misses++;
3457
+ this.updateHitRate();
3458
+ return null;
3459
+ } catch (error) {
3460
+ console.error("\u7F13\u5B58\u83B7\u53D6\u5931\u8D25:", error);
3461
+ this.stats.misses++;
3462
+ this.updateHitRate();
3463
+ return null;
3464
+ }
3465
+ }
3466
+ /**
3467
+ * 设置缓存数据
3468
+ */
3469
+ async set(key, data, ttl) {
3470
+ const cacheKey = this.generateKey(key);
3471
+ const expireTime = ttl || this.options.defaultTTL;
3472
+ const now = Date.now();
3473
+ const cacheItem = {
3474
+ data,
3475
+ createdAt: now,
3476
+ expiresAt: now + expireTime * 1e3,
3477
+ accessCount: 0,
3478
+ lastAccessAt: now
3479
+ };
3480
+ try {
3481
+ this.memoryCache.set(cacheKey, cacheItem);
3482
+ if (this.redisClient && this.stats.redisConnected) {
3483
+ await this.redisClient.setex(cacheKey, expireTime, JSON.stringify(cacheItem));
3484
+ }
3485
+ this.updateMemorySize();
3486
+ } catch (error) {
3487
+ console.error("\u7F13\u5B58\u8BBE\u7F6E\u5931\u8D25:", error);
3488
+ }
3489
+ }
3490
+ /**
3491
+ * 删除缓存数据
3492
+ */
3493
+ async delete(key) {
3494
+ const cacheKey = this.generateKey(key);
3495
+ try {
3496
+ this.memoryCache.delete(cacheKey);
3497
+ if (this.redisClient && this.stats.redisConnected) {
3498
+ await this.redisClient.del(cacheKey);
3499
+ }
3500
+ this.updateMemorySize();
3501
+ } catch (error) {
3502
+ console.error("\u7F13\u5B58\u5220\u9664\u5931\u8D25:", error);
3503
+ }
3504
+ }
3505
+ /**
3506
+ * 批量删除缓存(支持模式匹配)
3507
+ */
3508
+ async deletePattern(pattern) {
3509
+ const cachePattern = this.generateKey(pattern);
3510
+ try {
3511
+ const keys = Array.from(this.memoryCache.keys());
3512
+ for (const key of keys) {
3513
+ if (this.matchPattern(key, cachePattern)) {
3514
+ this.memoryCache.delete(key);
3515
+ }
3516
+ }
3517
+ if (this.redisClient && this.stats.redisConnected) {
3518
+ const redisKeys = await this.redisClient.keys(cachePattern);
3519
+ if (redisKeys.length > 0) {
3520
+ await this.redisClient.del(...redisKeys);
3521
+ }
3522
+ }
3523
+ this.updateMemorySize();
3524
+ } catch (error) {
3525
+ console.error("\u6279\u91CF\u7F13\u5B58\u5220\u9664\u5931\u8D25:", error);
3526
+ }
3527
+ }
3528
+ /**
3529
+ * 清空所有缓存
3530
+ */
3531
+ async clear() {
3532
+ try {
3533
+ this.memoryCache.clear();
3534
+ if (this.redisClient && this.stats.redisConnected) {
3535
+ const keys = await this.redisClient.keys(`${this.options.keyPrefix}*`);
3536
+ if (keys.length > 0) {
3537
+ await this.redisClient.del(...keys);
3538
+ }
3539
+ }
3540
+ this.stats.hits = 0;
3541
+ this.stats.misses = 0;
3542
+ this.stats.totalRequests = 0;
3543
+ this.stats.hitRate = 0;
3544
+ this.updateMemorySize();
3545
+ } catch (error) {
3546
+ console.error("\u6E05\u7A7A\u7F13\u5B58\u5931\u8D25:", error);
3547
+ }
3548
+ }
3549
+ /**
3550
+ * 获取缓存统计信息
3551
+ */
3552
+ getStats() {
3553
+ this.updateMemorySize();
3554
+ return { ...this.stats };
3555
+ }
3556
+ /**
3557
+ * 获取缓存项详情(用于调试)
3558
+ */
3559
+ getCacheItem(key) {
3560
+ const cacheKey = this.generateKey(key);
3561
+ return this.memoryCache.get(cacheKey) || null;
3562
+ }
3563
+ /**
3564
+ * 预热缓存
3565
+ */
3566
+ async warmup(items) {
3567
+ logger7.info(`\u5F00\u59CB\u9884\u70ED\u7F13\u5B58\uFF0C\u5171 ${items.length} \u9879...`);
3568
+ const promises = items.map((item) => this.set(item.key, item.data, item.ttl));
3569
+ try {
3570
+ await Promise.all(promises);
3571
+ logger7.info("\u7F13\u5B58\u9884\u70ED\u5B8C\u6210");
3572
+ } catch (error) {
3573
+ console.error("\u7F13\u5B58\u9884\u70ED\u5931\u8D25:", error);
3574
+ }
3575
+ }
3576
+ /**
3577
+ * 更新命中率
3578
+ */
3579
+ updateHitRate() {
3580
+ this.stats.hitRate = this.stats.totalRequests > 0 ? this.stats.hits / this.stats.totalRequests * 100 : 0;
3581
+ }
3582
+ /**
3583
+ * 更新内存使用量
3584
+ */
3585
+ updateMemorySize() {
3586
+ this.stats.memorySize = this.memoryCache.size;
3587
+ }
3588
+ /**
3589
+ * 模式匹配函数
3590
+ */
3591
+ matchPattern(key, pattern) {
3592
+ const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
3593
+ const regex = new RegExp(`^${regexPattern}$`);
3594
+ return regex.test(key);
3595
+ }
3596
+ /**
3597
+ * 获取或设置缓存(如果不存在则调用生成函数)
3598
+ */
3599
+ async getOrSet(key, generator, ttl) {
3600
+ const cachedData = await this.get(key);
3601
+ if (cachedData !== null) {
3602
+ return cachedData;
3603
+ }
3604
+ try {
3605
+ const data = await generator();
3606
+ await this.set(key, data, ttl);
3607
+ return data;
3608
+ } catch (error) {
3609
+ console.error("\u7F13\u5B58\u751F\u6210\u5931\u8D25:", error);
3610
+ throw error;
3611
+ }
3612
+ }
3613
+ /**
3614
+ * 关闭缓存管理器
3615
+ */
3616
+ async close() {
3617
+ try {
3618
+ this.memoryCache.clear();
3619
+ if (this.redisClient) {
3620
+ await this.redisClient.quit();
3621
+ }
3622
+ } catch (error) {
3623
+ console.error("\u5173\u95ED\u7F13\u5B58\u7BA1\u7406\u5668\u5931\u8D25:", error);
3624
+ }
3625
+ }
3626
+ };
3627
+ new CacheManager({
3628
+ defaultTTL: 300,
3629
+ // 5分钟
3630
+ maxMemoryItems: 2e3,
3631
+ enableRedis: false,
3632
+ // 开发环境暂时禁用Redis
3633
+ keyPrefix: "universal-file:"
3634
+ });
3635
+
3636
+ // src/universalFile/server/monitoring/PerformanceMonitor.ts
3637
+ var PerformanceMonitor = class {
3638
+ // 最大保存跟踪数量
3639
+ constructor() {
3640
+ this.metrics = [];
3641
+ this.requestTraces = /* @__PURE__ */ new Map();
3642
+ this.maxMetrics = 1e4;
3643
+ // 最大保存指标数量
3644
+ this.maxTraces = 1e3;
3645
+ this.stats = this.initializeStats();
3646
+ setInterval(() => {
3647
+ this.cleanupOldData();
3648
+ }, 6e4);
3649
+ }
3650
+ /**
3651
+ * 初始化统计信息
3652
+ */
3653
+ initializeStats() {
3654
+ return {
3655
+ apiResponseTimes: {
3656
+ average: 0,
3657
+ min: 0,
3658
+ max: 0,
3659
+ p95: 0,
3660
+ p99: 0,
3661
+ totalRequests: 0
3662
+ },
3663
+ databasePerformance: {
3664
+ averageQueryTime: 0,
3665
+ slowQueries: 0,
3666
+ totalQueries: 0,
3667
+ queryErrors: 0
3668
+ },
3669
+ fileOperations: {
3670
+ uploads: {
3671
+ total: 0,
3672
+ successful: 0,
3673
+ failed: 0,
3674
+ averageTime: 0,
3675
+ averageSize: 0
3676
+ },
3677
+ downloads: {
3678
+ total: 0,
3679
+ successful: 0,
3680
+ failed: 0,
3681
+ averageTime: 0
3682
+ }
3683
+ },
3684
+ systemResources: {
3685
+ memoryUsage: 0,
3686
+ cpuUsage: 0,
3687
+ diskUsage: 0
3688
+ }
3689
+ };
3690
+ }
3691
+ /**
3692
+ * 开始请求跟踪
3693
+ */
3694
+ startRequest(requestId, path6, method, userId) {
3695
+ const trace = {
3696
+ requestId,
3697
+ path: path6,
3698
+ method,
3699
+ startTime: Date.now(),
3700
+ userId
3701
+ };
3702
+ this.requestTraces.set(requestId, trace);
3703
+ }
3704
+ /**
3705
+ * 结束请求跟踪
3706
+ */
3707
+ endRequest(requestId, statusCode, error, fileSize) {
3708
+ const trace = this.requestTraces.get(requestId);
3709
+ if (!trace) return;
3710
+ const endTime = Date.now();
3711
+ const duration = endTime - trace.startTime;
3712
+ trace.endTime = endTime;
3713
+ trace.duration = duration;
3714
+ trace.statusCode = statusCode;
3715
+ trace.error = error;
3716
+ trace.fileSize = fileSize;
3717
+ this.recordMetric("api_response_time", duration, "ms", {
3718
+ path: trace.path,
3719
+ method: trace.method,
3720
+ status: statusCode.toString()
3721
+ });
3722
+ this.updateApiStats(duration);
3723
+ if (trace.path.includes("upload")) {
3724
+ this.updateUploadStats(statusCode < 400, duration, fileSize);
3725
+ } else if (trace.path.includes("download")) {
3726
+ this.updateDownloadStats(statusCode < 400, duration);
3727
+ }
3728
+ if (this.requestTraces.size > this.maxTraces) {
3729
+ const oldestKey = this.requestTraces.keys().next().value;
3730
+ if (oldestKey) {
3731
+ this.requestTraces.delete(oldestKey);
3732
+ }
3733
+ }
3734
+ }
3735
+ /**
3736
+ * 记录数据库查询性能
3737
+ */
3738
+ recordDatabaseQuery(queryTime, isError = false) {
3739
+ this.recordMetric("db_query_time", queryTime, "ms");
3740
+ this.stats.databasePerformance.totalQueries++;
3741
+ if (isError) {
3742
+ this.stats.databasePerformance.queryErrors++;
3743
+ }
3744
+ const total = this.stats.databasePerformance.totalQueries;
3745
+ const currentAvg = this.stats.databasePerformance.averageQueryTime;
3746
+ this.stats.databasePerformance.averageQueryTime = (currentAvg * (total - 1) + queryTime) / total;
3747
+ if (queryTime > 1e3) {
3748
+ this.stats.databasePerformance.slowQueries++;
3749
+ this.recordMetric("slow_query", 1, "count");
3750
+ }
3751
+ }
3752
+ /**
3753
+ * 记录系统资源使用情况
3754
+ */
3755
+ recordSystemResources() {
3756
+ try {
3757
+ const memoryUsage = process.memoryUsage();
3758
+ const memoryUsedMB = memoryUsage.heapUsed / 1024 / 1024;
3759
+ this.stats.systemResources.memoryUsage = memoryUsedMB;
3760
+ this.recordMetric("memory_usage", memoryUsedMB, "MB");
3761
+ this.recordMetric("heap_total", memoryUsage.heapTotal / 1024 / 1024, "MB");
3762
+ this.recordMetric("heap_used", memoryUsage.heapUsed / 1024 / 1024, "MB");
3763
+ this.recordMetric("external", memoryUsage.external / 1024 / 1024, "MB");
3764
+ } catch (error) {
3765
+ console.error("\u8BB0\u5F55\u7CFB\u7EDF\u8D44\u6E90\u5931\u8D25:", error);
3766
+ }
3767
+ }
3768
+ /**
3769
+ * 记录自定义指标
3770
+ */
3771
+ recordMetric(name, value, unit, labels) {
3772
+ const metric = {
3773
+ name,
3774
+ value,
3775
+ unit,
3776
+ timestamp: Date.now(),
3777
+ labels
3778
+ };
3779
+ this.metrics.push(metric);
3780
+ if (this.metrics.length > this.maxMetrics) {
3781
+ this.metrics.shift();
3782
+ }
3783
+ }
3784
+ /**
3785
+ * 获取性能统计信息
3786
+ */
3787
+ getStats() {
3788
+ this.recordSystemResources();
3789
+ return { ...this.stats };
3790
+ }
3791
+ /**
3792
+ * 获取指定时间范围内的指标
3793
+ */
3794
+ getMetrics(name, startTime, endTime) {
3795
+ let filteredMetrics = this.metrics;
3796
+ if (name) {
3797
+ filteredMetrics = filteredMetrics.filter((m) => m.name === name);
3798
+ }
3799
+ if (startTime) {
3800
+ filteredMetrics = filteredMetrics.filter((m) => m.timestamp >= startTime);
3801
+ }
3802
+ if (endTime) {
3803
+ filteredMetrics = filteredMetrics.filter((m) => m.timestamp <= endTime);
3804
+ }
3805
+ return filteredMetrics;
3806
+ }
3807
+ /**
3808
+ * 获取请求跟踪信息
3809
+ */
3810
+ getRequestTraces(limit = 100) {
3811
+ const traces = Array.from(this.requestTraces.values());
3812
+ return traces.sort((a, b) => b.startTime - a.startTime).slice(0, limit);
3813
+ }
3814
+ /**
3815
+ * 获取慢请求列表
3816
+ */
3817
+ getSlowRequests(threshold = 1e3) {
3818
+ return this.getRequestTraces().filter((trace) => trace.duration && trace.duration > threshold).sort((a, b) => (b.duration || 0) - (a.duration || 0));
3819
+ }
3820
+ /**
3821
+ * 获取错误请求列表
3822
+ */
3823
+ getErrorRequests() {
3824
+ return this.getRequestTraces().filter((trace) => trace.statusCode && trace.statusCode >= 400);
3825
+ }
3826
+ /**
3827
+ * 生成性能报告
3828
+ */
3829
+ generateReport() {
3830
+ const stats = this.getStats();
3831
+ const slowRequests = this.getSlowRequests(500);
3832
+ const errorRequests = this.getErrorRequests();
3833
+ return {
3834
+ summary: {
3835
+ totalRequests: stats.apiResponseTimes.totalRequests,
3836
+ averageResponseTime: stats.apiResponseTimes.average,
3837
+ errorRate: errorRequests.length / Math.max(stats.apiResponseTimes.totalRequests, 1) * 100,
3838
+ cacheHitRate: 0,
3839
+ // 需要从缓存管理器获取
3840
+ slowQueryCount: stats.databasePerformance.slowQueries
3841
+ },
3842
+ topSlowRequests: slowRequests.slice(0, 10),
3843
+ recentErrors: errorRequests.slice(0, 10),
3844
+ systemHealth: {
3845
+ memoryUsage: stats.systemResources.memoryUsage,
3846
+ queryPerformance: stats.databasePerformance.averageQueryTime,
3847
+ uptime: process.uptime()
3848
+ }
3849
+ };
3850
+ }
3851
+ /**
3852
+ * 更新API统计信息
3853
+ */
3854
+ updateApiStats(duration) {
3855
+ const stats = this.stats.apiResponseTimes;
3856
+ stats.totalRequests++;
3857
+ stats.average = (stats.average * (stats.totalRequests - 1) + duration) / stats.totalRequests;
3858
+ if (stats.totalRequests === 1 || duration < stats.min) {
3859
+ stats.min = duration;
3860
+ }
3861
+ if (stats.totalRequests === 1 || duration > stats.max) {
3862
+ stats.max = duration;
3863
+ }
3864
+ const recentDurations = this.getMetrics("api_response_time", Date.now() - 3e5).map((m) => m.value).sort((a, b) => a - b);
3865
+ if (recentDurations.length > 0) {
3866
+ stats.p95 = recentDurations[Math.floor(recentDurations.length * 0.95)] || 0;
3867
+ stats.p99 = recentDurations[Math.floor(recentDurations.length * 0.99)] || 0;
3868
+ }
3869
+ }
3870
+ /**
3871
+ * 更新上传统计信息
3872
+ */
3873
+ updateUploadStats(success, duration, fileSize) {
3874
+ const uploads = this.stats.fileOperations.uploads;
3875
+ uploads.total++;
3876
+ if (success) {
3877
+ uploads.successful++;
3878
+ } else {
3879
+ uploads.failed++;
3880
+ }
3881
+ uploads.averageTime = (uploads.averageTime * (uploads.total - 1) + duration) / uploads.total;
3882
+ if (fileSize) {
3883
+ uploads.averageSize = (uploads.averageSize * (uploads.total - 1) + fileSize) / uploads.total;
3884
+ }
3885
+ }
3886
+ /**
3887
+ * 更新下载统计信息
3888
+ */
3889
+ updateDownloadStats(success, duration) {
3890
+ const downloads = this.stats.fileOperations.downloads;
3891
+ downloads.total++;
3892
+ if (success) {
3893
+ downloads.successful++;
3894
+ } else {
3895
+ downloads.failed++;
3896
+ }
3897
+ downloads.averageTime = (downloads.averageTime * (downloads.total - 1) + duration) / downloads.total;
3898
+ }
3899
+ /**
3900
+ * 清理过期数据
3901
+ */
3902
+ cleanupOldData() {
3903
+ const oneHourAgo = Date.now() - 36e5;
3904
+ this.metrics = this.metrics.filter((m) => m.timestamp > oneHourAgo);
3905
+ for (const [requestId, trace] of this.requestTraces.entries()) {
3906
+ if (trace.startTime < oneHourAgo) {
3907
+ this.requestTraces.delete(requestId);
3908
+ }
3909
+ }
3910
+ }
3911
+ /**
3912
+ * 重置统计信息
3913
+ */
3914
+ reset() {
3915
+ this.metrics = [];
3916
+ this.requestTraces.clear();
3917
+ this.stats = this.initializeStats();
3918
+ }
3919
+ };
3920
+ new PerformanceMonitor();
3921
+
3922
+ // src/universalFile/server/cdn/CdnCacheStrategy.ts
3923
+ var CacheStrategyType2 = /* @__PURE__ */ ((CacheStrategyType3) => {
3924
+ CacheStrategyType3["IMAGE"] = "image";
3925
+ CacheStrategyType3["VIDEO"] = "video";
3926
+ CacheStrategyType3["AUDIO"] = "audio";
3927
+ CacheStrategyType3["DOCUMENT"] = "document";
3928
+ CacheStrategyType3["ARCHIVE"] = "archive";
3929
+ CacheStrategyType3["STATIC"] = "static";
3930
+ CacheStrategyType3["OTHER"] = "other";
3931
+ return CacheStrategyType3;
3932
+ })(CacheStrategyType2 || {});
3933
+ var CdnCacheStrategy2 = class {
3934
+ constructor() {
3935
+ this.strategies = /* @__PURE__ */ new Map();
3936
+ this.stats = /* @__PURE__ */ new Map();
3937
+ this.initializeDefaultStrategies();
3938
+ }
3939
+ /**
3940
+ * 初始化默认缓存策略
3941
+ */
3942
+ initializeDefaultStrategies() {
3943
+ this.strategies.set("image" /* IMAGE */, {
3944
+ type: "image" /* IMAGE */,
3945
+ ttl: 30 * 24 * 3600,
3946
+ // 30天
3947
+ browserCache: true,
3948
+ browserCacheTtl: 7 * 24 * 3600,
3949
+ // 7天
3950
+ cdnCache: true,
3951
+ cdnCacheTtl: 30 * 24 * 3600,
3952
+ // 30天
3953
+ enableWarmup: true,
3954
+ cacheControl: "public, max-age=604800, s-maxage=2592000",
3955
+ allowedMimeTypes: ["image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml"],
3956
+ maxFileSize: 50 * 1024 * 1024
3957
+ // 50MB
3958
+ });
3959
+ this.strategies.set("video" /* VIDEO */, {
3960
+ type: "video" /* VIDEO */,
3961
+ ttl: 7 * 24 * 3600,
3962
+ // 7天
3963
+ browserCache: true,
3964
+ browserCacheTtl: 24 * 3600,
3965
+ // 1天
3966
+ cdnCache: true,
3967
+ cdnCacheTtl: 7 * 24 * 3600,
3968
+ // 7天
3969
+ enableWarmup: false,
3970
+ // 视频文件通常较大,不预热
3971
+ cacheControl: "public, max-age=86400, s-maxage=604800",
3972
+ allowedMimeTypes: ["video/mp4", "video/avi", "video/mov", "video/wmv", "video/webm"],
3973
+ maxFileSize: 500 * 1024 * 1024
3974
+ // 500MB
3975
+ });
3976
+ this.strategies.set("audio" /* AUDIO */, {
3977
+ type: "audio" /* AUDIO */,
3978
+ ttl: 14 * 24 * 3600,
3979
+ // 14天
3980
+ browserCache: true,
3981
+ browserCacheTtl: 3 * 24 * 3600,
3982
+ // 3天
3983
+ cdnCache: true,
3984
+ cdnCacheTtl: 14 * 24 * 3600,
3985
+ // 14天
3986
+ enableWarmup: true,
3987
+ cacheControl: "public, max-age=259200, s-maxage=1209600",
3988
+ allowedMimeTypes: ["audio/mpeg", "audio/wav", "audio/ogg", "audio/m4a", "audio/flac"],
3989
+ maxFileSize: 100 * 1024 * 1024
3990
+ // 100MB
3991
+ });
3992
+ this.strategies.set("document" /* DOCUMENT */, {
3993
+ type: "document" /* DOCUMENT */,
3994
+ ttl: 24 * 3600,
3995
+ // 1天
3996
+ browserCache: true,
3997
+ browserCacheTtl: 3600,
3998
+ // 1小时
3999
+ cdnCache: true,
4000
+ cdnCacheTtl: 24 * 3600,
4001
+ // 1天
4002
+ enableWarmup: false,
4003
+ cacheControl: "public, max-age=3600, s-maxage=86400",
4004
+ allowedMimeTypes: [
4005
+ "application/pdf",
4006
+ "application/msword",
4007
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
4008
+ "application/vnd.ms-excel",
4009
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
4010
+ "text/plain",
4011
+ "text/csv"
4012
+ ],
4013
+ maxFileSize: 50 * 1024 * 1024
4014
+ // 50MB
4015
+ });
4016
+ this.strategies.set("archive" /* ARCHIVE */, {
4017
+ type: "archive" /* ARCHIVE */,
4018
+ ttl: 24 * 3600,
4019
+ // 1天
4020
+ browserCache: true,
4021
+ browserCacheTtl: 1800,
4022
+ // 30分钟
4023
+ cdnCache: true,
4024
+ cdnCacheTtl: 24 * 3600,
4025
+ // 1天
4026
+ enableWarmup: false,
4027
+ cacheControl: "public, max-age=1800, s-maxage=86400",
4028
+ allowedMimeTypes: [
4029
+ "application/zip",
4030
+ "application/x-rar-compressed",
4031
+ "application/x-7z-compressed",
4032
+ "application/gzip",
4033
+ "application/x-tar"
4034
+ ],
4035
+ maxFileSize: 100 * 1024 * 1024
4036
+ // 100MB
4037
+ });
4038
+ this.strategies.set("static" /* STATIC */, {
4039
+ type: "static" /* STATIC */,
4040
+ ttl: 365 * 24 * 3600,
4041
+ // 1年
4042
+ browserCache: true,
4043
+ browserCacheTtl: 30 * 24 * 3600,
4044
+ // 30天
4045
+ cdnCache: true,
4046
+ cdnCacheTtl: 365 * 24 * 3600,
4047
+ // 1年
4048
+ enableWarmup: true,
4049
+ cacheControl: "public, max-age=2592000, s-maxage=31536000, immutable",
4050
+ allowedMimeTypes: [
4051
+ "text/css",
4052
+ "application/javascript",
4053
+ "application/json",
4054
+ "font/woff",
4055
+ "font/woff2",
4056
+ "font/ttf"
4057
+ ],
4058
+ maxFileSize: 10 * 1024 * 1024
4059
+ // 10MB
4060
+ });
4061
+ this.strategies.set("other" /* OTHER */, {
4062
+ type: "other" /* OTHER */,
4063
+ ttl: 3600,
4064
+ // 1小时
4065
+ browserCache: true,
4066
+ browserCacheTtl: 300,
4067
+ // 5分钟
4068
+ cdnCache: false,
4069
+ cdnCacheTtl: 0,
4070
+ enableWarmup: false,
4071
+ cacheControl: "public, max-age=300",
4072
+ allowedMimeTypes: [],
4073
+ maxFileSize: 10 * 1024 * 1024
4074
+ // 10MB
4075
+ });
4076
+ for (const type of Object.values(CacheStrategyType2)) {
4077
+ this.stats.set(type, {
4078
+ hits: 0,
4079
+ misses: 0,
4080
+ hitRate: 0,
4081
+ totalRequests: 0,
4082
+ estimatedSize: 0,
4083
+ bandwidthSaved: 0
4084
+ });
4085
+ }
4086
+ }
4087
+ /**
4088
+ * 根据MIME类型获取缓存策略
4089
+ */
4090
+ getStrategyByMimeType(mimeType) {
4091
+ for (const [type, strategy] of this.strategies.entries()) {
4092
+ if (strategy.allowedMimeTypes.includes(mimeType)) {
4093
+ return strategy;
4094
+ }
4095
+ }
4096
+ if (mimeType.startsWith("image/")) {
4097
+ return this.strategies.get("image" /* IMAGE */);
4098
+ } else if (mimeType.startsWith("video/")) {
4099
+ return this.strategies.get("video" /* VIDEO */);
4100
+ } else if (mimeType.startsWith("audio/")) {
4101
+ return this.strategies.get("audio" /* AUDIO */);
4102
+ } else if (mimeType.includes("pdf") || mimeType.includes("document") || mimeType.includes("word") || mimeType.includes("excel")) {
4103
+ return this.strategies.get("document" /* DOCUMENT */);
4104
+ } else if (mimeType.includes("zip") || mimeType.includes("compressed") || mimeType.includes("archive")) {
4105
+ return this.strategies.get("archive" /* ARCHIVE */);
4106
+ }
4107
+ return this.strategies.get("other" /* OTHER */);
4108
+ }
4109
+ /**
4110
+ * 生成缓存控制头
4111
+ */
4112
+ generateCacheHeaders(mimeType, fileSize) {
4113
+ const strategy = this.getStrategyByMimeType(mimeType);
4114
+ if (fileSize && strategy.maxFileSize && fileSize > strategy.maxFileSize) {
4115
+ return {
4116
+ "Cache-Control": "public, max-age=300",
4117
+ // 5分钟
4118
+ Expires: new Date(Date.now() + 300 * 1e3).toUTCString()
4119
+ };
4120
+ }
4121
+ const headers = {
4122
+ "Cache-Control": strategy.cacheControl,
4123
+ Expires: new Date(Date.now() + strategy.browserCacheTtl * 1e3).toUTCString()
4124
+ };
4125
+ if (strategy.browserCache) {
4126
+ headers["ETag"] = `"${Date.now()}"`;
4127
+ }
4128
+ headers["Last-Modified"] = (/* @__PURE__ */ new Date()).toUTCString();
4129
+ if (strategy.type === "static" /* STATIC */) {
4130
+ headers["Cache-Control"] += ", immutable";
4131
+ }
4132
+ return headers;
4133
+ }
4134
+ /**
4135
+ * 检查是否需要缓存预热
4136
+ */
4137
+ shouldWarmupCache(mimeType) {
4138
+ const strategy = this.getStrategyByMimeType(mimeType);
4139
+ return strategy.enableWarmup;
4140
+ }
4141
+ /**
4142
+ * 记录缓存命中
4143
+ */
4144
+ recordCacheHit(mimeType, fileSize = 0) {
4145
+ const strategy = this.getStrategyByMimeType(mimeType);
4146
+ const stats = this.stats.get(strategy.type);
4147
+ if (stats) {
4148
+ stats.hits++;
4149
+ stats.totalRequests++;
4150
+ stats.bandwidthSaved += fileSize;
4151
+ stats.hitRate = stats.hits / stats.totalRequests * 100;
4152
+ }
4153
+ }
4154
+ /**
4155
+ * 记录缓存未命中
4156
+ */
4157
+ recordCacheMiss(mimeType, fileSize = 0) {
4158
+ const strategy = this.getStrategyByMimeType(mimeType);
4159
+ const stats = this.stats.get(strategy.type);
4160
+ if (stats) {
4161
+ stats.misses++;
4162
+ stats.totalRequests++;
4163
+ stats.estimatedSize += fileSize;
4164
+ stats.hitRate = stats.hits / stats.totalRequests * 100;
4165
+ }
4166
+ }
4167
+ /**
4168
+ * 获取缓存统计信息
4169
+ */
4170
+ getCacheStats() {
4171
+ return new Map(this.stats);
4172
+ }
4173
+ /**
4174
+ * 获取总体缓存统计
4175
+ */
4176
+ getOverallStats() {
4177
+ const overall = {
4178
+ hits: 0,
4179
+ misses: 0,
4180
+ hitRate: 0,
4181
+ totalRequests: 0,
4182
+ estimatedSize: 0,
4183
+ bandwidthSaved: 0
4184
+ };
4185
+ for (const stats of this.stats.values()) {
4186
+ overall.hits += stats.hits;
4187
+ overall.misses += stats.misses;
4188
+ overall.totalRequests += stats.totalRequests;
4189
+ overall.estimatedSize += stats.estimatedSize;
4190
+ overall.bandwidthSaved += stats.bandwidthSaved;
4191
+ }
4192
+ overall.hitRate = overall.totalRequests > 0 ? overall.hits / overall.totalRequests * 100 : 0;
4193
+ return overall;
4194
+ }
4195
+ /**
4196
+ * 生成缓存优化建议
4197
+ */
4198
+ generateOptimizationSuggestions() {
4199
+ const suggestions = [];
4200
+ for (const [type, stats] of this.stats.entries()) {
4201
+ if (stats.totalRequests > 100) {
4202
+ if (stats.hitRate < 60) {
4203
+ suggestions.push({
4204
+ type,
4205
+ issue: `${type}\u7C7B\u578B\u6587\u4EF6\u7F13\u5B58\u547D\u4E2D\u7387\u8FC7\u4F4E (${stats.hitRate.toFixed(1)}%)`,
4206
+ suggestion: "\u8003\u8651\u589E\u52A0\u7F13\u5B58\u65F6\u95F4\u6216\u542F\u7528\u9884\u70ED\u673A\u5236",
4207
+ severity: stats.hitRate < 30 ? "high" : "medium"
4208
+ });
4209
+ }
4210
+ if (stats.estimatedSize > 1024 * 1024 * 1024) {
4211
+ suggestions.push({
4212
+ type,
4213
+ issue: `${type}\u7C7B\u578B\u6587\u4EF6\u7F13\u5B58\u5360\u7528\u7A7A\u95F4\u8FC7\u5927 (${(stats.estimatedSize / 1024 / 1024 / 1024).toFixed(2)}GB)`,
4214
+ suggestion: "\u8003\u8651\u51CF\u5C11\u7F13\u5B58\u65F6\u95F4\u6216\u4F18\u5316\u6587\u4EF6\u538B\u7F29",
4215
+ severity: "medium"
4216
+ });
4217
+ }
4218
+ }
4219
+ }
4220
+ return suggestions;
4221
+ }
4222
+ /**
4223
+ * 更新缓存策略
4224
+ */
4225
+ updateStrategy(type, config) {
4226
+ const currentStrategy = this.strategies.get(type);
4227
+ if (currentStrategy) {
4228
+ this.strategies.set(type, { ...currentStrategy, ...config });
4229
+ }
4230
+ }
4231
+ /**
4232
+ * 重置统计信息
4233
+ */
4234
+ resetStats() {
4235
+ for (const type of Object.values(CacheStrategyType2)) {
4236
+ this.stats.set(type, {
4237
+ hits: 0,
4238
+ misses: 0,
4239
+ hitRate: 0,
4240
+ totalRequests: 0,
4241
+ estimatedSize: 0,
4242
+ bandwidthSaved: 0
4243
+ });
4244
+ }
4245
+ }
4246
+ };
4247
+ new CdnCacheStrategy2();
4248
+
4249
+ // src/universalFile/server/types/api.ts
4250
+ var ApiErrorCode = /* @__PURE__ */ ((ApiErrorCode2) => {
4251
+ ApiErrorCode2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
4252
+ ApiErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
4253
+ ApiErrorCode2["AUTHENTICATION_ERROR"] = "AUTHENTICATION_ERROR";
4254
+ ApiErrorCode2["AUTHORIZATION_ERROR"] = "AUTHORIZATION_ERROR";
4255
+ ApiErrorCode2["NOT_FOUND"] = "NOT_FOUND";
4256
+ ApiErrorCode2["CONFLICT"] = "CONFLICT";
4257
+ ApiErrorCode2["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED";
4258
+ ApiErrorCode2["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
4259
+ ApiErrorCode2["FILE_TOO_LARGE"] = "FILE_TOO_LARGE";
4260
+ ApiErrorCode2["FILE_TYPE_NOT_SUPPORTED"] = "FILE_TYPE_NOT_SUPPORTED";
4261
+ ApiErrorCode2["FILE_UPLOAD_FAILED"] = "FILE_UPLOAD_FAILED";
4262
+ ApiErrorCode2["FILE_PROCESSING_FAILED"] = "FILE_PROCESSING_FAILED";
4263
+ ApiErrorCode2["FILE_ALREADY_EXISTS"] = "FILE_ALREADY_EXISTS";
4264
+ ApiErrorCode2["FILE_CORRUPTED"] = "FILE_CORRUPTED";
4265
+ ApiErrorCode2["FOLDER_NOT_FOUND"] = "FOLDER_NOT_FOUND";
4266
+ ApiErrorCode2["FOLDER_NOT_EMPTY"] = "FOLDER_NOT_EMPTY";
4267
+ ApiErrorCode2["FOLDER_NAME_CONFLICT"] = "FOLDER_NAME_CONFLICT";
4268
+ ApiErrorCode2["FOLDER_DEPTH_EXCEEDED"] = "FOLDER_DEPTH_EXCEEDED";
4269
+ ApiErrorCode2["STORAGE_PROVIDER_ERROR"] = "STORAGE_PROVIDER_ERROR";
4270
+ ApiErrorCode2["STORAGE_QUOTA_EXCEEDED"] = "STORAGE_QUOTA_EXCEEDED";
4271
+ ApiErrorCode2["STORAGE_UNAVAILABLE"] = "STORAGE_UNAVAILABLE";
4272
+ ApiErrorCode2["SHARE_NOT_FOUND"] = "SHARE_NOT_FOUND";
4273
+ ApiErrorCode2["SHARE_EXPIRED"] = "SHARE_EXPIRED";
4274
+ ApiErrorCode2["SHARE_PASSWORD_INCORRECT"] = "SHARE_PASSWORD_INCORRECT";
4275
+ ApiErrorCode2["SHARE_ACCESS_DENIED"] = "SHARE_ACCESS_DENIED";
4276
+ ApiErrorCode2["SHARE_DOWNLOAD_LIMIT_EXCEEDED"] = "SHARE_DOWNLOAD_LIMIT_EXCEEDED";
4277
+ return ApiErrorCode2;
4278
+ })(ApiErrorCode || {});
4279
+ var ErrorMessages = {
4280
+ ["UNKNOWN_ERROR" /* UNKNOWN_ERROR */]: "\u672A\u77E5\u9519\u8BEF",
4281
+ ["VALIDATION_ERROR" /* VALIDATION_ERROR */]: "\u53C2\u6570\u9A8C\u8BC1\u5931\u8D25",
4282
+ ["AUTHENTICATION_ERROR" /* AUTHENTICATION_ERROR */]: "\u8EAB\u4EFD\u9A8C\u8BC1\u5931\u8D25",
4283
+ ["AUTHORIZATION_ERROR" /* AUTHORIZATION_ERROR */]: "\u6743\u9650\u4E0D\u8DB3",
4284
+ ["NOT_FOUND" /* NOT_FOUND */]: "\u8D44\u6E90\u4E0D\u5B58\u5728",
4285
+ ["CONFLICT" /* CONFLICT */]: "\u8D44\u6E90\u51B2\u7A81",
4286
+ ["RATE_LIMIT_EXCEEDED" /* RATE_LIMIT_EXCEEDED */]: "\u8BF7\u6C42\u9891\u7387\u8D85\u9650",
4287
+ ["FILE_NOT_FOUND" /* FILE_NOT_FOUND */]: "\u6587\u4EF6\u4E0D\u5B58\u5728",
4288
+ ["FILE_TOO_LARGE" /* FILE_TOO_LARGE */]: "\u6587\u4EF6\u8FC7\u5927",
4289
+ ["FILE_TYPE_NOT_SUPPORTED" /* FILE_TYPE_NOT_SUPPORTED */]: "\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B",
4290
+ ["FILE_UPLOAD_FAILED" /* FILE_UPLOAD_FAILED */]: "\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25",
4291
+ ["FILE_PROCESSING_FAILED" /* FILE_PROCESSING_FAILED */]: "\u6587\u4EF6\u5904\u7406\u5931\u8D25",
4292
+ ["FILE_ALREADY_EXISTS" /* FILE_ALREADY_EXISTS */]: "\u6587\u4EF6\u5DF2\u5B58\u5728",
4293
+ ["FILE_CORRUPTED" /* FILE_CORRUPTED */]: "\u6587\u4EF6\u5DF2\u635F\u574F",
4294
+ ["FOLDER_NOT_FOUND" /* FOLDER_NOT_FOUND */]: "\u6587\u4EF6\u5939\u4E0D\u5B58\u5728",
4295
+ ["FOLDER_NOT_EMPTY" /* FOLDER_NOT_EMPTY */]: "\u6587\u4EF6\u5939\u4E0D\u4E3A\u7A7A",
4296
+ ["FOLDER_NAME_CONFLICT" /* FOLDER_NAME_CONFLICT */]: "\u6587\u4EF6\u5939\u540D\u79F0\u51B2\u7A81",
4297
+ ["FOLDER_DEPTH_EXCEEDED" /* FOLDER_DEPTH_EXCEEDED */]: "\u6587\u4EF6\u5939\u5C42\u7EA7\u8FC7\u6DF1",
4298
+ ["STORAGE_PROVIDER_ERROR" /* STORAGE_PROVIDER_ERROR */]: "\u5B58\u50A8\u670D\u52A1\u9519\u8BEF",
4299
+ ["STORAGE_QUOTA_EXCEEDED" /* STORAGE_QUOTA_EXCEEDED */]: "\u5B58\u50A8\u914D\u989D\u5DF2\u6EE1",
4300
+ ["STORAGE_UNAVAILABLE" /* STORAGE_UNAVAILABLE */]: "\u5B58\u50A8\u670D\u52A1\u4E0D\u53EF\u7528",
4301
+ ["SHARE_NOT_FOUND" /* SHARE_NOT_FOUND */]: "\u5206\u4EAB\u4E0D\u5B58\u5728",
4302
+ ["SHARE_EXPIRED" /* SHARE_EXPIRED */]: "\u5206\u4EAB\u5DF2\u8FC7\u671F",
4303
+ ["SHARE_PASSWORD_INCORRECT" /* SHARE_PASSWORD_INCORRECT */]: "\u5206\u4EAB\u5BC6\u7801\u9519\u8BEF",
4304
+ ["SHARE_ACCESS_DENIED" /* SHARE_ACCESS_DENIED */]: "\u5206\u4EAB\u8BBF\u95EE\u88AB\u62D2\u7EDD",
4305
+ ["SHARE_DOWNLOAD_LIMIT_EXCEEDED" /* SHARE_DOWNLOAD_LIMIT_EXCEEDED */]: "\u5206\u4EAB\u4E0B\u8F7D\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650"
4306
+ };
4307
+ var ErrorHttpStatusMap = {
4308
+ ["UNKNOWN_ERROR" /* UNKNOWN_ERROR */]: 500,
4309
+ ["VALIDATION_ERROR" /* VALIDATION_ERROR */]: 400,
4310
+ ["AUTHENTICATION_ERROR" /* AUTHENTICATION_ERROR */]: 401,
4311
+ ["AUTHORIZATION_ERROR" /* AUTHORIZATION_ERROR */]: 403,
4312
+ ["NOT_FOUND" /* NOT_FOUND */]: 404,
4313
+ ["CONFLICT" /* CONFLICT */]: 409,
4314
+ ["RATE_LIMIT_EXCEEDED" /* RATE_LIMIT_EXCEEDED */]: 429,
4315
+ ["FILE_NOT_FOUND" /* FILE_NOT_FOUND */]: 404,
4316
+ ["FILE_TOO_LARGE" /* FILE_TOO_LARGE */]: 413,
4317
+ ["FILE_TYPE_NOT_SUPPORTED" /* FILE_TYPE_NOT_SUPPORTED */]: 415,
4318
+ ["FILE_UPLOAD_FAILED" /* FILE_UPLOAD_FAILED */]: 500,
4319
+ ["FILE_PROCESSING_FAILED" /* FILE_PROCESSING_FAILED */]: 500,
4320
+ ["FILE_ALREADY_EXISTS" /* FILE_ALREADY_EXISTS */]: 409,
4321
+ ["FILE_CORRUPTED" /* FILE_CORRUPTED */]: 422,
4322
+ ["FOLDER_NOT_FOUND" /* FOLDER_NOT_FOUND */]: 404,
4323
+ ["FOLDER_NOT_EMPTY" /* FOLDER_NOT_EMPTY */]: 409,
4324
+ ["FOLDER_NAME_CONFLICT" /* FOLDER_NAME_CONFLICT */]: 409,
4325
+ ["FOLDER_DEPTH_EXCEEDED" /* FOLDER_DEPTH_EXCEEDED */]: 400,
4326
+ ["STORAGE_PROVIDER_ERROR" /* STORAGE_PROVIDER_ERROR */]: 502,
4327
+ ["STORAGE_QUOTA_EXCEEDED" /* STORAGE_QUOTA_EXCEEDED */]: 507,
4328
+ ["STORAGE_UNAVAILABLE" /* STORAGE_UNAVAILABLE */]: 503,
4329
+ ["SHARE_NOT_FOUND" /* SHARE_NOT_FOUND */]: 404,
4330
+ ["SHARE_EXPIRED" /* SHARE_EXPIRED */]: 410,
4331
+ ["SHARE_PASSWORD_INCORRECT" /* SHARE_PASSWORD_INCORRECT */]: 401,
4332
+ ["SHARE_ACCESS_DENIED" /* SHARE_ACCESS_DENIED */]: 403,
4333
+ ["SHARE_DOWNLOAD_LIMIT_EXCEEDED" /* SHARE_DOWNLOAD_LIMIT_EXCEEDED */]: 429
4334
+ };
4335
+
4336
+ // src/universalFile/server/errors/ApiError.ts
4337
+ var ApiError = class _ApiError extends Error {
4338
+ constructor(code, message, details, statusCode) {
4339
+ super(message || ErrorMessages[code]);
4340
+ this.name = "ApiError";
4341
+ this.code = code;
4342
+ this.statusCode = statusCode || ErrorHttpStatusMap[code] || 500;
4343
+ this.details = details;
4344
+ if (Error.captureStackTrace) {
4345
+ Error.captureStackTrace(this, _ApiError);
4346
+ }
4347
+ }
4348
+ /**
4349
+ * 转换为API响应格式
4350
+ */
4351
+ toApiResponse() {
4352
+ return {
4353
+ success: false,
4354
+ error: {
4355
+ code: this.code,
4356
+ message: this.message,
4357
+ details: this.details
4358
+ },
4359
+ meta: {
4360
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4361
+ }
4362
+ };
4363
+ }
4364
+ /**
4365
+ * 转换为NextResponse
4366
+ */
4367
+ toNextResponse() {
4368
+ return server.NextResponse.json(this.toApiResponse(), {
4369
+ status: this.statusCode
4370
+ });
4371
+ }
4372
+ };
4373
+ var logger8 = chunk6PRFP5EG_js.createLogger("DrizzleFileRepository");
4374
+ function createDrizzleRepository(config) {
4375
+ const { db, table, fieldMapping = {} } = config;
4376
+ const getField = (field) => {
4377
+ return fieldMapping[field] || field;
4378
+ };
4379
+ const toDbRecord = (metadata) => {
4380
+ const record = {
4381
+ [getField("id")]: metadata.id,
4382
+ [getField("filename")]: metadata.filename,
4383
+ [getField("originalName")]: metadata.originalName,
4384
+ [getField("mimeType")]: metadata.mimeType,
4385
+ [getField("size")]: metadata.size,
4386
+ [getField("storageType")]: metadata.storageType,
4387
+ [getField("storagePath")]: metadata.storagePath,
4388
+ [getField("url")]: metadata.url,
4389
+ [getField("moduleId")]: metadata.moduleId,
4390
+ [getField("businessId")]: metadata.businessId,
4391
+ [getField("uploadedAt")]: metadata.uploadedAt
4392
+ };
4393
+ if (metadata.hash !== void 0) record[getField("hash")] = metadata.hash;
4394
+ if (metadata.cdnUrl !== void 0) record[getField("cdnUrl")] = metadata.cdnUrl;
4395
+ if (metadata.userId !== void 0) record[getField("userId")] = metadata.userId;
4396
+ if (metadata.expiresAt !== void 0) record[getField("expiresAt")] = metadata.expiresAt;
4397
+ if (metadata.metadata !== void 0) record[getField("metadata")] = metadata.metadata;
4398
+ if (metadata.status !== void 0) record[getField("status")] = metadata.status;
4399
+ if (metadata.processingStatus !== void 0) record[getField("processingStatus")] = metadata.processingStatus;
4400
+ if (metadata.versions !== void 0) record[getField("versions")] = metadata.versions;
4401
+ if (metadata.tags !== void 0) record[getField("tags")] = metadata.tags;
4402
+ return record;
4403
+ };
4404
+ const toFileMetadata = (record) => {
4405
+ const metadata = {
4406
+ id: record[getField("id")],
4407
+ filename: record[getField("filename")],
4408
+ originalName: record[getField("originalName")],
4409
+ mimeType: record[getField("mimeType")],
4410
+ size: record[getField("size")],
4411
+ storageType: record[getField("storageType")],
4412
+ storagePath: record[getField("storagePath")],
4413
+ url: record[getField("url")],
4414
+ moduleId: record[getField("moduleId")],
4415
+ businessId: record[getField("businessId")],
4416
+ uploadedAt: record[getField("uploadedAt")]
4417
+ };
4418
+ if (record[getField("hash")]) metadata.hash = record[getField("hash")];
4419
+ if (record[getField("cdnUrl")]) metadata.cdnUrl = record[getField("cdnUrl")];
4420
+ if (record[getField("userId")]) metadata.userId = record[getField("userId")];
4421
+ if (record[getField("expiresAt")]) metadata.expiresAt = record[getField("expiresAt")];
4422
+ if (record[getField("metadata")]) metadata.metadata = record[getField("metadata")];
4423
+ if (record[getField("status")]) metadata.status = record[getField("status")];
4424
+ if (record[getField("processingStatus")]) metadata.processingStatus = record[getField("processingStatus")];
4425
+ if (record[getField("versions")]) metadata.versions = record[getField("versions")];
4426
+ if (record[getField("tags")]) metadata.tags = record[getField("tags")];
4427
+ return metadata;
4428
+ };
4429
+ return {
4430
+ async save(metadata) {
4431
+ try {
4432
+ const record = toDbRecord(metadata);
4433
+ const existing = await db.select().from(table).where(drizzleOrm.eq(table[getField("id")], metadata.id)).limit(1);
4434
+ if (existing && existing.length > 0) {
4435
+ await db.update(table).set(record).where(drizzleOrm.eq(table[getField("id")], metadata.id));
4436
+ logger8.info(`\u2705 [DrizzleRepository] \u6587\u4EF6\u5143\u6570\u636E\u5DF2\u66F4\u65B0: ${metadata.id}`);
4437
+ } else {
4438
+ await db.insert(table).values(record);
4439
+ logger8.info(`\u2705 [DrizzleRepository] \u6587\u4EF6\u5143\u6570\u636E\u5DF2\u63D2\u5165: ${metadata.id}`);
4440
+ }
4441
+ } catch (error) {
4442
+ logger8.error(`\u274C [DrizzleRepository] \u4FDD\u5B58\u5931\u8D25: ${metadata.id}`, error);
4443
+ throw error;
4444
+ }
4445
+ },
4446
+ async get(fileId) {
4447
+ try {
4448
+ const result = await db.select().from(table).where(drizzleOrm.eq(table[getField("id")], fileId)).limit(1);
4449
+ if (!result || result.length === 0) {
4450
+ return null;
4451
+ }
4452
+ return toFileMetadata(result[0]);
4453
+ } catch (error) {
4454
+ logger8.error(`\u274C [DrizzleRepository] \u67E5\u8BE2\u5931\u8D25: ${fileId}`, error);
4455
+ throw error;
4456
+ }
4457
+ },
4458
+ async query(options) {
4459
+ try {
4460
+ const {
4461
+ page = 1,
4462
+ pageSize = 20,
4463
+ moduleId,
4464
+ businessId,
4465
+ userId,
4466
+ mimeType,
4467
+ status,
4468
+ startDate,
4469
+ endDate,
4470
+ tags
4471
+ } = options;
4472
+ const conditions = [];
4473
+ if (moduleId) {
4474
+ conditions.push(drizzleOrm.eq(table[getField("moduleId")], moduleId));
4475
+ }
4476
+ if (businessId) {
4477
+ conditions.push(drizzleOrm.eq(table[getField("businessId")], businessId));
4478
+ }
4479
+ if (userId) {
4480
+ conditions.push(drizzleOrm.eq(table[getField("userId")], userId));
4481
+ }
4482
+ if (mimeType) {
4483
+ conditions.push(drizzleOrm.eq(table[getField("mimeType")], mimeType));
4484
+ }
4485
+ if (status) {
4486
+ conditions.push(drizzleOrm.eq(table[getField("status")], status));
4487
+ }
4488
+ if (startDate) {
4489
+ conditions.push(drizzleOrm.sql`${table[getField("uploadedAt")]} >= ${startDate}`);
4490
+ }
4491
+ if (endDate) {
4492
+ conditions.push(drizzleOrm.sql`${table[getField("uploadedAt")]} <= ${endDate}`);
4493
+ }
4494
+ if (tags && tags.length > 0) {
4495
+ for (const tag of tags) {
4496
+ conditions.push(drizzleOrm.sql`${table[getField("tags")]} @> ${JSON.stringify([tag])}`);
4497
+ }
4498
+ }
4499
+ const countResult = await db.select({ count: drizzleOrm.sql`count(*)` }).from(table).where(conditions.length > 0 ? drizzleOrm.and(...conditions) : void 0);
4500
+ const total = Number(countResult[0]?.count || 0);
4501
+ const offset = (page - 1) * pageSize;
4502
+ const result = await db.select().from(table).where(conditions.length > 0 ? drizzleOrm.and(...conditions) : void 0).orderBy(drizzleOrm.desc(table[getField("uploadedAt")])).limit(pageSize).offset(offset);
4503
+ const items = result.map(toFileMetadata);
4504
+ return {
4505
+ items,
4506
+ total,
4507
+ page,
4508
+ pageSize,
4509
+ totalPages: Math.ceil(total / pageSize)
4510
+ };
4511
+ } catch (error) {
4512
+ logger8.error(`\u274C [DrizzleRepository] \u67E5\u8BE2\u5217\u8868\u5931\u8D25`, error);
4513
+ throw error;
4514
+ }
4515
+ },
4516
+ async delete(fileId) {
4517
+ try {
4518
+ await db.delete(table).where(drizzleOrm.eq(table[getField("id")], fileId));
4519
+ logger8.info(`\u{1F5D1}\uFE0F [DrizzleRepository] \u6587\u4EF6\u5143\u6570\u636E\u5DF2\u5220\u9664: ${fileId}`);
4520
+ } catch (error) {
4521
+ logger8.error(`\u274C [DrizzleRepository] \u5220\u9664\u5931\u8D25: ${fileId}`, error);
4522
+ throw error;
4523
+ }
4524
+ },
4525
+ async batchDelete(fileIds) {
4526
+ try {
4527
+ if (fileIds.length === 0) return;
4528
+ await db.delete(table).where(drizzleOrm.sql`${table[getField("id")]} = ANY(${fileIds})`);
4529
+ logger8.info(`\u{1F5D1}\uFE0F [DrizzleRepository] \u6279\u91CF\u5220\u9664\u6210\u529F: ${fileIds.length} \u4E2A\u6587\u4EF6`);
4530
+ } catch (error) {
4531
+ logger8.error(`\u274C [DrizzleRepository] \u6279\u91CF\u5220\u9664\u5931\u8D25`, error);
4532
+ throw error;
4533
+ }
4534
+ }
4535
+ };
4536
+ }
4537
+
4538
+ Object.defineProperty(exports, "LocalStorageProvider", {
4539
+ enumerable: true,
4540
+ get: function () { return chunkTKCYPDWU_js.LocalStorageProvider; }
4541
+ });
4542
+ Object.defineProperty(exports, "AliyunOSSProvider", {
4543
+ enumerable: true,
4544
+ get: function () { return chunk3RFBUDRA_js.AliyunOSSProvider; }
4545
+ });
4546
+ exports.AliyunCDNProvider = AliyunCDNProvider;
4547
+ exports.ApiError = ApiError;
4548
+ exports.ApiErrorCode = ApiErrorCode;
4549
+ exports.AudioProcessor = AudioProcessor;
4550
+ exports.CacheManager = CacheManager;
4551
+ exports.CacheStrategyType = CacheStrategyType;
4552
+ exports.CdnCacheStrategy = CdnCacheStrategy2;
4553
+ exports.ConfigValidationError = ConfigValidationError;
4554
+ exports.ErrorHttpStatusMap = ErrorHttpStatusMap;
4555
+ exports.ErrorMessages = ErrorMessages;
4556
+ exports.ImageProcessor = ImageProcessor;
4557
+ exports.PerformanceMonitor = PerformanceMonitor;
4558
+ exports.ProcessingQueue = ProcessingQueue;
4559
+ exports.UniversalFileService = UniversalFileService;
4560
+ exports.VideoProcessor = VideoProcessor;
4561
+ exports.cdnCacheStrategy = cdnCacheStrategy;
4562
+ exports.createAliyunOSSPreset = createAliyunOSSPreset;
4563
+ exports.createDocumentServicePreset = createDocumentServicePreset;
4564
+ exports.createDrizzleRepository = createDrizzleRepository;
4565
+ exports.createFileServiceConfig = createFileServiceConfig;
4566
+ exports.createFileServiceFromEnv = createFileServiceFromEnv;
4567
+ exports.createImageServicePreset = createImageServicePreset;
4568
+ exports.createLocalDevPreset = createLocalDevPreset;
4569
+ exports.createSmartPreset = createSmartPreset;
4570
+ exports.createUniversalFileService = createUniversalFileService;
4571
+ exports.createVideoServicePreset = createVideoServicePreset;
4572
+ exports.getMimeType = getMimeType;
4573
+ exports.getRequiredEnvVars = getRequiredEnvVars;
4574
+ exports.validateEnvironment = validateEnvironment;
4575
+ exports.validateServiceConfig = validateServiceConfig;
4576
+ exports.validateStorageConfig = validateStorageConfig;
4577
+ //# sourceMappingURL=index.js.map
4578
+ //# sourceMappingURL=index.js.map