198 lines
6.0 KiB
JavaScript
198 lines
6.0 KiB
JavaScript
const path = require("path");
|
|
|
|
const bodyParser = require("koa-body")({multipart: true});
|
|
const fetch = require("node-fetch");
|
|
const Koa = require("koa");
|
|
const koaPug = require("koa-pug");
|
|
const koaRouter = require("koa-router");
|
|
const { MysqlError } = require("mysql");
|
|
|
|
const config = require("./config");
|
|
const guestbook = require("./guestbook");
|
|
const mindflow = require("./mindflow");
|
|
const util = require("../shared/util");
|
|
const { URLSearchParams } = require("url");
|
|
|
|
|
|
const articles_meta = {
|
|
"rpi_root_on_external_drive": {
|
|
title: "How to move a root from SD card to external drive on Raspberry Pi",
|
|
description: "How to move a root partition to an ext. drive on Raspberry Pi."
|
|
},
|
|
"setting_up_a_tor_proxy_relay_hiddenserv": {
|
|
title: "Setting up a Tor proxy, relay and hidden service",
|
|
description: "How to setup a Tor proxy, relay and hidden service."
|
|
},
|
|
"setting_up_a_mail_server": {
|
|
title: "Setting up a mail server",
|
|
description: "How to create your own mail server using Postfix and Dovecot."
|
|
},
|
|
"nginx_recipes_and_tips": {
|
|
title: "NGiNX's recipes & tips",
|
|
description: "Tips and recipes for NGiNX webserver."
|
|
},
|
|
"hardening_mikrotik": {
|
|
title: "Hardening Mikrotik",
|
|
description: "How to harden security of your Mikrotik router."
|
|
},
|
|
};
|
|
|
|
async function getProcesses() {
|
|
try {
|
|
let reimu = await fetch("http://reimu.arav.home.arpa:14882/processes").then(r => r.json());
|
|
let sakuya = await fetch("http://sakuya.arav.home.arpa:14882/processes").then(r => r.json());
|
|
return Object.fromEntries(Object.entries(reimu).concat(Object.entries(sakuya)));
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// 127.0.0.1:19322
|
|
async function getNewCaptcha() {
|
|
try {
|
|
return await fetch("http://startpage.arav.home.arpa/captcha/", {method: "POST"}).then(r => r.text());
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function solveCaptcha(id, answer) {
|
|
try {
|
|
let body = new URLSearchParams();
|
|
body.append('answer', answer);
|
|
let result = fetch(`http://startpage.arav.home.arpa/captcha/${id}`, { method: "POST", body: body } );
|
|
return await result.then(r => {
|
|
return r.status == 202;
|
|
});
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function setRoutes() {
|
|
return koaRouter().get('/', async ctx => {
|
|
await ctx.render('index', {
|
|
description: "A homepage of a russian guy Arav. Not just homepage, but FTP, radio, and smth more as well."
|
|
});
|
|
})
|
|
.get('/rss.xml', async ctx => {
|
|
ctx.type = 'xml';
|
|
await ctx.render('rss', {
|
|
doctype: 'xml',
|
|
schemahost: `${ctx.header.schema}://${ctx.header.host}`,
|
|
author: "me@arav.top (Alexander \"Arav\" Andreev)",
|
|
items: await mindflow.getPosts()
|
|
})
|
|
})
|
|
.get('/stuff', async ctx => {
|
|
await ctx.render('stuff', {
|
|
title: "/ Stuff",
|
|
description: "Here I share my programs, scripts, articles, may be other stuff.",
|
|
git_site: util.getServiceByHost(ctx.request.host, "git"),
|
|
files_site: util.getServiceByHost(ctx.request.host, "files")
|
|
})
|
|
})
|
|
.get('/mindflow', async ctx => {
|
|
await ctx.render('mindflow', {
|
|
title: "/ Mindflow",
|
|
description: "Here I will post updates on my infrastructure, my very important opinions and thoughts.",
|
|
files_site: util.getServiceByHost(ctx.request.host, "files"),
|
|
tz: util.getClientTimezone(ctx),
|
|
posts: await mindflow.getPosts()
|
|
})
|
|
})
|
|
.get('/stuff/article/:article', async ctx => {
|
|
if (ctx.params.article) {
|
|
return await ctx.render("articles/" + ctx.params.article, {
|
|
title: "/ Article / " + articles_meta[ctx.params.article].title,
|
|
description: articles_meta[ctx.params.article].description
|
|
});
|
|
} else {
|
|
return ctx.throw(404);
|
|
}
|
|
})
|
|
.get('/about', async ctx => {
|
|
await ctx.render('about', {
|
|
title: "/ About",
|
|
description: "This is a page where I'm telling about me and my home server.",
|
|
files_site: util.getServiceByHost(ctx.request.host, "files"),
|
|
services: await getProcesses()
|
|
})
|
|
})
|
|
.get('/guestbook', async ctx => {
|
|
const page = ctx.request.query.p !== undefined ? ctx.request.query.p : 1;
|
|
const page_size = ctx.request.query.ps !== undefined ? ctx.request.query.ps : guestbook.pageSize;
|
|
|
|
let posts;
|
|
|
|
try {
|
|
posts = await guestbook.getPosts(page, page_size);
|
|
} catch(err) {
|
|
posts = null;
|
|
}
|
|
|
|
return await ctx.render('guestbook', {
|
|
title: "/ Guestbook",
|
|
description: "This is my guestbook. Welcome.",
|
|
tz: util.getClientTimezone(ctx),
|
|
owner: config.guestbook.owner,
|
|
posts: posts,
|
|
pages_count: Math.ceil(await guestbook.getPostsCount() / page_size),
|
|
captcha_id: await getNewCaptcha() });
|
|
})
|
|
.post('/guestbook', bodyParser, async ctx => {
|
|
const post = ctx.request.body;
|
|
|
|
post.hide_website = post.hide_website !== undefined;
|
|
|
|
try {
|
|
let check = await solveCaptcha(post.captcha_id, post.captcha_answer);
|
|
if (!check)
|
|
throw "CAPTCHA is wrong or expired";
|
|
|
|
if (await guestbook.addPost(post))
|
|
ctx.redirect("/guestbook");
|
|
} catch(err) {
|
|
if (typeof err == 'object' && err instanceof MysqlError) {
|
|
ctx.response.status = 500;
|
|
ctx.response.body = { error: `Database failed so your post wasn't added. Here's your message:`, message: post.message };
|
|
} else if (typeof err == 'string') {
|
|
ctx.response.status = 403;
|
|
ctx.response.body = { error: `Reason why your post was rejected is "${err}". Here's your message:`, message: post.message };
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
const app = new Koa();
|
|
const pug = new koaPug({
|
|
viewPath: path.join(__dirname, "views"),
|
|
locals: {
|
|
date_: (date, tz) => util.datetime(date, util.date_formats.post_date, tz),
|
|
date_id: (date, tz) => util.datetime(date, util.date_formats.id_date, tz),
|
|
date_rss: date => util.datetime(date, util.date_formats.rss_date, "UTC"),
|
|
rssLink: util.rssLink },
|
|
app: app
|
|
});
|
|
|
|
|
|
app.proxy = true;
|
|
app
|
|
.use(async (ctx, next) => {
|
|
try {
|
|
await next();
|
|
if (ctx.status !== 200) ctx.throw(ctx.status);
|
|
} catch (err) {
|
|
ctx.status = err.status || 500;
|
|
if (ctx.status === 404)
|
|
await ctx.render('404');
|
|
else if (ctx.status === 403)
|
|
await ctx.render('403', { error: ctx.body });
|
|
else
|
|
await ctx.render('500', { error: ctx.body });
|
|
}
|
|
})
|
|
.use(setRoutes().routes())
|
|
.listen(config.port, config.host);
|