vidspotai-shared 1.0.114 → 1.0.116
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.
- package/lib/globals/types.d.ts +8 -0
- package/lib/globals/types.d.ts.map +1 -1
- package/lib/globals/types.js +9 -0
- package/lib/libs/firebase.d.ts +11 -0
- package/lib/libs/firebase.d.ts.map +1 -1
- package/lib/libs/firebase.js +18 -0
- package/lib/libs/storage/MediaStorage.d.ts +80 -29
- package/lib/libs/storage/MediaStorage.d.ts.map +1 -1
- package/lib/libs/storage/MediaStorage.js +51 -39
- package/lib/libs/storage/R2Backend.d.ts +21 -2
- package/lib/libs/storage/R2Backend.d.ts.map +1 -1
- package/lib/libs/storage/R2Backend.js +31 -12
- package/lib/libs/storage/cdnUrl.d.ts +53 -0
- package/lib/libs/storage/cdnUrl.d.ts.map +1 -0
- package/lib/libs/storage/cdnUrl.js +77 -0
- package/lib/libs/storage/index.d.ts +1 -0
- package/lib/libs/storage/index.d.ts.map +1 -1
- package/lib/libs/storage/index.js +1 -0
- package/lib/models/agent.model.d.ts +27 -0
- package/lib/models/agent.model.d.ts.map +1 -1
- package/lib/models/index.d.ts +1 -0
- package/lib/models/index.d.ts.map +1 -1
- package/lib/models/index.js +1 -0
- package/lib/models/outreach.model.d.ts +7 -1
- package/lib/models/outreach.model.d.ts.map +1 -1
- package/lib/models/outreachIntegration.model.d.ts +42 -0
- package/lib/models/outreachIntegration.model.d.ts.map +1 -0
- package/lib/models/outreachIntegration.model.js +8 -0
- package/lib/models/video.model.d.ts +7 -1
- package/lib/models/video.model.d.ts.map +1 -1
- package/lib/schemas/videoPlan.schema.d.ts +1 -0
- package/lib/schemas/videoPlan.schema.d.ts.map +1 -1
- package/lib/schemas/videoPlan.schema.js +10 -0
- package/lib/services/agent/bibleImageVision.d.ts.map +1 -1
- package/lib/services/agent/bibleImageVision.js +2 -1
- package/lib/services/agent/tools/planVideo.tool.d.ts +1 -0
- package/lib/services/agent/tools/planVideo.tool.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/google.service.js +6 -1
- package/lib/services/aiGen/providers/google/googleFetch.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/googleFetch.js +2 -1
- package/lib/services/aiGen/providers/openai/openai.service.js +2 -2
- package/lib/services/aiGen/providers/pixverse/pixverse.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/pixverse/pixverse.service.js +6 -1
- package/lib/services/asr/assemblyai.service.d.ts.map +1 -1
- package/lib/services/asr/assemblyai.service.js +7 -1
- package/lib/services/asr/whisper.service.d.ts.map +1 -1
- package/lib/services/asr/whisper.service.js +5 -1
- package/lib/services/avatarGen/IAvatarVideoProvider.d.ts +25 -3
- package/lib/services/avatarGen/IAvatarVideoProvider.d.ts.map +1 -1
- package/lib/services/avatarGen/IAvatarVideoProvider.js +2 -1
- package/lib/services/avatarGen/providers/heygenAvatarProvider.d.ts +23 -2
- package/lib/services/avatarGen/providers/heygenAvatarProvider.d.ts.map +1 -1
- package/lib/services/avatarGen/providers/heygenAvatarProvider.js +71 -0
- package/lib/services/avatarGen/providers/tavusAvatarProvider.d.ts +3 -4
- package/lib/services/avatarGen/providers/tavusAvatarProvider.d.ts.map +1 -1
- package/lib/services/avatarGen/providers/tavusAvatarProvider.js +17 -0
- package/lib/services/editor/planToProject.d.ts.map +1 -1
- package/lib/services/editor/planToProject.js +29 -1
- package/lib/services/firestore.service.d.ts +3 -0
- package/lib/services/firestore.service.d.ts.map +1 -1
- package/lib/services/firestore.service.js +7 -0
- package/lib/services/index.d.ts +1 -0
- package/lib/services/index.d.ts.map +1 -1
- package/lib/services/index.js +1 -0
- package/lib/services/outreachWebhooks.service.d.ts +24 -0
- package/lib/services/outreachWebhooks.service.d.ts.map +1 -0
- package/lib/services/outreachWebhooks.service.js +41 -0
- package/package.json +1 -1
package/lib/globals/types.d.ts
CHANGED
|
@@ -152,6 +152,14 @@ export declare enum ERedisQueues {
|
|
|
152
152
|
* repairs, and writes the report back. See notes/SAAS_DEMO_GENERATOR_PLAN.md §12.
|
|
153
153
|
*/
|
|
154
154
|
VIDEO_QA_JOBS = "video-qa-job-queue",
|
|
155
|
+
/**
|
|
156
|
+
* VidSpot Reach — Phase 7 self-serve API integration. One job per outbound
|
|
157
|
+
* webhook delivery attempt (campaign.completed / video.ready /
|
|
158
|
+
* recipient.viewed). BullMQ's own attempts+backoff drive the retry — the
|
|
159
|
+
* dispatcher enqueues once per active subscriber per event, the worker owns
|
|
160
|
+
* the signed HTTP POST.
|
|
161
|
+
*/
|
|
162
|
+
WEBHOOK_DELIVERY_JOBS = "webhook-delivery-job-queue",
|
|
155
163
|
/**
|
|
156
164
|
* R2 cutover — mirrors a resumable-upload object (already in GCS, up to
|
|
157
165
|
* 1 GiB) into R2 off the request path. `completeUploadSessionController`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/globals/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,cAAc,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,MAAM,EAAE,GAAG,EAAE,CAAC;CACf;AAED,oBAAY,eAAe;IACzB,MAAM,WAAW,CAAE,wCAAwC;IAC3D,WAAW,eAAe,CAAE,2DAA2D;IAGvF,SAAS,cAAc,CAAE,8CAA8C;IACvE,MAAM,WAAW,CAAE,+BAA+B;IAClD,mBAAmB,uBAAuB,CAAE,oEAAoE;IAChH,SAAS,cAAc;CACxB;AAED,oBAAY,iBAAiB;IAC3B,WAAW,eAAe,CAAE,qFAAqF;IACjH,SAAS,cAAc,CAAE,2FAA2F;IACpH,OAAO,YAAY,CAAE,kKAAkK;IACvL,MAAM,WAAW,CAAE,kEAAkE;IACrF,SAAS,cAAc,CAAE,sCAAsC;IAC/D;;;;;OAKG;IACH,SAAS,aAAa;IACtB;;;;;;OAMG;IACH,SAAS,cAAc;CACxB;AAMD,oBAAY,YAAY;IACtB,UAAU,oBAAoB;IAC9B,iJAAiJ;IACjJ,eAAe,yBAAyB;IACxC,6FAA6F;IAC7F,WAAW,qBAAqB;IAChC,iFAAiF;IACjF,WAAW,qBAAqB;IAChC,mJAAmJ;IACnJ,UAAU,oBAAoB;IAC9B;;;;;OAKG;IACH,cAAc,wBAAwB;IACtC;;;;OAIG;IACH,gBAAgB,0BAA0B;IAC1C;;;;;;;;OAQG;IACH,eAAe,yBAAyB;IACxC;;;;;;;OAOG;IACH,eAAe,yBAAyB;IACxC;;;;;;;;OAQG;IACH,oBAAoB,8BAA8B;IAClD;;;;;;;;OAQG;IACH,iBAAiB,2BAA2B;IAC5C;;;;;OAKG;IACH,cAAc,wBAAwB;IACtC;;;;;;OAMG;IACH,mBAAmB,6BAA6B;IAChD;;;;OAIG;IACH,mBAAmB,6BAA6B;IAChD;;;;;;;;OAQG;IACH,eAAe,yBAAyB;IACxC;;;;;;OAMG;IACH,yBAAyB,mCAAmC;IAC5D;;;;;;;;OAQG;IACH,aAAa,uBAAuB;IACpC;;;;;;OAMG;IACH,iBAAiB,2BAA2B;CAC7C;AAED,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/globals/types.ts"],"names":[],"mappings":"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"}
|
package/lib/globals/types.js
CHANGED
|
@@ -153,6 +153,14 @@ var ERedisQueues;
|
|
|
153
153
|
* repairs, and writes the report back. See notes/SAAS_DEMO_GENERATOR_PLAN.md §12.
|
|
154
154
|
*/
|
|
155
155
|
ERedisQueues["VIDEO_QA_JOBS"] = "video-qa-job-queue";
|
|
156
|
+
/**
|
|
157
|
+
* VidSpot Reach — Phase 7 self-serve API integration. One job per outbound
|
|
158
|
+
* webhook delivery attempt (campaign.completed / video.ready /
|
|
159
|
+
* recipient.viewed). BullMQ's own attempts+backoff drive the retry — the
|
|
160
|
+
* dispatcher enqueues once per active subscriber per event, the worker owns
|
|
161
|
+
* the signed HTTP POST.
|
|
162
|
+
*/
|
|
163
|
+
ERedisQueues["WEBHOOK_DELIVERY_JOBS"] = "webhook-delivery-job-queue";
|
|
156
164
|
/**
|
|
157
165
|
* R2 cutover — mirrors a resumable-upload object (already in GCS, up to
|
|
158
166
|
* 1 GiB) into R2 off the request path. `completeUploadSessionController`
|
|
@@ -180,6 +188,7 @@ exports.REDIS_QUEUES = [
|
|
|
180
188
|
ERedisQueues.DEMO_VIDEO_JOBS,
|
|
181
189
|
ERedisQueues.OUTREACH_PERSONALIZE_JOBS,
|
|
182
190
|
ERedisQueues.VIDEO_QA_JOBS,
|
|
191
|
+
ERedisQueues.WEBHOOK_DELIVERY_JOBS,
|
|
183
192
|
ERedisQueues.MEDIA_MIRROR_JOBS,
|
|
184
193
|
];
|
|
185
194
|
/** X8 Social Suite — connected social platforms. Value = stable storage key. */
|
package/lib/libs/firebase.d.ts
CHANGED
|
@@ -4,4 +4,15 @@ export declare const firestore: admin.firestore.Firestore;
|
|
|
4
4
|
export declare const FieldValue: typeof FirebaseFirestore.FieldValue;
|
|
5
5
|
export declare const storage: import("firebase-admin/lib/storage/storage").Storage;
|
|
6
6
|
export declare function getBucket(): import("@google-cloud/storage").Bucket;
|
|
7
|
+
/**
|
|
8
|
+
* OAuth2 access token for the service account backing this Admin app.
|
|
9
|
+
* Scoped by firebase-admin to include cloud-platform + identitytoolkit
|
|
10
|
+
* (see firebase-admin's credential-internal.js), so it's valid against any
|
|
11
|
+
* Google API the service account has IAM permission for — not just the
|
|
12
|
+
* Admin SDK's own wrapped calls. Use this instead of minting a token by hand
|
|
13
|
+
* when a check needs a raw REST call the Admin SDK doesn't expose (e.g. the
|
|
14
|
+
* Identity Toolkit admin/v2 config endpoint, which the Node SDK's
|
|
15
|
+
* ProjectConfig doesn't surface `authorizedDomains` on).
|
|
16
|
+
*/
|
|
17
|
+
export declare function getGoogleAccessToken(): Promise<string>;
|
|
7
18
|
//# sourceMappingURL=firebase.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase.d.ts","sourceRoot":"","sources":["../../src/libs/firebase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAwBxC,eAAO,MAAM,IAAI,6CAAe,CAAC;AACjC,eAAO,MAAM,SAAS,2BAAoB,CAAC;AAE3C,eAAO,MAAM,UAAU,qCAA6B,CAAC;AACrD,eAAO,MAAM,OAAO,sDAAkB,CAAC;AAEvC,wBAAgB,SAAS,2CAExB"}
|
|
1
|
+
{"version":3,"file":"firebase.d.ts","sourceRoot":"","sources":["../../src/libs/firebase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAwBxC,eAAO,MAAM,IAAI,6CAAe,CAAC;AACjC,eAAO,MAAM,SAAS,2BAAoB,CAAC;AAE3C,eAAO,MAAM,UAAU,qCAA6B,CAAC;AACrD,eAAO,MAAM,OAAO,sDAAkB,CAAC;AAEvC,wBAAgB,SAAS,2CAExB;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAM5D"}
|
package/lib/libs/firebase.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.storage = exports.FieldValue = exports.firestore = exports.auth = void 0;
|
|
37
37
|
exports.getBucket = getBucket;
|
|
38
|
+
exports.getGoogleAccessToken = getGoogleAccessToken;
|
|
38
39
|
const admin = __importStar(require("firebase-admin"));
|
|
39
40
|
const dotenv = __importStar(require("dotenv"));
|
|
40
41
|
dotenv.config();
|
|
@@ -64,3 +65,20 @@ exports.storage = admin.storage();
|
|
|
64
65
|
function getBucket() {
|
|
65
66
|
return exports.storage.bucket();
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* OAuth2 access token for the service account backing this Admin app.
|
|
70
|
+
* Scoped by firebase-admin to include cloud-platform + identitytoolkit
|
|
71
|
+
* (see firebase-admin's credential-internal.js), so it's valid against any
|
|
72
|
+
* Google API the service account has IAM permission for — not just the
|
|
73
|
+
* Admin SDK's own wrapped calls. Use this instead of minting a token by hand
|
|
74
|
+
* when a check needs a raw REST call the Admin SDK doesn't expose (e.g. the
|
|
75
|
+
* Identity Toolkit admin/v2 config endpoint, which the Node SDK's
|
|
76
|
+
* ProjectConfig doesn't surface `authorizedDomains` on).
|
|
77
|
+
*/
|
|
78
|
+
async function getGoogleAccessToken() {
|
|
79
|
+
const token = await admin.app().options.credential?.getAccessToken();
|
|
80
|
+
if (!token?.access_token) {
|
|
81
|
+
throw new Error("Failed to obtain a Google access token from the Firebase Admin credential");
|
|
82
|
+
}
|
|
83
|
+
return token.access_token;
|
|
84
|
+
}
|
|
@@ -8,28 +8,62 @@
|
|
|
8
8
|
* is stored — swapping backends, or flipping which one is primary, never touches
|
|
9
9
|
* the ~30 call sites.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* •
|
|
11
|
+
* Stage 2 composition (flipped 2026-09-22 — full reasoning in
|
|
12
|
+
* notes/R2_MEDIA_MIGRATION_PLAN.md §9 "Stage 2 decision log — R2-primary flip"):
|
|
13
|
+
* • BYTES = R2 — the only place object bytes are written, read, or
|
|
14
|
+
* deleted for anything going through `put`/`putFromFile`/`delete` below.
|
|
15
|
+
* • URL MINTER = GCS — used ONLY to mint the V4-signed URL *shape* that
|
|
16
|
+
* the `cdn.vidspotai.com` Worker already recognizes, including its
|
|
17
|
+
* private-access gate (which just checks the signature param is
|
|
18
|
+
* present — see worker.js `privateRequestAllowed()`, it never
|
|
19
|
+
* cryptographically verifies it). V4 signing is a local crypto
|
|
20
|
+
* operation against the service-account key: no network call, and the
|
|
21
|
+
* object does NOT need to exist in the GCS bucket for a valid-shaped
|
|
22
|
+
* URL to be minted. So this eliminates real GCS *storage* (and its
|
|
23
|
+
* cost) while keeping one small, cost-free GCS *credential*
|
|
24
|
+
* dependency purely for the URL shape — a deliberate, documented
|
|
25
|
+
* trade-off, not an oversight. Building a fully GCS-independent
|
|
26
|
+
* signed-URL scheme (HMAC tokens + Worker verification) was considered
|
|
27
|
+
* and explicitly deferred — see the notes doc.
|
|
14
28
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
29
|
+
* NOT covered by this flip — a few bespoke pipeline paths write bytes
|
|
30
|
+
* directly to GCS via their own streaming/integrity logic instead of
|
|
31
|
+
* routing through `put`/`putFromFile` (direct multipart uploads in
|
|
32
|
+
* handleUploadFile.ts, resumable-upload completion in
|
|
33
|
+
* mediaUploadSession.controller.ts, the google.service.ts moov-atom guard,
|
|
34
|
+
* mediaMirrorQueueProcessor), then call `mirrorBytes`/`mirrorFromFile`/
|
|
35
|
+
* `mirrorFromReadable` below for a best-effort R2 copy. Those still write
|
|
36
|
+
* real bytes to GCS today — converting them is a separate, larger effort
|
|
37
|
+
* (see the notes doc §9).
|
|
18
38
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
39
|
+
* ⚠️ RE-DOWNLOADING a URL this returned, anywhere else in the backend (a
|
|
40
|
+
* provider re-fetching a bible reference image, a pipeline step re-fetching a
|
|
41
|
+
* scene clip to post-process it, ASR re-fetching an audio URL, handing a URL
|
|
42
|
+
* to an external API that dereferences it server-side, etc): NEVER call raw
|
|
43
|
+
* `fetch()`/`axios.get()` on it. Since the flip above, it's GCS-shaped but
|
|
44
|
+
* R2-backed, so a direct fetch against storage.googleapis.com 404s — it only
|
|
45
|
+
* resolves through the CDN Worker's R2 read-through. Use `fetchOwnMedia()`
|
|
46
|
+
* (drop-in `fetch()` replacement) when you're doing the fetching yourself, or
|
|
47
|
+
* `toCdnUrl(url, { visibility: "private" })` when you're just embedding the
|
|
48
|
+
* URL string into a payload for someone else to fetch — both in ./cdnUrl,
|
|
49
|
+
* both safe no-ops on a genuinely external URL. This is not hypothetical:
|
|
50
|
+
* ~10 independent call sites had each rolled their own raw download and
|
|
51
|
+
* broke in production the same day this flipped (see notes doc §9).
|
|
22
52
|
*/
|
|
23
53
|
import type { Readable } from "stream";
|
|
24
54
|
import type { IStorageBackend } from "./StorageBackend";
|
|
25
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Legacy pipeline mirror-writes (see NOT COVERED above) stay opt-in behind
|
|
57
|
+
* this flag; unrelated to the `put`/`putFromFile`/`delete` R2-primary path,
|
|
58
|
+
* which is unconditional since 2026-09-22.
|
|
59
|
+
*/
|
|
26
60
|
export declare function isR2WriteEnabled(): boolean;
|
|
27
61
|
export declare class MediaStorage {
|
|
28
|
-
/**
|
|
29
|
-
private readonly
|
|
30
|
-
/**
|
|
31
|
-
private readonly
|
|
32
|
-
constructor(
|
|
62
|
+
/** Actual byte store — reads/writes/deletes real object data. */
|
|
63
|
+
private readonly bytes;
|
|
64
|
+
/** URL-shape minter only — never written to, never read from directly. */
|
|
65
|
+
private readonly urlMinter;
|
|
66
|
+
constructor(bytes: IStorageBackend, urlMinter: IStorageBackend);
|
|
33
67
|
/**
|
|
34
68
|
* Write in-memory bytes and return a durable delivery URL. Drop-in for the
|
|
35
69
|
* ubiquitous `file.save(buf, { contentType })` + `getSignedUrl({ expires })`
|
|
@@ -49,32 +83,49 @@ export declare class MediaStorage {
|
|
|
49
83
|
size: number;
|
|
50
84
|
}>;
|
|
51
85
|
/**
|
|
52
|
-
* Best-effort R2
|
|
53
|
-
* bespoke
|
|
54
|
-
* through `put`).
|
|
86
|
+
* Best-effort R2 copy of an object ALREADY written to GCS by one of the
|
|
87
|
+
* bespoke pipeline paths listed in the class doc (a specialized
|
|
88
|
+
* streaming/integrity write that doesn't route through `put`). Distinct
|
|
89
|
+
* from `put`'s unconditional R2 write above: these callers still write
|
|
90
|
+
* their real/primary copy to GCS themselves, so this R2 copy is genuinely
|
|
91
|
+
* best-effort and stays flag-gated. No-op when disabled; never throws.
|
|
55
92
|
*/
|
|
56
93
|
mirrorBytes(objectPath: string, body: Buffer, contentType: string): Promise<void>;
|
|
57
94
|
/**
|
|
58
|
-
* Best-effort R2
|
|
59
|
-
*
|
|
60
|
-
* guard). Reads the file only when mirroring is enabled; never
|
|
95
|
+
* Best-effort R2 copy of a LOCAL FILE already written to GCS through a
|
|
96
|
+
* bespoke streaming/integrity path (e.g. the google scene-download
|
|
97
|
+
* moov-atom guard). Reads the file only when mirroring is enabled; never
|
|
98
|
+
* throws.
|
|
61
99
|
*/
|
|
62
100
|
mirrorFromFile(objectPath: string, localPath: string, contentType: string): Promise<void>;
|
|
63
101
|
/**
|
|
64
|
-
* Best-effort R2
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
102
|
+
* Best-effort R2 copy of an object ALREADY written to GCS, streamed
|
|
103
|
+
* straight through from an open Readable (e.g. a GCS read stream) without
|
|
104
|
+
* buffering it whole — for objects too large to safely hold in process
|
|
105
|
+
* memory (resumable uploads, up to 1 GiB). No-op when disabled, when the
|
|
106
|
+
* backend doesn't implement streaming writes, or on failure.
|
|
69
107
|
*/
|
|
70
108
|
mirrorFromReadable(objectPath: string, stream: Readable, contentType: string, size: number): Promise<void>;
|
|
71
109
|
/** Delivery URL for an object already stored. */
|
|
72
110
|
signedReadUrl(objectPath: string): Promise<string>;
|
|
73
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Delete the object's bytes from R2. Does NOT touch GCS — since 2026-09-22
|
|
113
|
+
* new writes never land there, so there's nothing of record to delete. Any
|
|
114
|
+
* pre-flip object that still has a GCS copy becomes orphaned bytes there;
|
|
115
|
+
* acceptable, since GCS bulk cleanup is its own explicitly user-gated step
|
|
116
|
+
* (see notes/R2_MEDIA_MIGRATION_PLAN.md) — this doesn't make that step
|
|
117
|
+
* harder, just means it'll find a few objects Firestore no longer
|
|
118
|
+
* references.
|
|
119
|
+
*/
|
|
74
120
|
delete(objectPath: string): Promise<void>;
|
|
75
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Run a legacy bespoke-pipeline mirror op, swallowing failures — the
|
|
123
|
+
* caller's own direct GCS write already stands regardless. Used only by
|
|
124
|
+
* mirrorBytes/mirrorFromFile/mirrorFromReadable above, never by
|
|
125
|
+
* put/putFromFile/delete (those need R2 to actually succeed).
|
|
126
|
+
*/
|
|
76
127
|
private bestEffortMirror;
|
|
77
128
|
}
|
|
78
|
-
/** App-wide singleton:
|
|
129
|
+
/** App-wide singleton: R2 is the byte store, GCS mints the URL shape only. */
|
|
79
130
|
export declare const mediaStorage: MediaStorage;
|
|
80
131
|
//# sourceMappingURL=MediaStorage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaStorage.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/MediaStorage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"MediaStorage.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/MediaStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,qBAAa,YAAY;IACvB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkB;gBAEhC,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,eAAe;IAK9D;;;;;;OAMG;IACG,GAAG,CACP,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC;IAKlB;;;;;OAKG;IACG,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAMzC;;;;;;;OAOG;IACG,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAQhB;;;;;OAKG;IACG,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAQhB;;;;;;OAMG;IACG,kBAAkB,CACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAShB,iDAAiD;IACjD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlD;;;;;;;;OAQG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C;;;;;OAKG;YACW,gBAAgB;CAa/B;AAED,8EAA8E;AAC9E,eAAO,MAAM,YAAY,cAAsD,CAAC"}
|
|
@@ -5,14 +5,18 @@ exports.isR2WriteEnabled = isR2WriteEnabled;
|
|
|
5
5
|
const GcsBackend_1 = require("./GcsBackend");
|
|
6
6
|
const R2Backend_1 = require("./R2Backend");
|
|
7
7
|
const logger_1 = require("../../utils/logger");
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Legacy pipeline mirror-writes (see NOT COVERED above) stay opt-in behind
|
|
10
|
+
* this flag; unrelated to the `put`/`putFromFile`/`delete` R2-primary path,
|
|
11
|
+
* which is unconditional since 2026-09-22.
|
|
12
|
+
*/
|
|
9
13
|
function isR2WriteEnabled() {
|
|
10
14
|
return process.env.R2_WRITE_ENABLED === "true";
|
|
11
15
|
}
|
|
12
16
|
class MediaStorage {
|
|
13
|
-
constructor(
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
17
|
+
constructor(bytes, urlMinter) {
|
|
18
|
+
this.bytes = bytes;
|
|
19
|
+
this.urlMinter = urlMinter;
|
|
16
20
|
}
|
|
17
21
|
/**
|
|
18
22
|
* Write in-memory bytes and return a durable delivery URL. Drop-in for the
|
|
@@ -22,11 +26,8 @@ class MediaStorage {
|
|
|
22
26
|
* @param objectPath canonical object key, e.g. "final_videos/<id>.mp4"
|
|
23
27
|
*/
|
|
24
28
|
async put(objectPath, body, contentType) {
|
|
25
|
-
await this.
|
|
26
|
-
|
|
27
|
-
await this.bestEffortMirror(() => this.mirror.write(objectPath, body, contentType), objectPath);
|
|
28
|
-
}
|
|
29
|
-
return this.primary.signedReadUrl(objectPath);
|
|
29
|
+
await this.bytes.write(objectPath, body, contentType);
|
|
30
|
+
return this.urlMinter.signedReadUrl(objectPath);
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
32
33
|
* Stream a local file to the primary (no whole-file buffering) and return the
|
|
@@ -35,65 +36,76 @@ class MediaStorage {
|
|
|
35
36
|
* scene-download moov-atom guard).
|
|
36
37
|
*/
|
|
37
38
|
async putFromFile(objectPath, localPath, contentType) {
|
|
38
|
-
const size = await this.
|
|
39
|
-
|
|
40
|
-
await this.bestEffortMirror(() => this.mirror.writeFromFile(objectPath, localPath, contentType), objectPath);
|
|
41
|
-
}
|
|
42
|
-
const url = await this.primary.signedReadUrl(objectPath);
|
|
39
|
+
const size = await this.bytes.writeFromFile(objectPath, localPath, contentType);
|
|
40
|
+
const url = await this.urlMinter.signedReadUrl(objectPath);
|
|
43
41
|
return { url, size };
|
|
44
42
|
}
|
|
45
43
|
/**
|
|
46
|
-
* Best-effort R2
|
|
47
|
-
* bespoke
|
|
48
|
-
* through `put`).
|
|
44
|
+
* Best-effort R2 copy of an object ALREADY written to GCS by one of the
|
|
45
|
+
* bespoke pipeline paths listed in the class doc (a specialized
|
|
46
|
+
* streaming/integrity write that doesn't route through `put`). Distinct
|
|
47
|
+
* from `put`'s unconditional R2 write above: these callers still write
|
|
48
|
+
* their real/primary copy to GCS themselves, so this R2 copy is genuinely
|
|
49
|
+
* best-effort and stays flag-gated. No-op when disabled; never throws.
|
|
49
50
|
*/
|
|
50
51
|
async mirrorBytes(objectPath, body, contentType) {
|
|
51
52
|
if (!isR2WriteEnabled())
|
|
52
53
|
return;
|
|
53
|
-
await this.bestEffortMirror(() => this.
|
|
54
|
+
await this.bestEffortMirror(() => this.bytes.write(objectPath, body, contentType), objectPath);
|
|
54
55
|
}
|
|
55
56
|
/**
|
|
56
|
-
* Best-effort R2
|
|
57
|
-
*
|
|
58
|
-
* guard). Reads the file only when mirroring is enabled; never
|
|
57
|
+
* Best-effort R2 copy of a LOCAL FILE already written to GCS through a
|
|
58
|
+
* bespoke streaming/integrity path (e.g. the google scene-download
|
|
59
|
+
* moov-atom guard). Reads the file only when mirroring is enabled; never
|
|
60
|
+
* throws.
|
|
59
61
|
*/
|
|
60
62
|
async mirrorFromFile(objectPath, localPath, contentType) {
|
|
61
63
|
if (!isR2WriteEnabled())
|
|
62
64
|
return;
|
|
63
|
-
await this.bestEffortMirror(() => this.
|
|
65
|
+
await this.bestEffortMirror(() => this.bytes.writeFromFile(objectPath, localPath, contentType), objectPath);
|
|
64
66
|
}
|
|
65
67
|
/**
|
|
66
|
-
* Best-effort R2
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
68
|
+
* Best-effort R2 copy of an object ALREADY written to GCS, streamed
|
|
69
|
+
* straight through from an open Readable (e.g. a GCS read stream) without
|
|
70
|
+
* buffering it whole — for objects too large to safely hold in process
|
|
71
|
+
* memory (resumable uploads, up to 1 GiB). No-op when disabled, when the
|
|
72
|
+
* backend doesn't implement streaming writes, or on failure.
|
|
71
73
|
*/
|
|
72
74
|
async mirrorFromReadable(objectPath, stream, contentType, size) {
|
|
73
75
|
if (!isR2WriteEnabled())
|
|
74
76
|
return;
|
|
75
|
-
if (!this.
|
|
77
|
+
if (!this.bytes.writeFromReadable)
|
|
76
78
|
return;
|
|
77
|
-
await this.bestEffortMirror(() => this.
|
|
79
|
+
await this.bestEffortMirror(() => this.bytes.writeFromReadable(objectPath, stream, contentType, size), objectPath);
|
|
78
80
|
}
|
|
79
81
|
/** Delivery URL for an object already stored. */
|
|
80
82
|
signedReadUrl(objectPath) {
|
|
81
|
-
return this.
|
|
83
|
+
return this.urlMinter.signedReadUrl(objectPath);
|
|
82
84
|
}
|
|
83
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Delete the object's bytes from R2. Does NOT touch GCS — since 2026-09-22
|
|
87
|
+
* new writes never land there, so there's nothing of record to delete. Any
|
|
88
|
+
* pre-flip object that still has a GCS copy becomes orphaned bytes there;
|
|
89
|
+
* acceptable, since GCS bulk cleanup is its own explicitly user-gated step
|
|
90
|
+
* (see notes/R2_MEDIA_MIGRATION_PLAN.md) — this doesn't make that step
|
|
91
|
+
* harder, just means it'll find a few objects Firestore no longer
|
|
92
|
+
* references.
|
|
93
|
+
*/
|
|
84
94
|
async delete(objectPath) {
|
|
85
|
-
await this.
|
|
86
|
-
if (isR2WriteEnabled()) {
|
|
87
|
-
await this.bestEffortMirror(() => this.mirror.delete(objectPath), objectPath);
|
|
88
|
-
}
|
|
95
|
+
await this.bytes.delete(objectPath);
|
|
89
96
|
}
|
|
90
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Run a legacy bespoke-pipeline mirror op, swallowing failures — the
|
|
99
|
+
* caller's own direct GCS write already stands regardless. Used only by
|
|
100
|
+
* mirrorBytes/mirrorFromFile/mirrorFromReadable above, never by
|
|
101
|
+
* put/putFromFile/delete (those need R2 to actually succeed).
|
|
102
|
+
*/
|
|
91
103
|
async bestEffortMirror(op, objectPath) {
|
|
92
104
|
try {
|
|
93
105
|
await op();
|
|
94
106
|
}
|
|
95
107
|
catch (err) {
|
|
96
|
-
logger_1.logger.error("MediaStorage: R2 mirror failed (
|
|
108
|
+
logger_1.logger.error("MediaStorage: R2 mirror failed (GCS write stands)", {
|
|
97
109
|
objectPath,
|
|
98
110
|
err: err instanceof Error ? err.stack ?? err.message : String(err),
|
|
99
111
|
});
|
|
@@ -101,5 +113,5 @@ class MediaStorage {
|
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
115
|
exports.MediaStorage = MediaStorage;
|
|
104
|
-
/** App-wide singleton:
|
|
105
|
-
exports.mediaStorage = new MediaStorage(new
|
|
116
|
+
/** App-wide singleton: R2 is the byte store, GCS mints the URL shape only. */
|
|
117
|
+
exports.mediaStorage = new MediaStorage(new R2Backend_1.R2Backend(), new GcsBackend_1.GcsBackend());
|
|
@@ -4,8 +4,20 @@ export declare class R2Backend implements IStorageBackend {
|
|
|
4
4
|
readonly name = "r2";
|
|
5
5
|
private client;
|
|
6
6
|
private init;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* True once creds+endpoint are present. Since R2 became the sole byte
|
|
9
|
+
* store for `MediaStorage.put`/`putFromFile`/`delete` (2026-09-22), a
|
|
10
|
+
* missing config can no longer silently no-op here — that used to be safe
|
|
11
|
+
* when R2 was only a best-effort mirror (GCS's write still stood), but
|
|
12
|
+
* would now mean a `put()` returns a delivery URL for an object that was
|
|
13
|
+
* never actually written anywhere. `write`/`writeFromFile`/
|
|
14
|
+
* `writeFromReadable`/`delete` below throw when this returns null;
|
|
15
|
+
* `MediaStorage`'s legacy bespoke-pipeline mirror calls
|
|
16
|
+
* (mirrorBytes/mirrorFromFile/mirrorFromReadable) already wrap those in a
|
|
17
|
+
* try/catch, so the throw is caught and stays best-effort there.
|
|
18
|
+
*/
|
|
8
19
|
private getClient;
|
|
20
|
+
private requireClient;
|
|
9
21
|
write(key: string, body: Buffer, contentType: string): Promise<void>;
|
|
10
22
|
writeFromFile(key: string, localPath: string, contentType: string): Promise<number>;
|
|
11
23
|
/**
|
|
@@ -16,7 +28,14 @@ export declare class R2Backend implements IStorageBackend {
|
|
|
16
28
|
* up to 5GiB without multipart, comfortably covering our upload ceiling.
|
|
17
29
|
*/
|
|
18
30
|
writeFromReadable(key: string, stream: Readable, contentType: string, size: number): Promise<void>;
|
|
19
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Bare CDN read URL (no signature/access-control). Not used as the actual
|
|
33
|
+
* delivery-URL source today — `MediaStorage` mints via `GcsBackend`
|
|
34
|
+
* instead, since the Worker doesn't recognize this bare-key shape and it
|
|
35
|
+
* carries no access control for private content (see MediaStorage.ts's
|
|
36
|
+
* class doc, "Stage 2 composition"). Kept for interface completeness /
|
|
37
|
+
* future use once a real signed-URL scheme exists for this shape.
|
|
38
|
+
*/
|
|
20
39
|
signedReadUrl(key: string): Promise<string>;
|
|
21
40
|
delete(key: string): Promise<void>;
|
|
22
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"R2Backend.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/R2Backend.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAIvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAyBxD,qBAAa,SAAU,YAAW,eAAe;IAC/C,QAAQ,CAAC,IAAI,QAAQ;IAGrB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,IAAI,CAAyC;IAErD
|
|
1
|
+
{"version":3,"file":"R2Backend.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/R2Backend.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAIvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAyBxD,qBAAa,SAAU,YAAW,eAAe;IAC/C,QAAQ,CAAC,IAAI,QAAQ;IAGrB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,IAAI,CAAyC;IAErD;;;;;;;;;;;OAWG;YACW,SAAS;YAoBT,aAAa;IAQrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAepE,aAAa,CACjB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC;IAQlB;;;;;;OAMG;IACG,iBAAiB,CACrB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,QAAQ,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAgBhB;;;;;;;OAOG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI3C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAOzC"}
|
|
@@ -76,7 +76,18 @@ class R2Backend {
|
|
|
76
76
|
this.client = null;
|
|
77
77
|
this.init = null;
|
|
78
78
|
}
|
|
79
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* True once creds+endpoint are present. Since R2 became the sole byte
|
|
81
|
+
* store for `MediaStorage.put`/`putFromFile`/`delete` (2026-09-22), a
|
|
82
|
+
* missing config can no longer silently no-op here — that used to be safe
|
|
83
|
+
* when R2 was only a best-effort mirror (GCS's write still stood), but
|
|
84
|
+
* would now mean a `put()` returns a delivery URL for an object that was
|
|
85
|
+
* never actually written anywhere. `write`/`writeFromFile`/
|
|
86
|
+
* `writeFromReadable`/`delete` below throw when this returns null;
|
|
87
|
+
* `MediaStorage`'s legacy bespoke-pipeline mirror calls
|
|
88
|
+
* (mirrorBytes/mirrorFromFile/mirrorFromReadable) already wrap those in a
|
|
89
|
+
* try/catch, so the throw is caught and stays best-effort there.
|
|
90
|
+
*/
|
|
80
91
|
async getClient() {
|
|
81
92
|
if (this.client)
|
|
82
93
|
return this.client;
|
|
@@ -85,7 +96,7 @@ class R2Backend {
|
|
|
85
96
|
this.init = (async () => {
|
|
86
97
|
const { accessKeyId, secretAccessKey, endpoint } = r2Config();
|
|
87
98
|
if (!accessKeyId || !secretAccessKey || !endpoint) {
|
|
88
|
-
logger_1.logger.warn("R2Backend: creds/endpoint missing — R2 ops will
|
|
99
|
+
logger_1.logger.warn("R2Backend: creds/endpoint missing — R2 ops will fail");
|
|
89
100
|
return null;
|
|
90
101
|
}
|
|
91
102
|
const { S3Client: Client } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-s3")));
|
|
@@ -98,10 +109,15 @@ class R2Backend {
|
|
|
98
109
|
})();
|
|
99
110
|
return this.init;
|
|
100
111
|
}
|
|
101
|
-
async
|
|
112
|
+
async requireClient() {
|
|
102
113
|
const client = await this.getClient();
|
|
103
|
-
if (!client)
|
|
104
|
-
|
|
114
|
+
if (!client) {
|
|
115
|
+
throw new Error("R2Backend: creds/endpoint missing — cannot write/delete");
|
|
116
|
+
}
|
|
117
|
+
return client;
|
|
118
|
+
}
|
|
119
|
+
async write(key, body, contentType) {
|
|
120
|
+
const client = await this.requireClient();
|
|
105
121
|
const { PutObjectCommand } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-s3")));
|
|
106
122
|
const r2Key = (0, r2KeyMap_1.toR2Key)(key);
|
|
107
123
|
await client.send(new PutObjectCommand({
|
|
@@ -127,9 +143,7 @@ class R2Backend {
|
|
|
127
143
|
* up to 5GiB without multipart, comfortably covering our upload ceiling.
|
|
128
144
|
*/
|
|
129
145
|
async writeFromReadable(key, stream, contentType, size) {
|
|
130
|
-
const client = await this.
|
|
131
|
-
if (!client)
|
|
132
|
-
return;
|
|
146
|
+
const client = await this.requireClient();
|
|
133
147
|
const { PutObjectCommand } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-s3")));
|
|
134
148
|
const r2Key = (0, r2KeyMap_1.toR2Key)(key);
|
|
135
149
|
await client.send(new PutObjectCommand({
|
|
@@ -141,14 +155,19 @@ class R2Backend {
|
|
|
141
155
|
}));
|
|
142
156
|
logger_1.logger.info("R2Backend: wrote object from stream", { key, r2Key, size });
|
|
143
157
|
}
|
|
144
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* Bare CDN read URL (no signature/access-control). Not used as the actual
|
|
160
|
+
* delivery-URL source today — `MediaStorage` mints via `GcsBackend`
|
|
161
|
+
* instead, since the Worker doesn't recognize this bare-key shape and it
|
|
162
|
+
* carries no access control for private content (see MediaStorage.ts's
|
|
163
|
+
* class doc, "Stage 2 composition"). Kept for interface completeness /
|
|
164
|
+
* future use once a real signed-URL scheme exists for this shape.
|
|
165
|
+
*/
|
|
145
166
|
async signedReadUrl(key) {
|
|
146
167
|
return `${cdnBase()}/${(0, r2KeyMap_1.toR2Key)(key)}`;
|
|
147
168
|
}
|
|
148
169
|
async delete(key) {
|
|
149
|
-
const client = await this.
|
|
150
|
-
if (!client)
|
|
151
|
-
return;
|
|
170
|
+
const client = await this.requireClient();
|
|
152
171
|
const { DeleteObjectCommand } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-s3")));
|
|
153
172
|
await client.send(new DeleteObjectCommand({ Bucket: R2_BUCKET, Key: (0, r2KeyMap_1.toR2Key)(key) }));
|
|
154
173
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend counterpart of the frontend's `cdn()` helper
|
|
3
|
+
* (vidspot-frontend/src/lib/mediaCdn.ts). Both must stay in sync — same host
|
|
4
|
+
* prefixes, same `/gcs-pub/` `/gcs-priv/` tagging scheme the Worker
|
|
5
|
+
* (infra/cloudflare-media-cdn/worker.js) expects.
|
|
6
|
+
*
|
|
7
|
+
* Most delivery URLs (finalVideoUrl, posterUrl, etc.) are stored once in
|
|
8
|
+
* Firestore and rewritten to the CDN host at RENDER TIME by the frontend. But
|
|
9
|
+
* a few backend endpoints mint a URL and hand it straight to an external
|
|
10
|
+
* client themselves (share links, download redirects) — those never pass
|
|
11
|
+
* through the frontend's cdn(), so they need this to get R2 zero-egress
|
|
12
|
+
* delivery instead of a bare storage.googleapis.com URL.
|
|
13
|
+
*/
|
|
14
|
+
export type MediaVisibility = "public" | "private";
|
|
15
|
+
/**
|
|
16
|
+
* Rewrite a Firebase Storage download URL or a GCS V4 signed URL (for our
|
|
17
|
+
* bucket) to the CDN host. Non-matching URLs are returned untouched.
|
|
18
|
+
*
|
|
19
|
+
* @param visibility Only affects GCS signed URLs — "public" (default) is a
|
|
20
|
+
* 1yr-immutable edge cache; "private" is a short edge TTL so a
|
|
21
|
+
* revoked/expired link stops being served within a day. Firebase download
|
|
22
|
+
* URLs are always treated as public.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toCdnUrl(url: string, opts?: {
|
|
25
|
+
visibility?: MediaVisibility;
|
|
26
|
+
}): string;
|
|
27
|
+
/**
|
|
28
|
+
* The sanctioned way to RE-DOWNLOAD a URL this backend itself minted via
|
|
29
|
+
* `mediaStorage.put()`/`putFromFile()`, from anywhere in the backend (a
|
|
30
|
+
* provider re-fetching a bible reference image, a pipeline step re-fetching a
|
|
31
|
+
* scene clip to post-process it, etc).
|
|
32
|
+
*
|
|
33
|
+
* Why this exists: since the Stage 2 R2-primary flip (see MediaStorage.ts's
|
|
34
|
+
* class doc), those URLs are GCS-signed-SHAPED but R2-BACKED — the object no
|
|
35
|
+
* longer exists at the literal storage.googleapis.com address, only under the
|
|
36
|
+
* CDN Worker's R2 read-through. A plain `fetch(url)` on one 404s. This
|
|
37
|
+
* wrapper is the fix, applied once, here — use it instead of a raw `fetch()`
|
|
38
|
+
* so a future call site can't reintroduce the same bug by forgetting to
|
|
39
|
+
* rewrite the URL first (which is exactly how this broke: ~10 independent
|
|
40
|
+
* fetch/axios call sites had each rolled their own download, see
|
|
41
|
+
* notes/R2_MEDIA_MIGRATION_PLAN.md §9).
|
|
42
|
+
*
|
|
43
|
+
* Safe no-op passthrough for a genuinely external URL (AI provider output,
|
|
44
|
+
* stock API, etc) — `toCdnUrl` only rewrites our own host prefixes.
|
|
45
|
+
*
|
|
46
|
+
* Not applicable when WE aren't the one fetching — e.g. handing a URL to an
|
|
47
|
+
* external API that dereferences it server-side (AssemblyAI's `audio_url`,
|
|
48
|
+
* Veo's `video.uri`, PixVerse's `image_url`). Those need the same rewrite,
|
|
49
|
+
* but via `toCdnUrl()` directly on the string embedded in the request, since
|
|
50
|
+
* there's no local `fetch()` call to wrap.
|
|
51
|
+
*/
|
|
52
|
+
export declare function fetchOwnMedia(url: string, init?: RequestInit): Promise<Response>;
|
|
53
|
+
//# sourceMappingURL=cdnUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdnUrl.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/cdnUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,eAAe,CAAA;CAAE,GACtC,MAAM,CAeR;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEhF"}
|