This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
config.yml
|
||||||
@@ -1,12 +1,28 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const yaml = require("js-yaml");
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const http = require("http");
|
const http = require("http");
|
||||||
const { Server } = require("socket.io");
|
const { Server } = require("socket.io");
|
||||||
const cors = require("cors");
|
const cors = require("cors");
|
||||||
const axios = require("axios");
|
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 || ["http://127.0.0.1:7088"];
|
||||||
|
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 app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
|
|
||||||
const io = new Server(server, {
|
const io = new Server(server, {
|
||||||
cors: { origin: "*", methods: ["GET", "POST"] },
|
cors: { origin: "*", methods: ["GET", "POST"] },
|
||||||
});
|
});
|
||||||
@@ -14,8 +30,9 @@ const io = new Server(server, {
|
|||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
// ─── POST /api/parallel-fetch ─────────────────────────────────────────────────
|
||||||
app.post("/api/parallel-fetch", async (req, res) => {
|
app.post("/api/parallel-fetch", async (req, res) => {
|
||||||
const { socketId, url, timeout = 120_000 } = req.body;
|
const { socketId, url, timeout = TIMEOUT } = req.body;
|
||||||
|
|
||||||
if (!socketId || !url) {
|
if (!socketId || !url) {
|
||||||
return res.status(400).json({ error: "socketId and url are required" });
|
return res.status(400).json({ error: "socketId and url are required" });
|
||||||
@@ -28,18 +45,14 @@ app.post("/api/parallel-fetch", async (req, res) => {
|
|||||||
|
|
||||||
res.json({ status: "started" });
|
res.json({ status: "started" });
|
||||||
|
|
||||||
const baseUrl = "http://127.0.0.1:7088";
|
const tasks = HOSTS.flatMap((host) =>
|
||||||
const endpoints = [
|
ENDPOINTS.map((endpoint) => ({ host, endpoint }))
|
||||||
"/testmontools/curl",
|
);
|
||||||
"/testmontools/traceroute",
|
|
||||||
"/testmontools/ping",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Сообщаем клиенту сколько запросов будет
|
socket.emit("batch:start", { url, total: tasks.length });
|
||||||
socket.emit("batch:start", { url, total: endpoints.length });
|
|
||||||
|
|
||||||
const localTasks = endpoints.map((endpoint) => {
|
const promises = tasks.map(({ host, endpoint }) => {
|
||||||
const fullUrl = baseUrl + endpoint;
|
const fullUrl = host + endpoint;
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
||||||
return axios({
|
return axios({
|
||||||
@@ -51,11 +64,9 @@ app.post("/api/parallel-fetch", async (req, res) => {
|
|||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const elapsed = Date.now() - start;
|
const elapsed = Date.now() - start;
|
||||||
console.log(`[OK] ${endpoint} (${elapsed}ms)`);
|
console.log(`[OK] ${host} ${endpoint} (${elapsed}ms)`);
|
||||||
console.log("data:", response.data);
|
|
||||||
|
|
||||||
socket.emit("tool:response", {
|
socket.emit("tool:response", {
|
||||||
endpoint,
|
host, endpoint,
|
||||||
ok: true,
|
ok: true,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
data: response.data,
|
data: response.data,
|
||||||
@@ -64,10 +75,9 @@ app.post("/api/parallel-fetch", async (req, res) => {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
const elapsed = Date.now() - start;
|
const elapsed = Date.now() - start;
|
||||||
console.log(`[ERROR] ${endpoint} (${elapsed}ms) — ${err.message}`);
|
console.log(`[ERROR] ${host} ${endpoint} (${elapsed}ms) — ${err.message}`);
|
||||||
|
|
||||||
socket.emit("tool:error", {
|
socket.emit("tool:error", {
|
||||||
endpoint,
|
host, endpoint,
|
||||||
ok: false,
|
ok: false,
|
||||||
error: err.message,
|
error: err.message,
|
||||||
code: err.code || null,
|
code: err.code || null,
|
||||||
@@ -76,19 +86,26 @@ app.post("/api/parallel-fetch", async (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Когда все завершились — шлём финальное событие
|
Promise.allSettled(promises).then(() => {
|
||||||
Promise.allSettled(localTasks).then(() => {
|
|
||||||
console.log("=== ALL DONE ===");
|
console.log("=== ALL DONE ===");
|
||||||
socket.emit("batch:done", { url, total: endpoints.length });
|
socket.emit("batch:done", { url, total: tasks.length });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/health", (_req, res) => res.json({ ok: true }));
|
// ─── Health ───────────────────────────────────────────────────────────────────
|
||||||
|
app.get("/health", (_req, res) =>
|
||||||
|
res.json({ ok: true, hosts: HOSTS, endpoints: ENDPOINTS })
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── Socket lifecycle ─────────────────────────────────────────────────────────
|
||||||
io.on("connection", (socket) => {
|
io.on("connection", (socket) => {
|
||||||
console.log(`[socket] connected ${socket.id}`);
|
console.log(`[socket] connected ${socket.id}`);
|
||||||
socket.on("disconnect", () => console.log(`[socket] disconnected ${socket.id}`));
|
socket.on("disconnect", () =>
|
||||||
|
console.log(`[socket] disconnected ${socket.id}`)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const PORT = process.env.PORT || 4000;
|
// ─── Start ────────────────────────────────────────────────────────────────────
|
||||||
server.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
|
server.listen(PORT, () =>
|
||||||
|
console.log(`Server running on http://localhost:${PORT}`)
|
||||||
|
);
|
||||||
Generated
+17
@@ -11,6 +11,7 @@
|
|||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"js-yaml": "^4.1.1",
|
||||||
"socket.io": "^4.6.1"
|
"socket.io": "^4.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -103,6 +104,11 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/argparse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
|
},
|
||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
@@ -853,6 +859,17 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-yaml": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"js-yaml": "bin/js-yaml.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@
|
|||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"socket.io": "^4.6.1"
|
"socket.io": "^4.6.1",
|
||||||
|
"js-yaml": "^4.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.0.2"
|
"nodemon": "^3.0.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user