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
+41 -29
View File
@@ -34,38 +34,50 @@ function uploadViaXhr(
onProgress: (fraction: number) => void
): Promise<{ url: string; key: string }> {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append("file", file);
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/upload/local");
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({ url: json.url, key: json.key ?? "" });
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}`));
(async () => {
// Ask the server for a presigned Hetzner PUT URL, then upload the
// bytes directly from the browser — this bypasses this app's server
// (and any reverse-proxy / CDN body size limit, e.g. Cloudflare's
// 100MB edge cap) entirely for large footage plates.
let presignedUrl: string;
let key: string;
let url: string;
try {
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 presignJson = await presignRes.json();
presignedUrl = presignJson.presignedUrl;
key = presignJson.key;
url = presignJson.url;
} catch (err) {
reject(err instanceof Error ? err : new Error("Failed to prepare upload"));
return;
}
});
xhr.addEventListener("error", () => reject(new Error("Network error")));
xhr.addEventListener("abort", () => reject(new Error("Upload aborted")));
xhr.send(formData);
const xhr = new XMLHttpRequest();
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) resolve({ url, key });
else reject(new Error(`Upload failed (HTTP ${xhr.status})`));
});
xhr.addEventListener("error", () => reject(new Error("Network error")));
xhr.addEventListener("abort", () => reject(new Error("Upload aborted")));
xhr.send(file);
})();
});
}