From 0eafac5e0f58dfcdcc7836f5bc2a54c86b576aff Mon Sep 17 00:00:00 2001 From: atul-upadhyay-7 Date: Sun, 7 Jun 2026 17:58:08 +0530 Subject: [PATCH] Fix processing status proxy route (fixes #450) --- server.js | 15 +++++++++++++++ server.test.js | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 6220c17..6337fec 100644 --- a/server.js +++ b/server.js @@ -1103,6 +1103,21 @@ app.post("/process-from-url", uploadLimiter, requireSupabaseAuth, async (req, re } }); +app.get("/processing-status/:sessionId", async (req, res) => { + try { + const response = await axios.get( + `${RAG_SERVICE_URL}/processing-status/${req.params.sessionId}`, + { headers: ragAuthHeaders(), timeout: 5000 } + ); + return res.json(response.data); + } catch (err) { + if (err.response?.status === 404) { + return res.status(404).json({ error: "Processing status not found." }); + } + return propagateRagError(err, res, "Error fetching processing status"); + } +}); + app.post("/ask", inferenceSlowDown, inferenceLimiter, async (req, res) => { const validation = validateAskBody(req.body); diff --git a/server.test.js b/server.test.js index 7181e53..e61ce68 100644 --- a/server.test.js +++ b/server.test.js @@ -453,6 +453,46 @@ describe("route error responses", () => { assert.deepEqual(data.details.fieldErrors.question, ["Question is required."]); }); + test("GET /processing-status/:sessionId forwards internal auth header and returns data", async () => { + const originalGet = axios.get; + let forwardedHeaders = null; + + axios.get = async (url, options) => { + forwardedHeaders = options?.headers; + return { data: { stage: "Extracting text", progress: 50 } }; + }; + + try { + const res = await fetch(`${baseUrl}/processing-status/550e8400-e29b-41d4-a716-446655440000`); + assert.equal(res.status, 200); + const data = await res.json(); + assert.equal(data.stage, "Extracting text"); + assert.equal(data.progress, 50); + assert.equal(forwardedHeaders["X-Internal-Token"], process.env.INTERNAL_RAG_TOKEN); + } finally { + axios.get = originalGet; + } + }); + + test("GET /processing-status/:sessionId returns 404 when upstream returns 404", async () => { + const originalGet = axios.get; + + axios.get = async () => { + const err = new Error("Not Found"); + err.response = { status: 404 }; + throw err; + }; + + try { + const res = await fetch(`${baseUrl}/processing-status/unknown-id`); + assert.equal(res.status, 404); + const data = await res.json(); + assert.equal(data.error, "Processing status not found."); + } finally { + axios.get = originalGet; + } + }); + test("POST /ask with invalid session_id returns 400", async () => { const res = await fetch(`${baseUrl}/ask`, { method: "POST", @@ -517,7 +557,7 @@ describe("route error responses", () => { method: "POST", headers: { "Content-Type": "application/json", - Authorization: "Bearer test-token", + Authorization: `Bearer ${jwt.sign({ role: "authenticated" }, process.env.SUPABASE_JWT_SECRET)}`, }, body: JSON.stringify({ url: "https://xyz.supabase.co//evil.com/file.pdf?download=1", @@ -560,7 +600,7 @@ describe("route error responses", () => { method: "POST", headers: { "Content-Type": "application/json", - Authorization: "Bearer test-token", + Authorization: `Bearer ${jwt.sign({ role: "authenticated" }, process.env.SUPABASE_JWT_SECRET)}`, }, body: JSON.stringify({ url: " https://xyz.supabase.co/storage/v1/object/public/docs/trimmed.pdf ",