sa2kit 2.0.0 → 2.0.2

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 (264) hide show
  1. package/README.md +1 -1
  2. package/dist/CollisionBalls-BpHufX3H.d.mts +41 -0
  3. package/dist/CollisionBalls-BpHufX3H.d.ts +41 -0
  4. package/dist/ConfigService-BxK06xP6.d.mts +262 -0
  5. package/dist/ConfigService-BxK06xP6.d.ts +262 -0
  6. package/dist/UniversalFileService-BpvbZitV.d.mts +139 -0
  7. package/dist/UniversalFileService-GsP6D3Rc.d.ts +139 -0
  8. package/dist/audioDetection/index.d.mts +449 -0
  9. package/dist/audioDetection/index.d.ts +449 -0
  10. package/dist/audioDetection/index.js +1244 -0
  11. package/dist/audioDetection/index.js.map +1 -0
  12. package/dist/audioDetection/index.mjs +1227 -0
  13. package/dist/audioDetection/index.mjs.map +1 -0
  14. package/dist/auth/legacy/core/index.d.mts +42 -0
  15. package/dist/auth/legacy/core/index.d.ts +42 -0
  16. package/dist/auth/legacy/core/index.js +242 -0
  17. package/dist/auth/legacy/core/index.js.map +1 -0
  18. package/dist/auth/legacy/core/index.mjs +226 -0
  19. package/dist/auth/legacy/core/index.mjs.map +1 -0
  20. package/dist/auth/legacy/db/index.d.mts +5 -0
  21. package/dist/auth/legacy/db/index.d.ts +5 -0
  22. package/dist/auth/legacy/db/index.js +261 -0
  23. package/dist/auth/legacy/db/index.js.map +1 -0
  24. package/dist/auth/legacy/db/index.mjs +250 -0
  25. package/dist/auth/legacy/db/index.mjs.map +1 -0
  26. package/dist/auth/legacy/index.d.mts +5 -0
  27. package/dist/auth/legacy/index.d.ts +5 -0
  28. package/dist/auth/legacy/index.js +1107 -0
  29. package/dist/auth/legacy/index.js.map +1 -0
  30. package/dist/auth/legacy/index.mjs +1086 -0
  31. package/dist/auth/legacy/index.mjs.map +1 -0
  32. package/dist/auth/legacy/logic/index.d.mts +9 -0
  33. package/dist/auth/legacy/logic/index.d.ts +9 -0
  34. package/dist/auth/legacy/logic/index.js +194 -0
  35. package/dist/auth/legacy/logic/index.js.map +1 -0
  36. package/dist/auth/legacy/logic/index.mjs +187 -0
  37. package/dist/auth/legacy/logic/index.mjs.map +1 -0
  38. package/dist/auth/legacy/miniapp/index.d.mts +5 -0
  39. package/dist/auth/legacy/miniapp/index.d.ts +5 -0
  40. package/dist/auth/legacy/miniapp/index.js +506 -0
  41. package/dist/auth/legacy/miniapp/index.js.map +1 -0
  42. package/dist/auth/legacy/miniapp/index.mjs +487 -0
  43. package/dist/auth/legacy/miniapp/index.mjs.map +1 -0
  44. package/dist/auth/legacy/routes/index.d.mts +53 -0
  45. package/dist/auth/legacy/routes/index.d.ts +53 -0
  46. package/dist/auth/legacy/routes/index.js +278 -0
  47. package/dist/auth/legacy/routes/index.js.map +1 -0
  48. package/dist/auth/legacy/routes/index.mjs +271 -0
  49. package/dist/auth/legacy/routes/index.mjs.map +1 -0
  50. package/dist/auth/legacy/schema/index.d.mts +401 -0
  51. package/dist/auth/legacy/schema/index.d.ts +401 -0
  52. package/dist/auth/legacy/schema/index.js +50 -0
  53. package/dist/auth/legacy/schema/index.js.map +1 -0
  54. package/dist/auth/legacy/schema/index.mjs +44 -0
  55. package/dist/auth/legacy/schema/index.mjs.map +1 -0
  56. package/dist/auth/legacy/server/index.d.mts +13 -0
  57. package/dist/auth/legacy/server/index.d.ts +13 -0
  58. package/dist/auth/legacy/server/index.js +21 -0
  59. package/dist/auth/legacy/server/index.js.map +1 -0
  60. package/dist/auth/legacy/server/index.mjs +19 -0
  61. package/dist/auth/legacy/server/index.mjs.map +1 -0
  62. package/dist/auth/legacy/services/index.d.mts +40 -0
  63. package/dist/auth/legacy/services/index.d.ts +40 -0
  64. package/dist/auth/legacy/services/index.js +258 -0
  65. package/dist/auth/legacy/services/index.js.map +1 -0
  66. package/dist/auth/legacy/services/index.mjs +252 -0
  67. package/dist/auth/legacy/services/index.mjs.map +1 -0
  68. package/dist/auth/legacy/ui/miniapp/index.d.mts +10 -0
  69. package/dist/auth/legacy/ui/miniapp/index.d.ts +10 -0
  70. package/dist/auth/legacy/ui/miniapp/index.js +298 -0
  71. package/dist/auth/legacy/ui/miniapp/index.js.map +1 -0
  72. package/dist/auth/legacy/ui/miniapp/index.mjs +290 -0
  73. package/dist/auth/legacy/ui/miniapp/index.mjs.map +1 -0
  74. package/dist/auth/legacy/ui/web/index.d.mts +22 -0
  75. package/dist/auth/legacy/ui/web/index.d.ts +22 -0
  76. package/dist/auth/legacy/ui/web/index.js +899 -0
  77. package/dist/auth/legacy/ui/web/index.js.map +1 -0
  78. package/dist/auth/legacy/ui/web/index.mjs +889 -0
  79. package/dist/auth/legacy/ui/web/index.mjs.map +1 -0
  80. package/dist/auth/legacy/web/index.d.mts +5 -0
  81. package/dist/auth/legacy/web/index.d.ts +5 -0
  82. package/dist/auth/legacy/web/index.js +1107 -0
  83. package/dist/auth/legacy/web/index.js.map +1 -0
  84. package/dist/auth/legacy/web/index.mjs +1086 -0
  85. package/dist/auth/legacy/web/index.mjs.map +1 -0
  86. package/dist/auth/rn/index.d.mts +64 -0
  87. package/dist/auth/rn/index.d.ts +64 -0
  88. package/dist/auth/rn/index.js +765 -0
  89. package/dist/auth/rn/index.js.map +1 -0
  90. package/dist/auth/rn/index.mjs +754 -0
  91. package/dist/auth/rn/index.mjs.map +1 -0
  92. package/dist/base-api-client-ACKKt13v.d.mts +277 -0
  93. package/dist/base-api-client-ACKKt13v.d.ts +277 -0
  94. package/dist/boothVaultService-Cn4WPhjg.d.mts +83 -0
  95. package/dist/boothVaultService-Cn4WPhjg.d.ts +83 -0
  96. package/dist/business/index.d.mts +6 -0
  97. package/dist/business/index.d.ts +6 -0
  98. package/dist/business/index.js +1682 -0
  99. package/dist/business/index.js.map +1 -0
  100. package/dist/business/index.mjs +1675 -0
  101. package/dist/business/index.mjs.map +1 -0
  102. package/dist/calendar/index.d.mts +1325 -0
  103. package/dist/calendar/index.d.ts +1325 -0
  104. package/dist/calendar/index.js +5964 -0
  105. package/dist/calendar/index.js.map +1 -0
  106. package/dist/calendar/index.mjs +5878 -0
  107. package/dist/calendar/index.mjs.map +1 -0
  108. package/dist/components/index.d.mts +405 -0
  109. package/dist/components/index.d.ts +405 -0
  110. package/dist/components/index.js +2516 -0
  111. package/dist/components/index.js.map +1 -0
  112. package/dist/components/index.mjs +2396 -0
  113. package/dist/components/index.mjs.map +1 -0
  114. package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
  115. package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
  116. package/dist/festivalCard/index.d.mts +75 -0
  117. package/dist/festivalCard/index.d.ts +75 -0
  118. package/dist/festivalCard/index.js +1492 -0
  119. package/dist/festivalCard/index.js.map +1 -0
  120. package/dist/festivalCard/index.mjs +1475 -0
  121. package/dist/festivalCard/index.mjs.map +1 -0
  122. package/dist/festivalCard/server/index.d.mts +120 -0
  123. package/dist/festivalCard/server/index.d.ts +120 -0
  124. package/dist/festivalCard/server/index.js +272 -0
  125. package/dist/festivalCard/server/index.js.map +1 -0
  126. package/dist/festivalCard/server/index.mjs +265 -0
  127. package/dist/festivalCard/server/index.mjs.map +1 -0
  128. package/dist/festivalCardService-CZomuQ4E.d.mts +80 -0
  129. package/dist/festivalCardService-CZomuQ4E.d.ts +80 -0
  130. package/dist/index-1Ag7IBXN.d.ts +144 -0
  131. package/dist/index-DNKZ7-R_.d.mts +184 -0
  132. package/dist/index-DNKZ7-R_.d.ts +184 -0
  133. package/dist/index-DSel44Ke.d.mts +93 -0
  134. package/dist/index-DSel44Ke.d.ts +93 -0
  135. package/dist/index-DdeZSeTJ.d.mts +144 -0
  136. package/dist/index-DrPcMJPc.d.mts +250 -0
  137. package/dist/index-DrPcMJPc.d.ts +250 -0
  138. package/dist/index.d.mts +5333 -0
  139. package/dist/index.d.ts +5333 -0
  140. package/dist/index.js +18809 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/index.mjs +18533 -0
  143. package/dist/index.mjs.map +1 -0
  144. package/dist/mikuContest/ui/web/index.d.mts +2 -0
  145. package/dist/mikuContest/ui/web/index.d.ts +2 -0
  146. package/dist/mikuContest/ui/web/index.js +353 -0
  147. package/dist/mikuContest/ui/web/index.js.map +1 -0
  148. package/dist/mikuContest/ui/web/index.mjs +343 -0
  149. package/dist/mikuContest/ui/web/index.mjs.map +1 -0
  150. package/dist/mikuFireworks3D/index.d.mts +268 -0
  151. package/dist/mikuFireworks3D/index.d.ts +268 -0
  152. package/dist/mikuFireworks3D/index.js +1267 -0
  153. package/dist/mikuFireworks3D/index.js.map +1 -0
  154. package/dist/mikuFireworks3D/index.mjs +1228 -0
  155. package/dist/mikuFireworks3D/index.mjs.map +1 -0
  156. package/dist/mikuFusionGame/index.d.mts +117 -0
  157. package/dist/mikuFusionGame/index.d.ts +117 -0
  158. package/dist/mikuFusionGame/index.js +1208 -0
  159. package/dist/mikuFusionGame/index.js.map +1 -0
  160. package/dist/mikuFusionGame/index.mjs +1195 -0
  161. package/dist/mikuFusionGame/index.mjs.map +1 -0
  162. package/dist/mmd/admin/index.d.mts +487 -0
  163. package/dist/mmd/admin/index.d.ts +487 -0
  164. package/dist/mmd/admin/index.js +1058 -0
  165. package/dist/mmd/admin/index.js.map +1 -0
  166. package/dist/mmd/admin/index.mjs +1027 -0
  167. package/dist/mmd/admin/index.mjs.map +1 -0
  168. package/dist/mmd/index.d.mts +2467 -0
  169. package/dist/mmd/index.d.ts +2467 -0
  170. package/dist/mmd/index.js +10119 -0
  171. package/dist/mmd/index.js.map +1 -0
  172. package/dist/mmd/index.mjs +10028 -0
  173. package/dist/mmd/index.mjs.map +1 -0
  174. package/dist/mmd/server/index.d.mts +139 -0
  175. package/dist/mmd/server/index.d.ts +139 -0
  176. package/dist/mmd/server/index.js +424 -0
  177. package/dist/mmd/server/index.js.map +1 -0
  178. package/dist/mmd/server/index.mjs +404 -0
  179. package/dist/mmd/server/index.mjs.map +1 -0
  180. package/dist/music/index.d.mts +74 -0
  181. package/dist/music/index.d.ts +74 -0
  182. package/dist/music/index.js +830 -0
  183. package/dist/music/index.js.map +1 -0
  184. package/dist/music/index.mjs +809 -0
  185. package/dist/music/index.mjs.map +1 -0
  186. package/dist/music/server/index.d.mts +1 -0
  187. package/dist/music/server/index.d.ts +1 -0
  188. package/dist/music/server/index.js +194 -0
  189. package/dist/music/server/index.js.map +1 -0
  190. package/dist/music/server/index.mjs +182 -0
  191. package/dist/music/server/index.mjs.map +1 -0
  192. package/dist/navigation/index.d.mts +93 -0
  193. package/dist/navigation/index.d.ts +93 -0
  194. package/dist/navigation/index.js +453 -0
  195. package/dist/navigation/index.js.map +1 -0
  196. package/dist/navigation/index.mjs +443 -0
  197. package/dist/navigation/index.mjs.map +1 -0
  198. package/dist/portfolio/index.d.mts +66 -0
  199. package/dist/portfolio/index.d.ts +66 -0
  200. package/dist/portfolio/index.js +736 -0
  201. package/dist/portfolio/index.js.map +1 -0
  202. package/dist/portfolio/index.mjs +724 -0
  203. package/dist/portfolio/index.mjs.map +1 -0
  204. package/dist/qqbot/server/index.d.mts +216 -0
  205. package/dist/qqbot/server/index.d.ts +216 -0
  206. package/dist/qqbot/server/index.js +394 -0
  207. package/dist/qqbot/server/index.js.map +1 -0
  208. package/dist/qqbot/server/index.mjs +385 -0
  209. package/dist/qqbot/server/index.mjs.map +1 -0
  210. package/dist/qqbot/ui/web/index.d.mts +10 -0
  211. package/dist/qqbot/ui/web/index.d.ts +10 -0
  212. package/dist/qqbot/ui/web/index.js +105 -0
  213. package/dist/qqbot/ui/web/index.js.map +1 -0
  214. package/dist/qqbot/ui/web/index.mjs +99 -0
  215. package/dist/qqbot/ui/web/index.mjs.map +1 -0
  216. package/dist/screenReceiver/index.d.mts +86 -0
  217. package/dist/screenReceiver/index.d.ts +86 -0
  218. package/dist/screenReceiver/index.js +281 -0
  219. package/dist/screenReceiver/index.js.map +1 -0
  220. package/dist/screenReceiver/index.mjs +273 -0
  221. package/dist/screenReceiver/index.mjs.map +1 -0
  222. package/dist/testYourself/admin/index.d.mts +58 -0
  223. package/dist/testYourself/admin/index.d.ts +58 -0
  224. package/dist/testYourself/admin/index.js +1009 -0
  225. package/dist/testYourself/admin/index.js.map +1 -0
  226. package/dist/testYourself/admin/index.mjs +1002 -0
  227. package/dist/testYourself/admin/index.mjs.map +1 -0
  228. package/dist/testYourself/index.d.mts +53 -0
  229. package/dist/testYourself/index.d.ts +53 -0
  230. package/dist/testYourself/index.js +2551 -0
  231. package/dist/testYourself/index.js.map +1 -0
  232. package/dist/testYourself/index.mjs +2531 -0
  233. package/dist/testYourself/index.mjs.map +1 -0
  234. package/dist/testYourself/server/index.d.mts +1029 -0
  235. package/dist/testYourself/server/index.d.ts +1029 -0
  236. package/dist/testYourself/server/index.js +825 -0
  237. package/dist/testYourself/server/index.js.map +1 -0
  238. package/dist/testYourself/server/index.mjs +816 -0
  239. package/dist/testYourself/server/index.mjs.map +1 -0
  240. package/dist/types-BTiaMsBz.d.mts +292 -0
  241. package/dist/types-DyG3ZV9V.d.mts +270 -0
  242. package/dist/types-DyG3ZV9V.d.ts +270 -0
  243. package/dist/types-ERmJyjx8.d.ts +292 -0
  244. package/dist/types-HorDyIRv.d.mts +303 -0
  245. package/dist/types-HorDyIRv.d.ts +303 -0
  246. package/dist/vocaloidBooth/index.d.mts +64 -0
  247. package/dist/vocaloidBooth/index.d.ts +64 -0
  248. package/dist/vocaloidBooth/index.js +376 -0
  249. package/dist/vocaloidBooth/index.js.map +1 -0
  250. package/dist/vocaloidBooth/index.mjs +362 -0
  251. package/dist/vocaloidBooth/index.mjs.map +1 -0
  252. package/dist/vocaloidBooth/server/index.d.mts +111 -0
  253. package/dist/vocaloidBooth/server/index.d.ts +111 -0
  254. package/dist/vocaloidBooth/server/index.js +247 -0
  255. package/dist/vocaloidBooth/server/index.js.map +1 -0
  256. package/dist/vocaloidBooth/server/index.mjs +237 -0
  257. package/dist/vocaloidBooth/server/index.mjs.map +1 -0
  258. package/dist/vocaloidBooth/web/index.d.mts +3 -0
  259. package/dist/vocaloidBooth/web/index.d.ts +3 -0
  260. package/dist/vocaloidBooth/web/index.js +376 -0
  261. package/dist/vocaloidBooth/web/index.js.map +1 -0
  262. package/dist/vocaloidBooth/web/index.mjs +362 -0
  263. package/dist/vocaloidBooth/web/index.mjs.map +1 -0
  264. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/vocaloidBooth/server/inMemoryBoothVaultStore.ts","../../../src/vocaloidBooth/server/adapters.ts","../../../src/vocaloidBooth/server/cleanup.ts","../../../src/vocaloidBooth/server/security.ts","../../../src/vocaloidBooth/server/ossIntegration.ts","../../../src/vocaloidBooth/server/audit.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { BoothUploadRecord, BoothVaultStore } from '../types';\n\nexport class InMemoryBoothVaultStore implements BoothVaultStore {\n private readonly recordsById = new Map<string, BoothUploadRecord>();\n private readonly idByCode = new Map<string, string>();\n\n async saveRecord(record: BoothUploadRecord): Promise<void> {\n this.recordsById.set(record.id, record);\n this.idByCode.set(record.matchCode, record.id);\n }\n\n async findByMatchCode(matchCode: string): Promise<BoothUploadRecord | null> {\n const id = this.idByCode.get(matchCode);\n if (!id) {\n return null;\n }\n return this.recordsById.get(id) ?? null;\n }\n\n async findByRecordId(recordId: string): Promise<BoothUploadRecord | null> {\n return this.recordsById.get(recordId) ?? null;\n }\n\n async incrementDownloadCount(recordId: string): Promise<void> {\n const record = this.recordsById.get(recordId);\n if (!record) {\n return;\n }\n\n this.recordsById.set(recordId, {\n ...record,\n downloadCount: record.downloadCount + 1,\n });\n }\n\n async existsByMatchCode(matchCode: string): Promise<boolean> {\n return this.idByCode.has(matchCode);\n }\n\n async listActiveRecords(): Promise<BoothUploadRecord[]> {\n return Array.from(this.recordsById.values()).filter((record) => record.status === 'active');\n }\n\n async updateStatus(recordId: string, status: BoothUploadRecord['status']): Promise<void> {\n const record = this.recordsById.get(recordId);\n if (!record) return;\n\n this.recordsById.set(recordId, {\n ...record,\n status,\n });\n }\n}\n","import type { BoothFileItem, BoothUploadRecord, BoothVaultStore } from '../types';\n\nexport interface BoothVaultRecordRepository {\n create(record: BoothUploadRecord): Promise<void>;\n findByMatchCode(matchCode: string): Promise<BoothUploadRecord | null>;\n findByRecordId?(recordId: string): Promise<BoothUploadRecord | null>;\n updateDownloadCount(recordId: string, downloadCount: number): Promise<void>;\n existsByMatchCode(matchCode: string): Promise<boolean>;\n}\n\nexport interface BoothObjectStorageProvider {\n save(file: File | Blob | Buffer, objectKey: string, contentType?: string): Promise<string>;\n getSignedDownloadUrl(objectKey: string, expiresInSeconds?: number): Promise<string>;\n delete(objectKey: string): Promise<void>;\n}\n\nexport class RepositoryBoothVaultStore implements BoothVaultStore {\n constructor(private readonly repository: BoothVaultRecordRepository) {}\n\n saveRecord(record: BoothUploadRecord): Promise<void> {\n return this.repository.create(record);\n }\n\n findByMatchCode(matchCode: string): Promise<BoothUploadRecord | null> {\n return this.repository.findByMatchCode(matchCode);\n }\n\n async incrementDownloadCount(recordId: string): Promise<void> {\n const record = await this.findByRecordId(recordId);\n if (!record) return;\n return this.repository.updateDownloadCount(recordId, record.downloadCount + 1);\n }\n\n existsByMatchCode(matchCode: string): Promise<boolean> {\n return this.repository.existsByMatchCode(matchCode);\n }\n\n async findByRecordId(recordId: string): Promise<BoothUploadRecord | null> {\n if (this.repository.findByRecordId) {\n return this.repository.findByRecordId(recordId);\n }\n return null;\n }\n}\n\nexport interface BoothSignedFile extends BoothFileItem {\n signedDownloadUrl: string;\n}\n\nexport const signRecordFiles = async (\n record: BoothUploadRecord,\n storage: BoothObjectStorageProvider,\n expiresInSeconds = 60 * 30\n): Promise<BoothSignedFile[]> => {\n const signed = await Promise.all(\n record.files.map(async (file) => ({\n ...file,\n signedDownloadUrl: await storage.getSignedDownloadUrl(file.objectKey, expiresInSeconds),\n }))\n );\n\n return signed;\n};\n","import type { BoothUploadRecord, BoothVaultStore } from '../types';\n\nexport interface BoothExpiryStore extends BoothVaultStore {\n listActiveRecords?(): Promise<BoothUploadRecord[]>;\n updateStatus?(recordId: string, status: BoothUploadRecord['status']): Promise<void>;\n}\n\nexport interface ExpireResult {\n scanned: number;\n expired: number;\n}\n\nexport const expireBoothRecords = async (\n store: BoothExpiryStore,\n now = Date.now(),\n onExpired?: (record: BoothUploadRecord) => void\n): Promise<ExpireResult> => {\n const records = store.listActiveRecords ? await store.listActiveRecords() : [];\n\n let expired = 0;\n for (const record of records) {\n if (record.status !== 'active') continue;\n if (new Date(record.expiresAt).getTime() <= now) {\n expired += 1;\n if (store.updateStatus) {\n // eslint-disable-next-line no-await-in-loop\n await store.updateStatus(record.id, 'expired');\n }\n onExpired?.(record);\n }\n }\n\n return {\n scanned: records.length,\n expired,\n };\n};\n","export interface BoothRedeemGuardOptions {\n maxAttempts?: number;\n windowMs?: number;\n blockMs?: number;\n}\n\ninterface AttemptState {\n attempts: number[];\n blockedUntil?: number;\n}\n\nexport class BoothRedeemGuard {\n private readonly maxAttempts: number;\n private readonly windowMs: number;\n private readonly blockMs: number;\n private readonly state = new Map<string, AttemptState>();\n\n constructor(options: BoothRedeemGuardOptions = {}) {\n this.maxAttempts = options.maxAttempts ?? 8;\n this.windowMs = options.windowMs ?? 60_000;\n this.blockMs = options.blockMs ?? 5 * 60_000;\n }\n\n assertAllowed(subjectKey: string, now = Date.now()): void {\n const state = this.getState(subjectKey, now);\n if (state.blockedUntil && state.blockedUntil > now) {\n const seconds = Math.ceil((state.blockedUntil - now) / 1000);\n throw new Error(`Too many attempts, retry in ${seconds}s`);\n }\n }\n\n registerAttempt(subjectKey: string, success: boolean, now = Date.now()): void {\n const state = this.getState(subjectKey, now);\n\n if (success) {\n this.state.delete(subjectKey);\n return;\n }\n\n state.attempts.push(now);\n state.attempts = state.attempts.filter((t) => now - t <= this.windowMs);\n\n if (state.attempts.length >= this.maxAttempts) {\n state.blockedUntil = now + this.blockMs;\n state.attempts = [];\n }\n\n this.state.set(subjectKey, state);\n }\n\n private getState(subjectKey: string, now: number): AttemptState {\n const state = this.state.get(subjectKey) ?? { attempts: [] };\n state.attempts = state.attempts.filter((t) => now - t <= this.windowMs);\n\n if (state.blockedUntil && state.blockedUntil <= now) {\n delete state.blockedUntil;\n }\n\n return state;\n }\n}\n\nexport interface ValidateUploadFilesOptions {\n maxFiles?: number;\n maxSingleFileSizeBytes?: number;\n maxTotalSizeBytes?: number;\n allowedExtensions?: string[];\n}\n\nexport interface UploadLikeFile {\n fileName: string;\n size: number;\n}\n\nconst ext = (name: string): string => {\n const i = name.lastIndexOf('.');\n return i >= 0 ? name.slice(i + 1).toLowerCase() : '';\n};\n\nexport const validateUploadFiles = (\n files: UploadLikeFile[],\n options: ValidateUploadFilesOptions = {}\n): void => {\n const maxFiles = options.maxFiles ?? 20;\n const maxSingle = options.maxSingleFileSizeBytes ?? 2 * 1024 * 1024 * 1024;\n const maxTotal = options.maxTotalSizeBytes ?? 5 * 1024 * 1024 * 1024;\n const allowed = options.allowedExtensions?.map((e) => e.toLowerCase());\n\n if (files.length === 0) {\n throw new Error('No files uploaded');\n }\n if (files.length > maxFiles) {\n throw new Error(`Too many files (max ${maxFiles})`);\n }\n\n const total = files.reduce((sum, f) => sum + f.size, 0);\n if (total > maxTotal) {\n throw new Error('Total upload size exceeded');\n }\n\n for (const file of files) {\n if (file.size > maxSingle) {\n throw new Error(`File too large: ${file.fileName}`);\n }\n if (allowed && allowed.length > 0 && !allowed.includes(ext(file.fileName))) {\n throw new Error(`File extension not allowed: ${file.fileName}`);\n }\n }\n};\n","import type { UniversalFileService } from '../../universalFile/server';\nimport type { AccessPermission, UploadProgress } from '../../universalFile/types';\nimport type { BoothFileKind, CreateBoothUploadInput, CreateBoothUploadResult } from '../types';\nimport { BoothVaultService } from '../core';\nimport { validateUploadFiles } from './security';\n\nexport interface BoothIncomingFile {\n file: File;\n kind?: BoothFileKind;\n}\n\nexport interface CreateBoothUploadWithOSSInput {\n boothId: string;\n files: BoothIncomingFile[];\n metadata?: CreateBoothUploadInput['metadata'];\n ttlHours?: number;\n moduleId?: string;\n businessId?: string;\n permission?: AccessPermission;\n onProgress?: (fileName: string, progress: UploadProgress) => void;\n}\n\nexport const uploadToOSSAndCreateBoothRecord = async (\n params: CreateBoothUploadWithOSSInput,\n deps: {\n fileService: UniversalFileService;\n vaultService: BoothVaultService;\n }\n): Promise<CreateBoothUploadResult> => {\n validateUploadFiles(\n params.files.map((item) => ({ fileName: item.file.name, size: item.file.size })),\n {\n maxFiles: 20,\n maxSingleFileSizeBytes: 2 * 1024 * 1024 * 1024,\n maxTotalSizeBytes: 5 * 1024 * 1024 * 1024,\n }\n );\n\n const moduleId = params.moduleId ?? 'vocaloid-booth';\n const businessId = params.businessId ?? params.boothId;\n const permission = params.permission ?? 'private';\n\n const uploaded = await Promise.all(\n params.files.map(async (item) => {\n const metadata = await deps.fileService.uploadFile(\n {\n file: item.file,\n moduleId,\n businessId,\n permission,\n metadata: {\n boothId: params.boothId,\n kind: item.kind ?? 'other',\n },\n },\n undefined,\n (progress) => params.onProgress?.(item.file.name, progress)\n );\n\n return {\n fileName: metadata.originalName,\n size: metadata.size,\n mimeType: metadata.mimeType,\n checksum: metadata.hash,\n objectKey: metadata.storagePath,\n kind: item.kind ?? 'other',\n };\n })\n );\n\n return deps.vaultService.createUpload({\n boothId: params.boothId,\n ttlHours: params.ttlHours,\n metadata: params.metadata,\n files: uploaded,\n });\n};\n","import type { BoothAuditEvent } from '../types';\n\nexport interface BoothAuditSink {\n log(event: BoothAuditEvent): Promise<void> | void;\n}\n\nexport class InMemoryBoothAuditSink implements BoothAuditSink {\n private readonly events: BoothAuditEvent[] = [];\n\n log(event: BoothAuditEvent): void {\n this.events.push(event);\n }\n\n list(): BoothAuditEvent[] {\n return [...this.events];\n }\n}\n\nexport const createAuditLogger = (sink: BoothAuditSink) => {\n return (event: BoothAuditEvent): void => {\n sink.log(event);\n };\n};\n"]}
@@ -0,0 +1,3 @@
1
+ export { f as BoothAuditEvent, g as BoothAuditEventType, b as BoothFileItem, c as BoothFileKind, h as BoothRedeemGuardLike, a as BoothUploadRecord, d as BoothVaultService, i as BoothVaultServiceOptions, B as BoothVaultStore, C as CreateBoothUploadInput, e as CreateBoothUploadResult } from '../../boothVaultService-Cn4WPhjg.mjs';
2
+ export { BoothConfigPage, BoothConfigPageProps, BoothRedeemPanel, BoothRedeemPanelProps, BoothSuccessCard, BoothSuccessCardProps, BoothUploadPanel, BoothUploadPanelProps, BoothUploadSubmitPayload, GenerateMatchCodeOptions, VocaloidBoothConfig, defaultVocaloidBoothConfig, generateMatchCode, normalizeMatchCode, normalizeVocaloidBoothConfig } from '../index.mjs';
3
+ import 'react';
@@ -0,0 +1,3 @@
1
+ export { f as BoothAuditEvent, g as BoothAuditEventType, b as BoothFileItem, c as BoothFileKind, h as BoothRedeemGuardLike, a as BoothUploadRecord, d as BoothVaultService, i as BoothVaultServiceOptions, B as BoothVaultStore, C as CreateBoothUploadInput, e as CreateBoothUploadResult } from '../../boothVaultService-Cn4WPhjg.js';
2
+ export { BoothConfigPage, BoothConfigPageProps, BoothRedeemPanel, BoothRedeemPanelProps, BoothSuccessCard, BoothSuccessCardProps, BoothUploadPanel, BoothUploadPanelProps, BoothUploadSubmitPayload, GenerateMatchCodeOptions, VocaloidBoothConfig, defaultVocaloidBoothConfig, generateMatchCode, normalizeMatchCode, normalizeVocaloidBoothConfig } from '../index.js';
3
+ import 'react';
@@ -0,0 +1,376 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+ var React = require('react');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var React__default = /*#__PURE__*/_interopDefault(React);
9
+
10
+ // src/vocaloidBooth/core/code.ts
11
+ var AMBIGUOUS = /* @__PURE__ */ new Set(["0", "1", "I", "O", "L"]);
12
+ var ALPHABET = "ABCDEFGHJKMNPQRSTUVWXYZ23456789".split("").filter((c) => !AMBIGUOUS.has(c));
13
+ var normalizeMatchCode = (value) => value.trim().toUpperCase();
14
+ var generateMatchCode = async ({
15
+ length = 6,
16
+ maxAttempts = 20,
17
+ exists
18
+ }) => {
19
+ if (length < 4) {
20
+ throw new Error("Match code length must be at least 4");
21
+ }
22
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
23
+ const code = Array.from({ length }).map(() => ALPHABET[Math.floor(Math.random() * ALPHABET.length)]).join("");
24
+ if (!await exists(code)) {
25
+ return code;
26
+ }
27
+ }
28
+ throw new Error("Unable to generate unique match code");
29
+ };
30
+ var BoothVaultService = class {
31
+ emitAudit(event) {
32
+ this.onAuditEvent?.({
33
+ ...event,
34
+ at: (/* @__PURE__ */ new Date()).toISOString()
35
+ });
36
+ }
37
+ constructor(options) {
38
+ this.store = options.store;
39
+ this.codeLength = options.codeLength ?? 6;
40
+ this.defaultTtlHours = options.defaultTtlHours ?? 24 * 14;
41
+ this.baseDownloadPath = options.baseDownloadPath ?? "/redeem";
42
+ this.redeemGuard = options.redeemGuard;
43
+ this.onAuditEvent = options.onAuditEvent;
44
+ }
45
+ async createUpload(input) {
46
+ if (!input.files?.length) {
47
+ throw new Error("At least one file is required");
48
+ }
49
+ const now = /* @__PURE__ */ new Date();
50
+ const ttlHours = Math.max(1, input.ttlHours ?? this.defaultTtlHours);
51
+ const expiresAt = new Date(now.getTime() + ttlHours * 60 * 60 * 1e3);
52
+ const matchCode = await generateMatchCode({
53
+ length: this.codeLength,
54
+ exists: (code) => this.store.existsByMatchCode(code)
55
+ });
56
+ const record = {
57
+ id: crypto.randomUUID(),
58
+ boothId: input.boothId,
59
+ matchCode,
60
+ createdAt: now.toISOString(),
61
+ expiresAt: expiresAt.toISOString(),
62
+ files: input.files.map((file) => ({
63
+ ...file,
64
+ id: crypto.randomUUID()
65
+ })),
66
+ metadata: input.metadata,
67
+ status: "active",
68
+ downloadCount: 0
69
+ };
70
+ await this.store.saveRecord(record);
71
+ this.emitAudit({
72
+ type: "upload.created",
73
+ boothId: record.boothId,
74
+ recordId: record.id,
75
+ matchCode: record.matchCode,
76
+ detail: { fileCount: record.files.length }
77
+ });
78
+ return {
79
+ record,
80
+ downloadUrlPath: `${this.baseDownloadPath}?code=${record.matchCode}`
81
+ };
82
+ }
83
+ async getByMatchCode(matchCode) {
84
+ const normalized = normalizeMatchCode(matchCode);
85
+ const record = await this.store.findByMatchCode(normalized);
86
+ if (!record) {
87
+ return null;
88
+ }
89
+ if (new Date(record.expiresAt).getTime() <= Date.now() && record.status === "active") {
90
+ return {
91
+ ...record,
92
+ status: "expired"
93
+ };
94
+ }
95
+ return record;
96
+ }
97
+ async markDownloaded(recordId) {
98
+ await this.store.incrementDownloadCount(recordId);
99
+ }
100
+ async resolveDownloadFilesByCode(matchCode, options) {
101
+ const requesterKey = options?.requesterKey;
102
+ if (requesterKey && this.redeemGuard) {
103
+ try {
104
+ this.redeemGuard.assertAllowed(requesterKey);
105
+ } catch (error) {
106
+ this.emitAudit({
107
+ type: "redeem.blocked",
108
+ requesterKey,
109
+ matchCode,
110
+ detail: { message: error instanceof Error ? error.message : "blocked" }
111
+ });
112
+ throw error;
113
+ }
114
+ }
115
+ const record = await this.getByMatchCode(matchCode);
116
+ const success = !!record && record.status === "active";
117
+ if (requesterKey && this.redeemGuard) {
118
+ this.redeemGuard.registerAttempt(requesterKey, success);
119
+ }
120
+ if (!success) {
121
+ this.emitAudit({
122
+ type: "redeem.failed",
123
+ requesterKey,
124
+ matchCode,
125
+ boothId: record?.boothId,
126
+ recordId: record?.id
127
+ });
128
+ return record;
129
+ }
130
+ await this.markDownloaded(record.id);
131
+ const reloaded = this.store.findByRecordId ? await this.store.findByRecordId(record.id) : await this.getByMatchCode(record.matchCode);
132
+ this.emitAudit({
133
+ type: "redeem.success",
134
+ requesterKey,
135
+ matchCode: record.matchCode,
136
+ boothId: record.boothId,
137
+ recordId: record.id
138
+ });
139
+ return reloaded ?? record;
140
+ }
141
+ };
142
+
143
+ // src/vocaloidBooth/core/config.ts
144
+ var defaultVocaloidBoothConfig = {
145
+ boothId: "default-booth",
146
+ title: "MMD / Vocaloid \u521B\u4F5C\u6587\u4EF6\u5BC4\u5B58\u7AD9",
147
+ description: "\u4E0A\u4F20\u521B\u4F5C\u6587\u4EF6\u5E76\u751F\u6210\u5339\u914D\u7801\uFF0C\u540E\u7EED\u53EF\u51ED\u7801\u4E0B\u8F7D",
148
+ defaultTtlHours: 24 * 14,
149
+ maxFiles: 20,
150
+ maxSingleFileSizeMb: 2048,
151
+ maxTotalFileSizeMb: 5120,
152
+ allowedExtensions: ["zip", "7z", "rar", "vsqx", "vpr", "vmd", "pmx", "wav", "mp3", "mp4"]
153
+ };
154
+ var normalizeVocaloidBoothConfig = (input) => {
155
+ const merged = {
156
+ ...defaultVocaloidBoothConfig,
157
+ ...input ?? {}
158
+ };
159
+ return {
160
+ ...merged,
161
+ boothId: merged.boothId || defaultVocaloidBoothConfig.boothId,
162
+ title: merged.title || defaultVocaloidBoothConfig.title,
163
+ defaultTtlHours: Math.max(1, merged.defaultTtlHours),
164
+ maxFiles: Math.max(1, merged.maxFiles),
165
+ maxSingleFileSizeMb: Math.max(1, merged.maxSingleFileSizeMb),
166
+ maxTotalFileSizeMb: Math.max(1, merged.maxTotalFileSizeMb),
167
+ allowedExtensions: (merged.allowedExtensions?.length ? merged.allowedExtensions : defaultVocaloidBoothConfig.allowedExtensions).map((ext) => ext.toLowerCase())
168
+ };
169
+ };
170
+ var BoothUploadPanel = ({
171
+ boothId,
172
+ maxFiles = 10,
173
+ maxFileSizeMb = 2048,
174
+ accept,
175
+ uploading = false,
176
+ onSubmit
177
+ }) => {
178
+ const [files, setFiles] = React.useState([]);
179
+ const [nickname, setNickname] = React.useState("");
180
+ const [contactTail, setContactTail] = React.useState("");
181
+ const [ttlHours, setTtlHours] = React.useState(24 * 14);
182
+ const [error, setError] = React.useState(null);
183
+ const totalSizeMb = React.useMemo(
184
+ () => files.reduce((acc, file) => acc + file.size, 0) / 1024 / 1024,
185
+ [files]
186
+ );
187
+ const addFiles = (newFiles) => {
188
+ if (!newFiles) return;
189
+ const incoming = Array.from(newFiles);
190
+ const next = [...files, ...incoming];
191
+ if (next.length > maxFiles) {
192
+ setError(`\u6700\u591A\u4E0A\u4F20 ${maxFiles} \u4E2A\u6587\u4EF6`);
193
+ return;
194
+ }
195
+ const oversized = incoming.find((f) => f.size > maxFileSizeMb * 1024 * 1024);
196
+ if (oversized) {
197
+ setError(`\u6587\u4EF6 ${oversized.name} \u8D85\u8FC7 ${maxFileSizeMb}MB \u9650\u5236`);
198
+ return;
199
+ }
200
+ setError(null);
201
+ setFiles(next);
202
+ };
203
+ const removeFile = (name) => setFiles((prev) => prev.filter((f) => f.name !== name));
204
+ const handleSubmit = async () => {
205
+ if (files.length === 0) {
206
+ setError("\u8BF7\u5148\u9009\u62E9\u81F3\u5C11\u4E00\u4E2A\u6587\u4EF6");
207
+ return;
208
+ }
209
+ setError(null);
210
+ await onSubmit({
211
+ boothId,
212
+ files,
213
+ nickname: nickname || void 0,
214
+ contactTail: contactTail || void 0,
215
+ ttlHours
216
+ });
217
+ };
218
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm" }, /* @__PURE__ */ React__default.default.createElement("h3", { className: "mb-3 text-lg font-semibold" }, "\u4E0A\u4F20\u521B\u4F5C\u6587\u4EF6"), /* @__PURE__ */ React__default.default.createElement(
219
+ "input",
220
+ {
221
+ type: "file",
222
+ multiple: true,
223
+ accept,
224
+ onChange: (e) => addFiles(e.target.files),
225
+ className: "mb-3 block w-full text-sm"
226
+ }
227
+ ), /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-3 grid grid-cols-1 gap-2 md:grid-cols-3" }, /* @__PURE__ */ React__default.default.createElement(
228
+ "input",
229
+ {
230
+ value: nickname,
231
+ onChange: (e) => setNickname(e.target.value),
232
+ placeholder: "\u6635\u79F0\uFF08\u53EF\u9009\uFF09",
233
+ className: "rounded-md border px-3 py-2 text-sm"
234
+ }
235
+ ), /* @__PURE__ */ React__default.default.createElement(
236
+ "input",
237
+ {
238
+ value: contactTail,
239
+ onChange: (e) => setContactTail(e.target.value),
240
+ placeholder: "\u8054\u7CFB\u65B9\u5F0F\u540E4\u4F4D\uFF08\u53EF\u9009\uFF09",
241
+ className: "rounded-md border px-3 py-2 text-sm"
242
+ }
243
+ ), /* @__PURE__ */ React__default.default.createElement(
244
+ "input",
245
+ {
246
+ value: ttlHours,
247
+ type: "number",
248
+ min: 1,
249
+ onChange: (e) => setTtlHours(Number(e.target.value) || 24),
250
+ placeholder: "\u4FDD\u5B58\u65F6\u957F\uFF08\u5C0F\u65F6\uFF09",
251
+ className: "rounded-md border px-3 py-2 text-sm"
252
+ }
253
+ )), /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-3 text-xs text-slate-500" }, "\u5DF2\u9009 ", files.length, " \u4E2A\u6587\u4EF6\uFF0C\u603B\u8BA1 ", totalSizeMb.toFixed(2), " MB"), /* @__PURE__ */ React__default.default.createElement("ul", { className: "mb-3 max-h-40 overflow-auto rounded-md border border-slate-100 p-2 text-sm" }, files.length === 0 && /* @__PURE__ */ React__default.default.createElement("li", { className: "text-slate-400" }, "\u5C1A\u672A\u9009\u62E9\u6587\u4EF6"), files.map((file) => /* @__PURE__ */ React__default.default.createElement("li", { key: `${file.name}-${file.size}`, className: "mb-1 flex items-center justify-between gap-2" }, /* @__PURE__ */ React__default.default.createElement("span", { className: "truncate" }, file.name), /* @__PURE__ */ React__default.default.createElement("button", { type: "button", className: "text-rose-500", onClick: () => removeFile(file.name) }, "\u79FB\u9664")))), error && /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-3 rounded-md bg-rose-50 p-2 text-sm text-rose-700" }, error), /* @__PURE__ */ React__default.default.createElement(
254
+ "button",
255
+ {
256
+ type: "button",
257
+ disabled: uploading,
258
+ onClick: handleSubmit,
259
+ className: "rounded-md bg-indigo-600 px-3 py-2 text-white disabled:cursor-not-allowed disabled:opacity-50"
260
+ },
261
+ uploading ? "\u4E0A\u4F20\u4E2D..." : "\u5F00\u59CB\u4E0A\u4F20"
262
+ ));
263
+ };
264
+ var BoothRedeemPanel = ({ onRedeem, loading }) => {
265
+ const [matchCode, setMatchCode] = React.useState("");
266
+ const [record, setRecord] = React.useState(null);
267
+ const [error, setError] = React.useState(null);
268
+ const handleRedeem = async () => {
269
+ setError(null);
270
+ const result = await onRedeem(matchCode.trim());
271
+ if (!result) {
272
+ setRecord(null);
273
+ setError("\u5339\u914D\u7801\u4E0D\u5B58\u5728\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5");
274
+ return;
275
+ }
276
+ if (result.status !== "active") {
277
+ setRecord(result);
278
+ setError("\u5339\u914D\u7801\u5DF2\u8FC7\u671F\u6216\u5931\u6548");
279
+ return;
280
+ }
281
+ setRecord(result);
282
+ };
283
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm" }, /* @__PURE__ */ React__default.default.createElement("h3", { className: "mb-3 text-lg font-semibold" }, "\u51ED\u5339\u914D\u7801\u4E0B\u8F7D"), /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-3 flex gap-2" }, /* @__PURE__ */ React__default.default.createElement(
284
+ "input",
285
+ {
286
+ value: matchCode,
287
+ onChange: (e) => setMatchCode(e.target.value.toUpperCase()),
288
+ placeholder: "\u8F93\u5165\u5339\u914D\u7801\uFF08\u5982 A7K9Q2\uFF09",
289
+ className: "w-full rounded-md border px-3 py-2 text-sm uppercase"
290
+ }
291
+ ), /* @__PURE__ */ React__default.default.createElement(
292
+ "button",
293
+ {
294
+ type: "button",
295
+ onClick: handleRedeem,
296
+ disabled: loading,
297
+ className: "rounded-md bg-slate-900 px-3 py-2 text-white disabled:opacity-50"
298
+ },
299
+ "\u67E5\u8BE2"
300
+ )), error && /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-3 rounded-md bg-amber-50 p-2 text-sm text-amber-700" }, error), record && /* @__PURE__ */ React__default.default.createElement("div", { className: "rounded-md border border-slate-100 p-3" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-2 text-xs text-slate-500" }, "\u5171 ", record.files.length, " \u4E2A\u6587\u4EF6"), /* @__PURE__ */ React__default.default.createElement("ul", { className: "space-y-1 text-sm" }, record.files.map((file) => /* @__PURE__ */ React__default.default.createElement("li", { key: file.id, className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React__default.default.createElement("span", { className: "truncate" }, file.fileName), /* @__PURE__ */ React__default.default.createElement("a", { href: file.objectKey, className: "text-indigo-600 hover:underline", download: true }, "\u4E0B\u8F7D"))))));
301
+ };
302
+ var BoothSuccessCard = ({
303
+ matchCode,
304
+ expiresAt,
305
+ downloadUrlPath,
306
+ onCopyCode,
307
+ className
308
+ }) => {
309
+ const handleCopy = async () => {
310
+ try {
311
+ await navigator.clipboard.writeText(matchCode);
312
+ } catch {
313
+ }
314
+ onCopyCode?.(matchCode);
315
+ };
316
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: `rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm ${className ?? ""}` }, /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-2 text-xs text-emerald-800" }, "\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5DF2\u751F\u6210\u5339\u914D\u7801"), /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-3 text-2xl font-bold tracking-widest text-emerald-900" }, matchCode), /* @__PURE__ */ React__default.default.createElement("div", { className: "mb-3 text-xs text-emerald-800" }, "\u8FC7\u671F\u65F6\u95F4\uFF1A", new Date(expiresAt).toLocaleString()), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React__default.default.createElement(
317
+ "button",
318
+ {
319
+ type: "button",
320
+ onClick: handleCopy,
321
+ className: "rounded-md bg-emerald-600 px-3 py-2 text-white hover:bg-emerald-700"
322
+ },
323
+ "\u590D\u5236\u5339\u914D\u7801"
324
+ ), /* @__PURE__ */ React__default.default.createElement(
325
+ "a",
326
+ {
327
+ href: downloadUrlPath,
328
+ className: "rounded-md border border-emerald-400 bg-white px-3 py-2 text-emerald-700 hover:bg-emerald-100"
329
+ },
330
+ "\u6253\u5F00\u4E0B\u8F7D\u9875"
331
+ )));
332
+ };
333
+ var BoothConfigPage = ({ initialConfig, onSave }) => {
334
+ const [config, setConfig] = React.useState(
335
+ normalizeVocaloidBoothConfig(initialConfig)
336
+ );
337
+ const [saving, setSaving] = React.useState(false);
338
+ const extText = React.useMemo(() => config.allowedExtensions.join(","), [config.allowedExtensions]);
339
+ const update = (key, value) => setConfig((prev) => ({ ...prev, [key]: value }));
340
+ const save = async () => {
341
+ setSaving(true);
342
+ try {
343
+ const normalized = normalizeVocaloidBoothConfig(config);
344
+ setConfig(normalized);
345
+ await onSave?.(normalized);
346
+ } finally {
347
+ setSaving(false);
348
+ }
349
+ };
350
+ const reset = () => setConfig(defaultVocaloidBoothConfig);
351
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm space-y-3" }, /* @__PURE__ */ React__default.default.createElement("h3", { className: "text-lg font-semibold" }, "Vocaloid Booth \u914D\u7F6E\u9875"), /* @__PURE__ */ React__default.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm" }, /* @__PURE__ */ React__default.default.createElement("input", { className: "rounded border px-3 py-2", value: config.boothId, onChange: (e) => update("boothId", e.target.value), placeholder: "boothId" }), /* @__PURE__ */ React__default.default.createElement("input", { className: "rounded border px-3 py-2", value: config.title, onChange: (e) => update("title", e.target.value), placeholder: "\u6807\u9898" }), /* @__PURE__ */ React__default.default.createElement("input", { className: "rounded border px-3 py-2 md:col-span-2", value: config.description ?? "", onChange: (e) => update("description", e.target.value), placeholder: "\u63CF\u8FF0" }), /* @__PURE__ */ React__default.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.defaultTtlHours, onChange: (e) => update("defaultTtlHours", Number(e.target.value) || 1), placeholder: "\u9ED8\u8BA4\u4FDD\u5B58\u65F6\u957F\uFF08\u5C0F\u65F6\uFF09" }), /* @__PURE__ */ React__default.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxFiles, onChange: (e) => update("maxFiles", Number(e.target.value) || 1), placeholder: "\u6700\u5927\u6587\u4EF6\u6570" }), /* @__PURE__ */ React__default.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxSingleFileSizeMb, onChange: (e) => update("maxSingleFileSizeMb", Number(e.target.value) || 1), placeholder: "\u5355\u6587\u4EF6\u4E0A\u9650 MB" }), /* @__PURE__ */ React__default.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxTotalFileSizeMb, onChange: (e) => update("maxTotalFileSizeMb", Number(e.target.value) || 1), placeholder: "\u603B\u5927\u5C0F\u4E0A\u9650 MB" }), /* @__PURE__ */ React__default.default.createElement(
352
+ "textarea",
353
+ {
354
+ className: "rounded border px-3 py-2 md:col-span-2",
355
+ rows: 3,
356
+ value: extText,
357
+ onChange: (e) => update(
358
+ "allowedExtensions",
359
+ e.target.value.split(",").map((v) => v.trim()).filter(Boolean)
360
+ ),
361
+ placeholder: "\u5141\u8BB8\u540E\u7F00\uFF0C\u9017\u53F7\u5206\u9694"
362
+ }
363
+ )), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React__default.default.createElement("button", { className: "rounded bg-indigo-600 px-3 py-2 text-white", disabled: saving, onClick: save }, saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"), /* @__PURE__ */ React__default.default.createElement("button", { className: "rounded border px-3 py-2", onClick: reset }, "\u6062\u590D\u9ED8\u8BA4")));
364
+ };
365
+
366
+ exports.BoothConfigPage = BoothConfigPage;
367
+ exports.BoothRedeemPanel = BoothRedeemPanel;
368
+ exports.BoothSuccessCard = BoothSuccessCard;
369
+ exports.BoothUploadPanel = BoothUploadPanel;
370
+ exports.BoothVaultService = BoothVaultService;
371
+ exports.defaultVocaloidBoothConfig = defaultVocaloidBoothConfig;
372
+ exports.generateMatchCode = generateMatchCode;
373
+ exports.normalizeMatchCode = normalizeMatchCode;
374
+ exports.normalizeVocaloidBoothConfig = normalizeVocaloidBoothConfig;
375
+ //# sourceMappingURL=index.js.map
376
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/vocaloidBooth/core/code.ts","../../../src/vocaloidBooth/core/boothVaultService.ts","../../../src/vocaloidBooth/core/config.ts","../../../src/vocaloidBooth/components/BoothUploadPanel.tsx","../../../src/vocaloidBooth/components/BoothRedeemPanel.tsx","../../../src/vocaloidBooth/components/BoothSuccessCard.tsx","../../../src/vocaloidBooth/components/BoothConfigPage.tsx"],"names":["randomUUID","useState","useMemo","React"],"mappings":"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","file":"index.js","sourcesContent":["const AMBIGUOUS = new Set(['0', '1', 'I', 'O', 'L']);\nconst ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789'.split('').filter((c) => !AMBIGUOUS.has(c));\n\nexport interface GenerateMatchCodeOptions {\n length?: number;\n maxAttempts?: number;\n exists: (code: string) => Promise<boolean>;\n}\n\nexport const normalizeMatchCode = (value: string): string => value.trim().toUpperCase();\n\nexport const generateMatchCode = async ({\n length = 6,\n maxAttempts = 20,\n exists,\n}: GenerateMatchCodeOptions): Promise<string> => {\n if (length < 4) {\n throw new Error('Match code length must be at least 4');\n }\n\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n const code = Array.from({ length })\n .map(() => ALPHABET[Math.floor(Math.random() * ALPHABET.length)])\n .join('');\n\n // eslint-disable-next-line no-await-in-loop\n if (!(await exists(code))) {\n return code;\n }\n }\n\n throw new Error('Unable to generate unique match code');\n};\n","import { randomUUID } from 'crypto';\nimport type {\n BoothAuditEvent,\n BoothUploadRecord,\n BoothVaultStore,\n CreateBoothUploadInput,\n CreateBoothUploadResult,\n} from '../types';\nimport { generateMatchCode, normalizeMatchCode } from './code';\n\nexport interface BoothRedeemGuardLike {\n assertAllowed(subjectKey: string): void;\n registerAttempt(subjectKey: string, success: boolean): void;\n}\n\nexport interface BoothVaultServiceOptions {\n store: BoothVaultStore;\n codeLength?: number;\n defaultTtlHours?: number;\n baseDownloadPath?: string;\n redeemGuard?: BoothRedeemGuardLike;\n onAuditEvent?: (event: BoothAuditEvent) => void;\n}\n\nexport class BoothVaultService {\n private emitAudit(event: Omit<BoothAuditEvent, 'at'>): void {\n this.onAuditEvent?.({\n ...event,\n at: new Date().toISOString(),\n });\n }\n private readonly store: BoothVaultStore;\n private readonly codeLength: number;\n private readonly defaultTtlHours: number;\n private readonly baseDownloadPath: string;\n private readonly redeemGuard?: BoothRedeemGuardLike;\n private readonly onAuditEvent?: (event: BoothAuditEvent) => void;\n\n constructor(options: BoothVaultServiceOptions) {\n this.store = options.store;\n this.codeLength = options.codeLength ?? 6;\n this.defaultTtlHours = options.defaultTtlHours ?? 24 * 14;\n this.baseDownloadPath = options.baseDownloadPath ?? '/redeem';\n this.redeemGuard = options.redeemGuard;\n this.onAuditEvent = options.onAuditEvent;\n }\n\n async createUpload(input: CreateBoothUploadInput): Promise<CreateBoothUploadResult> {\n if (!input.files?.length) {\n throw new Error('At least one file is required');\n }\n\n const now = new Date();\n const ttlHours = Math.max(1, input.ttlHours ?? this.defaultTtlHours);\n const expiresAt = new Date(now.getTime() + ttlHours * 60 * 60 * 1000);\n\n const matchCode = await generateMatchCode({\n length: this.codeLength,\n exists: (code) => this.store.existsByMatchCode(code),\n });\n\n const record: BoothUploadRecord = {\n id: randomUUID(),\n boothId: input.boothId,\n matchCode,\n createdAt: now.toISOString(),\n expiresAt: expiresAt.toISOString(),\n files: input.files.map((file) => ({\n ...file,\n id: randomUUID(),\n })),\n metadata: input.metadata,\n status: 'active',\n downloadCount: 0,\n };\n\n await this.store.saveRecord(record);\n this.emitAudit({\n type: 'upload.created',\n boothId: record.boothId,\n recordId: record.id,\n matchCode: record.matchCode,\n detail: { fileCount: record.files.length },\n });\n\n return {\n record,\n downloadUrlPath: `${this.baseDownloadPath}?code=${record.matchCode}`,\n };\n }\n\n async getByMatchCode(matchCode: string): Promise<BoothUploadRecord | null> {\n const normalized = normalizeMatchCode(matchCode);\n const record = await this.store.findByMatchCode(normalized);\n\n if (!record) {\n return null;\n }\n\n if (new Date(record.expiresAt).getTime() <= Date.now() && record.status === 'active') {\n return {\n ...record,\n status: 'expired',\n };\n }\n\n return record;\n }\n\n async markDownloaded(recordId: string): Promise<void> {\n await this.store.incrementDownloadCount(recordId);\n }\n\n async resolveDownloadFilesByCode(\n matchCode: string,\n options?: { requesterKey?: string }\n ): Promise<BoothUploadRecord | null> {\n const requesterKey = options?.requesterKey;\n if (requesterKey && this.redeemGuard) {\n try {\n this.redeemGuard.assertAllowed(requesterKey);\n } catch (error) {\n this.emitAudit({\n type: 'redeem.blocked',\n requesterKey,\n matchCode,\n detail: { message: error instanceof Error ? error.message : 'blocked' },\n });\n throw error;\n }\n }\n\n const record = await this.getByMatchCode(matchCode);\n const success = !!record && record.status === 'active';\n\n if (requesterKey && this.redeemGuard) {\n this.redeemGuard.registerAttempt(requesterKey, success);\n }\n\n if (!success) {\n this.emitAudit({\n type: 'redeem.failed',\n requesterKey,\n matchCode,\n boothId: record?.boothId,\n recordId: record?.id,\n });\n return record;\n }\n\n await this.markDownloaded(record.id);\n const reloaded = this.store.findByRecordId\n ? await this.store.findByRecordId(record.id)\n : await this.getByMatchCode(record.matchCode);\n\n this.emitAudit({\n type: 'redeem.success',\n requesterKey,\n matchCode: record.matchCode,\n boothId: record.boothId,\n recordId: record.id,\n });\n\n return reloaded ?? record;\n }\n}\n","export interface VocaloidBoothConfig {\n boothId: string;\n title: string;\n description?: string;\n defaultTtlHours: number;\n maxFiles: number;\n maxSingleFileSizeMb: number;\n maxTotalFileSizeMb: number;\n allowedExtensions: string[];\n}\n\nexport const defaultVocaloidBoothConfig: VocaloidBoothConfig = {\n boothId: 'default-booth',\n title: 'MMD / Vocaloid 创作文件寄存站',\n description: '上传创作文件并生成匹配码,后续可凭码下载',\n defaultTtlHours: 24 * 14,\n maxFiles: 20,\n maxSingleFileSizeMb: 2048,\n maxTotalFileSizeMb: 5120,\n allowedExtensions: ['zip', '7z', 'rar', 'vsqx', 'vpr', 'vmd', 'pmx', 'wav', 'mp3', 'mp4'],\n};\n\nexport const normalizeVocaloidBoothConfig = (\n input?: Partial<VocaloidBoothConfig>\n): VocaloidBoothConfig => {\n const merged = {\n ...defaultVocaloidBoothConfig,\n ...(input ?? {}),\n };\n\n return {\n ...merged,\n boothId: merged.boothId || defaultVocaloidBoothConfig.boothId,\n title: merged.title || defaultVocaloidBoothConfig.title,\n defaultTtlHours: Math.max(1, merged.defaultTtlHours),\n maxFiles: Math.max(1, merged.maxFiles),\n maxSingleFileSizeMb: Math.max(1, merged.maxSingleFileSizeMb),\n maxTotalFileSizeMb: Math.max(1, merged.maxTotalFileSizeMb),\n allowedExtensions: (merged.allowedExtensions?.length\n ? merged.allowedExtensions\n : defaultVocaloidBoothConfig.allowedExtensions\n ).map((ext) => ext.toLowerCase()),\n };\n};\n","'use client';\n\nimport React, { useMemo, useState } from 'react';\n\nexport interface BoothUploadSubmitPayload {\n boothId: string;\n files: File[];\n nickname?: string;\n contactTail?: string;\n ttlHours?: number;\n}\n\nexport interface BoothUploadPanelProps {\n boothId: string;\n maxFiles?: number;\n maxFileSizeMb?: number;\n accept?: string;\n uploading?: boolean;\n onSubmit: (payload: BoothUploadSubmitPayload) => Promise<void> | void;\n}\n\nexport const BoothUploadPanel: React.FC<BoothUploadPanelProps> = ({\n boothId,\n maxFiles = 10,\n maxFileSizeMb = 2048,\n accept,\n uploading = false,\n onSubmit,\n}) => {\n const [files, setFiles] = useState<File[]>([]);\n const [nickname, setNickname] = useState('');\n const [contactTail, setContactTail] = useState('');\n const [ttlHours, setTtlHours] = useState(24 * 14);\n const [error, setError] = useState<string | null>(null);\n\n const totalSizeMb = useMemo(\n () => files.reduce((acc, file) => acc + file.size, 0) / 1024 / 1024,\n [files]\n );\n\n const addFiles = (newFiles: FileList | null) => {\n if (!newFiles) return;\n const incoming = Array.from(newFiles);\n const next = [...files, ...incoming];\n\n if (next.length > maxFiles) {\n setError(`最多上传 ${maxFiles} 个文件`);\n return;\n }\n\n const oversized = incoming.find((f) => f.size > maxFileSizeMb * 1024 * 1024);\n if (oversized) {\n setError(`文件 ${oversized.name} 超过 ${maxFileSizeMb}MB 限制`);\n return;\n }\n\n setError(null);\n setFiles(next);\n };\n\n const removeFile = (name: string) => setFiles((prev) => prev.filter((f) => f.name !== name));\n\n const handleSubmit = async () => {\n if (files.length === 0) {\n setError('请先选择至少一个文件');\n return;\n }\n setError(null);\n await onSubmit({\n boothId,\n files,\n nickname: nickname || undefined,\n contactTail: contactTail || undefined,\n ttlHours,\n });\n };\n\n return (\n <div className=\"rounded-xl border border-slate-200 bg-white p-4 shadow-sm\">\n <h3 className=\"mb-3 text-lg font-semibold\">上传创作文件</h3>\n\n <input\n type=\"file\"\n multiple\n accept={accept}\n onChange={(e) => addFiles(e.target.files)}\n className=\"mb-3 block w-full text-sm\"\n />\n\n <div className=\"mb-3 grid grid-cols-1 gap-2 md:grid-cols-3\">\n <input\n value={nickname}\n onChange={(e) => setNickname(e.target.value)}\n placeholder=\"昵称(可选)\"\n className=\"rounded-md border px-3 py-2 text-sm\"\n />\n <input\n value={contactTail}\n onChange={(e) => setContactTail(e.target.value)}\n placeholder=\"联系方式后4位(可选)\"\n className=\"rounded-md border px-3 py-2 text-sm\"\n />\n <input\n value={ttlHours}\n type=\"number\"\n min={1}\n onChange={(e) => setTtlHours(Number(e.target.value) || 24)}\n placeholder=\"保存时长(小时)\"\n className=\"rounded-md border px-3 py-2 text-sm\"\n />\n </div>\n\n <div className=\"mb-3 text-xs text-slate-500\">\n 已选 {files.length} 个文件,总计 {totalSizeMb.toFixed(2)} MB\n </div>\n\n <ul className=\"mb-3 max-h-40 overflow-auto rounded-md border border-slate-100 p-2 text-sm\">\n {files.length === 0 && <li className=\"text-slate-400\">尚未选择文件</li>}\n {files.map((file) => (\n <li key={`${file.name}-${file.size}`} className=\"mb-1 flex items-center justify-between gap-2\">\n <span className=\"truncate\">{file.name}</span>\n <button type=\"button\" className=\"text-rose-500\" onClick={() => removeFile(file.name)}>\n 移除\n </button>\n </li>\n ))}\n </ul>\n\n {error && <div className=\"mb-3 rounded-md bg-rose-50 p-2 text-sm text-rose-700\">{error}</div>}\n\n <button\n type=\"button\"\n disabled={uploading}\n onClick={handleSubmit}\n className=\"rounded-md bg-indigo-600 px-3 py-2 text-white disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {uploading ? '上传中...' : '开始上传'}\n </button>\n </div>\n );\n};\n","'use client';\n\nimport React, { useState } from 'react';\nimport type { BoothUploadRecord } from '../types';\n\nexport interface BoothRedeemPanelProps {\n loading?: boolean;\n onRedeem: (matchCode: string) => Promise<BoothUploadRecord | null>;\n}\n\nexport const BoothRedeemPanel: React.FC<BoothRedeemPanelProps> = ({ onRedeem, loading }) => {\n const [matchCode, setMatchCode] = useState('');\n const [record, setRecord] = useState<BoothUploadRecord | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const handleRedeem = async () => {\n setError(null);\n const result = await onRedeem(matchCode.trim());\n\n if (!result) {\n setRecord(null);\n setError('匹配码不存在,请检查后重试');\n return;\n }\n\n if (result.status !== 'active') {\n setRecord(result);\n setError('匹配码已过期或失效');\n return;\n }\n\n setRecord(result);\n };\n\n return (\n <div className=\"rounded-xl border border-slate-200 bg-white p-4 shadow-sm\">\n <h3 className=\"mb-3 text-lg font-semibold\">凭匹配码下载</h3>\n\n <div className=\"mb-3 flex gap-2\">\n <input\n value={matchCode}\n onChange={(e) => setMatchCode(e.target.value.toUpperCase())}\n placeholder=\"输入匹配码(如 A7K9Q2)\"\n className=\"w-full rounded-md border px-3 py-2 text-sm uppercase\"\n />\n <button\n type=\"button\"\n onClick={handleRedeem}\n disabled={loading}\n className=\"rounded-md bg-slate-900 px-3 py-2 text-white disabled:opacity-50\"\n >\n 查询\n </button>\n </div>\n\n {error && <div className=\"mb-3 rounded-md bg-amber-50 p-2 text-sm text-amber-700\">{error}</div>}\n\n {record && (\n <div className=\"rounded-md border border-slate-100 p-3\">\n <div className=\"mb-2 text-xs text-slate-500\">共 {record.files.length} 个文件</div>\n <ul className=\"space-y-1 text-sm\">\n {record.files.map((file) => (\n <li key={file.id} className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate\">{file.fileName}</span>\n <a href={file.objectKey} className=\"text-indigo-600 hover:underline\" download>\n 下载\n </a>\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n );\n};\n","'use client';\n\nimport React from 'react';\n\nexport interface BoothSuccessCardProps {\n matchCode: string;\n expiresAt: string;\n downloadUrlPath: string;\n onCopyCode?: (code: string) => void;\n className?: string;\n}\n\nexport const BoothSuccessCard: React.FC<BoothSuccessCardProps> = ({\n matchCode,\n expiresAt,\n downloadUrlPath,\n onCopyCode,\n className,\n}) => {\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(matchCode);\n } catch {\n // ignore clipboard errors in non-secure contexts\n }\n onCopyCode?.(matchCode);\n };\n\n return (\n <div className={`rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm ${className ?? ''}`}>\n <div className=\"mb-2 text-xs text-emerald-800\">上传完成,已生成匹配码</div>\n <div className=\"mb-3 text-2xl font-bold tracking-widest text-emerald-900\">{matchCode}</div>\n <div className=\"mb-3 text-xs text-emerald-800\">过期时间:{new Date(expiresAt).toLocaleString()}</div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={handleCopy}\n className=\"rounded-md bg-emerald-600 px-3 py-2 text-white hover:bg-emerald-700\"\n >\n 复制匹配码\n </button>\n <a\n href={downloadUrlPath}\n className=\"rounded-md border border-emerald-400 bg-white px-3 py-2 text-emerald-700 hover:bg-emerald-100\"\n >\n 打开下载页\n </a>\n </div>\n </div>\n );\n};\n","'use client';\n\nimport React, { useMemo, useState } from 'react';\nimport {\n defaultVocaloidBoothConfig,\n normalizeVocaloidBoothConfig,\n type VocaloidBoothConfig,\n} from '../core';\n\nexport interface BoothConfigPageProps {\n initialConfig?: Partial<VocaloidBoothConfig>;\n onSave?: (config: VocaloidBoothConfig) => Promise<void> | void;\n}\n\nexport const BoothConfigPage: React.FC<BoothConfigPageProps> = ({ initialConfig, onSave }) => {\n const [config, setConfig] = useState<VocaloidBoothConfig>(\n normalizeVocaloidBoothConfig(initialConfig)\n );\n const [saving, setSaving] = useState(false);\n\n const extText = useMemo(() => config.allowedExtensions.join(','), [config.allowedExtensions]);\n\n const update = <K extends keyof VocaloidBoothConfig>(key: K, value: VocaloidBoothConfig[K]) =>\n setConfig((prev) => ({ ...prev, [key]: value }));\n\n const save = async () => {\n setSaving(true);\n try {\n const normalized = normalizeVocaloidBoothConfig(config);\n setConfig(normalized);\n await onSave?.(normalized);\n } finally {\n setSaving(false);\n }\n };\n\n const reset = () => setConfig(defaultVocaloidBoothConfig);\n\n return (\n <div className=\"rounded-xl border border-slate-200 bg-white p-4 shadow-sm space-y-3\">\n <h3 className=\"text-lg font-semibold\">Vocaloid Booth 配置页</h3>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-3 text-sm\">\n <input className=\"rounded border px-3 py-2\" value={config.boothId} onChange={(e) => update('boothId', e.target.value)} placeholder=\"boothId\" />\n <input className=\"rounded border px-3 py-2\" value={config.title} onChange={(e) => update('title', e.target.value)} placeholder=\"标题\" />\n <input className=\"rounded border px-3 py-2 md:col-span-2\" value={config.description ?? ''} onChange={(e) => update('description', e.target.value)} placeholder=\"描述\" />\n\n <input className=\"rounded border px-3 py-2\" type=\"number\" value={config.defaultTtlHours} onChange={(e) => update('defaultTtlHours', Number(e.target.value) || 1)} placeholder=\"默认保存时长(小时)\" />\n <input className=\"rounded border px-3 py-2\" type=\"number\" value={config.maxFiles} onChange={(e) => update('maxFiles', Number(e.target.value) || 1)} placeholder=\"最大文件数\" />\n <input className=\"rounded border px-3 py-2\" type=\"number\" value={config.maxSingleFileSizeMb} onChange={(e) => update('maxSingleFileSizeMb', Number(e.target.value) || 1)} placeholder=\"单文件上限 MB\" />\n <input className=\"rounded border px-3 py-2\" type=\"number\" value={config.maxTotalFileSizeMb} onChange={(e) => update('maxTotalFileSizeMb', Number(e.target.value) || 1)} placeholder=\"总大小上限 MB\" />\n\n <textarea\n className=\"rounded border px-3 py-2 md:col-span-2\"\n rows={3}\n value={extText}\n onChange={(e) =>\n update(\n 'allowedExtensions',\n e.target.value\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean)\n )\n }\n placeholder=\"允许后缀,逗号分隔\"\n />\n </div>\n\n <div className=\"flex gap-2\">\n <button className=\"rounded bg-indigo-600 px-3 py-2 text-white\" disabled={saving} onClick={save}>\n {saving ? '保存中...' : '保存配置'}\n </button>\n <button className=\"rounded border px-3 py-2\" onClick={reset}>\n 恢复默认\n </button>\n </div>\n </div>\n );\n};\n"]}