From e2aeba09f610be63fe5376c03841208c49adcbec Mon Sep 17 00:00:00 2001 From: vagdotdev Date: Tue, 30 Jun 2026 13:08:58 +0530 Subject: [PATCH] fix: keep static proxy responses inline --- lib/utils/resolve-file-proxy.ts | 26 ++++++++++++++++--------- routes/files/static/[[...file_path]].ts | 2 +- tests/routes/file-proxy02.test.ts | 24 +++++++++++++++++++++++ tests/routes/file-proxy03.test.ts | 21 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/lib/utils/resolve-file-proxy.ts b/lib/utils/resolve-file-proxy.ts index 3145b4e..1dd6771 100644 --- a/lib/utils/resolve-file-proxy.ts +++ b/lib/utils/resolve-file-proxy.ts @@ -6,9 +6,11 @@ import { normalizePath } from "./normalize-path" export async function resolveFileProxy( proxy: FileProxy, file_path: string, + opts: { attachment?: boolean } = {}, ): Promise { const normalizedPath = normalizePath(file_path) const pattern = proxy.matching_pattern + const attachment = opts.attachment ?? true // Extract the relative path after the pattern prefix // Pattern: "prefix/*" -> prefix is "prefix/" @@ -16,15 +18,16 @@ export async function resolveFileProxy( const relativePath = normalizedPath.slice(prefix.length) if (proxy.proxy_type === "disk") { - return resolveDiskProxy(proxy.disk_path, relativePath) + return resolveDiskProxy(proxy.disk_path, relativePath, { attachment }) } else { - return resolveHttpProxy(proxy.http_target_url, relativePath) + return resolveHttpProxy(proxy.http_target_url, relativePath, { attachment }) } } async function resolveDiskProxy( diskPath: string, relativePath: string, + opts: { attachment: boolean }, ): Promise { const fullPath = join(diskPath, relativePath) @@ -33,13 +36,15 @@ async function resolveDiskProxy( const fileName = relativePath.split("/").pop() || "file" const contentType = getContentType(fileName) - return new Response(content, { - headers: { - "Content-Type": contentType, - "Content-Disposition": `attachment; filename="${fileName}"`, - "Content-Length": content.byteLength.toString(), - }, + const headers = new Headers({ + "Content-Type": contentType, + "Content-Length": content.byteLength.toString(), }) + if (opts.attachment) { + headers.set("Content-Disposition", `attachment; filename="${fileName}"`) + } + + return new Response(content, { headers }) } catch (error: any) { if (error.code === "ENOENT") { return new Response("File not found", { status: 404 }) @@ -52,6 +57,7 @@ async function resolveDiskProxy( async function resolveHttpProxy( httpTargetUrl: string, relativePath: string, + opts: { attachment: boolean }, ): Promise { // Ensure the URL doesn't have double slashes const baseUrl = httpTargetUrl.endsWith("/") @@ -79,7 +85,9 @@ async function resolveHttpProxy( headers.set("Content-Length", contentLength) } const fileName = relativePath.split("/").pop() || "file" - headers.set("Content-Disposition", `attachment; filename="${fileName}"`) + if (opts.attachment) { + headers.set("Content-Disposition", `attachment; filename="${fileName}"`) + } return new Response(response.body, { status: response.status, diff --git a/routes/files/static/[[...file_path]].ts b/routes/files/static/[[...file_path]].ts index 27f2910..0535b1b 100644 --- a/routes/files/static/[[...file_path]].ts +++ b/routes/files/static/[[...file_path]].ts @@ -75,7 +75,7 @@ export default withRouteSpec({ // Check if there's a matching proxy const proxy = ctx.db.matchFileProxy(normalizedPath) if (proxy) { - return resolveFileProxy(proxy, normalizedPath) + return resolveFileProxy(proxy, normalizedPath, { attachment: false }) } return new Response("File not found", { status: 404 }) } diff --git a/tests/routes/file-proxy02.test.ts b/tests/routes/file-proxy02.test.ts index c1314e8..0853608 100644 --- a/tests/routes/file-proxy02.test.ts +++ b/tests/routes/file-proxy02.test.ts @@ -91,6 +91,30 @@ test("disk proxy with query param download", async () => { } }) +test("disk proxy static route does not force attachment download", async () => { + const { axios } = await getTestServer() + + const tempDir = await mkdtemp(join(tmpdir(), "file-proxy-static-test-")) + + try { + await writeFile(join(tempDir, "inline.txt"), "Inline proxy content") + + await axios.post("/file_proxies/create", { + proxy_type: "disk", + disk_path: tempDir, + matching_pattern: "static-disk/*", + }) + + const staticRes = await axios.get("/files/static/static-disk/inline.txt") + expect(staticRes.status).toBe(200) + expect(staticRes.data).toBe("Inline proxy content") + expect(staticRes.headers.get("content-type")).toBe("text/plain") + expect(staticRes.headers.get("content-disposition")).toBeNull() + } finally { + await rm(tempDir, { recursive: true, force: true }) + } +}) + test("disk proxy binary file", async () => { const { axios } = await getTestServer() diff --git a/tests/routes/file-proxy03.test.ts b/tests/routes/file-proxy03.test.ts index 0596f78..4975137 100644 --- a/tests/routes/file-proxy03.test.ts +++ b/tests/routes/file-proxy03.test.ts @@ -78,6 +78,27 @@ test("http proxy with query param download", async () => { expect(downloadRes.data).toBe("Query param HTTP proxy test") }) +test("http proxy static route does not force attachment download", async () => { + const { axios, url } = await getTestServer() + + await axios.post("/files/upsert", { + file_path: "/static-source/inline.txt", + text_content: "Inline HTTP proxy content", + }) + + await axios.post("/file_proxies/create", { + proxy_type: "http", + http_target_url: `${url}/files/static/static-source`, + matching_pattern: "http-static/*", + }) + + const staticRes = await axios.get("/files/static/http-static/inline.txt") + expect(staticRes.status).toBe(200) + expect(staticRes.data).toBe("Inline HTTP proxy content") + expect(staticRes.headers.get("content-type")).toBe("text/plain") + expect(staticRes.headers.get("content-disposition")).toBeNull() +}) + test("http proxy binary file", async () => { const { axios, url } = await getTestServer()