import { Router, Request, Response } from "express"; import { searchTransceivers, getTransceiverById } from "../db/queries"; import { pool } from "../db/client"; export const transceiverRouter = Router(); // GET /api/transceivers — Search/list transceivers transceiverRouter.get("/", async (req: Request, res: Response) => { try { const q = (p: string) => req.query[p] ? String(req.query[p]) : undefined; const result = await searchTransceivers({ q: q("q"), form_factor: q("form_factor"), speed: q("speed"), speed_gbps: q("speed_gbps") ? parseFloat(q("speed_gbps")!) : undefined, category: q("category"), fiber_type: q("fiber_type"), reach_min: q("reach_min") ? parseInt(q("reach_min")!) : undefined, reach_max: q("reach_max") ? parseInt(q("reach_max")!) : undefined, wdm_type: q("wdm_type"), coherent: q("coherent") === "true" ? true : q("coherent") === "false" ? false : undefined, market_status: q("market_status"), vendor: q("vendor"), verified: q("verified") as "price" | "image" | "details" | "full" | undefined, limit: q("limit") ? parseInt(q("limit")!) : 50, offset: q("offset") ? parseInt(q("offset")!) : 0, }); res.json({ success: true, ...result }); } catch (err) { console.error("Search transceivers error:", err); res.status(500).json({ success: false, error: "Internal server error" }); } }); // GET /api/transceivers/:id — Get single transceiver with latest prices per vendor transceiverRouter.get("/:id", async (req: Request, res: Response) => { try { const transceiver = await getTransceiverById(String(req.params.id)); if (!transceiver) { res.status(404).json({ success: false, error: "Transceiver not found" }); return; } // Latest price per source vendor — last 30 days, exclude anomalous prices const pricesResult = await pool.query( `SELECT DISTINCT ON (po.source_vendor_id) po.price, po.currency, po.url, po.time, po.stock_level, po.is_verified, v.name AS vendor_name, v.type AS vendor_type, v.website AS vendor_website FROM price_observations po JOIN vendors v ON po.source_vendor_id = v.id WHERE po.transceiver_id = $1 AND po.time > NOW() - INTERVAL '30 days' AND COALESCE(po.is_anomalous, false) = false ORDER BY po.source_vendor_id, po.time DESC`, [transceiver.id] ); // Flag: price is only "verified" if observed within 7 days with real URL const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); const prices = pricesResult.rows.map((row) => ({ vendor_name: row.vendor_name, vendor_type: row.vendor_type, vendor_website: row.vendor_website, price: parseFloat(row.price), currency: row.currency, url: row.url, stock_level: row.stock_level, observed_at: row.time, is_verified: !!(row.url && new Date(row.time) > sevenDaysAgo), is_same_product: true, })); // Market comparison: technically equivalent products from other vendors // Same form_factor + speed_gbps + reach within 20% tolerance, different vendor // Only real scraped prices (url IS NOT NULL), last 30 days, max 1 per source vendor const comparableResult = await pool.query( `SELECT DISTINCT ON (po.source_vendor_id) po.price, po.currency, po.url, po.time, sv.name AS vendor_name, sv.type AS vendor_type, t2.part_number, t2.standard_name, t2.id AS comparable_id FROM transceivers t1 JOIN transceivers t2 ON ( t2.form_factor = t1.form_factor AND t2.speed_gbps = t1.speed_gbps AND ( t1.reach_meters IS NULL OR t1.reach_meters = 0 OR t2.reach_meters IS NULL OR t2.reach_meters = 0 OR ABS(t2.reach_meters - t1.reach_meters) <= GREATEST(t1.reach_meters, 1) * 0.25 ) AND t2.id != t1.id ) JOIN price_observations po ON po.transceiver_id = t2.id JOIN vendors sv ON po.source_vendor_id = sv.id WHERE t1.id = $1 AND po.time > NOW() - INTERVAL '30 days' AND po.price > 0 AND po.url IS NOT NULL AND COALESCE(po.is_anomalous, false) = false -- Exclude vendors that already appear in direct prices AND sv.id NOT IN ( SELECT source_vendor_id FROM price_observations WHERE transceiver_id = $1 AND time > NOW() - INTERVAL '30 days' ) ORDER BY po.source_vendor_id, po.time DESC LIMIT 10`, [transceiver.id] ); const comparablePrices = comparableResult.rows.map((row) => ({ vendor_name: row.vendor_name, vendor_type: row.vendor_type, price: parseFloat(row.price), currency: row.currency, url: row.url, observed_at: row.time, is_verified: !!(row.url && new Date(row.time) > sevenDaysAgo), is_same_product: false, // different SKU, same spec class comparable_part: row.part_number || row.standard_name, comparable_id: row.comparable_id, })); const allPrices = [...prices, ...comparablePrices]; // Price anomaly detection: flag if max/min ratio > 10x (same-product prices only) const samePricesEur = allPrices .filter((p) => p.is_same_product && p.price > 0) .map((p) => { // Normalize to EUR for comparison if (p.currency === "EUR") return p.price; if (p.currency === "USD") return p.price * 0.92; if (p.currency === "GBP") return p.price * 1.17; return p.price; }); let priceAnomaly: { ratio: number; min_eur: number; max_eur: number } | null = null; if (samePricesEur.length >= 2) { const minEur = Math.min(...samePricesEur); const maxEur = Math.max(...samePricesEur); const ratio = minEur > 0 ? Math.round((maxEur / minEur) * 10) / 10 : 0; if (ratio >= 10) { priceAnomaly = { ratio, min_eur: Math.round(minEur * 100) / 100, max_eur: Math.round(maxEur * 100) / 100 }; } } // Last time ANY competitor scraper looked at this transceiver (regardless of result) const lastScanResult = await pool.query( `SELECT MAX(po.time) AS last_scan FROM price_observations po JOIN vendors v ON po.source_vendor_id = v.id WHERE po.transceiver_id = $1 AND v.is_competitor = true`, [transceiver.id] ); const lastCompetitorScan = lastScanResult.rows[0]?.last_scan ?? null; // Has any competitor ever listed a price for this exact product? const competitorHasProduct = prices.some( (p) => p.vendor_type !== "flexoptix" && p.price > 0 ); res.json({ success: true, data: { ...transceiver, competitor_prices: allPrices, price_anomaly: priceAnomaly, last_competitor_scan: lastCompetitorScan, competitor_has_product: competitorHasProduct, }, }); } catch (err) { console.error("Get transceiver error:", err); res.status(500).json({ success: false, error: "Internal server error" }); } }); // GET /api/transceivers/:id/compatibility — Compatible switches for a transceiver transceiverRouter.get("/:id/compatibility", async (req: Request, res: Response) => { try { const result = await pool.query( `SELECT sw.id, sw.model, sw.series, sw.category, sw.total_ports, sw.max_speed_gbps, sw.switching_capacity_tbps, sw.lifecycle_status, v.name as vendor_name, c.status, c.notes as compat_notes FROM compatibility c JOIN switches sw ON c.switch_id = sw.id LEFT JOIN vendors v ON sw.vendor_id = v.id WHERE c.transceiver_id::text = $1 AND c.status = 'compatible' ORDER BY v.name, sw.model`, [String(req.params.id)] ); res.json({ success: true, data: result.rows }); } catch (err) { console.error("Get transceiver compatibility error:", err); res.status(500).json({ success: false, error: "Internal server error" }); } });