basic server struct

This commit is contained in:
jusax23 2023-03-01 16:14:46 +01:00
parent 4e74d679cc
commit 70e31009e0
11 changed files with 249 additions and 20 deletions

34
package-lock.json generated
View file

@ -10,6 +10,7 @@
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"commander": "^10.0.0", "commander": "^10.0.0",
"cors": "^2.8.5",
"dblang": "https://jusax.de/git/attachments/c13552b7-c9f0-4f50-bcce-96a124c1c286", "dblang": "https://jusax.de/git/attachments/c13552b7-c9f0-4f50-bcce-96a124c1c286",
"express": "^4.18.2", "express": "^4.18.2",
"juml": "https://jusax.de/git/attachments/208913c5-2851-4b86-a53d-ca99fed168cc", "juml": "https://jusax.de/git/attachments/208913c5-2851-4b86-a53d-ca99fed168cc",
@ -18,10 +19,12 @@
"ws": "^8.12.1" "ws": "^8.12.1"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/node": "^18.11.18", "@types/node": "^18.11.18",
"@types/pg": "^8.6.6", "@types/pg": "^8.6.6",
"@types/prompts": "^2.4.2", "@types/prompts": "^2.4.2",
"@types/ws": "^8.5.4",
"esbuild": "^0.17.10", "esbuild": "^0.17.10",
"pkg": "^5.8.0", "pkg": "^5.8.0",
"typescript": "^4.9.4" "typescript": "^4.9.4"
@ -529,6 +532,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/cors": {
"version": "2.8.13",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "4.17.17", "version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
@ -612,6 +624,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -1046,6 +1067,18 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true "dev": true
}, },
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/data-uri-to-buffer": { "node_modules/data-uri-to-buffer": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
@ -2168,7 +2201,6 @@
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }

View file

