This commit is contained in:
Hri7566 2023-10-14 08:23:19 -04:00
commit 2bd43d8702
6 changed files with 346 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.env
node_modules

17
package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "sync",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"chokidar": "^3.5.3",
"dotenv": "^16.3.1",
"ws": "^8.14.2"
}
}

141
pnpm-lock.yaml Normal file
View File

@ -0,0 +1,141 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
chokidar:
specifier: ^3.5.3
version: 3.5.3
dotenv:
specifier: ^16.3.1
version: 16.3.1
ws:
specifier: ^8.14.2
version: 8.14.2
packages:
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
dev: false
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
dev: false
/braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
engines: {node: '>=8'}
dependencies:
fill-range: 7.0.1
dev: false
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
dependencies:
anymatch: 3.1.3
braces: 3.0.2
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
dev: false
/dotenv@16.3.1:
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
engines: {node: '>=12'}
dev: false
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
dev: false
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: false
optional: true
/glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
dependencies:
is-glob: 4.0.3
dev: false
/is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
dependencies:
binary-extensions: 2.2.0
dev: false
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
dev: false
/is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
dependencies:
is-extglob: 2.1.1
dev: false
/is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
dev: false
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
dev: false
/picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: false
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
dependencies:
picomatch: 2.3.1
dev: false
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
dependencies:
is-number: 7.0.0
dev: false
/ws@8.14.2:
resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==}
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
dev: false

80
src/client.js Normal file
View File

@ -0,0 +1,80 @@
const WebSocket = require("ws");
const { EventEmitter } = require("events");
class Client extends EventEmitter {
constructor() {
super();
this.bindEventListeners();
}
start() {
if (!this.started) return;
this.started = true;
this.connect();
}
connect() {
this.ws = new WebSocket(process.env.SYNC_URI);
this.ws.on("open", () => {});
this.ws.addEventListener("close", (evt) => {
this.emit("disconnected");
setTimeout(() => {
this.connect();
}, 1000);
});
this.ws.addEventListener("error", (evt) => {
this.emit("wserror", evt);
this.ws.close();
});
this.ws.addEventListener("message", (evt) => {
try {
const str = evt.data.toString();
const msg = JSON.parse(str);
this.emit(msg.m, msg);
} catch (err) {
this.emit("parse_error", err);
}
});
}
stop() {
this.started = false;
if (this.ws) {
this.ws.close();
delete this.ws;
}
}
say(msg) {
try {
if (this.isConnected()) this.ws.send(JSON.stringify(msg));
return true;
} catch (err) {
console.error(err);
return false;
}
}
log(level, message) {
if (!this.isConnected()) return;
}
isConnected() {
return this.ws.readyState == WebSocket.OPEN;
}
bindEventListeners() {
this.on("log", (msg) => {
console.log(`[${msg.level}] ${msg.message}`);
});
}
}
module.exports = { Client };

23
src/index.js Normal file
View File

@ -0,0 +1,23 @@
require("dotenv").config();
let isServer;
process.argv.forEach((val, index, array) => {
if (val == "client") isServer = false;
if (val == "server") isServer = true;
});
if (typeof isServer == "undefined") {
// Incorrect arguments
console.log(`Usage: ${process.argv[0]} ${process.argv[1]} [client|server]`);
process.exit(1);
}
if (isServer) {
const { Server } = require("./server");
Server.start();
} else {
const { Client } = require("./client");
const cl = new Client();
cl.connect();
}

83
src/server.js Normal file
View File

@ -0,0 +1,83 @@
const WebSocket = require("ws");
const { EventEmitter } = require("events");
function broadcast(msg) {
Server.wss.clients.forEach((ws) => {
ws.say(msg);
});
}
function log(level, message) {
console.log(`[${level}] ${message}`);
broadcast({ m: "log", level, message });
}
class Server {
static start() {
this.wss = new WebSocket.Server({
port: process.env.PORT || 10911,
});
this.wss.on("connection", (ws, req) => {
ws.preauth = new EventEmitter();
ws.evt = new EventEmitter();
ws.say = (msg) => {
ws.send(JSON.stringify(msg));
};
log("INFO", "Client connected at " + req.socket.remoteAddress);
ws.on("message", (data, isBinary) => {
try {
if (isBinary) return;
const str = data.toString();
const converted = JSON.parse(str);
if (converted.m) {
ws.preauth.emit(converted.m, converted);
if (ws.authed) {
ws.evt.emit(converted.m, converted);
}
}
} catch (err) {
console.error(err);
}
});
ws.preauth.on("hi", (msg) => {
if (!msg.project) return;
ws.authed = true;
ws.project = msg.project;
});
ws.evt.on("log", (msg) => {
if (!msg.level) return;
if (!msg.message) return;
log(msg.level, `From ${req.socket.remoteAddress}: ${msg.message}`);
});
ws.evt.on("broadcast", (msg) => {
if (!msg.msg) return;
if (typeof msg.msg !== "object") return;
if (!msg.msg.m) return;
broadcast({
m: "broadcast",
msg: msg.msg,
});
});
});
}
static stop() {
this.wss.close();
delete this.wss;
}
}
module.exports = { Server };