const fs = require("fs"); const path = require("path"); const yaml = require("js-yaml"); const express = require("express"); const http = require("http"); const { Server } = require("socket.io"); const cors = require("cors"); const axios = require("axios"); // ─── Config ─────────────────────────────────────────────────────────────────── const configPath = path.join(__dirname, "config.yml"); const config = yaml.load(fs.readFileSync(configPath, "utf8")); const PORT = config.server?.port || 4000; const HOSTS = config.targets?.hosts || [{url: "http://127.0.0.1:7088", name: "my-server"}]; const ENDPOINTS = config.targets?.endpoints || []; const TIMEOUT = config.targets?.timeout || 120_000; console.log(`[config] loaded from ${configPath}`); console.log(`[config] hosts (${HOSTS.length}):`, HOSTS); console.log(`[config] endpoints:`, ENDPOINTS); // ─── Express + Socket.io ────────────────────────────────────────────────────── const app = express(); const server = http.createServer(app); const io = new Server(server, { cors: { origin: "*", methods: ["GET", "POST"] }, }); app.use(cors()); app.use(express.json()); // ─── POST /api/parallel-fetch ───────────────────────────────────────────────── app.post("/api/parallel-fetch", async (req, res) => { const { socketId, url, timeout = TIMEOUT } = req.body; if (!socketId || !url) { return res.status(400).json({ error: "socketId and url are required" }); } const socket = io.sockets.sockets.get(socketId); if (!socket) { return res.status(400).json({ error: `Socket ${socketId} not found` }); } res.json({ status: "started" }); const tasks = HOSTS.flatMap((host) => ENDPOINTS.map((endpoint) => ({ host, endpoint })) ); socket.emit("batch:start", { url, total: tasks.length }); const promises = tasks.map(({ host, endpoint }) => { const fullUrl = host.url + endpoint; const start = Date.now(); return axios({ method: "POST", url: fullUrl, headers: { "Content-Type": "application/json" }, data: { url }, timeout, }) .then((response) => { const elapsed = Date.now() - start; console.log(`[OK] ${host} ${endpoint} (${elapsed}ms)`); socket.emit("tool:response", { host: host.url, serverName: host.name, endpoint, ok: true, status: response.status, data: response.data, elapsed, }); }) .catch((err) => { const elapsed = Date.now() - start; console.log(`[ERROR] ${host} ${endpoint} (${elapsed}ms) — ${err.message}`); socket.emit("tool:error", { host: host.url, serverName: host.name, endpoint, ok: false, error: err.message, code: err.code || null, elapsed, }); }); }); Promise.allSettled(promises).then(() => { console.log("=== ALL DONE ==="); socket.emit("batch:done", { url, total: tasks.length }); }); }); // ─── Health ─────────────────────────────────────────────────────────────────── app.get("/health", (_req, res) => res.json({ ok: true, hosts: HOSTS, endpoints: ENDPOINTS }) ); // ─── Socket lifecycle ───────────────────────────────────────────────────────── io.on("connection", (socket) => { console.log(`[socket] connected ${socket.id}`); socket.on("disconnect", () => console.log(`[socket] disconnected ${socket.id}`) ); }); // ─── Start ──────────────────────────────────────────────────────────────────── server.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`) );