transceiver-db/sql/008-product-assets.sql
Rene Fichtmueller 4b452ab49e feat(scrapers+mcp): ATGBICS + ProLabs scrapers, MCP HTTP/SSE server
Scrapers:
- atgbics.ts: PlaywrightCrawler for UK vendor ATGBICS (Shopify store),
  scrapes SFP/SFP+/SFP28/QSFP+/QSFP28/QSFP-DD in GBP, max 50 pages/run
- prolabs.ts: HttpCrawler for ProLabs (Legrand subsidiary), USD pricing,
  category-driven crawl with reach/fiber/speed detection
- Both registered in scheduler (every 8h, staggered) and index.ts CLI

MCP HTTP Server:
- packages/mcp-server/src/http-server.ts: Express + SSEServerTransport
- Exposes all 12 TIP tools via GET /sse + POST /message
- Bearer token auth (MCP_SECRET env), CORS-configurable
- GET /health → { status: "ok", tools: 12 }
- Port: MCP_HTTP_PORT (default 3201)

SQL + tools:
- sql/006-009: seed scripts for whitebox switches, vendors, assets
- switch-docs.ts: MCP tool for switch documentation queries
2026-03-29 02:26:45 +08:00

70 lines
4.6 KiB
SQL

-- TIP: Transceiver Intelligence Platform
-- Migration 008: Product Assets (Images, Datasheets, Manuals)
--
-- Adds columns for product images, datasheet PDFs, and manual/guide links
-- to both switches and transceivers tables.
-- ═══════════════════════════════════════════════════════
-- SWITCHES: Product assets
-- ═══════════════════════════════════════════════════════
ALTER TABLE switches ADD COLUMN IF NOT EXISTS image_url TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS image_local_path TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS datasheet_url TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS datasheet_local_path TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS manual_urls JSONB DEFAULT '[]'::jsonb;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS quick_start_url TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS cli_reference_url TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS release_notes_url TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS product_page_url TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS eol_url TEXT;
ALTER TABLE switches ADD COLUMN IF NOT EXISTS assets_scraped_at TIMESTAMPTZ;
-- ═══════════════════════════════════════════════════════
-- TRANSCEIVERS: Product assets
-- ═══════════════════════════════════════════════════════
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS image_url TEXT;
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS image_local_path TEXT;
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS datasheet_url TEXT;
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS datasheet_local_path TEXT;
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS manual_urls JSONB DEFAULT '[]'::jsonb;
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS product_page_url TEXT;
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS assets_scraped_at TIMESTAMPTZ;
-- ═══════════════════════════════════════════════════════
-- VENDORS: Documentation portal URLs
-- ═══════════════════════════════════════════════════════
ALTER TABLE vendors ADD COLUMN IF NOT EXISTS docs_portal_url TEXT;
ALTER TABLE vendors ADD COLUMN IF NOT EXISTS datasheet_library_url TEXT;
ALTER TABLE vendors ADD COLUMN IF NOT EXISTS image_cdn_base TEXT;
ALTER TABLE vendors ADD COLUMN IF NOT EXISTS support_portal_url TEXT;
-- ═══════════════════════════════════════════════════════
-- DOCUMENTS table for downloaded PDFs (datasheets, manuals)
-- ═══════════════════════════════════════════════════════
CREATE TABLE IF NOT EXISTS product_documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
switch_id UUID REFERENCES switches(id) ON DELETE CASCADE,
transceiver_id UUID REFERENCES transceivers(id) ON DELETE CASCADE,
vendor_id UUID REFERENCES vendors(id) ON DELETE SET NULL,
doc_type TEXT NOT NULL CHECK (doc_type IN ('datasheet', 'manual', 'quick_start', 'cli_reference', 'release_notes', 'installation_guide', 'compatibility_matrix', 'eol_notice')),
title TEXT NOT NULL,
source_url TEXT NOT NULL,
local_path TEXT,
r2_key TEXT,
file_size_bytes BIGINT,
content_hash TEXT,
page_count INTEGER,
language TEXT DEFAULT 'en',
extracted_text TEXT,
indexed_at TIMESTAMPTZ,
downloaded_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT chk_doc_ref CHECK (switch_id IS NOT NULL OR transceiver_id IS NOT NULL)
);
CREATE INDEX IF NOT EXISTS idx_product_docs_switch ON product_documents(switch_id) WHERE switch_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_product_docs_transceiver ON product_documents(transceiver_id) WHERE transceiver_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_product_docs_type ON product_documents(doc_type);
CREATE INDEX IF NOT EXISTS idx_product_docs_vendor ON product_documents(vendor_id);
CREATE INDEX IF NOT EXISTS idx_product_docs_hash ON product_documents(content_hash);