ai-adventure-labs/server/server.js

262 lines
7.3 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 EventEmitter = require("events");
const net = require("net");
const fs = require("fs");
const path = require("path");
const { API } = require("./api");
class Server extends EventEmitter {
constructor() {
super();
this.on("config.update", ({ oldConfig, newConfig }) => {});
}
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(peerAddress, 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(peerAddress, 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",
path: "./data",
},
ai: {
apiType: "kobold",
},
rootuser: {
"login": "admin",
"password": "admin",
"activated": true
}
};
const server = new Server();
cfgHandle(server, defaultConfigData, logger);
logger.log("Current config:", global.config);
server.run(global.config.server.address, global.config.server.port);
/*
if (global.config.server.secureMode) {
async function initEncryption () {
const datadir = global.config.server["ssl-path"] ?? path.join(__dirname, "/encryption");
function generate () {
logger.log("Generate new keys...");
global.openSSH = crypt.generateOpenSSH({
commonName: global.config.server.info.name
}, {
days: 365,
pkcs7: true,
clientCertificate: true,
clientCertificateCN: "AI Adventure Labs",
algorithm: 'sha256',
keySize: 2048,
});
fs.writeFileSync(path.join(datadir, "public.pem"), global.openSSH.public_key.bytes);
fs.writeFileSync(path.join(datadir, "private.pem"), global.openSSH.private_key.bytes);
fs.writeFileSync(path.join(datadir, "cert.crt"), global.openSSH.cert);
}
const readFilePromise = new Promise((resolve, reject) => {
fs.readFile(path.join(datadir, "public.pem"), (err, public_key) => {
if (err) {
return resolve(generate());
}
try {
public_key = new crypt.CryptKey(public_key);
const private_key = new crypt.CryptKey(fs.readFileSync(path.join(datadir, "private.pem")));
const cert = fs.readFileSync(path.join(datadir, "cert.crt"));
global.openSSH = new crypt.OpenSSH({
public_key, private_key, cert
});
resolve();
} catch (_) {
resolve(generate());
}
});
});
await readFilePromise;
logger.log(`loaded from ${datadir}`);
};
initEncryption().then(() => {
server.run(global.config.server.address, global.config.server.port);
}).catch(e => {
throw e;
});
}
else
server.run(global.config.server.address, global.config.server.port);
*/