import fetchKijijiItems from "@/kijiji"; import fetchFacebookItems from "@/facebook"; import fetchEbayItems from "@/ebay"; const PORT = process.env.PORT || 4005; const server = Bun.serve({ port: PORT, idleTimeout: 0, routes: { // Static routes "/api/status": new Response("OK"), // Dynamic routes "/api/kijiji": async (req: Request) => { const reqUrl = new URL(req.url); const SEARCH_QUERY = req.headers.get("query") || reqUrl.searchParams.get("q") || null; if (!SEARCH_QUERY) return Response.json( { message: "Request didn't have 'query' header or 'q' search parameter!", }, { status: 400 }, ); const items = await fetchKijijiItems(SEARCH_QUERY, 5); if (!items) return Response.json( { message: "Search didn't return any results!" }, { status: 404 }, ); return Response.json(items, { status: 200 }); }, "/api/facebook": async (req: Request) => { const reqUrl = new URL(req.url); const SEARCH_QUERY = req.headers.get("query") || reqUrl.searchParams.get("q") || null; if (!SEARCH_QUERY) return Response.json( { message: "Request didn't have 'query' header or 'q' search parameter!", }, { status: 400 }, ); const LOCATION = reqUrl.searchParams.get("location") || "toronto"; const COOKIES_SOURCE = reqUrl.searchParams.get("cookies") || undefined; try { const items = await fetchFacebookItems(SEARCH_QUERY, 5, LOCATION, 25, COOKIES_SOURCE); if (!items || items.length === 0) return Response.json( { message: "Search didn't return any results!" }, { status: 404 }, ); return Response.json(items, { status: 200 }); } catch (error) { console.error("Facebook scraping error:", error); const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return Response.json( { message: errorMessage }, { status: 400 }, ); } }, "/api/ebay": async (req: Request) => { const reqUrl = new URL(req.url); const SEARCH_QUERY = req.headers.get("query") || reqUrl.searchParams.get("q") || null; if (!SEARCH_QUERY) return Response.json( { message: "Request didn't have 'query' header or 'q' search parameter!", }, { status: 400 }, ); // Parse optional parameters with defaults const minPrice = reqUrl.searchParams.get("minPrice") ? parseInt(reqUrl.searchParams.get("minPrice")!) : undefined; const maxPrice = reqUrl.searchParams.get("maxPrice") ? parseInt(reqUrl.searchParams.get("maxPrice")!) : undefined; const strictMode = reqUrl.searchParams.get("strictMode") === "true"; const exclusionsParam = reqUrl.searchParams.get("exclusions"); const exclusions = exclusionsParam ? exclusionsParam.split(",").map(s => s.trim()) : []; const keywordsParam = reqUrl.searchParams.get("keywords"); const keywords = keywordsParam ? keywordsParam.split(",").map(s => s.trim()) : [SEARCH_QUERY]; try { const items = await fetchEbayItems(SEARCH_QUERY, 5, { minPrice, maxPrice, strictMode, exclusions, keywords, }); if (!items || items.length === 0) return Response.json( { message: "Search didn't return any results!" }, { status: 404 }, ); return Response.json(items, { status: 200 }); } catch (error) { console.error("eBay scraping error:", error); const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return Response.json( { message: errorMessage }, { status: 400 }, ); } }, // Wildcard route for all routes that start with "/api/" and aren't otherwise matched "/api/*": Response.json({ message: "Not found" }, { status: 404 }), // // Serve a file by buffering it in memory // "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), { // headers: { // "Content-Type": "image/x-icon", // }, // }), }, // (optional) fallback for unmatched routes: // Required if Bun's version < 1.2.3 fetch(req: Request) { return new Response("Not Found", { status: 404 }); }, }); console.log(`Serving on ${server.hostname}:${server.port}`);