262 lines
7.3 KiB
JavaScript
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(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",
|
|
path: "./data",
|
|
},
|
|
ai: {
|
|
apiType: "kobold",
|
|
},
|
|
rootuser: {
|
|
login: "admin",
|
|
password: "admin",
|
|
activated: true,
|
|
},
|
|
};
|
|
|
|
global.server = new Server();
|
|
cfgHandle(global.server, defaultConfigData, logger);
|
|
|
|
logger.log("Current config:", global.config);
|
|
|
|
global.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);
|
|
*/
|