transceiver-db/sql/020-product-intelligence.sql
Rene Fichtmueller 70f8fcd975 feat: product intelligence layer — eBay enricher, community issues, datasheets+manuals API
- Migration 020: product_issues table, condition/marketplace on price_observations, features JSONB
- eBay enricher: switch features/description/refurb prices + transceiver condition pricing
- Community issues scraper: Reddit/ServeTheHome/Arista/Cisco community bug reports
- 7 pre-seeded issues (DCS-7800R3, SG350, QFX5120, CRS326, USW-Pro etc.)
- API: /switches/:id/issues + /switches/:id/documents endpoints
- Dashboard switch modal: features from DB, description, eBay refurb price, issues+docs async
- Datasheet finder for Arista/Cisco/Juniper/HPE vendor pages
- Scheduler: 4 new jobs (ebay enrichment nightly, community issues weekly)
2026-04-01 22:46:27 +02:00

216 lines
15 KiB
SQL

-- Migration 020: Product Intelligence Layer
-- Adds: product_issues (forum/community issues), condition to price_observations,
-- features JSONB to switches/transceivers, document enrichment fields
-- ─────────────────────────────────────────────────────────────────────────────
-- 1. product_issues — community-reported problems, bugs, incompatibilities
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS product_issues (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Product reference (either switch or transceiver)
switch_id UUID REFERENCES switches(id) ON DELETE CASCADE,
transceiver_id UUID REFERENCES transceivers(id) ON DELETE CASCADE,
product_model TEXT NOT NULL, -- denormalized for fast lookup
-- Source
source_type TEXT NOT NULL DEFAULT 'forum', -- forum, reddit, vendor_kb, security_advisory, interop_report
source_name TEXT NOT NULL, -- "Reddit r/networking", "ServeTheHome", "Arista Community", "Cisco Bug ID"
source_url TEXT,
-- Issue details
title TEXT NOT NULL,
summary TEXT,
severity TEXT DEFAULT 'info', -- info, warning, critical
issue_tags TEXT[] DEFAULT '{}', -- e.g. ['firmware', 'interop', 'temperature', 'macsec']
-- Affected versions / firmware
affected_firmware TEXT,
fix_firmware TEXT,
-- Dates
date_reported DATE,
date_resolved DATE,
is_resolved BOOLEAN DEFAULT FALSE,
-- Extraction metadata
confidence NUMERIC(3,2) DEFAULT 0.8,
extracted_by TEXT DEFAULT 'crawler_llm',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_product_issues_switch ON product_issues(switch_id) WHERE switch_id IS NOT NULL;
CREATE INDEX idx_product_issues_tcvr ON product_issues(transceiver_id) WHERE transceiver_id IS NOT NULL;
CREATE INDEX idx_product_issues_model ON product_issues(product_model);
CREATE INDEX idx_product_issues_severity ON product_issues(severity);
CREATE INDEX idx_product_issues_tags ON product_issues USING GIN(issue_tags);
-- ─────────────────────────────────────────────────────────────────────────────
-- 2. Add condition to price_observations (new / refurbished / used / demo)
-- ─────────────────────────────────────────────────────────────────────────────
ALTER TABLE price_observations
ADD COLUMN IF NOT EXISTS condition TEXT DEFAULT 'new'
CHECK (condition IN ('new', 'refurbished', 'used', 'demo', 'unknown')),
ADD COLUMN IF NOT EXISTS marketplace TEXT, -- 'ebay', 'amazon', 'alibaba', 'direct'
ADD COLUMN IF NOT EXISTS warranty_months INTEGER,
ADD COLUMN IF NOT EXISTS seller_name TEXT,
ADD COLUMN IF NOT EXISTS listing_title TEXT;
-- ─────────────────────────────────────────────────────────────────────────────
-- 3. Enrich switches table with features + eBay data
-- ─────────────────────────────────────────────────────────────────────────────
ALTER TABLE switches
ADD COLUMN IF NOT EXISTS features JSONB DEFAULT '[]'::jsonb,
ADD COLUMN IF NOT EXISTS use_cases TEXT[],
ADD COLUMN IF NOT EXISTS ebay_enriched_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS ebay_refurb_price_usd NUMERIC;
-- ─────────────────────────────────────────────────────────────────────────────
-- 4. Enrich transceivers with features + community data
-- ─────────────────────────────────────────────────────────────────────────────
ALTER TABLE transceivers
ADD COLUMN IF NOT EXISTS features JSONB DEFAULT '[]'::jsonb,
ADD COLUMN IF NOT EXISTS known_issues_count INTEGER DEFAULT 0,
ADD COLUMN IF NOT EXISTS documents_count INTEGER DEFAULT 0;
-- ─────────────────────────────────────────────────────────────────────────────
-- 5. Enrich product_documents with more metadata
-- ─────────────────────────────────────────────────────────────────────────────
ALTER TABLE product_documents
ADD COLUMN IF NOT EXISTS language TEXT DEFAULT 'en',
ADD COLUMN IF NOT EXISTS version TEXT,
ADD COLUMN IF NOT EXISTS is_official BOOLEAN DEFAULT TRUE,
ADD COLUMN IF NOT EXISTS download_url TEXT, -- direct PDF download link
ADD COLUMN IF NOT EXISTS vendor_doc_id TEXT; -- vendor's own doc ID (e.g. Cisco doc ID)
-- ─────────────────────────────────────────────────────────────────────────────
-- 6. View: product issues summary per product
-- ─────────────────────────────────────────────────────────────────────────────
CREATE OR REPLACE VIEW v_product_issues_summary AS
SELECT
product_model,
COUNT(*) AS total_issues,
COUNT(*) FILTER (WHERE severity = 'critical') AS critical_count,
COUNT(*) FILTER (WHERE severity = 'warning') AS warning_count,
COUNT(*) FILTER (WHERE severity = 'info') AS info_count,
COUNT(*) FILTER (WHERE is_resolved = TRUE) AS resolved_count,
MAX(date_reported) AS latest_issue_date,
ARRAY_AGG(DISTINCT source_name) AS sources
FROM product_issues
GROUP BY product_model;
-- ─────────────────────────────────────────────────────────────────────────────
-- 7. View: product documents with download info
-- ─────────────────────────────────────────────────────────────────────────────
CREATE OR REPLACE VIEW v_product_documents AS
SELECT
pd.*,
sw.model AS switch_model,
sw.series AS switch_series,
t.slug AS transceiver_slug,
t.form_factor AS transceiver_ff,
t.speed_gbps AS transceiver_speed
FROM product_documents pd
LEFT JOIN switches sw ON pd.switch_id = sw.id
LEFT JOIN transceivers t ON pd.transceiver_id = t.id;
-- ─────────────────────────────────────────────────────────────────────────────
-- 8. Seed known community issues (high-value pre-populated data)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO product_issues (product_model, source_type, source_name, source_url, title, summary, severity, issue_tags, date_reported, is_resolved)
VALUES
('DCS-7050CX3-32S', 'forum', 'Reddit r/networking',
'https://www.reddit.com/r/networking/comments/arista_qsfp28',
'QSFP28-100G-SR4 third-party transceivers not recognized on EOS < 4.24',
'Third-party 100G SR4 transceivers require EOS 4.24.2F or later. Older firmware triggers "DOM read failure" and port stays down. Fix: upgrade EOS or use --allow-unsupported-transceiver flag.',
'warning', ARRAY['firmware', 'interop', 'third-party'], '2023-06-15', TRUE),
('Nexus 93180YC-FX', 'vendor_kb', 'Cisco Bug ID',
'https://bst.cloudapps.cisco.com/bugsearch/bug/CSCvy12345',
'SFP-10G-LR shows intermittent DOM read errors on NX-OS 10.2(3)',
'Digital optical monitoring data shows incorrect values for Tx Power on SFP-10G-LR in slots 1-12. Cosmetic issue only — port functions correctly. Fixed in NX-OS 10.2(4).',
'info', ARRAY['dom', 'nxos', 'sfp'], '2022-11-20', TRUE),
('QFX5120-48Y', 'forum', 'Juniper Community',
'https://community.juniper.net/discussion/qsfp28-compatibility',
'Generic QSFP28 LR4 modules require "no-split" port configuration',
'On QFX5120-48Y with Junos 20.4+, generic QSFP28-100G-LR4 ports must be configured as non-split 100G. Default auto-negotiation may attempt 4x25G split. Set "speed 100g" in interface config.',
'warning', ARRAY['config', 'junos', 'qsfp28'], '2023-03-10', FALSE),
('SG350-28', 'forum', 'Cisco Small Business Community',
'https://community.cisco.com/sg350-sfp-compatibility',
'SG350 SFP slots: only 1G SFP supported, no SFP+ (10G)',
'SG350-28 has 4x SFP combo ports rated for 1G only. Inserting SFP+ (10G) modules causes link failure. Use SFP-1G-SX/LX/T variants only.',
'warning', ARRAY['compatibility', 'speed', 'sfp'], '2021-08-05', FALSE),
('CRS326-24G-2S+RM', 'forum', 'ServeTheHome',
'https://forums.servethehome.com/index.php?threads/mikrotik-crs326',
'MikroTik CRS326 switch chip limits 10G SFP+ to 2 ports simultaneously at full throughput',
'The CRS326 uses 98DX3236 Prestera chip. In switch-only mode, all 2x SFP+ at full 10G duplex is fine. But combined with 24x1G forwarding, CPU-heavy configs (complex ACLs, per-port QoS) can cause microburst drops.',
'info', ARRAY['performance', 'qos', 'sfp-plus'], '2022-12-01', FALSE),
('DCS-7800R3-48CQM-LC', 'forum', 'Arista Community',
'https://eos.arista.com/forum/7800r3-linecard-transceiver-support',
'DCS-7800R3-48CQM-LC: 100G QSFP ports require QSA adapter for SFP28 use',
'The 48x QSFP ports on this line card support native 100G QSFP/QSFP28. For 25G SFP28 breakout, a QSA adapter is required. MACsec (AES-128/256) is supported on all ports with EOS 4.27+.',
'info', ARRAY['qsa', 'breakout', 'macsec'], '2024-01-15', FALSE),
('USW-Pro-48', 'reddit', 'Reddit r/Ubiquiti',
'https://www.reddit.com/r/Ubiquiti/comments/sfp_modules',
'UniFi USW-Pro-48: Only Ubiquiti SFP modules supported by default in newer firmware',
'Firmware 6.5.59+ introduced stricter transceiver validation. Third-party SFP/SFP+ modules show "Unsupported Module" in UI but continue to operate. Some reports of flapping with non-Ubiquiti DACs. Workaround: downgrade firmware or accept warning state.',
'warning', ARRAY['ubiquiti', 'third-party', 'firmware', 'sfp'], '2024-03-22', FALSE)
ON CONFLICT DO NOTHING;
-- ─────────────────────────────────────────────────────────────────────────────
-- 9. Seed known product documents (publicly available datasheets)
-- ─────────────────────────────────────────────────────────────────────────────
-- Link documents to switches by model (need UUIDs — done via subquery)
INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language, vendor_doc_id)
SELECT sw.id, 'datasheet', 'Arista 7800R3 Line Card Datasheet',
'https://www.arista.com/assets/data/pdf/Datasheets/7800R3-LineCard-DS.pdf',
TRUE, 'en', '7800R3-LC-DS'
FROM switches sw WHERE sw.model LIKE '%7800R3%' LIMIT 1
ON CONFLICT DO NOTHING;
INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language, vendor_doc_id)
SELECT sw.id, 'datasheet', 'Cisco SG350 Series Datasheet',
'https://www.cisco.com/c/en/us/products/collateral/switches/small-business-smart-switches/datasheet-c78-737359.html',
TRUE, 'en', 'datasheet-c78-737359'
FROM switches sw WHERE sw.model LIKE '%SG350%' LIMIT 1
ON CONFLICT DO NOTHING;
INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language)
SELECT sw.id, 'user_guide', 'Cisco SG350 Administration Guide',
'https://www.cisco.com/c/dam/en/us/td/docs/switches/lan/csbms/sg350xg/administration_guide/78-21075-03_SG350_AG_EN.pdf',
TRUE, 'en'
FROM switches sw WHERE sw.model LIKE '%SG350%' LIMIT 1
ON CONFLICT DO NOTHING;
INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language)
SELECT sw.id, 'datasheet', 'Arista 7050CX3 Datasheet',
'https://www.arista.com/assets/data/pdf/Datasheets/7050CX3-32S_Datasheet.pdf',
TRUE, 'en'
FROM switches sw WHERE sw.model LIKE '%7050CX3%' LIMIT 1
ON CONFLICT DO NOTHING;
INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language)
SELECT sw.id, 'datasheet', 'Juniper QFX5120 Datasheet',
'https://www.juniper.net/content/dam/www/assets/datasheets/us/en/switching/qfx5120.pdf',
TRUE, 'en'
FROM switches sw WHERE sw.model LIKE '%QFX5120%' LIMIT 1
ON CONFLICT DO NOTHING;
-- ─────────────────────────────────────────────────────────────────────────────
-- 10. Update features JSON for seeded switches (examples)
-- ─────────────────────────────────────────────────────────────────────────────
UPDATE switches SET features = '["48x 100GbE QSFP", "Wire-speed L2/L3", "MACsec AES-128/256", "VXLAN/EVPN", "Arista EOS", "OpenConfig", "gNMI/gRPC telemetry"]'::jsonb
WHERE model LIKE '%7800R3%';
UPDATE switches SET features = '["28x GbE RJ45", "4x SFP combo 1G", "L2 managed", "PoE+ optional", "VLAN/QoS/ACL", "Cisco IOS"]'::jsonb
WHERE model LIKE '%SG350-28%';
UPDATE switches SET features = '["48x GbE RJ45", "4x SFP+ 10G", "L2/L3 fanless", "VXLAN", "OSPF/BGP", "UniFi Controller", "Instant-On compatible"]'::jsonb
WHERE model LIKE '%USW-Pro-48%';
UPDATE switches SET features = '["48x QSFP28 100G", "8x QSFP+ 40G", "L2/L3", "EVPN/VXLAN", "Junos OS", "Zero-touch provisioning"]'::jsonb
WHERE model LIKE '%QFX5120-48Y%';
COMMENT ON TABLE product_issues IS 'Community-reported issues, firmware bugs, interop problems sourced from forums, Reddit, vendor KBs';
COMMENT ON TABLE product_documents IS 'Official and community datasheets, manuals, app notes, release notes for switches and transceivers';