commit 1530d52c90ad2e326a17ed8c3d52fdae3a0587a1 Author: Konstantin Date: Thu May 21 19:27:52 2026 +0300 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ccb2c80 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +package-lock.json \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..3b78f76 --- /dev/null +++ b/index.js @@ -0,0 +1,94 @@ +const express = require("express"); +const http = require("http"); +const { Server } = require("socket.io"); +const cors = require("cors"); +const axios = require("axios"); + +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()); + +app.post("/api/parallel-fetch", async (req, res) => { + const { socketId, url, timeout = 120_000 } = 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 baseUrl = "http://127.0.0.1:7088"; + const endpoints = [ + "/testmontools/curl", + "/testmontools/traceroute", + "/testmontools/ping", + ]; + + // Сообщаем клиенту сколько запросов будет + socket.emit("batch:start", { url, total: endpoints.length }); + + const localTasks = endpoints.map((endpoint) => { + const fullUrl = baseUrl + 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] ${endpoint} (${elapsed}ms)`); + console.log("data:", response.data); + + socket.emit("tool:response", { + endpoint, + ok: true, + status: response.status, + data: response.data, + elapsed, + }); + }) + .catch((err) => { + const elapsed = Date.now() - start; + console.log(`[ERROR] ${endpoint} (${elapsed}ms) — ${err.message}`); + + socket.emit("tool:error", { + endpoint, + ok: false, + error: err.message, + code: err.code || null, + elapsed, + }); + }); + }); + + // Когда все завершились — шлём финальное событие + Promise.allSettled(localTasks).then(() => { + console.log("=== ALL DONE ==="); + socket.emit("batch:done", { url, total: endpoints.length }); + }); +}); + +app.get("/health", (_req, res) => res.json({ ok: true })); + +io.on("connection", (socket) => { + console.log(`[socket] connected ${socket.id}`); + socket.on("disconnect", () => console.log(`[socket] disconnected ${socket.id}`)); +}); + +const PORT = process.env.PORT || 4000; +server.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`)); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..f6e3671 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "parallel-proxy-server", + "version": "1.0.0", + "description": "Parallel HTTP proxy with Socket.io streaming", + "main": "index.js", + "scripts": { + "start": "node index.js", + "dev": "nodemon index.js" + }, + "dependencies": { + "axios": "^1.6.0", + "cors": "^2.8.5", + "express": "^4.18.2", + "socket.io": "^4.6.1" + }, + "devDependencies": { + "nodemon": "^3.0.2" + } +} +