Image url update
Deploy / deploy (push) Successful in 2m30s

This commit is contained in:
twotalesanimation
2026-07-29 07:46:00 +02:00
parent f0dcf17e93
commit 0d0f3e1a33
3 changed files with 142 additions and 54 deletions
+32 -25
View File
@@ -77,8 +77,10 @@ export function VersionUpload({
setUploadProgress(0);
try {
// Upload via local API (XHR for progress) or UploadThing
const fileUrl = await uploadViaLocal(file, (p) =>
// Upload directly to storage (XHR for progress), bypassing this app's
// server/reverse-proxy so large videos aren't subject to
// client_max_body_size / Cloudflare's edge upload cap.
const fileUrl = await uploadDirectToStorage(file, (p) =>
setUploadProgress(Math.round(p * 0.85))
);
@@ -244,45 +246,50 @@ export function VersionUpload({
);
}
/** Upload a file to /api/upload/local using XHR so we get progress events. */
function uploadViaLocal(
/**
* Upload a file directly to Hetzner Object Storage using a presigned PUT URL,
* via XHR so we get progress events. Bypasses this app's server entirely —
* no reverse-proxy / CDN body size limit applies to the actual file bytes.
*/
async function uploadDirectToStorage(
file: File,
onProgress: (fraction: number) => void
): Promise<string> {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append("file", file);
const presignRes = await fetch("/api/upload/presign", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ fileName: file.name, contentType: file.type, folder: "videos" }),
});
if (!presignRes.ok) {
const json = await presignRes.json().catch(() => null);
throw new Error(json?.error ?? `Failed to prepare upload (HTTP ${presignRes.status})`);
}
const { presignedUrl, url } = (await presignRes.json()) as {
presignedUrl: string;
key: string;
url: string;
};
await new Promise<void>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/upload/local");
xhr.open("PUT", presignedUrl);
xhr.setRequestHeader("Content-Type", file.type);
xhr.upload.addEventListener("progress", (e) => {
if (e.lengthComputable) onProgress(e.loaded / e.total);
});
xhr.addEventListener("load", () => {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const json = JSON.parse(xhr.responseText);
if (json.url) resolve(json.url);
else reject(new Error(json.error ?? "Upload failed"));
} catch {
reject(new Error("Invalid server response"));
}
} else {
try {
const json = JSON.parse(xhr.responseText);
reject(new Error(json.error ?? `HTTP ${xhr.status}`));
} catch {
reject(new Error(`HTTP ${xhr.status}`));
}
}
if (xhr.status >= 200 && xhr.status < 300) resolve();
else reject(new Error(`Upload failed (HTTP ${xhr.status})`));
});
xhr.addEventListener("error", () => reject(new Error("Network error")));
xhr.addEventListener("abort", () => reject(new Error("Upload cancelled")));
xhr.send(formData);
xhr.send(file);
});
return url;
}