commit 23861292d17c0c661703e20e0e14ced778489146 Author: Alexander Arav Andreev Date: Wed Feb 10 01:01:50 2021 +0400 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7d4aa3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +config.js +package-lock.json diff --git a/backup.sql b/backup.sql new file mode 100644 index 0000000..8c86a42 --- /dev/null +++ b/backup.sql @@ -0,0 +1,90 @@ +INSERT INTO `blog` (`date`, `category`, `title`, `body`) +VALUES + ( + "2020-07-28 16:51:00", 1, "Banners.", + "Konnichiwa. I made banners for my websites of 88x31 and 240x60 sizes."), + ( + "2020-07-29 00:00:00", 1, "Quick rundown on what happened since the december of the last year.", + "At first I moved off to aravs.ru domain in december 2019 that I was gonna change with, maybe, old arav.icu if it'll be available for cheap because renewal prices are too high for me. But then I found a new registrar with dirt cheap renewal price and now I have arav.top domain I've payed for 2 years and if nothing change I'll renew it again and again. So, yes, I finally found my domain that I'm not gonna switch if only I will be forced to.n" + "And mostly because of my lazyness I wasn't updating arav.neocities.org for fucking 3 months. That's also because I was trying to reduce its about page.n" + "Then at July 13th my RPi suddenly stopped working. It didn't take much time to realise that the problem was a dying HDD. Hopefully, fsck fixed filesystem and I successfully moved all the stuff onto another drive I took from a laptop that was holding my data. And at July 16th a new SSD arrivedto replace old laptop's system drive that now holds all my stuff and I came back online."), + ( + "2020-11-07 00:40:00", 1, "Looks like I'm back to work on this website.", + "And maybe I'll finally do something with my neocities' website. Maybe..."), + ( + "2020-11-10 14:54:00", 1, "New banners.", + "I made new banners. They're pretty much the same as was before. I fixed frames order and logo now takes all width."), + ( + "2020-11-14 00:50:00", 1, "Oh, PGP key wasn't accessible... Shite.", + "I just found that my PGP key isn't available. Now it is. I apologise to those who wanted it. According to logs this was a problem since the second half of september. :/"), + ( + "2020-11-16 00:10:00", 1, "Dark theme flickering fixed.", + "Now a check for a current theme is inside a DOMContentLoaded event listener and script itself is now in a <head>.\n" + "I also added an async attribute to a guestbook's script so page won't wait for guestbook posts to load and won't flicker light theme because of that.\n" + "And I remade 88x31 banner and made banner's dark counterparts as well. So you have what to choose from. :)"), + ( + "2020-11-16 11:28:00", 1, "Maintanance.", + "It's time to replace a thermal compound in the laptop, so radio, FTP and Gitea would be down for a while today."), + ( + "2020-11-16 13:40:00", 1, "Maintanance complete.", + "Thermal compound replaced, not much dust there was. It used to work at 62°C. Will see later. :) Now it runs on 49°C after 11 minutes. That only means I did everything right.\n" + "P.S.: Now it runs on 49-52°C. Nice."), + ( + "2020-11-16 15:59:00", 1, "Uuhh, this motherfucker still flickers...", + "Is there a cure for it ... ? At least, I managed to reduce this soykaf."), + ( + "2020-11-17 18:13:00", 1, "Dark theme is default from now.", + "Ok, in order to make flickering less painful I made dark theme default."), + ( + "2020-11-17 21:57:00", 1, "Yay, fuck JS!", + "I rewrote the guestbook without JS. Hooray!"), + ( + "2020-11-19 00:12:00", 1, "ScrapTheChan ver. 0.4.0 released.", + "Today I fixed most of noticed problems and released it. Changelog is behind the link."), + ( + "2020-11-19 22:51:00", 2, "Yesterday I was replacing a thermal compound on my PC.", + "Yesterday (19 Nov for me is it :)) I replaced a thermal compound on my PC, and, of course, it just couldn't be an ordinary procedure.\n" + "When I assembled everything back and turned on the PC it started swearing at me with 26 beeps and rebooting. I knew that it could be a problem with a RAM stick, but I didn't believe it. And I dissassembled it again and found that a few pins were bended and one missing (!). I fixed them all (around 5 didn't count) and was thinking what to do with that missing pin. I thought it was causing problem but searching led me to a pinout of the socket 1150 and that pin was doing something called VDDQ, and I found that it isn't a problem at all. There was a questionable pin SA_DQ57 (whatever it means), but I bended it a little and it's fine.\n" + "Oh, why I even took it from the socket? To clean it from an old thermal compound.\n" + "Well, I almost give in to a thought of taking a motherboard to a service and then I tried to put a RAM stick to the second slot and ... it worked! Shit, I don't know what happened. I've vacuumed the motherboard and was absolutely careful. No doubt I didn't break anything, yet, fuck. That vacuum cleaner isn't even powerful enough to suck in anything soldered.\n" + "But there's one breakage for sure. One of fixators of a cooler has broken. Shit. Now radiator isn't attached well to the CPU. Hopefully it led to the same temperatures as they was before at least. xD Don't want to whip up it with shit and bricks a zip tie, so I'd better get a new one. And I already ordered it, a little smaller, but supports socket AM4, so I will reuse it in the future, great.\n" + "What's next after all the stress I got? I'm going to replace a compound on my graphics card! Ahahahahaha! xDD\n" + "P.S.: I made a mindflow.html after I wrote this post.\n" + "Update: Okay, SA_DQ is a data bus for memory, so by bending that pin I lost first ram slot. Well, that's not because of cleaning at least.\n"), + ( + "2020-11-23 00:30:00", 1, "Recent updates.", + "I did a good work at this website. Especially I love that heading with a name of a site's section under the nav menu. And I finally made this section. I keep moving on. Next major step is to make a links page. My banners are ready, but no one would add me if I won't have a place for theirs banners. :)\n" + "Yeah, still no updates to Neocities' website. Now I'm working on a design of this place, and once I'll be satisfied that place will get an update. I work on it too, especially on an about page's content. Last not noticeable update was changing link of a guestbook to a JS-free new one."), + ( + "2020-11-26 23:54:00", 1, "Article on userdir in NGiNX.", + "I thought why not to make an article on how to implement a userdir functionality in NGiNX. Maybe, someone will want to implement this on their server.\n" + "And, an article on e-mail server is hard to finish, because every time I sit on it I find flaws in e-mail server's configuration, LMAO."), + ( + "2020-11-27 15:07:00", 1, "Tor and I2P access for this site and files.", + "I was thinking why not to make my place available through Tor and I2P. So after minor links fixing I made this place work like a charm through darkweb. Currently only main website and file share are available.\n" + "t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p and file share.\n" + "moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion and file share.\n"), + ( + "2020-12-01 20:30:00", 1, "Laptop's RAM update.", + "Today I replaced old 1333MHz 2GB stick with a new 8GB 1600MHz. Now laptop has 10 gigs. I am to figure out what to fill it with. :)"), + ( + "2020-12-04 22:27:00", 1, "Article on userdir in NGiNX rewrote to be an article with recipes for it.", + "Yep, I think whole article for such a small thing is too much, so now there is an article with recipes and tips for NGiNX.\n" + "Now there is info on how to implement userdir functionality and a note about how NGiNX works with HTTP headers."), + ( + "2021-02-08 22:19:00", 1, "New year. Big changes.", + "All this time I was working on a look of a website. And just for a last week I said to myself that I'm tired of editing every single thingy across all pages. So I came to conclusion that it's time to try NodeJS. So I took Koa and Pug and ported my website to it. Well, it's really amazing. Code's so clean now (if you only saw a code of a PHP version of guestbook... xD). Template engines are an invaluably great invention. :)\n" + "Yeah, I got rid of Javascript on the outside, but instead, bring it inside. xD Anyway, I was always thinking that Javascript itself never was a problem. Problem is that it is often used where absolutely unnecessary. And browser's APIs brings many convenient tools for us alongside with the ways of doing malicious deeds as well (like fingerprinting, crypto-mining, viruses, etc.). Alas, this is one of the laws of life, everything could be used for evil.\n" + "There's one project I'm working on (now developing database). It's a service for file uploading, but files could be downloaded just one time. Yeah, I called it "One-Time File". It'll be available by otf.arav.top link. A file would be kept for 7 days until it won't be downloaded, and size limit is 120MB. That'll be a really interesting experience, since I'll've to figure out how to be sure that file was fully downloaded before it can be deleted. :)\n" + "And speaking of my Neocities' site. Well, it looks like that I move out of there, but I definitely don't. That place would be my outpost with links to my website and services. :/"); + + +INSERT INTO `guestbook` +VALUES + (1, "2020-09-01 00:15:22", "Serana", "", "", "hey whats up! i recently found that out neocities is actually really cool and have spent the past few hours looking through whatever sites i can find (yours included!). figured i'd drop a message and say to keep up what you're doing! this is all really cool and i'm feeling all motivated to make something becuase of it. hope you have a nice day <3", 1, 1), + (2, "2020-11-09 04:18:27", "duli", "", "", "your website is super cool! hope you're having a good day <3", 1, 1); + +INSERT INTO `guestbook_feedback` +VALUES + (1, "2020-11-10 00:17:31", "Well, better late than never. :) Hope feedbacks now work fine. Dunno when and if you ever read it again, but still, hope you're doing good as well. :) I'm so glad to read that I motivate others. Personal website is indeed a great thing that allows us to show who we are immeasuarably better than soulless social media. <3"), + (2, "2020-11-10 00:17:31", "Hi! Yeah, hope and you do so! <3 Great, that means I'm defenitely moving in a right direction. :) Need to dedicate more time for the websites, especially neocities' one literally begs to be updated, heh. But I have to rethink this first."); \ No newline at end of file diff --git a/config.example.js b/config.example.js new file mode 100644 index 0000000..2170302 --- /dev/null +++ b/config.example.js @@ -0,0 +1,16 @@ + +exports.dwelling = {} + +exports.dwelling.port = 3200; +exports.dwelling.host = "127.0.0.1"; + +exports.dwelling.database = { + host: "192.168.0.1", + database: "db", + user: "user", + password: "password" }; + +exports.dwelling.guestbook = {} + +exports.dwelling.guestbook.owner = "Owner"; +exports.dwelling.guestbook.pageSize = 30; \ No newline at end of file diff --git a/dwelling.js b/dwelling.js new file mode 100644 index 0000000..b04c9eb --- /dev/null +++ b/dwelling.js @@ -0,0 +1,161 @@ +const path = require("path"); + +const Koa = require("koa"); +const koaPug = require("koa-pug"); +const koaRouter = require("koa-router"); +const koaServe = require("koa-static"); +const bodyParser = require("koa-body")({multipart: true}); +const mysql = require("mysql"); +const fetch = require("node-fetch"); + +const config = require("./config"); +const util = require("./util"); +const guestbook = require("./dwelling/guestbook"); +const mindflow = require("./dwelling/mindflow"); + + +const articles_meta = { + "rpi_root_on_external_drive": { + title: "How to move a root from SD card to external drive on Raspberry Pi", + description: "Article on moving 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: "Article on setting up a Tor proxy, relay and hidden service." + }, + "setting_up_a_mail_server": { + title: "Setting up a mail server", + description: "Article on creating your own mail server using Postfix and Dovecot." + }, + "nginx_recipes_and_tips": { + title: "NGiNX's recipes & tips", + description: "Article on tips and recipes for NGiNX webserver." + }, +}; + +async function getProcesses() { + let reimu = await fetch("http://reimu.home:14882/processes").then(r => r.json()); + let sakuya = await fetch("http://sakuya.home:14882/processes").then(r => r.json()); + return Object.fromEntries(Object.entries(reimu.processes).concat(Object.entries(sakuya.processes))); +} + +function setRoutes(router) { + router.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', + protocol: ctx.protocol, + host: ctx.header.host, + author: "me@arav.top (Alexander \"Arav\")", + items: await mindflow.getPostsForRSS() + }) + }) + .get('/stuff', async ctx => { + await ctx.render('stuff', { + title: "/ Stuff", + description: "Here I share my programs, scripts, articles, may be other stuff." + }) + }) + .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.", + diary: await mindflow.getPosts("Diary"), + updates: await mindflow.getPosts("Update") + }) + }) + .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.", + 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.", + owner: config.dwelling.guestbook.owner, + posts: posts, + error: posts === null, + pages_count: Math.ceil(await guestbook.getPostsCount() / page_size) }); + }) + .post('/guestbook', bodyParser, async ctx => { + const post = ctx.request.body; + + post.hide_email = post.hide_email !== undefined; + post.hide_website = post.hide_website !== undefined; + + try { + if (await guestbook.addPost(post)) + ctx.redirect("/guestbook"); + } catch(err) { + ctx.type = "text/plain"; + if (err instanceof mysql.MysqlError) + ctx.body = `Database failed so your post wasn't added. So your time wasn't wasted here's your message:\n${post.message}`; + else + ctx.body = `Your post was rejected because of "${err}". So your time wasn't wasted here's your message:\n${post.message}`; + } + }); +} + + +module.exports = () => { + const app = new Koa(); + const pug = new koaPug({ + viewPath: path.join(__dirname, "views", "dwelling"), + locals: { + date_: date => util.datetime(date, util.formats.post_date), + mindflowDateToId: date => util.datetime(date, util.formats.id_date), + rssLink: util.rssLink }, + app: app + }); + + const dwelling_router = koaRouter(); + setRoutes(dwelling_router); + + app + .use(async (ctx, next) => { + try { + await next(); + if (ctx.status === 404) ctx.throw(404); + } catch (err) { + if (ctx.status === 404) + await ctx.render('404'); + else + await ctx.render('500'); + } + }) + .use(koaServe(path.join("static", "shared"))) + .use(koaServe(path.join("static", "dwelling"))) + .use(dwelling_router.routes()) + .listen(config.dwelling.port, config.dwelling.host); + + return app; +}; \ No newline at end of file diff --git a/dwelling/guestbook.js b/dwelling/guestbook.js new file mode 100644 index 0000000..0d56317 --- /dev/null +++ b/dwelling/guestbook.js @@ -0,0 +1,92 @@ +const mysql = require("mysql"); + +const config = require("../config"); + + +let connection = mysql.createConnection(config.dwelling.database); + + +// This code was taken from https://www.npmjs.com/package/mysql#custom-format +// Only fourth line was changed. +connection.config.queryFormat = function (query, values) { + if (!values) return query; + return query.replace(/\:(\w+)/g, function (txt, key) { + if (values[key] !== undefined) + return this.escape(values[key]); + return txt; + }.bind(this)); +}; + + +exports.pageSize = config.dwelling.guestbook.pageSize !== undefined + ? config.dwelling.guestbook.pageSize : 20; + + +exports.closeConnection = () => connection.end(); + +exports.getPosts = async (page = 1, page_size = exports.pageSize) => { + return new Promise((resolve, reject) => { + let query = "\ + SELECT \ + `guestbook`.`post_id`, \ + `guestbook`.`created`, \ + `guestbook`.`name`, \ + (CASE \ + WHEN `guestbook`.`hide_email` IS false \ + THEN `guestbook`.`email` \ + ELSE NULL \ + END) AS `email`, \ + (CASE \ + WHEN `guestbook`.`hide_website` IS false \ + THEN `guestbook`.`website` \ + ELSE NULL \ + END) AS `website`, \ + `guestbook`.`message`, \ + `guestbook_feedback`.`comment` AS `feedback`, \ + `guestbook_feedback`.`created` AS `feedback_created` \ + FROM `guestbook` \ + LEFT JOIN `guestbook_feedback` ON `guestbook`.`post_id` = `guestbook_feedback`.`post_id` \ + ORDER BY `guestbook`.`created` DESC \ + LIMIT :page_size \ + OFFSET :page_offset;"; + + connection.query(query, { page_size: page_size, page_offset: (page-1) * page_size }, + (err, results) => { + if (err) reject(err); + resolve(results); }); + }); +}; + +exports.getPostsCount = async () => { + let promise = new Promise((resolve, reject) => { + connection.query("SELECT COUNT(`post_id`) AS total FROM `guestbook`;", (err, results, fields) => { + if (err) reject(err); + resolve(results[0].total); }); + }); + + return promise.then(count => count); +}; + +exports.addPost = async (post) => { + return new Promise((resolve, reject) => { + let query = "INSERT INTO `guestbook` (`name`, `email`, `website`, `message`, `hide_website`, `hide_email`) \ + VALUES (:name, :email, :website, :message, :hide_website, :hide_email);"; + + if (post.message === undefined || post.message === "") + reject("empty message"); + + if (post.message.includes("://")) + reject("spam"); + + if (post.name === undefined || post.name === "") + post.name = "Anonymous"; + + post.message = post.message.trim(); + post.message = post.message.replace(/(?:\r\n|\n\r|\r)/g, "\n"); + post.message = post.message.replace(/(?:\n\n|\n \n)/g, "\n"); + + connection.query(query, post, (err, results, fields) => { + if (err) reject(err); + resolve(true); }); + }); +} \ No newline at end of file diff --git a/dwelling/mindflow.js b/dwelling/mindflow.js new file mode 100644 index 0000000..d19f77b --- /dev/null +++ b/dwelling/mindflow.js @@ -0,0 +1,56 @@ +const mysql = require("mysql"); + +const config = require("../config"); + +let connection = mysql.createConnection(config.dwelling.database); + +// This code was taken from https://www.npmjs.com/package/mysql#custom-format +// Only fourth line was changed. +connection.config.queryFormat = function (query, values) { + if (!values) return query; + return query.replace(/\:(\w+)/g, function (txt, key) { + if (values[key] !== undefined) + return this.escape(values[key]); + return txt; + }.bind(this)); +}; + +exports.closeConnection = () => connection.end(); + +exports.getPosts = category => { + return new Promise((resolve, reject) => { + let query = "\ + SELECT\ + `blog`.`entry_id` AS `post_id`,\ + `blog`.`date`,\ + `blog`.`title`,\ + `blog`.`body`\ + FROM `blog`\ + LEFT JOIN `blog_categories` ON `blog_categories`.`category_id` = `blog`.`category`\ + WHERE `blog_categories`.`category` = :category\ + ORDER BY `blog`.`date` DESC;"; + + connection.query(query, { category: category }, (err, results) => { + if (err) reject(err); + resolve(results); }); + }); +}; + +exports.getPostsForRSS = () => { + return new Promise((resolve, reject) => { + let query = "\ + SELECT\ + `blog`.`entry_id` AS `post_id`,\ + `blog`.`date`,\ + `blog_categories`.`category`,\ + `blog`.`title`,\ + `blog`.`body`\ + FROM `blog`\ + LEFT JOIN `blog_categories` ON `blog_categories`.`category_id` = `blog`.`category`\ + ORDER BY `blog`.`date` DESC;"; + + connection.query(query, (err, results) => { + if (err) reject(err); + resolve(results); }); + }); +}; \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..2fef958 Binary files /dev/null and b/favicon.ico differ diff --git a/files.js b/files.js new file mode 100644 index 0000000..2137a21 --- /dev/null +++ b/files.js @@ -0,0 +1,139 @@ +const fs = require("fs"); +const path = require("path"); + +const Koa = require("koa"); +const koaPug = require("koa-pug"); +const koaRouter = require("koa-router"); +const koaRange = require("koa-range"); +const koaServe = require("koa-static"); +const FileType = require("file-type"); + +const config = require("./config"); +const util = require("./util"); +const { type } = require("os"); +const { ENOENT, ECONNRESET } = require("constants"); + +const sizeUnits = [ "B", "KiB", "MiB", "GiB" ]; + +function makeCurrentPathField(path) { + let current = ""; + if (path.endsWith("/")) + path = path.slice(1, path.length-1); + const path_parts = path.split("/"); + + current = `root`; + + for (i = 0; i < path_parts.length; ++i) { + let lnk = ""; + for (j = 0; j < i+1; ++j) { + lnk += `/${path_parts[j]}`; + } + current += `/${path_parts[i]}`; + } + + return current; +} + +function convertSize(size) { + let i = 0; + for (; size > 1024; size /= 1024) { ++i; } + return [i > 0 ? size.toFixed(2) : size, sizeUnits[i]]; +} + +function fileType(name) { + if (name.endsWith("txt") || name.endsWith("md")) + return "text/plain"; + return "octet-stream"; +} + +async function getDirectoryList(dir_path, base_url) { + let dirs = []; + let files = []; + let d = await fs.promises.opendir(dir_path); + for await (const dirent of d) { + const stat = fs.lstatSync(path.join(dir_path, dirent.name)); + const [s, u] = convertSize(stat.size); + if (stat.isDirectory()) + dirs.push({ + name: dirent.name, + link: `${base_url}${encodeURIComponent(dirent.name)}/`, + datetime: util.datetime(stat.ctime, util.formats.file_date), + size: "DIR", + size_unit: '' }); + else + files.push({ + name: dirent.name, + link: `${base_url}${encodeURIComponent(dirent.name)}`, + datetime: util.datetime(stat.ctime, util.formats.file_date), + size: +s, + size_unit: u }); + } + + const sort_by_name = (a, b) => { + if (a.name > b.name) + return 1; + else if (a.name < b.name) + return -1; + return 0; + } + + dirs.sort(sort_by_name); + files.sort(sort_by_name); + + return dirs.concat(files); +} + +function setRoutes(router) { + router.get('/(.*)?', async (ctx, next) => { + if (ctx.originalUrl.startsWith("/assets") || ctx.originalUrl.startsWith("/shared")) { + await next(); + return; + } + const file_path = path.join(config.files.file_path, decodeURI(ctx.originalUrl)); + let stat = fs.lstatSync(file_path); + if (stat.isDirectory()) { + await ctx.render('index', { + title: "/ Files", + description: "File share.", + host: util.getBaseHost(ctx.header.host), + current_path: makeCurrentPathField(decodeURI(ctx.originalUrl)), + items: await getDirectoryList(file_path, ctx.originalUrl) }); + } else { + let f = await FileType.fromStream(fs.createReadStream(file_path)); + if (f === undefined) + ctx.type = fileType(file_path); + else + ctx.type = f.mime; + ctx.set("Content-Length", stat.size); + + const stream = fs.createReadStream(file_path); + // let ch = 0; + // stream.on("end", () => { }); + // stream.on("data", (chunk) => { ch += chunk.length; }); + ctx.body = stream; + } + }); +} + +module.exports = () => { + const app = new Koa(); + const pug = new koaPug({ + viewPath: path.join(__dirname, "views", "files"), + locals: { + moment: date => util.datetime(date, util.formats.file_date) }, + app: app + }); + + const radio_router = koaRouter(); + setRoutes(radio_router); + + app + .use(koaRange) + .use(radio_router.routes()) + // .use(koaServe(path.join("static", "shared"))) + // .use(koaServe(path.join("static", "files"))) + .on("error", err => { if (!err.code == ECONNRESET || !err.code == ENOENT) console.log(err.code); }) + .listen(config.files.port, config.files.host); + + return app; +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..b21a21f --- /dev/null +++ b/index.js @@ -0,0 +1,9 @@ + +const app_dwelling = require("./dwelling")(); +console.log("Arav's dwelling / Main website is up."); + +const app_radio = require("./radio")(); +console.log("Arav's dwelling / Radio is up."); + +const app_files = require("./files")(); +console.log("Arav's dwelling / Files is up."); \ No newline at end of file diff --git a/install.sql b/install.sql new file mode 100644 index 0000000..9d5ec19 --- /dev/null +++ b/install.sql @@ -0,0 +1,53 @@ +-- Was meant to work on MySQL/MariaDB + + CREATE DATABASE `dwelling` +COLLATE utf8mb4_unicode_ci; + +USE `dwelling`; + + CREATE USER `dweller`@`%` +IDENTIFIED BY 'password'; + +GRANT ALL PRIVILEGES + ON `dwelling`.* + TO `dweller`@`%`; + +CREATE TABLE `blog_categories` ( + `category_id` MEDIUMINT PRIMARY KEY AUTO_INCREMENT, + `category` VARCHAR(40) NOT NULL UNIQUE ); + +INSERT INTO `blog_categories` (`category`) +VALUES ('Update'), ('Diary'); + +CREATE INDEX `blog_categories_ix_category` + ON `blog_categories` (`category`); + +CREATE TABLE `blog` ( + `entry_id` SERIAL PRIMARY KEY, + `date` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + `category` MEDIUMINT NOT NULL, + `title` VARCHAR(255) NOT NULL, + `body` MEDIUMTEXT NOT NULL, + FOREIGN KEY (`category`) REFERENCES `blog_categories` (`category_id`) + ON UPDATE CASCADE ); + +CREATE TABLE IF NOT EXISTS `guestbook` ( + `post_id` SERIAL PRIMARY KEY, + `created` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + `name` VARCHAR(80) NOT NULL, + `email` VARCHAR(255), + `website` VARCHAR(255), + `message` VARCHAR(4096) NOT NULL, + `hide_website` BOOLEAN DEFAULT TRUE NOT NULL, + `hide_email` BOOLEAN DEFAULT TRUE NOT NULL); + +CREATE INDEX IF NOT EXISTS `guestbook_ix_created` + ON `guestbook` (`created`); + +CREATE TABLE IF NOT EXISTS `guestbook_feedback` ( + `post_id` SERIAL PRIMARY KEY, + `created` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + `comment` VARCHAR(4096) NOT NULL, + FOREIGN KEY (`post_id`) REFERENCES `guestbook` (`post_id`) + ON DELETE CASCADE + ON UPDATE CASCADE ); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..847f437 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "arav-dwelling", + "version": "21.6.0", + "description": "Arav's dwelling", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "homepage", + "dwelling", + "Arav" + ], + "author": "Arav", + "license": "GPLv3", + "dependencies": { + "file-type": "^16.2.0", + "koa": "^2.13.1", + "koa-body": "^4.2.0", + "koa-pug": "^4.0.4", + "koa-range": "^0.3.0", + "koa-router": "^10.0.0", + "koa-static": "^5.0.0", + "moment": "^2.29.1", + "moment-timezone": "^0.5.33", + "mysql": "^2.18.1", + "node-fetch": "^2.6.1" + } +} diff --git a/radio.js b/radio.js new file mode 100644 index 0000000..b2c4620 --- /dev/null +++ b/radio.js @@ -0,0 +1,62 @@ +const fs = require("fs"); +const path = require("path"); + +const Koa = require("koa"); +const koaPug = require("koa-pug"); +const koaRouter = require("koa-router"); +const koaServe = require("koa-static"); +const fetch = require("node-fetch"); + +const config = require("./config"); +const util = require("./util"); + +function setRoutes(router) { + router.get('/', async ctx => { + await ctx.render('index', { + title: "/ Radio", + description: "Internet-radio broadcasting from under my desk.", + host: util.getBaseHost(ctx.header.host) + }); + }) + .get('/filelist', async ctx => { + ctx.type = 'text/html'; + ctx.body = fs.readFileSync( + path.join(__dirname, '/static/radio/assets/files/radio_filelist.html')); + }) + .get('/playlist', async ctx => { + ctx.attachment('radio.arav.top.m3u'); + ctx.body = fs.readFileSync( + path.join(__dirname, '/static/radio/assets/files/radio.arav.top.m3u')); + }) + .get('/stats', async ctx => { + let stats = await fetch('http://radio.home/status-json.xsl').then(r => r.json()); + ctx.body = { + server_start_iso8601: stats.icestats.server_start_iso8601, + server_start_date: util.datetime(stats.icestats.server_start_iso8601, util.post_date_format), + song: `${stats.icestats.source.artist} - ${stats.icestats.source.title}`, + listener_peak: stats.icestats.source.listener_peak, + listeners: stats.icestats.source.listeners, + }; + }); +} + +module.exports = () => { + const app = new Koa(); + const pug = new koaPug({ + viewPath: path.join(__dirname, "views", "radio"), + locals: { + moment: util.datetime }, + app: app + }); + + const radio_router = koaRouter(); + setRoutes(radio_router); + + app + .use(koaServe(path.join("static", "shared"))) + .use(koaServe(path.join("static", "radio"))) + .use(radio_router.routes()) + .listen(config.radio.port, config.radio.host); + + return app; +} \ No newline at end of file diff --git a/static/dwelling/assets/css/articles.css b/static/dwelling/assets/css/articles.css new file mode 100644 index 0000000..5f46c37 --- /dev/null +++ b/static/dwelling/assets/css/articles.css @@ -0,0 +1,58 @@ +@font-face { + font-family: 'Share Tech Mono'; + font-style: normal; + font-weight: 400; + src: local('ShareTechMono'), local('ShareTechMono-Regular'), + url(/shared/fonts/ShareTechMono-Regular.ttf) format('truetype'); } + + +h3 { + font-size: 1.1rem; + font-variant: normal; + text-align: left; } + +h4 { + text-indent: 1.5rem; + margin: 1rem 0 1rem 0; } + +code, +pre { + font-family: 'Share Tech Mono'; + font-size: 1.1rem; + letter-spacing: -1px; + overflow-x: auto; + overflow-y: auto; } + +code { margin: 0 .2rem; } + +pre { + border-left: 1px solid var(--primary-color); + padding-left: .25rem; + margin-bottom: .5rem; + margin-left: 1.6rem; + white-space: pre; } + +article { + margin-top: 1rem; + max-width: 100%; } + +article:last-child { margin-bottom: 1rem; } + +article header { + display: flex; + flex-direction: column; } + +article header .menu { + display: flex; + font-size: .8rem; + justify-content: space-between; } + +article header nav a { font-variant: normal; } + +article header nav ol { + counter-reset: item; + list-style-type: none; } + +article header nav ol > li { counter-increment: item; } + +article header nav ol > li:before { content: counters(item, '.') '. '; } \ No newline at end of file diff --git a/static/dwelling/assets/css/guestbook.css b/static/dwelling/assets/css/guestbook.css new file mode 100644 index 0000000..750b6eb --- /dev/null +++ b/static/dwelling/assets/css/guestbook.css @@ -0,0 +1,66 @@ +::placeholder { color: var(--primary-color); } + +#new-post { + display: grid; + gap: .5rem; + grid-template-areas: + "n e w" + "m m m" + "c c s"; + grid-template-columns: 1fr 1fr 1fr; } + +#new-post input[name="name"] { grid-area: n; } + +#new-post input[name="email"] { grid-area: e; } + +#new-post input[name="website"] { grid-area: w; } + +#new-post span.checkboxes { grid-area: c; } + +#new-post textarea { + grid-area: m; + height: 5.5rem; + max-width: 100%; + min-width: 100%; } + +#new-post input[type="submit"] { + background-color: var(--primary-color); + border: none; + color: #f5f5f5; + font: inherit; + grid-area: s; + height: 2rem; } + +#new-post input[type="submit"]:hover { background-color: var(--secondary-color); } + +#new-post span.checkbox:not(:last-child) { margin-right: 2rem; } + +#new-post input[type="text"], +#new-post textarea { + background-color: var(--background-color); + border: none; + border-bottom: 1px solid var(--primary-color); + color: var(--text-color); + font: inherit; } + +#posts article:not(:last-child) { margin-bottom: 1rem; } + +#posts article header { display: inline; font-size: .85rem; } + +#posts article > *, +#posts article div.feedback > *, +#posts article div.feedback { margin-left: .5rem; } + +#pagination a:not(:first-child) { margin-left: .5rem; } + +@media screen and (max-width: 641px) { + #new-post { + gap: 1.5rem; + grid-template-areas: + "n" + "e" + "w" + "m" + "c" + "s"; + grid-template-columns: 1fr; } } \ No newline at end of file diff --git a/static/dwelling/assets/css/guestbook.js.css b/static/dwelling/assets/css/guestbook.js.css new file mode 100644 index 0000000..7ac7e57 --- /dev/null +++ b/static/dwelling/assets/css/guestbook.js.css @@ -0,0 +1,73 @@ +::placeholder { + color: #a5a5a5; + font-family: 'Roboto Condensed', Roboto, sans-serif; + font-size: 1.1rem; } + +#new-post { + display: grid; + gap: .5rem; + grid-template-areas: + "n e w" + "m m m" + "c c b"; + grid-template-columns: 1fr 1fr 1fr; +} + +#new-post input[name="name"] { grid-area: n; } + +#new-post input[name="email"] { grid-area: e; } + +#new-post input[name="website"] { grid-area: w; } + +#new-post .chkgrp { grid-area: c; } + +#new-post textarea { + grid-area: m; + height: 5.5rem; + max-width: 100%; + min-width: 100%; } + +#new-post input[type="submit"] { + background-color: var(--primary-color); + border: none; + color: #f5f5f5; + grid-area: b; + height: 2rem; } + +#new-post span .checkbox:not(:last-child) { margin-right: 2rem; } + +#new-post input[type="text"], +#new-post textarea, +#posts-pagination input#current-page, +#posts-pagination button { + background-color: var(--background-color); + border: none; + border-bottom: 1px solid var(--primary-color); + color: var(--text-color); + font-family: 'Roboto Condensed', Roboto, sans-serif; + font-size: 1.1rem; } + +#posts article:not(:last-child) { margin-bottom: 1rem; } + +#posts article header { display: inline; font-size: .8rem; } + +#posts article main, +#posts article div.feedback main, +#posts article div.feedback { margin-left: .5rem; } + +#posts-pagination input#current-page { width: 1.7rem; } + +#posts-pagination button { border: none; } + +@media screen and (max-width: 641px) { + #new-post { + gap: 1.5rem; + grid-template-areas: + "n" + "e" + "w" + "m" + "c" + "b"; + grid-template-columns: 1fr; } +} \ No newline at end of file diff --git a/static/dwelling/assets/css/index.css b/static/dwelling/assets/css/index.css new file mode 100644 index 0000000..9652d6e --- /dev/null +++ b/static/dwelling/assets/css/index.css @@ -0,0 +1,17 @@ +body { margin-top: 25vh; } + +header { position: relative; } + +#logo { width: 100%; } + +nav a:last-child { + margin-left: .6rem; } + +nav, +#services { + text-align: center; + width: 100%; } + +#services a, +#services span { margin: .3rem; } +#services span a { margin: 0; } \ No newline at end of file diff --git a/static/dwelling/assets/css/main.css b/static/dwelling/assets/css/main.css new file mode 100644 index 0000000..05ad62c --- /dev/null +++ b/static/dwelling/assets/css/main.css @@ -0,0 +1,165 @@ +@font-face { + font-family: 'Roboto Condensed'; + font-style: normal; + font-weight: 400; + src: local('RobotoCondensed'), local('RobotoCondensed-Regular'), + url(/shared/fonts/RobotoCondensed-Regular.ttf); } + +:root { + --background-color: #0a0a0a; + --primary-color: #cd2682; + --secondary-color: #9f2b68; + --text-color: #f5f5f5; + --text-indent: 1.6rem; + scrollbar-color: var(--primary-color) var(--background-color); } + +@media (prefers-color-scheme: light) { + :root { + --background-color: #f5f5f5; + --primary-color: #9f2b68; + --secondary-color: #cd2682; + --text-color: #0a0a0a; } } + +* { margin: 0; } + +::selection { + background-color: var(--secondary-color); + color: var(--background-color); } + +a { + color: var(--primary-color); + text-decoration: none; } + +a:hover { + color: var(--secondary-color); + text-decoration: underline; + text-decoration-style: dotted; + transition: .5s; } + +p { + text-align: justify; + line-height: var(--text-indent); + text-indent: var(--text-indent); } + +p:not(:last-child) { margin-bottom: .1rem; } + +p.center { + text-align: center; + text-indent: 0px; } + +h1 { + font-size: 1.8rem; + text-align: center; + color: var(--secondary-color); + margin: 0; } + +h1, +h2 { font-variant: small-caps; } + +h2, +h3 { + text-align: center; + margin: 1rem; } + +h2 { font-size: 1.4rem; } + +h3 { font-size: 1.05rem; } + +small, +figure figcaption { font-size: .8rem; } + +figure img { width: 100%; } + +figure figcaption { text-align: center; } + +table { + border-collapse: collapse; + margin-bottom: .5rem; } + +tr th, +tr td { + text-align: left; + vertical-align: top; } + +tr td:not(:last-child), +tr th:not(:last-child) { padding-right: .5rem; } + +#banners figure img { width: auto; } + +.highlighted { color: var(--primary-color); } + +.grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-gap: 1rem; } + +.grid h3 { color: var(--primary-color); } + +.grid.figs { grid-template-columns: 1fr 1fr; } + +.grid.banners { + display: flex; + flex-wrap: wrap; + justify-content: center; } + +html { + margin-left: calc(100vw - 100%); + margin-right: 0; } + +body { + background-color: var(--background-color); + color: var(--text-color); + font-family: 'Roboto Condensed', Roboto, sans-serif; + font-size: 1.1rem; + margin: 0 auto; + max-width: 960px; + width: 98%; } + +header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; } + +#logo { + display: block; + width: 360px; } + +#logo text { fill: var(--text-color); } + +#logo .logo { + font-size: 2rem; + font-variant-caps: small-caps; + font-weight: bold; } + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #logo .logo { font-size: 2.082rem; } } + +@-moz-document url-prefix() { + #logo .logo { font-size: 2rem; } } + +#logo .under { font-size: .88rem; } + +nav { margin-top: .5rem; } + +nav a { font-variant: small-caps; } + +nav a:not(:first-child) { margin-left: .6rem; } + +section { + margin-top: 1rem; + max-width: 100%; } + +footer { + font-size: .8rem; + text-align: center; + padding: 1rem 0; } + +@media screen and (max-width: 640px) { + body > header { display: block; } + + #logo { width: 100%; } + + body > header nav { text-align: center; } + + .grid, + .grid.figs { grid-template-columns: 1fr; } } \ No newline at end of file diff --git a/static/dwelling/assets/css/mindflow.css b/static/dwelling/assets/css/mindflow.css new file mode 100644 index 0000000..3b21305 --- /dev/null +++ b/static/dwelling/assets/css/mindflow.css @@ -0,0 +1,5 @@ +article header a { color: var(--text-color); } + +article footer { + text-align: right; + padding: 0; } \ No newline at end of file diff --git a/static/dwelling/assets/fonts/RobotoCondensed-Regular.ttf b/static/dwelling/assets/fonts/RobotoCondensed-Regular.ttf new file mode 100644 index 0000000..9a1418d Binary files /dev/null and b/static/dwelling/assets/fonts/RobotoCondensed-Regular.ttf differ diff --git a/static/dwelling/assets/fonts/ShareTechMono-Regular.ttf b/static/dwelling/assets/fonts/ShareTechMono-Regular.ttf new file mode 100644 index 0000000..c8e530f Binary files /dev/null and b/static/dwelling/assets/fonts/ShareTechMono-Regular.ttf differ diff --git a/static/dwelling/assets/img/acer.webp b/static/dwelling/assets/img/acer.webp new file mode 100644 index 0000000..e83f1f2 Binary files /dev/null and b/static/dwelling/assets/img/acer.webp differ diff --git a/static/dwelling/assets/img/acer_thumb.webp b/static/dwelling/assets/img/acer_thumb.webp new file mode 100644 index 0000000..5702c16 Binary files /dev/null and b/static/dwelling/assets/img/acer_thumb.webp differ diff --git a/static/dwelling/assets/img/asd_logo_.svg b/static/dwelling/assets/img/asd_logo_.svg new file mode 100644 index 0000000..a1c6fcf --- /dev/null +++ b/static/dwelling/assets/img/asd_logo_.svg @@ -0,0 +1,56 @@ + + + + + + + image/svg+xml + + + + + + + ARAV'S DWELLING + Welcome to my sacred place, wanderer + + diff --git a/static/dwelling/assets/img/banner_240x60.gif b/static/dwelling/assets/img/banner_240x60.gif new file mode 100644 index 0000000..86420f8 Binary files /dev/null and b/static/dwelling/assets/img/banner_240x60.gif differ diff --git a/static/dwelling/assets/img/banner_240x60.webp b/static/dwelling/assets/img/banner_240x60.webp new file mode 100644 index 0000000..d407725 Binary files /dev/null and b/static/dwelling/assets/img/banner_240x60.webp differ diff --git a/static/dwelling/assets/img/banner_88x31.gif b/static/dwelling/assets/img/banner_88x31.gif new file mode 100644 index 0000000..31bd091 Binary files /dev/null and b/static/dwelling/assets/img/banner_88x31.gif differ diff --git a/static/dwelling/assets/img/banner_88x31.webp b/static/dwelling/assets/img/banner_88x31.webp new file mode 100644 index 0000000..5dc00ce Binary files /dev/null and b/static/dwelling/assets/img/banner_88x31.webp differ diff --git a/static/dwelling/assets/img/banner_dark_240x60.gif b/static/dwelling/assets/img/banner_dark_240x60.gif new file mode 100644 index 0000000..4e7f9d6 Binary files /dev/null and b/static/dwelling/assets/img/banner_dark_240x60.gif differ diff --git a/static/dwelling/assets/img/banner_dark_88x31.gif b/static/dwelling/assets/img/banner_dark_88x31.gif new file mode 100644 index 0000000..7211523 Binary files /dev/null and b/static/dwelling/assets/img/banner_dark_88x31.gif differ diff --git a/static/dwelling/assets/img/favicon.ico b/static/dwelling/assets/img/favicon.ico new file mode 100644 index 0000000..2fef958 Binary files /dev/null and b/static/dwelling/assets/img/favicon.ico differ diff --git a/static/dwelling/assets/img/favicon_128.png b/static/dwelling/assets/img/favicon_128.png new file mode 100644 index 0000000..c7d066e Binary files /dev/null and b/static/dwelling/assets/img/favicon_128.png differ diff --git a/static/dwelling/assets/img/padoru.png b/static/dwelling/assets/img/padoru.png new file mode 100644 index 0000000..43abd6f Binary files /dev/null and b/static/dwelling/assets/img/padoru.png differ diff --git a/static/dwelling/assets/img/padoru.webp b/static/dwelling/assets/img/padoru.webp new file mode 100644 index 0000000..773633b Binary files /dev/null and b/static/dwelling/assets/img/padoru.webp differ diff --git a/static/dwelling/assets/img/pic01.webp b/static/dwelling/assets/img/pic01.webp new file mode 100644 index 0000000..11d7b8a Binary files /dev/null and b/static/dwelling/assets/img/pic01.webp differ diff --git a/static/dwelling/assets/img/pic02.webp b/static/dwelling/assets/img/pic02.webp new file mode 100644 index 0000000..aafed92 Binary files /dev/null and b/static/dwelling/assets/img/pic02.webp differ diff --git a/static/dwelling/assets/img/raspi.webp b/static/dwelling/assets/img/raspi.webp new file mode 100644 index 0000000..6b58b67 Binary files /dev/null and b/static/dwelling/assets/img/raspi.webp differ diff --git a/static/dwelling/assets/img/raspi_thumb.webp b/static/dwelling/assets/img/raspi_thumb.webp new file mode 100644 index 0000000..fe6fa33 Binary files /dev/null and b/static/dwelling/assets/img/raspi_thumb.webp differ diff --git a/static/dwelling/assets/img/reimu.01.webp b/static/dwelling/assets/img/reimu.01.webp new file mode 100644 index 0000000..df09c54 Binary files /dev/null and b/static/dwelling/assets/img/reimu.01.webp differ diff --git a/static/dwelling/assets/img/teamspeak3_banner.png b/static/dwelling/assets/img/teamspeak3_banner.png new file mode 100644 index 0000000..5a6764d Binary files /dev/null and b/static/dwelling/assets/img/teamspeak3_banner.png differ diff --git a/static/dwelling/assets/img/ts3_banner_960.png b/static/dwelling/assets/img/ts3_banner_960.png new file mode 100644 index 0000000..51a1355 Binary files /dev/null and b/static/dwelling/assets/img/ts3_banner_960.png differ diff --git a/static/dwelling/assets/img/xmpp_compliance.svg b/static/dwelling/assets/img/xmpp_compliance.svg new file mode 100644 index 0000000..faa05da --- /dev/null +++ b/static/dwelling/assets/img/xmpp_compliance.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + XMPP + Specifications compliance + + XMPP Specifications compliance + + + + + 80% (17/21) + + + 80% (17/21) + + + + diff --git a/static/dwelling/assets/img/xmpp_score.svg b/static/dwelling/assets/img/xmpp_score.svg new file mode 100644 index 0000000..6003c50 --- /dev/null +++ b/static/dwelling/assets/img/xmpp_score.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + xmpp.net score + xmpp.net score + c2s: A + c2s: A + + + s2s: A + s2s: A + + + \ No newline at end of file diff --git a/static/files/assets/css/main.css b/static/files/assets/css/main.css new file mode 100644 index 0000000..849de94 --- /dev/null +++ b/static/files/assets/css/main.css @@ -0,0 +1,132 @@ +@font-face { + font-family: 'Roboto Condensed'; + font-style: normal; + font-weight: 400; + src: local('RobotoCondensed'), local('RobotoCondensed-Regular'), + url(/shared/assets/fonts/RobotoCondensed-Regular.ttf); } + +:root { + --background-color: #0a0a0a; + --primary-color: #cd2682; + --secondary-color: #9f2b68; + --text-color: #f5f5f5; + --text-indent: 1.6rem; + scrollbar-color: var(--primary-color) var(--background-color); } + +@media (prefers-color-scheme: light) { + :root { + --background-color: #f5f5f5; + --primary-color: #9f2b68; + --secondary-color: #cd2682; + --text-color: #0a0a0a; } } + +* { margin: 0; } + +::selection { + background-color: var(--secondary-color); + color: var(--background-color); } + +a { + color: var(--primary-color); + text-decoration: none; } + +a:hover { + color: var(--secondary-color); + text-decoration: underline; + text-decoration-style: dotted; + transition: .5s; } + +p { + text-align: justify; + line-height: var(--text-indent); + text-indent: var(--text-indent); } + +p:not(:last-child) { margin-bottom: .1rem; } + +h1, +h2 { + font-size: 1.8rem; + font-variant: small-caps; + text-align: center; + margin-bottom: 1rem; } + +h2 { + font-size: 1.4rem; + margin: 1rem 0; } + +html { margin-left: calc(100vw - 100%); } + +body { + background-color: var(--background-color); + color: var(--text-color); + font-family: 'Roboto Condensed', Roboto, sans-serif; + font-size: 1.1rem; + margin: 0 auto; + max-width: 960px; + width: 98%; } + +header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; } + +#logo { + display: block; + width: 360px; } + +#logo text { fill: var(--text-color); } + +#logo .logo { + font-size: 2rem; + font-variant-caps: small-caps; + font-weight: bold; } + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #logo .logo { font-size: 2.082rem; } } + +@-moz-document url-prefix() { + #logo .logo { font-size: 2rem; } } + +#logo .under { font-size: .88rem; } + +nav { margin-top: .5rem; } + +nav a { font-variant: small-caps; } + +nav h1 { + color: var(--secondary-color); + margin: 0; } + +section { margin-top: 1rem; } + +#files table { overflow-y: scroll; width: 100%; } + +#files table th { text-align: left; } + +#files table th, +#files table td { line-break: strict; } + +#files table th:nth-child(2), +#files table th:last-child { width: 1%; white-space: nowrap; } + +#files table td:nth-child(2), +#files table td:last-child { white-space: nowrap; } + +#files table th:nth-child(2), +#files table td:nth-child(2) { padding: 0 1rem; } + +footer { + font-size: .8rem; + text-align: center; + padding: 1rem 0; } + +@media screen and (max-width: 640px) { + header { display: block; } + + #logo { + margin: 0 auto; + width: 100%; } + + nav { + width: 100%; + text-align: center; } } \ No newline at end of file diff --git a/static/radio/assets/css/main.css b/static/radio/assets/css/main.css new file mode 100644 index 0000000..0915256 --- /dev/null +++ b/static/radio/assets/css/main.css @@ -0,0 +1,134 @@ +@font-face { + font-family: 'Roboto Condensed'; + font-style: normal; + font-weight: 400; + src: local('RobotoCondensed'), local('RobotoCondensed-Regular'), + url(/shared/assets/fonts/RobotoCondensed-Regular.ttf); } + +:root { + --background-color: #0a0a0a; + --primary-color: #cd2682; + --secondary-color: #9f2b68; + --text-color: #f5f5f5; + --text-indent: 1.6rem; + scrollbar-color: var(--primary-color) var(--background-color); } + +@media (prefers-color-scheme: light) { + :root { + --background-color: #f5f5f5; + --primary-color: #9f2b68; + --secondary-color: #cd2682; + --text-color: #0a0a0a; } } + +* { margin: 0; } + +::selection { + background-color: var(--secondary-color); + color: var(--background-color); } + +a { + color: var(--primary-color); + text-decoration: none; } + +a:hover { + color: var(--secondary-color); + text-decoration: underline; + text-decoration-style: dotted; + transition: .5s; } + +p { + text-align: justify; + line-height: var(--text-indent); + text-indent: var(--text-indent); } + +p:not(:last-child) { margin-bottom: .1rem; } + +h1, +h2 { + font-size: 1.8rem; + font-variant: small-caps; + text-align: center; + margin-bottom: 1rem; } + +h2 { + font-size: 1.4rem; + margin: 1rem 0; } + +small { font-size: .8rem; } + +small.player-links a { margin: 0 .2rem; } + +audio { + background-color: var(--primary-color); + box-shadow: 5px 5px var(--primary-color); + width: 100%; } + +@media screen and (-webkit-min-device-pixel-ratio:0) { + audio::-webkit-media-controls-panel { + background-color: var(--secondary-color); } + + audio { border-radius: 1.6rem; } } + +@-moz-document url-prefix() { + audio { border-radius: 0; } } + +html { margin-left: calc(100vw - 100%); } + +body { + background-color: var(--background-color); + color: var(--text-color); + font-family: 'Roboto Condensed', Roboto, sans-serif; + font-size: 1.1rem; + margin: 0 auto; + max-width: 960px; + width: 98%; } + +header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; } + +#logo { + display: block; + width: 360px; } + +#logo text { fill: var(--text-color); } + +#logo .logo { + font-size: 2rem; + font-variant-caps: small-caps; + font-weight: bold; } + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #logo .logo { font-size: 2.082rem; } } + +@-moz-document url-prefix() { + #logo .logo { font-size: 2rem; } } + +#logo .under { font-size: .88rem; } + +nav { margin-top: .5rem; } + +nav a { font-variant: small-caps; } + +nav h1 { + color: var(--secondary-color); + margin: 0; } + +section { margin-top: 1rem; } + +footer { + font-size: .8rem; + text-align: center; + padding: 1rem 0; } + +@media screen and (max-width: 640px) { + header { display: block; } + + #logo { + margin: 0 auto; + width: 100%; } + + nav { + width: 100%; + text-align: center; } } \ No newline at end of file diff --git a/static/radio/assets/files/radio.arav.top.m3u b/static/radio/assets/files/radio.arav.top.m3u new file mode 100644 index 0000000..3feaecc --- /dev/null +++ b/static/radio/assets/files/radio.arav.top.m3u @@ -0,0 +1,9 @@ +#EXTM3U +#EXTINF:-1,Arav's dwelling / Radio +http://radio.arav.top:8000/stream.ogg +#EXTINF:-1,Arav's dwelling / Radio (HTTPS) +https://radio.arav.top/live/stream.ogg +#EXTINF:-1,Arav's dwelling / Radio on Tor +http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion/radio/live/stream.ogg +#EXTINF:-1,Arav's dwelling / Radio on I2P +http://t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p/radio/live/stream.ogg \ No newline at end of file diff --git a/static/radio/assets/files/radio_filelist.html b/static/radio/assets/files/radio_filelist.html new file mode 100644 index 0000000..9aff83c --- /dev/null +++ b/static/radio/assets/files/radio_filelist.html @@ -0,0 +1,874 @@ + + + + + + + Arav's dwelling / Radio + + + +

