const cfgHandle = require("./configHandler"); const logger = require("./logger"); const parser = require("./packet-parser"); const frontend = require("./frontend"); const websocket = require("./ws-handler"); const crypt = require("./crypt"); const Database = require("./database"); const EventEmitter = require("events"); const net = require("net"); const fs = require("fs"); const path = require("path"); const { API } = require("./api"); // So.. what part of the WeakMap/Server class made you think // Oh! Developer of this AI client agrees that // [Object Server].isLocked can be writeable? const isLockedServer = new WeakMap(); class Server extends EventEmitter { constructor() { super(); this.on("config.update", ({ oldConfig, newConfig }) => {}); //this.isLocked = false; isLockedServer.set(this, false); } set isLocked (_) { throw new Error("isLocked not writeable"); } get isLocked () { return isLockedServer.get(this); } toggleLock (mode = true) { isLockedServer.set(this, !!mode); } run(address, port) { this.address = address; this.port = port; const wsPort = global.config.server?.staticInfo?.wsPort ?? 57891; const httpPort = global.config.server?.staticInfo?.httpPort ?? 49901; logger.log({ wsPort, httpPort, }); this.api = new API(); if (global.config.server.useFrontend) this.frontend = frontend("127.0.0.1", httpPort); if (global.config.server.useWebSocketAPI) this.websocket = websocket("127.0.0.1", wsPort, this.api); this.server = net.createServer((srvSocket) => { srvSocket.on("error", (err) => { logger.log("Caught server socket error: "); logger.log(err.stack); }); }); function socketErrHanler(err) { if (err) { logger.log("Caught server socket error: "); logger.log(err.stack); } } function socketDataHandler(data, socket) { socket.write(data, function (err) { if (err) { logger.log( `error: ${socket.remoteAddress} (remote) throwed: ${err.name}\n${err.stack}`, ); } }); } this.server.on("connection", (socket) => { logger.log(`${socket.remoteAddress}${socket.remotePort} is connected`); socket.mainconnect = null; if (global.config.server.useWebSocketAPI) { socket.wsReverse = net.createConnection(wsPort, "127.0.0.1"); socket.wsReverse.on("error", socketErrHanler); socket.wsReverse.on("data", (data) => socketDataHandler(data, socket)); } if (global.config.server.useFrontend) { socket.httpReverse = net.createConnection(httpPort, "127.0.0.1"); socket.httpReverse.on("error", socketErrHanler); socket.httpReverse.on("data", (data) => socketDataHandler(data, socket), ); } socket.on("error", (err) => { logger.log(`socket.on(${socket.remoteAddress}) error:`); logger.log(err.stack); try { socket.end(); } catch (_) {} // socket.end(); }); socket.on("data", async (data) => { const parserResult = await parser(data); // if (global.config.server.secureMode && data.toString().includes("http/1.1")) { // logger.log("Encrypted WS!"); // parserResult.result.isWebSocket = true; // } // const parserDecryptedResult = await parser(global.openSSH.decrypt(data)); const peerAddress = `${socket.remoteAddress}:${socket.remotePort}`; //logger.log("Parser result is", parserResult); if (!socket.mainconnect) { // if (parserResult.result.isWebSocket || parserDecryptedResult.result.isWebSocket) { if (parserResult.result.isWebSocket) { socket.mainconnect = "ws"; if (global.config.server.useFrontend) socket.httpReverse.end(); if (global.config.server.useWebSocketAPI) { return socket.wsReverse.write(data, socketErrHanler); } return; } else if (parserResult.result.isHTTP) { socket.mainconnect = "http"; socket.wsReverse.end(); if (global.config.server.useFrontend) { return socket.httpReverse.write(data, socketErrHanler); } return; } else { if (global.config.server.useFrontend) socket.httpReverse.end(); if (global.config.server.useWebSocketAPI) socket.wsReverse.end(); socket.mainconnect = "tcp"; return await this.api.exec(socket, data, () => {}); } } else { switch (socket.mainconnect) { case "ws": if (global.config.server.useWebSocketAPI) { if (!socket.wsReverse.destroyed) { return socket.wsReverse.write(data, socketErrHanler); } else { socket.wsReverse = net.createConnection(wsPort, "127.0.0.1"); socket.wsReverse.on("error", socketErrHanler); socket.wsReverse.on("data", (data) => socketDataHandler(data, socket), ); return socket.wsReverse.write(data, socketErrHanler); } } case "http": if (global.config.server.useFrontend) { if (!socket.httpReverse.destroyed) { return socket.httpReverse.write(data, socketErrHanler); } else { socket.httpReverse = net.createConnection( httpPort, "127.0.0.1", ); socket.httpReverse.on("error", socketErrHanler); socket.httpReverse.on("data", (data) => socketDataHandler(data, socket), ); return socket.httpReverse.write(data, socketErrHanler); } } case "tcp": return await this.api.exec(socket, data, () => {}); default: return; } } }); }); this.server.on("error", (err) => { logger.log("Error of server"); logger.log(err.stack); }); this.server.listen(this.port, this.address, (err) => { if (err) throw err; logger.log( `TCP server for ai-adventure labs connected on ${this.address}:${this.port}`, ); }); } } const defaultConfigData = { server: { address: "0.0.0.0", port: 8841, useWebSocketAPI: true, useFrontend: true, staticInfo: { wsPort: 57891, httpPort: 49901, }, }, database: { dialect: "sqlite", storage: "./data.db", logging: false }, ai: { apiType: "kobold", }, rootuser: { login: "admin", password: "admin", activated: true, }, }; global.server = new Server(); cfgHandle(global.server, defaultConfigData, logger); global.database = new Database(global.config.database); global.database.init() .then(() => { logger.log("Current config:", global.config); global.server.run(global.config.server.address, global.config.server.port); }) .catch((err) => { throw err; });