Initial commit: Holiday Travel App with resort comparison, trip management, and multi-provider search

This commit is contained in:
2025-10-29 16:22:35 -04:00
commit 74f8e268c3
167 changed files with 18721 additions and 0 deletions

42
lib/providers/yowDeals.ts Normal file
View File

@@ -0,0 +1,42 @@
import * as cheerio from "cheerio";
import type { Deal, SearchCriteria } from "../types";
const SITE_BY_ORIGIN: Record<string, string> = {
"YOW": "https://www.yowdeals.com",
"YYZ": "https://www.yyzdeals.com",
"YUL": "https://www.yuldeals.com",
"YVR": "https://www.yvrdeals.com",
"YYC": "https://www.yycdeals.com",
"YEG": "https://www.yegdeals.com",
};
export async function fetchCityDeals(criteria: SearchCriteria): Promise<Deal[]> {
const site = SITE_BY_ORIGIN[(criteria.origin || "").toUpperCase()] || SITE_BY_ORIGIN["YOW"];
try {
const res = await fetch(site, { cache: "no-store" });
const html = await res.text();
const $ = cheerio.load(html);
const deals: Deal[] = [];
$("h2.post-title a, .post h2 a, .post-title a").each((_, el) => {
const title = $(el).text().trim();
const link = $(el).attr("href") || site;
const priceMatch = title.match(/\$\s?(\d+[\,\d+]*)/);
const price = priceMatch ? parseInt(priceMatch[1].replace(/,/g, "")) : null;
const id = `dealsite-${Buffer.from(link).toString("base64").slice(0,16)}`;
deals.push({
id,
title,
source: new URL(site).host.replace("www.",""),
link,
price,
currency: "CAD",
origin: criteria.origin.toUpperCase(),
});
});
return deals.slice(0, 20); // don't flood
} catch (e) {
return [];
}
}