admin stuff

This commit is contained in:
Flummi
2021-05-19 14:19:26 +02:00
parent a681f1da0e
commit 96a3b47935
12 changed files with 272 additions and 16 deletions

View File

@ -1,3 +1,5 @@
import crypto from "crypto";
const epochs = [
["year", 31536000],
["month", 2592000],
@ -27,4 +29,7 @@ export default new class {
const { interval, epoch } = getDuration(~~((new Date() - new Date(date)) / 1e3));
return `${interval} ${epoch}${interval === 1 ? "" : "s"} ago`;
}
md5(str) {
return crypto.createHash('md5').update(str).digest("hex");
}
};

98
src/inc/routes/admin.mjs Normal file
View File

@ -0,0 +1,98 @@
import router from "../router.mjs";
import sql from "../sql.mjs";
import tpl from "../tpl.mjs";
import lib from "../lib.mjs";
import util from "util";
import crypto from "crypto";
import cfg from "../../../config.json";
const scrypt = util.promisify(crypto.scrypt);
const hash = async str => {
const salt = crypto.randomBytes(16).toString("hex");
const derivedKey = await scrypt(str, salt, 64);
return "$f0ck$" + salt + ":" + derivedKey.toString("hex");
};
const verify = async (str, hash) => {
const [ salt, key ] = hash.substring(6).split(":");
const keyBuffer = Buffer.from(key, "hex");
const derivedKey = await scrypt(str, salt, 64);
return crypto.timingSafeEqual(keyBuffer, derivedKey);
};
const createID = () => crypto.randomBytes(16).toString("hex") + Date.now().toString(24);
router.get(/^\/login(\/)?$/, async (req, res) => {
if(req.cookies.session)
return res.reply({ body: "du bist schon eingeloggt lol<pre>"+util.inspect(req.session)+"</pre>" });
res.reply({
body: tpl.render("views/login", {}, req)
});
});
router.post(/^\/login(\/)?$/, async (req, res) => {
const user = await sql("user").where("login", req.post.username.toLowerCase()).limit(1);
if(user.length === 0)
return res.reply({ body: "user doesn't exist or wrong password" });
if(!(await verify(req.post.password, user[0].password)))
return res.reply({ body: "user doesn't exist or wrong password" });
const stamp = Date.now() / 1e3;
const session = lib.md5(createID());
await sql("user_sessions").insert({
user_id: user[0].id,
session: lib.md5(session),
browser: req.headers["user-agent"],
created_at: stamp,
last_used: stamp
});
return res.writeHead(301, {
"Cache-Control": "no-cache, public",
"Set-Cookie": `session=${session}; Path=/`,
"Location": "/"
}).end();
});
router.get(/^\/logout$/, async (req, res) => {
if(!req.session)
return res.redirect("/");
const usersession = await sql("user_sessions").where("id", req.session.sess_id);
if(usersession.length === 0)
return res.reply({ body: "nope 2" });
await sql("user_sessions").where("id", req.session.sess_id).del();
return res.writeHead(301, {
"Cache-Control": "no-cache, public",
"Set-Cookie": "session=; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
"Location": "/login"
}).end();
});
router.get(/^\/login\/pwdgen$/, async (req, res) => {
res.reply({
body: "<form action=\"/login/pwdgen\" method=\"post\"><input type=\"text\" name=\"pwd\" placeholder=\"pwd\" /><input type=\"submit\" value=\"f0ck it\" /></form>"
});
});
router.post(/^\/login\/pwdgen$/, async (req, res) => {
res.reply({
body: await hash(req.post.pwd)
});
});
router.get(/^\/login\/test$/, async (req, res) => {
res.reply({
body: "<pre>" + util.inspect(req) + "</pre>"
});
});
router.get(/^\/admin(\/)?$/, async (req, res) => {
if(!req.session)
return res.redirect("/");
res.reply({
body: tpl.render("views/admin", {}, req)
});
});

View File

