ai-adventure-labs/server/server.js
2025-08-17 17:55:23 +03:00

233 lines
6.4 KiB
JavaScript

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;
});