diff --git a/tracker/database.js b/tracker/database.js index 9b4527b..8b584f8 100644 --- a/tracker/database.js +++ b/tracker/database.js @@ -1,118 +1,131 @@ -let prefix = { - onlineWorkers: "onlineWorkers", - fileNames: "fileNames", - fileChunks: "fileChunks", - nodeChunks: "nodeChunks", - chunkNodes: "chunkNodes", -}; +// let prefix = { +// onlineWorkers: "onlineWorkers", +// fileNames: "fileNames", +// fileChunks: "fileChunks", +// nodeChunks: "nodeChunks", +// chunkNodes: "chunkNodes", +// }; -// add a new worker -export async function addWorker(client, id, route, status) { - lastSeen = new Date().getTime(); - console.log("Adding worker", id, route, status, lastSeen); - let value = await client.get(prefix.onlineWorkers); - let onlineWorkers = JSON.parse(value); - onlineWorkers = onlineWorkers || {}; - onlineWorkers[id] = { route, status, lastSeen }; - console.log(onlineWorkers); - client.set(prefix.onlineWorkers, JSON.stringify(onlineWorkers)); - return true; -} +// // add a new worker +// export async function addWorker(client, id, route, status) { +// lastSeen = new Date().getTime(); +// console.log("Adding worker", id, route, status, lastSeen); +// let value = await client.get(prefix.onlineWorkers); +// let onlineWorkers = JSON.parse(value); +// onlineWorkers = onlineWorkers || {}; +// onlineWorkers[id] = { route, status, lastSeen }; +// console.log(onlineWorkers); +// client.set(prefix.onlineWorkers, JSON.stringify(onlineWorkers)); +// return true; +// } -// update a worker with a new status -export async function updateWorker(client, id, status) { - lastSeen = new Date().getTime(); - console.log("Updating worker", id, status, lastSeen); - let value = await client.get(prefix.onlineWorkers); - let onlineWorkers = JSON.parse(value); - onlineWorkers = onlineWorkers || {}; - onlineWorkers[id] = { ...onlineWorkers[id], status, lastSeen }; - console.log(onlineWorkers); - client.set(prefix.onlineWorkers, JSON.stringify(onlineWorkers)); - return true; -} +// // update a worker with a new status +// export async function updateWorker(client, id, status) { +// lastSeen = new Date().getTime(); +// console.log("Updating worker", id, status, lastSeen); +// let value = await client.get(prefix.onlineWorkers); +// let onlineWorkers = JSON.parse(value); +// onlineWorkers = onlineWorkers || {}; +// onlineWorkers[id] = { ...onlineWorkers[id], status, lastSeen }; +// console.log(onlineWorkers); +// client.set(prefix.onlineWorkers, JSON.stringify(onlineWorkers)); +// return true; +// } -// get all workers -export async function getWorkers(client) { - let workers = await client.get(prefix.onlineWorkers); - workers = JSON.parse(workers); - return workers; -} +// // get all workers +// export async function getWorkers(client) { +// let workers = await client.get(prefix.onlineWorkers); +// workers = JSON.parse(workers); +// return workers; +// } // change this // get a map of a fileHash -> fileId, size -export async function addFileId(client, fileId, fileHash, size) { - console.log("Adding file id", fileId, fileHash); - let value = await client.get(prefix.fileNames); - let fileHashes = JSON.parse(value); - fileHashes = fileHashes || {}; - fileHashes[fileHash] = { fileId, size }; - client.set(prefix.fileNames, JSON.stringify(fileNames)); - return true; -} +// export async function addFileId(client, fileId, fileHash, size) { +// console.log("Adding file id", fileId, fileHash); +// let value = await client.get(prefix.fileNames); +// let fileHashes = JSON.parse(value); +// fileHashes = fileHashes || {}; +// fileHashes[fileHash] = { fileId, size }; +// client.set(prefix.fileNames, JSON.stringify(fileNames)); +// return true; +// } -// get a hash of a file using the fileId -export async function getFileId(client, fileHash) { - let value = await client.get(prefix.fileNames); - let fileNames = JSON.parse(value); - return fileNames[fileHash]; -} +// // get a hash of a file using the fileId +// export async function getFileId(client, fileHash) { +// let value = await client.get(prefix.fileNames); +// let fileNames = JSON.parse(value); +// return fileNames[fileHash]; +// } -// add the new fileHash -> [chunkId] combo -export async function addFileChunks(client, fileHash, chunkIds) { - console.log("Adding file chunks", fileHash, chunkIds); - let value = await client.get(prefix.fileChunks); - let fileChunks = JSON.parse(value); - fileChunks = fileChunks || {}; - fileChunks[fileHash] = chunkIds; - console.log(fileChunks); - client.set(prefix.fileChunks, JSON.stringify(fileChunks)); - return true; -} +// // add the new fileHash -> [chunkId] combo +// export async function addFileChunks(client, fileHash, chunkIds) { +// console.log("Adding file chunks", fileHash, chunkIds); +// let value = await client.get(prefix.fileChunks); +// let fileChunks = JSON.parse(value); +// fileChunks = fileChunks || {}; +// fileChunks[fileHash] = chunkIds; +// console.log(fileChunks); +// client.set(prefix.fileChunks, JSON.stringify(fileChunks)); +// return true; +// } -// get the chunks of a file using the fileHash -export async function getFileChunks(client, fileHash) { - let value = await client.get(prefix.fileChunks); - let fileChunks = JSON.parse(value); - return fileChunks[fileHash]; -} +// // get the chunks of a file using the fileHash +// export async function getFileChunks(client, fileHash) { +// let value = await client.get(prefix.fileChunks); +// let fileChunks = JSON.parse(value); +// return fileChunks[fileHash]; +// } -// add a new chunk to a node -export async function addWorkerChunk(client, nodeId, chunkId) { - console.log("Adding node chunk", nodeId, chunkId); - let value = await client.get(prefix.nodeChunks); - let nodeChunks = JSON.parse(value); - nodeChunks = nodeChunks || {}; - nodeChunks[nodeId] = nodeChunks[nodeId] || []; - nodeChunks[nodeId].push(chunkId); - console.log(nodeChunks); - client.set(prefix.nodeChunks, JSON.stringify(nodeChunks)); - return true; -} +// // add a new chunk to a node +// export async function addWorkerChunk(client, nodeId, chunkId) { +// console.log("Adding node chunk", nodeId, chunkId); +// let value = await client.get(prefix.nodeChunks); +// let nodeChunks = JSON.parse(value); +// nodeChunks = nodeChunks || {}; +// nodeChunks[nodeId] = nodeChunks[nodeId] || []; +// nodeChunks[nodeId].push(chunkId); +// console.log(nodeChunks); +// client.set(prefix.nodeChunks, JSON.stringify(nodeChunks)); +// return true; +// } -// get all the chunks of a node -export async function getWorkerChunks(client, nodeId) { - let value = await client.get(prefix.nodeChunks); - let nodeChunks = JSON.parse(value); - return nodeChunks[nodeId]; -} +// // get all the chunks of a node +// export async function getWorkerChunks(client, nodeId) { +// let value = await client.get(prefix.nodeChunks); +// let nodeChunks = JSON.parse(value); +// return nodeChunks[nodeId]; +// } -// add a node to a chunk -export async function addChunkNode(client, chunkId, nodeId) { - console.log("Adding chunk node", chunkId, nodeId); - let value = await client.get(prefix.chunkNodes); - let chunkNodes = JSON.parse(value); - chunkNodes = chunkNodes || {}; - chunkNodes[chunkId] = chunkNodes[chunkId] || []; - chunkNodes[chunkId].push(nodeId); - console.log(chunkNodes); - client.set(prefix.chunkNodes, JSON.stringify(chunkNodes)); +// // add a node to a chunk +// export async function addChunkNode(client, chunkId, nodeId) { +// console.log("Adding chunk node", chunkId, nodeId); +// let value = await client.get(prefix.chunkNodes); +// let chunkNodes = JSON.parse(value); +// chunkNodes = chunkNodes || {}; +// chunkNodes[chunkId] = chunkNodes[chunkId] || []; +// chunkNodes[chunkId].push(nodeId); +// console.log(chunkNodes); +// client.set(prefix.chunkNodes, JSON.stringify(chunkNodes)); +// return true; +// } + +// // get all the nodes of a chunk +// export async function getChunkNodes(client, chunkId) { +// let value = await client.get(prefix.chunkNodes); +// let chunkNodes = JSON.parse(value); +// return chunkNodes[chunkId]; +// } + +// add file chunks for the database +export async function addFileChunks(client, fileHash, chunkIds) { + console.log("Adding file chunks", fileHash, chunkIds); + client.set(fileHash, JSON.stringify(chunkIds)); return true; } -// get all the nodes of a chunk -export async function getChunkNodes(client, chunkId) { - let value = await client.get(prefix.chunkNodes); - let chunkNodes = JSON.parse(value); - return chunkNodes[chunkId]; +// get file chunks for the database +export async function getFileChunks(client, fileHash) { + let value = await client.get(fileHash); + return JSON.parse(value); } diff --git a/tracker/index-rest.js b/tracker/index-rest.js index 69ed043..825db8f 100644 --- a/tracker/index-rest.js +++ b/tracker/index-rest.js @@ -1,32 +1,25 @@ import express from "express"; import { createClient } from "redis"; -import { - addWorker, - getWorkers, - addFileId, - getFileId, - getFileChunks, - addFileChunks, - addWorkerChunk, - getWorkerChunks, - getChunkNodes, - addChunkNode, - updateWorker, -} from "./database.js"; +import { addFileChunks, getFileChunks } from "./database.js"; +import cors from "cors"; const app = express(); +app.use(cors()); app.use(express.json()); const client = createClient({ url: "redis://localhost:6379", }); client.on("error", (err) => console.error("Redis Client Error", err)); + +let workers = []; +let files = new Map(); + await client.connect(); // get all workers app.get("/worker", async (req, res) => { console.log("Getting all workers"); - let workers = await getWorkers(client); res.json(workers); }); @@ -39,11 +32,12 @@ app.post("/worker", async (req, res) => { res.status(400).send("Please provide all the fields"); return; } - if (await addWorker(client, id, route, status)) { - res.json({ id, route, status }); - } else { - res.status(500).send("Redis did not save the new Worker"); - } + const lastSeen = new Date().getTime(); + + workers = workers.filter((worker) => worker.id !== id); + workers.push({ id, route, status, lastSeen }); + console.log("Adding a worker", id, route, status, lastSeen); + res.send({ id, route, status, lastSeen }); }); // update a worker @@ -56,98 +50,137 @@ app.put("/worker/:id", async (req, res) => { res.status(400).send("Please provide a status"); return; } - if (await updateWorker(client, id, status)) { - res.json({ id, status }); - } else { - res.status(500).send("Redis did not update the Worker"); - } + workers[id] = { ...workers[id], status }; + res.json({ id, status }); }); -// get all the chunks of a file +// add a file id app.post("/files", async (req, res) => { - let fileId, fileHash; + let fileName, fileHash, size; try { - ({ fileId, fileHash } = req.body); + ({ fileName, fileHash, size } = req.body); } catch (e) { res.status(400).send("Please provide a file id and hash"); return; } - if (await addFileId(client, fileId, fileHash)) { - res.json({ fileId, fileHash }); - } else { - res.status(500).send("Redis did not save the new File"); - } + console.log("Adding a file", fileName, fileHash, size); + files[fileHash] = { fileName, size }; + res.json("success"); }); -// gets the file hash -app.get("/files:id", async (req, res) => { +// gets the file info +app.get("/files/:id", async (req, res) => { let fileId = req.params.id; - let fileHash = await getFileId(client, fileId); - res.json({ fileId, fileHash }); + let file = files[fileId]; + res.json(file); +}); + +// get all the files +app.get("/files", async (req, res) => { + res.json(files); }); -// add all the chunks that a file has +// add the chunks meta data app.post("/files/:id/chunks", async (req, res) => { - let fileId = req.params.id; + let fileHash = req.params.id; let chunks; + console.log("Adding chunks", fileHash); try { - chunks = req.body; + chunks = req.body.chunk["fileId"]; + console.log(chunks); } catch (e) { res.status(400).send("Please provide all the fields"); return; } - await addFileChunks(client, fileId, chunks); - res.json({ fileId, chunks }); + if (await addFileChunks(client, fileHash, chunks)) { + res.json({ fileHash, chunks }); + + // for (let chunk of chunks.keys) { + // // chunk: [worker1, worker2, worker3] + // for (let worker of chunk) { + // let workerChunks = workers[worker]; + // if (!workerChunks) { + // workerChunks = []; + // workers[worker] = workerChunks; + // } + // workerChunks.push(chunk); + // workers[worker] = workerChunks; + // } + // } + } else { + res.status(400).send("Could not add the chunks"); + } }); -// get all the chunks of a file +// get the chunks meta data app.get("/files/:id/chunks", async (req, res) => { let fileId = req.params.id; - let chunks = await getFileChunks(client, fileId); - res.json({ fileId, chunks }); + const files = await getFileChunks(client, fileId); + res.json(files); }); -// add all the node chunks to a node -app.post("/worker/:id/chunks", async (req, res) => { - let workerId = req.params.id; - let chunks; - try { - chunks = req.body; - } catch (e) { - res.status(400).send("Please provide all the fields"); - return; - } - await addWorkerChunk(client, workerId, chunks); - res.json({ workerId, chunks }); -}); +// // add all the chunks that a file has +// app.post("/files/:id/chunks", async (req, res) => { +// let fileId = req.params.id; +// let chunks; +// try { +// chunks = req.body; +// } catch (e) { +// res.status(400).send("Please provide all the fields"); +// return; +// } +// await addFileChunks(client, fileId, chunks); +// res.json({ fileId, chunks }); +// }); -// get all the chunks of a node -app.get("/worker/:id/chunks", async (req, res) => { - let nodeId = req.params.id; - let chunks = await getWorkerChunks(client, nodeId); - res.json({ nodeId, chunks }); -}); +// // get all the chunks of a file +// app.get("/files/:id/chunks", async (req, res) => { +// let fileId = req.params.id; +// let chunks = await getFileChunks(client, fileId); +// res.json({ fileId, chunks }); +// }); -// get all nodes in a chunk -app.get("/chunk/:id", async (req, res) => { - let chunkId = req.params.id; - let nodes = await getChunkNodes(client, chunkId); - res.json({ chunkId, nodes }); -}); +// // add all the node chunks to a node +// app.post("/worker/:id/chunks", async (req, res) => { +// let workerId = req.params.id; +// let chunks; +// try { +// chunks = req.body; +// } catch (e) { +// res.status(400).send("Please provide all the fields"); +// return; +// } +// await addWorkerChunk(client, workerId, chunks); +// res.json({ workerId, chunks }); +// }); -// add a node to a chunk -app.post("/chunk/:id", async (req, res) => { - let chunkId = req.params.id; - let nodeId; - try { - nodeId = req.body.nodeId; - } catch (e) { - res.status(400).send("Please provide all the fields"); - return; - } - await addChunkNode(client, chunkId, nodeId); - res.json({ chunkId, nodeId }); -}); +// // get all the chunks of a node +// app.get("/worker/:id/chunks", async (req, res) => { +// let nodeId = req.params.id; +// let chunks = await getWorkerChunks(client, nodeId); +// res.json({ nodeId, chunks }); +// }); + +// // get all nodes in a chunk +// app.get("/chunk/:id", async (req, res) => { +// let chunkId = req.params.id; +// let nodes = await getChunkNodes(client, chunkId); +// res.json({ chunkId, nodes }); +// }); + +// // add a node to a chunk +// app.post("/chunk/:id", async (req, res) => { +// let chunkId = req.params.id; +// let nodeId; +// try { +// nodeId = req.body.nodeId; +// } catch (e) { +// res.status(400).send("Please provide all the fields"); +// return; +// } +// await addChunkNode(client, chunkId, nodeId); +// res.json({ chunkId, nodeId }); +// }); // make the final 404 route app.get("*", (req, res) => { diff --git a/tracker/package-lock.json b/tracker/package-lock.json index 58cbd56..9a2b13d 100644 --- a/tracker/package-lock.json +++ b/tracker/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "cors": "^2.8.5", "express": "^4.21.0", "redis": "^4.7.0", "socket.io": "^4.8.0" diff --git a/tracker/package.json b/tracker/package.json index 334d167..8fb7aab 100644 --- a/tracker/package.json +++ b/tracker/package.json @@ -2,20 +2,19 @@ "name": "tracker", "version": "1.0.0", "description": "", + "type": "module", + "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start_rest": "node index-rest.js", "start_socket": "node index-socket.js" }, - "keywords": [], "author": "", "license": "ISC", - "packageManager": "pnpm@9.1.2+sha1.907f57a52517de34399d66401bf220a549e47b57", "dependencies": { + "cors": "^2.8.5", "express": "^4.21.0", "redis": "^4.7.0", - "http": "0.0.1-security", - "socket.io": "^4.8.0", - "socket.io-client": "^4.8.0" + "socket.io": "^4.8.0" } } diff --git a/tracker/pnpm-lock.yaml b/tracker/pnpm-lock.yaml index 9cd6411..71130d4 100644 --- a/tracker/pnpm-lock.yaml +++ b/tracker/pnpm-lock.yaml @@ -8,21 +8,50 @@ importers: .: dependencies: + cors: + specifier: ^2.8.5 + version: 2.8.5 express: specifier: ^4.21.0 version: 4.21.0 - http: - specifier: 0.0.1-security - version: 0.0.1-security + redis: + specifier: ^4.7.0 + version: 4.7.0 socket.io: specifier: ^4.8.0 version: 4.8.0 - socket.io-client: - specifier: ^4.8.0 - version: 4.8.0 packages: + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/client@1.6.0': + resolution: {integrity: sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==} + engines: {node: '>=14'} + + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/json@1.0.7': + resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/search@1.2.0': + resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/time-series@1.1.0': + resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} + peerDependencies: + '@redis/client': ^1.0.0 + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -58,6 +87,10 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -121,9 +154,6 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - engine.io-client@6.6.1: - resolution: {integrity: sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==} - engine.io-parser@5.2.3: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} @@ -166,6 +196,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -192,9 +226,6 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} - http@0.0.1-security: - resolution: {integrity: sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==} - iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -275,6 +306,9 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} + redis@4.7.0: + resolution: {integrity: sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -303,10 +337,6 @@ packages: socket.io-adapter@2.5.5: resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} - socket.io-client@4.8.0: - resolution: {integrity: sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==} - engines: {node: '>=10.0.0'} - socket.io-parser@4.2.4: resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} engines: {node: '>=10.0.0'} @@ -354,12 +384,37 @@ packages: utf-8-validate: optional: true - xmlhttprequest-ssl@2.1.1: - resolution: {integrity: sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==} - engines: {node: '>=0.4.0'} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} snapshots: + '@redis/bloom@1.2.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/client@1.6.0': + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + + '@redis/graph@1.1.1(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/json@1.0.7(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/search@1.2.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/time-series@1.1.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + '@socket.io/component-emitter@3.1.2': {} '@types/cookie@0.4.1': {} @@ -408,6 +463,8 @@ snapshots: get-intrinsic: 1.2.4 set-function-length: 1.2.2 + cluster-key-slot@1.1.2: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -449,18 +506,6 @@ snapshots: encodeurl@2.0.0: {} - engine.io-client@6.6.1: - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 - engine.io-parser: 5.2.3 - ws: 8.17.1 - xmlhttprequest-ssl: 2.1.1 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - engine.io-parser@5.2.3: {} engine.io@6.6.1: @@ -544,6 +589,8 @@ snapshots: function-bind@1.1.2: {} + generic-pool@3.9.0: {} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 @@ -576,8 +623,6 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 - http@0.0.1-security: {} - iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -636,6 +681,15 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + redis@4.7.0: + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.6.0) + '@redis/client': 1.6.0 + '@redis/graph': 1.1.1(@redis/client@1.6.0) + '@redis/json': 1.0.7(@redis/client@1.6.0) + '@redis/search': 1.2.0(@redis/client@1.6.0) + '@redis/time-series': 1.1.0(@redis/client@1.6.0) + safe-buffer@5.2.1: {} safer-buffer@2.1.2: {} @@ -694,17 +748,6 @@ snapshots: - supports-color - utf-8-validate - socket.io-client@4.8.0: - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 - engine.io-client: 6.6.1 - socket.io-parser: 4.2.4 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - socket.io-parser@4.2.4: dependencies: '@socket.io/component-emitter': 3.1.2 @@ -745,4 +788,4 @@ snapshots: ws@8.17.1: {} - xmlhttprequest-ssl@2.1.1: {} + yallist@4.0.0: {} diff --git a/worker/package-lock.json b/worker/package-lock.json new file mode 100644 index 0000000..71cb627 --- /dev/null +++ b/worker/package-lock.json @@ -0,0 +1,1216 @@ +{ + "name": "nodes", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nodes", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.21.0", + "http": "0.0.1-security", + "socket.io": "^4.8.0", + "socket.io-client": "^4.8.0" + }, + "devDependencies": { + "ts-node": "^10.9.2", + "typescript": "^5.6.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.7.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", + "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz", + "integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz", + "integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", + "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", + "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/socket.io-client": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz", + "integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-client/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz", + "integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/worker/tsconfig.json b/worker/tsconfig.json index 760b2a6..d6b2d6a 100644 --- a/worker/tsconfig.json +++ b/worker/tsconfig.json @@ -1,4 +1,4 @@ -{ + { "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ diff --git a/worker/worker.ts b/worker/worker.ts index bf7826b..0913301 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -1,289 +1,411 @@ -import { Server } from 'socket.io'; -import ioClient, { Socket as ClientSocket } from 'socket.io-client'; -import * as fs from 'fs'; -import * as crypto from 'crypto'; -import * as readline from 'readline'; -import * as path from 'path'; +import { Server } from "socket.io"; +import ioClient, { Socket as ClientSocket } from "socket.io-client"; +import * as fs from "fs"; +import * as crypto from "crypto"; +import * as readline from "readline"; +import * as path from "path"; + +const TRACKER_URL = process.env.TRACKER_IP || "http://localhost:3000"; interface ChunkData { - fileId: string; - chunkId: number; - chunk: Buffer; + fileId: string; + chunkId: number; + chunk: Buffer; } interface WorkerInfo { - address: string; - port: number; + address: string; + port: number; + route: string; } class Worker { - private server: Server; - private trackerSocket: ClientSocket; - private chunks: Map>; - private workerSockets: Map; - private address: string; - private port: number; - - constructor(port: number, trackerAddress: string) { - this.port = port; - this.server = new Server(port); - this.trackerSocket = ioClient(trackerAddress); - this.chunks = new Map(); - this.setupEventListeners(); - this.workerSockets = new Map(); - this.address = 'localhost'; // Assuming the address is localhost - console.log(`Worker started on port ${port}`); - } - - private setupEventListeners() { - this.server.on('connection', (socket) => { - console.log(`New connection on port ${this.port}`); - socket.on('store_chunk', (data: ChunkData, callback: (response: { success: boolean }) => void) => { - this.storeChunk(data.fileId, data.chunkId, data.chunk); - this.trackerSocket.emit('store_chunk_info', { - fileId: data.fileId, - chunkId: data.chunkId - }); - callback({ success: true }); - }); - - socket.on('retrieve_chunk', (data: { fileId: string; chunkId: number }, callback: (chunk: Buffer | null) => void) => { - const chunk = this.retrieveChunk(data.fileId, data.chunkId); - callback(chunk); - }); - }); - - this.trackerSocket.on('connect', () => { - this.trackerSocket.emit('register_worker', { - address: this.address, - port: this.port - }); - }); - } - - private storeChunk(fileId: string, chunkId: number, chunk: Buffer) { - if (!this.chunks.has(fileId)) { - this.chunks.set(fileId, new Map()); + private server: Server; + private trackerSocket: ClientSocket; + private chunks: Map>; + private workerSockets: Map; + private address: string; + private port: number; + private route: string; + + constructor(port: number, trackerAddress: string) { + this.port = port; + this.server = new Server(port); + this.trackerSocket = ioClient(trackerAddress); + this.chunks = new Map(); + this.setupEventListeners(); + this.workerSockets = new Map(); + this.address = "localhost"; // Assuming the address is localhost + console.log(`Worker started on port ${port}`); + this.route = `http://${this.address}:${this.port}`; + + fetch(`${TRACKER_URL}/worker`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: this.port, + address: this.address, + port: this.port, + route: this.route, + }), + }); + } + + private setupEventListeners() { + this.server.on("connection", (socket) => { + console.log(`New connection on port ${this.port}`); + + socket.on( + "store_chunk", + ( + data: ChunkData, + callback: (response: { success: boolean }) => void + ) => { + this.storeChunk(data.fileId, data.chunkId, data.chunk); + this.trackerSocket.emit("store_chunk_info", { + fileId: data.fileId, + chunkId: data.chunkId, + }); + callback({ success: true }); } - this.chunks.get(fileId)!.set(chunkId, chunk); - console.log(`Stored chunk ${chunkId} of file ${fileId} to node ${this.trackerSocket}:${this.port}`); - } - - private retrieveChunk(fileId: string, chunkId: number): Buffer | null { - if (this.chunks.has(fileId) && this.chunks.get(fileId)!.has(chunkId)) { - return this.chunks.get(fileId)!.get(chunkId)!; + ); + + socket.on( + "retrieve_chunk", + ( + data: { fileId: string; chunkId: number }, + callback: (chunk: Buffer | null) => void + ) => { + const chunk = this.retrieveChunk(data.fileId, data.chunkId); + callback(chunk); } - return null; + ); + }); + + this.trackerSocket.on("connect", () => { + this.trackerSocket.emit("register_worker", { + address: this.address, + port: this.port, + }); + }); + } + + private storeChunk(fileId: string, chunkId: number, chunk: Buffer) { + if (!this.chunks.has(fileId)) { + this.chunks.set(fileId, new Map()); } - - async uploadFile(filePath: string): Promise { - const fileContent = fs.readFileSync(filePath); - const fileId = crypto.createHash('sha256').update(fileContent).digest('hex'); - const chunks = this.splitIntoChunks(fileContent); - - const activeWorkers: WorkerInfo[] = await this.getActiveWorkers(); - const replicationFactor = Math.ceil(activeWorkers.length / 2); - - const workerSockets: { [key: string]: ClientSocket } = {}; - for (const worker of activeWorkers) { - if (worker.address !== this.address || worker.port !== this.port) { - const workerSocket = ioClient(`http://${worker.address}:${worker.port}`); - workerSockets[`${worker.address}:${worker.port}`] = workerSocket; - } - } - - for (let i = 0; i < chunks.length; i++) { - const targetWorkers = this.selectRandomWorkers(activeWorkers, replicationFactor); - - for (const worker of targetWorkers) { - const workerKey = `${worker.address}:${worker.port}`; - const workerSocket = workerSockets[workerKey]; - - await new Promise((resolve) => { - workerSocket.emit('store_chunk', { - fileId, - chunkId: i, - chunk: chunks[i] - }, (response: { success: boolean }) => { - if (response.success) { - resolve(); - } - }); - }); + this.chunks.get(fileId)!.set(chunkId, chunk); + console.log( + `Stored chunk ${chunkId} of file ${fileId} to node ${this.trackerSocket}:${this.port}` + ); + } + + private retrieveChunk(fileId: string, chunkId: number): Buffer | null { + console.log(this.chunks.has(fileId), this.chunks.get(fileId)); + console.log(this.chunks.get(fileId)!.has(Number(chunkId))); + if ( + this.chunks.has(fileId) && + this.chunks.get(fileId)!.has(Number(chunkId)) + ) { + return this.chunks.get(fileId)!.get(Number(chunkId))!; + } + return null; + } + + async uploadFile(filePath: string): Promise { + const fileContent = fs.readFileSync(filePath); + const fileId = crypto + .createHash("sha256") + .update(fileContent) + .digest("hex"); + const chunks = this.splitIntoChunks(fileContent); + + const activeWorkers: WorkerInfo[] = await this.getActiveWorkers(); + const replicationFactor = Math.ceil(activeWorkers.length / 2); + + const workerSockets: { [key: string]: ClientSocket } = {}; + console.log("active workers: ", activeWorkers); + for (const worker of activeWorkers) { + console.log(worker.route, this.route); + if (worker.route !== this.route) { + const workerSocket = ioClient(`${worker.route}`); + workerSockets[`${worker.route}`] = workerSocket; + } + } + const filteredWorkers = activeWorkers.filter( + (worker) => worker.route !== this.route + ); + + const chunkDistribution: { [chunkId: number]: string[] } = {}; + + for (let i = 0; i < chunks.length; i++) { + const targetWorkers = this.selectRandomWorkers( + filteredWorkers, + replicationFactor + ); + chunkDistribution[i] = []; + + for (const worker of targetWorkers) { + chunkDistribution[i].push(...chunkDistribution[i], worker.route); + const workerKey = worker.route; + const workerSocket = workerSockets[workerKey]; + await new Promise((resolve) => { + workerSocket.emit( + "store_chunk", + { + fileId, + chunkId: i, + chunk: chunks[i], + }, + (response: { success: boolean }) => { + if (response.success) { + resolve(); + } } - } - - for (const workerKey in workerSockets) { - workerSockets[workerKey].close(); - console.log(`Connection to worker ${workerKey} closed.`); - } - - this.trackerSocket.emit('store_file', { - fileId, - fileName: path.basename(filePath), - fileSize: fileContent.length + ); }); - - console.log(`File uploaded with ID: ${fileId}`); - return fileId; + } } - private async getActiveWorkers(): Promise { - return new Promise((resolve) => { - this.trackerSocket.emit('get_active_workers', (workers: WorkerInfo[]) => { - resolve(workers); - }); - }); + console.log("Chunk distribution:", chunkDistribution); + // Send chunk distribution data to tracker + fetch(`${TRACKER_URL}/files/${fileId}/chunks`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + chunk: { fileId: chunkDistribution }, + }), + }) + .then((response) => response.json()) + .catch((error) => + console.error("Error sending chunk distribution to tracker:", error) + ); + + for (const workerKey in workerSockets) { + workerSockets[workerKey].close(); + console.log(`Connection to worker ${workerKey} closed.`); } - private selectRandomWorkers(workers: WorkerInfo[], count: number): WorkerInfo[] { - const shuffled = workers.slice().sort(() => 0.5 - Math.random()); - return shuffled.slice(0, count); + this.trackerSocket.emit("store_file", { + fileId, + fileName: path.basename(filePath), + fileSize: fileContent.length, + }); + + // Send file metadata to tracker + fetch(`${TRACKER_URL}/files`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + fileHash: fileId, + fileName: path.basename(filePath), + size: fileContent.length, + }), + }) + .then((response) => response.json()) + .catch((error) => + console.error("Error sending file metadata to tracker:", error) + ); + + console.log(`File uploaded with ID: ${fileId}`); + return fileId; + } + + private async getActiveWorkers(): Promise { + return new Promise((resolve) => { + fetch(`${TRACKER_URL}/worker`) + .then((response) => response.json()) + .then((data) => resolve(data)); + }); + } + + private selectRandomWorkers( + workers: WorkerInfo[], + count: number + ): WorkerInfo[] { + const shuffled = workers.slice().sort(() => 0.5 - Math.random()); + return shuffled.slice(0, count); + } + + async downloadFile(fileId: string, outputPath: string) { + const chunks: (Buffer | null)[] = []; + const fileChunks: any = await this.getFileChunks(fileId); + + console.log("retrieving file with ID: ", fileId); + + for (const chunkId of Object.keys(fileChunks)) { + const locations = fileChunks[chunkId]; + if (locations.length === 0) { + console.error(`Chunk ${chunkId} of file ${fileId} not found`); + return; + } + console.log("trying to retrieve chunk from: "); + const chunk = await new Promise((resolve) => { + const nodeSocket = ioClient(`${locations[0]}`); + nodeSocket.emit( + "retrieve_chunk", + { fileId, chunkId }, + (chunk: Buffer) => { + resolve(chunk); + } + ); + }); + console.log("chunk retrieved: ", chunk); + chunks[Number(chunkId)] = chunk; } - async downloadFile(fileId: string, outputPath: string) { - const chunks: (Buffer | null)[] = []; - const fileChunks = await this.getFileChunks(fileId); + const validChunks = chunks.filter(Boolean) as Buffer[]; - console.log("retrieving file with ID: ", fileId); - - for (const chunkId of fileChunks) { - const locations = await this.getChunkLocations(fileId, chunkId); - if (locations.length === 0) { - console.error(`Chunk ${chunkId} of file ${fileId} not found`); - return; - } - console.log("trying to retrieve chunk"); - const chunk = await new Promise((resolve) => { - const nodeSocket = ioClient(`http://${locations[0].address}:${locations[0].port}`); - nodeSocket.emit('retrieve_chunk', { fileId, chunkId }, (chunk: Buffer) => { - resolve(chunk); - }); - }); - - chunks[chunkId] = chunk; - } - - const validChunks = chunks.filter(Boolean) as Buffer[]; - - if (validChunks.length === 0) { - console.error('No valid chunks retrieved for the file.'); - return; - } - - const fileContent = Buffer.concat(validChunks); - this.verifyFileIntegrity(fileContent, fileId); - fs.writeFileSync(outputPath, fileContent); - console.log(`File downloaded to ${outputPath}`); + if (validChunks.length === 0) { + console.error("No valid chunks retrieved for the file."); + return; } - private splitIntoChunks(buffer: Buffer): Buffer[] { - const chunks: Buffer[] = []; - const chunkSize = 512; - for (let i = 0; i < buffer.length; i += chunkSize) { - chunks.push(buffer.slice(i, i + chunkSize)); - } - return chunks; + const fileContent = Buffer.concat(validChunks); + this.verifyFileIntegrity(fileContent, fileId); + fs.writeFileSync(outputPath, fileContent); + console.log(`File downloaded to ${outputPath}`); + } + + private splitIntoChunks(buffer: Buffer): Buffer[] { + const chunks: Buffer[] = []; + const chunkSize = 512; + for (let i = 0; i < buffer.length; i += chunkSize) { + chunks.push(buffer.slice(i, i + chunkSize)); } + return chunks; + } - private verifyFileIntegrity(fileContent: Buffer, fileId: string) { - const hash = crypto.createHash('sha256').update(fileContent).digest('hex'); - console.log(`File integrity check: + private verifyFileIntegrity(fileContent: Buffer, fileId: string) { + const hash = crypto.createHash("sha256").update(fileContent).digest("hex"); + console.log(`File integrity check: File ID: ${fileId} SHA-256 Hash: ${hash} `); - } - - private getChunkLocations(fileId: string, chunkId: number): Promise { - return new Promise((resolve) => { - this.trackerSocket.emit('get_chunk_locations', { fileId, chunkId }, (locations: WorkerInfo[]) => { - resolve(locations); - }); - }); - } - - private getFileChunks(fileId: string): Promise { - return new Promise((resolve) => { - this.trackerSocket.emit('get_file_chunks', fileId, (chunks: number[]) => { - resolve(chunks); - }); - }); - } - - private listStoredChunks() { - console.log('Stored chunks:'); - this.chunks.forEach((fileChunks, fileId) => { - console.log(`File ID: ${fileId}`); - fileChunks.forEach((chunk, chunkId) => { - console.log(`Chunk ID: ${chunkId}, Size: ${chunk.length} bytes`); - }); - }); - } - - private listStoredFiles() { - this.trackerSocket.emit('list_files', (files: any[]) => { - console.log('Stored files:', files); - files.forEach((file, index) => { - console.log(`File ID: ${file.fileId}, Name: ${file.fileName}, Size: ${file.fileSize} bytes`); - }); - }); - } - - cli() { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); - - console.log(`Worker CLI (Port ${this.port}):`); - console.log('Available commands:'); - console.log('- upload : Upload a file'); - console.log('- download : Download a file'); - console.log('- list_chunks: List all stored chunks'); - console.log('- list_files: List all stored files'); - console.log('- exit: Exit the worker'); - - rl.on('line', async (input) => { - const [command, ...args] = input.trim().split(' '); - - switch (command) { - case 'upload': - if (args.length !== 1) { - console.log('Usage: upload '); - break; - } - await this.uploadFile(args[0]); - break; - case 'download': - if (args.length !== 2) { - console.log('Usage: download '); - break; - } - await this.downloadFile(args[0], args[1]); - break; - case 'list_chunks': - this.listStoredChunks(); - break; - case 'list_files': - this.listStoredFiles(); - break; - case 'exit': - rl.close(); - break; - default: - console.log('Unknown command:', command); - } - }); - - rl.on('close', () => { - console.log('Exiting worker...'); - process.exit(0); + } + + private getChunkLocations( + fileId: string, + chunkId: number + ): Promise { + return new Promise((resolve) => { + this.trackerSocket.emit( + "get_chunk_locations", + { fileId, chunkId }, + (locations: WorkerInfo[]) => { + resolve(locations); + } + ); + }); + } + + private getFileChunks(fileId: string): Promise<{ string: string[] }> { + return new Promise((resolve) => { + fetch(`${TRACKER_URL}/files/${fileId}/chunks`) + .then((response) => response.json()) + .then((data) => resolve(data)); + }); + } + + private listStoredChunks() { + console.log("Stored chunks:"); + this.chunks.forEach((fileChunks, fileId) => { + console.log(`File ID: ${fileId}`); + fileChunks.forEach((chunk, chunkId) => { + console.log(`Chunk ID: ${chunkId}, Size: ${chunk.length} bytes`); + }); + }); + } + + private listStoredFiles() { + console.log("Fetching stored files from tracker..."); + fetch(`${TRACKER_URL}/files`) + .then((response) => response.json()) + .then((files) => { + console.log("Stored files:"); + Object.entries(files).forEach(([fileId, fileInfo]) => { + if ( + typeof fileInfo === "object" && + fileInfo !== null && + "fileName" in fileInfo && + "size" in fileInfo + ) { + console.log( + `File ID: ${fileId}, Name: ${fileInfo.fileName}, Size: ${fileInfo.size} bytes` + ); + } else { + console.log( + `File ID: ${fileId}, Info: ${JSON.stringify(fileInfo)}` + ); + } }); - } + }) + .catch((error) => { + console.error("Error fetching stored files:", error); + }); + } + + cli() { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + console.log(`Worker CLI (Port ${this.port}):`); + console.log("Available commands:"); + console.log("- upload : Upload a file"); + console.log("- download : Download a file"); + console.log("- list_chunks: List all stored chunks"); + console.log("- list_files: List all stored files"); + console.log("- exit: Exit the worker"); + + rl.on("line", async (input) => { + const [command, ...args] = input.trim().split(" "); + + switch (command) { + case "upload": + if (args.length !== 1) { + console.log("Usage: upload "); + break; + } + await this.uploadFile(args[0]); + break; + case "download": + if (args.length !== 2) { + console.log("Usage: download "); + break; + } + await this.downloadFile(args[0], args[1]); + break; + case "list_chunks": + this.listStoredChunks(); + break; + case "list_files": + this.listStoredFiles(); + break; + case "exit": + rl.close(); + break; + default: + console.log("Unknown command:", command); + } + }); + + rl.on("close", () => { + console.log("Exiting worker..."); + process.exit(0); + }); + } } // Usage const port = Number(process.argv[2]); -const trackerAddress = process.argv[3] || 'http://localhost:3000'; +const trackerAddress = process.argv[3] || "http://localhost:3000"; const worker = new Worker(port, trackerAddress); worker.cli();