@ -47,12 +47,10 @@ router.get(/^\/(audio\/?|image\/?|video\/?)?(p\/\d+)?$/, async (req, res) => {
link: `/${tmpmime ? tmpmime + "/" : ""}p/`
},
last: rows[rows.length - 1].id,
filter: tmpmime ? tmpmime : undefined,
themes: cfg.websrv.themes,
theme: (typeof req.cookies.theme !== "undefined" && cfg.websrv.themes.includes(req.cookies.theme)) ? req.cookies.theme : cfg.websrv.themes[0]
filter: tmpmime ? tmpmime : undefined
};
res.reply({ body: tpl.render("views/index", data) });
res.reply({ body: tpl.render("views/index", data, req) });
});
router.get(/^\/((audio\/|video\/|image\/)?[0-9]+)$/, async (req, res) => {
@ -121,18 +119,13 @@ router.get(/^\/((audio\/|video\/|image\/)?[0-9]+)$/, async (req, res) => {
link: `/${tmpmime ? tmpmime + "/" : ""}`
},
filter: tmpmime ? tmpmime : undefined,
themes: cfg.websrv.themes,
theme: (typeof req.cookies.theme !== "undefined" && cfg.websrv.themes.includes(req.cookies.theme)) ? req.cookies.theme : cfg.websrv.themes[0],
lul: cfg.websrv.phrases[~~(Math.random() * cfg.websrv.phrases.length)]
};
res.reply({ body: tpl.render("views/item", data) });
res.reply({ body: tpl.render("views/item", data, req) });
});
router.get(/^\/(about)$/, (req, res) => {
res.reply({
body: tpl.render(`views/${req.url.split[0]}`, {
themes: cfg.websrv.themes,
theme: (typeof req.cookies.theme !== "undefined" && cfg.websrv.themes.includes(req.cookies.theme)) ? req.cookies.theme : cfg.websrv.themes[0]
})
body: tpl.render(`views/${req.url.split[0]}`, {}, req)
});
});

View File

@ -31,7 +31,12 @@ export default new class {
else
throw new Error(`${o} is not a iterable object`);
}
render(tpl, data = {}, f) {
render(tpl, data = {}, req = false, f) {
if(req) { // globals
data.themes = cfg.websrv.themes;
data.theme = (typeof req.cookies.theme !== "undefined" && cfg.websrv.themes.includes(req.cookies.theme)) ? req.cookies.theme : cfg.websrv.themes[0];
data.session = req.session ? req.session : false;
}
return new Function("util", "data", "let html = \"\";with(data){" + (cfg.websrv.cache ? this.#templates[tpl] : this.readtpl(path.resolve(), `${tpl}.html`))
.trim()
.replace(/[\n\r]/g, "")

View File

@ -3,6 +3,8 @@ import url from "url";
import { promises as fs } from "fs";
import querystring from "querystring";
import cfg from "../config.json";
import sql from "./inc/sql.mjs";
import lib from "./inc/lib.mjs";
import router from "./inc/router.mjs";
(async () => {
@ -17,13 +19,13 @@ import router from "./inc/router.mjs";
req.url.split = req.url.pathname.split("/").slice(1);
req.url.qs = querystring.parse(req.url.query);
req.post = new Promise((resolve, _, data = "") => req
req.post = await new Promise((resolve, _, data = "") => req
.on("data", d => void (data += d))
.on("end", () => void resolve(Object.fromEntries(Object.entries(querystring.parse(data)).map(([k, v]) => {
try {
return [k, decodeURIComponent(v)];
} catch(err) {
console.error(err);
return [k, v];
}
})))));
@ -45,8 +47,29 @@ import router from "./inc/router.mjs";
req.cookies[parts.shift().trim()] = decodeURI(parts.join('='));
});
}
req.session = false;
if(req.cookies.session) {
const user = await sql("user_sessions")
.select("user.id", "user.login", "user.user", "user.level", "user_sessions.id as sess_id")
.where("user_sessions.session", lib.md5(req.cookies.session))
.leftJoin("user", "user.id", "user_sessions.user_id")
.limit(1);
if(user.length > 0) {
req.session = user[0];
await sql("user_sessions").update("last_used", (Date.now() / 1e3)).where("id", user[0].sess_id);
}
else { // delete session
return res.writeHead(301, {
"Cache-Control": "no-cache, public",
"Set-Cookie": "session=; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
"Location": req.url.path
}).end();
}
}
!(r = router.routes.getRoute(req.url.pathname, req.method)) ? res.writeHead(404).end(`404 - ${req.url.pathname}`) : await r(req, res);
!(r = router.routes.getRoute(req.url.pathname, req.method)) ? res.writeHead(404).end(`404 - ${req.method} ${req.url.pathname}`) : await r(req, res);
console.log([
`[${(new Date()).toLocaleTimeString()}]`,
`${(process.hrtime(t_start)[1] / 1e6).toFixed(2)}ms`,