Files
bitvid/js/utils/uploadErrorHints.js
thePR0M3TH3ANandClaude Opus 4.8 fdebf05e1a feat(s3): bring generic S3 to parity with R2 (cleanup + CORS guidance)
Two gaps where generic S3 trailed Cloudflare R2:

1. Object cleanup on delete/edit didn't work for generic S3. deleteVideoStorage
   built its client via makeR2Client, which hard-forces path-style and ignores
   the connection's forcePathStyle -- so vhost-style S3 deletes were misaddressed
   and cleanup silently failed (S3 users' deleted/edited files orphaned). It is
   now provider-aware (mirrors uploadFile): R2 -> makeR2Client (path-style),
   generic S3 -> makeS3Client honoring forcePathStyle. makeS3Client is now
   injectable for testing.

2. CORS guidance on upload failure was R2-only. Extracted isLikelyCorsError +
   a generic-S3 guidance builder into js/utils/uploadErrorHints.js (re-exported
   from r2Service for back-compat); s3UploadService now appends actionable CORS
   guidance on an opaque "Failed to fetch" too.

Generic S3 is now at functional parity with R2 for upload -> publish -> cleanup.

Tests: r2-delete-video-storage (adds generic-S3 forcePathStyle routing),
s3-upload-cors-guidance (new). Full suite green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-17 06:13:38 -04:00

43 lines
1.5 KiB
JavaScript

// Shared helpers for turning opaque upload failures into actionable guidance.
// Used by both upload paths (Cloudflare R2 via r2Service, generic S3 via
// s3UploadService) so the behavior stays in sync.
/**
* A browser CORS preflight/rejection surfaces as an opaque network error
* ("Failed to fetch" / "NetworkError" / Safari's "Load failed") with no HTTP
* status — indistinguishable from a real network drop. We treat these as
* likely-CORS so the upload path can attach actionable guidance instead of a
* bare "Failed to fetch".
*/
export function isLikelyCorsError(err) {
const message = (
(err && (err.message || (err.toString && err.toString()))) ||
""
).toLowerCase();
return (
message.includes("failed to fetch") ||
message.includes("networkerror") ||
message.includes("load failed")
);
}
/**
* Provider-neutral CORS guidance for a generic S3 bucket. (Cloudflare R2 has a
* more specific variant in r2Service that names the R2 S3 API endpoint.)
*/
export function buildGenericCorsGuidance({ endpoint } = {}) {
const origin =
typeof window !== "undefined" &&
window.location &&
window.location.origin &&
window.location.origin !== "null"
? window.location.origin
: "<your-app-origin>";
const target = endpoint ? endpoint : "your bucket's S3 API endpoint";
return [
"This is likely a CORS issue.",
`Configure CORS on ${target} to allow origin ${origin},`,
"with AllowedMethods: GET, HEAD, PUT, POST, DELETE, OPTIONS and AllowedHeaders: *.",
].join(" ");
}