/** * WS2: Automated Datasheet Generation * * Generates professional PDF datasheets for transceivers and switches. * Uses HTML templates rendered via the dashboard's static serving. * Returns structured HTML that can be printed to PDF client-side or via Puppeteer. */ import { Router } from "express"; import { pool } from "../db/client"; export const datasheetRouter = Router(); /** * GET /api/datasheets/transceiver/:id * Returns structured datasheet data for a transceiver (JSON or HTML) */ datasheetRouter.get("/transceiver/:id", async (req, res) => { try { const { format = "json" } = req.query; const id = req.params.id; // Get transceiver with full details const t = await pool.query( `SELECT t.*, v.name AS vendor_name, v.website AS vendor_website, v.logo_r2_key, s.name AS standard_full_name, s.ieee_reference, s.year_ratified FROM transceivers t LEFT JOIN vendors v ON t.vendor_id = v.id LEFT JOIN standards s ON t.standard_id = s.id WHERE t.slug = $1 OR t.id::text = $1`, [id] ); if (!t.rows[0]) return res.status(404).json({ error: "Transceiver not found" }); const product = t.rows[0]; // Get compatible switches (top 30) const compat = await pool.query( `SELECT sw.model, sw.series, sv.name AS vendor, c.firmware_min, c.verified_by FROM compatibility c JOIN switches sw ON c.switch_id = sw.id JOIN vendors sv ON sw.vendor_id = sv.id WHERE c.transceiver_id = $1 AND c.status = 'compatible' ORDER BY sv.name, sw.model LIMIT 30`, [product.id] ); // Get latest prices from multiple vendors const prices = await pool.query( `SELECT DISTINCT ON (sv.name) sv.name AS vendor, po.price, po.currency, po.stock_level, po.url, po.time FROM price_observations po JOIN vendors sv ON po.source_vendor_id = sv.id WHERE po.transceiver_id = $1 ORDER BY sv.name, po.time DESC`, [product.id] ); // Power budget calculation const txPowerMin = product.tx_power_min_dbm ? parseFloat(product.tx_power_min_dbm) : null; const txPowerMax = product.tx_power_max_dbm ? parseFloat(product.tx_power_max_dbm) : null; const rxSensitivity = product.rx_sensitivity_dbm ? parseFloat(product.rx_sensitivity_dbm) : null; const opticalBudget = product.optical_budget_db ? parseFloat(product.optical_budget_db) : null; let powerBudget = null; if (txPowerMin !== null && rxSensitivity !== null) { const budget = txPowerMin - rxSensitivity; const connectorLoss = 0.5; // 2 connectors × 0.25 dB const spliceLoss = 0.1; const fiberLoss = product.fiber_type === 'SMF' ? (product.reach_meters / 1000) * 0.35 // 0.35 dB/km at 1310nm : (product.reach_meters / 1000) * 3.5; // 3.5 dB/km at 850nm MMF const margin = budget - fiberLoss - connectorLoss - spliceLoss; powerBudget = { tx_power_min_dbm: txPowerMin, tx_power_max_dbm: txPowerMax, rx_sensitivity_dbm: rxSensitivity, link_budget_db: Math.round(budget * 10) / 10, fiber_loss_db: Math.round(fiberLoss * 10) / 10, connector_loss_db: connectorLoss, splice_loss_db: spliceLoss, margin_db: Math.round(margin * 10) / 10, sufficient: margin >= 3, }; } const datasheet = { product: { slug: product.slug, part_number: product.part_number, vendor: product.vendor_name, standard: product.standard_full_name || product.standard_name, ieee_reference: product.ieee_reference, form_factor: product.form_factor, speed: product.speed, speed_gbps: parseFloat(product.speed_gbps), lanes: product.lanes, lane_rate: product.lane_rate, modulation: product.modulation, reach_label: product.reach_label, reach_meters: product.reach_meters, fiber_type: product.fiber_type, wavelengths: product.wavelengths, connector: product.connector, power_consumption_w: product.power_consumption_w ? parseFloat(product.power_consumption_w) : null, temp_range: product.temp_range, dom_support: product.dom_support, category: product.category, market_status: product.market_status, image_url: product.image_url, }, optical: powerBudget, wdm: product.wdm_type ? { type: product.wdm_type, channels: product.channel_count, spacing_ghz: product.channel_spacing_ghz ? parseFloat(product.channel_spacing_ghz) : null, tunable: product.tunable, itu_grid: product.itu_grid, } : null, coherent: product.coherent ? { baud_rate_gbaud: product.baud_rate_gbaud ? parseFloat(product.baud_rate_gbaud) : null, fec_type: product.fec_type, dsp_vendor: product.dsp_vendor, } : null, compatible_switches: compat.rows, pricing: prices.rows.map(p => ({ vendor: p.vendor, price: parseFloat(p.price), currency: p.currency, stock: p.stock_level, url: p.url, as_of: p.time, })), flexoptix: { buy_url: `https://www.flexoptix.net/en/catalogsearch/result/?q=${encodeURIComponent(product.form_factor + ' ' + product.speed_gbps + 'G ' + product.reach_label)}`, flexbox_note: "Flexoptix transceivers support FlexBox coding — one module works in any vendor's switch.", }, generated_at: new Date().toISOString(), }; if (format === "html") { res.setHeader("Content-Type", "text/html"); res.send(renderDatasheetHtml(datasheet)); } else { res.json(datasheet); } } catch (err) { console.error("Datasheet error:", err); res.status(500).json({ error: "Internal server error" }); } }); /** * GET /api/datasheets/switch/:id */ datasheetRouter.get("/switch/:id", async (req, res) => { try { const id = req.params.id; const sw = await pool.query( `SELECT sw.*, v.name AS vendor_name, v.website AS vendor_website FROM switches sw JOIN vendors v ON sw.vendor_id = v.id WHERE sw.model = $1 OR sw.id::text = $1`, [id] ); if (!sw.rows[0]) return res.status(404).json({ error: "Switch not found" }); const device = sw.rows[0]; const compat = await pool.query( `SELECT t.form_factor, t.speed, t.speed_gbps, t.reach_label, t.fiber_type, tv.name AS transceiver_vendor, c.firmware_min FROM compatibility c JOIN transceivers t ON c.transceiver_id = t.id JOIN vendors tv ON t.vendor_id = tv.id WHERE c.switch_id = $1 AND c.status = 'compatible' ORDER BY t.speed_gbps DESC, tv.name LIMIT 50`, [device.id] ); const docs = await pool.query( `SELECT doc_type, title, source_url FROM product_documents WHERE switch_id = $1 ORDER BY doc_type`, [device.id] ); res.json({ switch: { model: device.model, series: device.series, vendor: device.vendor_name, category: device.category, ports_config: device.ports_config, total_ports: device.total_ports, max_speed_gbps: device.max_speed_gbps, switching_capacity_tbps: device.switching_capacity_tbps, asic: device.asic_vendor ? `${device.asic_vendor} ${device.asic_model || ''}`.trim() : null, sonic_compatible: device.sonic_compatible, image_url: device.image_url, }, compatible_transceivers: compat.rows, documents: docs.rows, generated_at: new Date().toISOString(), }); } catch (err) { console.error("Switch datasheet error:", err); res.status(500).json({ error: "Internal server error" }); } }); /** * GET /api/datasheets/list — recently generated datasheets */ datasheetRouter.get("/list", async (_req, res) => { try { const result = await pool.query( `SELECT * FROM generated_datasheets ORDER BY generated_at DESC LIMIT 50` ); res.json({ datasheets: result.rows }); } catch (err) { res.status(500).json({ error: "Internal server error" }); } }); function renderDatasheetHtml(ds: any): string { const p = ds.product; const prices = ds.pricing.map((pr: any) => `
${p.standard || ''} ${p.ieee_reference ? '(' + p.ieee_reference + ')' : ''}
| Vendor | Price | Stock |
|---|
| Vendor | Model | Min Firmware |
|---|
${ds.flexoptix.flexbox_note}