mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
B2 doesn't map onto the generic-S3 "paste an endpoint" model: its S3-compatible endpoint is region-scoped (s3.<region>.backblazeb2.com) and a public bucket is addressed virtual-hosted-style, so the public download URL is https://<bucket>.s3.<region>.backblazeb2.com/<key> — not derivable from a pasted generic endpoint. So B2 silently failed under "Custom S3". - s3-url: deriveB2Endpoint(region) -> https://s3.<region>.backblazeb2.com (empty for auto/blank/pasted-host so callers can require a real region); re-exported from s3Service. - storageService: PROVIDERS.B2 + resolveB2Endpoint (explicit endpoint wins, else derive) + PROVIDER_TESTS[B2] (derived endpoint, forcePathStyle false). - Public URL = S3-style virtual-hosted, auto-derived; an explicit Public Access URL (custom domain/CDN) overrides it. The save path now forwards the user's public URL to prepareS3Connection (also fixes generic-S3 ignoring a custom CDN URL when blank -> still derives, so no regression). - UI: "Backblaze B2" provider option + helper; raw Endpoint hidden for B2 (region drives it), region shows a us-west-004 example, force-path-style toggle hidden; save/test derive endpoint from region with a clear "enter your region" error. UploadModal shows B2-specific labels. - Tests: tests/storage-b2-provider.test.mjs (8). All s3/storage suites green (50); build + lint clean. Co-Authored-By: Claude Opus 4.8 <[email protected]>
112 lines
3.8 KiB
JavaScript
112 lines
3.8 KiB
JavaScript
// #38: Backblaze B2 as a first-class storage provider.
|
|
//
|
|
// B2 doesn't map onto the generic-S3 "paste an endpoint" model: its S3-compatible
|
|
// endpoint is region-scoped (s3.<region>.backblazeb2.com) and a public bucket is
|
|
// addressed virtual-hosted-style, so the public download URL is
|
|
// https://<bucket>.s3.<region>.backblazeb2.com/<key> — NOT endpoint/bucket/key.
|
|
//
|
|
// These tests pin the derivation that makes B2 work: region -> endpoint, and the
|
|
// (forcePathStyle:false) public-URL derivation, plus the explicit-override path.
|
|
|
|
import test from "node:test";
|
|
import { strict as assert } from "node:assert";
|
|
import {
|
|
deriveB2Endpoint,
|
|
derivePublicBaseUrl,
|
|
validateS3Connection,
|
|
buildS3ObjectUrl,
|
|
} from "../js/services/s3Service.js";
|
|
import { PROVIDERS } from "../js/services/storageService.js";
|
|
|
|
test("PROVIDERS exposes a dedicated Backblaze B2 id", () => {
|
|
assert.equal(PROVIDERS.B2, "backblaze_b2");
|
|
});
|
|
|
|
test("deriveB2Endpoint builds the region-scoped S3 endpoint", () => {
|
|
assert.equal(
|
|
deriveB2Endpoint("us-west-004"),
|
|
"https://s3.us-west-004.backblazeb2.com",
|
|
);
|
|
assert.equal(
|
|
deriveB2Endpoint(" EU-CENTRAL-003 "),
|
|
"https://s3.eu-central-003.backblazeb2.com",
|
|
"trims + lowercases",
|
|
);
|
|
});
|
|
|
|
test("deriveB2Endpoint returns empty for a missing/placeholder region (so callers can require it)", () => {
|
|
assert.equal(deriveB2Endpoint(""), "");
|
|
assert.equal(deriveB2Endpoint("auto"), "");
|
|
assert.equal(deriveB2Endpoint(undefined), "");
|
|
});
|
|
|
|
test("deriveB2Endpoint refuses a pasted host/endpoint in the region box", () => {
|
|
assert.equal(deriveB2Endpoint("s3.us-west-004.backblazeb2.com"), "");
|
|
assert.equal(deriveB2Endpoint("https://s3.us-west-004.backblazeb2.com"), "");
|
|
});
|
|
|
|
test("a B2 bucket's public URL derives virtual-hosted (the S3-style download host)", () => {
|
|
// This is the crux: with the derived endpoint + forcePathStyle false, the bucket
|
|
// becomes a subdomain — the URL a public B2 bucket actually serves files from.
|
|
const endpoint = deriveB2Endpoint("us-west-004");
|
|
const publicBaseUrl = derivePublicBaseUrl({
|
|
endpoint,
|
|
bucket: "my-videos",
|
|
forcePathStyle: false,
|
|
});
|
|
assert.equal(
|
|
publicBaseUrl,
|
|
"https://my-videos.s3.us-west-004.backblazeb2.com",
|
|
);
|
|
});
|
|
|
|
test("validateS3Connection derives the B2 public URL end-to-end (no manual public URL)", () => {
|
|
const normalized = validateS3Connection({
|
|
endpoint: deriveB2Endpoint("us-west-004"),
|
|
region: "us-west-004",
|
|
accessKeyId: "k",
|
|
secretAccessKey: "s",
|
|
bucket: "my-videos",
|
|
forcePathStyle: false,
|
|
// publicBaseUrl intentionally omitted -> must derive
|
|
});
|
|
assert.equal(normalized.endpoint, "https://s3.us-west-004.backblazeb2.com");
|
|
assert.equal(
|
|
normalized.publicBaseUrl,
|
|
"https://my-videos.s3.us-west-004.backblazeb2.com",
|
|
);
|
|
assert.equal(normalized.forcePathStyle, false);
|
|
});
|
|
|
|
test("an explicit Public Access URL (custom domain / CDN) overrides the derived B2 URL", () => {
|
|
const normalized = validateS3Connection({
|
|
endpoint: deriveB2Endpoint("us-west-004"),
|
|
region: "us-west-004",
|
|
accessKeyId: "k",
|
|
secretAccessKey: "s",
|
|
bucket: "my-videos",
|
|
forcePathStyle: false,
|
|
publicBaseUrl: "https://cdn.example.com",
|
|
});
|
|
assert.equal(normalized.publicBaseUrl, "https://cdn.example.com");
|
|
});
|
|
|
|
test("object URLs for a B2 connection resolve under the virtual-hosted bucket host", () => {
|
|
const endpoint = deriveB2Endpoint("us-west-004");
|
|
const url = buildS3ObjectUrl({
|
|
publicBaseUrl: derivePublicBaseUrl({
|
|
endpoint,
|
|
bucket: "my-videos",
|
|
forcePathStyle: false,
|
|
}),
|
|
endpoint,
|
|
bucket: "my-videos",
|
|
key: "u/npub1/abc/video.mp4",
|
|
forcePathStyle: false,
|
|
});
|
|
assert.equal(
|
|
url,
|
|
"https://my-videos.s3.us-west-004.backblazeb2.com/u/npub1/abc/video.mp4",
|
|
);
|
|
});
|