diff --git a/web2/app/api/[...path]/route.ts b/web2/app/api/[...path]/route.ts index 9e1680e..f6ea963 100644 --- a/web2/app/api/[...path]/route.ts +++ b/web2/app/api/[...path]/route.ts @@ -106,27 +106,20 @@ export async function GET(request: NextRequest, { params }: { params: { path: st return NextResponse.json({ error: "Failed to fetch data" }, { status: 500 }); } } - export async function POST(request: NextRequest, { params }: { params: { path: string[] } }) { - const url = new URL(request.url); - const path = url.pathname.replace("/api/", ""); // Remove "/api/" prefix - - // if (!path) { - // return new Response("Invalid request", { status: 400 }); - // } - //const path = params.path.join("/") + const path = url.pathname.replace("/api/", ""); + const token = request.headers.get("Authorization")?.split(" ")[1]; - const token = request.headers.get("Authorization")?.split(" ")[1] - let body = {} + let body = {}; try { - body = request.body ? await request.json() : {}; + const text = await request.text(); + body = text ? JSON.parse(text) : {}; } catch (error) { console.error("Invalid JSON:", error); return NextResponse.json({ error: "Invalid JSON input" }, { status: 400 }); } - try { const response = await fetch(`${process.env.API_URL}/api/${path}`, { method: "POST", @@ -135,13 +128,20 @@ export async function POST(request: NextRequest, { params }: { params: { path: s "Content-Type": "application/json", }, body: JSON.stringify(body), - }) + }); - const data = await response.json() - return NextResponse.json(data, { status: response.status }) + const responseText = await response.text(); + console.log(responseText); + + if (responseText.trim() !== "") { + const responseData = JSON.parse(responseText); + return NextResponse.json(responseData, { status: response.status }); + } else { + return new Response(null, { status: response.status }); // <-- return something even if empty + } } catch (error) { - console.error("API error:", error) - return NextResponse.json({ error: "Failed to post data" }, { status: 500 }) + console.error("API error:", error); + return NextResponse.json({ error: "Failed to post data" }, { status: 500 }); } } diff --git a/web2/app/api/sse/[...path]/route.ts b/web2/app/api/sse/[...path]/route.ts index a64dcb0..2a7ce1e 100644 --- a/web2/app/api/sse/[...path]/route.ts +++ b/web2/app/api/sse/[...path]/route.ts @@ -1,5 +1,55 @@ +// // app/api/sse/[...path]/route.ts +// import { type NextRequest } from "next/server"; + +// export async function GET(request: NextRequest) { +// const url = new URL(request.url); +// const path = url.pathname.replace("/api/sse/", ""); // e.g., "timeline" or "comments/123" +// const token = +// url.searchParams.get("token") || +// request.headers.get("Authorization")?.split(" ")[1]; + +// try { +// const backendResponse = await fetch( +// `${process.env.API_URL}/api/${path}?${url.searchParams.toString()}`, +// { +// method: "GET", +// headers: { +// Authorization: token ? `Bearer ${token}` : "", +// Accept: "text/event-stream", +// }, +// } +// ); + +// // Proxy the backend SSE stream directly to the client +// console.log(backendResponse.body) +// // let passedValue = await new Response(backendResponse.body).text(); +// // if (passedValue){ +// // let valueToJson = JSON.parse(passedValue); +// // console.log("jsonval:", valueToJson) +// // } +// return new Response(backendResponse.body, { +// status: 200, +// headers: { +// "Content-Type": "text/event-stream", +// "Cache-Control": "no-cache", +// Connection: "keep-alive", +// // Optional headers for CORS (if needed): +// // "Access-Control-Allow-Origin": "*", +// }, +// }); +// } catch (error) { +// console.log("SSE proxy error:", error); +// return new Response("Failed to connect to SSE", { status: 502 }); +// } +// } + + + + + + // app/api/sse/[...path]/route.ts -import { type NextRequest } from "next/server"; +import { type NextRequest, NextResponse } from "next/server"; export async function GET(request: NextRequest) { const url = new URL(request.url); @@ -8,37 +58,92 @@ export async function GET(request: NextRequest) { url.searchParams.get("token") || request.headers.get("Authorization")?.split(" ")[1]; + // --- Ensure token exists (optional but good practice) --- + if (!token) { + return new Response("Authentication token required", { status: 401 }); + } + // --- --- + try { + const backendUrl = `${process.env.API_URL}/api/${path}`; + // Create a new URL object to safely append search params + const targetUrl = new URL(backendUrl); + + // Forward existing search params EXCEPT the token we added client-side + url.searchParams.forEach((value, key) => { + if (key !== 'token') { // Don't forward the token query param if backend expects header + targetUrl.searchParams.append(key, value); + } + }); + + + console.log(`Proxying SSE request to:${targetUrl.toString()}`); + const backendResponse = await fetch( - `${process.env.API_URL}/api/${path}?${url.searchParams.toString()}`, + targetUrl.toString(), // Use the constructed URL with search params { method: "GET", headers: { - Authorization: token ? `Bearer ${token}` : "", + // Pass the token in the Authorization header to the actual backend + Authorization: `Bearer ${token}`, Accept: "text/event-stream", + // Forward other relevant headers if needed + // 'X-Forwarded-For': request.ip ?? 'unknown', }, + // Important for streaming responses in Node fetch / Next.js Edge runtime + cache: 'no-store', // Ensure fresh data + // If using Node >= 18, duplex might be needed depending on the exact env + // duplex: 'half' } ); - // Proxy the backend SSE stream directly to the client - console.log(backendResponse.body) - let passedValue = await new Response(backendResponse.body).text(); - if (passedValue){ - let valueToJson = JSON.parse(passedValue); - console.log("jsonval:", valueToJson) + // Check if the backend responded successfully + if (!backendResponse.ok) { + console.error(`SSE Backend error (${backendResponse.status}): ${await backendResponse.text()}`); + return new Response(`Backend request failed with status ${backendResponse.status}`, { status: backendResponse.status }); + } + + // Check if the backend response is actually an event stream + const contentType = backendResponse.headers.get("Content-Type"); + if (!contentType || !contentType.includes("text/event-stream")) { + console.error(`Backend did not respond with Content-Type: text/event-stream. Received: ${contentType}`); + // Return an error, maybe log the response body if small + // const responseBody = await backendResponse.text(); + // console.error("Backend response body:", responseBody); + return new Response("Backend did not return an event stream", { status: 502 }); // 502 Bad Gateway + } + + + // --- CORRECT WAY: Stream the backend response directly --- + // Ensure the body exists and is a ReadableStream + if (!backendResponse.body) { + console.error("Backend response body is null."); + return new Response("Backend response body is null", { status: 502 }); } - return new Response("", { - status: 200, + + // Return the backend's stream directly to the client + console.log("body", backendResponse.body) + return new Response(backendResponse.body, { + status: 200, // Or backendResponse.status if you want to mirror it headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", - Connection: "keep-alive", - // Optional headers for CORS (if needed): - // "Access-Control-Allow-Origin": "*", + "Connection": "keep-alive", + // Copy other relevant headers from backendResponse if needed + // e.g., backendResponse.headers.get('X-My-Custom-Header') }, }); + // --- --- + } catch (error) { - console.log("SSE proxy error:", error); - return new Response("Failed to connect to SSE", { status: 502 }); + console.error("SSE proxy fetch error:", error); + // Check if it's a fetch error (e.g., connection refused) + if (error instanceof TypeError && error.message === 'fetch failed') { + return new Response("Failed to connect to the backend SSE service", { status: 502 }); // Bad Gateway might be appropriate + } + return new Response(`Internal Server Error: ${error instanceof Error ? error.message : 'Unknown error'}`, { status: 500 }); } } + +// Optional: Configure Edge Runtime for potentially better performance with streaming +// export const runtime = 'edge'; \ No newline at end of file diff --git a/web2/components/comments-list.tsx b/web2/components/comments-list.tsx index eab5a60..79d8a2a 100644 --- a/web2/components/comments-list.tsx +++ b/web2/components/comments-list.tsx @@ -47,6 +47,8 @@ export function CommentsList({ proseId }: { proseId: string }) { }) useEffect(() => { + fetchComments() + if (data) { setComments(data) setIsLoading(false) @@ -55,7 +57,7 @@ export function CommentsList({ proseId }: { proseId: string }) { if (sseError) { fetchComments() } - }, [data, sseError]) + }, [sseError]) const fetchComments = async () => { try { @@ -71,7 +73,10 @@ export function CommentsList({ proseId }: { proseId: string }) { const data = await response.json() console.log(data) - setComments(data) + if (data!=null){ + setComments(data) + } + else setComments([]) } catch (err) { toast({ title: "Error", @@ -90,6 +95,7 @@ export function CommentsList({ proseId }: { proseId: string }) { headers: { Authorization: `Bearer ${token}`, }, + body: null }) if (!response.ok) { diff --git a/web2/components/timeline.tsx b/web2/components/timeline.tsx index 5c79d82..1c69d42 100644 --- a/web2/components/timeline.tsx +++ b/web2/components/timeline.tsx @@ -138,6 +138,7 @@ export function Timeline() { headers: { Authorization: `Bearer ${token}`, }, + body: "" }); if (!response.ok) { diff --git a/web2/lib/use-sse.tsx b/web2/lib/use-sse.tsx index 342a101..a65730d 100644 --- a/web2/lib/use-sse.tsx +++ b/web2/lib/use-sse.tsx @@ -191,14 +191,16 @@ export function useSSE( eventSource.addEventListener(eventName, (event) => { try { console.log(`Received ${eventName} event:`, event); - - if (event.data) { - console.log(event.data) - - const parsedData = JSON.parse(event.data) as T; - setData(parsedData); - options.onMessage?.(parsedData); + if (event.data){ + console.log("raw data", event.data) } + + // if (event.data) { + // console.log(event.data) + // const parsedData = JSON.parse(event.data) as T; + // setData(parsedData); + // options.onMessage?.(parsedData); + // } } catch (err) { console.error('Error parsing SSE data:', err); const error = err instanceof Error ? err : new Error(String(err));