@openinary/core 1.1.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 (242) hide show
  1. package/LICENSE +661 -0
  2. package/dist/config/deps.d.ts +14 -0
  3. package/dist/config/deps.d.ts.map +1 -0
  4. package/dist/config/deps.js +2 -0
  5. package/dist/config/deps.js.map +1 -0
  6. package/dist/index.d.ts +27 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +28 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/routes/authenticated.d.ts +4 -0
  11. package/dist/routes/authenticated.d.ts.map +1 -0
  12. package/dist/routes/authenticated.js +130 -0
  13. package/dist/routes/authenticated.js.map +1 -0
  14. package/dist/routes/download-folder.d.ts +4 -0
  15. package/dist/routes/download-folder.d.ts.map +1 -0
  16. package/dist/routes/download-folder.js +101 -0
  17. package/dist/routes/download-folder.js.map +1 -0
  18. package/dist/routes/download-zip.d.ts +15 -0
  19. package/dist/routes/download-zip.d.ts.map +1 -0
  20. package/dist/routes/download-zip.js +147 -0
  21. package/dist/routes/download-zip.js.map +1 -0
  22. package/dist/routes/download.d.ts +4 -0
  23. package/dist/routes/download.d.ts.map +1 -0
  24. package/dist/routes/download.js +83 -0
  25. package/dist/routes/download.js.map +1 -0
  26. package/dist/routes/invalidate.d.ts +4 -0
  27. package/dist/routes/invalidate.d.ts.map +1 -0
  28. package/dist/routes/invalidate.js +114 -0
  29. package/dist/routes/invalidate.js.map +1 -0
  30. package/dist/routes/queue-events.d.ts +4 -0
  31. package/dist/routes/queue-events.d.ts.map +1 -0
  32. package/dist/routes/queue-events.js +125 -0
  33. package/dist/routes/queue-events.js.map +1 -0
  34. package/dist/routes/queue.d.ts +4 -0
  35. package/dist/routes/queue.d.ts.map +1 -0
  36. package/dist/routes/queue.js +130 -0
  37. package/dist/routes/queue.js.map +1 -0
  38. package/dist/routes/storage.d.ts +4 -0
  39. package/dist/routes/storage.d.ts.map +1 -0
  40. package/dist/routes/storage.js +694 -0
  41. package/dist/routes/storage.js.map +1 -0
  42. package/dist/routes/transform-helpers.d.ts +62 -0
  43. package/dist/routes/transform-helpers.d.ts.map +1 -0
  44. package/dist/routes/transform-helpers.js +372 -0
  45. package/dist/routes/transform-helpers.js.map +1 -0
  46. package/dist/routes/transform.d.ts +4 -0
  47. package/dist/routes/transform.d.ts.map +1 -0
  48. package/dist/routes/transform.js +65 -0
  49. package/dist/routes/transform.js.map +1 -0
  50. package/dist/routes/video-status.d.ts +4 -0
  51. package/dist/routes/video-status.d.ts.map +1 -0
  52. package/dist/routes/video-status.js +208 -0
  53. package/dist/routes/video-status.js.map +1 -0
  54. package/dist/services/transform.service.d.ts +71 -0
  55. package/dist/services/transform.service.d.ts.map +1 -0
  56. package/dist/services/transform.service.js +362 -0
  57. package/dist/services/transform.service.js.map +1 -0
  58. package/dist/types.d.ts +82 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/asset-deletion.d.ts +16 -0
  63. package/dist/utils/asset-deletion.d.ts.map +1 -0
  64. package/dist/utils/asset-deletion.js +131 -0
  65. package/dist/utils/asset-deletion.js.map +1 -0
  66. package/dist/utils/cache.d.ts +44 -0
  67. package/dist/utils/cache.d.ts.map +1 -0
  68. package/dist/utils/cache.js +248 -0
  69. package/dist/utils/cache.js.map +1 -0
  70. package/dist/utils/get-unique-file-path.d.ts +8 -0
  71. package/dist/utils/get-unique-file-path.d.ts.map +1 -0
  72. package/dist/utils/get-unique-file-path.js +26 -0
  73. package/dist/utils/get-unique-file-path.js.map +1 -0
  74. package/dist/utils/image/aspect-ratio.d.ts +7 -0
  75. package/dist/utils/image/aspect-ratio.d.ts.map +1 -0
  76. package/dist/utils/image/aspect-ratio.js +40 -0
  77. package/dist/utils/image/aspect-ratio.js.map +1 -0
  78. package/dist/utils/image/background.d.ts +6 -0
  79. package/dist/utils/image/background.d.ts.map +1 -0
  80. package/dist/utils/image/background.js +22 -0
  81. package/dist/utils/image/background.js.map +1 -0
  82. package/dist/utils/image/compression.d.ts +44 -0
  83. package/dist/utils/image/compression.d.ts.map +1 -0
  84. package/dist/utils/image/compression.js +345 -0
  85. package/dist/utils/image/compression.js.map +1 -0
  86. package/dist/utils/image/gravity.d.ts +6 -0
  87. package/dist/utils/image/gravity.d.ts.map +1 -0
  88. package/dist/utils/image/gravity.js +24 -0
  89. package/dist/utils/image/gravity.js.map +1 -0
  90. package/dist/utils/image/index.d.ts +8 -0
  91. package/dist/utils/image/index.d.ts.map +1 -0
  92. package/dist/utils/image/index.js +77 -0
  93. package/dist/utils/image/index.js.map +1 -0
  94. package/dist/utils/image/param-registry.d.ts +63 -0
  95. package/dist/utils/image/param-registry.d.ts.map +1 -0
  96. package/dist/utils/image/param-registry.js +196 -0
  97. package/dist/utils/image/param-registry.js.map +1 -0
  98. package/dist/utils/image/quality.d.ts +8 -0
  99. package/dist/utils/image/quality.d.ts.map +1 -0
  100. package/dist/utils/image/quality.js +9 -0
  101. package/dist/utils/image/quality.js.map +1 -0
  102. package/dist/utils/image/resize.d.ts +7 -0
  103. package/dist/utils/image/resize.d.ts.map +1 -0
  104. package/dist/utils/image/resize.js +62 -0
  105. package/dist/utils/image/resize.js.map +1 -0
  106. package/dist/utils/image/rotation.d.ts +6 -0
  107. package/dist/utils/image/rotation.d.ts.map +1 -0
  108. package/dist/utils/image/rotation.js +20 -0
  109. package/dist/utils/image/rotation.js.map +1 -0
  110. package/dist/utils/image/round-corners.d.ts +12 -0
  111. package/dist/utils/image/round-corners.d.ts.map +1 -0
  112. package/dist/utils/image/round-corners.js +102 -0
  113. package/dist/utils/image/round-corners.js.map +1 -0
  114. package/dist/utils/image/types.d.ts +2 -0
  115. package/dist/utils/image/types.d.ts.map +1 -0
  116. package/dist/utils/image/types.js +2 -0
  117. package/dist/utils/image/types.js.map +1 -0
  118. package/dist/utils/logger.d.ts +21 -0
  119. package/dist/utils/logger.d.ts.map +1 -0
  120. package/dist/utils/logger.js +82 -0
  121. package/dist/utils/logger.js.map +1 -0
  122. package/dist/utils/parser.d.ts +8 -0
  123. package/dist/utils/parser.d.ts.map +1 -0
  124. package/dist/utils/parser.js +226 -0
  125. package/dist/utils/parser.js.map +1 -0
  126. package/dist/utils/signature.d.ts +35 -0
  127. package/dist/utils/signature.d.ts.map +1 -0
  128. package/dist/utils/signature.js +100 -0
  129. package/dist/utils/signature.js.map +1 -0
  130. package/dist/utils/storage/cache.d.ts +41 -0
  131. package/dist/utils/storage/cache.d.ts.map +1 -0
  132. package/dist/utils/storage/cache.js +90 -0
  133. package/dist/utils/storage/cache.js.map +1 -0
  134. package/dist/utils/storage/cloud-storage.d.ts +188 -0
  135. package/dist/utils/storage/cloud-storage.d.ts.map +1 -0
  136. package/dist/utils/storage/cloud-storage.js +514 -0
  137. package/dist/utils/storage/cloud-storage.js.map +1 -0
  138. package/dist/utils/storage/factory.d.ts +10 -0
  139. package/dist/utils/storage/factory.d.ts.map +1 -0
  140. package/dist/utils/storage/factory.js +17 -0
  141. package/dist/utils/storage/factory.js.map +1 -0
  142. package/dist/utils/storage/index.d.ts +7 -0
  143. package/dist/utils/storage/index.d.ts.map +1 -0
  144. package/dist/utils/storage/index.js +8 -0
  145. package/dist/utils/storage/index.js.map +1 -0
  146. package/dist/utils/storage/key-generator.d.ts +11 -0
  147. package/dist/utils/storage/key-generator.d.ts.map +1 -0
  148. package/dist/utils/storage/key-generator.js +23 -0
  149. package/dist/utils/storage/key-generator.js.map +1 -0
  150. package/dist/utils/storage/listing-cache.d.ts +14 -0
  151. package/dist/utils/storage/listing-cache.d.ts.map +1 -0
  152. package/dist/utils/storage/listing-cache.js +29 -0
  153. package/dist/utils/storage/listing-cache.js.map +1 -0
  154. package/dist/utils/storage/s3-client.d.ts +97 -0
  155. package/dist/utils/storage/s3-client.d.ts.map +1 -0
  156. package/dist/utils/storage/s3-client.js +297 -0
  157. package/dist/utils/storage/s3-client.js.map +1 -0
  158. package/dist/utils/storage/stats-tracker.d.ts +45 -0
  159. package/dist/utils/storage/stats-tracker.d.ts.map +1 -0
  160. package/dist/utils/storage/stats-tracker.js +97 -0
  161. package/dist/utils/storage/stats-tracker.js.map +1 -0
  162. package/dist/utils/storage/types.d.ts +2 -0
  163. package/dist/utils/storage/types.d.ts.map +1 -0
  164. package/dist/utils/storage/types.js +2 -0
  165. package/dist/utils/storage/types.js.map +1 -0
  166. package/dist/utils/storage-level.d.ts +66 -0
  167. package/dist/utils/storage-level.d.ts.map +1 -0
  168. package/dist/utils/storage-level.js +106 -0
  169. package/dist/utils/storage-level.js.map +1 -0
  170. package/dist/utils/storage-tree.d.ts +30 -0
  171. package/dist/utils/storage-tree.d.ts.map +1 -0
  172. package/dist/utils/storage-tree.js +81 -0
  173. package/dist/utils/storage-tree.js.map +1 -0
  174. package/dist/utils/upload-signature.d.ts +25 -0
  175. package/dist/utils/upload-signature.d.ts.map +1 -0
  176. package/dist/utils/upload-signature.js +56 -0
  177. package/dist/utils/upload-signature.js.map +1 -0
  178. package/dist/utils/video/auto-downscale.d.ts +12 -0
  179. package/dist/utils/video/auto-downscale.d.ts.map +1 -0
  180. package/dist/utils/video/auto-downscale.js +29 -0
  181. package/dist/utils/video/auto-downscale.js.map +1 -0
  182. package/dist/utils/video/command-builder.d.ts +22 -0
  183. package/dist/utils/video/command-builder.d.ts.map +1 -0
  184. package/dist/utils/video/command-builder.js +79 -0
  185. package/dist/utils/video/command-builder.js.map +1 -0
  186. package/dist/utils/video/config.d.ts +51 -0
  187. package/dist/utils/video/config.d.ts.map +1 -0
  188. package/dist/utils/video/config.js +170 -0
  189. package/dist/utils/video/config.js.map +1 -0
  190. package/dist/utils/video/format.d.ts +22 -0
  191. package/dist/utils/video/format.d.ts.map +1 -0
  192. package/dist/utils/video/format.js +64 -0
  193. package/dist/utils/video/format.js.map +1 -0
  194. package/dist/utils/video/index.d.ts +25 -0
  195. package/dist/utils/video/index.d.ts.map +1 -0
  196. package/dist/utils/video/index.js +104 -0
  197. package/dist/utils/video/index.js.map +1 -0
  198. package/dist/utils/video/param-registry.d.ts +64 -0
  199. package/dist/utils/video/param-registry.d.ts.map +1 -0
  200. package/dist/utils/video/param-registry.js +201 -0
  201. package/dist/utils/video/param-registry.js.map +1 -0
  202. package/dist/utils/video/quality.d.ts +12 -0
  203. package/dist/utils/video/quality.d.ts.map +1 -0
  204. package/dist/utils/video/quality.js +45 -0
  205. package/dist/utils/video/quality.js.map +1 -0
  206. package/dist/utils/video/queue-store.d.ts +51 -0
  207. package/dist/utils/video/queue-store.d.ts.map +1 -0
  208. package/dist/utils/video/queue-store.js +2 -0
  209. package/dist/utils/video/queue-store.js.map +1 -0
  210. package/dist/utils/video/resize.d.ts +7 -0
  211. package/dist/utils/video/resize.d.ts.map +1 -0
  212. package/dist/utils/video/resize.js +40 -0
  213. package/dist/utils/video/resize.js.map +1 -0
  214. package/dist/utils/video/sqlite-video-job-store.d.ts +29 -0
  215. package/dist/utils/video/sqlite-video-job-store.d.ts.map +1 -0
  216. package/dist/utils/video/sqlite-video-job-store.js +320 -0
  217. package/dist/utils/video/sqlite-video-job-store.js.map +1 -0
  218. package/dist/utils/video/thumbnail.d.ts +7 -0
  219. package/dist/utils/video/thumbnail.d.ts.map +1 -0
  220. package/dist/utils/video/thumbnail.js +22 -0
  221. package/dist/utils/video/thumbnail.js.map +1 -0
  222. package/dist/utils/video/trim.d.ts +7 -0
  223. package/dist/utils/video/trim.d.ts.map +1 -0
  224. package/dist/utils/video/trim.js +36 -0
  225. package/dist/utils/video/trim.js.map +1 -0
  226. package/dist/utils/video/types.d.ts +19 -0
  227. package/dist/utils/video/types.d.ts.map +1 -0
  228. package/dist/utils/video/types.js +2 -0
  229. package/dist/utils/video/types.js.map +1 -0
  230. package/dist/utils/video/video-info.d.ts +25 -0
  231. package/dist/utils/video/video-info.d.ts.map +1 -0
  232. package/dist/utils/video/video-info.js +58 -0
  233. package/dist/utils/video/video-info.js.map +1 -0
  234. package/dist/utils/video/video-worker.d.ts +52 -0
  235. package/dist/utils/video/video-worker.d.ts.map +1 -0
  236. package/dist/utils/video/video-worker.js +192 -0
  237. package/dist/utils/video/video-worker.js.map +1 -0
  238. package/dist/utils/video-job-queue.d.ts +62 -0
  239. package/dist/utils/video-job-queue.d.ts.map +1 -0
  240. package/dist/utils/video-job-queue.js +122 -0
  241. package/dist/utils/video-job-queue.js.map +1 -0
  242. package/package.json +51 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/config/deps.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ export { TransformService, type TransformRequest, type TransformResult, type CacheCheckResult, } from "./services/transform.service";
