import express from "express"; import cors from "cors"; import helmet from "helmet"; import rateLimit from "express-rate-limit"; import { join } from "path"; import { cfg } from "./config"; import { authRouter } from "./routes/auth"; import { requireAuth } from "./middleware/require-auth"; import { transceiverRouter } from "./routes/transceivers"; import { switchRouter } from "./routes/switches"; import { vendorRouter } from "./routes/vendors"; import { standardRouter } from "./routes/standards"; import { healthRouter } from "./routes/health"; import { hypeCycleRouter } from "./routes/hype-cycle"; import { searchRouter } from "./routes/search"; import { documentRouter } from "./routes/documents"; import { blogRouter } from "./routes/blog"; import { blogSllRouter } from "./routes/blog-sll"; import { scraperRouter } from "./routes/scrapers"; import { finderRouter } from "./routes/finder"; import { competitorRouter } from "./routes/competitor-alerts"; import { forecastRouter } from "./routes/forecast"; import { transportRouter } from "./routes/transport"; import { datasheetRouter } from "./routes/datasheets"; import { hotTopicsRouter } from "./routes/hot-topics"; import { adoptionRouter } from "./routes/adoption"; import { procurementRouter } from "./routes/procurement"; import { changelogRouter } from "./routes/changelog"; import { newsRouter } from "./routes/news"; import { proxyRouter } from "./routes/proxy"; import { reviewRouter } from "./routes/review"; import { stockRouter } from "./routes/stock"; import { priceComparisonRouter } from "./routes/price-comparison"; import { selflearningRouter } from "./routes/selflearning"; import { internalDemandRouter } from "./routes/internal-demand"; import { formFactorsRouter } from "./routes/form-factors"; import { tipLlmRouter } from "./routes/tip-llm"; import { equivalencesRouter } from "./routes/equivalences"; import { priceHistoryRouter } from "./routes/price-history"; import { kbRouter } from "./routes/kb"; import { bulkPriceRouter } from "./routes/bulk-price"; import { vendorReliabilityRouter } from "./routes/vendor-reliability"; import { priceForecastRouter } from "./routes/price-forecast"; import { priceMatrixRouter } from "./routes/price-matrix"; import { trainingRouter } from "./routes/training"; import { rfqRouter } from "./routes/rfq"; import { priceAlertsRouter } from "./routes/price-alerts"; import { winLossRouter } from "./routes/win-loss"; import { apiKeysRouter } from "./routes/api-keys"; import { roiRouter } from "./routes/roi"; const app = express(); // Trust Cloudflare proxy (required for express-rate-limit with X-Forwarded-For) app.set("trust proxy", 1); // Middleware app.use(helmet({ contentSecurityPolicy: false })); app.use(cors()); app.use(express.json({ limit: "30mb" })); // 30MB to support base64-encoded PDF uploads app.use( rateLimit({ windowMs: 60 * 1000, max: 200, standardHeaders: true, legacyHeaders: false, }) ); // Auth (public — no requireAuth here) app.use("/api/auth", authRouter); // Proxy public endpoints (register + heartbeat + stats + next — no auth) app.use("/api/proxy", proxyRouter); // All other API routes require a valid token app.use("/api", (req, res, next) => { // Always allow: health check, auth endpoints, proxy public routes, hot-topics (public market data) if (req.path.startsWith("/health") || req.path.startsWith("/auth")) return next(); if (req.path.startsWith("/proxy")) return next(); if (req.path.startsWith("/hot-topics")) return next(); if (req.path.startsWith("/price-comparison")) return next(); if (req.path.startsWith("/training")) return next(); requireAuth(req, res, next); }); // Routes app.use("/api/transceivers", transceiverRouter); app.use("/api/switches", switchRouter); app.use("/api/vendors", vendorRouter); app.use("/api/standards", standardRouter); app.use("/api/health", healthRouter); app.use("/api/hype-cycle", hypeCycleRouter); app.use("/api/search", searchRouter); app.use("/api/documents", documentRouter); app.use("/api/blog", blogSllRouter); app.use("/api/blog", blogRouter); app.use("/api/scrapers", scraperRouter); app.use("/api/finder", finderRouter); app.use("/api/competitor-alerts", competitorRouter); app.use("/api/forecast", forecastRouter); app.use("/api/transport", transportRouter); app.use("/api/datasheets", datasheetRouter); app.use("/api/adoption", adoptionRouter); app.use("/api/hot-topics", hotTopicsRouter); app.use("/api/procurement", procurementRouter); app.use("/api/changelog", changelogRouter); app.use("/api/news", newsRouter); app.use("/api/review", reviewRouter); app.use("/api/stock", stockRouter); app.use("/api/price-comparison", priceComparisonRouter); app.use("/api/selflearning", selflearningRouter); // Form Factors reference app.use("/api/form-factors", formFactorsRouter); // Internal-only — restricted to localhost / LAN by the router itself app.use("/api/internal/demand", internalDemandRouter); // tip-llm-v1 guided inference app.use("/api/tip-llm", tipLlmRouter); // Equivalences (cross-brand alternatives) app.use("/api/equivalences", equivalencesRouter); // Price history charts app.use("/api/price-history", priceHistoryRouter); app.use("/api/kb", kbRouter); // Bulk price lookup (G) app.use("/api/bulk-price", bulkPriceRouter); // Vendor reliability scores (I) app.use("/api/vendors/reliability", vendorReliabilityRouter); // Price forecast (O) app.use("/api/price-forecast", priceForecastRouter); // Price matrix / heat map (J) app.use("/api/price-matrix", priceMatrixRouter); // Transceiver Academy — public training content (no auth required) app.use("/api/training", trainingRouter); // RFQ Analyzer — quote vs market comparison app.use("/api/rfq", rfqRouter); // Price Alert Subscriptions app.use("/api/price-alerts", priceAlertsRouter); // Win/Loss Intelligence app.use("/api/win-loss", winLossRouter); // Customer API Key Management app.use("/api/api-keys", apiKeysRouter); // ROI Calculator app.use("/api/roi", roiRouter); // Dashboard (static HTML) app.use("/dashboard", express.static(join(__dirname, "..", "..", "dashboard"))); // Root — redirect to dashboard app.get("/", (_req, res) => { res.redirect("/dashboard/"); }); // API info app.get("/api", (_req, res) => { res.json({ name: "Transceiver Intelligence Platform", version: "0.3.0", endpoints: [ "GET /api/transceivers?q=&form_factor=&speed=&category=&fiber_type=&wdm_type=&coherent=", "GET /api/transceivers/:id", "GET /api/switches?q=&category=", "GET /api/switches/:id", "GET /api/switches/:id/compatibility", "GET /api/vendors?type=", "GET /api/standards?speed=", "GET /api/health", "GET /api/hype-cycle", "GET /api/hype-cycle/:tech", "GET /api/search?q=&collection=&limit=", "GET /api/search/products?q=&form_factor=&speed_gbps=&fiber_type=", "GET /api/search/documents?q=&doc_type=&vendor=&collection=", "GET /api/search/news?q=&source=", "GET /api/search/stats", "POST /api/documents/process {url, title?, doc_type?, vendor?, collection?}", "GET /api/documents", "GET /api/documents/:id", "POST /api/blog/generate {topic, speed?, form_factor?, use_case?}", "GET /api/blog", "GET /api/blog/:id", "PUT /api/blog/:id/status {status: draft|review|approved|published}", "GET /api/selflearning/status", "POST /api/selflearning/build", "POST /api/selflearning/publish-hf", "POST /api/selflearning/train {lane, provider, seed_only?, max_steps?}", ], }); }); // Start app.listen(cfg.port, () => { console.log(`\n TIP API running on http://localhost:${cfg.port}`); console.log(` Environment: ${cfg.nodeEnv}`); console.log(` Database: ${cfg.db.host}:${cfg.db.port}/${cfg.db.database}\n`); });