Rene Fichtmueller 3a00ff4d33 feat: initial llm-gateway implementation
- Complete Fastify gateway with 8-stage pipeline
- Circuit breaker (opossum) per model tier
- Rate limiting per caller
- Ban list validation (EN/DE/auto-detected)
- TIP validator (SFF-8024, part numbers, wavelengths)
- Prometheus metrics
- pg-boss async queue
- PostgreSQL audit log + review queue
- 9 prompt templates (TIP, LinkedIn, ShieldX)
- Learning engine scaffolding
- Auto-learning: ban-list, few-shot, routing, prompt optimizer
2026-04-02 22:48:55 +02:00

153 lines
4.1 KiB
TypeScript

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'] ?? '217.154.82.179',
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'] ?? 'tip_prod_2026',
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<TransceiverRecord | null> {
const pool = getTipPool();
try {
const result = await pool.query<TransceiverRecord>(
`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<TransceiverRecord[]> {
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<TransceiverRecord>(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<PriceRecord[]> {
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<PriceRecord>(sql, params);
return result.rows;
} catch (err) {
logger.warn({ err, partNumber }, 'TIP DB price history lookup failed');
return [];
}
}
export async function getVendorList(): Promise<string[]> {
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<void> {
if (tipPool) {
await tipPool.end();
tipPool = null;
}
}
export async function testTipConnection(): Promise<boolean> {
const pool = getTipPool();
try {
await pool.query('SELECT 1');
return true;
} catch (err) {
logger.warn({ err }, 'TIP DB connection test failed');
return false;
}
}