2
+ export { VideoJobQueue, type VideoJob, type JobStatus } from "./utils/video-job-queue";
3
+ export type { VideoJobStore, VideoJob as VideoJobRecord, JobStats, } from "./utils/video/queue-store";
4
+ export { SqliteVideoJobStore } from "./utils/video/sqlite-video-job-store";
5
+ export { THUMBNAIL_PRIORITY, TRANSFORMATION_PRIORITY, LOW_PRIORITY, } from "./utils/video/config";
6
+ export { CloudStorage, StorageCache, S3ClientWrapper, KeyGenerator, createStorageClient, createOptimizedStorageClient, type StorageConfig, type CacheEntry, } from "./utils/storage/index";
7
+ export type { StorageClientOptions } from "./types";
8
+ export { invalidateListingCache } from "./utils/storage/listing-cache";
9
+ export type { RouteDeps } from "./config/deps";
10
+ export { createTransformRoute } from "./routes/transform";
11
+ export { createAuthenticatedRoute } from "./routes/authenticated";
12
+ export { createStorageRoute } from "./routes/storage";
13
+ export { createDownloadRoute } from "./routes/download";
14
+ export { createDownloadFolderRoute } from "./routes/download-folder";
15
+ export { createDownloadZipRoute } from "./routes/download-zip";
16
+ export { createVideoStatusRoute } from "./routes/video-status";
17
+ export { createQueueEventsRoute } from "./routes/queue-events";
18
+ export { createQueueRoute } from "./routes/queue";
19
+ export { createInvalidateRoute } from "./routes/invalidate";
20
+ export { getUniqueFilePath } from "./utils/get-unique-file-path";
21
+ export { getCachePath } from "./utils/cache";
22
+ export { parseParams } from "./utils/parser";
23
+ export { validateApiSecret } from "./utils/signature";
24
+ export { generateUploadSignature, verifyUploadSignature } from "./utils/upload-signature";
25
+ export { deleteAssetCompletely, type DeleteAssetResult } from "./utils/asset-deletion";
26
+ export { default as logger, serializeError } from "./utils/logger";
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACvF,YAAY,EACV,aAAa,EACb,QAAQ,IAAI,cAAc,EAC1B,QAAQ,GACT,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,YAAY,GACb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,4BAA4B,EAC5B,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAGvE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ // Transform engine
2
+ export { TransformService, } from "./services/transform.service.js";
3
+ // Video job queue
4
+ export { VideoJobQueue } from "./utils/video-job-queue.js";
5
+ export { SqliteVideoJobStore } from "./utils/video/sqlite-video-job-store.js";
6
+ export { THUMBNAIL_PRIORITY, TRANSFORMATION_PRIORITY, LOW_PRIORITY, } from "./utils/video/config.js";
7
+ // Storage
8
+ export { CloudStorage, StorageCache, S3ClientWrapper, KeyGenerator, createStorageClient, createOptimizedStorageClient, } from "./utils/storage/index.js";
9
+ export { invalidateListingCache } from "./utils/storage/listing-cache.js";
10
+ export { createTransformRoute } from "./routes/transform.js";
11
+ export { createAuthenticatedRoute } from "./routes/authenticated.js";
12
+ export { createStorageRoute } from "./routes/storage.js";
13
+ export { createDownloadRoute } from "./routes/download.js";
14
+ export { createDownloadFolderRoute } from "./routes/download-folder.js";
15
+ export { createDownloadZipRoute } from "./routes/download-zip.js";
16
+ export { createVideoStatusRoute } from "./routes/video-status.js";
17
+ export { createQueueEventsRoute } from "./routes/queue-events.js";
18
+ export { createQueueRoute } from "./routes/queue.js";
19
+ export { createInvalidateRoute } from "./routes/invalidate.js";
20
+ // Shared utilities
21
+ export { getUniqueFilePath } from "./utils/get-unique-file-path.js";
22
+ export { getCachePath } from "./utils/cache.js";
23
+ export { parseParams } from "./utils/parser.js";
24
+ export { validateApiSecret } from "./utils/signature.js";
25
+ export { generateUploadSignature, verifyUploadSignature } from "./utils/upload-signature.js";
26
+ export { deleteAssetCompletely } from "./utils/asset-deletion.js";
27
+ export { default as logger, serializeError } from "./utils/logger.js";
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,OAAO,EACL,gBAAgB,GAIjB,MAAM,8BAA8B,CAAC;AAEtC,kBAAkB;AAClB,OAAO,EAAE,aAAa,EAAiC,MAAM,yBAAyB,CAAC;AAMvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,YAAY,GACb,MAAM,sBAAsB,CAAC;AAE9B,UAAU;AACV,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,4BAA4B,GAG7B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAIvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,mBAAmB;AACnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAA0B,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Hono } from "hono";
2
+ import type { RouteDeps } from "../config/deps";
3
+ export declare function createAuthenticatedRoute(deps: RouteDeps): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ //# sourceMappingURL=authenticated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated.d.ts","sourceRoot":"","sources":["../../src/routes/authenticated.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAYhD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS,8EAwKvD"}
@@ -0,0 +1,130 @@
1
+ import { Hono } from "hono";
2
+ import { TransformService } from "../services/transform.service.js";
3
+ import logger, { serializeError } from "../utils/logger.js";
4
+ import { verifySignature, SIGNATURE_LENGTH, sanitizeFilePath, } from "../utils/signature.js";
5
+ import { isTransformSegment } from "../utils/parser.js";
6
+ // Get API_SECRET from environment variables
7
+ const API_SECRET = process.env.API_SECRET;
8
+ export function createAuthenticatedRoute(deps) {
9
+ const transformService = new TransformService(deps.storage, deps.queue);
10
+ const t = new Hono();
11
+ t.get("/*", async (c) => {
12
+ const path = c.req.path;
13
+ const userAgent = c.req.header("User-Agent") ?? "";
14
+ const acceptHeader = c.req.header("Accept");
15
+ try {
16
+ // Parse the authenticated URL format: /s--{signature}/{transformations}/{route}
17
+ const segments = path.split("/").slice(2); // Remove '/authenticated' prefix
18
+ if (segments.length < 2) {
19
+ return c.text("Invalid authenticated URL format. Expected: /s--{signature}/{transformations}/{route}", 400);
20
+ }
21
+ // Extract signature from first segment (format: s--{signature})
22
+ const firstSegment = segments[0];
23
+ if (!firstSegment.startsWith("s--")) {
24
+ return c.text("Invalid signature format. Expected: s--{signature}", 400);
25
+ }
26
+ const signature = firstSegment.slice(3); // Remove 's--' prefix
27
+ if (signature.length !== SIGNATURE_LENGTH) {
28
+ return c.text(`Invalid signature length. Expected ${SIGNATURE_LENGTH} characters.`, 400);
29
+ }
30
+ // Extract transformations and route from remaining segments
31
+ const routeSegments = segments.slice(1);
32
+ if (routeSegments.length < 1) {
33
+ return c.text("No route specified.", 400);
34
+ }
35
+ // Determine transformation string and file path
36
+ // Format: {transformations}/{route}
37
+ const hasTransform = routeSegments.length > 0 && isTransformSegment(routeSegments[0]);
38
+ const transformations = hasTransform ? routeSegments[0] : "";
39
+ const filePathSegments = hasTransform
40
+ ? routeSegments.slice(1)
41
+ : routeSegments;
42
+ const filePath = filePathSegments.join("/");
43
+ if (!filePath) {
44
+ return c.text("No file path specified.", 400);
45
+ }
46
+ // Sanitize the file path to prevent path traversal attacks
47
+ let sanitizedFilePath;
48
+ try {
49
+ sanitizedFilePath = sanitizeFilePath(filePath);
50
+ }
51
+ catch (error) {
52
+ logger.warn({
53
+ path,
54
+ filePath,
55
+ error,
56
+ }, "Path traversal attempt detected in authenticated request");
57
+ return c.text("Invalid file path", 400);
58
+ }
59
+ // Verify the signature using timing-safe comparison and HMAC-SHA256
60
+ if (!API_SECRET) {
61
+ return c.text("API_SECRET not configured on server.", 500);
62
+ }
63
+ const isValidSignature = verifySignature(signature, transformations, sanitizedFilePath, API_SECRET);
64
+ if (!isValidSignature) {
65
+ logger.warn({
66
+ path,
67
+ signature,
68
+ transformations,
69
+ filePath: sanitizedFilePath,
70
+ }, "Invalid signature for authenticated request");
71
+ return c.text("Invalid signature", 401);
72
+ }
73
+ // Construct the path for the transform service using the sanitized path
74
+ // Format: /t/{transformations}/{filePath}
75
+ const transformPath = `/t/${transformations ? `${transformations}/` : ""}${sanitizedFilePath}`;
76
+ // Use the transform service with the constructed path
77
+ const result = await transformService.transform({
78
+ path: transformPath,
79
+ userAgent,
80
+ acceptHeader,
81
+ context: c,
82
+ });
83
+ // Set response headers
84
+ Object.entries(result.headers).forEach(([key, value]) => {
85
+ c.header(key, value);
86
+ });
87
+ // Determine content type if not set in headers
88
+ if (!result.contentType) {
89
+ // Extract file extension from sanitized path
90
+ const ext = sanitizedFilePath.split(".").pop()?.toLowerCase();
91
+ const contentTypeMap = {
92
+ jpg: "image/jpeg",
93
+ jpeg: "image/jpeg",
94
+ png: "image/png",
95
+ webp: "image/webp",
96
+ avif: "image/avif",
97
+ gif: "image/gif",
98
+ mp4: "video/mp4",
99
+ mov: "video/quicktime",
100
+ webm: "video/webm",
101
+ };
102
+ const contentType = contentTypeMap[ext || ""] || "application/octet-stream";
103
+ c.header("Content-Type", contentType);
104
+ }
105
+ else {
106
+ c.header("Content-Type", result.contentType);
107
+ }
108
+ // Large payloads (e.g. original videos during processing) are streamed
109
+ if (result.stream) {
110
+ return c.body(result.stream);
111
+ }
112
+ // Check if this is an error response
113
+ if (result.contentType === "text/plain" &&
114
+ result.buffer.toString().includes("failed")) {
115
+ const errorMessage = result.buffer.toString();
116
+ if (errorMessage.includes("File not found")) {
117
+ return c.text(errorMessage, 404);
118
+ }
119
+ return c.text(errorMessage, 500);
120
+ }
121
+ return c.body(new Uint8Array(result.buffer));
122
+ }
123
+ catch (error) {
124
+ logger.error({ error: serializeError(error), path }, "Authenticated transform route error");
125
+ return c.text("Internal server error", 500);
126
+ }
127
+ });
128
+ return t;
129
+ }
130
+ //# sourceMappingURL=authenticated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated.js","sourceRoot":"","sources":["../../src/routes/authenticated.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Hono } from "hono";
2
+ import type { RouteDeps } from "../config/deps";
3
+ export declare function createDownloadFolderRoute(deps: RouteDeps): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ //# sourceMappingURL=download-folder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-folder.d.ts","sourceRoot":"","sources":["../../src/routes/download-folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAwBhD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,SAAS,8EAoGxD"}
@@ -0,0 +1,101 @@
1
+ import { Hono } from "hono";
2
+ import { zipSync } from "fflate";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import logger, { serializeError } from "../utils/logger.js";
6
+ function collectLocalFiles(dir, base) {
7
+ const results = [];
8
+ if (!fs.existsSync(dir))
9
+ return results;
10
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
11
+ const full = path.join(dir, entry.name);
12
+ const rel = path.join(base, entry.name);
13
+ if (entry.isDirectory()) {
14
+ results.push(...collectLocalFiles(full, rel));
15
+ }
16
+ else {
17
+ results.push({ arcPath: rel.replace(/\\/g, "/"), fullPath: full });
18
+ }
19
+ }
20
+ return results;
21
+ }
22
+ export function createDownloadFolderRoute(deps) {
23
+ const { storage } = deps;
24
+ const downloadFolder = new Hono();
25
+ /**
26
+ * GET /download-folder/:path
27
+ * Returns all files under the given folder path as a ZIP archive.
28
+ */
29
+ downloadFolder.get("/*", async (c) => {
30
+ const requestPath = c.req.path;
31
+ const rawFolderPath = requestPath.replace(/^\/download-folder\/?/, "");
32
+ if (!rawFolderPath) {
33
+ return c.text("Folder path is required", 400);
34
+ }
35
+ let folderPath;
36
+ try {
37
+ folderPath = decodeURIComponent(rawFolderPath)
38
+ .replace(/\.\./g, "")
39
+ .replace(/\\/g, "/")
40
+ .replace(/\/+/g, "/")
41
+ .replace(/^\/+/, "")
42
+ .replace(/\/+$/, "");
43
+ }
44
+ catch {
45
+ return c.text("Invalid folder path", 400);
46
+ }
47
+ if (!folderPath) {
48
+ return c.text("Invalid folder path", 400);
49
+ }
50
+ const folderName = folderPath.split("/").pop() ?? folderPath;
51
+ try {
52
+ const zipFiles = {};
53
+ if (storage) {
54
+ // Cloud storage: list all objects under the prefix
55
+ const prefix = `public/${folderPath}/`;
56
+ const objects = await storage.list(prefix);
57
+ if (objects.length === 0) {
58
+ return c.text("Folder not found or empty", 404);
59
+ }
60
+ for (const obj of objects) {
61
+ // obj.key looks like "public/videos/clip.mp4"
62
+ const relPath = obj.key.replace(`public/${folderPath}/`, "");
63
+ // Skip folder marker objects (empty keys or keys ending in /)
64
+ if (!relPath || relPath.endsWith("/"))
65
+ continue;
66
+ const buffer = await storage.downloadOriginal(`${folderPath}/${relPath}`);
67
+ zipFiles[relPath] = new Uint8Array(buffer);
68
+ }
69
+ }
70
+ else {
71
+ // Local storage
72
+ const localDir = path.join("./public", folderPath);
73
+ if (!fs.existsSync(localDir) || !fs.statSync(localDir).isDirectory()) {
74
+ return c.text("Folder not found", 404);
75
+ }
76
+ const entries = collectLocalFiles(localDir, "");
77
+ if (entries.length === 0) {
78
+ return c.text("Folder is empty", 404);
79
+ }
80
+ for (const entry of entries) {
81
+ zipFiles[entry.arcPath] = new Uint8Array(fs.readFileSync(entry.fullPath));
82
+ }
83
+ }
84
+ if (Object.keys(zipFiles).length === 0) {
85
+ return c.text("Folder is empty", 404);
86
+ }
87
+ const zipBuffer = zipSync(zipFiles);
88
+ c.header("Content-Type", "application/zip");
89
+ c.header("Content-Disposition", `attachment; filename="${encodeURIComponent(folderName)}.zip"`);
90
+ c.header("Content-Length", zipBuffer.length.toString());
91
+ c.header("Cache-Control", "private, no-store");
92
+ return c.body(zipBuffer);
93
+ }
94
+ catch (error) {
95
+ logger.error({ error: serializeError(error), folderPath }, "Folder download failed");
96
+ return c.text("Internal server error", 500);
97
+ }
98
+ });
99
+ return downloadFolder;
100
+ }
101
+ //# sourceMappingURL=download-folder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-folder.js","sourceRoot":"","sources":["../../src/routes/download-folder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { Hono } from "hono";
2
+ import type { RouteDeps } from "../config/deps";
3
+ /**
4
+ * POST /download-zip
5
+ * body: { items: { path: string; kind?: "file" | "folder" }[] } or { paths: string[] }
6
+ * Bundles multiple files and/or folders (mixed) into a single ZIP archive.
7
+ * Entries keep their full relative path so items with the same filename in
8
+ * different folders don't collide inside the archive.
9
+ *
10
+ * When the caller already knows an item's kind, passing it via `items` skips
11
+ * the folderExists probe (an extra HeadObject + ListObjects round trip per
12
+ * item) and lets every item download in parallel instead of one at a time.
13
+ */
14
+ export declare function createDownloadZipRoute(deps: RouteDeps): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
15
+ //# sourceMappingURL=download-zip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-zip.d.ts","sourceRoot":"","sources":["../../src/routes/download-zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAqChD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,8EAwIrD"}
@@ -0,0 +1,147 @@
1
+ import { Hono } from "hono";
2
+ import { zipSync } from "fflate";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import logger, { serializeError } from "../utils/logger.js";
6
+ const MAX_PATHS = 200;
7
+ function sanitizePath(raw) {
8
+ return decodeURIComponent(raw)
9
+ .replace(/\.\./g, "")
10
+ .replace(/\\/g, "/")
11
+ .replace(/\/+/g, "/")
12
+ .replace(/^\/+/, "")
13
+ .replace(/\/+$/, "");
14
+ }
15
+ function collectLocalFiles(dir, base) {
16
+ const results = [];
17
+ if (!fs.existsSync(dir))
18
+ return results;
19
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
20
+ const full = path.join(dir, entry.name);
21
+ const rel = path.join(base, entry.name);
22
+ if (entry.isDirectory()) {
23
+ results.push(...collectLocalFiles(full, rel));
24
+ }
25
+ else {
26
+ results.push({ arcPath: rel.replace(/\\/g, "/"), fullPath: full });
27
+ }
28
+ }
29
+ return results;
30
+ }
31
+ /**
32
+ * POST /download-zip
33
+ * body: { items: { path: string; kind?: "file" | "folder" }[] } or { paths: string[] }
34
+ * Bundles multiple files and/or folders (mixed) into a single ZIP archive.
35
+ * Entries keep their full relative path so items with the same filename in
36
+ * different folders don't collide inside the archive.
37
+ *
38
+ * When the caller already knows an item's kind, passing it via `items` skips
39
+ * the folderExists probe (an extra HeadObject + ListObjects round trip per
40
+ * item) and lets every item download in parallel instead of one at a time.
41
+ */
42
+ export function createDownloadZipRoute(deps) {
43
+ const { storage } = deps;
44
+ const downloadZip = new Hono();
45
+ downloadZip.post("/", async (c) => {
46
+ let body = {};
47
+ try {
48
+ body = await c.req.json();
49
+ }
50
+ catch {
51
+ return c.json({ error: "Bad request", message: "Invalid JSON body" }, 400);
52
+ }
53
+ const items = Array.isArray(body.items)
54
+ ? body.items
55
+ .filter((i) => typeof i === "object" &&
56
+ i !== null &&
57
+ typeof i.path === "string")
58
+ .map((i) => ({
59
+ path: i.path,
60
+ kind: i.kind === "file" || i.kind === "folder" ? i.kind : undefined,
61
+ }))
62
+ : Array.isArray(body.paths)
63
+ ? body.paths
64
+ .filter((p) => typeof p === "string")
65
+ .map((path) => ({ path }))
66
+ : [];
67
+ if (items.length === 0) {
68
+ return c.json({
69
+ error: "Bad request",
70
+ message: "A non-empty 'items' or 'paths' array is required",
71
+ }, 400);
72
+ }
73
+ if (items.length > MAX_PATHS) {
74
+ return c.json({
75
+ error: "Bad request",
76
+ message: `Too many items requested (max ${MAX_PATHS})`,
77
+ }, 400);
78
+ }
79
+ try {
80
+ const zipFiles = {};
81
+ const addFolder = async (cleanPath) => {
82
+ const prefix = `public/${cleanPath}/`;
83
+ const objects = await storage.list(prefix);
84
+ await Promise.all(objects.map(async (obj) => {
85
+ const relPath = obj.key.replace(`public/${cleanPath}/`, "");
86
+ if (!relPath || relPath.endsWith("/"))
87
+ return;
88
+ const buffer = await storage.downloadOriginal(`${cleanPath}/${relPath}`);
89
+ zipFiles[`${cleanPath}/${relPath}`] = new Uint8Array(buffer);
90
+ }));
91
+ };
92
+ await Promise.all(items.map(async ({ path: raw, kind }) => {
93
+ const cleanPath = sanitizePath(raw);
94
+ if (!cleanPath)
95
+ return;
96
+ if (storage) {
97
+ const isFolder = kind
98
+ ? kind === "folder"
99
+ : await storage.folderExists(cleanPath);
100
+ if (isFolder) {
101
+ await addFolder(cleanPath);
102
+ }
103
+ else {
104
+ try {
105
+ const buffer = await storage.downloadOriginal(cleanPath);
106
+ zipFiles[cleanPath] = new Uint8Array(buffer);
107
+ }
108
+ catch {
109
+ // Skip missing files rather than failing the whole archive
110
+ }
111
+ }
112
+ }
113
+ else {
114
+ const localPath = path.join("./public", cleanPath);
115
+ if (!fs.existsSync(localPath))
116
+ return;
117
+ if (fs.statSync(localPath).isDirectory()) {
118
+ for (const entry of collectLocalFiles(localPath, cleanPath)) {
119
+ zipFiles[entry.arcPath] = new Uint8Array(fs.readFileSync(entry.fullPath));
120
+ }
121
+ }
122
+ else {
123
+ zipFiles[cleanPath] = new Uint8Array(fs.readFileSync(localPath));
124
+ }
125
+ }
126
+ }));
127
+ if (Object.keys(zipFiles).length === 0) {
128
+ return c.json({ error: "Not found", message: "No files found for the given paths" }, 404);
129
+ }
130
+ const zipBuffer = zipSync(zipFiles);
131
+ c.header("Content-Type", "application/zip");
132
+ c.header("Content-Disposition", `attachment; filename="download.zip"`);
133
+ c.header("Content-Length", zipBuffer.length.toString());
134
+ c.header("Cache-Control", "private, no-store");
135
+ return c.body(zipBuffer);
136
+ }
137
+ catch (error) {
138
+ logger.error({ error: serializeError(error) }, "Bulk zip download failed");
139
+ return c.json({
140
+ error: "Internal server error",
141
+ message: error instanceof Error ? error.message : "Unknown error",
142
+ }, 500);
143
+ }
144
+ });
145
+ return downloadZip;
146
+ }
147
+ //# sourceMappingURL=download-zip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-zip.js","sourceRoot":"","sources":["../../src/routes/download-zip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Hono } from "hono";
2
+ import type { RouteDeps } from "../config/deps";
3
+ export declare function createDownloadRoute(deps: RouteDeps): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ //# sourceMappingURL=download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/routes/download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKhD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,8EA0FlD"}
@@ -0,0 +1,83 @@
1
+ import { Hono } from "hono";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import logger, { serializeError } from "../utils/logger.js";
5
+ export function createDownloadRoute(deps) {
6
+ const { storage } = deps;
7
+ const download = new Hono();
8
+ /**
9
+ * GET /download/:path
10
+ * Serves the original stored file as an attachment (no transformation applied).
11
+ * Public route, consistent with /t/* which is also public.
12
+ */
13
+ download.get("/*", async (c) => {
14
+ const requestPath = c.req.path;
15
+ // Strip leading /download/ prefix
16
+ const rawFilePath = requestPath.replace(/^\/download\/?/, "");
17
+ if (!rawFilePath) {
18
+ return c.text("File path is required", 400);
19
+ }
20
+ // Decode and sanitise path (prevent directory traversal)
21
+ let filePath;
22
+ try {
23
+ filePath = decodeURIComponent(rawFilePath)
24
+ .replace(/\.\./g, "")
25
+ .replace(/\\/g, "/")
26
+ .replace(/\/+/g, "/")
27
+ .replace(/^\/+/, "");
28
+ }
29
+ catch {
30
+ return c.text("Invalid file path", 400);
31
+ }
32
+ if (!filePath) {
33
+ return c.text("Invalid file path", 400);
34
+ }
35
+ const filename = path.basename(filePath);
36
+ try {
37
+ let buffer;
38
+ if (storage) {
39
+ // Cloud storage
40
+ try {
41
+ buffer = await storage.downloadOriginal(filePath);
42
+ }
43
+ catch {
44
+ return c.text("File not found", 404);
45
+ }
46
+ }
47
+ else {
48
+ // Local storage
49
+ const localPath = path.join("./public", filePath);
50
+ if (!fs.existsSync(localPath) || fs.statSync(localPath).isDirectory()) {
51
+ return c.text("File not found", 404);
52
+ }
53
+ buffer = fs.readFileSync(localPath);
54
+ }
55
+ // Derive a safe Content-Type from the extension
56
+ const ext = filename.split(".").pop()?.toLowerCase() ?? "";
57
+ const contentTypeMap = {
58
+ jpg: "image/jpeg",
59
+ jpeg: "image/jpeg",
60
+ png: "image/png",
61
+ webp: "image/webp",
62
+ avif: "image/avif",
63
+ gif: "image/gif",
64
+ psd: "image/vnd.adobe.photoshop",
65
+ mp4: "video/mp4",
66
+ mov: "video/quicktime",
67
+ webm: "video/webm",
68
+ };
69
+ const contentType = contentTypeMap[ext] ?? "application/octet-stream";
70
+ c.header("Content-Type", contentType);
71
+ c.header("Content-Disposition", `attachment; filename="${encodeURIComponent(filename)}"`);
72
+ c.header("Content-Length", buffer.length.toString());
73
+ c.header("Cache-Control", "private, no-store");
74
+ return c.body(new Uint8Array(buffer));
75
+ }
76
+ catch (error) {
77
+ logger.error({ error: serializeError(error), filePath }, "Download failed");
78
+ return c.text("Internal server error", 500);
79
+ }
80
+ });
81
+ return download;
82
+ }
83
+ //# sourceMappingURL=download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/routes/download.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Hono } from "hono";
2
+ import type { RouteDeps } from "../config/deps";
3
+ export declare function createInvalidateRoute(deps: RouteDeps): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ //# sourceMappingURL=invalidate.d.ts.map