fix(rsplat): serve staged images via /api/ path to avoid Cloudflare redirect
Cloudflare/Traefik 301-redirects /data/ paths to data.rspace.online, which fal.ai can't follow. Staged images now served at /api/files/generated/ which passes through correctly. Added route alias for backwards compat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d4c0fdf7eb
commit
3c1802ab07
|
|
@ -185,8 +185,9 @@ app.get("/collect.js", async (c) => {
|
|||
});
|
||||
});
|
||||
|
||||
// ── Serve generated files from /data/files/generated/ ──
|
||||
app.get("/data/files/generated/:filename", async (c) => {
|
||||
// ── Serve generated files from /data/files/generated/ and /api/files/generated/ ──
|
||||
// The /api/ route avoids Cloudflare/Traefik redirecting /data/ paths
|
||||
function serveGeneratedFile(c: any) {
|
||||
const filename = c.req.param("filename");
|
||||
if (!filename || filename.includes("..") || filename.includes("/")) {
|
||||
return c.json({ error: "Invalid filename" }, 400);
|
||||
|
|
@ -194,11 +195,15 @@ app.get("/data/files/generated/:filename", async (c) => {
|
|||
const dir = resolve(process.env.FILES_DIR || "./data/files", "generated");
|
||||
const filePath = resolve(dir, filename);
|
||||
const file = Bun.file(filePath);
|
||||
if (!(await file.exists())) return c.notFound();
|
||||
const ext = filename.split(".").pop() || "";
|
||||
const mimeMap: Record<string, string> = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", webp: "image/webp", glb: "model/gltf-binary", gltf: "model/gltf+json" };
|
||||
return new Response(file, { headers: { "Content-Type": mimeMap[ext] || "application/octet-stream", "Cache-Control": "public, max-age=86400" } });
|
||||
});
|
||||
return file.exists().then((exists: boolean) => {
|
||||
if (!exists) return c.notFound();
|
||||
const ext = filename.split(".").pop() || "";
|
||||
const mimeMap: Record<string, string> = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", webp: "image/webp", glb: "model/gltf-binary", gltf: "model/gltf+json" };
|
||||
return new Response(file, { headers: { "Content-Type": mimeMap[ext] || "application/octet-stream", "Cache-Control": "public, max-age=86400" } });
|
||||
});
|
||||
}
|
||||
app.get("/data/files/generated/:filename", serveGeneratedFile);
|
||||
app.get("/api/files/generated/:filename", serveGeneratedFile);
|
||||
|
||||
// ── Link preview / unfurl API ──
|
||||
const linkPreviewCache = new Map<string, { title: string; description: string; image: string | null; domain: string; fetchedAt: number }>();
|
||||
|
|
@ -1212,7 +1217,7 @@ app.post("/api/image-stage", async (c) => {
|
|||
const dir = resolve(process.env.FILES_DIR || "./data/files", "generated");
|
||||
const filename = `stage-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.jpg`;
|
||||
await Bun.write(resolve(dir, filename), buf);
|
||||
return c.json({ url: `${PUBLIC_ORIGIN}/data/files/generated/${filename}` });
|
||||
return c.json({ url: `${PUBLIC_ORIGIN}/api/files/generated/${filename}` });
|
||||
}
|
||||
return c.json({ error: "Invalid upload format. Expected multipart/form-data." }, 400);
|
||||
}
|
||||
|
|
@ -1244,7 +1249,7 @@ app.post("/api/image-stage", async (c) => {
|
|||
const buf = Buffer.from(await file.arrayBuffer());
|
||||
await Bun.write(resolve(dir, filename), buf);
|
||||
|
||||
return c.json({ url: `${PUBLIC_ORIGIN}/data/files/generated/${filename}` });
|
||||
return c.json({ url: `${PUBLIC_ORIGIN}/api/files/generated/${filename}` });
|
||||
});
|
||||
|
||||
// Image-to-3D via fal.ai Hunyuan3D v2.1 (async job queue)
|
||||
|
|
|
|||
Loading…
Reference in New Issue