188 lines
5.9 KiB
TypeScript
188 lines
5.9 KiB
TypeScript
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 },
|
|
);
|
|
|
|
// Parse optional parameters with enhanced defaults
|
|
const location = reqUrl.searchParams.get("location");
|
|
const category = reqUrl.searchParams.get("category");
|
|
const maxPagesParam = reqUrl.searchParams.get("maxPages");
|
|
const maxPages = maxPagesParam
|
|
? Number.parseInt(maxPagesParam, 10)
|
|
: 5; // Default: 5 pages
|
|
const sortBy = reqUrl.searchParams.get("sortBy") as 'relevancy' | 'date' | 'price' | 'distance' | undefined;
|
|
const sortOrder = reqUrl.searchParams.get("sortOrder") as 'asc' | 'desc' | undefined;
|
|
|
|
// Build search options
|
|
const locationValue = location ? (/^\d+$/.test(location) ? Number(location) : location) : 1700272;
|
|
const categoryValue = category ? (/^\d+$/.test(category) ? Number(category) : category) : 0;
|
|
|
|
const searchOptions: import("@/kijiji").SearchOptions = {
|
|
location: locationValue,
|
|
category: categoryValue,
|
|
keywords: SEARCH_QUERY,
|
|
sortBy: sortBy || 'relevancy',
|
|
sortOrder: sortOrder || 'desc',
|
|
maxPages,
|
|
};
|
|
|
|
// Build listing fetch options with enhanced defaults
|
|
const listingOptions: import("@/kijiji").ListingFetchOptions = {
|
|
includeImages: true, // Always include full image arrays
|
|
sellerDataDepth: 'detailed', // Default: detailed seller info
|
|
includeClientSideData: false, // GraphQL reviews disabled by default
|
|
};
|
|
|
|
try {
|
|
const items = await fetchKijijiItems(SEARCH_QUERY, 1, undefined, searchOptions, listingOptions);
|
|
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("Kijiji scraping error:", error);
|
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
return Response.json(
|
|
{
|
|
message: `Scraping failed: ${errorMessage}`,
|
|
query: SEARCH_QUERY,
|
|
options: { searchOptions, listingOptions }
|
|
},
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
},
|
|
|
|
"/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 minPriceParam = reqUrl.searchParams.get("minPrice");
|
|
const minPrice = minPriceParam
|
|
? Number.parseInt(minPriceParam, 10)
|
|
: undefined;
|
|
const maxPriceParam = reqUrl.searchParams.get("maxPrice");
|
|
const maxPrice = maxPriceParam
|
|
? Number.parseInt(maxPriceParam, 10)
|
|
: 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}`);
|