ai-adventure-labs/server/server.js

234 lines
7.8 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) {
return socket.wsReverse.write(data, socketErrHanler);
}
case "http":
if (global.config.server.useFrontend) {
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"
}
};
const server = new Server();
cfgHandle(server, defaultConfigData, logger);
logger.log("Current config:", global.config);
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);