feat(cloudflare): add linearvoid-media edge worker for R2 HLS delivery
Replace the ad-hoc `worker.js` with a self-contained `linearvoid-media` worker under `infrastructure/cloudflare/` that owns `media.linearvoid.tv`. It serves HLS objects from the R2 bucket, enforcing immutable Cache-Control on segments, a short TTL on live manifests, plus HLS CORS, OPTIONS preflight, Range support, and a `/health` probe. Includes wrapper config with production domain, Vitest unit tests, Miniflare R2 integration tests, automated deploy workflow, and docs updates.
This commit is contained in:
1 parent
5f1f796a20
commit
8c5e70959b
16 files changed
+3359
-42
No files matched your search
@@ -0,0 +1,54 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { cacheControlForPath, contentTypeForPath, parseByteRange } from "../src/worker.js";
|
||||
|
||||
const IMMUTABLE = "public, max-age=31536000, immutable";
|
||||
const MANIFEST = "public, max-age=1, s-maxage=2, must-revalidate";
|
||||
const DEFAULT = "public, max-age=60";
|
||||
|
||||
describe("cacheControlForPath", () => {
|
||||
it("marks media segments as immutable", () => {
|
||||
expect(cacheControlForPath("/hls/seg-000001.m4s")).toBe(IMMUTABLE);
|
||||
expect(cacheControlForPath("/hls/init.mp4")).toBe(IMMUTABLE);
|
||||
expect(cacheControlForPath("/hls/seg-000001.ts")).toBe(IMMUTABLE);
|
||||
expect(cacheControlForPath("/media/audio.m4a")).toBe(IMMUTABLE);
|
||||
});
|
||||
|
||||
it("keeps live manifests short-lived", () => {
|
||||
expect(cacheControlForPath("/hls/playlist.m3u8")).toBe(MANIFEST);
|
||||
expect(cacheControlForPath("/hls/master.m3u8")).toBe(MANIFEST);
|
||||
});
|
||||
|
||||
it("falls back to a sane default for other paths", () => {
|
||||
expect(cacheControlForPath("/hls/unknown.txt")).toBe(DEFAULT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("contentTypeForPath", () => {
|
||||
it("maps known media types", () => {
|
||||
expect(contentTypeForPath("/hls/playlist.m3u8")).toBe("application/vnd.apple.mpegurl");
|
||||
expect(contentTypeForPath("/hls/seg-000001.m4s")).toBe("video/mp4");
|
||||
expect(contentTypeForPath("/hls/init.mp4")).toBe("video/mp4");
|
||||
expect(contentTypeForPath("/hls/seg-000001.ts")).toBe("video/MP2T");
|
||||
expect(contentTypeForPath("/media/audio.m4a")).toBe("audio/mp4");
|
||||
});
|
||||
|
||||
it("falls back to octet-stream", () => {
|
||||
expect(contentTypeForPath("/hls/unknown.bin")).toBe("application/octet-stream");
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseByteRange", () => {
|
||||
it("parses a closed byte range", () => {
|
||||
expect(parseByteRange("bytes=0-2")).toEqual({ offset: 0, length: 3 });
|
||||
expect(parseByteRange("bytes=10-19")).toEqual({ offset: 10, length: 10 });
|
||||
});
|
||||
|
||||
it("rejects open, suffix, multi, or malformed ranges", () => {
|
||||
expect(parseByteRange("bytes=0-")).toBeNull();
|
||||
expect(parseByteRange("bytes=-500")).toBeNull();
|
||||
expect(parseByteRange("bytes=0-1,3-4")).toBeNull();
|
||||
expect(parseByteRange("items=0-2")).toBeNull();
|
||||
expect(parseByteRange("")).toBeNull();
|
||||
expect(parseByteRange(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user