@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 @@
1
+ {"version":3,"file":"listing-cache.d.ts","sourceRoot":"","sources":["../../../src/utils/storage/listing-cache.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AASxE;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC,GACrC,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
@@ -0,0 +1,29 @@
1
+ const LISTING_CACHE_TTL = 60 * 1000;
2
+ // Module-level singleton: storage.ts and upload.ts each create their own
3
+ // CloudStorage instance, so the shared listing cache cannot live on the class
4
+ let entry = null;
5
+ /**
6
+ * Returns the cached full bucket listing, fetching it at most once per TTL.
7
+ * The promise itself is cached so concurrent callers (e.g. GET /storage and
8
+ * GET /storage/stats firing together on dashboard load) share one S3 fan-out.
9
+ */
10
+ export function getCachedFullListing(fetcher) {
11
+ if (entry && Date.now() - entry.timestamp < LISTING_CACHE_TTL) {
12
+ return entry.promise;
13
+ }
14
+ const current = {
15
+ promise: fetcher(),
16
+ timestamp: Date.now(),
17
+ };
18
+ entry = current;
19
+ current.promise.catch(() => {
20
+ if (entry === current) {
21
+ entry = null;
22
+ }
23
+ });
24
+ return current.promise;
25
+ }
26
+ export function invalidateListingCache() {
27
+ entry = null;
28
+ }
29
+ //# sourceMappingURL=listing-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listing-cache.js","sourceRoot":"","sources":["../../../src/utils/storage/listing-cache.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpC,yEAAyE;AACzE,8EAA8E;AAC9E,IAAI,KAAK,GACP,IAAI,CAAC;AAEP;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAsC;IAEtC,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,iBAAiB,EAAE,CAAC;QAC9D,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,MAAM,OAAO,GAA4D;QACvE,OAAO,EAAE,OAAO,EAAE;QAClB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,KAAK,GAAG,OAAO,CAAC;IAEhB,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;QACzB,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YACtB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,OAAO,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,KAAK,GAAG,IAAI,CAAC;AACf,CAAC"}
@@ -0,0 +1,97 @@
1
+ import { StorageConfig, StorageClientOptions } from "./types";
2
+ export declare class S3ClientWrapper {
3
+ private s3Client;
4
+ private config;
5
+ constructor(config: StorageConfig, clientOptions?: StorageClientOptions);
6
+ /**
7
+ * Checks if an object exists in the bucket
8
+ */
9
+ objectExists(key: string): Promise<boolean>;
10
+ /**
11
+ * Downloads an object from the bucket
12
+ */
13
+ downloadObject(key: string): Promise<Buffer>;
14
+ /**
15
+ * Downloads an object as a stream, without buffering it in memory.
16
+ * Used to serve large files (e.g. original videos) directly to clients.
17
+ */
18
+ downloadObjectStream(key: string): Promise<{
19
+ stream: ReadableStream<Uint8Array>;
20
+ contentLength?: number;
21
+ contentType?: string;
22
+ }>;
23
+ /**
24
+ * Lists objects under an optional prefix
25
+ */
26
+ listObjects(prefix?: string, maxKeys?: number): Promise<{
27
+ key: string;
28
+ size?: number;
29
+ lastModified?: Date;
30
+ }[]>;
31
+ /**
32
+ * Lists a single page of one directory level using Delimiter: "/"
33
+ * Returns direct child prefixes (CommonPrefixes) and direct child objects
34
+ */
35
+ listDelimitedPage(prefix: string, maxKeys?: number, continuationToken?: string): Promise<{
36
+ prefixes: string[];
37
+ objects: {
38
+ key: string;
39
+ size?: number;
40
+ lastModified?: Date;
41
+ }[];
42
+ isTruncated: boolean;
43
+ nextToken?: string;
44
+ }>;
45
+ /**
46
+ * Lists one full directory level (all pages) using Delimiter: "/"
47
+ */
48
+ listDelimited(prefix: string): Promise<{
49
+ prefixes: string[];
50
+ objects: {
51
+ key: string;
52
+ size?: number;
53
+ lastModified?: Date;
54
+ }[];
55
+ }>;
56
+ /**
57
+ * Uploads an object to the bucket
58
+ */
59
+ uploadObject(key: string, buffer: Buffer, contentType: string, metadata?: Record<string, string>): Promise<void>;
60
+ /**
61
+ * Creates a zero-byte object to represent a folder in S3-compatible storage
62
+ */
63
+ createFolderMarker(key: string): Promise<void>;
64
+ /**
65
+ * Generates a signed URL for temporary access
66
+ */
67
+ getSignedUrl(key: string, expiresIn?: number): Promise<string>;
68
+ /**
69
+ * Generates public URL for the object
70
+ */
71
+ getPublicUrl(key: string): string;
72
+ /**
73
+ * Copies an object within the bucket to a new key
74
+ */
75
+ copyObject(sourceKey: string, destKey: string): Promise<void>;
76
+ /**
77
+ * Deletes an object from the bucket
78
+ */
79
+ deleteObject(key: string): Promise<void>;
80
+ /**
81
+ * Gets object metadata (size, lastModified) without downloading the file
82
+ */
83
+ getObjectMetadata(key: string): Promise<{
84
+ size: number;
85
+ lastModified: Date;
86
+ metadata?: Record<string, string>;
87
+ } | null>;
88
+ /**
89
+ * Deletes multiple objects from the bucket (up to 1000 at once)
90
+ */
91
+ deleteObjects(keys: string[]): Promise<number>;
92
+ /**
93
+ * Gets the bucket name
94
+ */
95
+ get bucketName(): string;
96
+ }
97
+ //# sourceMappingURL=s3-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-client.d.ts","sourceRoot":"","sources":["../../../src/utils/storage/s3-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,297 @@
1
+ import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, DeleteObjectCommand, DeleteObjectsCommand, CopyObjectCommand, } from "@aws-sdk/client-s3";
2
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
3
+ import https from "https";
4
+ export class S3ClientWrapper {
5
+ s3Client;
6
+ config;
7
+ constructor(config, clientOptions = {}) {
8
+ this.config = config;
9
+ // Configure HTTP handler with socket settings (caller resolves these,
10
+ // e.g. from environment variables in self-hosted mode)
11
+ const maxSockets = clientOptions.maxSockets ?? 50;
12
+ const connectionTimeout = clientOptions.connectionTimeout ?? 0;
13
+ const requestTimeout = clientOptions.requestTimeout ?? 0;
14
+ const socketTimeout = clientOptions.socketTimeout ?? 0;
15
+ const clientConfig = {
16
+ region: config.region,
17
+ credentials: {
18
+ accessKeyId: config.accessKeyId,
19
+ secretAccessKey: config.secretAccessKey,
20
+ },
21
+ ...(config.endpoint && {
22
+ endpoint: config.endpoint,
23
+ forcePathStyle: true, // Required for most S3-compatible providers
24
+ }),
25
+ requestHandler: {
26
+ httpsAgent: new https.Agent({
27
+ keepAlive: true,
28
+ maxSockets,
29
+ }),
30
+ ...(connectionTimeout > 0 && { connectionTimeout }),
31
+ ...(requestTimeout > 0 && { requestTimeout }),
32
+ ...(socketTimeout > 0 && { socketTimeout }),
33
+ },
34
+ };
35
+ this.s3Client = new S3Client(clientConfig);
36
+ }
37
+ /**
38
+ * Checks if an object exists in the bucket
39
+ */
40
+ async objectExists(key) {
41
+ try {
42
+ await this.s3Client.send(new HeadObjectCommand({
43
+ Bucket: this.config.bucketName,
44
+ Key: key,
45
+ }));
46
+ return true;
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ }
52
+ /**
53
+ * Downloads an object from the bucket
54
+ */
55
+ async downloadObject(key) {
56
+ const response = await this.s3Client.send(new GetObjectCommand({
57
+ Bucket: this.config.bucketName,
58
+ Key: key,
59
+ }));
60
+ if (!response.Body) {
61
+ throw new Error("File not found");
62
+ }
63
+ // Converts the stream to buffer
64
+ const chunks = [];
65
+ const reader = response.Body.transformToWebStream().getReader();
66
+ while (true) {
67
+ const { done, value } = await reader.read();
68
+ if (done)
69
+ break;
70
+ chunks.push(value);
71
+ }
72
+ return Buffer.concat(chunks);
73
+ }
74
+ /**
75
+ * Downloads an object as a stream, without buffering it in memory.
76
+ * Used to serve large files (e.g. original videos) directly to clients.
77
+ */
78
+ async downloadObjectStream(key) {
79
+ const response = await this.s3Client.send(new GetObjectCommand({
80
+ Bucket: this.config.bucketName,
81
+ Key: key,
82
+ }));
83
+ if (!response.Body) {
84
+ throw new Error("File not found");
85
+ }
86
+ return {
87
+ stream: response.Body.transformToWebStream(),
88
+ contentLength: response.ContentLength,
89
+ contentType: response.ContentType,
90
+ };
91
+ }
92
+ /**
93
+ * Lists objects under an optional prefix
94
+ */
95
+ async listObjects(prefix, maxKeys) {
96
+ const results = [];
97
+ let continuationToken;
98
+ do {
99
+ const response = await this.s3Client.send(new ListObjectsV2Command({
100
+ Bucket: this.config.bucketName,
101
+ Prefix: prefix,
102
+ MaxKeys: maxKeys,
103
+ ContinuationToken: continuationToken,
104
+ }));
105
+ if (response.Contents) {
106
+ for (const object of response.Contents) {
107
+ if (object.Key) {
108
+ results.push({
109
+ key: object.Key,
110
+ size: object.Size,
111
+ lastModified: object.LastModified,
112
+ });
113
+ }
114
+ }
115
+ }
116
+ continuationToken =
117
+ response.IsTruncated && (!maxKeys || results.length < maxKeys)
118
+ ? response.NextContinuationToken
119
+ : undefined;
120
+ } while (continuationToken);
121
+ return results;
122
+ }
123
+ /**
124
+ * Lists a single page of one directory level using Delimiter: "/"
125
+ * Returns direct child prefixes (CommonPrefixes) and direct child objects
126
+ */
127
+ async listDelimitedPage(prefix, maxKeys = 1000, continuationToken) {
128
+ const response = await this.s3Client.send(new ListObjectsV2Command({
129
+ Bucket: this.config.bucketName,
130
+ Prefix: prefix,
131
+ Delimiter: "/",
132
+ MaxKeys: maxKeys,
133
+ ContinuationToken: continuationToken,
134
+ }));
135
+ const prefixes = [];
136
+ for (const commonPrefix of response.CommonPrefixes ?? []) {
137
+ if (commonPrefix.Prefix) {
138
+ prefixes.push(commonPrefix.Prefix);
139
+ }
140
+ }
141
+ const objects = [];
142
+ for (const object of response.Contents ?? []) {
143
+ if (object.Key) {
144
+ objects.push({
145
+ key: object.Key,
146
+ size: object.Size,
147
+ lastModified: object.LastModified,
148
+ });
149
+ }
150
+ }
151
+ return {
152
+ prefixes,
153
+ objects,
154
+ isTruncated: response.IsTruncated ?? false,
155
+ nextToken: response.NextContinuationToken,
156
+ };
157
+ }
158
+ /**
159
+ * Lists one full directory level (all pages) using Delimiter: "/"
160
+ */
161
+ async listDelimited(prefix) {
162
+ // Some S3-compatible providers repeat CommonPrefixes across pages
163
+ const prefixes = new Set();
164
+ const objects = [];
165
+ let continuationToken;
166
+ do {
167
+ const page = await this.listDelimitedPage(prefix, 1000, continuationToken);
168
+ for (const p of page.prefixes) {
169
+ prefixes.add(p);
170
+ }
171
+ objects.push(...page.objects);
172
+ continuationToken = page.isTruncated ? page.nextToken : undefined;
173
+ } while (continuationToken);
174
+ return { prefixes: [...prefixes], objects };
175
+ }
176
+ /**
177
+ * Uploads an object to the bucket
178
+ */
179
+ async uploadObject(key, buffer, contentType, metadata) {
180
+ await this.s3Client.send(new PutObjectCommand({
181
+ Bucket: this.config.bucketName,
182
+ Key: key,
183
+ Body: buffer,
184
+ ContentType: contentType,
185
+ CacheControl: "public, max-age=31536000", // Cache 1 year
186
+ Metadata: metadata,
187
+ }));
188
+ }
189
+ /**
190
+ * Creates a zero-byte object to represent a folder in S3-compatible storage
191
+ */
192
+ async createFolderMarker(key) {
193
+ await this.s3Client.send(new PutObjectCommand({
194
+ Bucket: this.config.bucketName,
195
+ Key: key,
196
+ Body: "",
197
+ ContentType: "application/x-directory",
198
+ }));
199
+ }
200
+ /**
201
+ * Generates a signed URL for temporary access
202
+ */
203
+ async getSignedUrl(key, expiresIn = 3600) {
204
+ return await getSignedUrl(this.s3Client, new GetObjectCommand({
205
+ Bucket: this.config.bucketName,
206
+ Key: key,
207
+ }), { expiresIn });
208
+ }
209
+ /**
210
+ * Generates public URL for the object
211
+ */
212
+ getPublicUrl(key) {
213
+ if (this.config.publicUrl) {
214
+ return `${this.config.publicUrl}/${key}`;
215
+ }
216
+ // For custom endpoints, construct URL from endpoint
217
+ if (this.config.endpoint) {
218
+ const endpointUrl = this.config.endpoint.replace(/\/$/, "");
219
+ return `${endpointUrl}/${this.config.bucketName}/${key}`;
220
+ }
221
+ // Default AWS S3 URL format
222
+ return `https://${this.config.bucketName}.s3.${this.config.region}.amazonaws.com/${key}`;
223
+ }
224
+ /**
225
+ * Copies an object within the bucket to a new key
226
+ */
227
+ async copyObject(sourceKey, destKey) {
228
+ await this.s3Client.send(new CopyObjectCommand({
229
+ Bucket: this.config.bucketName,
230
+ CopySource: `${this.config.bucketName}/${sourceKey
231
+ .split("/")
232
+ .map(encodeURIComponent)
233
+ .join("/")}`,
234
+ Key: destKey,
235
+ }));
236
+ }
237
+ /**
238
+ * Deletes an object from the bucket
239
+ */
240
+ async deleteObject(key) {
241
+ await this.s3Client.send(new DeleteObjectCommand({
242
+ Bucket: this.config.bucketName,
243
+ Key: key,
244
+ }));
245
+ }
246
+ /**
247
+ * Gets object metadata (size, lastModified) without downloading the file
248
+ */
249
+ async getObjectMetadata(key) {
250
+ try {
251
+ const response = await this.s3Client.send(new HeadObjectCommand({
252
+ Bucket: this.config.bucketName,
253
+ Key: key,
254
+ }));
255
+ return {
256
+ size: response.ContentLength ?? 0,
257
+ lastModified: response.LastModified ?? new Date(),
258
+ metadata: response.Metadata,
259
+ };
260
+ }
261
+ catch {
262
+ return null;
263
+ }
264
+ }
265
+ /**
266
+ * Deletes multiple objects from the bucket (up to 1000 at once)
267
+ */
268
+ async deleteObjects(keys) {
269
+ if (keys.length === 0) {
270
+ return 0;
271
+ }
272
+ // S3 DeleteObjects can delete up to 1000 objects at once
273
+ const batches = [];
274
+ for (let i = 0; i < keys.length; i += 1000) {
275
+ batches.push(keys.slice(i, i + 1000));
276
+ }
277
+ let totalDeleted = 0;
278
+ for (const batch of batches) {
279
+ const response = await this.s3Client.send(new DeleteObjectsCommand({
280
+ Bucket: this.config.bucketName,
281
+ Delete: {
282
+ Objects: batch.map((key) => ({ Key: key })),
283
+ Quiet: true,
284
+ },
285
+ }));
286
+ totalDeleted += batch.length - (response.Errors?.length ?? 0);
287
+ }
288
+ return totalDeleted;
289
+ }
290
+ /**
291
+ * Gets the bucket name
292
+ */
293
+ get bucketName() {
294
+ return this.config.bucketName;
295
+ }
296
+ }
297
+ //# sourceMappingURL=s3-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-client.js","sourceRoot":"","sources":["../../../src/utils/storage/s3-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ export type AggregateStats = {
2
+ size: number;
3
+ fileCount: number;
4
+ };
5
+ export type BucketStats = {
6
+ storage: AggregateStats;
7
+ cache: AggregateStats;
8
+ updatedAt: string;
9
+ };
10
+ /**
11
+ * Persistence and reconciliation operations the tracker delegates to the
12
+ * storage layer (implemented by CloudStorage)
13
+ */
14
+ export type StatsBackend = {
15
+ loadPersistedStats(): Promise<BucketStats | null>;
16
+ persistStats(stats: BucketStats): Promise<void>;
17
+ computeBucketStats(): Promise<{
18
+ storage: AggregateStats;
19
+ cache: AggregateStats;
20
+ }>;
21
+ };
22
+ /**
23
+ * Returns the tracked aggregate stats, loading them from the persisted
24
+ * bucket object on first call. Falls back to a one-time full recomputation
25
+ * (recursive listing) when no persisted stats exist yet.
26
+ */
27
+ export declare function getBucketStats(backend: StatsBackend): Promise<BucketStats>;
28
+ /**
29
+ * Forces a full recomputation from bucket listings and persists the result.
30
+ * Reconciles any drift accumulated by the incremental adjustments (e.g.
31
+ * objects modified outside the app, or a crash before a persist flush).
32
+ * Keeps the previous stats when the recomputation fails.
33
+ */
34
+ export declare function recalculateBucketStats(backend: StatsBackend): Promise<BucketStats>;
35
+ /**
36
+ * Applies an incremental delta to the tracked stats after a bucket mutation
37
+ * (upload, delete, ...). Fire-and-forget: callers never wait on it and it
38
+ * never throws, so stats tracking cannot break the mutation itself.
39
+ */
40
+ export declare function adjustBucketStats(backend: StatsBackend, target: "storage" | "cache", delta: {
41
+ size: number;
42
+ fileCount: number;
43
+ }): void;
44
+ export declare function resetBucketStatsTracker(): void;
45
+ //# sourceMappingURL=stats-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats-tracker.d.ts","sourceRoot":"","sources":["../../../src/utils/storage/stats-tracker.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,kBAAkB,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAClD,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,kBAAkB,IAAI,OAAO,CAAC;QAC5B,OAAO,EAAE,cAAc,CAAC;QACxB,KAAK,EAAE,cAAc,CAAC;KACvB,CAAC,CAAC;CACJ,CAAC;AAoBF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAc1E;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,WAAW,CAAC,CAItB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,SAAS,GAAG,OAAO,EAC3B,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzC,IAAI,CAeN;AA8BD,wBAAgB,uBAAuB,IAAI,IAAI,CAM9C"}
@@ -0,0 +1,97 @@
1
+ import logger, { serializeError } from "../logger.js";
2
+ const PERSIST_THROTTLE_MS = 5_000;
3
+ // Module-level singleton: storage.ts and upload.ts each create their own
4
+ // CloudStorage instance, so the shared aggregate cannot live on the class
5
+ let statsPromise = null;
6
+ let persistTimer = null;
7
+ async function recompute(backend) {
8
+ const { storage, cache } = await backend.computeBucketStats();
9
+ const stats = {
10
+ storage,
11
+ cache,
12
+ updatedAt: new Date().toISOString(),
13
+ };
14
+ await backend.persistStats(stats);
15
+ return stats;
16
+ }
17
+ /**
18
+ * Returns the tracked aggregate stats, loading them from the persisted
19
+ * bucket object on first call. Falls back to a one-time full recomputation
20
+ * (recursive listing) when no persisted stats exist yet.
21
+ */
22
+ export function getBucketStats(backend) {
23
+ if (!statsPromise) {
24
+ const promise = (async () => {
25
+ const persisted = await backend.loadPersistedStats();
26
+ return persisted ?? (await recompute(backend));
27
+ })();
28
+ promise.catch(() => {
29
+ if (statsPromise === promise) {
30
+ statsPromise = null;
31
+ }
32
+ });
33
+ statsPromise = promise;
34
+ }
35
+ return statsPromise;
36
+ }
37
+ /**
38
+ * Forces a full recomputation from bucket listings and persists the result.
39
+ * Reconciles any drift accumulated by the incremental adjustments (e.g.
40
+ * objects modified outside the app, or a crash before a persist flush).
41
+ * Keeps the previous stats when the recomputation fails.
42
+ */
43
+ export async function recalculateBucketStats(backend) {
44
+ const stats = await recompute(backend);
45
+ statsPromise = Promise.resolve(stats);
46
+ return stats;
47
+ }
48
+ /**
49
+ * Applies an incremental delta to the tracked stats after a bucket mutation
50
+ * (upload, delete, ...). Fire-and-forget: callers never wait on it and it
51
+ * never throws, so stats tracking cannot break the mutation itself.
52
+ */
53
+ export function adjustBucketStats(backend, target, delta) {
54
+ void getBucketStats(backend)
55
+ .then((stats) => {
56
+ const aggregate = stats[target];
57
+ aggregate.size = Math.max(0, aggregate.size + delta.size);
58
+ aggregate.fileCount = Math.max(0, aggregate.fileCount + delta.fileCount);
59
+ stats.updatedAt = new Date().toISOString();
60
+ schedulePersist(backend);
61
+ })
62
+ .catch((error) => {
63
+ logger.warn({ error: serializeError(error), target, delta }, "Failed to adjust bucket stats");
64
+ });
65
+ }
66
+ /**
67
+ * Throttled persistence: bursts of mutations (bulk uploads/deletes) produce
68
+ * at most one stats write per window instead of one per object
69
+ */
70
+ function schedulePersist(backend) {
71
+ if (persistTimer) {
72
+ return;
73
+ }
74
+ persistTimer = setTimeout(() => {
75
+ persistTimer = null;
76
+ const current = statsPromise;
77
+ if (!current) {
78
+ return;
79
+ }
80
+ void current
81
+ .then((stats) => backend.persistStats(stats))
82
+ .catch((error) => {
83
+ logger.warn({ error: serializeError(error) }, "Failed to persist bucket stats");
84
+ });
85
+ }, PERSIST_THROTTLE_MS);
86
+ // Don't keep the process alive just to flush stats; reconciliation
87
+ // covers any write lost to a shutdown
88
+ persistTimer.unref?.();
89
+ }
90
+ export function resetBucketStatsTracker() {
91
+ statsPromise = null;
92
+ if (persistTimer) {
93
+ clearTimeout(persistTimer);
94
+ persistTimer = null;
95
+ }
96
+ }
97
+ //# sourceMappingURL=stats-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats-tracker.js","sourceRoot":"","sources":["../../../src/utils/storage/stats-tracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export type { StorageConfig, StorageClientOptions, CacheEntry } from '../../types';
2
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/storage/types.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,UAAU,EACX,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/storage/types.ts"],"names":[],"mappings":""}