115 lines
4.3 KiB
JavaScript
115 lines
4.3 KiB
JavaScript
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}`)
|
|
); |