Rene Fichtmueller de5bdb24ca Initial TIP foundation: schema, seed data, crawlers, API, MCP server
- PostgreSQL 17 + TimescaleDB schema with 12 tables
- 48 standards (IEEE, SFF, ITU-T, OIF, MSA)
- 33 form factors (SFP through OSFP-XD/CPO)
- 85+ vendors (OEM, compatible, manufacturers, marketplaces)
- 80+ seed transceivers (1G-1.6T, CWDM, BiDi, DAC, AOC, FC, PON)
- 60+ network devices (Cisco, Juniper, Arista, HPE, Dell, etc.)
- Crawler framework with fs.com and eBay crawlers
- REST API (15 endpoints) on port 3200
- MCP server (12 tools) on port 3201
- PM2 ecosystem for production deployment on Erik (.82)
2026-03-31 08:11:49 +02:00

143 lines
6.0 KiB
TypeScript

import 'dotenv/config';
import { query, transaction } from '../src/utils/db.js';
import { standards } from '../seed/standards.js';
import { formFactors } from '../seed/form-factors.js';
import { vendors } from '../seed/vendors.js';
import { transceivers } from '../seed/transceivers.js';
import { networkDevices } from '../seed/network-devices.js';
import { pino } from 'pino';
const log = pino({ name: 'seed' });
async function seedStandards() {
log.info(`Seeding ${standards.length} standards...`);
for (const s of standards) {
await query(
`INSERT INTO standards (name, body, version, year, url, description)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (name) DO UPDATE SET version = $3, year = $4, url = $5, description = $6, updated_at = NOW()`,
[s.name, s.body, s.version ?? null, s.year ?? null, s.url ?? null, s.description ?? null]
);
}
log.info('Standards seeded');
}
async function seedFormFactors() {
log.info(`Seeding ${formFactors.length} form factors...`);
for (const ff of formFactors) {
await query(
`INSERT INTO form_factors (name, full_name, lanes, max_data_rate, data_rate_unit, width_mm, height_mm, depth_mm, power_max_w, generation, release_year, eol_year, description, image_url)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT (name) DO UPDATE SET full_name = $2, lanes = $3, max_data_rate = $4, power_max_w = $9, updated_at = NOW()`,
[ff.name, ff.full_name ?? null, ff.lanes ?? null, ff.max_data_rate ?? null, ff.data_rate_unit ?? 'Gbps',
ff.width_mm ?? null, ff.height_mm ?? null, ff.depth_mm ?? null, ff.power_max_w ?? null,
ff.generation ?? null, ff.release_year ?? null, ff.eol_year ?? null, ff.description ?? null, ff.image_url ?? null]
);
}
log.info('Form factors seeded');
}
async function seedVendors() {
log.info(`Seeding ${vendors.length} vendors...`);
for (const v of vendors) {
await query(
`INSERT INTO vendors (name, slug, vendor_type, website, logo_url, country, founded_year, description, is_oem, is_factory, aliases, scrape_url, scrape_enabled)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
ON CONFLICT (slug) DO UPDATE SET name = $1, website = $4, description = $8, scrape_url = $12, scrape_enabled = $13, updated_at = NOW()`,
[v.name, v.slug, v.vendor_type, v.website ?? null, v.logo_url ?? null,
v.country ?? null, v.founded_year ?? null, v.description ?? null,
v.is_oem ?? false, v.is_factory ?? false, v.aliases ?? null,
v.scrape_url ?? null, v.scrape_enabled ?? false]
);
}
log.info('Vendors seeded');
}
async function seedTransceivers() {
log.info(`Seeding ${transceivers.length} transceivers...`);
// Build lookup maps
const vendorRows = await query('SELECT id, slug FROM vendors');
const vendorMap = new Map(vendorRows.rows.map(r => [r.slug, r.id]));
const ffRows = await query('SELECT id, name FROM form_factors');
const ffMap = new Map(ffRows.rows.map(r => [r.name, r.id]));
for (const t of transceivers) {
const vendorId = vendorMap.get(t.vendor_slug);
if (!vendorId) { log.warn(`Vendor not found: ${t.vendor_slug}`); continue; }
const ffId = ffMap.get(t.form_factor) ?? null;
const oemVendorId = t.oem_vendor_slug ? vendorMap.get(t.oem_vendor_slug) ?? null : null;
await query(
`INSERT INTO transceivers (part_number, vendor_id, form_factor_id, name, category, subcategory,
data_rate, data_rate_unit, max_reach, reach_unit, wavelength_nm, wavelength_rx,
connector, fiber_type, duplex, temp_range, dom_support,
oem_part_number, oem_vendor_id, description, tags, status, source)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, 'seed')
ON CONFLICT (part_number, vendor_id) DO UPDATE SET
name = $4, category = $5, subcategory = $6, data_rate = $7, max_reach = $9,
wavelength_nm = $11, tags = $21, updated_at = NOW()`,
[
t.part_number, vendorId, ffId, t.name, t.category, t.subcategory,
t.data_rate, t.data_rate_unit, t.max_reach, t.reach_unit,
t.wavelength_nm, t.wavelength_rx ?? null,
t.connector, t.fiber_type, t.duplex, t.temp_range, t.dom_support,
t.oem_part_number ?? null, oemVendorId, t.description ?? null,
t.tags, t.status
]
);
}
log.info('Transceivers seeded');
}
async function seedNetworkDevices() {
log.info(`Seeding ${networkDevices.length} network devices...`);
const vendorRows = await query('SELECT id, slug FROM vendors');
const vendorMap = new Map(vendorRows.rows.map(r => [r.slug, r.id]));
for (const d of networkDevices) {
const vendorId = vendorMap.get(d.vendor_slug);
if (!vendorId) { log.warn(`Vendor not found: ${d.vendor_slug}`); continue; }
await query(
`INSERT INTO network_devices (vendor_id, model, series, device_type,
ports_sfp, ports_sfp_plus, ports_sfp28, ports_qsfp_plus, ports_qsfp28, ports_qsfp_dd, ports_osfp, ports_rj45,
max_throughput, release_year, status)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
ON CONFLICT (vendor_id, model) DO UPDATE SET
series = $3, max_throughput = $13, updated_at = NOW()`,
[
vendorId, d.model, d.series, d.device_type,
d.ports_sfp ?? 0, d.ports_sfp_plus ?? 0, d.ports_sfp28 ?? 0,
d.ports_qsfp_plus ?? 0, d.ports_qsfp28 ?? 0, d.ports_qsfp_dd ?? 0,
d.ports_osfp ?? 0, d.ports_rj45 ?? 0,
d.max_throughput ?? null, d.release_year ?? null, d.status
]
);
}
log.info('Network devices seeded');
}
async function main() {
log.info('Starting TIP seed...');
const start = Date.now();
await seedStandards();
await seedFormFactors();
await seedVendors();
await seedTransceivers();
await seedNetworkDevices();
const duration = ((Date.now() - start) / 1000).toFixed(1);
log.info(`Seed complete in ${duration}s`);
process.exit(0);
}
main().catch(err => {
log.error({ err }, 'Seed failed');
process.exit(1);
});