sirx: und weg damit :D
This commit is contained in:
parent
83fbc557e8
commit
f3e357d8a4
228
public/s/js/admin.js
Normal file
228
public/s/js/admin.js
Normal file
@ -0,0 +1,228 @@
|
||||
let flashActive = false;
|
||||
const flashTypes = [ "error", "success", "warn" ];
|
||||
|
||||
const flash = ({ type, msg }) => {
|
||||
let flashContainer;
|
||||
if(tmp = document.querySelector("div#flash"))
|
||||
flashContainer = tmp;
|
||||
else {
|
||||
flashContainer = document.createElement("div");
|
||||
flashContainer.id = "flash";
|
||||
document.body.insertAdjacentElement("afterbegin", flashContainer);
|
||||
}
|
||||
|
||||
flashContainer.innerHTML = msg;
|
||||
if(flashTypes.includes(type)) {
|
||||
flashContainer.className = "";
|
||||
flashContainer.classList.add(type);
|
||||
}
|
||||
if(flashActive)
|
||||
return false;
|
||||
|
||||
flashActive = true;
|
||||
flashContainer.animate(
|
||||
[ { bottom: "-28px" }, { bottom: 0 } ], {
|
||||
duration: 400,
|
||||
fill: "both"
|
||||
}
|
||||
).onfinish = () => setTimeout(() => {
|
||||
flashContainer.animate(
|
||||
[ { bottom: 0 }, { bottom: "-28px" } ], {
|
||||
duration: 400,
|
||||
fill: "both"
|
||||
}
|
||||
).onfinish = () => flashActive = false;
|
||||
}, 4 * 1e3);
|
||||
return true;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
if(_addtag = document.querySelector("a#a_addtag")) {
|
||||
const postid = +document.querySelector("a.id-link").innerText;
|
||||
const poster = document.querySelector("span#a_username").innerText;
|
||||
let tags = [...document.querySelectorAll("#tags > .badge")].map(t => t.innerText.slice(0, -2));
|
||||
|
||||
const deleteEvent = async e => {
|
||||
e.preventDefault();
|
||||
if(!confirm("Do you really want to delete this tag?"))
|
||||
return;
|
||||
const tag = e.target.parentElement.innerText.slice(0, -2);
|
||||
if(!tags.includes(tag))
|
||||
return alert("wtf");
|
||||
const res = await deleteTag(postid, tag);
|
||||
if(!res.success)
|
||||
return alert("uff");
|
||||
tags = res.tags.map(t => t.tag);
|
||||
renderTags(res.tags);
|
||||
};
|
||||
|
||||
const post = async (url, data) => fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const getTags = async postid => await (await fetch("/api/v2/admin/tags/get/" + postid)).json();
|
||||
|
||||
const deletePost = async postid => await (await fetch("/api/v2/admin/deletepost/" + postid)).json();
|
||||
|
||||
const addTag = async (postid, tag) => await (await post("/api/v2/admin/tags/add", {
|
||||
postid: postid,
|
||||
tag: tag
|
||||
})).json();
|
||||
|
||||
const deleteTag = async (postid, tag) => await (await post("/api/v2/admin/tags/delete", {
|
||||
postid: postid,
|
||||
tag: tag
|
||||
})).json();
|
||||
|
||||
const renderTags = _tags => {
|
||||
[...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag));
|
||||
_tags.reverse().forEach(tag => {
|
||||
const a = document.createElement("a");
|
||||
a.href = `/admin/test?tag=${tag.tag.replace(/\s/g, "%20")}`;
|
||||
a.target = "_blank";
|
||||
a.style = "color: inherit !important";
|
||||
a.innerText = tag.tag;
|
||||
|
||||
const span = document.createElement("span");
|
||||
span.classList.add("badge", "badge-light", "mr-2");
|
||||
span.title = tag.prefix;
|
||||
if(tag.tag == "sfw") {
|
||||
span.classList.remove("badge-light");
|
||||
span.classList.add("badge-success");
|
||||
}
|
||||
if(tag.tag == "nsfw") {
|
||||
span.classList.remove("badge-light");
|
||||
span.classList.add("badge-danger");
|
||||
}
|
||||
if(tag.tag.startsWith(">")) {
|
||||
span.classList.add("badge-greentext");
|
||||
}
|
||||
|
||||
const delbutton = document.createElement("a");
|
||||
delbutton.innerHTML = " ×";
|
||||
delbutton.href = "#";
|
||||
delbutton.addEventListener("click", deleteEvent);
|
||||
span.insertAdjacentElement("beforeend", a);
|
||||
span.insertAdjacentElement("beforeend", delbutton);
|
||||
|
||||
document.querySelector("#tags").insertAdjacentElement("afterbegin", span);
|
||||
});
|
||||
};
|
||||
|
||||
const addtagClick = (ae = false) => {
|
||||
if(ae)
|
||||
ae.preventDefault();
|
||||
|
||||
const insert = document.querySelector("a#a_addtag");
|
||||
const span = document.createElement("span");
|
||||
span.classList.add("badge", "badge-light", "mr-2");
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.size = "10";
|
||||
input.value = "";
|
||||
|
||||
let tt;
|
||||
input.addEventListener("keydown", async e => {
|
||||
if(e.key === "Enter") {
|
||||
const tmptag = input.value;
|
||||
if(tags.includes(tmptag))
|
||||
return alert("tag already exists");
|
||||
const res = await addTag(postid, tmptag);
|
||||
if(!res.success) {
|
||||
return flash({
|
||||
type: "error",
|
||||
msg: res.msg
|
||||
});
|
||||
}
|
||||
tags = res.tags.map(t => t.tag);
|
||||
renderTags(res.tags);
|
||||
addtagClick();
|
||||
}
|
||||
else if(e.key === "Escape") {
|
||||
span.parentElement.removeChild(span);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
span.insertAdjacentElement("afterbegin", input);
|
||||
insert.insertAdjacentElement("beforebegin", span);
|
||||
|
||||
input.focus();
|
||||
|
||||
input.addEventListener("focusout", ie => {
|
||||
if(input.value.length === 0)
|
||||
input.parentElement.parentElement.removeChild(input.parentElement);
|
||||
});
|
||||
};
|
||||
|
||||
const toggleEvent = async (e = false) => {
|
||||
if(e)
|
||||
e.preventDefault();
|
||||
let res;
|
||||
if(tags.includes("sfw")) {
|
||||
await deleteTag(postid, "sfw");
|
||||
res = await addTag(postid, "nsfw");
|
||||
}
|
||||
else if(tags.includes("nsfw")) {
|
||||
await deleteTag(postid, "nsfw");
|
||||
res = await addTag(postid, "sfw");
|
||||
}
|
||||
else
|
||||
res = await addTag(postid, "sfw");
|
||||
|
||||
if(!res.success)
|
||||
return flash({
|
||||
type: "error",
|
||||
msg: res.msg
|
||||
});
|
||||
|
||||
renderTags(res.tags);
|
||||
tags = res.tags.map(t => t.tag);
|
||||
|
||||
flash({
|
||||
type: "success",
|
||||
msg: tags.join()
|
||||
});
|
||||
};
|
||||
|
||||
const deleteButtonEvent = async e => {
|
||||
if(e)
|
||||
e.preventDefault();
|
||||
if(!confirm(`Reason for deleting f0ckpost ${postid} by ${poster} (Weihnachten™)`))
|
||||
return;
|
||||
const res = await deletePost(postid);
|
||||
if(res.success) {
|
||||
flash({
|
||||
type: "success",
|
||||
msg: "post was successfully deleted"
|
||||
});
|
||||
}
|
||||
else {
|
||||
flash({
|
||||
type: "error",
|
||||
msg: res.msg
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_addtag.addEventListener("click", addtagClick);
|
||||
document.querySelector("a#a_toggle").addEventListener("click", toggleEvent);
|
||||
[...document.querySelectorAll("#tags > .badge > a:last-child")].map(t => t.addEventListener("click", deleteEvent));
|
||||
document.querySelector("a#a_delete").addEventListener("click", deleteButtonEvent);
|
||||
|
||||
document.addEventListener("keyup", e => {
|
||||
if(e.target.tagName === "INPUT")
|
||||
return;
|
||||
if(e.key === "p")
|
||||
toggleEvent();
|
||||
else if(e.key === "i")
|
||||
addtagClick();
|
||||
else if(e.key === "x")
|
||||
deleteButtonEvent();
|
||||
});
|
||||
}
|
||||
})();
|
@ -2,7 +2,7 @@
|
||||
if(elem = document.querySelector("#my-video")) {
|
||||
const video = new v0ck(elem);
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.key === " ") {
|
||||
if(e.key === " " && e.target.tagName !== "INPUT") {
|
||||
video[video.paused ? 'play' : 'pause']();
|
||||
document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
|
||||
}
|
||||
@ -26,7 +26,7 @@
|
||||
"r": clickOnElementBinding("#random")
|
||||
};
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.key in keybindings) {
|
||||
if(e.key in keybindings && e.target.tagName !== "INPUT") {
|
||||
e.preventDefault();
|
||||
keybindings[e.key]();
|
||||
}
|
||||
@ -144,19 +144,6 @@
|
||||
analyser.getByteFrequencyData(data);
|
||||
draw(data);
|
||||
}
|
||||
/*function draw(data) {
|
||||
data = [ ...data ];
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
const space = canvas.width / data.length;
|
||||
const sstyle = getComputedStyle(document.body).getPropertyValue("--accent");
|
||||
data.forEach((value, i) => {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(space * i, canvas.height);
|
||||
ctx.lineTo(space * i, canvas.height - value);
|
||||
ctx.strokeStyle = sstyle;
|
||||
ctx.stroke();
|
||||
});
|
||||
}*/
|
||||
function draw(data) {
|
||||
data = [ ...data ];
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
@ -29,6 +29,8 @@ const Cookie = {
|
||||
}));
|
||||
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.target.tagName === "INPUT")
|
||||
return;
|
||||
const acttheme = Cookie.get('theme') ?? "f0ck";
|
||||
const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase());
|
||||
const k = e.key;
|
||||
|
11
src/inc/meddlware.mjs
Normal file
11
src/inc/meddlware.mjs
Normal file
@ -0,0 +1,11 @@
|
||||
export const auth = (req, res) => {
|
||||
if(!req.session) {
|
||||
return {
|
||||
success: false,
|
||||
redirect: "/"
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
};
|
@ -7,8 +7,21 @@ export default new class Router {
|
||||
this.routes = new Map();
|
||||
};
|
||||
route(method, args) {
|
||||
this.routes.set(args[0], { method: method, f: args[1] });
|
||||
console.info("route set", method, args[0]);
|
||||
// args[0]: route
|
||||
// args[1]: func || meddlware
|
||||
const route = args[0];
|
||||
let func;
|
||||
let meddlware = null;
|
||||
if(args.length === 2) {
|
||||
func = args[1];
|
||||
}
|
||||
else {
|
||||
meddlware = args[1];
|
||||
func = args[2];
|
||||
}
|
||||
|
||||
this.routes.set(route, { method: method, meddlware: meddlware, f: func });
|
||||
console.info("route set", method, route);
|
||||
};
|
||||
get() {
|
||||
this.route("GET", arguments);
|
||||
@ -81,5 +94,5 @@ export default new class Router {
|
||||
};
|
||||
|
||||
Map.prototype.getRoute = function(path, method, tmp) {
|
||||
return (!(tmp = [...this.entries()].filter(r => ( r[0] === path || r[0].exec?.(path) ) && r[1].method.includes(method) )[0])) ? false : tmp[1].f;
|
||||
return (!(tmp = [...this.entries()].filter(r => ( r[0] === path || r[0].exec?.(path) ) && r[1].method.includes(method) )[0])) ? false : tmp[1];
|
||||
};
|
||||
|
@ -4,7 +4,8 @@ import tpl from "../tpl.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
import util from "util";
|
||||
import crypto from "crypto";
|
||||
import cfg from "../../../config.json";
|
||||
import { auth } from "../meddlware.mjs";
|
||||
import search from "./inc/search.mjs";
|
||||
|
||||
const scrypt = util.promisify(crypto.scrypt);
|
||||
|
||||
@ -50,15 +51,12 @@ router.post(/^\/login(\/)?$/, async (req, res) => {
|
||||
|
||||
return res.writeHead(301, {
|
||||
"Cache-Control": "no-cache, public",
|
||||
"Set-Cookie": `session=${session}; Path=/`,
|
||||
"Set-Cookie": `session=${session}; Path=/; Expires=Fri, 31 Dec 9999 23:59:59 GMT`,
|
||||
"Location": "/"
|
||||
}).end();
|
||||
});
|
||||
|
||||
router.get(/^\/logout$/, async (req, res) => {
|
||||
if(!req.session)
|
||||
return res.redirect("/");
|
||||
|
||||
router.get(/^\/logout$/, auth, async (req, res) => {
|
||||
const usersession = await sql("user_sessions").where("id", req.session.sess_id);
|
||||
if(usersession.length === 0)
|
||||
return res.reply({ body: "nope 2" });
|
||||
@ -66,7 +64,7 @@ router.get(/^\/logout$/, async (req, res) => {
|
||||
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",
|
||||
"Set-Cookie": "session=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT",
|
||||
"Location": "/login"
|
||||
}).end();
|
||||
});
|
||||
@ -88,11 +86,42 @@ router.get(/^\/login\/test$/, async (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/admin(\/)?$/, async (req, res) => {
|
||||
if(!req.session)
|
||||
return res.redirect("/");
|
||||
|
||||
router.get(/^\/admin(\/)?$/, auth, async (req, res) => { // frontpage
|
||||
res.reply({
|
||||
body: tpl.render("views/admin", {}, req)
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/admin\/sessions(\/)?$/, auth, async (req, res) => {
|
||||
const rows = await sql("user_sessions")
|
||||
.leftJoin("user", "user.id", "user_sessions.user_id")
|
||||
.select("user_sessions.*", "user.user")
|
||||
.orderBy("user.id");
|
||||
|
||||
res.reply({
|
||||
body: tpl.render("views/admin_sessions", {
|
||||
sessions: rows
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
||||
router.get(/^\/admin\/test(\/)?$/, auth, async (req, res) => {
|
||||
let ret;
|
||||
if(Object.keys(req.url.qs).length > 0) {
|
||||
const tag = req.url.qs.tag;
|
||||
|
||||
const rows = await sql("tags")
|
||||
.select("items.id", "items.username", "tags.tag")
|
||||
.leftJoin("tags_assign", "tags_assign.tag_id", "tags.id")
|
||||
.leftJoin("items", "items.id", "tags_assign.item_id")
|
||||
.where("tags.tag", "regexp", tag);
|
||||
|
||||
ret = search(rows, tag);
|
||||
}
|
||||
|
||||
res.reply({
|
||||
body: tpl.render("views/admin_search", {
|
||||
result: ret
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,7 @@
|
||||
import router from "../router.mjs";
|
||||
import sql from "../sql.mjs";
|
||||
import { auth } from "../meddlware.mjs";
|
||||
import util from "util";
|
||||
|
||||
const allowedMimes = [ "audio", "image", "video", "%" ];
|
||||
|
||||
@ -71,3 +73,132 @@ router.get(/^\/api\/v2\/user\/.*(\/\d+)?$/, async (req, res) => { // auf qs umst
|
||||
body: JSON.stringify(rows.length > 0 ? rows : [])
|
||||
});
|
||||
});
|
||||
|
||||
// adminzeugs
|
||||
router.post(/^\/api\/v2\/admin\/tags\/add$/, auth, async (req, res) => {
|
||||
if(!req.post.postid || !req.post.tag) {
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: false,
|
||||
msg: "missing postid or tag"
|
||||
})});
|
||||
}
|
||||
|
||||
const postid = +req.post.postid;
|
||||
const tag = req.post.tag;
|
||||
|
||||
if(tag.length >= 45) {
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: false,
|
||||
msg: "tag is too long!"
|
||||
})});
|
||||
}
|
||||
|
||||
try {
|
||||
let tagid;
|
||||
const tag_exists = await sql("tags").select("id", "tag").where("tag", tag);
|
||||
if(tag_exists.length === 0) { // create new tag
|
||||
tagid = (await sql("tags").insert({
|
||||
tag: tag
|
||||
}))[0];
|
||||
}
|
||||
else {
|
||||
tagid = tag_exists[0].id;
|
||||
}
|
||||
await sql("tags_assign").insert({
|
||||
tag_id: tagid,
|
||||
item_id: postid,
|
||||
prefix: `${req.session.user}@webinterface`
|
||||
});
|
||||
} catch(err) {
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: false,
|
||||
msg: err.message,
|
||||
tags: await getTags(postid)
|
||||
})});
|
||||
}
|
||||
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: true,
|
||||
postid: req.post.postid,
|
||||
tag: req.post.tag,
|
||||
tags: await getTags(postid)
|
||||
})});
|
||||
});
|
||||
|
||||
const getTags = async itemid => await sql("tags_assign")
|
||||
.leftJoin("tags", "tags.id", "tags_assign.tag_id")
|
||||
.where("tags_assign.item_id", itemid)
|
||||
.select("tags.id", "tags.tag", "tags_assign.prefix");
|
||||
|
||||
const cleanTags = async () => {
|
||||
const tags = await sql("tags").leftJoin("tags_assign", "tags_assign.tag_id", "tags.id").whereNull("tags_assign.item_id");
|
||||
if(tags.length === 0)
|
||||
return;
|
||||
|
||||
let deleteTag = sql("tags");
|
||||
tags.forEach(tag => {
|
||||
if(["sfw", "nsfw"].includes(tag.tag.toLowerCase()))
|
||||
return;
|
||||
deleteTag = deleteTag.orWhere("id", tag.id);
|
||||
});
|
||||
await deleteTag.del();
|
||||
};
|
||||
|
||||
router.post(/^\/api\/v2\/admin\/tags\/delete$/, auth, async (req, res) => {
|
||||
if(!req.post.postid || !req.post.tag) {
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: false,
|
||||
msg: "missing postid or tag"
|
||||
})});
|
||||
}
|
||||
|
||||
const postid = +req.post.postid;
|
||||
const tag = req.post.tag;
|
||||
|
||||
const tags = await getTags(postid);
|
||||
|
||||
const tagid = tags.filter(t => t.tag === tag)[0]?.id ?? null;
|
||||
if(!tagid || tagid?.length === 0) {
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: false,
|
||||
tag: tag,
|
||||
msg: "tag is not assigned",
|
||||
tags: await getTags(postid)
|
||||
})});
|
||||
}
|
||||
|
||||
let q = sql("tags_assign").where("tag_id", tagid).andWhere("item_id", postid).del();
|
||||
if(req.session.level < 50)
|
||||
q = q.andWhere("prefix", `${req.session.user}@webinterface`);
|
||||
const reply = !!(await q);
|
||||
|
||||
await cleanTags();
|
||||
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: reply,
|
||||
tag: tag,
|
||||
tagid: tagid,
|
||||
tags: await getTags(postid)
|
||||
})});
|
||||
});
|
||||
|
||||
router.get(/^\/api\/v2\/admin\/tags\/get\/\d+$/, auth, async (req, res) => {
|
||||
return res.reply({ body: JSON.stringify({
|
||||
tags: await getTags(+req.url.split[5])
|
||||
})});
|
||||
});
|
||||
|
||||
router.get(/^\/api\/v2\/admin\/deletepost\/\d+$/, auth, async (req, res) => {
|
||||
if(!req.url.split[4]) {
|
||||
return res.reply({ body: JSON.stringify({
|
||||
success: true,
|
||||
msg: "no postid"
|
||||
})});
|
||||
}
|
||||
const postid = +req.url.split[4];
|
||||
|
||||
const rows = await sql("items").where("id", postid).del();
|
||||
res.reply({ body: JSON.stringify({
|
||||
success: true
|
||||
})});
|
||||
});
|
||||
|
35
src/inc/routes/inc/search.mjs
Normal file
35
src/inc/routes/inc/search.mjs
Normal file
@ -0,0 +1,35 @@
|
||||
export default (obj, word) => {
|
||||
if(typeof obj !== "object")
|
||||
return false;
|
||||
return obj.map(tmp => {
|
||||
let rscore = 0
|
||||
, startat = 0
|
||||
, string = tmp.tag
|
||||
, cscore
|
||||
, score;
|
||||
for(let i = 0; i < word.length; i++) {
|
||||
const idxOf = string.toLowerCase().indexOf(word.toLowerCase()[i], startat);
|
||||
if(-1 === idxOf)
|
||||
return 0;
|
||||
if(startat === idxOf)
|
||||
cscore = 0.7;
|
||||
else {
|
||||
cscore = 0.1;
|
||||
if(string[idxOf - 1] === ' ')
|
||||
cscore += 0.8;
|
||||
}
|
||||
if(string[idxOf] === word[i])
|
||||
cscore += 0.1;
|
||||
rscore += cscore;
|
||||
startat = idxOf + 1;
|
||||
}
|
||||
score = 0.5 * (rscore / string.length + rscore / word.length);
|
||||
if(word.toLowerCase()[0] === string.toLowerCase()[0] && score < 0.85)
|
||||
score += 0.15;
|
||||
|
||||
return {
|
||||
...tmp,
|
||||
score: score
|
||||
};
|
||||
}).sort((a, b) => b.score - a.score);
|
||||
};
|
@ -17,17 +17,24 @@ import router from "./inc/router.mjs";
|
||||
|
||||
req.url = url.parse(req.url.replace(/(?!^.)(\/+)?$/, ''));
|
||||
req.url.split = req.url.pathname.split("/").slice(1);
|
||||
req.url.qs = querystring.parse(req.url.query);
|
||||
req.url.qs = {...querystring.parse(req.url.query)};
|
||||
|
||||
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) {
|
||||
return [k, v];
|
||||
.on("end", () => {
|
||||
if(req.headers['content-type'] == "application/json") {
|
||||
try {
|
||||
return void resolve(JSON.parse(data));
|
||||
} catch(err) {}
|
||||
}
|
||||
})))));
|
||||
void resolve(Object.fromEntries(Object.entries(querystring.parse(data)).map(([k, v]) => {
|
||||
try {
|
||||
return [k, decodeURIComponent(v)];
|
||||
} catch(err) {
|
||||
return [k, v];
|
||||
}
|
||||
})));
|
||||
}));
|
||||
|
||||
res.reply = ({
|
||||
code = 200,
|
||||
@ -68,13 +75,29 @@ import router from "./inc/router.mjs";
|
||||
}).end();
|
||||
}
|
||||
}
|
||||
|
||||
!(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`,
|
||||
`${req.method} ${res.statusCode}`,
|
||||
req.url.pathname].map(e=>e.toString().padEnd(15)).join(""));
|
||||
req.url.pathname
|
||||
].map(e => e.toString().padEnd(15)).join(""));
|
||||
|
||||
if(r = router.routes.getRoute(req.url.pathname, req.method)) {
|
||||
if(r.meddlware) {
|
||||
const tmp = r.meddlware(req, req);
|
||||
if(tmp.redirect)
|
||||
return res.redirect(tmp.redirect);
|
||||
if(!tmp.success)
|
||||
return res.reply({ body: tmp.msg });
|
||||
}
|
||||
await r.f(req, res);
|
||||
}
|
||||
else {
|
||||
res
|
||||
.writeHead(404)
|
||||
.end(`404 - ${req.method} ${req.url.pathname}`);
|
||||
}
|
||||
}).listen(cfg.websrv.port, () => setTimeout(() => {
|
||||
console.log(`f0ck is listening on port ${cfg.websrv.port}.`);
|
||||
}, 500));
|
||||
|
@ -44,9 +44,9 @@
|
||||
<p>f0ck is completely functional without javascript enabled, you can be the beardiest neckbeard of all, we got you m'gentleman, if you want to use a custom theme you gotta allow our style cookie.</p>
|
||||
<h5>Known Bugs</h5>
|
||||
<p style="text-decoration: line-through">Theme plopper (If theme is selected the default f0ck sheme will appear for a small amount of time until the custom stylesheet is applied) - ETA: Christmas™</p>
|
||||
<p>Magical video seeker (If you hold mouseclick for too long on the video timeline and drag to a specific position it will go crazy, don't!) - ETA: Christmas™</p>
|
||||
<p style="text-decoration: line-through">Magical video seeker (If you hold mouseclick for too long on the video timeline and drag to a specific position it will go crazy, don't!) - ETA: Christmas™</p>
|
||||
<h5>f0ck Privacy?</h5>
|
||||
<p>Cookies: Yes, we set 1 cookie for your prefered stylesheet, for the _cfduid cookie please see <a href="https://blog.cloudflare.com/deprecating-cfduid-cookie/">https://blog.cloudflare.com/deprecating-cfduid-cookie/</a></p>
|
||||
<p>Cookies: Yes, we set 1 cookie for your prefered stylesheet, for the _cfduid cookie please see <a href="https://blog.cloudflare.com/deprecating-cfduid-cookie/" target="_blank">https://blog.cloudflare.com/deprecating-cfduid-cookie/</a></p>
|
||||
<p>Logs: No for Tor - Yes for cloudflare and cloudflare probably sells your soul to the devil, however our webserver doesn't log cloudflare connecting to our webserver, if you want to lurk without being flared by the cloud, see the above tor section my man</p>
|
||||
<p>But let me tell you something about internet "privacy". At first you need to understand what it means to have "privacy", for me as a human the word privacy means that I am by myself, private not observable by others, on the internet this concept does not work, in real life you might lock your door and then no one can enter the normal way to your room and you have some good old fashioned privacy, but on the internet various applications on your computer including extensions for your browser might make connections without you knowing or even giving consent if you knew, most applications send heartbeats, store information, read files on your computer, they might even process the gained data with or without you knowing or consenting to any of it, you probably accepted in good faith the ToS of many services without ever reading them, in the end it's up to you if you give a shit about your privacy, btw a VPN wont help if you still got all the tracking cookies and shit in your browser, they will just add 1+1 and you are identified again. My honest advice for anyone who seeks total privacy without bullshit, disconnect from the internet, remove the internet from your life, it's a bulletproof solution! With that being said, have a good day!</p>
|
||||
</div>
|
||||
|
27
views/admin_search.html
Normal file
27
views/admin_search.html
Normal file
@ -0,0 +1,27 @@
|
||||
{{include main/header_admin}}
|
||||
<form action="/admin/test" style="margin-top: 15px;">
|
||||
<input type="text" name="tag" /><button type="submit">search</button>
|
||||
</form>
|
||||
<hr />
|
||||
{{if result}}
|
||||
<h1>{{=result.length}} f0cks given</h1>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="text-align: center;">Thumbnail</td>
|
||||
<td style="text-align: center;">ID</td>
|
||||
<td style="text-align: center;">Tag</td>
|
||||
<td style="text-align: center;">Username</td>
|
||||
<td style="text-align: center;">Score</td>
|
||||
</tr>
|
||||
{{each result as line}}
|
||||
<tr>
|
||||
<td style="width: 128px;"><a href="/{{=line.id}}" target="_blank"><img src="/t/{{=line.id}}.png" /></a></td>
|
||||
<td style="text-align: center;"><a href="/{{=line.id}}" target="_blank">{{=line.id}}</a></td>
|
||||
<td style="text-align: center;"><a href="/admin/test?tag={{=line.tag.replace(/\s/g, "+")}}">{{=line.tag}}</a></td>
|
||||
<td style="text-align: center;">{{=line.username}}</td>
|
||||
<td style="text-align: center;">{{=line.score}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/if}}
|
||||
{{include main/footer}}
|
22
views/admin_sessions.html
Normal file
22
views/admin_sessions.html
Normal file
@ -0,0 +1,22 @@
|
||||
{{include main/header_admin}}
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>userid</td>
|
||||
<td>user</td>
|
||||
<td>browser</td>
|
||||
<td>created_at</td>
|
||||
<td>last_used</td>
|
||||
</tr>
|
||||
{{each sessions as session}}
|
||||
<tr>
|
||||
<td>{{=session.id}}</td>
|
||||
<td>{{=session.user_id}}</td>
|
||||
<td>{{=session.user}}</td>
|
||||
<td>{{=session.browser}}</td>
|
||||
<td>{{=new Date(session.created_at * 1e3).toLocaleString("de-DE")}}</td>
|
||||
<td>{{=new Date(session.last_used * 1e3).toLocaleString("de-DE")}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{include main/footer}}
|
@ -46,14 +46,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="metadata">
|
||||
<span class="badge badge-dark"><a href="/{{=item.id}}" style="--hover-image: url('/t/{{=item.id}}.png');" class="id-link">{{=item.id}}</a></span>
|
||||
<span class="badge badge-dark">
|
||||
<a href="/{{=item.id}}" style="--hover-image: url('/t/{{=item.id}}.png');" class="id-link">{{=item.id}}</a>{{if session}} (<span id="a_username">{{=user.name}}</span>){{/if}}
|
||||
</span>
|
||||
<span class="badge badge-dark">{{=user.network}} / {{=user.channel}}</span>
|
||||
<span class="badge badge-dark image-source"><a class="post_source" title="{{=item.src.long}}" href="{{=item.src.long}}" target="_blank">{{=item.src.short}}</a></span>
|
||||
<span class="badge badge-dark"><a class="dest-link" href="{{=item.dest}}" target="_blank">{{=item.mime}}</a> / {{=item.size}}</span>
|
||||
<span class="badge badge-dark"><time class="timeago" title="{{=item.timestamp}}" datetime="{{=item.timestamp}}">{{=item.timestamp}}</time></span>
|
||||
<span class="badge badge-dark">
|
||||
{{if session}}
|
||||
<a href="#">delete</a>
|
||||
<a href="#" id="a_delete">delete</a>
|
||||
{{else}}
|
||||
{{=lul}}
|
||||
{{/if}}
|
||||
@ -61,9 +63,16 @@
|
||||
<span class="badge badge-dark" id="tags">
|
||||
{{if typeof item.tags !== "undefined"}}
|
||||
{{each item.tags as tag}}
|
||||
<span class="badge badge-{{=(tag.tag === "nsfw" ? "danger" : tag.tag === "sfw" ? "success" : "light")}} mr-2">{{=tag.tag}}</span>
|
||||
<span {{if session}}title="{{=tag.prefix}}"{{/if}} class="badge {{if tag.tag[0] == "&"}}badge-greentext{{/if}} badge-{{=(tag.tag === "nsfw" ? "danger" : tag.tag === "sfw" ? "success" : "light")}} mr-2">
|
||||
{{if session}}<a href="/admin/test?tag={{=tag.tag.replace(/\s/g, "%20")}}" target="_blank" style="color: inherit !important;">{{/if}}{{=tag.tag}}{{if session}}</a> <a href="#">×</a>{{/if}}
|
||||
</span>
|
||||
{{/each}}
|
||||
{{/if}}</span>
|
||||
{{/if}}
|
||||
{{if session}}
|
||||
<a href="#" id="a_addtag">add tag</a>
|
||||
- <a href="#" id="a_toggle">toggle</a>
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{include main/footer}}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script async src="/s/js/theme.js"></script>
|
||||
<script src="/s/js/v0ck.js"></script>
|
||||
<script src="/s/js/f0ck.js"></script>
|
||||
{{if session}}<script src="/s/js/admin.js?v=3"></script>{{/if}}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -6,12 +6,15 @@
|
||||
<link rel="stylesheet" href="/s/css/f0ck.css">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="f0ck.me is the place where internet purists gather to celebrate content of all kinds">
|
||||
{{if data.item}}
|
||||
<meta property="og:site_name" content="f0ck.me" />
|
||||
<meta property="og:description"/>
|
||||
<meta name="Description"/>
|
||||
{{if item.tags}}
|
||||
<meta property="og:description" content="{{each item.tags as tag}}{{=tag.tag.replace(/[\\$'"]/g, "\\$&")}},{{/each}}f0ck" />
|
||||
<meta name="description" content="{{each item.tags as tag}}{{=tag.tag.replace(/[\\$'"]/g, "\\$&")}},{{/each}}f0ck" />
|
||||
{{/if}}
|
||||
<meta property="og:image" content="{{=item.thumbnail}}" />
|
||||
{{else}}
|
||||
<meta name="description" content="f0ck.me is the place where internet purists gather to celebrate content of all kinds">
|
||||
{{/if}}
|
||||
</head>
|
||||
<body>
|
||||
|
@ -7,8 +7,10 @@
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" href="/admin" content="{{=session.user}}" data-toggle="dropdown">Admin</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/admin">Adminpanel</a></li>
|
||||
<li><a href="/logout">Logout</a></li>
|
||||
<li><a href="/admin">adminpanel</a></li>
|
||||
<li><a href="/admin/sessions">sessions</a></li>
|
||||
<li><a href="/admin/test">search (wip)</a></li>
|
||||
<li><a href="/logout">logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{{else}}
|
||||
|
@ -11,13 +11,13 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<span class="nav-link-identifier">blah</span>
|
||||
<a class="nav-link" href="/admin/sessions">
|
||||
<span class="nav-link-identifier">sessions</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<span class="nav-link-identifier">blah</span>
|
||||
<a class="nav-link" href="/admin/test">
|
||||
<span class="nav-link-identifier">search (wip)</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
Loading…
Reference in New Issue
Block a user