import pg from 'pg'; import { logger } from '../observability/logger.js'; const { Pool } = pg; // TIP database on Erik (IONOS VPS) const TIP_DB_CONFIG = { host: process.env['TIP_DB_HOST'] ?? '82.165.222.127', port: parseInt(process.env['TIP_DB_PORT'] ?? '5433', 10), database: process.env['TIP_DB_NAME'] ?? 'transceiver_db', user: process.env['TIP_DB_USER'] ?? 'tip', password: process.env['TIP_DB_PASSWORD']!, max: 5, idleTimeoutMillis: 60_000, connectionTimeoutMillis: 10_000, ssl: process.env['TIP_DB_SSL'] === 'true' ? { rejectUnauthorized: false } : false, }; let tipPool: pg.Pool | null = null; function getTipPool(): pg.Pool { if (!tipPool) { tipPool = new Pool(TIP_DB_CONFIG); tipPool.on('error', (err) => { logger.error({ err }, 'TIP database pool error'); }); tipPool.on('connect', () => { logger.debug('TIP database connection established'); }); } return tipPool; } export interface TransceiverRecord { id: string; part_number: string; vendor: string; form_factor: string; data_rate_gbps: number; wavelength_nm: number | null; fiber_type: string; connector: string; reach_m: number | null; temperature_class: string; price_usd: number | null; compatible_with: string[]; sff8024_identifier: string | null; created_at: string; updated_at: string; } export interface PriceRecord { vendor: string; part_number: string; price_usd: number; currency: string; source_url: string; scraped_at: string; } export async function lookupTransceiver(partNumber: string): Promise { const pool = getTipPool(); try { const result = await pool.query( `SELECT * FROM transceivers WHERE UPPER(part_number) = UPPER($1) LIMIT 1`, [partNumber], ); return result.rows[0] ?? null; } catch (err) { logger.warn({ err, partNumber }, 'TIP DB transceiver lookup failed'); return null; } } export async function lookupByFormFactor( formFactor: string, dataRateGbps?: number, ): Promise { const pool = getTipPool(); try { const params: unknown[] = [formFactor]; let sql = `SELECT * FROM transceivers WHERE UPPER(form_factor) = UPPER($1)`; if (dataRateGbps !== undefined) { params.push(dataRateGbps); sql += ` AND data_rate_gbps = $2`; } sql += ` ORDER BY price_usd ASC NULLS LAST LIMIT 20`; const result = await pool.query(sql, params); return result.rows; } catch (err) { logger.warn({ err, formFactor }, 'TIP DB form factor lookup failed'); return []; } } export async function getPriceHistory( partNumber: string, vendor?: string, daysBack = 30, ): Promise { const pool = getTipPool(); try { const params: unknown[] = [partNumber, daysBack]; let sql = ` SELECT vendor, part_number, price_usd, currency, source_url, scraped_at FROM price_history WHERE UPPER(part_number) = UPPER($1) AND scraped_at > NOW() - INTERVAL '$2 days' `; if (vendor) { params.push(vendor); sql += ` AND UPPER(vendor) = UPPER($${params.length})`; } sql += ` ORDER BY scraped_at DESC LIMIT 100`; const result = await pool.query(sql, params); return result.rows; } catch (err) { logger.warn({ err, partNumber }, 'TIP DB price history lookup failed'); return []; } } export async function getVendorList(): Promise { const pool = getTipPool(); try { const result = await pool.query<{ vendor: string }>( `SELECT DISTINCT vendor FROM transceivers WHERE vendor IS NOT NULL ORDER BY vendor`, ); return result.rows.map((r) => r.vendor); } catch (err) { logger.warn({ err }, 'TIP DB vendor list lookup failed'); return []; } } export async function closeTipPool(): Promise { if (tipPool) { await tipPool.end(); tipPool = null; } } export async function testTipConnection(): Promise { const pool = getTipPool(); try { await pool.query('SELECT 1'); return true; } catch (err) { logger.warn({ err }, 'TIP DB connection test failed'); return false; } }