Arav's dwelling / Radio

+
+ ├── [397M]  Burzum
+ │   ├── [ 17M]  1992
+ │   │   ├── [ 11M]  02. Ea, Lord Of The Depths.mp3
+ │   │   └── [5.7M]  05. War.mp3
+ │   ├── [ 16M]  1996
+ │   │   └── [ 16M]  01. Burzum.mp3
+ │   ├── [ 15M]  1999
+ │   │   └── [ 15M]  02. Der Tod Wuotans.mp3
+ │   ├── [ 96M]  2010
+ │   │   ├── [ 15M]  02. Belus’ Doed.mp3
+ │   │   ├── [ 27M]  03. Glemselens Elv.mp3
+ │   │   ├── [ 15M]  04. Kaimadalthas Nedstigning.mp3
+ │   │   ├── [5.6M]  05. Sverddans.mp3
+ │   │   ├── [ 13M]  06. Keliohesten.mp3
+ │   │   └── [ 20M]  07. Morgenroede.mp3
+ │   ├── [162M]  2011
+ │   │   ├── [ 18M]  02. Feeble Screams From Forests Unknown.mp3
+ │   │   ├── [ 18M]  02. Jeg Faller.mp3
+ │   │   ├── [ 21M]  03. Valen.mp3
+ │   │   ├── [ 12M]  04. Ea, Lord Of The Depths.mp3
+ │   │   ├── [ 16M]  04. Vanvidd.mp3
+ │   │   ├── [ 14M]  05. Enhver Til Sitt.mp3
+ │   │   ├── [ 26M]  06. A Lost Forgotten Sad Spirit.mp3
+ │   │   ├── [ 23M]  06. Budstikken.mp3
+ │   │   └── [ 12M]  09. Key To The Gate.mp3
+ │   └── [ 90M]  2012
+ │       ├── [ 13M]  02. Joln.mp3
+ │       ├── [ 21M]  03. Alfadanz.mp3
+ │       ├── [ 16M]  04. Hit Helga Tre.mp3
+ │       ├── [ 17M]  08. Galgvidr.mp3
+ │       └── [ 23M]  10. Gullaldr.mp3
+ ├── [398M]  Depeche Mode
+ │   ├── [ 11M]  1982 - A Broken Frame
+ │   │   └── [ 11M]  01. Leave In Silence.mp3
+ │   ├── [ 19M]  1986 - Black Celebration
+ │   │   ├── [9.6M]  06. A Question Of Time.mp3
+ │   │   └── [9.9M]  07. Stripped.mp3
+ │   ├── [ 21M]  1987 - Music For The Masses
+ │   │   ├── [ 11M]  03. Strangelove.mp3
+ │   │   └── [9.9M]  05. Little 15.mp3
+ │   ├── [ 26M]  1990 - Violator
+ │   │   ├── [ 11M]  03. Personal Jesus.mp3
+ │   │   └── [ 14M]  06. Enjoy The Silence.mp3
+ │   ├── [ 13M]  1993 - Songs Of Faith And Devotion
+ │   │   └── [ 12M]  02. Walking In My Shoes.mp3
+ │   ├── [ 28M]  1997 - Ultra
+ │   │   ├── [ 13M]  01. Barrel Of A Gun.mp3
+ │   │   └── [ 15M]  09. Freestate.mp3
+ │   ├── [ 21M]  2001 - Exciter
+ │   │   ├── [10.0M]  01. Dream On.mp3
+ │   │   └── [ 11M]  05. The Dead Of Night.mp3
+ │   ├── [ 55M]  2005 - Playing The Angel
+ │   │   ├── [9.6M]  01. A Pain That I'm Used To.mp3
+ │   │   ├── [8.6M]  02. John The Revelator.mp3
+ │   │   ├── [8.8M]  03. Suffer Well.mp3
+ │   │   ├── [9.6M]  05. Precious.mp3
+ │   │   ├── [ 10M]  08. Nothing's Impossible.mp3
+ │   │   └── [8.1M]  10. Damaged People.mp3
+ │   ├── [ 60M]  2009 - Sounds Of The Universe
+ │   │   ├── [ 16M]  01. In Chains.mp3
+ │   │   ├── [9.1M]  02. Hole To Feed.mp3
+ │   │   ├── [7.4M]  03. Wrong.mp3
+ │   │   ├── [9.6M]  04. Fragile Tension.mp3
+ │   │   ├── [8.1M]  05. Little Soul.mp3
+ │   │   └── [ 10M]  07. Peace.mp3
+ │   ├── [ 39M]  2017 - Spirit
+ │   │   ├── [ 11M]  02. Where's The Revolution.mp3
+ │   │   ├── [8.8M]  03. The Worst Crime.mp3
+ │   │   ├── [7.5M]  04. Scum.mp3
+ │   │   └── [ 12M]  12. Fail.mp3
+ │   ├── [ 37M]  2020 - D 1993 - 2005
+ │   │   ├── [ 12M]  04. Slowblow (Single Version).mp3
+ │   │   ├── [ 17M]  05. Painkiller (Single Version).mp3
+ │   │   └── [7.8M]  15. Martyr (Single Version).mp3
+ │   ├── [ 25M]  2020 - E 2006 - 2017
+ │   │   ├── [ 14M]  02. Oh Well.mp3
+ │   │   └── [ 11M]  03. Light.mp3
+ │   └── [ 42M]  2020 - O 1986 - 1990
+ │       ├── [9.6M]  09. Route 66 (Single Version).mp3
+ │       ├── [ 13M]  11. Sonata No 14 in C#m (Moonlight Sonata).mp3
+ │       ├── [ 10M]  12. Dangerous (Single Version).mp3
+ │       └── [9.5M]  13. Memphisto.mp3
+ ├── [ 51M]  Equinoxious
+ │   └── [ 51M]  2014 - Cosmódromo (L.P)
+ │       ├── [3.2M]  01 - Венера-11.mp3
+ │       ├── [4.5M]  02 - Astrónomo Insumiso.mp3
+ │       ├── [4.7M]  03 - Búnker.mp3
+ │       ├── [4.2M]  04 - El satélite del ejército negro.mp3
+ │       ├── [4.5M]  05 - Cosmódromo (Feat. Kriistal Ann).mp3
+ │       ├── [3.7M]  06 - Control Voltage.mp3
+ │       ├── [3.9M]  07 - Flesh.mp3
+ │       ├── [3.9M]  08 - Arena.mp3
+ │       ├── [3.2M]  09 - Próxima Centauri.mp3
+ │       ├── [3.9M]  10 - Secrets.mp3
+ │       ├── [3.0M]  11 - Escape pod.mp3
+ │       ├── [4.0M]  12 - A-26.mp3
+ │       └── [4.6M]  13 - Ядерный реактор (Bonus Track).mp3
+ ├── [ 89M]  Escalator
+ │   ├── [9.3M]  01. Számítógépes Lovagok.mp3
+ │   ├── [ 13M]  02. Rew Stop Play.mp3
+ │   ├── [9.0M]  03. Everybody's Lying.mp3
+ │   ├── [ 11M]  03. Out Of My Ego.mp3
+ │   ├── [7.6M]  05. Sohase Mondd.mp3
+ │   ├── [ 12M]  07. Offenzíva.mp3
+ │   ├── [8.5M]  09. Amnezia (Vegetatív Mix).mp3
+ │   ├── [7.2M]  10. Időzített Bomba.mp3
+ │   └── [ 12M]  11. Hívom A Múltat.mp3
+ ├── [ 54M]  Hacking The Wave
+ │   ├── [9.1M]  02. Hacking The Wave.mp3
+ │   ├── [ 16M]  04. Illusions.mp3
+ │   ├── [ 11M]  05. The Dome.mp3
+ │   ├── [7.5M]  08. Inevitable.mp3
+ │   └── [ 11M]  09. Faith.mp3
+ ├── [392M]  Korpiklaani
+ │   ├── [ 88M]  2003
+ │   │   ├── [8.5M]  01. Wooden Pints.mp3
+ │   │   ├── [ 10M]  02. Before The Morning Sun.mp3
+ │   │   ├── [7.5M]  03. God Of Wind.mp3
+ │   │   ├── [ 19M]  04. With Trees.mp3
+ │   │   ├── [5.2M]  06. You Looked Into My Eyes.mp3
+ │   │   ├── [5.5M]  08. Man Can Go Even Through The Grey Stone.mp3
+ │   │   ├── [5.4M]  09. Pixies Dance.mp3
+ │   │   ├── [3.0M]  10. Juokse Sinä Humma (Keep on Running, You My Horse).mp3
+ │   │   ├── [ 12M]  11. Hengettömiltä Hengiltä (From the Dead People's Spirit).mp3
+ │   │   └── [ 11M]  13. Shaman Drum.mp3
+ │   ├── [ 41M]  2005
+ │   │   ├── [7.5M]  01. Cottages & Saunas.mp3
+ │   │   ├── [5.9M]  02. Journey Man.mp3
+ │   │   ├── [ 10M]  05. Spirit of the Forest.mp3
+ │   │   ├── [ 10M]  06. Native Land.mp3
+ │   │   └── [7.0M]  07. Hunting Song.mp3
+ │   ├── [ 54M]  2006
+ │   │   ├── [8.2M]  01. Happy Little Boozer.mp3
+ │   │   ├── [8.6M]  02. Väkirauta.mp3
+ │   │   ├── [8.0M]  03. Midsummer Night.mp3
+ │   │   ├── [7.1M]  05. Spring Dance.mp3
+ │   │   ├── [9.7M]  06. Under The Sun.mp3
+ │   │   └── [ 12M]  08. Rise.mp3
+ │   ├── [ 45M]  2007
+ │   │   ├── [9.0M]  02. Tervaskanto.mp3
+ │   │   ├── [8.2M]  03. Viima.mp3
+ │   │   ├── [9.0M]  05. Running With the Wolves.mp3
+ │   │   ├── [6.8M]  06. Liekkiön Isku.mp3
+ │   │   └── [ 12M]  07. Palovana.mp3
+ │   ├── [ 54M]  2008
+ │   │   ├── [9.7M]  01. Tapporauta.mp3
+ │   │   ├── [6.9M]  02. Metsämies.mp3
+ │   │   ├── [9.5M]  03. Keep On Galloping.mp3
+ │   │   ├── [8.6M]  06. Paljon on Koskessa Kiviä.mp3
+ │   │   ├── [8.9M]  11. Kipumylly.mp3
+ │   │   └── [ 10M]  12. Suden Joiku.mp3
+ │   ├── [ 22M]  2009
+ │   │   ├── [6.8M]  02. Erämaan Ärjyt.mp3
+ │   │   └── [ 15M]  04. Mettänpeiton Valtiaalle.mp3
+ │   ├── [ 22M]  2011
+ │   │   ├── [7.7M]  02. Paat pois tai hirteen.mp3
+ │   │   └── [ 15M]  10. Surma.mp3
+ │   ├── [ 19M]  2012
+ │   │   ├── [7.2M]  03 - Rauta.mp3
+ │   │   ├── [7.3M]  07 - Ievan Polkka.mp3
+ │   │   └── [4.2M]  08 - Husky-Sledge.mp3
+ │   └── [ 48M]  2015
+ │       ├── [ 13M]  03 - Lempo.mp3
+ │       ├── [8.0M]  04 - Sahti.mp3
+ │       ├── [ 15M]  10 - Sen Verran Minäkin Noita.mp3
+ │       └── [ 12M]  11 - Antaja [Bonus track].mp3
+ ├── [464M]  Lacrimosa
+ │   ├── [ 13M]  01. Wenn Unsere Helden Sterben.mp3
+ │   ├── [9.8M]  02. Alleine Zu Zweit.mp3
+ │   ├── [ 10M]  02. Der Morgen Danach.mp3
+ │   ├── [ 14M]  02. Kaleidoskop.mp3
+ │   ├── [ 14M]  03. Durch Nacht & Flut.mp3
+ │   ├── [ 12M]  03. Einsamkeit.mp3
+ │   ├── [9.1M]  03. Halt Mich.mp3
+ │   ├── [ 12M]  03. Lichtgestalt.mp3
+ │   ├── [8.8M]  03. Unterwelt.mp3
+ │   ├── [ 10M]  04. Feuer.mp3
+ │   ├── [ 16M]  04. Nachtschatten.mp3
+ │   ├── [ 18M]  04. Weltenbrand.mp3
+ │   ├── [ 12M]  05. A Prayer For Your Heart.mp3
+ │   ├── [7.8M]  05. Der Kelch Der Hoffnung.mp3
+ │   ├── [ 17M]  05. Lass Die Nacht Nicht Über Mich Fallen.mp3
+ │   ├── [ 19M]  05. My Last Goodbye.mp3
+ │   ├── [ 12M]  06. Bresso.mp3
+ │   ├── [ 13M]  06. I Lost My Star In Krasnodar [Special Version].mp3
+ │   ├── [9.0M]  06. Thunder And Lightning.mp3
+ │   ├── [ 11M]  07. Malina [Bittruf - Part II].mp3
+ │   ├── [ 14M]  08. Der Freie Fall - Apeiron Part I .mp3
+ │   ├── [9.9M]  08. My Pain.mp3
+ │   ├── [ 11M]  09. Der Leise Tod.mp3
+ │   ├── [5.7M]  09. Keine Schatten Mehr.mp3
+ │   ├── [ 21M]  10. Apeiron - Der Freie Fall Part II .mp3
+ │   ├── [ 18M]  10. Koma.mp3
+ │   └── [136M]  2012 - Revolution [Limited Digipack Edition]
+ │       ├── [ 12M]  01. Irgendein Arsch Ist Immer Unterwegs.mp3
+ │       ├── [8.2M]  02. If The World Stood Still A Day.mp3
+ │       ├── [ 17M]  03. Verloren.mp3
+ │       ├── [ 12M]  04. This Is The Night.mp3
+ │       ├── [1.8M]  05. Interlude - Feuerzug [Part I].mp3
+ │       ├── [ 11M]  06. Feuerzug [Part II].mp3
+ │       ├── [ 11M]  07. Refugium.mp3
+ │       ├── [ 14M]  08. Weil Du Hilfe Brauchst.mp3
+ │       ├── [ 25M]  09. Rote Sinfonie.mp3
+ │       ├── [ 12M]  10. Revolution.mp3
+ │       └── [ 11M]  11. Morning Glory.mp3
+ ├── [337M]  MASTER BOOT RECORD
+ │   ├── [ 75M]  MASTER BOOT RECORD - C-CHKDSK -F - 02 MSDOS.SYS.flac
+ │   ├── [ 70M]  MASTER BOOT RECORD - C-CHKDSK -F - 03 XCOPY.EXE.flac
+ │   ├── [ 58M]  MASTER BOOT RECORD - C-CHKDSK -F - 08 NWOSHM.TXT.flac
+ │   ├── [ 48M]  MASTER BOOT RECORD - C-CHKDSK -F - 09 BAYAREA.BMP.flac
+ │   ├── [ 47M]  MASTER BOOT RECORD - C-EDIT AUTOEXEC.BAT - 03 SET PATH=C-METAL.flac
+ │   └── [ 39M]  MASTER BOOT RECORD - C-EDIT CONFIG.SYS - 03 FILES=666.flac
+ ├── [108M]  Myslovitz
+ │   ├── [ 31M]  03. Długość dźwięku samotności.flac
+ │   ├── [ 26M]  11. My.flac
+ │   └── [ 51M]  12. Zamiana.flac
+ ├── [111M]  Nagrobki
+ │   ├── [7.0M]  01. Co Z Nami Będzie.mp3
+ │   ├── [ 10M]  02. Matka Jedyna.mp3
+ │   ├── [5.0M]  02. Niedziela.mp3
+ │   ├── [9.0M]  03. Blady Świt.mp3
+ │   ├── [7.1M]  03. Kolejny Rok W Urnie.mp3
+ │   ├── [ 11M]  04. To Był Tylko Cień.mp3
+ │   ├── [8.9M]  04. Zaraza.mp3
+ │   ├── [ 15M]  06. Na Śmierć Zapomniałem 2015.mp3
+ │   ├── [ 11M]  06. Nie Chcę Myśleć O Śmierci.mp3
+ │   ├── [5.5M]  07. Martwi.mp3
+ │   ├── [ 10M]  07. Nekropolo 2017.mp3
+ │   └── [ 11M]  08. Nie Będzie Już Nic.mp3
+ ├── [ 23M]  Odyssey
+ │   ├── [4.2M]  Eurobeat Brony ft. Odyssey - Batty (Extended).mp3
+ │   ├── [5.0M]  Luna 2018 ft. Odyssey - Luna 2018 (Nightmare mode).mp3
+ │   ├── [8.2M]  Odyssey - Deceptive.mp3
+ │   └── [5.4M]  Odyssey & the DNA Team - Chained Lady [Extended].mp3
+ ├── [155M]  Of The Wand And The Moon
+ │   ├── [ 35M]  2001
+ │   │   ├── [ 10M]  01 - Lost In Emptiness.mp3
+ │   │   ├── [ 13M]  05 - Silver Rain.mp3
+ │   │   └── [ 12M]  06 - Gal Anda.mp3
+ │   ├── [ 37M]  2003
+ │   │   ├── [8.9M]  02-Naer Skog Naer Fjollum.mp3
+ │   │   ├── [ 10M]  03-Megin Runar.mp3
+ │   │   ├── [6.6M]  04-Followe Thy Faire Sunne Unhappy Shaddowe.mp3
+ │   │   └── [ 11M]  05-Time Time Time.mp3
+ │   ├── [ 28M]  2005
+ │   │   ├── [7.5M]  03 - Summer Solstice.mp3
+ │   │   ├── [5.5M]  06 - Wonderful Wonderful Sun.mp3
+ │   │   ├── [7.0M]  13 - Like Wolves.mp3
+ │   │   └── [8.0M]  14 - Winter Solstice .mp3
+ │   ├── [ 19M]  2010
+ │   │   ├── [8.6M]  01 We Are Dust.mp3
+ │   │   └── [ 10M]  02 Dirtnap Stories.mp3
+ │   └── [ 36M]  2011
+ │       ├── [ 16M]  01 - Sunspot.mp3
+ │       └── [ 20M]  03 - A Pyre Of Black Sunflowers.mp3
+ ├── [ 49M]  OST
+ │   ├── [ 37M]  Anime
+ │   │   ├── [3.4M]  D Pai - Le temps de la rentrée.mp3
+ │   │   ├── [ 10M]  Hanazawa Kana - Delusion Express.mp3
+ │   │   ├── [ 11M]  Hanazawa Kana - Mousou Express.mp3
+ │   │   ├── [9.7M]  Hanazawa Kana - Ren'ai Circulation.mp3
+ │   │   └── [3.6M]  Spice and Wolf (狼と香辛料) OST - Tabi no Tochuu.mp3
+ │   └── [ 12M]  Games
+ │       ├── [5.7M]  Touhou - Night of Knights.mp3
+ │       └── [6.5M]  【東方】 IOSYS - 断罪ヤマザナドゥ!.mp3
+ ├── [ 57M]  Rainbowdragoneyes
+ │   ├── [8.9M]  Rainbowdragoneyes - 16-Bit Universe.mp3
+ │   ├── [5.6M]  Rainbowdragoneyes – Blue Sky Forever.mp3
+ │   ├── [5.3M]  Rainbowdragoneyes - Chipwrecked.mp3
+ │   ├── [4.2M]  Rainbowdragoneyes – Creatures of Deception.mp3
+ │   ├── [4.7M]  Rainbowdragoneyes – Eur Hawt 2008.mp3
+ │   ├── [9.2M]  Rainbowdragoneyes – Starvved.mp3
+ │   ├── [6.8M]  Rainbowdragoneyes – The Primordial Booze.mp3
+ │   ├── [5.5M]  Rainbowdragoneyes – The Rift.mp3
+ │   └── [7.2M]  Rainbowdragoneyes – The Secret Mirror.mp3
+ ├── [1.2G]  Royksopp
+ │   ├── [174M]  2003
+ │   │   ├── [ 50M]  01. Don't Go.flac
+ │   │   ├── [ 24M]  01. So Easy.flac
+ │   │   ├── [ 24M]  02. Eple.flac
+ │   │   ├── [ 28M]  05. Poor Leno.flac
+ │   │   └── [ 47M]  07. Royksopp's Night Out.flac
+ │   ├── [441M]  2005
+ │   │   ├── [ 30M]  01. Triumphant.flac
+ │   │   ├── [ 29M]  02. Go Away.flac
+ │   │   ├── [ 30M]  02. Only This Moment.flac
+ │   │   ├── [ 36M]  03. 49 Percent.flac
+ │   │   ├── [ 33M]  03. Clean Sweep.flac
+ │   │   ├── [ 29M]  04. Boys.flac
+ │   │   ├── [ 31M]  04. Sombre Detune.flac
+ │   │   ├── [ 30M]  05. Follow My Ruin.flac
+ │   │   ├── [ 37M]  06. Beautiful Day Without You.flac
+ │   │   ├── [ 34M]  07. What Else Is There.flac
+ │   │   ├── [ 54M]  09. Alpha Male.flac
+ │   │   ├── [ 34M]  10. Someone Like Me.flac
+ │   │   └── [ 33M]  13. Curves (Bonus Track).flac
+ │   ├── [ 25M]  2006
+ │   │   └── [ 25M]  08. Go With The Flow.flac
+ │   ├── [331M]  2009
+ │   │   ├── [ 22M]  01. Happy Up Here.flac
+ │   │   ├── [ 33M]  02. The Girl and the Robot.flac
+ │   │   ├── [ 38M]  03. Vision One.flac
+ │   │   ├── [ 37M]  04. This Must Be It.flac
+ │   │   ├── [ 32M]  05. Royksopp Forever.flac
+ │   │   ├── [ 38M]  06. Miss It So Much.flac
+ │   │   ├── [ 41M]  07. Tricky Tricky.flac
+ │   │   ├── [ 27M]  09. Silver Cruiser.flac
+ │   │   ├── [ 23M]  11. It's What I Want.flac
+ │   │   └── [ 40M]  12. Were You Ever Wanted (Bonus Track For Japan).flac
+ │   ├── [138M]  2010
+ │   │   ├── [ 45M]  02. Tricky Two.flac
+ │   │   ├── [ 33M]  03. The Alcoholic.flac
+ │   │   ├── [ 30M]  05. The Drug.flac
+ │   │   └── [ 30M]  06. Forsaken Cowboy.flac
+ │   ├── [ 95M]  2014
+ │   │   ├── [ 27M]  01. Skulls.flac
+ │   │   ├── [ 35M]  09. Running To The Sea.flac
+ │   │   └── [ 34M]  12. Thank You.flac
+ │   └── [ 64M]  2016
+ │       └── [ 64M]  01. Bounty Hunters.flac
+ ├── [ 80M]  Scandroid
+ │   ├── [ 13M]  01. Aphelion.mp3
+ │   ├── [9.8M]  04 Shout [Tears for Fears cover].mp3
+ │   ├── [9.1M]  05 Destination unknown.mp3
+ │   ├── [9.8M]  06 Connection.mp3
+ │   ├── [ 11M]  07 Datastream.mp3
+ │   ├── [ 12M]  08 Empty streets.mp3
+ │   └── [ 15M]  11 Neo-Tokyo.mp3
+ ├── [294M]  Shamaani Duo, Shaman
+ │   ├── [ 91M]  Shamaani Duo - 1996 - Hunka lunka
+ │   │   ├── [8.6M]  01. Šamanát.mp3
+ │   │   ├── [4.9M]  02. Hunka lunka.mp3
+ │   │   ├── [ 11M]  03. Moai letne duoddaris.mp3
+ │   │   ├── [8.4M]  04. Gula gula.mp3
+ │   │   ├── [ 11M]  05. Riehču.mp3
+ │   │   ├── [ 11M]  06. Geahčan dan máilmmi.mp3
+ │   │   ├── [6.8M]  07. Meahcis.mp3
+ │   │   ├── [6.3M]  08. Mánážan.mp3
+ │   │   ├── [6.1M]  09. Nisson čahppes biktasiinnes -Lady In Black-.mp3
+ │   │   ├── [6.5M]  10. Idja dál lea.mp3
+ │   │   └── [9.7M]  11. Okto ijas.mp3
+ │   ├── [ 97M]  Shaman - 1999 - Idja
+ │   │   ├── [9.8M]  01. Odda Mailbmi.mp3
+ │   │   ├── [7.8M]  02. Idja.mp3
+ │   │   ├── [ 10M]  03. Ulda.mp3
+ │   │   ├── [ 10M]  04. Vuojan.mp3
+ │   │   ├── [9.4M]  05. Riehcu.mp3
+ │   │   ├── [6.2M]  06. Giella.mp3
+ │   │   ├── [5.6M]  07. Festet.mp3
+ │   │   ├── [9.1M]  08. Orbina.mp3
+ │   │   ├── [7.9M]  09. It Sat Duolmma Mu.mp3
+ │   │   ├── [8.7M]  10. Ostir Boo.mp3
+ │   │   ├── [4.8M]  11. Hunka Lunka.mp3
+ │   │   └── [7.0M]  12. Suollemas Bahcci.mp3
+ │   └── [106M]  Shaman - 2002 - Shamaniac
+ │       ├── [ 40M]  01. Mu Sieiddi Beales Mun Gottan.mp3
+ │       ├── [8.1M]  02. Il Lea Voibmi.mp3
+ │       ├── [9.6M]  03. Shamaniac.mp3
+ │       ├── [ 11M]  04. Jalla.mp3
+ │       ├── [ 20M]  05. Sugadit.mp3
+ │       ├── [ 11M]  06. Duoddarhalti.mp3
+ │       └── [6.4M]  07. Vuola Lavlla.mp3
+ ├── [167M]  Solstafir
+ │   ├── [ 20M]  01 Lagnaetti.mp3
+ │   ├── [ 15M]  02. Fjara.mp3
+ │   ├── [ 11M]  02 - Ísafold.mp3
+ │   ├── [ 22M]  02 Ótta.mp3
+ │   ├── [ 15M]  03. Þín orð.mp3
+ │   ├── [ 21M]  04-Ljósfari.mp3
+ │   ├── [ 19M]  05. Svartir Sandar.mp3
+ │   ├── [ 25M]  06. Djákninn.mp3
+ │   └── [ 18M]  08 - Bláfjall.mp3
+ ├── [ 98M]  Tor
+ │   └── [ 98M]  2012 - Drum Therapy
+ │       ├── [ 11M]  01. Tor - Glass & Stone.mp3
+ │       ├── [ 12M]  02. Tor - Heikki.mp3
+ │       ├── [ 12M]  03. Tor - Nomad.mp3
+ │       ├── [7.7M]  04. Tor - The Thickness.mp3
+ │       ├── [9.2M]  05. Tor - Aperture.mp3
+ │       ├── [9.8M]  07. Tor - Drum Therapy.mp3
+ │       ├── [ 15M]  08. Tor - Moon II.mp3
+ │       ├── [ 11M]  09. Tor - Paper Rain.mp3
+ │       └── [ 10M]  10. Tor - Let Me Down.mp3
+ ├── [251M]  Trevor Something
+ │   ├── [ 20M]  02. I Don't Care.flac
+ │   ├── [ 13M]  02. In My System.flac
+ │   ├── [ 22M]  03. Come Back Down.flac
+ │   ├── [ 25M]  03. Suicide.flac
+ │   ├── [ 26M]  05. Infatuation.flac
+ │   ├── [ 20M]  05. Underworld.flac
+ │   ├── [ 37M]  06. Run Away.flac
+ │   ├── [ 34M]  08. Disappear.flac
+ │   ├── [ 22M]  10. Sega Genesis.flac
+ │   └── [ 32M]  16. Enjoy The Silence.flac
+ ├── [ 84M]  Turbowolf
+ │   ├── [6.1M]  01 - Invisible Hand.mp3
+ │   ├── [5.5M]  02 Ancient Snake.mp3
+ │   ├── [6.4M]  04 Bag O Bones.mp3
+ │   ├── [7.3M]  06 - Nine Lives.mp3
+ │   ├── [9.8M]  07 The Big Cut.mp3
+ │   ├── [8.7M]  09 A Rose For The Crows.mp3
+ │   ├── [ 11M]  09 - Twelve Houses.mp3
+ │   ├── [ 10M]  10 Son (Sun).mp3
+ │   ├── [7.9M]  12 All The Trees.mp3
+ │   └── [ 11M]  13 Lets Die.mp3
+ ├── [ 15M]  Vali
+ │   ├── [8.2M]  01 - Naar Vinden Graater.mp3
+ │   └── [7.2M]  14 - Stein Og Bark.mp3
+ ├── [ 19M]  Valkyrien Allstars
+ │   ├── [3.5M]  02 Å Gjev Du Batt Meg.mp3
+ │   ├── [3.1M]  03 Hvis Jeg Var Deg.mp3
+ │   ├── [4.4M]  05 Fyll Mitt Beger På Ny.mp3
+ │   ├── [3.5M]  08 Eg Vil Ha Deg.mp3
+ │   └── [4.7M]  10 Reven.mp3
+ ├── [2.3G]  Various
+ │   ├── [ 29M]  Aaskereia - Als der Blick erlosch.mp3
+ │   ├── [4.1M]  Aaskereia - Gedanken.mp3
+ │   ├── [ 16M]  Aaskereia - Im Schattenlicht, Zwischen Den Welten.mp3
+ │   ├── [ 12M]  A Flock Of Seagulls - I Ran.mp3
+ │   ├── [5.8M]  Agnes Obel - Chord Left.mp3
+ │   ├── [8.2M]  Agnes Obel - Pass Them By.mp3
+ │   ├── [ 10M]  Agnes Obel - Run Cried The Crawling.mp3
+ │   ├── [ 10M]  Agnes Obel - The Curse.mp3
+ │   ├── [5.7M]  A-ha - Lifelines (Remastered Version).mp3
+ │   ├── [8.7M]  A-ha - Take On Me.mp3
+ │   ├── [6.6M]  Alan Jackson - Freight Train.mp3
+ │   ├── [5.9M]  Alan Tam - Wu Ye Qi Shi.mp3
+ │   ├── [3.3M]  Allie X - Bitch.mp3
+ │   ├── [5.6M]  Alphaville - Big in Japan (Remaster; Single Version).mp3
+ │   ├── [5.7M]  Alphaville - Dance with Me.mp3
+ │   ├── [5.4M]  Alphaville - Forever Young (Remaster).mp3
+ │   ├── [8.5M]  Anika - Sadness Hides the Sun.mp3
+ │   ├── [4.6M]  AnnenMayKantereit - 21, 22, 23.mp3
+ │   ├── [10.0M]  AnnenMayKantereit K.I.Z. - Hurra die Welt geht unter.mp3
+ │   ├── [6.0M]  AnnenMayKantereit - Nicht Nichts.mp3
+ │   ├── [4.2M]  AnnenMayKantereit - Ozean (Hennings Version).mp3
+ │   ├── [5.7M]  Annett Louisan - Engel.mp3
+ │   ├── [6.6M]  Arany Zoltan - Deixame subir.mp3
+ │   ├── [4.6M]  Arany Zoltán - Tri martolod.mp3
+ │   ├── [4.8M]  AronChupa, Cazzette, The High - She Wants Me Dead.mp3
+ │   ├── [4.2M]  Arthur Brown - Fire.mp3
+ │   ├── [9.8M]  Asana - Gravity-Free Love.mp3
+ │   ├── [4.7M]  ATB - 9 PM - Till I Come (Radio Edit).mp3
+ │   ├── [3.2M]  Björn Thorarensen - Skór - Ylfa Mist.mp3
+ │   ├── [6.4M]  Black Sabbath - Paranoid.mp3
+ │   ├── [6.6M]  Blues Saraceno - Howlin' At The Moon.mp3
+ │   ├── [ 10M]  Blues Saraceno - Judgement Day.mp3
+ │   ├── [ 10M]  Bots - Das weiche Wasser.mp3
+ │   ├── [9.7M]  Bots - Die Karawane.mp3
+ │   ├── [ 10M]  Bots - Paradijs.mp3
+ │   ├── [8.8M]  Brazzaville - Star Called Sun.mp3
+ │   ├── [9.3M]  Burak Yeter - Tuesday.mp3
+ │   ├── [4.1M]  Carly Comando - Everyday.mp3
+ │   ├── [5.1M]  C. C. Catch - Heaven and Hell.mp3
+ │   ├── [ 13M]  Chris Christodoulou - Coalescence.mp3
+ │   ├── [4.9M]  Cold Play - Beautiful World.mp3
+ │   ├── [ 13M]  Complex Numbers - Неизбежность.mp3
+ │   ├── [5.3M]  Creedence Clearwater Revival - Fortunate Son.mp3
+ │   ├── [6.6M]  Cutting Crew - (I Just) Died In Your Arms.mp3
+ │   ├── [7.8M]  Dalriada - A Nap és a Szél Háza.mp3
+ │   ├── [8.0M]  Dalriada - Hajdútánc.mp3
+ │   ├── [ 12M]  Dalriada - Hej, virágom.mp3
+ │   ├── [7.1M]  Dalriada - Igazi Tûz.mp3
+ │   ├── [7.7M]  Dalriada - Tavaszköszöntő.mp3
+ │   ├── [9.4M]  Daniel Pemberton - Circular Story.mp3
+ │   ├── [9.1M]  Daryl Hall & John Oates - Out of Touch.mp3
+ │   ├── [6.2M]  Deep Purple - Hush.mp3
+ │   ├── [6.1M]  Desireless - Voyage Voyage.mp3
+ │   ├── [4.6M]  Detektivbyrån - E18.mp3
+ │   ├── [4.6M]  Detektivbyrån - Om Du Möter Varg.mp3
+ │   ├── [6.9M]  Die Toten Hosen - The Guns of Brixton.mp3
+ │   ├── [6.3M]  Dschinghis Khan - Moskau.mp3
+ │   ├── [4.9M]  Dubioza Kolektiv - Free.Mp3 (The Pirate Bay Song).mp3
+ │   ├── [5.1M]  Dubioza Kolektiv - Himna generacije.mp3
+ │   ├── [9.4M]  DZIDZIO feat. Вова зі Львова - Павук.mp3
+ │   ├── [5.3M]  Eivør - Í Tokuni.mp3
+ │   ├── [4.9M]  Eivør Pálsdóttir - Ástarstund.mp3
+ │   ├── [4.9M]  Eivør Pálsdóttir - Brotin.mp3
+ │   ├── [5.1M]  Eivør Pálsdóttir - Into the Mist.mp3
+ │   ├── [5.4M]  Eivør Pálsdóttir - Við gengum tvö.mp3
+ │   ├── [8.0M]  Emilíana Torrini - Gun.mp3
+ │   ├── [6.5M]  Emilíana Torrini - If You Go Away.mp3
+ │   ├── [3.2M]  Emilíana Torrini - Jungle Drum.mp3
+ │   ├── [6.4M]  Falconer - The Past Still Lives On.mp3
+ │   ├── [6.1M]  Feint, Coma - Snake Eyes (feat. CoMa).mp3
+ │   ├── [ 12M]  Fejd - Äril.mp3
+ │   ├── [7.9M]  FGFC820 - Killing Fields.mp3
+ │   ├── [4.2M]  Folque - Alison Gross.mp3
+ │   ├── [4.4M]  Fool's Garden - Lemon Tree (Studio Version 2009).mp3
+ │   ├── [ 17M]  Forndom - Flykt.mp3
+ │   ├── [4.8M]  Forndom - Nio Nätters Led.mp3
+ │   ├── [5.1M]  Fortune 600 - Московский Бит.mp3
+ │   ├── [9.8M]  Foster The People - Pumped Up Kicks.mp3
+ │   ├── [2.0M]  Franck Carrel - Dernier Baisers.mp3
+ │   ├── [6.4M]  Franz Ferdinand - Evil Eye.mp3
+ │   ├── [ 10M]  Fred Ventura - Wind Of Change (Remastering).mp3
+ │   ├── [4.4M]  Full Dawa - Dabai Dabai (Radio Edit).mp3
+ │   ├── [2.3M]  Gary Jules - Mad World.mp3
+ │   ├── [3.4M]  Gershon Kingsley - Pop Corn (1969).mp3
+ │   ├── [5.6M]  Getter Jaani - Rockefeller Street (Nightcore).mp3
+ │   ├── [ 11M]  Giorgio Moroder - Shannon's Eyes - Single Version - Remastered.mp3
+ │   ├── [8.5M]  Glittertind - Kvilelaus.mp3
+ │   ├── [9.1M]  Glittertind - Om Kvelden Når Det Mørkner.mp3
+ │   ├── [ 11M]  Glittertind - Stjerneslør.mp3
+ │   ├── [ 41M]  GNOM - Battletoads.mp3
+ │   ├── [7.6M]  Goga - Bobi-Boba.mp3
+ │   ├── [8.6M]  Gorillaz - Dirty Harry.mp3
+ │   ├── [4.4M]  Green Gnome - Seven Nation Army.mp3
+ │   ├── [9.6M]  Guðrið Hansdóttir - Morgun Í Mars.mp3
+ │   ├── [8.4M]  Helium Vola - Omnis Mundi Creatura.mp3
+ │   ├── [9.2M]  Helium Vola - Selig.mp3
+ │   ├── [9.9M]  Helium Vola - Veni Veni.mp3
+ │   ├── [8.1M]  HOME - Resonance.mp3
+ │   ├── [ 12M]  iamamiwhoami - Goods.mp3
+ │   ├── [ 13M]  iamamiwhoami - shadowshow.mp3
+ │   ├── [4.0M]  Icelandic Folk Music - Sofðu Unga Ástin Min.mp3
+ │   ├── [9.4M]  Infinity of Sound - Million Roses (Live Version).mp3
+ │   ├── [ 11M]  I Shot The Duck Hunt Dog - BGM 13.mp3
+ │   ├── [3.1M]  I Shot The Duck Hunt Dog - BGM 1.mp3
+ │   ├── [4.1M]  I Shot The Duck Hunt Dog - BGM 6.mp3
+ │   ├── [2.4M]  I Shot The Duck Hunt Dog - Fap Party At Pedo Bear's House (featuring Peet from Iamerror).mp3
+ │   ├── [2.2M]  I Shot The Duck Hunt Dog - I Sniffed Tifa's Orthopedic Underwear.mp3
+ │   ├── [2.9M]  Islandica - Á Sprengisandi.mp3
+ │   ├── [3.0M]  Islandsklukkur - Á Sprengisandi.mp3
+ │   ├── [2.0M]  Islandsklukkur - Krummavisur.mp3
+ │   ├── [3.6M]  Ivan Mládek, Banjo Band, Banjo Band Ivana Mládka, - Jožin z bažin.mp3
+ │   ├── [5.1M]  Jan Hammer, Mark Ayres - Miami Vice - Crockett's Theme.mp3
+ │   ├── [5.9M]  Jean-Michel Jarre - Magnetic Fields, Pt. 2.mp3
+ │   ├── [5.5M]  Joy - Japanese Girls.mp3
+ │   ├── [ 19M]  Junip - At the Doors.mp3
+ │   ├── [6.4M]  Junip - Far Away.mp3
+ │   ├── [ 13M]  Junip - Line of Fire.mp3
+ │   ├── [ 12M]  Junip - Oba, Lá Vem Ela.mp3
+ │   ├── [ 12M]  Junip - Rope and Summit.mp3
+ │   ├── [7.3M]  Junip - White Rain.mp3
+ │   ├── [6.7M]  KONGOS - Come With Me Now.mp3
+ │   ├── [ 11M]  Konto S - Biełyje Rozy.mp3
+ │   ├── [ 12M]  Korpiklaani - Harmaja.mp3
+ │   ├── [ 13M]  Kraftwerk - Neonlicht.mp3
+ │   ├── [3.6M]  Kraftwerk - Popcorn.mp3
+ │   ├── [4.4M]  KSHMR, Kaaze, KARRA - Devil Inside Me (feat. KARRA).mp3
+ │   ├── [5.8M]  Laura Branigan - Self Control.mp3
+ │   ├── [3.8M]  Lenka - Everything at Once.mp3
+ │   ├── [8.9M]  Les Discrets - Apres l'Ombre.mp3
+ │   ├── [6.8M]  Lia - New World (Omega Force Mix) (200 BPM Version).mp3
+ │   ├── [9.2M]  Lian Ross - Say You'll Never.mp3
+ │   ├── [6.1M]  Mahala Rai Banda, Shantel - Mahalageasca (Bucovina Dub).mp3
+ │   ├── [4.3M]  Marie Laforet - Marie douceur, Marie colère.mp3
+ │   ├── [ 15M]  Marie Madeleine - Swiming Pool.mp3
+ │   ├── [9.8M]  Massive Attack - Paradise Circus.mp3
+ │   ├── [8.7M]  Matthew Koma - Kisses Back.mp3
+ │   ├── [5.8M]  Mayfair - Gold Coated Girls.mp3
+ │   ├── [2.9M]  Merv Griffin - House of Horrors.mp3
+ │   ├── [4.2M]  Messer Chups - The Hound of the Baskervilles.mp3
+ │   ├── [5.1M]  M & H Band - Pop-Corn (Disco Mix).mp3
+ │   ├── [7.4M]  Mike Oldfield - Moonlight Shadow.mp3
+ │   ├── [ 12M]  Mike Oldfield - Nuclear.mp3
+ │   ├── [ 10M]  Moby - Why Does My Heart Feel so Bad.mp3
+ │   ├── [ 12M]  Modern Talking - Atlantis Is Calling (S.O.S. for Love).mp3
+ │   ├── [8.4M]  Modern Talking - Brother Louie.mp3
+ │   ├── [8.6M]  Modern Talking - Cheri Cheri Lady.mp3
+ │   ├── [5.3M]  Modern Talking - Cheri Cheri Lady (Remastered).mp3
+ │   ├── [7.5M]  Modern Talking - Geronimo's Cadillac.mp3
+ │   ├── [8.7M]  Modern Talking - Who WiII Save The World.mp3
+ │   ├── [7.7M]  Modern Talking - You're My Heart You're My Soul.mp3
+ │   ├── [6.3M]  Moi Dix Mois - Front Et Baiser.mp3
+ │   ├── [ 10M]  Moi Dix Mois - Last temptation.mp3
+ │   ├── [4.5M]  Moi Dix Mois - Xanadu.mp3
+ │   ├── [2.5M]  Mounika - Marie Laforet I Tu Fais Semblant.mp3
+ │   ├── [3.9M]  Mr.Kitty - After Dark.mp3
+ │   ├── [7.6M]  Mylène Farmer - Innamoramento.mp3
+ │   ├── [6.9M]  Mylène Farmer - Pourvu Qu'Elles Soient Douces.mp3
+ │   ├── [6.4M]  Mylène Farmer - Sans Logique.mp3
+ │   ├── [9.7M]  Nachtgeschrei - Herz Aus Stein.mp3
+ │   ├── [ 11M]  Nakaido 'Chabo' Reichi - Tooi Sakebi.mp3
+ │   ├── [9.0M]  Nekromantix - Haunted Cathouse.mp3
+ │   ├── [4.3M]  Nekromantix - Sea of Red.mp3
+ │   ├── [5.4M]  Nena - 99 Luftballons.mp3
+ │   ├── [5.6M]  Nena - Irgendwie, Irgendwo, Irgendwann.mp3
+ │   ├── [ 10M]  Nominal - Everyday Anyone.mp3
+ │   ├── [ 16M]  N'TO - La Cle Des Champs.mp3
+ │   ├── [ 22M]  Nu - Who Loves the Sun (feat. Joke).mp3
+ │   ├── [4.6M]  Nym - 900 Miles.mp3
+ │   ├── [3.9M]  Ólafur Þórðarson - Á Sprengisandi.mp3
+ │   ├── [1.8M]  Oldelaf - Le cafe.mp3
+ │   ├── [5.3M]  Omega - Gyöngyhajú Lány.mp3
+ │   ├── [6.3M]  Opus - Live Is Life.mp3
+ │   ├── [ 10M]  Orgy - Blue Monday.mp3
+ │   ├── [5.8M]  O-Zone - De Ce Plang Chitarele.mp3
+ │   ├── [5.4M]  O-Zone - Despre Tine.mp3
+ │   ├── [4.9M]  O-Zone - Dragostea Din Tei.mp3
+ │   ├── [5.5M]  O-Zone - Printre Nori.mp3
+ │   ├── [6.6M]  Papillon Rising - High Upon High (Flight of the Dragon).mp3
+ │   ├── [2.7M]  Paul Revere & The Raiders - Cherokee Nation.mp3
+ │   ├── [3.9M]  P.E.P.E. - Shadilay.mp3
+ │   ├── [5.4M]  Pépite - Hiéroglyphes.mp3
+ │   ├── [ 11M]  Persephone's Bees - Я стою на том берегу.mp3
+ │   ├── [7.0M]  Pet Shop Boys - It's A Sin.mp3
+ │   ├── [6.1M]  Phil Coulter, David L. Cooke, Dermot Byrne, The Ne - Coultergeist.mp3
+ │   ├── [2.4M]  Pidżama Porno - Chryzantemy.mp3
+ │   ├── [6.8M]  Pidżama Porno - Koszmary Koszmarów 4 pary.mp3
+ │   ├── [8.4M]  Power Glove - Resurrection.mp3
+ │   ├── [ 11M]  PPK - Воскрешение.mp3
+ │   ├── [6.7M]  Princess Chelsea - Cigarette.mp3
+ │   ├── [9.6M]  Radiohead - Creep.mp3
+ │   ├── [6.4M]  Raffaella Carrà - A far l'amore comincia tu.mp3
+ │   ├── [5.1M]  Rednex - Wish You Were Here.mp3
+ │   ├── [6.3M]  R.E.M. - Losing My Religion.mp3
+ │   ├── [5.8M]  Richard Cheese - Creep.mp3
+ │   ├── [8.8M]  Robbie Williams - King Of The Bongo.mp3
+ │   ├── [5.8M]  Rolling Stones - Paint it Black.mp3
+ │   ├── [4.5M]  Sandra - Around My Heart.mp3
+ │   ├── [5.2M]  Sandra - Everlasting Love.mp3
+ │   ├── [5.4M]  Sandra - Innocent Love (Single Version).mp3
+ │   ├── [5.6M]  Sandra - In The Heat Of The Night.mp3
+ │   ├── [5.4M]  Sandra - Japan ist weit.mp3
+ │   ├── [5.6M]  Sandra - Maria Magdalena.mp3
+ │   ├── [5.7M]  Sandra - Secret Land.mp3
+ │   ├── [5.4M]  Sandra - Stop For A Minute (Single Version).mp3
+ │   ├── [6.0M]  Sandra - Such A Shame.mp3
+ │   ├── [6.6M]  Schiller, Jan Blomqvist - In Between.mp3
+ │   ├── [5.4M]  Scooter - How Much Is the Fish.mp3
+ │   ├── [5.5M]  Scooter - The Logical Song.mp3
+ │   ├── [6.0M]  Shantel - Disko Boy.mp3
+ │   ├── [6.7M]  Shantel - Disko Partizani.mp3
+ │   ├── [5.2M]  Shocking Blue - Long And Lonesome Road.mp3
+ │   ├── [4.8M]  Silent Partner - Tidal Wave.mp3
+ │   ├── [4.6M]  Simon Viklund - Evil Eye (Full).mp3
+ │   ├── [2.8M]  Sissel Kyrkjebø - Sofðu unga ástin mín.mp3
+ │   ├── [7.0M]  Spanxti - Aš pas savo giminėlę.mp3
+ │   ├── [ 15M]  Spanxti - Gailia Gailiai Zylė Verkė.mp3
+ │   ├── [ 15M]  Spanxti - Jūs mano kūmužėliai.mp3
+ │   ├── [ 17M]  Spanxti - Leliumai.mp3
+ │   ├── [9.8M]  Spanxti - Oi ką kalba apynėlis.mp3
+ │   ├── [ 13M]  Spanxti - Pas Leišius Alaus Gert.mp3
+ │   ├── [ 15M]  Spanxti - Saulė rieda dangumi.mp3
+ │   ├── [ 12M]  Spanxti - Už kalnelio ežerėlis.mp3
+ │   ├── [7.5M]  Steeleye Span - Alison Gross (Old English Ballad).mp3
+ │   ├── [4.7M]  Stephen Paul Taylor - Everybody Knows Shit's Fucked.mp3
+ │   ├── [7.2M]  Steve Miller Band - Abracadabra.mp3
+ │   ├── [7.9M]  Strachy Na Lachy - Czarny chleb i czarna kawa.mp3
+ │   ├── [4.9M]  Strachy Na Lachy - Przedskole.mp3
+ │   ├── [4.5M]  Styx - Boat On The River.mp3
+ │   ├── [2.4M]  Supertramp - Breakfast in America.opus
+ │   ├── [4.7M]  Sylver - Forgiven (Remixes).mp3
+ │   ├── [6.3M]  The Animals - House of the Rising Sun.mp3
+ │   ├── [9.3M]  The Cyborgs - I'm Just a Cyborg and I Don't Believe in God.mp3
+ │   ├── [ 10M]  The Fray - How to Save a Life.mp3
+ │   ├── [9.7M]  The Glitch Mob - We Can Make The World Stop.mp3
+ │   ├── [7.8M]  The Hu, Dashdondog Bayarmagnai - Wolf Totem.mp3
+ │   ├── [6.7M]  The Hu, Dashdondog Bayarmagnai - Yuve Yuve Yu.mp3
+ │   ├── [9.2M]  The HU - Shoog Shoog.mp3
+ │   ├── [ 11M]  The HU - The Gereg.mp3
+ │   ├── [ 10M]  The HU - The Great Chinggis Khaan.mp3
+ │   ├── [4.9M]  The Living Tombstone - Nippontradamus.mp3
+ │   ├── [6.4M]  The Living Tombstone - We Are Number One.mp3
+ │   ├── [3.2M]  The Meteors - Breaking The Law.mp3
+ │   ├── [4.7M]  The Meteors - King Vlad.mp3
+ │   ├── [9.3M]  The Meteors - Never Stop The Hate Train.mp3
+ │   ├── [6.3M]  The Meteors - Paradise Lost.mp3
+ │   ├── [4.7M]  The Meteors - The Phantom Rider.mp3
+ │   ├── [7.8M]  The Meteors - These Evil Things.mp3
+ │   ├── [4.5M]  The Pierces - Sticks and Stones.mp3
+ │   ├── [1.8M]  The Revels - Comanche.mp3
+ │   ├── [4.9M]  The Rolling Stones - Paint It Black.mp3
+ │   ├── [5.2M]  The Rolling Stones - Somebody To Love.mp3
+ │   ├── [3.3M]  The Rumjacks - An poc ar buile.mp3
+ │   ├── [6.5M]  The Rumjacks - The Foreman O’Rourke.mp3
+ │   ├── [8.4M]  The Rumjacks - The Pot & Kettle.mp3
+ │   ├── [5.7M]  The Trashmen - Surfin` bird.mp3
+ │   ├── [7.7M]  The Weeknd - Blinding Lights.mp3
+ │   ├── [4.0M]  Those Poor Bastards - Phantom Pool.mp3
+ │   ├── [9.1M]  Tilt! - Running Up That Hill.mp3
+ │   ├── [4.5M]  Tony Tam - Hard Life (w lyrics).mp3
+ │   ├── [ 12M]  Týr - By The Light Of The Northern Star.mp3
+ │   ├── [10.0M]  Týr - Evening Star.mp3
+ │   ├── [ 10M]  Týr - Shadow of The Swastika.mp3
+ │   ├── [9.4M]  U 96 - Club Bizarre.mp3
+ │   ├── [ 11M]  U 96 - Das Boot II.mp3
+ │   ├── [ 12M]  U 96 - Das Boot.mp3
+ │   ├── [6.7M]  U 96 - Love Religion.mp3
+ │   ├── [ 10M]  Ultravox - Hymn.mp3
+ │   ├── [9.1M]  Vitalic - Poison Lips.mp3
+ │   ├── [ 11M]  Waveshaper - Donnas Dream.mp3
+ │   ├── [ 14M]  Whomadewho - Below the Cherry Moon.mp3
+ │   ├── [ 11M]  Yasushi Ishii - Echoing Truth.mp3
+ │   ├── [4.7M]  Yeah Yeah Yeahs - Down Boy.mp3
+ │   ├── [4.6M]  Yeah Yeah Yeahs - Gold Lion.mp3
+ │   ├── [7.7M]  Yeah Yeah Yeahs - Heads Will Roll.mp3
+ │   ├── [7.2M]  Yeah Yeah Yeahs - Phenomena.mp3
+ │   ├── [7.5M]  Yeah Yeah Yeahs - Run away.mp3
+ │   ├── [8.0M]  Yeah Yeah Yeahs - Sacrilege.mp3
+ │   ├── [4.3M]  Yiruma - River Flows In You (Original).mp3
+ │   ├── [7.6M]  Ylfa Mist - Fljótið.mp3
+ │   ├── [4.5M]  Ylfa Mist - Gestur.mp3
+ │   ├── [3.2M]  Ylfa Mist - Skór.mp3
+ │   ├── [7.2M]  Yogscast - Diggy Diggy Hole.mp3
+ │   ├── [4.6M]  Zespół Krakusy - Czerwone jagody.mp3
+ │   ├── [5.1M]  Агата Кристи - Вечная любовь.mp3
+ │   ├── [5.7M]  Агата Кристи - Как на войне.mp3
+ │   ├── [4.7M]  Агата Кристи - Ковёр-вертолёт.mp3
+ │   ├── [5.6M]  Агата Кристи - Опиум для никого.mp3
+ │   ├── [4.1M]  Агата Кристи - Сказочная тайга.mp3
+ │   ├── [ 13M]  Альянс - На заре.mp3
+ │   ├── [4.6M]  АукцЫон - Дорога.mp3
+ │   ├── [5.9M]  АукцЫон - На краю.mp3
+ │   ├── [7.3M]  Браво - Этот город.mp3
+ │   ├── [8.4M]  Виктор Цой - Спокойная ночь.mp3
+ │   ├── [569K]  Гражданская Оборона - Вот такая вот хуйня!.mp3
+ │   ├── [ 13M]  Гражданская оборона - Долгая счастливая жизнь.mp3
+ │   ├── [4.4M]  Гражданская Оборона - Дурачок.mp3
+ │   ├── [3.7M]  Гражданская Оборона - Мёртвые.mp3
+ │   ├── [8.5M]  Гражданская Оборона - Моя оборона.mp3
+ │   ├── [6.3M]  Гражданская Оборона - Про Мишутку.mp3
+ │   ├── [4.4M]  Егор Летов - Всё идёт по плану.mp3
+ │   ├── [4.3M]  ЖЩ - Мрак.mp3
+ │   ├── [ 10M]  Изморозь - Заебись!.mp3
+ │   ├── [ 15M]  Кобыла и Трупоглазые Жабы Искали Цезию Нашли Поздно Утром Свистящего Хна - Молох.mp3
+ │   ├── [6.1M]  Монгол Шуудан - Москва.mp3
+ │   ├── [6.8M]  Мурат Насыров - Мальчик хочет в Тамбов (Tambov One Love).mp3
+ │   ├── [3.5M]  Мясокомбинат им. Путина №1 - Изумрудный хуй.mp3
+ │   ├── [4.1M]  Нейро дюбель - Дохуя советчиков.mp3
+ │   ├── [3.9M]  Нейро дюбель - Марскі чалавек.mp3
+ │   ├── [5.3M]  Нейро дюбель - Резиновый дом.mp3
+ │   ├── [7.3M]  Никого Нет Дома - 5000.mp3
+ │   ├── [ 10M]  Никого Нет Дома - 9 Дней.mp3
+ │   ├── [5.9M]  Никого Нет Дома - Окинава.mp3
+ │   ├── [ 10M]  Никого Нет Дома - Опасайся Остерегайся.mp3
+ │   ├── [ 11M]  Никого Нет Дома - Проверка.mp3
+ │   ├── [7.4M]  Никого Нет Дома - Себя убивать.mp3
+ │   ├── [ 13M]  Никого Нет Дома - Уходит транспорт.mp3
+ │   ├── [8.6M]  Ногу Cвело - Харумамбуру.mp3
+ │   ├── [8.0M]  Павел Пламенев - Геймер.mp3
+ │   ├── [7.7M]  Павел Пламенев - Герой с тысячью лиц.mp3
+ │   ├── [5.2M]  ПОСТная херня - Пойдем в Макдак.mp3
+ │   ├── [7.7M]  Сети - Смайл.mp3
+ │   ├── [3.7M]  Хитобои - Долбоеб.mp3
+ │   ├── [4.1M]  Хитобои - К хуям.mp3
+ │   └── [2.2M]  日笠陽子 - クリムゾンの微熱 (深红色的微热).opus
+ ├── [126M]  Наутилус Помпилиус
+ │   ├── [8.4M]  Наутилус Помпилиус - Дыхание.mp3
+ │   ├── [ 12M]  Наутилус Помпилиус - Железнодорожник.mp3
+ │   ├── [ 15M]  Наутилус Помпилиус - Зверь.mp3
+ │   ├── [8.2M]  Наутилус Помпилиус - Князь тишины.mp3
+ │   ├── [8.6M]  Наутилус Помпилиус - Крылья.mp3
+ │   ├── [ 13M]  Наутилус Помпилиус - Люди на холме.mp3
+ │   ├── [8.4M]  Наутилус Помпилиус - Последнее письмо (Гудбай Америка).mp3
+ │   ├── [8.6M]  Наутилус Помпилиус - Прогулки по воде.mp3
+ │   ├── [ 11M]  Наутилус Помпилиус - Синоптики.mp3
+ │   ├── [9.8M]  Наутилус Помпилиус - Скованные одной цепью.mp3
+ │   ├── [7.3M]  Наутилус Помпилиус - Труби Гавриил.mp3
+ │   ├── [9.0M]  Наутилус Помпилиус - Хлоп-хлоп.mp3
+ │   └── [7.8M]  Наутилус Помпилиус - Черные птицы.mp3
+ ├── [206M]  Пикник
+ │   ├── [9.8M]  Пикник - Азбука Морзе.mp3
+ │   ├── [ 13M]  Пикник - А может быть и не было меня.mp3
+ │   ├── [10.0M]  Пикник - Говорит и показывает.mp3
+ │   ├── [ 12M]  Пикник - Египтянин.mp3
+ │   ├── [ 11M]  Пикник - Железные мантры.mp3
+ │   ├── [ 10M]  Пикник - Игла.mp3
+ │   ├── [ 14M]  Пикник - Иероглиф.mp3
+ │   ├── [ 10M]  Пикник - Из коры себе подругу выстругал.mp3
+ │   ├── [ 10M]  Пикник - Из мышеловки.mp3
+ │   ├── [ 11M]  Пикник - Королевство кривых.mp3
+ │   ├── [9.0M]  Пикник - Кукла с человеческим лицом.mp3
+ │   ├── [9.2M]  Пикник - Любо братцы любо.mp3
+ │   ├── [ 12M]  Пикник - Мы как трепетные птицы.mp3
+ │   ├── [ 10M]  Пикник - Ой да не вечер....mp3
+ │   ├── [ 11M]  Пикник - От Кореи до Карелии.mp3
+ │   ├── [5.1M]  Пикник - Там на самом на краю земли.mp3
+ │   ├── [8.1M]  Пикник - У шамана три руки.mp3
+ │   ├── [9.5M]  Пикник - Фиолетово - чёрный.mp3
+ │   ├── [9.4M]  Пикник - Чёрный ворон.mp3
+ │   └── [ 11M]  Пикник - Я - пущенная стрела.mp3
+ ├── [ 27M]  Пневмослон
+ │   ├── [3.6M]  Пневмослон - В говне, с говном, в говно.mp3
+ │   ├── [3.9M]  Пневмослон - Герои похуизма.mp3
+ │   ├── [4.5M]  Пневмослон - Добрая девочка.mp3
+ │   ├── [4.5M]  Пневмослон - Надежда страны.mp3
+ │   ├── [3.1M]  Пневмослон - Невероятное амбре.mp3
+ │   ├── [4.4M]  Пневмослон - Работники культуры.mp3
+ │   └── [3.1M]  Пневмослон - Финита ля комедия.mp3
+ ├── [102M]  Последние Танки В Париже
+ │   ├── [8.5M]  01 - Время говорить.mp3
+ │   ├── [ 10M]  01 - Ненависть.mp3
+ │   ├── [8.3M]  02 - Ключи.mp3
+ │   ├── [6.0M]  02 - Лестница в никуда.mp3
+ │   ├── [5.7M]  03 - Розы в грязи.mp3
+ │   ├── [7.6M]  04 - Листовки.mp3
+ │   ├── [4.1M]  04 - Реакция.mp3
+ │   ├── [6.5M]  05 - Чёрный пиар.mp3
+ │   ├── [6.5M]  06. Что делать.mp3
+ │   ├── [ 12M]  07 - Дворы.mp3
+ │   ├── [4.1M]  08 - Гордость.mp3
+ │   ├── [6.1M]  09 - Новые времена.mp3
+ │   ├── [ 11M]  09 - Уйдём.mp3
+ │   └── [4.6M]  10. Ночь длинных ножей.mp3
+ └── [586M]  やくしまるえつこ
+     ├── [ 75M]  [2010 · KSCL 1588] 神様のいうとおり
+     │   ├── [ 26M]  01. 神様のいうとおり.flac
+     │   ├── [ 24M]  02. 神様のいうとおり (Version Z80).flac
+     │   ├── [ 25M]  03. 神様のいうとおり (Instrumental).flac
+     │   ├── [ 865]  神様のいうとおり.cue
+     │   └── [2.4K]  神様のいうとおり.log
+     ├── [377M]  [2013 · XNMR-0122] Radio Onsen Eutopia
+     │   ├── [ 51M]  01. ノルニル.flac
+     │   ├── [ 15M]  02. 恋するニワトリ.flac
+     │   ├── [ 24M]  03. ヴィーナスとジーザス.flac
+     │   ├── [ 35M]  04. COSMOS vs ALIEN.flac
+     │   ├── [ 26M]  05. 北風小僧の寒太郎.flac
+     │   ├── [ 27M]  06. ヤミヤミ.flac
+     │   ├── [ 46M]  07. 少年よ我に帰れ.flac
+     │   ├── [ 15M]  08. キャベツUFO.flac
+     │   ├── [ 14M]  09. ラジャ・マハラジャー.flac
+     │   ├── [ 43M]  10. ときめきハッカー.flac
+     │   ├── [ 14M]  11. メトロポリタン美術館.flac
+     │   ├── [ 67M]  12. ロンリープラネット.flac
+     │   ├── [2.2K]  Radio Onsen Eutopia.cue
+     │   └── [ 13K]  Radio Onsen Eutopia.log
+     └── [134M]  [2014 · VTCL-35175] X次元へようこそ
+         ├── [ 35M]  01. X次元へようこそ.flac
+         ├── [ 28M]  02. 絶対ムッシュ制.flac
+         ├── [ 33M]  03. X次元へようこそ (off vocal).flac
+         ├── [ 27M]  04. 絶対ムッシュ制 (off vocal).flac
+         ├── [ 11M]  05. X次元へようこそ (TV size).flac
+         ├── [1.2K]  X次元へようこそ.cue
+         └── [8.5K]  X次元へようこそ.log
+

+

+

+ + 8.2G used in 80 directories, 747 files +

+

+
+

+ tree v1.8.0 © 1996 - 2018 by Steve Baker and Thomas Moore
+ HTML output hacked and copyleft © 1998 by Francesc Rocher
+ JSON output hacked and copyleft © 2014 by Florian Sesser
+ Charsets / OS/2 support © 2001 by Kyosuke Tokoro +

+ + diff --git a/static/radio/assets/js/main.js b/static/radio/assets/js/main.js new file mode 100644 index 0000000..9bfbc39 --- /dev/null +++ b/static/radio/assets/js/main.js @@ -0,0 +1,22 @@ +function getRadioStats() { + const $ = id => document.getElementById(id); + + fetch("/stats") + .then(r => r.json()) + .then(radio => { + $("radio-status").innerHTML = + `On-air since `; + $("radio-song").textContent = radio.song; + $("radio-listeners").textContent = radio.listeners; + $("radio-listener-peak").textContent = radio.listener_peak; + }).catch(() => { + $("radio-status").textContent = "Now is offline"; + $("radio-song").textContent = + $("radio-listeners").textContent = + $("radio-listener-peak").textContent = "n/a"; + }); +} + + +getRadioStats(); +setInterval(getRadioStats, 45000); \ No newline at end of file diff --git a/static/shared/fonts/RobotoCondensed-Regular.ttf b/static/shared/fonts/RobotoCondensed-Regular.ttf new file mode 100644 index 0000000..9a1418d Binary files /dev/null and b/static/shared/fonts/RobotoCondensed-Regular.ttf differ diff --git a/static/shared/fonts/ShareTechMono-Regular.ttf b/static/shared/fonts/ShareTechMono-Regular.ttf new file mode 100644 index 0000000..c8e530f Binary files /dev/null and b/static/shared/fonts/ShareTechMono-Regular.ttf differ diff --git a/static/shared/img/favicon.ico b/static/shared/img/favicon.ico new file mode 100644 index 0000000..2fef958 Binary files /dev/null and b/static/shared/img/favicon.ico differ diff --git a/static/shared/img/favicon_128.png b/static/shared/img/favicon_128.png new file mode 100644 index 0000000..c7d066e Binary files /dev/null and b/static/shared/img/favicon_128.png differ diff --git a/util.js b/util.js new file mode 100644 index 0000000..0018f7a --- /dev/null +++ b/util.js @@ -0,0 +1,28 @@ +const moment = require("moment-timezone"); + +const formats = Object.freeze({ + post_date: "Do MMMM YYYY HH:mm:ss z", + file_date: "YYYY-MM-DD HH:mm:ss z", + id_date: "YYYYMMDD-HHmm" }); + +function datetime(date, format=formats.post_date, timezone="UTC") { + return moment.utc(date).tz(timezone).format(format); +} + +exports.getBaseHost = (host) => { + switch (host) { + case host.endsWith("onion"): + return `http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion`; + case host.endsWith("b32.i2p"): + return `http://t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p`; + default: + return "https://arav.top"; + } +} + +exports.formats = formats; +exports.datetime = datetime; + +exports.rssLink = (proto, host, date, category, timezone="UTC") => + `${proto}://${host}/mindflow#${category}-${datetime(date, formats.id_date, timezone)}`; + diff --git a/views/dwelling/404.pug b/views/dwelling/404.pug new file mode 100644 index 0000000..2aa0c83 --- /dev/null +++ b/views/dwelling/404.pug @@ -0,0 +1,25 @@ +extends base.pug + +block head + style(type="text/css"). + #error { + font-size: 3.5rem; + line-height: 5rem; + text-align: center; + margin: 6rem 0; } + + #error h1 { font-size: 8rem; } + +block nav + nav + a(href='/') Home + a(href='/stuff') Stuff + a(href='/mindflow') Mindflow + a(href='/about') About + a(href='/guestbook') Guestbook + h1 Watcha doin' here? :^) + +block content + section#error + h1 404 + | Page Not Found \ No newline at end of file diff --git a/views/dwelling/500.pug b/views/dwelling/500.pug new file mode 100644 index 0000000..8f7782d --- /dev/null +++ b/views/dwelling/500.pug @@ -0,0 +1,25 @@ +extends base.pug + +block head + style(type="text/css"). + #error { + font-size: 3.5rem; + line-height: 5rem; + text-align: center; + margin: 6rem 0; } + + #error h1 { font-size: 8rem; } + +block nav + nav + a(href='/') Home + a(href='/stuff') Stuff + a(href='/mindflow') Mindflow + a(href='/about') About + a(href='/guestbook') Guestbook + h1 Ohh... Shite! + +block content + section#error + h1 50x + | Internal Server Error \ No newline at end of file diff --git a/views/dwelling/about.pug b/views/dwelling/about.pug new file mode 100644 index 0000000..1560403 --- /dev/null +++ b/views/dwelling/about.pug @@ -0,0 +1,209 @@ +extends base.pug + +block nav + nav + a(href='/') Home + a(href='/stuff') Stuff + a(href='/mindflow') Mindflow + a(href='/guestbook') Guestbook + h1 About + +mixin isServiceUp(service) + if (services[service]) + p Service is running. + else + p Service is down. + +block content + section#about-me + h2 Me + p #[span.highlighted Who am I?] I'm a russian guy who likes tinkering with computers. Some more on me you can read on #[a(href='https://arav.neocities.org/about.html') neocities' page]. + p #[span.highlighted Why am I doing all of this?] I run my machines 24/7 anyway, so why not make them do some useful work that they can handle. Such as simple radio, file share via HTTP and FTP, Tor relay and I2P node, e-mail server, web and so on. + p #[span.highlighted Why am I almost “doxxing” myself?] Yes, since I host straight from my home you can get my city, but so can I do with you (if you don't use VPN or Tor :)). I don't think that's a problem. What's more important is that my data belongs only to me and I can access it from anywhere. :) That was a main goal of having a home server. + p But in addition I have my age, name and surname disclosed! Not a big deal for me as well. :) + p #[span.highlighted Why particularly that slogan?] I just wanted sort of a slogan that'd fit under the logo and that was the first thing came in my mind. And eventually it became permanent as everything that was ever meant to be temporary. :) I actually like it. Don't you think of your websites as of very special place of your own? Of course you do. :) And who we are if not the wanderers that are wandering through the Intenets in searching for something we'd like? xD + p #[span.highlighted Why English only?] I want this place to be accessible by wide audience, and English happen to be todays lingua franca. Second, I do that to practice English. And I'm lazy to maintain two versions of the same thing. + p P.S.: Yeah, I'm not so glad that this site partially repeats neocities' one. + p Contact information and banners are #[a(href='#contacts') at the bottom of this page]. Yes, I totally agree with you not wanting to add my banner since I don't have a link page myself. :) Well, it's way easier to make a banner than a links page. :) + section#servers-summary + h2 My servers + p I have two of them, the first one is a #[a(href='https://www.raspberrypi.org/products/raspberry-pi-3-model-b/' rel='nofollow noreferrer') Raspberry Pi 3 rev. B] single board computer and the second one is a laptop. Yeah, not quite impressive, but they do their work just fine. + p Laptop is Acer Packard Bell TE69CX that has a 2 core Intel Pentium 2117U 1.8GHz and 10GB RAM (2GB + 8GB), the system disk is a 120GB SSD and the other one is 500GB HDD where all the shit I have is stored. I'm fine with that storage, yes I'd like to have more and mirrored in RAID 1, but that's fine. Everything that is just a bit important is backed up, encrypted and stored in the clouds. :) + p #[span.highlighted TL;DR about services.] Laptop runs a network file share, a seedbox, a FTP/HTTP file share, TeamSpeak 3, Mumble, Matrix, I2P, an internet-radio, git server, ClamAV for email server, a web-server, and a database. Raspberry Pi 3 runs OpenVPN, BIND9, DNSCrypt-proxy, a Tor relay, an email server, and a XMPP server. + section#servers-photos + div.grid.figs + figure + a(href='/assets/img/raspi.webp') + img(src='/assets/img/raspi_thumb.webp' title='Patch cord\'s tongue is broken. :(' alt='Raspberry Pi 3 rev. B') + figcaption Raspberry Pi 3 rev. B + figure + a(href='/assets/img/acer.webp') + img(src='/assets/img/acer_thumb.webp' title='Screen and keyboard are broken anyway.' alt='Laptop') + figcaption Acer Packard Bell TE69CX + section#servers-public-services + h2 Public services + p.center That are available for everyone. + div.grid + div + h3 Internet-radio + +isServiceUp("liquidsoap") + p + a(href='https://radio.arav.top') radio.arav.top + | . + a(href='http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion/radio/') onion + | . + a(href='http://t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p/radio/') i2p + p Runs using Liquidsoap through Icecast, which goes through NGiNX. I broadcast almost everything I have. Sorry, but I'm too lazy to make some kind of broadcasting program to stream so I just randomise the playlist and throw new music there from time to time. + div + h3 FTP and HTTP file share + +isServiceUp("vsftpd") + p + a(href='ftp://anonymous@files.arav.top') ftp + | :// + a(href='https://files.arav.top') files.arav.top + | . + a(href='http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion/files/') onion + | . + a(href='http://t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p/files/') i2p + p vsFTPd. And now available through HTTP. + p There you'll find music, videos, films, animes, TV shows, books, games, programs, drivers (a very few), OS images (Winblows :)). Go check it out! + div + h3 Git + +isServiceUp("gitea") + p #[a(href='https://git.arav.top') git.arav.top] + p Gitea. This is a place where I put source code of everything I ever wrote and consider useful and worth sharing. + div + h3 Tor relay + p #[a(href='https://metrics.torproject.org/rs.html#details/CEF2FD0E1973EA04D1444DDAEFF1B0BC3C0C39B1' rel='nofollow noreferrer') metrics.torproject.org] + p It is set to use 8 MBits of my Internet connection. It cannot became a guard node because of dynamic IP-address that changes every 10 days. + div + h3 I2P router + p I help the network using an official #[a(href='https://geti2p.net' rel='nofollow noreferrer') I2P router]. + section#servers-private-services + h2 Private services + p.center That are available for a narrow circle of people. + div.grid + div + h3 E-Mail server + +isServiceUp("postfix") + p Postfix, Dovecot (w/Sieve), Spamassassin, ClamAV, OpenDMARC and OpenDKIM. + p Alas, I don't have a static IP. And there is not much sense in it because my ISP doesn't offer reverse DNS for regular clients. + p It results in my messages ends up in a spam box at best. Well, Yandex lets me in an Inbox. :) + div + h3 XMPP/Jabber + +isServiceUp("prosody") + p Prosody. Only I can register for security's sake. + p Think on shutting it down, because no one of my friends uses it anyway. + div + h3 Matrix + +isServiceUp("synapse.app") + p #[a(href='https://the.arav.top') the.arav.top] + p Synapse. Think on dumping XMPP in favour of it. + div + h3 Mumble + +isServiceUp("murmurd") + p #[a(href="mumble://arav.top") mumble://arav.top].#[a(href="mumble://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion") onion] + p It's private, but you still can join an entrance room where you can only write messages. A certificate is necessary. + div + h3 TeamSpeak 3 + +isServiceUp("ts3server") + p #[a(href="ts3server://arav.top") ts3server://arav.top] + p Same as with Mumble applies to TS. It's security level is set to 34, so is yours identifier's level should be at least that high. + div + h3 Game servers + p All game servers are private, if there's exception I'll mention it in its description. They are listed in a section below. + p Some servers are listed in an in-game server list and theirs names looks like “Arav's dwelling / <Game name>”. + p And, of course, I don't run them simultaneously and start them only when needed. + p Expect poor performance because the server is a low-end laptop. + section#servers-games + h2 Game servers + div.grid + div + h3 Minecraft + +isServiceUp("forge-1.16.4") + p.highlighted arav.top:25565 + p Version is 1.16.4 currently. I homebrew clients for it. Here is #[a(href="https://files.arav.top/games/minecraft/aravsdwelling-minecraft-1.16.4.exe") an installer for Windows] I make with NSIS. And a #[a(href="https://files.arav.top/games/minecraft/aravsdwelling-minecraft-1.16.4.zip") ZIP archive] that is for both Windows and Linux. You'll need to set a PLAYER and MC_DIR variables in scripts. And change JVM_PARAMS to your preferences (-Xmx usually needs to be adjusted). + p And you can get #[a(href="https://files.arav.top/programs/windows/installators/jre-8u281-windows-x64.exe") jre-8u281-windows-x64.exe] from my file share. + p SHA-1 hash for a Windows installer is D9D97BA99C74176D47C9E163A0E6B723145E5040. + p SHA-1 hash for a ZIP archive is F743FEB9CF8F6C7A0477904C33CD65163E20E049. + div + h3 Starbound + +isServiceUp("starbound_server") + p.highlighted arav.top:21065 + p Server is using a Frackin' Universe mod. + p Don't forget to set “Allow assets mismatch” option that you can find on the first page of the “Options” menu. + p Access is restricted using accounts. Contact me if you want to play here, you'll need to give me a nickname and a password. + div + h3 Avorion + +isServiceUp("AvorionServer") + p Server is listed in a server list as “Arav's dwelling / Avorion”. Whitelist enabled. Bring your SteamID64 if you want to play here. + div + h3 Terraria + p.highlighted arav.top:7777 + p TShock. But it's not used. Just wanted to look at TShock. + section#servers-inner-services + h2 Inner services + p.center Services that maintains other services or used exclusively by me. + div.grid + div + h3 Web-server + p NGiNX + PHP-FPM. + div + h3 Database server + p MariaDB. + div + h3 VPN + p OpenVPN. + div + h3 Network file share + p Samba and NFS. + div + h3 Torrent seedbox + p Transmission-cli gets shit done. + p The only annoying thing is that it sometimes creates .part files for one of unchecked files. + div + h3 Print server + p CUPS with CCP (Canon CAPT printer). + div + h3 DNS server + p At first I wanted to learn more about DNS and now BIND9 serves a DNS cache and domain zone for my LAN. I also use DNSCrypt-proxy to encrypt DNS traffic and added #[a(href="https://opennic.org" rel="nofollow noreferrer") OpenNIC] records so I have access to that domains. + section#contacts + h2 Contacts + table + tr + td E-Mail: + td #[a(href="mailto:me@arav.top") me@arav.top] + tr + td Jabber: + td #[a(href="xmpp://arav@arav.top") arav@arav.top] + tr + td Matrix: + td #[a(href="https://matrix.to/#/@arav:arav.top") @arav:arav.top] + tr + td PGP key: + td #[a(href="~arav/5A2ECA58F93EB5C44F02D50B1327FE8A374CC86F.asc") 5A2E CA58 F93E B5C4 4F02 D50B 1327 FE8A 374C C86F] (available through #[a(href="https://wiki.gnupg.org/WKD" rel="nofollow noreferrer") Web Key Directory]) + tr + td Neocities: + td #[a(href="https://arav.neocities.org") arav.neocities.org] + tr + td Git: + td #[a(href="https://git.arav.top/Arav") git.arav.top] + section#banners + h2 Banners + p In case you found my site anyhow useful and like to add me to your links page here are the banners for you. + div.grid.banners + figure + img(src="/assets/img/banner_88x31.gif" alt="Light banner 88x31") + figure + img(src="/assets/img/banner_240x60.gif" alt="Light banner 240x60") + p And theirs dark counterparts are also available. + div.grid.banners + figure + img(src="/assets/img/banner_dark_88x31.gif" alt="Dark banner 88x31") + figure + img(src="/assets/img/banner_dark_240x60.gif" alt="Dark banner 240x60") + section#privacy-statements + h2 Privacy statements + p I respect your privacy as my own, and now I got rid of any JavaScript code, and now theme is switched based on your OS's preference using media query. + p This website collects access logs which includes a date and time of access, your IP-address, User-Agent, referer URL that tells me where have you came from and a request itself. Additionally there is a GeoIP information being stored that is gathered from your IP-address for convenience of logs analysing. It includes country, region, and city. + p FTP server also collects access logs which includes a date and time of access, your IP-address and the commands sent. \ No newline at end of file diff --git a/views/dwelling/articles/article.pug b/views/dwelling/articles/article.pug new file mode 100644 index 0000000..fa9905c --- /dev/null +++ b/views/dwelling/articles/article.pug @@ -0,0 +1,17 @@ +extends ../base.pug + +block head + link(href='/assets/css/articles.css' rel='stylesheet') + +block nav + nav + a(href='/') Home + a(href='/stuff') Stuff + a(href='/mindflow') Mindflow + a(href='/about') About + a(href='/guestbook') Guestbook + h1 Articles + +block content + article + block article \ No newline at end of file diff --git a/views/dwelling/articles/nginx_recipes_and_tips.pug b/views/dwelling/articles/nginx_recipes_and_tips.pug new file mode 100644 index 0000000..229a7e9 --- /dev/null +++ b/views/dwelling/articles/nginx_recipes_and_tips.pug @@ -0,0 +1,33 @@ +extends article.pug + +block article + header + h2 NGiNX's recipes & tips + div.menu + a(href='/stuff#articles') Go back to articles list + time(datetime='2020-12-04') 4 December 2020 + nav + h3 Contents + ol + li #[a(href='#art-1') Introduction] + li #[a(href='#art-2') Userdir functionality like Apache's mod_userdir] + li #[a(href='#art-3') Note on how NGiNX works with HTTP headers] + + h3#art-1 1. Introduction + p Here I'll place recipes for implementing different functionalities and notes on webserver's behaviour. + + h3#art-2 2. Userdir functionality like Apache's mod_userdir + p Once I wanted to have a “tilde user directories” like #[code /~user/] which is more known as Apache's #[code mod_userdir] feature. I work with NGiNX so regular expressions is the way to do that. + p Nothing special in my case. I only need to keep files there, so others may get them. Thing is simple, but I took some time to realise what regexp to use here back then. + p The implementation is quite simple: + pre + | location ~ ^/~(.+?)(/.*)?$ { + | alias /home/$1/pub$2; + | autoindex on; + | } + p Of course, you can choose whatever place for user's public directories. In my case you need to give a read and execute permissions to user's home directory and to public directories inside them to others (#[code chmod o=rX /home/user]), which may be a security concern. + p #[code autoindex on] will make an index of files that lies by URL. And, of course, you can put there an #[code index.html] file. + + h3#art-3 3. Note on how NGiNX works with HTTP headers + p Let's say in #[code http] block you specified common headers like #[code X-Frame-Options], #[code X-XSS-Protection], and so on for all #[code server] directives to use. But, if you add some other header for a specific #[code server] or #[code location] block then all those headers would be dropped. + p For now the only cure for it is to place all that headers in a separate file like #[code common-headers.inc] and using #[code include] directive to include them in all the #[code server] and #[code location] blocks where additional headers are added. diff --git a/views/dwelling/articles/rpi_root_on_external_drive.pug b/views/dwelling/articles/rpi_root_on_external_drive.pug new file mode 100644 index 0000000..91aacc9 --- /dev/null +++ b/views/dwelling/articles/rpi_root_on_external_drive.pug @@ -0,0 +1,28 @@ +extends article.pug + +block article + header + h2 How to move a root from SD card to external drive on Raspberry Pi + div.menu + a(href='/stuff#articles') Go back to articles list + time(datetime='2020-08-15') 15 August 2020 + nav + h3 Contents + ol + li #[a(href='#art-1') Introduction] + li #[a(href='#art-2') Moving / to an external drive] + ol + li #[a(href='#art-2-1') If you're doing a fresh install] + li #[a(href='#art-3') cmdline.txt] + + h3#art-1 1. Introduction + p Raspberry Pi is known for how it wears off the SD cards. Thankfully you can move the root off the SD card to an external drive and leave there just a boot partition. I'm gonna use a #[s SystemD]GNU/Linux distribution for that. + + h3#art-2 2. Moving / to an external drive + p I assume that you have your drive partitioned already. Create the temporary directories for a SD card's boot and root partitions and for a drive. Mount the partitions and issue a command #[code cp -a path/to/sdcard/root/* path/to/drive]. #[code -a] stands for archive. Which recursively copies everything preserving all the attributes and links. + + h3#art-2-1 2.1. If you're doing a fresh install + p Then you just install it right on a drive. And then move files from #[code /boot] to a SD card's boot partition. Like that: #[code mv path/to/boot/* path/to/sdcard]. Keep in mind that the boot partition should be formatted in FAT32. + + h3#art-3 3. cmdline.txt + p In a #[code cmdline.txt.] file we need to change #[code root=] part. Now it'll look like #[code root=/dev/mmcblk0p2 rw rootwait], and we need to put our drive's root partition instead of that, e.g. #[code root=/dev/sda1 rw rootwait]. diff --git a/views/dwelling/articles/setting_up_a_mail_server.pug b/views/dwelling/articles/setting_up_a_mail_server.pug new file mode 100644 index 0000000..8bd8c84 --- /dev/null +++ b/views/dwelling/articles/setting_up_a_mail_server.pug @@ -0,0 +1,264 @@ +extends article.pug + +block article + header + h2 Setting up a mail server (WIP) + div.menu + a(href='/stuff#articles') Go back to articles list + time(datetime='2020-09-22') 22 September 2020 + nav + h3 Contents + ol + li #[a(href='#art-1') Introduction] + li #[a(href='#art-2') Installing] + li #[a(href='#art-3') Postfix SMTP server] + ol + li #[a(href='#art-3-1') main.cf] + li #[a(href='#art-3-2') master.cf] + li #[a(href='#art-3-3') User aliases] + li #[a(href='#art-3-4') Starting Postfix] + li #[a(href='#art-4') Dovecot POP3/IMAP server with Sieve mail filter] + li #[a(href='#art-5') SpamAssassin spam filter] + li #[a(href='#art-6') OpenDKIM signing and verifying filter] + ol + li #[a(href='#art-6-1') opendkim.conf] + li #[a(href='#art-6-2') Generating keys] + li #[a(href='#art-6-3') Populating KeyTable and SigningTable] + li #[a(href='#art-6-4') internal-hosts file] + li #[a(href='#art-6-5') Starting OpenDKIM] + li #[a(href='#art-7') OpenDMARC email policy filter] + li #[a(href='#art-8') DNS records] + ol + li #[a(href='#art-8-1') MX and A/AAAA] + li #[a(href='#art-8-2') PTR] + li #[a(href='#art-8-3') SPF] + li #[a(href='#art-8-4') DMARC] + li #[a(href='#art-8-5') DKIM] + li #[a(href='#art-9') Setting up a ClamAV antivirus] + + h3#art-1 1. Introduction + p I use Postfix as a SMTP and Dovecot (with Pigeonhole (Sieve)) as an IMAP server. ClamAV for an antivirus. For anti-spam I use SpamAssassin. For DKIM and DMARC — OpenDKIM and OpenDMARC respectively. I could use rspamd instead of the latter three, but it doesn't work on Raspberry Pi. + p It is vital to make the DKIM, DMARC and SPF DNS records. Also, if you want your mail server to be trusted by every other mail servers then you should get a static IP-address if you don't yet. And you have to ask your ISP to edit PTR DNS record for your static IP-address to point to your domain. + p Unfortunately for me I don't have neither, and I'm afraid that even if I get the static IP-address, my ISP won't edit PTR record, because that's available only for bussiness customers. + p Server is configured in a simple way using PAM (real system users) with user's passwords and with mail stored in ~/Maildir. + + h3#art-2 2. Installing + p You need to install following packages: #[code postfix], #[code dovecot], #[code pidgeonhole] (or could be #[code dovecot-sieve]), #[code clamav], #[code opendkim], #[code opendmarc] and #[code spamassassin]. + + h3#art-3 3. Postfix SMTP server + p Its configuration files are in directory #[code /etc/postfix]. First we need to work with #[code main.cf] file. Then configure services in #[code master.cf]. Also I'll show you how to make aliases for users. + + h4#art-3-1 3.1. main.cf + p Set #[code myhostname] to a hostname of a server (e.g. #[code mail.example.org]). Set #[code mydomain] to your domain name (e.g. #[code example.org]). Set #[code myorigin] to #[code $mydomain] to set origin of mail being sent from your server. + p #[code mydestination] is a list of domains that are delivered through a local transport. If server should go outside then this parameter must include #[code $mydomain] alongside names for the local machine. E.g. #[code $myhostname, localhost, $mydomain, mail.$mydomain]. + p #[code local_recipient_maps] are lookup tables with all names and/or addresses of local recipients. In my case it set to #[code unix:passwd.byname $alias_maps]. + p I have #[code inet_interfaces = all] to listen on all the interfaces. + p In #[code mynetworks], as stated in a Postfix's manual, we specify a list of “trusted” clients that have more privileges than “strangers”. In particular, such clients are allowed to relay mail through Postfix. I have it set to localhost and my LAN. + p In #[code alias_maps] we specify a list of lookup tables that contain aliases for existing users. And in #[code alias_database] just add #[code $alias_maps]. #[code alias_database] is, as stated in a manual, separate because not all the tables specified with #[code $alias_maps] have to be local files. + p #[code recipient_delimeter = +]. Here we set a delimeter to a plus sign (that's just a usual practice that I obeyed). + p I use a Maildir-style mailboxes, so #[code home_mailbox] is set to #[code Maildir/] (slash is necessary). + p We use Dovecot, so #[code mailbox_transport] should be set to #[code lmtp:unix:private/dovecot-lmtp]. Here we point to where Dovecot LMTP server listens, in our case it is a UNIX-socket. + p Optionaly, you can set #[code inet_protocols] to IP versions used by you, I set it just to #[code ipv4] for a quite legitimate reason of not having IPv6 address. :) + p Here are all the modified parameters listed: + pre + | myhostname = mail.example.org + | mydomain = example.org + | myorigin = $mydomain + | + | inet_interfaces = all + | + | mydestination = $myhostname, localhost, $mydomain, mail.$mydomain + | + | local_recipient_maps = unix:passwd.byname $alias_maps + | + | mynetworks = localhost, 192.168.0.0/24 + + | + | alias_maps = hash:/etc/postfix/aliases + | alias_database = $alias_maps + | + | recipient_delimiter = + + | + | home_mailbox = Maildir/ + | + | mailbox_transport = lmtp:unix:private/dovecot-lmtp + | + | inet_protocols = ipv4 + p Next I'll cover how to make encryption working, set up milters (mail filters (i.e. OpenDKIM and OpenDMARC)), and restrictions. + + h4#art-3-2 3.2. master.cf + p Here are all needed lines to be added or modified: + pre + | smtp inet n - n - - smtpd + | -o content_filter=spamassassin + | submission inet n - n - - smtpd + | -o syslog_name=postfix/submission + | -o smtpd_tls_security_level=encrypt + | -o smtpd_sasl_auth_enable=yes + | -o smtpd_tls_auth_only=yes + | smtps inet n - n - - smtpd + | -o content_filter=spamassassin + | -o syslog_name=postfix/smtps + | -o smtpd_tls_wrappermode=yes + | -o smtpd_sasl_auth_enable=yes + | + | spamassassin unix - n n - - pipe + | user=spamd argv=/bin/vendor_perl/spamc + | -e /sbin/sendmail -oi -f ${sender} ${recipient} + + h4#art-3-3 3.3. User aliases + p User aliases are in #[code aliases] file. They has a form "#[code <alias>: <username>]", e.g. #[code me: arav]. Where #[code username] may be other alias. After modifications you need to run #[code newaliases] program to update #[code aliases.db] database file. + + h4#art-3-4 3.4. Starting Postfix + p To start a Postfix service on systemd-based Linux distro run #[code systemctl start postfix]. To make Postfix run on every boot run #[code systemctl enable postfix]. + + + h3#art-4 4. Dovecot POP3/IMAP server with Sieve mail filter + + h3#art-5 5. SpamAssassin spam filter + + h3#art-6 6. OpenDKIM signing and verifying filter + p On ArchLinux OpenDKIM is unable to write in #[code /run], so I created #[code /var/spool/opendkim] directory for it. + + h4#art-6-1 6.1. opendkim.conf + p Well, that's main config file + pre + | KeyTable refile:/etc/opendkim/keytable + | SigningTable refile:/etc/opendkim/signingtable + | InternalHosts refile:/etc/opendkim/internal-hosts + | + | Socket local:/var/spool/opendkim/opendkim.sock + | PidFile /var/spool/opendkim/opendkim.pid + | UMask 000 + | UserID opendkim:opendkim + | + | Mode sv + | SubDomains yes + | + | Canonicalization relaxed/simple + | + | Syslog yes + | SyslogSuccess yes + | LogWhy yes + | + | SoftwareHeader yes + + p I myself set up a multi-domain variant just in case. So, here we have two main tables: #[code KeyTable] and #[code SigningTable]. Those files tells OpenDKIM where to find keys and what domains to sign. You may use one key for all domains or generate keys for each domain. + p #[code InternalHosts] tells OpenDKIM what hosts should be signed rather than verified. + p #[code Socket] tells where to listen to connections, in this case we use UNIX sockets. + p #[code Mode] selects operating mode(s). In our case we have two modes: (s)igner and (v)erifier. + p #[code SubDomains] set to yes tells that we allow subdomains of our domains to be signed and verified. + p #[code Canonicalization] selects the canonicalization method(s) to be used with signing. We set relaxed for header and simple for body. I don't fully understand it and just use what suggested. + p Below are logging options that tells to write in syslog. + p With #[code SoftwareHeader] set to yes OpenDKIM will be always adding "DKIM-Filter" header field. + + h4#art-6-2 6.2. Generating keys + pre + | opendkim-genkey -r -s myselector -b 2048 -d example.com + p This command will generate a key pair stored in files "myselector.private" and "myselector.txt" for a given domain. + p #[code -r] restricts the key to emails use only. #[code -s] is a name of selector.#[code -b] is the size of the key in bits. #[code -d] is our domain. + p Name of a selector is usually a #[code mail], but that's just what I use, you can choose whatever you want. + + + h4#art-6-3 6.3. Populating KeyTable and SigningTable + p KeyTable has following structure (a line per domain): + pre + | myselector._domainkey.example.com example.com:myselector:/etc/opendkim/myselector.private + p And SigningTable this one: + pre + | *@example.com myselector._domainkey.example.com + + h4#art-6-4 6.4. internal-hosts file + p As stated above in this file we put hosts whose mail should be signed rather than verified. And its structure is the following: + pre + | 127.0.0.1 + | 192.168.0.0/24 + p #[code 127.0.0.1] is necessary to be there according to a manual. + + + h4#art-6-5 6.5. Starting OpenDKIM + p #[code systemctl start opendkim] and #[code systemctl enable opendkim] to start and enable OpenDKIM service to run on OS start up if you got Poetteringed just like me. :) + + h3#art-7 7. OpenDMARC email policy filter + p Its configuration lies in #[code /etc/opendmarc/opendmarc.conf] and is fully documented. Here are the options I changed: + pre + | AuthservID OpenDMARC + | FailureReports true + | FailureReportsBcc admin@example.org + | FailureReportsSentBy admin@example.org + | IgnoreAuthenticatedClient yes + | RejectFailures true + | RequiredHeaders yes + | Socket unix:/var/spool/opendmarc/opendmarc.sock + | SoftwareHeader true + | SPFSelfValidate true + | Syslog true + | TrustedAuthservIDs mail.example.org,example.org + | UMask 002 + p What's in a #[code Socket] option should be added to Postfix's #[code smtpd_milters] and #[code non_smtpd_milters]. + p Creating DMARC DNS record covered in 7.4. + + h3#art-8 8. DNS records + + h4#art-8-1 8.1. MX and A/AAAA + p It's good to have a dedicated A (IPv4 address) or AAAA (IPv6 address) record for a mail server's hostname instead of a CNAME record so other servers won't need to do two DNS requests. Hostname is usually mail.example.org if there's just one server, you can call it whatever you want. Remind you that we set it in Postfix in #[code myhostname] parameter. + p And A record looks like this: + pre + | mail  IN  86400  A  203.0.113.4 + p Where #[code mail] is a hostname, 86400 is a TTL of a record in seconds. + p Next we need to add a MX (mail exchanger) record that looks like this: + pre + |   MX 10 mail.example.org. + p Here 10 is a priority of a record. The lower a number the higher a priority. + p A period at the end of the hostnames is necessary in DNS records. + + h4#art-8-2 8.2. PTR + p PTR is a reverse DNS record that stands for pointer and is used to “bind” a hostname to IP-address. Mail servers looks for this record and check so this name equals to a hostname provided in EHLO. Most servers will reject your mail if your PTR looks something like 1.2.3.4.pppoe.someisp.net or not set at all. + p There are three ways to set this record: ask your hosting or internet-provider, or get your own Autonomous System (:^)). + p Example of this record: + pre + | 1 IN PTR mail.example.org. + + h4#art-8-3 8.3. SPF + p SPF stands for Sender Policy Framework and in my case it looks exactly like this: + pre + | v=spf1 +a +mx -all + p So, #[code v] is a version of a protocol. #[code +a +mx] means that only servers specified in the A and MX DNS records could send email, and #[code -all] that no one else could do that. + + h4#art-8-4 8.4. DMARC + p DMARC stands for Domain-based Message Authentication Reporting and Conformance. And its DNS record could be like this one that I use: + pre + | _dmarc IN TXT "v=DMARC1; p=reject; rua=mailto:admin@example.org; ruf=mailto:admin@example.org" + p #[code v] is a version of a protocol. + p #[code p] is a default policy that could be set to #[code none], #[code quarantine] and #[code reject]. I chose to #[code reject] mail that comes from «me” if there's something wrong with a origin of a message. If you could get email from subdomains then you need to set #[code sp] as well. + p #[code rua] is an address for the reports and #[code ruf] is for the forensic reports. + + h4#art-8-5 8.5. DKIM + p In 5.2 we generated a key pair for our domain and now we'll take what's inside a #[code myselector.txt] file and add it to our DNS. + p DKIM DNS record looks like this: + pre + | myselector._domainkey IN TXT ( "v=DKIMv1; k=rsa; s=email; p=<public key goes here>" ) + p By the way, brackets are used in case a content of a record doesn't fit on one line. + + h3#art-9 9. Setting up a ClamAV antivirus + p All you need to make it work together with Postfix is to add #[code /run/clamav/milter.sock] to #[code smtpd_milters] and #[code non_smtpd_milters] options in Postfix, also make some changes in configs of ClamAV. + p In #[code clamav-milter.conf] you need the following: + pre + | MilterSocket unix:/run/clamav/milter.sock + | ClamdSocket unix:/run/clamav/clamd.ctl + p Also, in case you need ClamAV to add headers also in case a message is free of viruses add #[code AddHeader Add] or #[code AddHeader Replace] option. The difference between them is detaily described in config file itself. + p Before starting ClamAV you need to update its virus definitions with #[code freshclam] util. Also, enable and start #[code clamav-freshclam] systemd service to keep definitions recent. + p I don't know how it is in other distros, but, for whatever reason, an Arch Linux's package doesn't have a systemd service file for the ClamAV milter. So I just copy it here from ArchWiki: + pre + | [Unit] + | Description='ClamAV Milter' + | After=clamav-daemon.service + | + | [Service] + | Type=forking + | ExecStart=/usr/bin/clamav-milter --config-file /etc/clamav/clamav-milter.conf + | + | [Install] + | WantedBy=multi-user.target + p Save it as #[code /usr/lib/systemd/system/clamav-milter.service] and run #[code systemctl daemon-reload]. + p Next you need to enable and start #[code clamav-daemon] and #[code clamav-milter]. \ No newline at end of file diff --git a/views/dwelling/articles/setting_up_a_tor_proxy_relay_hiddenserv.pug b/views/dwelling/articles/setting_up_a_tor_proxy_relay_hiddenserv.pug new file mode 100644 index 0000000..0402327 --- /dev/null +++ b/views/dwelling/articles/setting_up_a_tor_proxy_relay_hiddenserv.pug @@ -0,0 +1,108 @@ +extends article.pug + +block article + header + h2 Setting up a Tor proxy, relay and hidden service + div.menu + a(href='/stuff#articles') Go back to articles list + time(datetime='2020-08-15') 15 August 2020 + nav + h3 Contents + ol + li #[a(href='#art-1') Introduction] + li #[a(href='#art-2') Installation] + li #[a(href='#art-3') Configuration] + ol + li #[a(href='#art-3-1') Tor SOCKS/HTTP proxy] + li #[a(href='#art-3-2') Tor relay] + li #[a(href='#art-3-3') Hidden service] + li #[a(href='#art-4') Miscellaneous] + ol + li #[a(href='#art-4-1') Nyx — status monitor for Tor nodes] + li #[a(href='#art-4-2') Notes] + + h3#art-1 1. Introduction + p In this article I'll show you how to setup your own Tor proxy (SOCKS5 and HTTP), relay, and hidden service. + + h3#art-2 2. Installation + p Install #[code tor] package using your package manager. + p You definitely want to have control over your node and monitor it. For that purpose there is #[code nyx] tool. I'll cover it in 3.1 section. + p Any program can be passed through Tor using #[code torify] from #[code torsocks] package. + + h3#art-3 3. Configuration + p Tor is already bundled with a great documented #[code torrc-dist] file. You may just copy a #[code torrc-dist] file and name it as #[code torrc] and change what you need. + + h4#art-3-1 3.1. Tor SOCKS/HTTP proxy + p Here is an example of SOCKS/HTTP proxy settings: + pre + | SocksPort 192.168.0.100:9050, [ipv6 address]:9050 + | HTTPTunnelPort 192.168.0.100:8118, [ipv6 address]:8118 + | + | SocksPolicy accept 192.168.0.0/24 + | SocksPolicy accept6 [012:3fe1:337::]/48 + | SocksPolicy reject * + | + | BandwithRate 70MBits + | BandwithBurst 100MBits + | + | ExcludeNodes {ru}, {ua}, {by}, {kz}, {??} + | ExcludeExitNodes {ru}, {ua}, {by}, {kz}, {??} + p #[code SocksPort] assigns IP-address and port for SOCKS5 proxy to listen on. #[code HTTPTunnelPort] is for HTTP proxy. You may leave just port number to listen on all interfaces. + p #[code SocksPolicy] option has the form #[code accept|reject IP-subnet[, ...]]. It points which subnetwork is dis-/allowed to use SOCKS proxy, so there may be multiple entries. For example, #[code SocksPolicy accept 192.168.1.0/24] is allowing everyone from that network to use it. In order to forbid all other networks add #[code reject *] after all #[code SocksPolicy] entries. You may preffer to have them on separate lines for the sake of readability. + p #[code BandwithRate] and #[code BandwithBurst] set the average and maximum speed of incoming and outgoing connections for proxy. #[code BandwithBurst] must be greater or equal to #[code BandwithRate]. E.g. values may look like #[code 70MBits], #[code 10MBytes], #[code 5MB]. + p #[code HTTPTunnelPort] enables HTTP proxy, set it to desireable IP:Port. + p There are also #[code ExcludeNodes] and #[code ExcludeExitNodes] options that are the comma separated lists of forbidden nodes. There may be placed country codes, address patterns and identity fingerprints of nodes to never use in circuits. They are looking like #[code {ru}, {??}, 123.45.*]. I recommend to leave there at least #[code {??}] to forbid misconfigured nodes or nodes of an unknown origin, especially for exit nodes. + + h4#art-3-2 3.2. Tor relay + p Here is an example of relay settings. + pre + | ORPort 8443, [::]:8443 + | Nickname Anon + | Address example.org + | ContactInfo Anon <anon@example.org> + | + | RelayBandwithRate 8MBits + | RelayBandwithBurst 10MBits + | + | AccountingMax 6 GB + | # Every midnight. + | AccountingStart day 00:00 + | # Every 3rd day of month. + | AccountingStart month 3 00:00 + | + | # In case you don't want to be an exit node. + | ExitPolicy reject *:* + p To enable a Tor relay you need to set #[code ORPort] option. + p You should also provide some information. You have to set #[code Nickname] option that is a name of a relay. Also, optionally, yet recommended, you may fill #[code ContactInfo] option with your e-mail address. + p If you leave your e-mail address Tor weather service will send you a notification if your node goes down. + p You can also set #[code Address] option if you have a domain name or set it to your white IP-address. Otherwise, if you don't add it or comment out it, Tor will guess it. + p #[code RelayBandwithRate] and #[code RelayBandwithBurst] are the same as #[code BandwithRate] and #[code BandwithBurst] but for relay. + p Add #[code ExitPolicy reject *:*] to disable exit node if you don't need it. I found out that setting #[code ExitRelay] to 0 doesn't disable an exit node, so you must add rejecting policy. + p #[code AccountingMax] and #[code AccountingStart] are used to limit traffic for given period. + + h4#art-3-3 3.3. Hidden service + p There are two mandatory options to work with: #[code HiddenServiceDir] and #[code HiddenServicePort]. + p Of course, there are many other options, e.g. client authentication. But in simple case all you need are those two options above. + p Every hidden service starts with #[code HiddenServiceDir <path>] directory that contains public and secret keys, hostname for a hidden service and a directory called #[code authorized_clients] that stores info on all clients that are authorized to access this hidden service. + p And at least one #[code HiddenServicePort <tor port> <host:port of service>] sets port to listen to in Tor network as first parameter and the second one is a service that you want to give access from Tor to. E.g. #[code HiddenServicePort 80 localhost:8201] for webserver. + p It allows you to give access to many services by one Tor hostname. + p Example: + pre + | HiddenServiceDir /var/lib/tor/hidden-services/website + | HiddenServicePort 80 localhost:8201 + | HiddenServicePort 25 192.168.1.160:25 + + h3#art-4 4. Miscellaneous + + h4#art-4-1 4.1. Nyx — status monitor for Tor nodes + p You have to set #[code ControlPort] option to desired port, it will listen on localhost. If you need access from outside then set it to IP:9051. Also, you need to set #[code DisableDebuggerAttachment] option to 0, otherwise you'll not be able to use nyx. At least in my case nyx cannot connect with this option being set to 1. + p Example: + pre + | ControlPort 192.168.1.200:9051 + | DisableDebuggerAttachment 0 + p You may install it using package manager, but guaranteed last version can be installed from Python's #[code pip] package manager (#[code pip install nyx]). + + h4#art-4-2 4.2. Notes + p I hope you have a static IP-address or your IP changes once a month at least, otherwise you'll never become a guard (entry) node. + p An option #[code AvoidDiskWrites] is usefull in case you're running from SSD or SD card. + p You have to make a backup of #[code /var/lib/tor/keys] folder to save your node's cryptographic identity keys. They are used to identify your node. You can see stats on your node at #[a(href="https://metrics.torproject.org" rel="noopener noreferrer" target="_blank") metrics.torproject.org]. To find your node use what you put in #[code Nickname] parameter or a fingerprint that is shown in nyx. \ No newline at end of file diff --git a/views/dwelling/base.pug b/views/dwelling/base.pug new file mode 100644 index 0000000..eae4316 --- /dev/null +++ b/views/dwelling/base.pug @@ -0,0 +1,24 @@ +doctype html +html(lang='en') + head + title Arav's dwelling #{title} + meta(charset='utf-8') + meta(http-equiv='X-UA-Compatible' content='IE=edge') + meta(name='viewport' content='width=device-width, initial-scale=1.0') + meta(name='theme-color' content='#cd2682') + meta(name='description' content=description) + link(rel='icon' href='/shared/img/favicon_128.png' sizes='16x16 32x32 64x64 128x128' type='image/png') + link(rel='alternate' href='rss.xml' type='application/rss+xml' title='Arav\'s dwelling') + link(href='/assets/css/main.css' rel='stylesheet') + block head + body + header + svg#logo(viewBox='0 -25 216 40') + text.logo Arav's dwelling + text.under(y='11') Welcome to my sacred place, wanderer + block nav + block content + footer + a(href='rss.xml' title='Stay up to date on what\'s going on.') RSS feed + br + | 2017—2021 Arav <#[a(href='mailto:me@arav.top') me@arav.top]> \ No newline at end of file diff --git a/views/dwelling/guestbook.pug b/views/dwelling/guestbook.pug new file mode 100644 index 0000000..5233855 --- /dev/null +++ b/views/dwelling/guestbook.pug @@ -0,0 +1,52 @@ +extends base.pug + +block head + link(href='/assets/css/guestbook.css' rel='stylesheet') + +block nav + nav + a(href='/') Home + a(href='/stuff') Stuff + a(href='/mindflow') Mindflow + a(href='/about') About + h1 Guestbook + +block content + form#new-post(action='/guestbook', method='POST') + input(type='text' maxlength='80' placeholder='Name (Anonymous if left blank)' name='name') + input(type='text' maxlength='255' placeholder='E-Mail (optional)' name='email') + input(type='text' maxlength='255' placeholder='Website (optional)' name='website') + textarea(maxlength='4096' placeholder='Your message' name='message' required) + span.checkboxes + span.checkbox + input(type='checkbox' id='hide-email' name='hide_email' checked) + label(for='hide-email') Hide E-Mail* + span.checkbox + input(type='checkbox' id='hide-website' name='hide_website' checked) + label(for='hide-website') Hide website* + small *Only I can see if set. + input(type='submit' value='Send a post') + section#posts + if (posts) + each post in posts + article + header + | Posted by #[span.highlighted= post.name] #{post.email} #{post.website} on #[time(datetime=post.created)= date_(post.created)] + each line in post.message.split("\n") + p= line + if post.feedback + .feedback + header + | Feedback by #[span.highlighted #{owner}] on #[time(datetime=post.feedback_created)= date_(post.feedback_created)] + each line in post.feedback.split("\n") + p= line + else + if (error) + p.center Cannot establish database connection. + else + p.center No posts. + if pages_count > 1 + section#pagination + - let n = 1; + while n <= pages_count + a(href='/guestbook?p='+n)= n++ diff --git a/views/dwelling/index.pug b/views/dwelling/index.pug new file mode 100644 index 0000000..656fd63 --- /dev/null +++ b/views/dwelling/index.pug @@ -0,0 +1,34 @@ +extends base.pug + +block head + link(href='assets/css/index.css' rel='stylesheet') + +block nav + nav + a(href='/stuff') Stuff + a(href='/mindflow') Mindflow + a(href='/about') About + a(href='/guestbook') Guestbook + +block content + section#services + span + a(href='https://radio.arav.top') radio.arav.top + | . + a(href='http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion/radio/') onion + | . + a(href='http://t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p/radio/') i2p + span + a(href='ftp://anonymous@files.arav.top') ftp + | :// + a(href='https://files.arav.top') files.arav.top + | . + a(href='http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion/files/') onion + | . + a(href='http://t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p/files/') i2p + a(href='https://git.arav.top') git.arav.top + || + a(href='https://arav.neocities.org') arav.neocities.org + section + p Welcome, Anon. I'm Arav, I self-host some services for myself, friends and you. Not much I have to offer, but maybe you'll find something useful for yourself. :) + p Now this place is available through I2P and Tor. \ No newline at end of file diff --git a/views/dwelling/mindflow.pug b/views/dwelling/mindflow.pug new file mode 100644 index 0000000..c87f390 --- /dev/null +++ b/views/dwelling/mindflow.pug @@ -0,0 +1,44 @@ +extends base.pug + +block head + link(href='/assets/css/mindflow.css' rel='stylesheet') + +block nav + nav + a(href='/') Home + a(href='/stuff') Stuff + a(href='/about') About + a(href='/guestbook') Guestbook + h1 Mindflow + +block content + p.center Here I post updates on websites and infrastructure, my very important opinions and thoughts. If you'd like to subscribe to this bullshittery then #[a(href="rss.xml") RSS feed] at your service. :) + + section + h2 Updates + if (updates) + each update in updates + article(id='update-'+mindflowDateToId(update.date)) + header + a(href='#update-'+mindflowDateToId(update.date)) + h3= update.title + each line in update.body.split('\n') + p!= line + footer + time(datetime=update.date)= date_(update.date) + else + p.center No updates? There must be some... Looks like database isn't connected. + section + h2 Diary + if (diary) + each entry in diary + article(id='diary-'+mindflowDateToId(entry.date)) + header + a(href='#diary-'+mindflowDateToId(entry.date)) + h3= entry.title + each line in entry.body.split('\n') + p!= line + footer + time(datetime=entry.date)= date_(entry.date) + else + p.center No records? Well, that's definitely something's wrong happened to the database. :( diff --git a/views/dwelling/rss.pug b/views/dwelling/rss.pug new file mode 100644 index 0000000..649f743 --- /dev/null +++ b/views/dwelling/rss.pug @@ -0,0 +1,22 @@ +doctype xml + +rss(version='2.0') + channel + title Arav's dwelling + description Updates on my websites and infrastructure. + language en-gb + link #{protocol}://#{host}/ + + each item in items + item + title= item.title + category= item.category + guid!= rssLink(protocol, host, item.date, item.category.toLowerCase()) + pubDate= item.date + link!= rssLink(protocol, host, item.date, item.category.toLowerCase()) + author!= author + description + | \ No newline at end of file diff --git a/views/dwelling/stuff.pug b/views/dwelling/stuff.pug new file mode 100644 index 0000000..8a92652 --- /dev/null +++ b/views/dwelling/stuff.pug @@ -0,0 +1,86 @@ +extends base.pug + +block nav + nav + a(href='/') Home + a(href='/mindflow') Mindflow + a(href='/about') About + a(href='/guestbook') Guestbook + h1 Stuff + +block content + p.center Here lies everything I've made that I'm willing to share. + section#articles + h2 Articles + p These articles are more like the sysadmin's notes. I describe those parts here that I did myself. The date here represents when article was updated last time. + table + tr + td + time(datetime='2020-12-04') 4 December 2020 + td + a(href='stuff/article/nginx_recipes_and_tips') NGiNX's recipes & tips + tr + td + time(datetime='2020-09-22') 22 September 2020 (WIP) + td + a(href='stuff/article/setting_up_a_mail_server') Setting up a mail server + tr + td + time(datetime='2020-08-15') 15 August 2020 + td + a(href='stuff/article/setting_up_a_tor_proxy_relay_hiddenserv') Setting up a Tor proxy, relay and hidden service + tr + td + time(datetime='2020-08-15') 15 August 2020 + td + a(href='stuff/article/rpi_root_on_external_drive') How to move a root partition from SD card off to external drive on Raspberry Pi + section#programs-scripts + h2 Programs and scripts + p.center Simple, yet useful (at least for me) programs and scripts I made. Everything is licensed under MIT. + table + tr + td + td February 2021 + td Go + td + tr + td(colspan='4') + p I will soon release two tools written in Go. I just want to finish them and give them proper names. The one is a wrapper for nsupdate BIND9's utility used for updating DNS resource records through a POST query. And the other one is a tool for monitoring up/down status of a set of processes and giving it out in form of a JSON object. + tr + td PiggyBank + td 1.0.0 (8 July 2020) + td Python + td + a(href='http://git.arav.top/Arav/PiggyBank') source + | + a(href='http://git.arav.top/Arav/PiggyBank/releases') releases + tr + td(colspan='4') + p One of the most autistic shit I ever made. :) + p Many years ago I wrote a little script to keep track of my piggy bank to learn how to work with SQLite 3 in Python. + p Now I wanted to learn how to make packages that could be installed using pip. And I took that script and made a universal simple solution that can work with any currency (that script was nailed to rubles). I threw SQLite and made a simple text format. + tr + td ScrapTheChan + td 0.4.0 (18 November 2020) + td Python + td + a(href='http://git.arav.top/Arav/ScrapTheChan') source + | + a(href='http://git.arav.top/Arav/ScrapTheChan/releases') releases + tr + td(colspan='4') + p Imageboards file scraper using theirs JSON API. Currently supported: 4chan.org, lainchan.org, 2ch.hk and 8kun.top. + p Attention! Was made to the point it works. Lotta shitcode! + tr + td OpenNIC active domains extraction script + td 10 July 2020 + td Bash + td + a(href='http://git.arav.top/Arav/opennic-extract-domains') source + tr + td(colspan='4') + p Bash script for BIND9 that extracts domains from zone files of OpenNIC and forms a list of domains that have an IP-address. + section#music + h2 Music + p There was a period in my life when I was playing with audio sequencers. I lost all project files and only 3 tracks survived in mp3. You can get them here. I've maybe like 15-20-ish project files ever created and just like 5 of them was finished. + diff --git a/views/files/base.pug b/views/files/base.pug new file mode 100644 index 0000000..27d315e --- /dev/null +++ b/views/files/base.pug @@ -0,0 +1,21 @@ +doctype html +html(lang='en') + head + title Arav's dwelling #{title} + meta(charset='utf-8') + meta(http-equiv='X-UA-Compatible' content='IE=edge') + meta(name='viewport' content='width=device-width, initial-scale=1.0') + meta(name='theme-color' content='#cd2682') + meta(name='description' content=description) + link(rel='icon' href='/shared/img/favicon_128.png' sizes='16x16 32x32 64x64 128x128' type='image/png') + link(href='/assets/css/main.css' rel='stylesheet') + block head + body + header + svg#logo(viewBox='0 -25 216 40') + text.logo Arav's dwelling + text.under(y='11') Welcome to my sacred place, wanderer + block nav + block content + footer + | 2017—2021 Arav <#[a(href='mailto:me@arav.top') me@arav.top]> \ No newline at end of file diff --git a/views/files/index.pug b/views/files/index.pug new file mode 100644 index 0000000..df0d273 --- /dev/null +++ b/views/files/index.pug @@ -0,0 +1,24 @@ +extends base.pug + +block nav + nav + a(href=host) Back to main website + h1 Files + +block content + section#files + span#current-path!= current_path + table + thead + tr + th Name + th Date + th Size + tbody + tr + td #[a(href="../") ../] + each item in items + tr + td #[a(href=item.link)= item.name] + td!= item.datetime + td= item.size + ' ' + item.size_unit \ No newline at end of file diff --git a/views/radio/base.pug b/views/radio/base.pug new file mode 100644 index 0000000..27d315e --- /dev/null +++ b/views/radio/base.pug @@ -0,0 +1,21 @@ +doctype html +html(lang='en') + head + title Arav's dwelling #{title} + meta(charset='utf-8') + meta(http-equiv='X-UA-Compatible' content='IE=edge') + meta(name='viewport' content='width=device-width, initial-scale=1.0') + meta(name='theme-color' content='#cd2682') + meta(name='description' content=description) + link(rel='icon' href='/shared/img/favicon_128.png' sizes='16x16 32x32 64x64 128x128' type='image/png') + link(href='/assets/css/main.css' rel='stylesheet') + block head + body + header + svg#logo(viewBox='0 -25 216 40') + text.logo Arav's dwelling + text.under(y='11') Welcome to my sacred place, wanderer + block nav + block content + footer + | 2017—2021 Arav <#[a(href='mailto:me@arav.top') me@arav.top]> \ No newline at end of file diff --git a/views/radio/index.pug b/views/radio/index.pug new file mode 100644 index 0000000..53e9799 --- /dev/null +++ b/views/radio/index.pug @@ -0,0 +1,37 @@ +extends base.pug + +block head + script(src='/assets/js/main.js' defer) + +block nav + nav + a(href=host) Back to main website + h1 Radio + +block content + section + small.player-links + a(href="/filelist") file list + a(href="/playlist") playlist (.m3u) + a(href="/live/stream.ogg") direct link + a(href="http://radio.arav.top:8000/stream.ogg") direct link (http) + a(href="http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion/radio/live/stream.ogg") direct link (Tor) + a(href="http://t42fkp6zp5dfqywantq3zp427ig3q2onrmfv246tyaztpg4ckb5a.b32.i2p/radio/live/stream.ogg") direct link (I2P) + | OGG 128 Kb/s + audio(preload='none' controls) + source(src="live/stream.ogg" type="audio/ogg") + | Your browser doesn't support an audio element, it's sad... But you always can take the #[a(href="playlist.m3u") playlist]! + p #[span#radio-status n/a] + p Now playing: #[span#radio-song n/a] + p Current/peak listeners: #[span#radio-listeners n/a] / #[span#radio-listener-peak n/a] + p + small Notice: information updates every 45 seconds. But you can #[a(href="javascript:getRadioStats()") update] it forcibly. + section + h2 About the radio + p This project grew from MPD I used to stream music over LAN for myself. Then I let it out and placed a player on my neocities website. Then I added Icecast to see if there are listeners. I wasn't happy on how MPD was nearly overloading CPU, and later on, when I moved radio off to a laptop, I found a great tool for streams called Liquidsoap. + p Radio is also available through HTTP port 8000, but browsers may try to redirect to HTTPS. Any other applications should work fine. + p Radio is listed in #[a(href="https://dir.xiph.org/search?q=arav's+dwelling") Xiph directory]. + section + h2 Privacy statements + p I collect access logs that include access date and time, IP-address, User-Agent, referer URL that tells me where have you came from, request that you sent to me. In addition there are GeoIP information added based on your IP-address that includes country, region, and city for my convenience. + p This website makes use of JavaScript to show you information on a radio. \ No newline at end of file