@ -5,7 +5,8 @@
"main": "dist/main.js", "main": "dist/main.js",
"type": "module", "type": "module",
"scripts": { "scripts": {
"main": "tsc && node . -c config.juml" "main": "tsc && node . -c config.juml",
"setup": "tsc && node . -c config.juml -s"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -17,16 +18,19 @@
"author": "jusax23, comcloudway", "author": "jusax23, comcloudway",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/node": "^18.11.18", "@types/node": "^18.11.18",
"@types/pg": "^8.6.6", "@types/pg": "^8.6.6",
"@types/prompts": "^2.4.2", "@types/prompts": "^2.4.2",
"@types/ws": "^8.5.4",
"esbuild": "^0.17.10", "esbuild": "^0.17.10",
"pkg": "^5.8.0", "pkg": "^5.8.0",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },
"dependencies": { "dependencies": {
"commander": "^10.0.0", "commander": "^10.0.0",
"cors": "^2.8.5",
"dblang": "https://jusax.de/git/attachments/c13552b7-c9f0-4f50-bcce-96a124c1c286", "dblang": "https://jusax.de/git/attachments/c13552b7-c9f0-4f50-bcce-96a124c1c286",
"express": "^4.18.2", "express": "^4.18.2",
"juml": "https://jusax.de/git/attachments/208913c5-2851-4b86-a53d-ca99fed168cc", "juml": "https://jusax.de/git/attachments/208913c5-2851-4b86-a53d-ca99fed168cc",

72
src/api/get.ts Normal file
View file

@ -0,0 +1,72 @@
import { eq, ge, select } from "dblang";
import express, { Request, Response } from "express";
import { suspectRequest } from "../sys/bruteforce.js";
import { oConf } from "../sys/config.js";
import { accounts, db, rooms } from "../sys/db.js";
import { error } from "../sys/log.js";
import { getSettings, SETTINGS } from "../sys/settings.js";
var Methods: ([string, (req: Request, res: Response) => Promise<void>])[] = [
["users", async (req: Request, res: Response) => {
let resp = await select([accounts.name], accounts)
.where(eq(accounts.viewable, true))
.query(db);
res.json(resp.map(d => d[accounts.name]));
}],
["server/publicKey", async (req: Request, res: Response) => {
let publicKey = await getSettings(SETTINGS.publicKey);
let expires = await getSettings(SETTINGS.certExpires);
if (publicKey && expires) {
res.json({
publicKey,
expires
});
}
}],
["isUser/:user", async (req: suspectRequest, res: Response) => {
var user = req.params.user;
let data = await select([accounts.accID], accounts)
.where(eq(accounts.name, user))
.query(db);
if (data.length == 0) {
if (req.suspect) req.suspect();
res.send("error");
} else {
res.send("ok");
}
}],
["publicRooms", async (req: Request, res: Response) => {
let data = await select([rooms.name, rooms.title, rooms.description, rooms.icon], rooms)
.where(ge(rooms.public, 1))
.query(db);
var out = data.map(d => {
let name = d[rooms.name];
let title = d[rooms.title];
let description = d[rooms.description];
let icon = d[rooms.icon];
if (name != null && title != null && description != null && icon != null) {
return { name, title, description, icon };
}
return null;
});
res.json(out.filter(d => d != null));
}]
];
export const addGetMethods = (server: express.Express) => {
server.get("/.well-known/outbag/server", (req, res) => {
res.json({
port: oConf.get("System", "PORTexposed"),
path: oConf.get("System", "PATHexposed"),
})
});
Methods.forEach((d) => void server.get("/api/" + d[0], async (req, res) => {
try {
await d[1](req, res);
if (!res.headersSent) res.status(500).send();
} catch (e) {
error("API", "Get Request Error:", e);
res.status(500).send();
}
}));
}

5
src/api/post.ts Normal file
View file

@ -0,0 +1,5 @@
import express from "express";
export const addPostMethods = (server: express.Express) => {
}

16
src/api/user.ts Normal file
View file

@ -0,0 +1,16 @@
export const STATE = {
no: 0b0001,
remoteP: 0b0010,
remote: 0b0100,
client: 0b1000,
};
export abstract class Client{
name: String;
server: String;
ip: String;
rights: number;
state: number;
accID = -1;
}

6
src/api/ws.ts Normal file
View file

@ -0,0 +1,6 @@
import ws from "ws";
import http from "http";
export const wsOnConnection = (socket:ws.WebSocket, req: http.IncomingMessage) => {
}

View file

@ -4,14 +4,20 @@ declare global {
import express from "express"; import express from "express";
import https from "https"; import https from "https";
import http from "http"; import http from "http";
import nman from "nman";
import cors from "cors";
import { WebSocketServer } from "ws";
import { Command } from "commander"; import { Command } from "commander";
import { oConf } from "./sys/config.js" import { oConf } from "./sys/config.js";
import { log } from "./sys/log.js" import { error, log } from "./sys/log.js";
import { connectToDB } from "./sys/db.js" import { connectToDB } from "./sys/db.js";
import bruteforce from "./sys/bruteforce.js" import bruteforce, { suspectRequest } from "./sys/bruteforce.js";
import { getSettings, setSettings } from "./sys/settings.js" import { fullSetup, partiellSetup } from "./setup/config.js";
import { addGetMethods } from "./api/get.js";
import { addPostMethods } from "./api/post.js";
import { wsOnConnection } from "./api/ws.js";
import { startUpdateCerts } from "./server/serverCerts.js";
import { fullSetup, partiellSetup } from "./setup/config.js"
const config = { const config = {
version: "0.0.1" version: "0.0.1"
@ -32,13 +38,13 @@ program
global.debug = debug != null; global.debug = debug != null;
if (config) { if (config) {
log("System", "Starting with config:", config); log("System", "Starting with config:", config);
if(!oConf.connect(config)) dofullSetup = true; if (!oConf.connect(config)) dofullSetup = true;
} }
if(setup || dofullSetup){ if (setup || dofullSetup) {
if(dofullSetup) fullSetup(); if (dofullSetup) fullSetup();
else partiellSetup(); else partiellSetup();
}else{ } else {
startServer(); startServer();
} }
}); });
@ -46,10 +52,50 @@ program
program.parse(); program.parse();
var serverclose = { close: (d: () => void) => d() };
nman.addShutdownTask(() => new Promise(async (res, rej) => {
//await closeWebSocket();
serverclose.close(() => res());
}), 30000);
async function startServer() { async function startServer() {
await connectToDB(); await connectToDB();
const server = express(); const server = express();
server.use(express.json({ limit: '100mb' }));
server.use(express.urlencoded({ limit: '100mb', extended: false }));
server.use(cors());
server.use(bruteforce); server.use(bruteforce);
addGetMethods(server);
addPostMethods(server);
if (oConf.get("ssl", "enable")) {
log("Server", "SSL Enabled");
oConf.readPathes(oConf.get("ssl", "privkey"), oConf.get("ssl", "cert"), oConf.get("ssl", "chain"))
.then(([privkey, cert, chain]: any) => {
const HTTPserver = https.createServer({ key: privkey, cert: cert, ca: chain }, server);
const wssServer = new WebSocketServer({ server: HTTPserver });
wssServer.on('connection', wsOnConnection);
serverclose = HTTPserver.listen(oConf.get("System", "PORT"), () => {
log("Server", 'Listening...');
complete_loaded();
});
}).catch(err => {
error("Server", err);
nman.shutdown();
});
} else {
log("Server", "SSL Disabled");
const HTTPserver = http.createServer({}, server);
const wssServer = new WebSocketServer({ server: HTTPserver });
wssServer.on('connection', wsOnConnection);
serverclose = HTTPserver.listen(oConf.get("System", "PORT"), () => {
log("Server", 'Listening...');
complete_loaded();
});
}
} }
function complete_loaded() {
startUpdateCerts();
}

View file

@ -1,3 +1,6 @@
import { eq, select, update } from "dblang";
import { accounts, db } from "../sys/db.js";
export const PERMISSIONS = { export const PERMISSIONS = {
NONE: 0b0000000000000000, // equal to no account or blocked account NONE: 0b0000000000000000, // equal to no account or blocked account
@ -14,3 +17,23 @@ export const PERMISSIONS = {
EDIT_USERS: 0b1000000000000000, EDIT_USERS: 0b1000000000000000,
ALL: 0b1111111111111111, ALL: 0b1111111111111111,
}; };
export const checkRight = async (accID: number, right: number) => {
try {
if (right == 0) return true;
let data = await select([accounts.rights], accounts)
.where(eq(accounts.accID, accID))
.query(db);
return ((data[0][accounts.rights]) & right) == right;
} catch (e) {
}
return false;
};
export const setRights = async (accID: number, rights: number) => {
await update(accounts)
.set(accounts.rights, rights)
.where(eq(accounts.accID, accID))
.query(db);
};

View file

@ -1,8 +1,31 @@
import { outbagURL } from "./outbagURL.js"; import { outbagURL } from "./outbagURL.js";
import { db, serverCerts } from "../sys/db.js" import { db, serverCerts } from "../sys/db.js"
import { eq, exists, insert, not, select, update } from "dblang"; import { eq, exists, insert, not, remove, select, update } from "dblang";
import { error } from "../sys/log.js"; import { error, log } from "../sys/log.js";
import { uts } from "../sys/tools.js" import { uts } from "../sys/tools.js";
import { getSettings, setSettings, SETTINGS } from "../sys/settings.js"
import { generateSigningKey } from "../sys/crypto.js"
import { oConf } from "../sys/config.js";
import nman from "nman";
export const startUpdateCerts = () => {
const update = async () => {
var exp = parseInt(await getSettings(SETTINGS.certExpires));
if (uts() - 60 >= (exp || 0)) {
log("serverCert", "generete new Cert");
let { privateKey, publicKey } = await generateSigningKey();
await setSettings(SETTINGS.publicKey, publicKey);
await setSettings(SETTINGS.privateKey, privateKey);
await setSettings(SETTINGS.certExpires, String(uts() + oConf.get("System", "CertLiveSec")));
}
};
let intervalId = setInterval(update, 1000 * 60);
update();
nman.addShutdownTask(() => {
clearInterval(intervalId);
}, 100);
}
async function updateRemote(url: string, pKey: string = ""): Promise<boolean | string> { async function updateRemote(url: string, pKey: string = ""): Promise<boolean | string> {

View file

@ -22,10 +22,10 @@ const isFile = async (path: string) => {
} }
const loading = () => { const loading = () => {
var P = ["\\", "|", "/", "-"]; var P = "⣾⣽⣻⢿⡿⣟⣯⣷";
var x = 0; var x = 0;
let intID = setInterval(function () { let intID = setInterval(function () {
process.stdout.write("\r" + P[(x++) % P.length]); process.stdout.write("\r\x1b[1m" + P[(x++) % P.length]+" ");
}, 150); }, 150);
return () => { return () => {
process.stdout.write("\r"); process.stdout.write("\r");
@ -214,7 +214,7 @@ const database = async () => {
oConf.set("Database", "password", resp.password); oConf.set("Database", "password", resp.password);
oConf.set("Database", "database", resp.database); oConf.set("Database", "database", resp.database);
oConf.save(); oConf.save();
db.close(); await db.close();
return true; return true;
} catch (e) { } catch (e) {
cancle(); cancle();
@ -235,7 +235,7 @@ const outbag = async () => {
}, { }, {
type: "number", type: "number",
name: "defaultMaxRooms", name: "defaultMaxRooms",
message: "Please enter the default maximum nuber of rooms!", message: "Please enter the default maximum number of rooms!",
initial: oConf.get("Settings", "defaultMaxRooms"), initial: oConf.get("Settings", "defaultMaxRooms"),
validate: i => i >= -1 validate: i => i >= -1
}, { }, {

View file

@ -47,6 +47,8 @@ accounts.addAttributes({
accID: { type: INT, primaryKey: true, autoIncrement: true }, accID: { type: INT, primaryKey: true, autoIncrement: true },
name: { type: VARCHAR(255), default: PERMISSIONS.DEFAULT }, name: { type: VARCHAR(255), default: PERMISSIONS.DEFAULT },
rights: { type: BIGINT, default: PERMISSIONS.DEFAULT },
accountKeySalt: { type: VARCHAR(64) }, accountKeySalt: { type: VARCHAR(64) },
accountKey: { type: VARCHAR(64) }, accountKey: { type: VARCHAR(64) },