Compare commits
2 Commits
1ef209d9a6
...
82e1d078f5
Author | SHA1 | Date | |
---|---|---|---|
82e1d078f5 | |||
993a0cd276 |
@ -2,6 +2,12 @@ const crypt = require("../crypt");
|
||||
|
||||
// methods
|
||||
const isAuthed = require("./is-authed");
|
||||
const logIn = require("./log-in");
|
||||
const setupToken = require("./setup-token");
|
||||
|
||||
// authed sessions
|
||||
global.authed = new WeakMap();
|
||||
global.authed.tokens = new Object();
|
||||
|
||||
function parseRq (bytes) {
|
||||
try {
|
||||
@ -12,18 +18,55 @@ function parseRq (bytes) {
|
||||
}
|
||||
|
||||
class APIMethods {
|
||||
constructor (parent) {
|
||||
this.parent = parent;
|
||||
constructor (parentObj) {
|
||||
this.parentObj = parentObj;
|
||||
}
|
||||
|
||||
info (con, req, cb) {
|
||||
cb({ result: require("./server-info"), trace_id: req.trace_id, ended: true });
|
||||
const serverInfo = require("./server-info");
|
||||
cb({ result: serverInfo, trace_id: req.trace_id, ended: true });
|
||||
}
|
||||
|
||||
authed (con, req, cb) {
|
||||
isAuthed(con, req, cb);
|
||||
}
|
||||
|
||||
async token (con, req, cb) {
|
||||
const authedMethod = this.authed;
|
||||
const isAuthed = await (new Promise((rs, rj) => {
|
||||
authedMethod(con, req, ({ result, ended }) => {
|
||||
if (ended)
|
||||
return rs(result);
|
||||
});
|
||||
}));
|
||||
if (!isAuthed) {
|
||||
return setupToken(con, req, cb);
|
||||
}
|
||||
cb({
|
||||
error: "already logged-in",
|
||||
trace_id: req.trace_id,
|
||||
ended: true
|
||||
});
|
||||
}
|
||||
|
||||
async login (con, req, cb) {
|
||||
const authedMethod = this.authed;
|
||||
const isAuthed = await (new Promise((rs, rj) => {
|
||||
authedMethod(con, req, ({ result, ended }) => {
|
||||
if (ended)
|
||||
return rs(result);
|
||||
});
|
||||
}));
|
||||
if (!isAuthed) {
|
||||
return logIn(con, req, cb);
|
||||
}
|
||||
cb({
|
||||
error: "already logged-in",
|
||||
trace_id: req.trace_id,
|
||||
ended: true
|
||||
});
|
||||
}
|
||||
|
||||
/*setSession (isEncrypted, address, { key, counter }, cb) {
|
||||
if (!global.config.server.secureMode)
|
||||
return cb({ error: "Encryption is off by configuration file into server" });
|
||||
@ -41,7 +84,7 @@ class APIMethods {
|
||||
class API {
|
||||
constructor () {
|
||||
this.methods = new APIMethods(this);
|
||||
this.sessions = new WeakMap();
|
||||
this.sessions = global.authed;
|
||||
}
|
||||
|
||||
decrypt (address, bytes) {
|
||||
@ -73,14 +116,13 @@ class API {
|
||||
trace_id: null,
|
||||
ended: true
|
||||
});
|
||||
const selmethod = this.methods[method];
|
||||
if (selmethod === undefined)
|
||||
if (this.methods[method] === undefined)
|
||||
return cb({
|
||||
error: "unknown method: " + method,
|
||||
trace_id,
|
||||
ended: true
|
||||
});
|
||||
return selmethod(connection, request, cb);
|
||||
return this.methods[method](connection, request, cb);
|
||||
} else {
|
||||
return cb({
|
||||
error: "required JSON-object based request",
|
||||
|
@ -1,4 +1,3 @@
|
||||
module.exports = function (con, req, cb) {
|
||||
return cb({ error: "At develop", trace_id: req.trace_id, ended: true });
|
||||
//;
|
||||
};
|
||||
return cb({ result: global.authed.has(con), trace_id: req.trace_id, ended: true });
|
||||
};
|
||||
|
57
server/api/log-in.js
Normal file
57
server/api/log-in.js
Normal file
@ -0,0 +1,57 @@
|
||||
const { User } = require("./utils/user");
|
||||
const genToken = require("./utils/gen-token");
|
||||
|
||||
function verifyParams(req) {
|
||||
return (
|
||||
typeof req.username === "string" &&
|
||||
typeof req.password === "string" &&
|
||||
(req.gtf === undefined || typeof req.gtf === "boolean")
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = async function (con, req, cb) {
|
||||
if (!verifyParams(req))
|
||||
return cb({
|
||||
error:
|
||||
"Invalid params. Params username and password must be a string and exists, gtf must be a boolean",
|
||||
trace_id: req.trace_id,
|
||||
ended: true,
|
||||
});
|
||||
const invalidData = () =>
|
||||
cb({
|
||||
error: "Invalid login or password",
|
||||
trace_id: req.trace_id,
|
||||
ended: true,
|
||||
});
|
||||
const ok = (userdata, token = null) =>
|
||||
cb({ result: { ...userdata, token }, trace_id: req.trace_id, ended: true });
|
||||
|
||||
// At develop!
|
||||
cb({ warning: "Method at develop", trace_id: req.trace_id, ended: false });
|
||||
if (
|
||||
!!global.config.rootuser?.activated &&
|
||||
req.username === global.config.rootuser.login
|
||||
) {
|
||||
if (req.password === global.config.rootuser.password) {
|
||||
const user = await User.getUserByName(req.username);
|
||||
if (user === null) {
|
||||
console.error("Root user object is null");
|
||||
return cb({
|
||||
error: "Unknown error",
|
||||
trace_id: req.trace_id,
|
||||
ended: true,
|
||||
});
|
||||
}
|
||||
global.authed.set(con, user);
|
||||
if (!req.gtf) return ok(user.getUserForAPI(true));
|
||||
return ok(user.getUserForAPI(true), genToken.registerToken(user));
|
||||
}
|
||||
return invalidData();
|
||||
}
|
||||
return cb({
|
||||
error: "Method at develop",
|
||||
trace_id: req.trace_id,
|
||||
ended: true,
|
||||
});
|
||||
// (!) Добавить чтение из БД
|
||||
};
|
24
server/api/setup-token.js
Normal file
24
server/api/setup-token.js
Normal file
@ -0,0 +1,24 @@
|
||||
const { User } = require("./utils/user");
|
||||
|
||||
function verifyParams(req) {
|
||||
return typeof req.token === "string";
|
||||
}
|
||||
|
||||
module.exports = async function (con, req, cb) {
|
||||
if (!verifyParams(req))
|
||||
return cb({
|
||||
error: "Invalid params. Required param token must be a string",
|
||||
trace_id: req.trace_id,
|
||||
ended: true,
|
||||
});
|
||||
const invalidData = () =>
|
||||
cb({ error: "Invalid token", trace_id: req.trace_id, ended: true });
|
||||
const ok = (userdata) =>
|
||||
cb({ result: userdata, trace_id: req.trace_id, ended: true });
|
||||
|
||||
// At develop!
|
||||
const user = User.getByToken(req.token);
|
||||
if (user === null) return invalidData();
|
||||
global.authed.set(con, user);
|
||||
ok(user.getUserForAPI(true));
|
||||
};
|
19
server/api/utils/gen-token.js
Normal file
19
server/api/utils/gen-token.js
Normal file
@ -0,0 +1,19 @@
|
||||
function randint(min, max) {
|
||||
return Math.ceil(Math.random() * (max - min) + min);
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
return Buffer.from([...new Array(128)].map(() => randint(0, 255))).toString(
|
||||
"base64",
|
||||
);
|
||||
};
|
||||
module.exports.registerToken = function (user) {
|
||||
let token;
|
||||
do {
|
||||
token = module.exports();
|
||||
} while (global.authed.tokens[token] !== undefined);
|
||||
global.authed.tokens[token] = user;
|
||||
// (!) Потом добавить регистрацию в БД!
|
||||
|
||||
return token;
|
||||
};
|
62
server/api/utils/user.js
Normal file
62
server/api/utils/user.js
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Acces Levels:
|
||||
* * -1 - banned
|
||||
* * 0 - user
|
||||
* * 1 - moderator
|
||||
* * 2 - editor
|
||||
* * 3 - admin
|
||||
* * 4 - superadmin
|
||||
* * 5 - root
|
||||
*/
|
||||
|
||||
class User {
|
||||
constructor(name, accessLevel) {
|
||||
if (
|
||||
typeof accessLevel !== "number" ||
|
||||
{
|
||||
"-1": true,
|
||||
0: true,
|
||||
1: true,
|
||||
2: true,
|
||||
3: true,
|
||||
4: true,
|
||||
5: true,
|
||||
}["" + accessLevel] !== true
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid access level: ${accessLevel} (must be ranged from -1 to 5)`,
|
||||
);
|
||||
}
|
||||
this.name = name;
|
||||
this.accessLevel = accessLevel;
|
||||
}
|
||||
|
||||
static async getUserByName(name) {
|
||||
if (User.cache[name]) return User.cache[name];
|
||||
if (
|
||||
!!global.config.rootuser?.activated &&
|
||||
name === global.config.rootuser.login
|
||||
) {
|
||||
const user = new User(name, 5);
|
||||
User.cache[name] = user;
|
||||
return user;
|
||||
}
|
||||
// Потом добавить чтение из БД
|
||||
return null;
|
||||
}
|
||||
|
||||
static getByToken(token) {
|
||||
const user = global.authed.tokens[token];
|
||||
return user ?? null;
|
||||
}
|
||||
|
||||
getUserForAPI(getFullInfo = false) {
|
||||
return {
|
||||
username: this.name,
|
||||
accessLevel: this.accessLevel,
|
||||
};
|
||||
}
|
||||
}
|
||||
User.cache = {};
|
||||
|
||||
module.exports = { User };
|
@ -1,13 +1,13 @@
|
||||
{
|
||||
"server": {
|
||||
"address": "0.0.0.0",
|
||||
"port": 8841,
|
||||
"useWebSocketAPI": true,
|
||||
"useFrontend": true,
|
||||
"staticInfo": {
|
||||
"wsPort": 57891,
|
||||
"httpPort": 49901
|
||||
},
|
||||
"server": {
|
||||
"address": "0.0.0.0",
|
||||
"port": 8841,
|
||||
"useWebSocketAPI": true,
|
||||
"useFrontend": true,
|
||||
"staticInfo": {
|
||||
"wsPort": 57891,
|
||||
"httpPort": 49901
|
||||
},
|
||||
"auth-mode": 1,
|
||||
"info": {
|
||||
"name": "FullGreaM Test Neuro-RP Server",
|
||||
@ -15,20 +15,20 @@
|
||||
"pubaddress": "airp.fullgream.tech",
|
||||
"pubusetls": true
|
||||
}
|
||||
},
|
||||
"database": {
|
||||
"dialect": "sqlite",
|
||||
"path": "./data"
|
||||
},
|
||||
"ai": {
|
||||
"apiType": "kobold"
|
||||
},
|
||||
"source": {
|
||||
"bgmain": "/home/fullgream/Загрузки/21380429c504a974479d31c96a9c3c3f.jpg"
|
||||
},
|
||||
"rootuser": {
|
||||
"login": "admin",
|
||||
"password": "admin",
|
||||
"activated": true
|
||||
}
|
||||
},
|
||||
"database": {
|
||||
"dialect": "sqlite",
|
||||
"path": "./data"
|
||||
},
|
||||
"ai": {
|
||||
"apiType": "kobold"
|
||||
},
|
||||
"source": {
|
||||
"bgmain": "/home/fullgream/Загрузки/21380429c504a974479d31c96a9c3c3f.jpg"
|
||||
},
|
||||
"rootuser": {
|
||||
"login": "admin",
|
||||
"password": "admin",
|
||||
"activated": true
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,16 @@ function getTraceId() {
|
||||
.join("");
|
||||
}
|
||||
|
||||
const curUrl = new URL(location.href);
|
||||
const [address, port] = curUrl.searchParams.get("server").split(/:/);
|
||||
const isTLSmode = curUrl.searchParams.get("encrypted") === "true";
|
||||
|
||||
class ProtoApiMethods {
|
||||
constructor (api) {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
async _protoMethod (rqdata, threadcb) {
|
||||
async _protoMethod (rqdata, threadcb = null) {
|
||||
const socket = this.api.socket;
|
||||
const trace_id = getTraceId()
|
||||
|
||||
@ -24,14 +28,18 @@ class ProtoApiMethods {
|
||||
socket.onmessage = (ev) => {
|
||||
ev = JSON.parse(ev.data);
|
||||
const data = ev.result;
|
||||
return rs(data);
|
||||
console.debug("Method sended:", ev);
|
||||
if (ev.trace_id === trace_id)
|
||||
if (ev.warning)
|
||||
console.warn("Method warning:", ev.warning, "\ndetails:", ev);
|
||||
if (!!ev.ended)
|
||||
return rs(data);
|
||||
threadcb(data);
|
||||
if (typeof threadcb === 'function')
|
||||
threadcb(data);
|
||||
};
|
||||
socket.onerror = (err) => rj(err);
|
||||
});
|
||||
console.debug("Client send:", { trace_id, ...rqdata });
|
||||
socket.send(
|
||||
JSON.stringify({ trace_id, ...rqdata }),
|
||||
);
|
||||
@ -59,6 +67,20 @@ class ApiMethods extends ProtoApiMethods {
|
||||
method: "authed",
|
||||
});
|
||||
}
|
||||
|
||||
async token (token) {
|
||||
return await super._protoMethod({
|
||||
method: "token", token
|
||||
});
|
||||
}
|
||||
|
||||
async login (username, password, gtf = false) {
|
||||
return await super._protoMethod({
|
||||
method: "login",
|
||||
username, password,
|
||||
gtf
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ApiHTML {
|
||||
@ -66,6 +88,18 @@ class ApiHTML {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
async renderMainMenu (user, bgUrl = null) {
|
||||
bgUrl = bgUrl ?? "assets/hello/1.png";
|
||||
|
||||
document.body.style.backgroundImage = `url(${
|
||||
JSON.stringify(bgUrl)
|
||||
})`;
|
||||
document.body.style.backgroundSize = `${screen.width}px ${screen.height}px`;
|
||||
|
||||
document.getElementById("server.area").innerHTML = '';
|
||||
//$(document.getElementById("server.area")).append(ServerAuth.authForm);
|
||||
}
|
||||
|
||||
async renderAuth (authMode, bgUrl = null) {
|
||||
bgUrl = bgUrl ?? "assets/hello/1.png";
|
||||
|
||||
@ -77,6 +111,8 @@ class ApiHTML {
|
||||
document.getElementById("server.area").innerHTML = '';
|
||||
$(document.getElementById("server.area")).append(ServerAuth.authForm);
|
||||
|
||||
$.find("#server-code-form")[0].hidden = authMode !== "password";
|
||||
|
||||
[...$.find(".auth-act-item")].forEach(authEl => {
|
||||
const authElJQ = $(authEl);
|
||||
const isHidden = authEl.hidden;
|
||||
@ -98,7 +134,19 @@ class ApiHTML {
|
||||
$.find("#register")[0].hidden = false;
|
||||
}
|
||||
|
||||
$.find("#server-code-form")[0].hidden = authMode !== "password";
|
||||
$.find("#auth-btn")[0].onclick = () => {
|
||||
this.api.methods.login(
|
||||
$.find("#username")[0].value,
|
||||
$.find("#password")[0].value,
|
||||
true
|
||||
)
|
||||
.then(user => {
|
||||
const { token } = user;
|
||||
localStorage.setItem(`my-token>${!isTLSmode ? "ws" : "wss"}://${address}:${port}`, token);
|
||||
this.renderMainMenu(user, bgUrl);
|
||||
});
|
||||
}
|
||||
$.find("#reg-btn")[0].onclick = () => { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,6 +160,7 @@ export class ApiSocket {
|
||||
|
||||
this.socket = new WebSocket(`${!isTLSmode ? "ws" : "wss"}://${address}:${port}`);
|
||||
this.methods = new ApiMethods(this);
|
||||
this.user = null;
|
||||
|
||||
this.html = new ApiHTML(this);
|
||||
}
|
||||
|
@ -10,10 +10,10 @@ export class ServerAuth {
|
||||
!!this.isTLSenabled ? "wss" : "ws"
|
||||
}:${address}:${port}`) ?? "null");
|
||||
if (!serverdata) return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Authorization form
|
||||
ServerAuth.authForm = document.createElement("div");
|
||||
["auth-window"].forEach(c =>
|
||||
ServerAuth.authForm.classList.add(c));
|
||||
@ -89,3 +89,5 @@ ServerAuth.authForm.innerHTML = `
|
||||
</div>
|
||||
</center>
|
||||
`;
|
||||
|
||||
// Main menu form
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { ApiSocket } from "/js/connect/api.js";
|
||||
// cookie parser function
|
||||
function getCookies () {
|
||||
return Object.fromEntries(document.cookie.split(/\s{0,};\s{0,}/gmiu).map(i => i.split(/\s{0,}=\s{0,}/gmiu)));
|
||||
}
|
||||
if (getCookies().cw !== "1") {
|
||||
window.location.href = "/connect";
|
||||
}
|
||||
|
||||
// load styles
|
||||
document.head.innerHTML += '\\n <link href="/bootstrap/bootstrap.min.css" rel="stylesheet">';
|
||||
@ -23,7 +30,7 @@ document.body.innerHTML = `<nav id="navbar-main" class="navbar navbar-connect-da
|
||||
|
||||
<div class="vr" style="margin-right:5px"></div>
|
||||
<div class="d-flex" style="margin-right:10px" id="navbar-user-menu">
|
||||
<button id="toggle-theme" class="btn btn-outline-light" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><i class="fas fa-sun"></i></button>
|
||||
<button id="toggle-theme" class="btn btn-outline-light" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false"><i class="fas fa-sun"></i></button>
|
||||
</div>
|
||||
</nav>
|
||||
<div id="server.area">
|
||||
@ -49,7 +56,21 @@ socket.run()
|
||||
console.log("socket sends:", data);
|
||||
document.title = data.name;
|
||||
document.getElementById("server-name").innerText = data.name;
|
||||
await socket.html.renderAuth(data.authMode, data.extSource?.bgmain ?? null);
|
||||
const token = localStorage.getItem(`my-token>${!isTLSmode ? "ws" : "wss"}://${address}:${port}`);
|
||||
let user;
|
||||
if (token) {
|
||||
try {
|
||||
user = await socket.methods.token(token);
|
||||
}
|
||||
catch (_) {}
|
||||
}
|
||||
|
||||
const isAuthed = await socket.methods.authed();
|
||||
|
||||
if (!user || !isAuthed)
|
||||
await socket.html.renderAuth(data.authMode, data.extSource?.bgmain ?? null);
|
||||
else
|
||||
await socket.html.renderMainMenu(user, data.extSource?.bgmain ?? null);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
|
@ -16,7 +16,7 @@ class PacketParseResult {
|
||||
}
|
||||
|
||||
module.exports = async (bytes) => {
|
||||
console.log("((('", bytes.toString("utf8"), "')))");
|
||||
//console.log("((('", bytes.toString("utf8"), "')))");
|
||||
const packet = bytes.toString("utf8");
|
||||
|
||||
const isHTTP = packet.match(/(GET|POST|PUT|DELETE|OPTIONS|HEAD|PATCH|TRACE|CONNECT)\s{1,}\/[a-zA-Z0-9_\-\.\/?=&%:]{0,}\s{1,}HTTP(S|)\/(1\.0|1\.1|2\.0)(\n|\r)/gmu)?.length > 0 ?? false;
|
||||
|
@ -117,7 +117,7 @@ class Server extends EventEmitter {
|
||||
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, () => {});
|
||||
return await this.api.exec(socket, data, () => {});
|
||||
}
|
||||
} else {
|
||||
switch (socket.mainconnect) {
|
||||
@ -151,7 +151,7 @@ class Server extends EventEmitter {
|
||||
}
|
||||
}
|
||||
case "tcp":
|
||||
return await this.api.exec(peerAddress, data, () => {});
|
||||
return await this.api.exec(socket, data, () => {});
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@ -190,11 +190,11 @@ const defaultConfigData = {
|
||||
ai: {
|
||||
apiType: "kobold",
|
||||
},
|
||||
rootuser: {
|
||||
"login": "admin",
|
||||
"password": "admin",
|
||||
"activated": true
|
||||
}
|
||||
rootuser: {
|
||||
login: "admin",
|
||||
password: "admin",
|
||||
activated: true,
|
||||
},
|
||||
};
|
||||
|
||||
const server = new Server();
|
||||
|
@ -13,7 +13,7 @@ module.exports = (address, port, api) => {
|
||||
console.log("peerAddress is", peerAddress);
|
||||
ws.on('message', async (message) => {
|
||||
logger.wslog("api received:", message);
|
||||
await api.exec(peerAddress, message, (rs, encryptor) => {
|
||||
await api.exec(req.socket, message, (rs, encryptor) => {
|
||||
logger.wslog("api sended:", rs);
|
||||
let result = JSON.stringify(rs);
|
||||
if (encryptor)
|
||||
|
Loading…
Reference in New Issue
Block a user