Merge pull request 'node_migration' (#1) from node_migration into main
Reviewed-on: https://git.yarmo.eu/yarmo/yarmo.eu/pulls/1
87
.drone.yml
@ -1,87 +0,0 @@
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: deploy dev
|
||||
|
||||
steps:
|
||||
- name: composer
|
||||
image: composer
|
||||
commands:
|
||||
- composer install
|
||||
- composer run-script minifyCSS
|
||||
- name: rsync to prism
|
||||
image: drillster/drone-rsync
|
||||
settings:
|
||||
hosts:
|
||||
from_secret: ssh_host
|
||||
port:
|
||||
from_secret: ssh_port
|
||||
user:
|
||||
from_secret: ssh_user
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
source: ./
|
||||
target: ~/web/dev.yarmo.eu
|
||||
exclude: [ ".git/", ".gitignore", ".drone.yml", "composer.json", "composer.lock" ]
|
||||
- name: purge cache
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host:
|
||||
from_secret: ssh_host
|
||||
port:
|
||||
from_secret: ssh_port
|
||||
username:
|
||||
from_secret: ssh_user
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
script:
|
||||
- rm -rf web/dev.yarmo.eu/cache/*
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- dev
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: deploy prod
|
||||
|
||||
steps:
|
||||
- name: composer
|
||||
image: composer
|
||||
commands:
|
||||
- composer install
|
||||
- composer run-script minifyCSS
|
||||
- name: rsync to prism
|
||||
image: drillster/drone-rsync
|
||||
settings:
|
||||
hosts:
|
||||
from_secret: ssh_host
|
||||
port:
|
||||
from_secret: ssh_port
|
||||
user:
|
||||
from_secret: ssh_user
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
source: ./
|
||||
target: ~/web/yarmo.eu
|
||||
exclude: [ ".git/", ".gitignore", ".drone.yml", "composer.json", "composer.lock" ]
|
||||
- name: purge cache
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host:
|
||||
from_secret: ssh_host
|
||||
port:
|
||||
from_secret: ssh_port
|
||||
username:
|
||||
from_secret: ssh_user
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
script:
|
||||
- rm -rf web/yarmo.eu/cache/*
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
3
.gitignore
vendored
@ -1,3 +1,2 @@
|
||||
.well-known
|
||||
vendor
|
||||
cache
|
||||
node_modules
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"require": {
|
||||
"pug-php/pug": "^3.4",
|
||||
"altorouter/altorouter": "^2.0",
|
||||
"pagerange/metaparsedown": "^1.0",
|
||||
"bhaktaraz/php-rss-generator": "dev-master",
|
||||
"symfony/yaml": "^5.1",
|
||||
"tubalmartin/cssmin": "^4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"minifyCSS": [
|
||||
"php scripts/minifyCSS.php"
|
||||
]
|
||||
}
|
||||
}
|
1023
composer.lock
generated
@ -70,7 +70,7 @@ In other words, VPSs are severely lacking in sources of entropy. That is why the
|
||||
|
||||
There is a way to remedy the situation: [haveged](https://wiki.archlinux.org/index.php/Haveged). Having only discovered it last night, I do not fully understand it yet but from what I have read, it is a pseudorandom number generator (PRNG) that fills the `entropy pool` with "pseudorandomness". Installing `haveged` immediately solved my issue, all docker commands were running instantly again.
|
||||
|
||||

|
||||

|
||||
*Can you tell when I installed haveged?*
|
||||
|
||||
## Caveat: pseudorandomness
|
||||
|
@ -28,13 +28,13 @@ I decided to use [WebPageTest.org](https://www.webpagetest.org) to measure load
|
||||
|
||||
First, a baseline measurement of my existing Cloudways solution.
|
||||
|
||||

|
||||

|
||||
*Cloudways - overview*
|
||||
|
||||

|
||||

|
||||
*Cloudways - rating*
|
||||
|
||||

|
||||

|
||||
*Cloudways - waterfall*
|
||||
|
||||
So the server returns the first byte of information after 480 milliseconds. Now, I should tell you that my website is based on [Phug](https://phug-lang.com), the PHP port of [pug.js templating](https://pugjs.org). The page is rendered in real-time and apparently, that takes a little over 300 ms.
|
||||
@ -53,13 +53,13 @@ Anyway, can Caddy do better?
|
||||
|
||||
### Caddy
|
||||
|
||||

|
||||

|
||||
*Caddy - overview*
|
||||
|
||||

|
||||

|
||||
*Caddy - rating*
|
||||
|
||||

|
||||

|
||||
*Caddy - waterfall*
|
||||
|
||||
Well, as it turns out, it's largely the same performance. First byte arrived after 459 ms, but I've ran it a few times and there's really little difference between Cloudways and Caddy.
|
||||
@ -76,13 +76,13 @@ I've tried a lot of things, I'll just narrow it down to the two most important f
|
||||
|
||||
As it turned out, I had a few small SVG icons and some CSS files. I tried rendering them into the HTML page, so the data would be sent on the first data transmission and no separate requests were needed. For good measure, I also minified the CSS files which, for one file, reduced the size by 30%!
|
||||
|
||||

|
||||

|
||||
*Caddy+inline - overview*
|
||||
|
||||

|
||||

|
||||
*Caddy+inline - rating*
|
||||
|
||||

|
||||

|
||||
*Caddy+inline - waterfall*
|
||||
|
||||
On the waterfall above, you can clearly see the `dank-mono.css` was not inlined but I tried multiple configurations, there was no real gain as the image also needed to load and took longer anyway. So, all in all, inlining the SVG and CSS content did little in this case.
|
||||
@ -97,13 +97,13 @@ As described on [their website](https://phug-lang.com/#usage), PHUG has support
|
||||
|
||||
### Caddy - PHUG optimization
|
||||
|
||||

|
||||

|
||||
*Caddy+PHUG - overview*
|
||||
|
||||

|
||||

|
||||
*Caddy+PHUG - rating*
|
||||
|
||||

|
||||

|
||||
*Caddy+PHUG - waterfall*
|
||||
|
||||
Well, there it is!!! First byte of data arrived after a mere 173 ms, website is useable in less than half a second and all scores are `A`!
|
||||
|
@ -71,7 +71,7 @@ I have bought .io domains in the past. I did not have the knowledge of what was
|
||||
|
||||
---
|
||||
|
||||
## Update 1<a name="update-1"></a>
|
||||
## Update 1
|
||||
|
||||
There is also the issue of the **.io** TLD's [future](https://www.prolificlondon.co.uk/marketing-tech-news/tech-news/2019/05/future-popular-io-domains-question-over-british-empire-row):
|
||||
|
||||
@ -81,7 +81,7 @@ Who knows what will happen to your domain registration when control is passed to
|
||||
|
||||
---
|
||||
|
||||
## Update 2<a name="update-2"></a>
|
||||
## Update 2
|
||||
|
||||
It has been pointed out by many that this post focuses too much on the **.xyz** gTLD. This was not my intention. In fact, any gTLD will do just fine, after all they are generic. A non-exhaustive list of gTLDs that could perfectly replace **.io** (assuming **.io** simply stands for "input/output"):
|
||||
|
||||
|
@ -26,19 +26,19 @@ When you load that specific page, make sure to load it in a private session or w
|
||||
|
||||
It is slow. Really slow. I noticed so too and decided to run a [Webpagetest (link to result)](https://www.webpagetest.org/result/200627_0Q_044080ef3ab8a678721658c90d2f4706/). Out of three runs, we analyze only the median run (so not the best one, not the worst one).
|
||||
|
||||

|
||||

|
||||
*keybase.io/encrypt*
|
||||
|
||||
## The content loaded
|
||||
|
||||
It takes **6.25 seconds** to fully load the **2.9 megabytes** that are used on this page. That is hefty for a page that is essentially a single form. I mean, look at it:
|
||||
|
||||

|
||||

|
||||
*Why 2.9 megabytes?*
|
||||
|
||||
That's a regular web form. What could possibly be **2.9 megabytes**? The javascript?
|
||||
|
||||

|
||||

|
||||
*How many requests? How many bytes?*
|
||||
|
||||
Most requests are fonts. That makes sense. Earlier, we saw the page only makes **12 requests**, so I could imagine a few of those being several fonts files. Fortunately, fonts are only **6.5%** of the bytes loaded, so we'll forgive them.
|
||||
@ -49,7 +49,7 @@ Most requests are fonts. That makes sense. Earlier, we saw the page only makes *
|
||||
|
||||
Let's grab the [waterfall](https://www.webpagetest.org/result/200627_0Q_044080ef3ab8a678721658c90d2f4706/1/details/#waterfall_view_step1) and see what is going on:
|
||||
|
||||

|
||||

|
||||
*Run 1 waterfall*
|
||||
|
||||
At two points in time, the loading of the website stalls. The first stall is **2.6 seconds** for the file `sitewide-js.js`. The second stall is **2.5 seconds** for the file `footprints_transp.png`. Let's go.
|
||||
@ -58,7 +58,7 @@ At two points in time, the loading of the website stalls. The first stall is **2
|
||||
|
||||
This file is **4.7 megabytes** raw and **1.2 megabytes** gzipped. Let us look at a random excerpt:
|
||||
|
||||

|
||||

|
||||
*Javascript excerpt*
|
||||
|
||||
This is not really optimized for performance: one could choose to minimize the javascript. Allow me to use `@node-minify/cli`.
|
||||
@ -78,7 +78,7 @@ Well, I need to specify one thing: the website loads a gzipped version of the or
|
||||
|
||||
Have you found the image yet? It's the little image at the bottom of the dog (?) following footprints. Cute :)
|
||||
|
||||

|
||||

|
||||
*Footprints image*
|
||||
|
||||
Dimensions on page: **330 x 90 pixels**
|
||||
@ -122,7 +122,7 @@ Anything else? Given that this is all cryptography related, maybe some security
|
||||
|
||||
## Security
|
||||
|
||||

|
||||

|
||||
*Webpagetest security score*
|
||||
|
||||
I've ran quite a few webpagetests on different website, but a **0** security score is new to me. What does that even mean?
|
||||
|
@ -14,39 +14,39 @@ I'm not going to lie, Flipper Zero sounds like a cool project for hackers. Here'
|
||||
|
||||
Something extremely scummy is going on right now! Have a look:
|
||||
|
||||

|
||||

|
||||
|
||||
Looking good, lot's of stuff to read, let's take our time.
|
||||
|
||||

|
||||

|
||||
|
||||
My word, they're almost out of Early Birds! Please, for the love of god, if you want to save some money, pledge now, only 9 left and it clearly says "Limited"!
|
||||
|
||||
### One minute later
|
||||
|
||||

|
||||

|
||||
|
||||
A person has just pledged! Where's my credit card?
|
||||
|
||||
### Another minute later
|
||||
|
||||

|
||||

|
||||
|
||||
Wait, 9 left? Someone bailed? Doesn't matter, I need this!
|
||||
|
||||
### Yet another minute later
|
||||
|
||||

|
||||

|
||||
|
||||
Wait, what?
|
||||
|
||||
### And it goes on
|
||||
|
||||

|
||||

|
||||
|
||||
### And on
|
||||
|
||||

|
||||

|
||||
|
||||
## This needs to stop
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
- week: 2020-26
|
||||
year: 2017
|
||||
artist: Peter Silberman
|
||||
title: Impermanence
|
||||
- week: 2020-25
|
||||
year: 1984
|
||||
artist: Jean-Michel Jarre
|
||||
title: Zoolook
|
||||
- week: 2020-24
|
||||
year: 1962
|
||||
artist: Booker T. & the M.G.'s
|
||||
title: Green Onions
|
@ -14,5 +14,5 @@ If you haven't cleaned the fan in a while, your best bet is to open the NUC up a
|
||||
|
||||
To prevent having to open a NUC up too often, I bought a few cans of compressed air and regularly blow air through the device. I'm also looking into placing air filters near the air intake.
|
||||
|
||||

|
||||

|
||||
*Can you tell when compressed air was applied to the NUC?*
|
||||
|
@ -10,7 +10,7 @@ published: true
|
||||
|
||||
I rarely interact with [Github](https://github.com) anymore. All my projects are either on my selfhosted [Gitea](https://gitea.io) instance or on [Codeberg.org](https://codeberg.org/). That's why I missed the following on [Github Status](https://www.githubstatus.com/):
|
||||
|
||||

|
||||

|
||||
*Yikes*
|
||||
|
||||
Yikes, indeed. How everyone handles this is up to them. Large projects will find it hard to move, no doubt.
|
||||
|
235
functions.php
@ -1,235 +0,0 @@
|
||||
<?php
|
||||
|
||||
include_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use Pagerange\Markdown\MetaParsedown;
|
||||
use Bhaktaraz\RSSGenerator\Item;
|
||||
use Bhaktaraz\RSSGenerator\Feed;
|
||||
use Bhaktaraz\RSSGenerator\Channel;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
function getIcons() {
|
||||
$icons = array();
|
||||
$icons["github"] = file_get_contents('static/img/github.svg');
|
||||
$icons["gitlab"] = file_get_contents('static/img/gitlab.svg');
|
||||
$icons["gnuprivacyguard"] = file_get_contents('static/img/gnuprivacyguard.svg');
|
||||
$icons["mail"] = file_get_contents('static/img/mail.svg');
|
||||
$icons["mastodon"] = file_get_contents('static/img/mastodon.svg');
|
||||
$icons["rss"] = file_get_contents('static/img/rss.svg');
|
||||
$icons["xmpp"] = file_get_contents('static/img/xmpp.svg');
|
||||
return $icons;
|
||||
}
|
||||
|
||||
function getBlogPosts($params) {
|
||||
$posts = scandirRec('content/blog/', 'blog', $params);
|
||||
$posts = array_reverse($posts);
|
||||
return $posts;
|
||||
}
|
||||
|
||||
function getNotes($params) {
|
||||
$posts = scandirRec('content/notes/', 'notes', $params);
|
||||
$posts = array_reverse($posts);
|
||||
return $posts;
|
||||
}
|
||||
|
||||
function getProjects($params) {
|
||||
$projects = scandirRec('content/projects/', 'projects', $params);
|
||||
return $projects;
|
||||
}
|
||||
|
||||
function getReplies($params) {
|
||||
$projects = scandirRec('content/replies/', 'replies', $params);
|
||||
return $projects;
|
||||
}
|
||||
|
||||
function getWebmentions($target) {
|
||||
$data = json_decode(file_get_contents("https://webm.yarmo.eu/get?target=$target"), true);
|
||||
|
||||
foreach ($data as $id => $entry) {
|
||||
$time = strtotime($entry["created_at"]);
|
||||
$data[$id]["date"] = date("Y-m-d",$time);
|
||||
$data[$id]["time"] = date("H:i:s",$time);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function getFOSS() {
|
||||
return Yaml::parseFile('content/foss/foss.yaml');
|
||||
}
|
||||
|
||||
function getVinyl() {
|
||||
return Yaml::parseFile('content/music/vinyl.yaml');
|
||||
}
|
||||
|
||||
function getAOTW() {
|
||||
return Yaml::parseFile('content/music/aotw.yaml');
|
||||
}
|
||||
|
||||
function getRSS($params) {
|
||||
header("Content-Type: text/xml");
|
||||
$feed = new Feed();
|
||||
|
||||
$channel = new Channel();
|
||||
$channel
|
||||
->title($params['feed']['title'])
|
||||
->description($params['feed']['description'])
|
||||
->url($params['feed']['url'])
|
||||
->atomLinkSelf($params['feed']['linkself'])
|
||||
->appendTo($feed);
|
||||
|
||||
$dates = array_column($params['posts'], 'date');
|
||||
array_multisort($dates, SORT_DESC, $params['posts']);
|
||||
|
||||
foreach($params['posts'] as $post) {
|
||||
$item = new Item();
|
||||
$item
|
||||
->title($post['title'])
|
||||
->creator($post['author'])
|
||||
// ->content($post['content'])
|
||||
->description($post['content'])
|
||||
->url($post['url'])
|
||||
->guid($post['url'], true)
|
||||
->pubDate(strtotime($post['date']))
|
||||
->appendTo($channel);
|
||||
}
|
||||
|
||||
echo $feed;
|
||||
}
|
||||
|
||||
function scandirRec($path, $category, $params) {
|
||||
$Parsedown = new Parsedown();
|
||||
$mp = new MetaParsedown();
|
||||
$items = array();
|
||||
|
||||
// If a directory
|
||||
if(is_dir($path)) {
|
||||
$files = glob($path . '*', GLOB_MARK);
|
||||
foreach($files as $file) {
|
||||
if(is_dir($file)) {
|
||||
$items = array_merge($items, scandirRec($file, $category, $params));
|
||||
} else {
|
||||
$return = scandirRec($file, $category, $params);
|
||||
if (!empty($return)) {
|
||||
$items[] = $return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
// If a file
|
||||
if (($path == '.') || ($path == '..')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = file_get_contents($path);
|
||||
$meta = $mp->meta($content);
|
||||
|
||||
switch ($category) {
|
||||
case 'blog':
|
||||
$item = array(
|
||||
"type" => "blog",
|
||||
"title" => $meta["title"],
|
||||
"author" => $meta["author"],
|
||||
"urlrel" => "/post/".$meta["slug"],
|
||||
"url" => "https://yarmo.eu/post/".$meta["slug"],
|
||||
"slug" => $meta["slug"],
|
||||
"date" => $meta["date"],
|
||||
"published" => $meta["published"],
|
||||
"discussion" => $meta["discussion"],
|
||||
"content" => $mp->text($content));
|
||||
|
||||
$date = new DateTime($item['date']);
|
||||
$item['date_formatted'] = $date->format('Y-m-d');
|
||||
$item['date_rss'] = $date->format('Y-m-d');
|
||||
|
||||
if (!$item['published'] && !(isset($params['slug']) && $item['slug'] == $params['slug'])) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'notes':
|
||||
$item = array(
|
||||
"type" => "note",
|
||||
"title" => $meta["title"],
|
||||
"author" => $meta["author"],
|
||||
"urlrel" => "/post/".$meta["slug"],
|
||||
"url" => "https://yarmo.eu/post/".$meta["slug"],
|
||||
"slug" => $meta["slug"],
|
||||
"date" => $meta["date"],
|
||||
"published" => $meta["published"],
|
||||
"discussion" => $meta["discussion"],
|
||||
"content" => $mp->text($content));
|
||||
|
||||
$date = new DateTime($item['date']);
|
||||
$item['date_formatted'] = $date->format('Y-m-d');
|
||||
$item['date_rss'] = $date->format('Y-m-d');
|
||||
|
||||
if (!$item['published'] && !(isset($params['slug']) && $item['slug'] == $params['slug'])) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'projects':
|
||||
$item = array(
|
||||
"title" => $meta["title"],
|
||||
"status" => $meta["status"],
|
||||
"urlrel" => "/projects/".$meta["slug"],
|
||||
"url" => "https://yarmo.eu/projects/".$meta["slug"],
|
||||
"slug" => $meta["slug"],
|
||||
"date" => $meta["date"],
|
||||
"listed" => $meta["listed"],
|
||||
"content" => $mp->text($content));
|
||||
|
||||
$date = new DateTime($item['date']);
|
||||
$item['date_formatted'] = $date->format('Y-m-d');
|
||||
$item['date_rss'] = $date->format('Y-m-d');
|
||||
|
||||
if (!$item['listed'] && !(isset($params['slug']) && $item['slug'] == $params['slug'])) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'replies':
|
||||
$item = array(
|
||||
"type" => "note",
|
||||
"title" => $meta["title"],
|
||||
"reply-url" => $meta["reply-url"],
|
||||
"reply-title" => $meta["reply-title"],
|
||||
"author" => $meta["author"],
|
||||
"urlrel" => "/reply/".$meta["slug"],
|
||||
"url" => "https://yarmo.eu/reply/".$meta["slug"],
|
||||
"slug" => $meta["slug"],
|
||||
"date" => $meta["date"],
|
||||
"published" => $meta["published"],
|
||||
"content" => $mp->text($content));
|
||||
|
||||
$date = new DateTime($item['date']);
|
||||
$item['date_formatted'] = $date->format('Y-m-d');
|
||||
$item['date_rss'] = $date->format('Y-m-d');
|
||||
|
||||
if (!$item['published'] && !(isset($params['slug']) && $item['slug'] == $params['slug'])) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($params['slug']) && $item['slug'] != $params['slug']) {
|
||||
return;
|
||||
}
|
||||
if (isset($params['slug'])) {
|
||||
$item["webmentions"] = array_merge(
|
||||
getWebmentions($item["url"]),
|
||||
getWebmentions("https://yarmo.eu/blog/".$meta["slug"]),
|
||||
getWebmentions("https://yarmo.eu/notes/".$meta["slug"])
|
||||
);
|
||||
$item["hasWebmentions"] = count($item["webmentions"]) > 0;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
17
index.js
Normal file
@ -0,0 +1,17 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const fs = require('fs')
|
||||
require('dotenv').config()
|
||||
|
||||
app.set('env', process.env.NODE_ENV || "production")
|
||||
app.set('view engine', 'pug')
|
||||
app.set('port', process.env.PORT || 3000)
|
||||
|
||||
app.use('/', require('./routes/main'))
|
||||
app.use('/', require('./routes/static'))
|
||||
app.use('/feed', require('./routes/feed'))
|
||||
app.use('/rss', require('./routes/feed'))
|
||||
|
||||
app.listen(app.get('port'), () => {
|
||||
console.log(`Node server listening at http://localhost:${app.get('port')}`)
|
||||
})
|
413
index.php
@ -1,413 +0,0 @@
|
||||
<?php
|
||||
|
||||
include_once __DIR__ . '/vendor/autoload.php';
|
||||
include 'functions.php';
|
||||
use Pagerange\Markdown\MetaParsedown;
|
||||
|
||||
// Init router
|
||||
$router = new AltoRouter();
|
||||
|
||||
// Router mapping
|
||||
$router->map('GET', '/', function() {}, 'home');
|
||||
$router->map('GET', '/rss', function() {}, 'rss');
|
||||
$router->map('GET', '/rss.xml', function() {}, 'rss-xml');
|
||||
$router->map('GET', '/rss/all', function() {}, 'rss-all');
|
||||
$router->map('GET', '/rss/blog', function() {}, 'rss-blog');
|
||||
$router->map('GET', '/rss/notes', function() {}, 'rss-notes');
|
||||
$router->map('GET', '/about', function() {}, 'about');
|
||||
$router->map('GET', '/feeds', function() {}, 'feeds');
|
||||
$router->map('GET', '/uses', function() {}, 'uses');
|
||||
$router->map('GET', '/now', function() {}, 'now');
|
||||
$router->map('GET', '/blogroll', function() {}, 'blogroll');
|
||||
$router->map('GET', '/blog', function() {}, 'blog');
|
||||
$router->map('GET', '/blog/[*:slug]', function() {}, 'blog_post');
|
||||
$router->map('GET', '/notes', function() {}, 'notes');
|
||||
$router->map('GET', '/notes/[*:slug]', function() {}, 'notes_post');
|
||||
$router->map('GET', '/post/[*:slug]', function() {}, 'post');
|
||||
$router->map('GET', '/reply/[*:slug]', function() {}, 'reply');
|
||||
$router->map('GET', '/projects', function() {}, 'projects');
|
||||
$router->map('GET', '/projects/[*:slug]', function() {}, 'projects_details');
|
||||
$router->map('GET', '/foss', function() {}, 'foss');
|
||||
$router->map('GET', '/music', function() {}, 'music');
|
||||
$router->map('GET', '/vinyl', function() {}, 'vinyl');
|
||||
$router->map('GET', '/aotw', function() {}, 'aotw');
|
||||
$router->map('GET', '/pgp', function() {}, 'pgp');
|
||||
$router->map('GET', '/contact', function() {}, 'contact');
|
||||
|
||||
// Router matching
|
||||
$match = $router->match();
|
||||
|
||||
// Template engine settings and variables
|
||||
$basetitle = 'yarmo';
|
||||
$environment = getenv('ENVIRONMENT') ?: 'production';
|
||||
$options = [
|
||||
'paths' => [
|
||||
'views/',
|
||||
],
|
||||
'cache_dir' => 'cache/',
|
||||
'enable_profiler' => false,
|
||||
'profiler' => [
|
||||
'time_precision' => 3,
|
||||
'line_height' => 30,
|
||||
'display' => true,
|
||||
'log' => false,
|
||||
],
|
||||
];
|
||||
$variables = [
|
||||
'title' => $basetitle
|
||||
];
|
||||
|
||||
// If we are dealing with the home page
|
||||
if ($match['name'] == 'home') {
|
||||
$variables['posts'] = getBlogPosts($match['params']);
|
||||
$variables['title'] = 'Blog — '.$basetitle;
|
||||
// $variables['icons'] = getIcons();
|
||||
}
|
||||
|
||||
// If we are dealing with the about page
|
||||
if ($match['name'] == 'about') {
|
||||
}
|
||||
|
||||
// If we are dealing with the rss feed
|
||||
if ($match['name'] == 'rss' || $match['name'] == 'rss-all' || $match['name'] == 'rss-xml') {
|
||||
$blogposts = getBlogPosts($match['params']);
|
||||
$notes = getNotes($match['params']);
|
||||
$variables['posts'] = array_merge($blogposts, $notes);
|
||||
$variables['feed']['title'] = "Yarmo's blog and notes";
|
||||
$variables['feed']['description'] = "Blog and notes feed for yarmo.eu discussing homelab and selfhosted stuff";
|
||||
$variables['feed']['url'] = "https://yarmo.eu";
|
||||
$variables['feed']['linkself'] = "https://yarmo.eu/rss/all";
|
||||
}
|
||||
// If we are dealing with the blog rss feed
|
||||
if ($match['name'] == 'rss-blog') {
|
||||
$variables['posts'] = getBlogPosts($match['params']);
|
||||
$variables['feed']['title'] = "Yarmo's blog";
|
||||
$variables['feed']['description'] = "Blog feed for yarmo.eu discussing homelab and selfhosted stuff";
|
||||
$variables['feed']['url'] = "https://yarmo.eu/blog";
|
||||
$variables['feed']['linkself'] = "https://yarmo.eu/rss/blog";
|
||||
}
|
||||
// If we are dealing with the notes rss feed
|
||||
if ($match['name'] == 'rss-notes') {
|
||||
$variables['posts'] = getNotes($match['params']);
|
||||
$variables['feed']['title'] = "Yarmo's blog";
|
||||
$variables['feed']['description'] = "Notes feed for yarmo.eu discussing homelab and selfhosted stuff";
|
||||
$variables['feed']['url'] = "https://yarmo.eu/notes";
|
||||
$variables['feed']['linkself'] = "https://yarmo.eu/rss/notes";
|
||||
}
|
||||
|
||||
// If we are dealing with the feeds
|
||||
if ($match['name'] == 'feeds') {
|
||||
$variables['title'] = 'Feeds — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the uses
|
||||
if ($match['name'] == 'uses') {
|
||||
$variables['title'] = 'Uses — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the now
|
||||
if ($match['name'] == 'now') {
|
||||
$variables['title'] = 'Now — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the blogroll
|
||||
if ($match['name'] == 'blogroll') {
|
||||
$variables['title'] = 'Blogroll — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the blog
|
||||
if ($match['name'] == 'blog') {
|
||||
$variables['posts'] = getBlogPosts($match['params']);
|
||||
$variables['title'] = 'Blog — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the notes
|
||||
if ($match['name'] == 'notes') {
|
||||
$variables['posts'] = getNotes($match['params']);
|
||||
$variables['title'] = 'Notes — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with a post (old format)
|
||||
if (($match['name'] == 'blog_post') || ($match['name'] == 'notes_post')) {
|
||||
header('Location: https://yarmo.eu/post/'.$match['params']['slug']);
|
||||
}
|
||||
|
||||
// If we are dealing with a post
|
||||
if ($match['name'] == 'post') {
|
||||
$variables['post'] = getBlogPosts($match['params']);
|
||||
if (count($variables['post']) == 0) {
|
||||
$variables['post'] = getNotes($match['params']);
|
||||
}
|
||||
if (count($variables['post']) > 0) {
|
||||
$variables['post'] = $variables['post'][0];
|
||||
$variables['title'] = htmlspecialchars_decode(str_replace('·','·',$variables['post']['title'])).' — '.$basetitle;
|
||||
} else {
|
||||
$match['name'] = '404';
|
||||
}
|
||||
}
|
||||
|
||||
// If we are dealing with a reply
|
||||
if ($match['name'] == 'reply') {
|
||||
$variables['reply'] = getReplies($match['params']);
|
||||
$variables['reply'] = $variables['reply'][0];
|
||||
$variables['title'] = htmlspecialchars_decode(str_replace('·','·',$variables['reply']['title'])).' — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the projects
|
||||
if ($match['name'] == 'projects') {
|
||||
$variables['projects'] = getProjects($match['params']);
|
||||
$variables['title'] = 'Projects — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with a project's details
|
||||
if ($match['name'] == 'projects_details') {
|
||||
$variables['project'] = getProjects($match['params']);
|
||||
$variables['project'] = $variables['project'][0];
|
||||
$variables['title'] = htmlspecialchars_decode(str_replace('·','·',$variables['project']['title'])).' — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with foss
|
||||
if ($match['name'] == 'foss') {
|
||||
$variables['foss'] = getFOSS();
|
||||
$variables['title'] = 'FOSS — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with vinyl
|
||||
if ($match['name'] == 'vinyl') {
|
||||
$variables['vinyl'] = getVinyl();
|
||||
$variables['albums'] = $variables['vinyl']['albums'];
|
||||
$variables['title'] = 'Vinyl — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with Album Of The Week
|
||||
if ($match['name'] == 'aotw') {
|
||||
$variables['albums'] = getAOTW();
|
||||
$variables['title'] = 'Album Of The Week — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the pgp page
|
||||
if ($match['name'] == 'pgp') {
|
||||
$variables['title'] = 'PGP — '.$basetitle;
|
||||
}
|
||||
|
||||
// If we are dealing with the contact page
|
||||
if ($match['name'] == 'contact') {
|
||||
$variables['title'] = 'Contact me — '.$basetitle;
|
||||
}
|
||||
|
||||
// Add Phug dyninclude
|
||||
Phug::addKeyword('dyninclude', function ($args) {
|
||||
return array(
|
||||
'beginPhp' => 'echo file_get_contents(' . $args . ');',
|
||||
);
|
||||
});
|
||||
|
||||
// PRODUCTION
|
||||
// Render the appropriate route
|
||||
if ($environment === 'production') {
|
||||
if(is_array($match) && is_callable($match['target'])) {
|
||||
switch ($match['name']) {
|
||||
case 'home':
|
||||
// Phug optimizer
|
||||
\Phug\Optimizer::call('displayFile', ['index', $variables], $options);
|
||||
// Phug::displayFile('index', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'about':
|
||||
// Phug optimizer
|
||||
\Phug\Optimizer::call('displayFile', ['about', $variables], $options);
|
||||
// Phug::displayFile('index', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'rss':
|
||||
case 'rss-xml':
|
||||
case 'rss-all':
|
||||
case 'rss-blog':
|
||||
case 'rss-notes':
|
||||
getRSS($variables);
|
||||
break;
|
||||
|
||||
case 'feeds':
|
||||
\Phug\Optimizer::call('displayFile', ['feeds', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'uses':
|
||||
\Phug\Optimizer::call('displayFile', ['uses', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'now':
|
||||
\Phug\Optimizer::call('displayFile', ['now', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'blogroll':
|
||||
\Phug\Optimizer::call('displayFile', ['blogroll', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'blog':
|
||||
\Phug\Optimizer::call('displayFile', ['blog', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'blog_post':
|
||||
\Phug\Optimizer::call('displayFile', ['blog_post', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'notes':
|
||||
\Phug\Optimizer::call('displayFile', ['notes', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'notes_post':
|
||||
\Phug\Optimizer::call('displayFile', ['notes_post', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'post':
|
||||
\Phug\Optimizer::call('displayFile', ['post', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'reply':
|
||||
\Phug\Optimizer::call('displayFile', ['reply', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'projects':
|
||||
\Phug\Optimizer::call('displayFile', ['projects', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'projects_details':
|
||||
\Phug\Optimizer::call('displayFile', ['projects_details', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'foss':
|
||||
\Phug\Optimizer::call('displayFile', ['foss', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'music':
|
||||
\Phug\Optimizer::call('displayFile', ['music', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'vinyl':
|
||||
\Phug\Optimizer::call('displayFile', ['vinyl', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'aotw':
|
||||
\Phug\Optimizer::call('displayFile', ['aotw', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'contact':
|
||||
\Phug\Optimizer::call('displayFile', ['contact', $variables], $options);
|
||||
break;
|
||||
|
||||
case 'pgp':
|
||||
\Phug\Optimizer::call('displayFile', ['pgp', $variables], $options);
|
||||
break;
|
||||
|
||||
default:
|
||||
\Phug\Optimizer::call('displayFile', ['404', $variables], $options);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// No route was matched
|
||||
\Phug\Optimizer::call('displayFile', ['404', $variables], $options);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
# DEVELOPMENT
|
||||
// Render the appropriate route
|
||||
if(is_array($match) && is_callable($match['target'])) {
|
||||
switch ($match['name']) {
|
||||
case 'home':
|
||||
Phug::displayFile('index', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'about':
|
||||
Phug::displayFile('about', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'rss':
|
||||
case 'rss-xml':
|
||||
case 'rss-all':
|
||||
case 'rss-blog':
|
||||
case 'rss-notes':
|
||||
getRSS($variables);
|
||||
break;
|
||||
|
||||
case 'feeds':
|
||||
Phug::displayFile('feeds', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'uses':
|
||||
Phug::displayFile('uses', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'now':
|
||||
Phug::displayFile('now', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'blogroll':
|
||||
Phug::displayFile('blogroll', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'blog':
|
||||
Phug::displayFile('blog', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'blog_post':
|
||||
Phug::displayFile('blog_post', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'notes':
|
||||
Phug::displayFile('notes', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'notes_post':
|
||||
Phug::displayFile('notes_post', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'post':
|
||||
Phug::displayFile('post', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'reply':
|
||||
Phug::displayFile('reply', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'projects':
|
||||
Phug::displayFile('projects', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'projects_details':
|
||||
Phug::displayFile('projects_details', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'foss':
|
||||
Phug::displayFile('foss', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'music':
|
||||
Phug::displayFile('music', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'vinyl':
|
||||
Phug::displayFile('vinyl', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'aotw':
|
||||
Phug::displayFile('aotw', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'contact':
|
||||
Phug::displayFile('contact', $variables, $options);
|
||||
break;
|
||||
|
||||
case 'pgp':
|
||||
Phug::displayFile('pgp', $variables, $options);
|
||||
break;
|
||||
|
||||
default:
|
||||
Phug::displayFile('404', $variables, $options);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// No route was matched
|
||||
Phug::displayFile('404', $variables, $options);
|
||||
}
|
6
nodemon.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"ignore": [],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
}
|
||||
}
|
1803
package-lock.json
generated
Normal file
36
package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "yarmo.eu",
|
||||
"version": "0.1.0",
|
||||
"description": "Yarmo.eu website",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index",
|
||||
"dev": "nodemon --config nodemon.json index"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.yarmo.eu/yarmo/yarmo.eu"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Yarmo Mackenbach <yarmo@yarmo.eu> (https://yarmo.eu)",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"bent": "^7.3.10",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"express-validator": "^6.6.1",
|
||||
"feed": "^4.2.1",
|
||||
"jstransformer-markdown-it": "^2.1.0",
|
||||
"lodash": "^4.17.20",
|
||||
"luxon": "^1.25.0",
|
||||
"markdown-it-anchor": "^5.3.0",
|
||||
"markdown-it-table-of-contents": "^0.4.4",
|
||||
"markdown-it-title": "^3.0.0",
|
||||
"pug": "^3.0.0",
|
||||
"yaml": "^1.10.0",
|
||||
"yaml-front-matter": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.4"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 838 B After Width: | Height: | Size: 838 B |
Before Width: | Height: | Size: 850 B After Width: | Height: | Size: 850 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 327 B |
Before Width: | Height: | Size: 990 B After Width: | Height: | Size: 990 B |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 416 B After Width: | Height: | Size: 416 B |
Before Width: | Height: | Size: 658 B After Width: | Height: | Size: 658 B |
358
public/style.css
Normal file
@ -0,0 +1,358 @@
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-Regular.otf");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-Bold.otf");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-Italic.otf");
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-BoldItalic.otf");
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
:root {
|
||||
--clr-bg: hsl(0,0%,95%);
|
||||
--clr-bg-alt: hsl(0,0%,85%);
|
||||
|
||||
--clr-border: hsl(0,0%,80%);
|
||||
|
||||
--clr-text: hsl(0,0%,15%);
|
||||
--clr-text-long: hsl(0,0%,10%);
|
||||
--clr-text-highlight: hsl(0,50%,40%);
|
||||
--clr-header: hsl(0,0%,15%);
|
||||
--clr-subheader: hsl(0,0%,15%);
|
||||
--clr-link: hsl(175,50%,40%);
|
||||
--clr-link-alt: hsl(175,38%,60%);
|
||||
--clr-code: hsl(0,0%,15%);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: 'dm', 'Courier New', Courier, monospace;
|
||||
color: var(--clr-text);
|
||||
line-height: 1.4em;
|
||||
font-size: 1.1em;
|
||||
background-color: var(--clr-bg);
|
||||
margin: 24px;
|
||||
}
|
||||
header {
|
||||
margin: 0 0 48px;
|
||||
padding: 0 0 24px;
|
||||
border-bottom: 3px dashed rgba(0,0,0,0.5);
|
||||
}
|
||||
footer {
|
||||
margin: 64px 0 0;
|
||||
text-align: center;
|
||||
border-top: 3px dashed rgba(0,0,0,0.5);
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
margin: 64px auto 128px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.select-all {
|
||||
user-select: all;
|
||||
-ms-user-select: all;
|
||||
-moz-user-select: all;
|
||||
-webkit-user-select: all;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--clr-link);
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: var(--clr-text);
|
||||
background-color: var(--clr-link-alt);
|
||||
text-decoration: none;
|
||||
}
|
||||
a.header-anchor {
|
||||
text-decoration: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
h2:hover a.header-anchor {
|
||||
opacity: 1;
|
||||
}
|
||||
h1 {
|
||||
line-height: 1.2em;
|
||||
margin: 8px 0 12px 0;
|
||||
}
|
||||
h2 {
|
||||
color: var(--clr-header);
|
||||
font-weight: bold;
|
||||
margin: 64px 0 0;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
h3 {
|
||||
margin: 32px 0 0;
|
||||
color: var(--clr-subheader);
|
||||
line-height: 1.2em;
|
||||
}
|
||||
.small {
|
||||
font-size: 0.75em !important;
|
||||
}
|
||||
pre {
|
||||
padding: 0 4px;
|
||||
background-color: var(--clr-bg-alt);
|
||||
border: 2px solid var(--clr-border);
|
||||
overflow-x: auto;
|
||||
}
|
||||
code {
|
||||
font-family: 'dm', 'Courier New', Courier, monospace;
|
||||
color: var(--clr-code);
|
||||
background-color: var(--clr-bg-alt);
|
||||
border: 2px solid var(--clr-border);
|
||||
font-size: 1rem !important;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
pre > code {
|
||||
border: 0;
|
||||
}
|
||||
p > code, li > code {
|
||||
padding: 0 2px;
|
||||
}
|
||||
ul li {
|
||||
list-style-type: "|> ";
|
||||
}
|
||||
blockquote {
|
||||
margin-left: 0;
|
||||
padding-left: 32px;
|
||||
border-left: 2px solid #4ab4ab;
|
||||
}
|
||||
blockquote strong em {
|
||||
color: var(--clr-text-highlight);
|
||||
}
|
||||
|
||||
.id-card {
|
||||
margin: 0 0 64px 0;
|
||||
}
|
||||
.ellipsis {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.id-card {
|
||||
/* display: flex;
|
||||
align-items: top;
|
||||
flex-wrap: wrap; */
|
||||
position: relative;
|
||||
padding: 24px 24px 0px;
|
||||
background: #f9f9f9;
|
||||
border: 2px solid rgba(0,0,0,0.5);
|
||||
}
|
||||
.id-card .header-image {
|
||||
/* flex: 1;
|
||||
flex-basis: auto; */
|
||||
/* margin: 8px 32px 8px 0; */
|
||||
text-align: center;
|
||||
}
|
||||
.id-card .header-description {
|
||||
width: 100%;
|
||||
/* flex: 100;
|
||||
flex-basis: auto; */
|
||||
/* margin: 8px 0; */
|
||||
/* border-left: 2px solid var(--clr-border);
|
||||
border-right: 2px solid var(--clr-border); */
|
||||
}
|
||||
.id-card .header-description-id {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
.id-card .profile-picture {
|
||||
/* position: absolute; */
|
||||
float: right;
|
||||
width: 128px;
|
||||
margin: 0 0 12px 12px;
|
||||
/* margin: 8px 0; */
|
||||
/* margin: 0 1em 0 0; */
|
||||
border-radius: 100%;
|
||||
}
|
||||
.id-card h1 {
|
||||
font-size: 1.6em;
|
||||
/* margin: 0; */
|
||||
margin: 0 0 1em;
|
||||
/* margin: 0 0 16px; */
|
||||
}
|
||||
.id-card p {
|
||||
width: 100%;
|
||||
font-size: 1.1em;
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
.id-card .social a {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 8px;
|
||||
margin: 0 4px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.wrapper-table {
|
||||
display: block;
|
||||
margin: 24px 0 48px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
min-width: 640px;
|
||||
border-top: 1px var(--clr-text) solid;
|
||||
border-bottom: 1px var(--clr-text) solid;
|
||||
border-collapse: collapse;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
table thead {
|
||||
border-bottom: 1px var(--clr-text) solid;
|
||||
}
|
||||
table th {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
table td {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
table.academic-record th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.academic-record th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.academic-record th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
table.work-experience th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.work-experience th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.work-experience th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
table.publications th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.publications th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.publications th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
table.music th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.music th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.music th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Longform */
|
||||
.longform_list {
|
||||
margin: 2em 0 0;
|
||||
}
|
||||
.longform_list__item {
|
||||
margin: 2em 0 0;
|
||||
font-family: 'Lora', Georgia, Times, serif;
|
||||
}
|
||||
.longform_list__item a {
|
||||
display: inline-block;
|
||||
margin: 4px 0;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.longform__header {
|
||||
font-size: 0.9em;
|
||||
color: var(--clr-subheader);
|
||||
}
|
||||
.longform__content {
|
||||
margin: 64px 0 0;
|
||||
font-family: 'Lora', Georgia, Times, serif;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.8em;
|
||||
color: var(--clr-text-long);
|
||||
}
|
||||
.longform__content p {
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
.longform__content code {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.longform__content ul li {
|
||||
list-style-type: "- ";
|
||||
}
|
||||
.longform__content img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 480px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
.longform__content img + br {
|
||||
display: none;
|
||||
}
|
||||
.longform__content img + br + em {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
font-size: 90%;
|
||||
}
|
||||
.comment {
|
||||
padding: 12px;
|
||||
margin: 32px 0 0;
|
||||
background-color: var(--clr-bg-alt);
|
||||
}
|
||||
.comment .quote {
|
||||
margin: 0 0 12px;
|
||||
font-family: 'Lora', Georgia, Times, serif;
|
||||
}
|
||||
.comment .sub {
|
||||
margin: 0;
|
||||
}
|
||||
.subsection {
|
||||
margin: 64px 0 0;
|
||||
border-top: 3px dashed rgba(0,0,0,0.5);
|
||||
}
|
||||
.subsection h2 {
|
||||
margin: 48px 0 0;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
.list {
|
||||
margin: 64px 0 0;
|
||||
}
|
||||
.list__item p {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.id-card .profile-picture {
|
||||
display: block;
|
||||
float: none;
|
||||
margin: 0 auto 1em;
|
||||
}
|
||||
}
|
45
routes/feed.js
Normal file
@ -0,0 +1,45 @@
|
||||
const router = require('express').Router()
|
||||
const fs = require('fs')
|
||||
const _ = require('lodash')
|
||||
const util = require('../server/util')
|
||||
|
||||
router.get('/all', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/rss+xml')
|
||||
res.end(await util.getRSS({ channel: 'all', format: 'rss' }))
|
||||
})
|
||||
router.get('/all.atom', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/atom+xml')
|
||||
res.end(await util.getRSS({ channel: 'all', format: 'atom' }))
|
||||
})
|
||||
router.get('/all.json', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/feed+json')
|
||||
res.end(await util.getRSS({ channel: 'all', format: 'json' }))
|
||||
})
|
||||
|
||||
router.get('/blog', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/rss+xml')
|
||||
res.end(await util.getRSS({ include: ['blog'], channel: 'blog', format: 'rss' }))
|
||||
})
|
||||
router.get('/blog.atom', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/atom+xml')
|
||||
res.end(await util.getRSS({ include: ['blog'], channel: 'blog', format: 'atom' }))
|
||||
})
|
||||
router.get('/blog.json', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/feed+json')
|
||||
res.end(await util.getRSS({ include: ['blog'], channel: 'blog', format: 'json' }))
|
||||
})
|
||||
|
||||
router.get('/notes', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/rss+xml')
|
||||
res.end(await util.getRSS({ include: ['notes'], channel: 'notes', format: 'rss' }))
|
||||
})
|
||||
router.get('/notes.atom', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/atom+xml')
|
||||
res.end(await util.getRSS({ include: ['notes'], channel: 'notes', format: 'atom' }))
|
||||
})
|
||||
router.get('/notes.json', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/feed+json')
|
||||
res.end(await util.getRSS({ include: ['notes'], channel: 'notes', format: 'json' }))
|
||||
})
|
||||
|
||||
module.exports = router
|
58
routes/main.js
Normal file
@ -0,0 +1,58 @@
|
||||
const router = require('express').Router()
|
||||
const fs = require('fs')
|
||||
const _ = require('lodash')
|
||||
const mw = require('../server/middlewares')
|
||||
const util = require('../server/util')
|
||||
|
||||
router.param('slug', mw.getPostBySlug)
|
||||
|
||||
router.get('/', mw.getBlogPosts, (req, res) => {
|
||||
res.render('blog', { title: 'Blog — yarmo.eu' })
|
||||
})
|
||||
router.get('/blog', mw.getBlogPosts, (req, res) => {
|
||||
res.render('blog', { title: 'Blog — yarmo.eu' })
|
||||
})
|
||||
router.get('/blog/:s', (req, res) => {
|
||||
res.redirect(`/post/${req.params.s}`)
|
||||
})
|
||||
router.get('/notes', mw.getNotes, (req, res) => {
|
||||
res.render('notes', { title: 'Notes — yarmo.eu' })
|
||||
})
|
||||
router.get('/notes/:s', (req, res) => {
|
||||
res.redirect(`/post/${req.params.s}`)
|
||||
})
|
||||
router.get('/post/:slug', (req, res) => {
|
||||
res.render('post', { title: `${res.locals.post.title} — yarmo.eu` })
|
||||
})
|
||||
router.get('/blogroll', (req, res) => {
|
||||
res.render('blogroll', { title: 'Blogroll — yarmo.eu' })
|
||||
})
|
||||
router.get('/feeds', (req, res) => {
|
||||
res.render('feeds', { title: 'Feeds — yarmo.eu' })
|
||||
})
|
||||
router.get('/about', (req, res) => {
|
||||
res.render('about', { title: 'About me — yarmo.eu' })
|
||||
})
|
||||
router.get('/work', (req, res) => {
|
||||
res.render('work', { title: 'Work — yarmo.eu' })
|
||||
})
|
||||
router.get('/contact', (req, res) => {
|
||||
res.render('contact', { title: 'Contact — yarmo.eu' })
|
||||
})
|
||||
router.get('/now', (req, res) => {
|
||||
res.render('now', { title: 'Now — yarmo.eu' })
|
||||
})
|
||||
router.get('/uses', (req, res) => {
|
||||
res.render('uses', { title: 'Uses — yarmo.eu' })
|
||||
})
|
||||
router.get('/music', (req, res) => {
|
||||
res.render('music', { title: 'Music — yarmo.eu' })
|
||||
})
|
||||
router.get('/vinyl', mw.getVinyl, (req, res) => {
|
||||
res.render('vinyl', { title: 'Vinyl — yarmo.eu' })
|
||||
})
|
||||
router.get('/pgp', (req, res) => {
|
||||
res.render('pgp', { title: 'PGP — yarmo.eu' })
|
||||
})
|
||||
|
||||
module.exports = router
|
16
routes/static.js
Normal file
@ -0,0 +1,16 @@
|
||||
const express = require('express')
|
||||
const router = require('express').Router()
|
||||
const path = require('path')
|
||||
|
||||
router.use('/favicon.png', express.static(path.join(__dirname, '../', 'public', 'favicon.png')))
|
||||
router.use('/static', express.static(path.join(__dirname, '../', 'public')))
|
||||
router.use('/static', express.static(path.join(__dirname, '../', 'content', 'img')))
|
||||
|
||||
router.use('/*.asc', (req, res, next) => {
|
||||
res.sendFile(path.join(__dirname, '../', 'keys', `${req.params[0]}.asc`))
|
||||
})
|
||||
router.use('/*.pgp', (req, res, next) => {
|
||||
res.sendFile(path.join(__dirname, '../', 'keys', `${req.params[0]}.pgp`))
|
||||
})
|
||||
|
||||
module.exports = router
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
require './vendor/autoload.php';
|
||||
|
||||
use tubalmartin\CssMin\Minifier as CSSmin;
|
||||
|
||||
$compressor = new CSSmin;
|
||||
$compressor->removeImportantComments();
|
||||
$compressor->setLineBreakPosition(1000);
|
||||
|
||||
$input_css = file_get_contents('./static/style.css');
|
||||
$output_css = $compressor->run($input_css);
|
||||
file_put_contents('./static/style.css', $output_css);
|
||||
|
||||
$input_css = file_get_contents('./static/norm.css');
|
||||
$output_css = $compressor->run($input_css);
|
||||
file_put_contents('./static/norm.css', $output_css);
|
||||
|
||||
?>
|
32
server/middlewares.js
Normal file
@ -0,0 +1,32 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const YAML = require('yaml')
|
||||
const util = require('./util')
|
||||
|
||||
module.exports.getBlogPosts = async (req, res, next) => {
|
||||
res.locals.blogPosts = await util.getBlogPosts({ publishedOnly: true })
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports.getNotes = async (req, res, next) => {
|
||||
res.locals.notes = await util.getNotes({ publishedOnly: true })
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports.getPostBySlug = async (req, res, next, slug) => {
|
||||
let post = await util.getPost(slug)
|
||||
|
||||
if (post) {
|
||||
post.webmentions = await util.getWebmentions(post.url)
|
||||
post.hasWebmentions = post.webmentions.length > 0
|
||||
}
|
||||
|
||||
res.locals.post = post
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports.getVinyl = async (req, res, next) => {
|
||||
const file = fs.readFileSync(path.join(__dirname, '../', 'content', 'music', 'vinyl.yaml'), 'utf8')
|
||||
res.locals.vinyl = YAML.parse(file)
|
||||
next()
|
||||
}
|
206
server/util.js
Normal file
@ -0,0 +1,206 @@
|
||||
const fs = require('fs')
|
||||
const { readdir } = require('fs').promises
|
||||
const path = require('path')
|
||||
const { resolve } = require('path')
|
||||
const bent = require('bent')
|
||||
const getJSON = bent('json')
|
||||
const _ = require('lodash')
|
||||
const md = require('markdown-it')({ typographer: true })
|
||||
const yamlFront = require('yaml-front-matter')
|
||||
const { DateTime } = require('luxon')
|
||||
const Feed = require('feed').Feed
|
||||
|
||||
md.use(require("markdown-it-anchor"), {
|
||||
"level": 2,
|
||||
"permalink": true,
|
||||
"permalinkClass": 'header-anchor',
|
||||
"permalinkSymbol": '¶',
|
||||
"permalinkBefore": false
|
||||
})
|
||||
|
||||
async function* getFiles(dir) {
|
||||
const dirents = await readdir(dir, { withFileTypes: true })
|
||||
for (const dirent of dirents) {
|
||||
const res = resolve(dir, dirent.name)
|
||||
if (dirent.isDirectory()) {
|
||||
yield* getFiles(res)
|
||||
} else {
|
||||
yield res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getBlogPosts = async (opts) => {
|
||||
if (!opts) { opts = {} }
|
||||
opts.publishedOnly = 'publishedOnly' in opts ? opts.publishedOnly : true
|
||||
|
||||
let data = []
|
||||
for await (const f of getFiles(path.join(__dirname, '../', 'content', 'blog'))) {
|
||||
const rawContent = fs.readFileSync(f, 'utf8')
|
||||
const fm = yamlFront.loadFront(rawContent)
|
||||
|
||||
if (opts.publishedOnly && !fm.published) { continue }
|
||||
|
||||
data.push({
|
||||
type: 'blog',
|
||||
title: fm.title,
|
||||
author: fm.author,
|
||||
urlrel: `/post/${fm.slug}`,
|
||||
url: `https://yarmo.eu/post/${fm.slug}`,
|
||||
slug: fm.slug,
|
||||
date: fm.date,
|
||||
date_formatted: DateTime.fromFormat(fm.date, 'yyyy-LL-dd hh:mm:ss').setLocale("en").toLocaleString(DateTime.DATE_MED),
|
||||
published: fm.published,
|
||||
discussion: fm.discussion,
|
||||
content: md.render(fm.__content)
|
||||
})
|
||||
}
|
||||
return data.reverse()
|
||||
}
|
||||
|
||||
const getNotes = async (opts) => {
|
||||
if (!opts) { opts = {} }
|
||||
opts.publishedOnly = 'publishedOnly' in opts ? opts.publishedOnly : true
|
||||
|
||||
let data = []
|
||||
for await (const f of getFiles(path.join(__dirname, '../', 'content', 'notes'))) {
|
||||
const rawContent = fs.readFileSync(f, 'utf8')
|
||||
const fm = yamlFront.loadFront(rawContent)
|
||||
|
||||
if (opts.publishedOnly && !fm.published) { continue }
|
||||
|
||||
data.push({
|
||||
type: 'note',
|
||||
title: fm.title,
|
||||
author: fm.author,
|
||||
urlrel: `/post/${fm.slug}`,
|
||||
url: `https://yarmo.eu/post/${fm.slug}`,
|
||||
slug: fm.slug,
|
||||
date: fm.date,
|
||||
date_formatted: DateTime.fromFormat(fm.date, 'yyyy-LL-dd hh:mm:ss').setLocale("en").setZone('utc').toLocaleString(DateTime.DATE_MED),
|
||||
published: fm.published,
|
||||
discussion: fm.discussion,
|
||||
content: md.render(fm.__content)
|
||||
})
|
||||
}
|
||||
return data.reverse()
|
||||
}
|
||||
|
||||
const getPost = async (slug) => {
|
||||
let post = null, posts = await getBlogPosts()
|
||||
posts = _.filter(posts, (p) => { return slug == p.slug })
|
||||
post = posts.length > 0 ? posts[0] : null
|
||||
|
||||
if (!post) {
|
||||
posts = await getNotes()
|
||||
posts = _.filter(posts, (p) => { return slug == p.slug })
|
||||
post = posts.length > 0 ? posts[0] : null
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
const getWebmentions = async (url) => {
|
||||
const data_1 = await getJSON(`https://webm.yarmo.eu/get?target=${url}`)
|
||||
const data_2 = await getJSON(`https://webm.yarmo.eu/get?target=${url.replace('/post/', '/blog/')}`)
|
||||
const data_3 = await getJSON(`https://webm.yarmo.eu/get?target=${url.replace('/post/', '/notes/')}`)
|
||||
|
||||
const dataRaw = data_1.concat(data_2).concat(data_3)
|
||||
const data = _.map(dataRaw, (x) => {
|
||||
x.date = DateTime.fromISO(x.created_at).setLocale("en").setZone('utc').toLocaleString(DateTime.DATE_MED),
|
||||
x.time = DateTime.fromISO(x.created_at).setLocale("en").setZone('utc').toLocaleString(DateTime.TIME_24_WITH_SHORT_OFFSET)
|
||||
return x
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const getRSS = async (opts) => {
|
||||
if (!opts) { opts = {} }
|
||||
opts.include = 'include' in opts ? opts.include : ['blog', 'notes']
|
||||
opts.channel = 'channel' in opts ? opts.channel : 'all'
|
||||
opts.format = 'format' in opts ? opts.format : 'rss'
|
||||
|
||||
const feed = new Feed({
|
||||
title: "Yarmo's blog and notes",
|
||||
description: "Blog posts and notes feed for yarmo.eu discussing open source, privacy and selfhosted stuff",
|
||||
id: "https://yarmo.eu",
|
||||
link: "https://yarmo.eu",
|
||||
language: "en",
|
||||
favicon: "https://yarmo.eu/favicon.png",
|
||||
copyright: "All rights reserved 2020 Yarmo Mackenbach",
|
||||
updated: new Date(Date.now()),
|
||||
feedLinks: {
|
||||
rss: `https://yarmo.eu/feed/${opts.channel}`,
|
||||
json: `https://yarmo.eu/feed/${opts.channel}.json`,
|
||||
atom: `https://yarmo.eu/feed/${opts.channel}.atom`
|
||||
},
|
||||
author: {
|
||||
name: "Yarmo Mackenbach",
|
||||
email: "yarmo@yarmo.eu",
|
||||
link: "https://yarmo.eu"
|
||||
}
|
||||
})
|
||||
|
||||
feed.addCategory("Technology")
|
||||
feed.addCategory("FOSS")
|
||||
feed.addCategory("Decentralization")
|
||||
feed.addCategory("Privacy")
|
||||
feed.addCategory("Identity")
|
||||
feed.addCategory("Cryptography")
|
||||
feed.addCategory("Selfhosted")
|
||||
|
||||
let posts = []
|
||||
if (opts.include.includes('blog')) {
|
||||
posts = posts.concat(await getBlogPosts())
|
||||
}
|
||||
if (opts.include.includes('notes')) {
|
||||
posts = posts.concat(await getNotes())
|
||||
}
|
||||
|
||||
posts = _.sortBy(posts, ['date']).reverse()
|
||||
|
||||
posts.forEach((item, i) => {
|
||||
feed.addItem({
|
||||
title: item.title,
|
||||
id: item.url,
|
||||
link: item.url,
|
||||
description: item.content,
|
||||
content: item.content,
|
||||
author: [
|
||||
{
|
||||
name: "Yarmo Mackenbach",
|
||||
email: "yarmo@yarmo.eu",
|
||||
link: "https://yarmo.eu"
|
||||
}
|
||||
],
|
||||
date: new Date(item.date)
|
||||
})
|
||||
})
|
||||
|
||||
let response
|
||||
switch (opts.format) {
|
||||
case 'rss':
|
||||
case 'xml':
|
||||
default:
|
||||
response = feed.rss2()
|
||||
break;
|
||||
case 'atom':
|
||||
response = feed.atom1()
|
||||
break;
|
||||
case 'json':
|
||||
response = feed.json1()
|
||||
break;
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getFiles: getFiles,
|
||||
getBlogPosts: getBlogPosts,
|
||||
getNotes: getNotes,
|
||||
getPost: getPost,
|
||||
getWebmentions: getWebmentions,
|
||||
getRSS: getRSS
|
||||
}
|
351
static/style.css
@ -1,351 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-Regular.otf");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-Bold.otf");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-Italic.otf");
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/static/fonts/Lora-BoldItalic.otf");
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
:root {
|
||||
--clr-bg: hsl(0,0%,95%);
|
||||
--clr-bg-alt: hsl(0,0%,85%);
|
||||
|
||||
--clr-border: hsl(0,0%,80%);
|
||||
|
||||
--clr-text: hsl(0,0%,15%);
|
||||
--clr-text-long: hsl(0,0%,10%);
|
||||
--clr-text-highlight: hsl(0,50%,40%);
|
||||
--clr-header: hsl(0,0%,15%);
|
||||
--clr-subheader: hsl(0,0%,15%);
|
||||
--clr-link: hsl(175,50%,40%);
|
||||
--clr-link-alt: hsl(175,38%,60%);
|
||||
--clr-code: hsl(0,0%,15%);
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: 'dm', 'Courier New', Courier, monospace;
|
||||
color: var(--clr-text);
|
||||
line-height: 1.4em;
|
||||
font-size: 1.1em;
|
||||
background-color: var(--clr-bg);
|
||||
margin: 24px;
|
||||
}
|
||||
header {
|
||||
margin: 0 0 48px;
|
||||
padding: 0 0 24px;
|
||||
border-bottom: 3px dashed rgba(0,0,0,0.5);
|
||||
}
|
||||
footer {
|
||||
margin: 64px 0 0;
|
||||
text-align: center;
|
||||
border-top: 3px dashed rgba(0,0,0,0.5);
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
margin: 64px auto 128px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.select-all {
|
||||
user-select: all;
|
||||
-ms-user-select: all;
|
||||
-moz-user-select: all;
|
||||
-webkit-user-select: all;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--clr-link);
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: var(--clr-text);
|
||||
background-color: var(--clr-link-alt);
|
||||
text-decoration: none;
|
||||
}
|
||||
h1 {
|
||||
line-height: 1.2em;
|
||||
margin: 8px 0 12px 0;
|
||||
}
|
||||
h2 {
|
||||
color: var(--clr-header);
|
||||
font-weight: bold;
|
||||
margin: 64px 0 0;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
h3 {
|
||||
margin: 32px 0 0;
|
||||
color: var(--clr-subheader);
|
||||
line-height: 1.2em;
|
||||
}
|
||||
.small {
|
||||
font-size: 0.75em !important;
|
||||
}
|
||||
pre {
|
||||
padding: 0 4px;
|
||||
background-color: var(--clr-bg-alt);
|
||||
border: 2px solid var(--clr-border);
|
||||
overflow-x: auto;
|
||||
}
|
||||
code {
|
||||
font-family: 'dm', 'Courier New', Courier, monospace;
|
||||
color: var(--clr-code);
|
||||
background-color: var(--clr-bg-alt);
|
||||
border: 2px solid var(--clr-border);
|
||||
font-size: 1rem !important;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
pre > code {
|
||||
border: 0;
|
||||
}
|
||||
p > code, li > code {
|
||||
padding: 0 2px;
|
||||
}
|
||||
ul li {
|
||||
list-style-type: "|> ";
|
||||
}
|
||||
blockquote {
|
||||
margin-left: 0;
|
||||
padding-left: 32px;
|
||||
border-left: 2px solid #4ab4ab;
|
||||
}
|
||||
blockquote strong em {
|
||||
color: var(--clr-text-highlight);
|
||||
}
|
||||
|
||||
.id-card {
|
||||
margin: 0 0 64px 0;
|
||||
}
|
||||
.ellipsis {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.id-card {
|
||||
/* display: flex;
|
||||
align-items: top;
|
||||
flex-wrap: wrap; */
|
||||
position: relative;
|
||||
padding: 24px 24px 0px;
|
||||
background: #f9f9f9;
|
||||
border: 2px solid rgba(0,0,0,0.5);
|
||||
}
|
||||
.id-card .header-image {
|
||||
/* flex: 1;
|
||||
flex-basis: auto; */
|
||||
/* margin: 8px 32px 8px 0; */
|
||||
text-align: center;
|
||||
}
|
||||
.id-card .header-description {
|
||||
width: 100%;
|
||||
/* flex: 100;
|
||||
flex-basis: auto; */
|
||||
/* margin: 8px 0; */
|
||||
/* border-left: 2px solid var(--clr-border);
|
||||
border-right: 2px solid var(--clr-border); */
|
||||
}
|
||||
.id-card .header-description-id {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
.id-card .profile-picture {
|
||||
/* position: absolute; */
|
||||
float: right;
|
||||
width: 128px;
|
||||
margin: 0 0 12px 12px;
|
||||
/* margin: 8px 0; */
|
||||
/* margin: 0 1em 0 0; */
|
||||
border-radius: 100%;
|
||||
|
||||
}
|
||||
.id-card h1 {
|
||||
font-size: 1.6em;
|
||||
/* margin: 0; */
|
||||
margin: 0 0 1em;
|
||||
/* margin: 0 0 16px; */
|
||||
}
|
||||
.id-card p {
|
||||
width: 100%;
|
||||
font-size: 1.1em;
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
.id-card .social a {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 8px;
|
||||
margin: 0 4px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.wrapper-table {
|
||||
display: block;
|
||||
margin: 24px 0 48px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
min-width: 640px;
|
||||
border-top: 1px var(--clr-text) solid;
|
||||
border-bottom: 1px var(--clr-text) solid;
|
||||
border-collapse: collapse;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
table thead {
|
||||
border-bottom: 1px var(--clr-text) solid;
|
||||
}
|
||||
table th {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
table td {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
table.academic-record th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.academic-record th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.academic-record th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
table.work-experience th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.work-experience th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.work-experience th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
table.publications th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.publications th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.publications th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
table.music th:nth-child(1) {
|
||||
width: 15%;
|
||||
}
|
||||
table.music th:nth-child(2) {
|
||||
width: 35%;
|
||||
}
|
||||
table.music th:nth-child(3) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Longform */
|
||||
.longform_list {
|
||||
margin: 2em 0 0;
|
||||
}
|
||||
.longform_list__item {
|
||||
margin: 2em 0 0;
|
||||
font-family: 'Lora', Georgia, Times, serif;
|
||||
}
|
||||
.longform_list__item a {
|
||||
display: inline-block;
|
||||
margin: 4px 0;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.longform__header {
|
||||
font-size: 0.9em;
|
||||
color: var(--clr-subheader);
|
||||
}
|
||||
.longform__content {
|
||||
margin: 64px 0 0;
|
||||
font-family: 'Lora', Georgia, Times, serif;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.8em;
|
||||
color: var(--clr-text-long);
|
||||
}
|
||||
.longform__content p {
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
.longform__content code {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.longform__content ul li {
|
||||
list-style-type: "- ";
|
||||
}
|
||||
.longform__content img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 480px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
.longform__content img + br {
|
||||
display: none;
|
||||
}
|
||||
.longform__content img + br + em {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
font-size: 90%;
|
||||
}
|
||||
.comment {
|
||||
padding: 12px;
|
||||
margin: 32px 0 0;
|
||||
background-color: var(--clr-bg-alt);
|
||||
}
|
||||
.comment .quote {
|
||||
margin: 0 0 12px;
|
||||
font-family: 'Lora', Georgia, Times, serif;
|
||||
}
|
||||
.comment .sub {
|
||||
margin: 0;
|
||||
}
|
||||
.subsection {
|
||||
margin: 64px 0 0;
|
||||
border-top: 3px dashed rgba(0,0,0,0.5);
|
||||
}
|
||||
.subsection h2 {
|
||||
margin: 48px 0 0;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
.list {
|
||||
margin: 64px 0 0;
|
||||
}
|
||||
.list__item p {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.id-card .profile-picture {
|
||||
display: block;
|
||||
float: none;
|
||||
margin: 0 auto 1em;
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
h2 HTTP 404
|
||||
p
|
||||
| This page could not be found... Go back to
|
||||
a(href="/") yarmo.eu
|
||||
| and try again!
|
||||
h2 HTTP 404
|
||||
p
|
||||
| This page could not be found... Go back to
|
||||
a(href="/") yarmo.eu
|
||||
| and try again!
|
329
views/about.pug
@ -1,172 +1,169 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
| about me
|
||||
h1
|
||||
| About Me
|
||||
nav
|
||||
| Go to:
|
||||
a(href="/now") now
|
||||
| |
|
||||
a(href="/") blog
|
||||
| |
|
||||
a(href="/notes") notes
|
||||
| |
|
||||
a(href="/feeds") feeds
|
||||
| |
|
||||
a(href="/blogroll") blogroll
|
||||
| |
|
||||
a(href="/uses") uses
|
||||
| |
|
||||
a(href="/foss") FOSS
|
||||
| |
|
||||
a(href="/projects") projects
|
||||
| |
|
||||
a(href="/music") music
|
||||
| |
|
||||
a(href="/pgp") PGP
|
||||
| |
|
||||
a(href="/contact") contact
|
||||
|
||||
main
|
||||
|
||||
include id
|
||||
|
||||
h2 >> About me
|
||||
|
||||
h3 Principles
|
||||
ul
|
||||
li The internet needs to be decentralized
|
||||
li
|
||||
a(href="https://publiccode.eu/") Public money? Public code!
|
||||
li Open science benefits society
|
||||
li Code documentation is essential
|
||||
li
|
||||
| Remove
|
||||
a(href="https://en.wikipedia.org/wiki/Big_Four_tech_companies") GAFAM
|
||||
| from one's life
|
||||
|
||||
h3 Interests
|
||||
ul
|
||||
li Cryptography and decentralized identity
|
||||
li Advancing and democratizing science
|
||||
li Building and maintaining a homelab
|
||||
li Statistics, machine learning
|
||||
li Halo universe
|
||||
li Making music
|
||||
li Podcasts
|
||||
|
||||
h3 Languages (spoken, written, read)
|
||||
ul
|
||||
li Dutch (native)
|
||||
li English (+++)
|
||||
li French (+++)
|
||||
li Portuguese (+)
|
||||
|
||||
h2 >> Science
|
||||
p I have finished a PhD project in the field of Neurosciences. My research focused on the neuronal basis of directional hearing.
|
||||
header
|
||||
h1
|
||||
| About Me
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
nav
|
||||
| Go to:
|
||||
a(href="/") blog
|
||||
| |
|
||||
a(href="/notes") notes
|
||||
| |
|
||||
a(href="/feeds") feeds
|
||||
| |
|
||||
a(href="/now") now
|
||||
| |
|
||||
a(href="/uses") uses
|
||||
| |
|
||||
a(href="/work") work
|
||||
| |
|
||||
a(href="/music") music
|
||||
| |
|
||||
a(href="/pgp") PGP
|
||||
| |
|
||||
a(href="/contact") contact
|
||||
|
||||
main
|
||||
|
||||
include id
|
||||
|
||||
h2 >> About me
|
||||
|
||||
h3 Principles
|
||||
ul
|
||||
li The internet needs to be decentralized
|
||||
li
|
||||
a(href="https://publiccode.eu/") Public money? Public code!
|
||||
li Open science benefits society
|
||||
li Code documentation is essential
|
||||
li
|
||||
| Remove
|
||||
a(href="https://en.wikipedia.org/wiki/Big_Four_tech_companies") GAFAM
|
||||
| from one's life
|
||||
|
||||
h3 Interests
|
||||
ul
|
||||
li Cryptography and decentralized identity
|
||||
li Advancing and democratizing science
|
||||
li Building and maintaining a homelab
|
||||
li Statistics, machine learning
|
||||
li Halo universe
|
||||
li Making music
|
||||
li Podcasts
|
||||
|
||||
h3 Languages (spoken, written, read)
|
||||
ul
|
||||
li Dutch (native)
|
||||
li English (+++)
|
||||
li French (+++)
|
||||
li Portuguese (+)
|
||||
|
||||
h2 >> Science
|
||||
p I have finished a PhD project in the field of Neurosciences. My research focused on the neuronal basis of directional hearing.
|
||||
|
||||
h3 Skills
|
||||
ul
|
||||
li Experimental electrophysiology
|
||||
li Histology
|
||||
li Behavioral techniques
|
||||
li Programming & data analysis
|
||||
ul
|
||||
li Data visualisation
|
||||
li Fourier / signal processing
|
||||
li Circular statistics
|
||||
li Machine learning
|
||||
li Database management (SQL & NoSQL)
|
||||
|
||||
h3 Work experience
|
||||
.wrapper-table
|
||||
table.work-experience
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Name
|
||||
th Location
|
||||
tbody
|
||||
tr
|
||||
td 2015-2019
|
||||
td PhD in Neurosciences
|
||||
td Erasmus MC, Rotterdam, The Netherlands
|
||||
|
||||
h3 Academic record
|
||||
.wrapper-table
|
||||
table.academic-record
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Name
|
||||
th Location
|
||||
tbody
|
||||
tr
|
||||
td 2010-2013
|
||||
td Bachelor Biology
|
||||
td Université Victor Segalen, Bordeaux, France
|
||||
tr
|
||||
td 2013-2015
|
||||
td Master Neurosciences & Neuropsychopharmacology
|
||||
td Université Victor Segalen, Bordeaux, France
|
||||
tr
|
||||
td 2013-2015
|
||||
td Master Cellular & Molecular Biology
|
||||
td Universidade de Coimbra, Portugal
|
||||
tr
|
||||
td 2015-2019
|
||||
td PhD in Neurosciences
|
||||
td Erasmus MC, Rotterdam, The Netherlands
|
||||
|
||||
h3 Publications
|
||||
.wrapper-table
|
||||
table.publications
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Authors
|
||||
th Title
|
||||
tbody
|
||||
tr
|
||||
td 2017
|
||||
td Arnau Busquets-Garcia, [...], Giovanni Marsicano
|
||||
td
|
||||
a(href="https://www.nature.com/articles/mp20174") Pregnenolone blocks cannabinoid-induced acute psychotic-like states in mice
|
||||
|
||||
h2 >> Programming
|
||||
h3 Skills
|
||||
ul
|
||||
li Experimental electrophysiology
|
||||
li Histology
|
||||
li Behavioral techniques
|
||||
li Programming & data analysis
|
||||
ul
|
||||
li Data visualisation
|
||||
li Fourier / signal processing
|
||||
li Circular statistics
|
||||
li Machine learning
|
||||
li Database management (SQL & NoSQL)
|
||||
|
||||
h3 Work experience
|
||||
.wrapper-table
|
||||
table.work-experience
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Name
|
||||
th Location
|
||||
tbody
|
||||
tr
|
||||
td 2015-2019
|
||||
td PhD in Neurosciences
|
||||
td Erasmus MC, Rotterdam, The Netherlands
|
||||
|
||||
h3 Academic record
|
||||
.wrapper-table
|
||||
table.academic-record
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Name
|
||||
th Location
|
||||
tbody
|
||||
tr
|
||||
td 2010-2013
|
||||
td Bachelor Biology
|
||||
td Université Victor Segalen, Bordeaux, France
|
||||
tr
|
||||
td 2013-2015
|
||||
td Master Neurosciences & Neuropsychopharmacology
|
||||
td Université Victor Segalen, Bordeaux, France
|
||||
tr
|
||||
td 2013-2015
|
||||
td Master Cellular & Molecular Biology
|
||||
td Universidade de Coimbra, Portugal
|
||||
tr
|
||||
td 2015-2019
|
||||
td PhD in Neurosciences
|
||||
td Erasmus MC, Rotterdam, The Netherlands
|
||||
|
||||
h3 Publications
|
||||
.wrapper-table
|
||||
table.publications
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Authors
|
||||
th Title
|
||||
tbody
|
||||
tr
|
||||
td 2017
|
||||
td Arnau Busquets-Garcia, [...], Giovanni Marsicano
|
||||
td
|
||||
a(href="https://www.nature.com/articles/mp20174") Pregnenolone blocks cannabinoid-induced acute psychotic-like states in mice
|
||||
|
||||
h2 >> Programming
|
||||
|
||||
h3 Languages used
|
||||
ul
|
||||
li Matlab / Octave
|
||||
li Python / R
|
||||
li HTML / JS / (S)CSS
|
||||
li PHP / NodeJS
|
||||
li SQL / NoSQL (cypher)
|
||||
h3 Languages used
|
||||
ul
|
||||
li Matlab / Octave
|
||||
li Python / R
|
||||
li HTML / JS / (S)CSS
|
||||
li PHP / NodeJS
|
||||
li SQL / NoSQL (cypher)
|
||||
|
||||
h3 Experience (science)
|
||||
ul
|
||||
li Data visualisation
|
||||
li Fourier / signal processing
|
||||
li Circular statistics
|
||||
li Machine learning
|
||||
h3 Experience (science)
|
||||
ul
|
||||
li Data visualisation
|
||||
li Fourier / signal processing
|
||||
li Circular statistics
|
||||
li Machine learning
|
||||
|
||||
h3 Experience (hobbies)
|
||||
ul
|
||||
li Homelab / self-hosting
|
||||
li Web design
|
||||
li Raspberry Pi / Arduino
|
||||
li Home automation
|
||||
li Cryptography
|
||||
|
||||
h3 Open source contributions
|
||||
p
|
||||
| Go to:
|
||||
a(href="/foss") FOSS
|
||||
|
||||
h2 >> Music
|
||||
p
|
||||
| Go to:
|
||||
a(href="/music") music
|
||||
h3 Experience (hobbies)
|
||||
ul
|
||||
li Homelab / self-hosting
|
||||
li Web design
|
||||
li Raspberry Pi / Arduino
|
||||
li Home automation
|
||||
li Cryptography
|
||||
|
||||
h3 Open source contributions
|
||||
p
|
||||
| Go to:
|
||||
a(href="/foss") FOSS
|
||||
|
||||
h2 >> Music
|
||||
p
|
||||
| Go to:
|
||||
a(href="/music") music
|
||||
|
@ -1,19 +0,0 @@
|
||||
extends layout
|
||||
|
||||
mixin entry($item)
|
||||
.list__item
|
||||
p !{$item['week']} >> !{$item['artist']} - !{$item['title']} (!{$item['year']})
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/music") music
|
||||
| > album of the week
|
||||
h1 Yarmo's Album Of The Week
|
||||
|
||||
main
|
||||
.list
|
||||
each $item in $albums
|
||||
+entry($item)
|
41
views/blog.pug
Normal file
@ -0,0 +1,41 @@
|
||||
extends templates/main
|
||||
|
||||
mixin entry(item)
|
||||
article.longform_list__item.h-entry
|
||||
p
|
||||
a(href=item['urlrel']).p-name.u-url !{item['title']}
|
||||
br
|
||||
| Posted on
|
||||
time(datetime=item['date']).dt-published !{item['date_formatted']}
|
||||
|
||||
block content
|
||||
.h-card
|
||||
header
|
||||
//nav
|
||||
a(href="/about") about me
|
||||
| > blog
|
||||
h1
|
||||
| Yarmo's blog
|
||||
nav
|
||||
| >>
|
||||
a(href="/") blog
|
||||
nav
|
||||
| Go to:
|
||||
a(href="/about") about me
|
||||
| |
|
||||
a(href="/notes") notes
|
||||
| |
|
||||
a(href="/blogroll") blogroll
|
||||
| |
|
||||
a(href="/feeds") feeds
|
||||
| |
|
||||
a(href="/contact") contact
|
||||
|
||||
main
|
||||
|
||||
div(style="display:none")
|
||||
include id
|
||||
|
||||
.longform_list.h-feed
|
||||
each item in blogPosts
|
||||
+entry(item)
|
@ -1,24 +1,26 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > blogroll
|
||||
h1
|
||||
| Blogroll
|
||||
|
||||
main
|
||||
a(href="https://ar.al/") Aral Balkan
|
||||
br
|
||||
a(href="https://write.privacytools.io/freddy/") Freddy's Ramblings
|
||||
br
|
||||
a(href="https://www.garron.blog/posts/") Guillermo Garron
|
||||
br
|
||||
a(href="https://goel.io/") Karan Goel
|
||||
br
|
||||
a(href="https://kevq.uk/") Kev Quirk
|
||||
br
|
||||
a(href="https://mikestone.me/") Mike Stone
|
||||
br
|
||||
a(href="https://degruchy.org/") Nathan DeGruchy
|
||||
header
|
||||
h1
|
||||
| Blogroll
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/blogroll") blogroll
|
||||
|
||||
main
|
||||
a(href="https://ar.al/") Aral Balkan
|
||||
br
|
||||
a(href="https://write.privacytools.io/freddy/") Freddy's Ramblings
|
||||
br
|
||||
a(href="https://www.garron.blog/posts/") Guillermo Garron
|
||||
br
|
||||
a(href="https://goel.io/") Karan Goel
|
||||
br
|
||||
a(href="https://kevq.uk/") Kev Quirk
|
||||
br
|
||||
a(href="https://mikestone.me/") Mike Stone
|
||||
br
|
||||
a(href="https://degruchy.org/") Nathan DeGruchy
|
||||
|
@ -1,145 +1,147 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > contact
|
||||
h1
|
||||
| Contact me
|
||||
|
||||
main
|
||||
h2 >> Online presence
|
||||
|
||||
.wrapper-table
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Fediverse
|
||||
td
|
||||
a(href="https://fosstodon.org/@yarmo" rel="me") @yarmo@fosstodon.org
|
||||
tr
|
||||
td Lobste.rs
|
||||
td
|
||||
a(href="https://lobste.rs/u/yarmo" rel="me") yarmo
|
||||
tr
|
||||
td Codeberg
|
||||
td
|
||||
a(href="https://codeberg.org/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td GitLab
|
||||
td
|
||||
a(href="https://gitlab.com/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td Github
|
||||
td
|
||||
a(href="https://github.com/YarmoM" rel="me") @YarmoM
|
||||
|
||||
h2 >> Instant messaging
|
||||
|
||||
.wrapper-table
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Mail *
|
||||
td
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
tr
|
||||
td Delta Chat **
|
||||
td
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
tr
|
||||
td XMPP **
|
||||
td
|
||||
a(href="xmpp:yarmo@404.city") yarmo@404.city
|
||||
tr
|
||||
td Matrix **
|
||||
td
|
||||
a(href="https://matrix.to/#/@yarmo:matrix.org") @yarmo:matrix.org
|
||||
tr
|
||||
td Wire **
|
||||
td @yarmo
|
||||
tr
|
||||
td Briar **
|
||||
td
|
||||
a(href="briar://acdxuw4lym5l77ezsonbswj3aerwphckgnnrskokxw3t3mz3ncxq4" rel="me") acdxuw4lym5l77ezsonbswj3aerwphckgnnrskokxw3t3mz3ncxq4
|
||||
tr
|
||||
td Session *
|
||||
td 0526f4bf4feb46f61b3382b317276ec2e207d75b11c995d41a78c1f4f2cefbcd5f
|
||||
tr
|
||||
td Tox *
|
||||
td AD4EF6D540949E55A666D8D7BF58EF5422A91F0C99EE334ADBB7123189C4C17A640915B3CC64
|
||||
tr
|
||||
td Signal *
|
||||
td ask me on any platform above
|
||||
tr
|
||||
td Telegram
|
||||
td
|
||||
a(href="https://t.me/yarmom" rel="me") YarmoM
|
||||
tr
|
||||
td Manyverse
|
||||
td ask me on any platform above
|
||||
|
||||
p
|
||||
| ** Excellent for secure messaging
|
||||
br
|
||||
| * Adequate for secure messaging
|
||||
|
||||
h2 >> How to send an encrypted message
|
||||
|
||||
p If you wish to send me an encrypted message and make sure that I and only I can read it, use any of the instant messaging services marked with ** (preferred) or * from the list above.
|
||||
|
||||
p If you do not have accounts on any of the services above and/or prefer email, use any of the two guides below.
|
||||
header
|
||||
h1
|
||||
| Contact me
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/contact") contact
|
||||
|
||||
main
|
||||
h2 >> Online presence
|
||||
|
||||
.wrapper-table
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Fediverse
|
||||
td
|
||||
a(href="https://fosstodon.org/@yarmo" rel="me") @yarmo@fosstodon.org
|
||||
tr
|
||||
td Lobste.rs
|
||||
td
|
||||
a(href="https://lobste.rs/u/yarmo" rel="me") yarmo
|
||||
tr
|
||||
td Codeberg
|
||||
td
|
||||
a(href="https://codeberg.org/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td GitLab
|
||||
td
|
||||
a(href="https://gitlab.com/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td Github
|
||||
td
|
||||
a(href="https://github.com/YarmoM" rel="me") @YarmoM
|
||||
|
||||
h2 >> Instant messaging
|
||||
|
||||
.wrapper-table
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Mail *
|
||||
td
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
tr
|
||||
td Delta Chat **
|
||||
td
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
tr
|
||||
td XMPP **
|
||||
td
|
||||
a(href="xmpp:yarmo@404.city") yarmo@404.city
|
||||
tr
|
||||
td Matrix **
|
||||
td
|
||||
a(href="https://matrix.to/#/@yarmo:matrix.org") @yarmo:matrix.org
|
||||
tr
|
||||
td Wire **
|
||||
td @yarmo
|
||||
tr
|
||||
td Briar **
|
||||
td
|
||||
a(href="briar://acdxuw4lym5l77ezsonbswj3aerwphckgnnrskokxw3t3mz3ncxq4" rel="me") acdxuw4lym5l77ezsonbswj3aerwphckgnnrskokxw3t3mz3ncxq4
|
||||
tr
|
||||
td Session *
|
||||
td 0526f4bf4feb46f61b3382b317276ec2e207d75b11c995d41a78c1f4f2cefbcd5f
|
||||
tr
|
||||
td Tox *
|
||||
td AD4EF6D540949E55A666D8D7BF58EF5422A91F0C99EE334ADBB7123189C4C17A640915B3CC64
|
||||
tr
|
||||
td Signal *
|
||||
td ask me on any platform above
|
||||
tr
|
||||
td Telegram
|
||||
td
|
||||
a(href="https://t.me/yarmom" rel="me") YarmoM
|
||||
tr
|
||||
td Manyverse
|
||||
td ask me on any platform above
|
||||
|
||||
p
|
||||
| ** Excellent for secure messaging
|
||||
br
|
||||
| * Adequate for secure messaging
|
||||
|
||||
h2 >> How to send an encrypted message
|
||||
|
||||
p If you wish to send me an encrypted message and make sure that I and only I can read it, use any of the instant messaging services marked with ** (preferred) or * from the list above.
|
||||
|
||||
p If you do not have accounts on any of the services above and/or prefer email, use any of the two guides below.
|
||||
|
||||
h3#pgp-easy Using Keyoxide (easy)
|
||||
h3#pgp-easy Using Keyoxide (easy)
|
||||
|
||||
ol
|
||||
li
|
||||
| Visit my
|
||||
a(href="https://keyoxide.org/9f0048ac0b23301e1f77e994909f6bd6f80f485d", title="Keyoxide") Keyoxide profile
|
||||
| .
|
||||
li Press the <code>encrypt message</code> link at the bottom of the page.
|
||||
li Write the message in the big textarea.
|
||||
li Press the <code>ENCRYPT MESSAGE</code> button and copy the encrypted content.
|
||||
li
|
||||
| Send an email with the encrypted content to
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
| .
|
||||
ol
|
||||
li
|
||||
| Visit my
|
||||
a(href="https://keyoxide.org/9f0048ac0b23301e1f77e994909f6bd6f80f485d", title="Keyoxide") Keyoxide profile
|
||||
| .
|
||||
li Press the <code>encrypt message</code> link at the bottom of the page.
|
||||
li Write the message in the big textarea.
|
||||
li Press the <code>ENCRYPT MESSAGE</code> button and copy the encrypted content.
|
||||
li
|
||||
| Send an email with the encrypted content to
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
| .
|
||||
|
||||
h3#pgp-advanced#kleopatra Using the Kleopatra software (advanced)
|
||||
|
||||
ol
|
||||
li
|
||||
| Download my
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", title="PGP public key") public key
|
||||
| .
|
||||
li
|
||||
| Download Kleopatra (
|
||||
a(href="https://www.openpgp.org/software/kleopatra/") Linux
|
||||
| -
|
||||
a(href="https://www.gpg4win.org/get-gpg4win.html") Windows
|
||||
| -
|
||||
a(href="https://chocolatey.org/packages/Gpg4win") Chocolatey
|
||||
| ) and run it.
|
||||
li Press <code>Import</code> and locate the file downloaded in the first step.
|
||||
li You may be asked to "Certify" the public key. This is optional (read below) and can be skipped.
|
||||
li Go to the <code>Notepad</code> section.
|
||||
li After you are done writing your message, go to the <code>Recipients</code> tab and choose <code>Yarmo Mackenbach</code> in the <code>Encrypt for others:</code> field.
|
||||
li Return to the <code>Notepad</code> tab and press the <code>Encrypt Notepad</code> button.
|
||||
li
|
||||
| Copy the entire message that is now displayed and send it to
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
| .
|
||||
|
||||
p
|
||||
| "Certifying" my key via Kleopatra requires you to make your own keypair. To manually verify the validity of my public key, right-click on my key, press on <code>Details</code> and make sure the <code>fingerprint</code> matches my
|
||||
a(href="/pgp") PGP signature
|
||||
| .
|
||||
|
||||
h3 Getting an encrypted response
|
||||
h3#pgp-advanced Using the Kleopatra software (advanced)
|
||||
|
||||
ol
|
||||
li
|
||||
| Download my
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", title="PGP public key") public key
|
||||
| .
|
||||
li
|
||||
| Download Kleopatra (
|
||||
a(href="https://www.openpgp.org/software/kleopatra/") Linux
|
||||
| -
|
||||
a(href="https://www.gpg4win.org/get-gpg4win.html") Windows
|
||||
| -
|
||||
a(href="https://chocolatey.org/packages/Gpg4win") Chocolatey
|
||||
| ) and run it.
|
||||
li Press <code>Import</code> and locate the file downloaded in the first step.
|
||||
li You may be asked to "Certify" the public key. This is optional (read below) and can be skipped.
|
||||
li Go to the <code>Notepad</code> section.
|
||||
li After you are done writing your message, go to the <code>Recipients</code> tab and choose <code>Yarmo Mackenbach</code> in the <code>Encrypt for others:</code> field.
|
||||
li Return to the <code>Notepad</code> tab and press the <code>Encrypt Notepad</code> button.
|
||||
li
|
||||
| Copy the entire message that is now displayed and send it to
|
||||
a(href="mailto:yarmo@yarmo.eu") yarmo@yarmo.eu
|
||||
| .
|
||||
|
||||
p
|
||||
| "Certifying" my key via Kleopatra requires you to make your own keypair. To manually verify the validity of my public key, right-click on my key, press on <code>Details</code> and make sure the <code>fingerprint</code> matches my
|
||||
a(href="/pgp") PGP signature
|
||||
| .
|
||||
|
||||
h3 Getting an encrypted response
|
||||
|
||||
p If you would like to get an encrypted response from me, please include the public key of your personal keypair or upload it to a key server.
|
||||
|
||||
p If you do not have a personal keypair yet, both Mailvelope and Kleopatra allow you to generate a new keypair. The public part of your keypair can be used by anyone to encrypt their messages to you, and the secret part MUST REMAIN SECRET and can be used by you AND ONLY YOU to decrypt those messages.
|
||||
|
||||
p Please note that dealing with PGP keys is advanced stuff: if you are not comfortable with PGP, let's talk using any of the encrypted instant messaging tools listed above.
|
||||
p If you would like to get an encrypted response from me, please include the public key of your personal keypair or upload it to a key server.
|
||||
|
||||
p If you do not have a personal keypair yet, both Mailvelope and Kleopatra allow you to generate a new keypair. The public part of your keypair can be used by anyone to encrypt their messages to you, and the secret part MUST REMAIN SECRET and can be used by you AND ONLY YOU to decrypt those messages.
|
||||
|
||||
p Please note that dealing with PGP keys is advanced stuff: if you are not comfortable with PGP, let's talk using any of the encrypted instant messaging tools listed above.
|
||||
|
@ -1,18 +1,46 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > feeds
|
||||
h1
|
||||
| Feeds
|
||||
|
||||
main
|
||||
ul
|
||||
li
|
||||
a(href="/rss/all") RSS blog and notes
|
||||
li
|
||||
a(href="/rss/blog") RSS blog
|
||||
li
|
||||
a(href="/rss/notes") RSS notes
|
||||
header
|
||||
h1
|
||||
| Feeds
|
||||
nav
|
||||
| >>
|
||||
a(href="/feeds") feeds
|
||||
nav
|
||||
| Go to:
|
||||
a(href="/about") about me
|
||||
| |
|
||||
a(href="/blog") blog
|
||||
| |
|
||||
a(href="/notes") notes
|
||||
|
||||
main
|
||||
ul
|
||||
li
|
||||
p
|
||||
| Feed for blog posts and notes (
|
||||
a(href="/feed/all") RSS
|
||||
| -
|
||||
a(href="/feed/all.atom") ATOM
|
||||
| -
|
||||
a(href="/feed/all.") JSON
|
||||
| )
|
||||
li
|
||||
p
|
||||
| Feed for blog posts (
|
||||
a(href="/feed/blog") RSS
|
||||
| -
|
||||
a(href="/feed/blog.atom") ATOM
|
||||
| -
|
||||
a(href="/feed/blog.json") JSON
|
||||
| )
|
||||
li
|
||||
p
|
||||
| Feed for notes (
|
||||
a(href="/feed/notes") RSS
|
||||
| -
|
||||
a(href="/feed/notes.atom") ATOM
|
||||
| -
|
||||
a(href="/feed/notes.json") JSON
|
||||
| )
|
||||
|
@ -1,42 +1,42 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
mixin foss_contribution($item)
|
||||
p
|
||||
a(href="{$item['url-repo']}") !{$item['repo']}
|
||||
| —
|
||||
a(href="{$item['url-item']}") #!{$item['id']}
|
||||
br
|
||||
| !{$item['title']}
|
||||
|
||||
p
|
||||
a(href="{$item['url-repo']}") !{$item['repo']}
|
||||
| —
|
||||
a(href="{$item['url-item']}") #!{$item['id']}
|
||||
br
|
||||
| !{$item['title']}
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > foss
|
||||
h1
|
||||
| FOSS
|
||||
|
||||
main
|
||||
h2 >> VCS accounts
|
||||
|
||||
.wrapper-table
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Codeberg
|
||||
td
|
||||
a(href="https://codeberg.org/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td GitLab
|
||||
td
|
||||
a(href="https://gitlab.com/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td Github
|
||||
td
|
||||
a(href="https://github.com/YarmoM" rel="me") @YarmoM
|
||||
|
||||
h2 >> Contributions
|
||||
|
||||
each $item in $foss
|
||||
+foss_contribution($item)
|
||||
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > foss
|
||||
h1
|
||||
| FOSS
|
||||
|
||||
main
|
||||
h2 >> VCS accounts
|
||||
|
||||
.wrapper-table
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Codeberg
|
||||
td
|
||||
a(href="https://codeberg.org/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td GitLab
|
||||
td
|
||||
a(href="https://gitlab.com/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td Github
|
||||
td
|
||||
a(href="https://github.com/YarmoM" rel="me") @YarmoM
|
||||
|
||||
h2 >> Contributions
|
||||
|
||||
each $item in $foss
|
||||
+foss_contribution($item)
|
||||
|
||||
|
92
views/id.pug
@ -1,47 +1,47 @@
|
||||
.id-card
|
||||
img(src="/static/img/profile.png", alt="Yarmo's profile picture").profile-picture.u-photo
|
||||
h1
|
||||
a(href="https://yarmo.eu", rel="me", title="yarmo.eu").u-url.u-uid
|
||||
span(style="display:none").p-honorific-prefix Mr.
|
||||
span.p-name
|
||||
span.p-given-name Yarmo
|
||||
|
|
||||
span.p-additional-name Mackenbach
|
||||
br
|
||||
p.p-note
|
||||
| Developer of
|
||||
a(href="https://keyoxide.org") Keyoxide.org
|
||||
| .
|
||||
br
|
||||
| Finished a PhD in <span class="p-category">neurosciences</span>.
|
||||
p
|
||||
| Pronouns:
|
||||
a(href="https://pronoun.is/he").u-pronoun he/him/his
|
||||
p
|
||||
| PGP:
|
||||
a(href="https://keyoxide.org/9f0048ac0b23301e1f77e994909f6bd6f80f485d", rel="me", title="Keyoxide profile").u-url Keyoxide profile
|
||||
| /
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", rel="pgpkey publickey authn", type="text/plain", title="PGP public key").u-key public.asc
|
||||
| /
|
||||
a(href="/static/img/qr_fp.png", title="PGP fingerprint QR") QR
|
||||
p
|
||||
| Topics:
|
||||
| #<span class="p-category">FOSS</span>
|
||||
| #<span class="p-category">privacy</span>
|
||||
| #<span class="p-category">encryption</span>
|
||||
| #<span class="p-category">decentralization</span>
|
||||
| #<span class="p-category">music</span>
|
||||
p
|
||||
| Links:
|
||||
a(href="mailto:yarmo@yarmo.eu", rel="me", title="Email").u-email Email
|
||||
| /
|
||||
a(href="xmpp:yarmo@snopyta.org", rel="me", title="XMPP").u-xmpp XMPP
|
||||
| /
|
||||
a(href="https://fosstodon.org/@yarmo", rel="me", title="Fediverse").u-url Fediverse
|
||||
| /
|
||||
a(href="https://git.yarmo.eu/yarmo", rel="me", title="Gitea").u-url Gitea
|
||||
p
|
||||
| Location:
|
||||
span.p-country-name The Netherlands
|
||||
pre(style="display:none").key
|
||||
include ../9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc
|
||||
img(src="/static/img/profile.png", alt="Yarmo's profile picture").profile-picture.u-photo
|
||||
h1
|
||||
a(href="https://yarmo.eu", rel="me", title="yarmo.eu").u-url.u-uid
|
||||
span(style="display:none").p-honorific-prefix Mr.
|
||||
span.p-name
|
||||
span.p-given-name Yarmo
|
||||
|
|
||||
span.p-additional-name Mackenbach
|
||||
br
|
||||
p.p-note
|
||||
| Developer of
|
||||
a(href="https://keyoxide.org") Keyoxide.org
|
||||
| .
|
||||
br
|
||||
| Finished a PhD in <span class="p-category">neurosciences</span>.
|
||||
p
|
||||
| Pronouns:
|
||||
a(href="https://pronoun.is/he").u-pronoun he/him/his
|
||||
p
|
||||
| PGP:
|
||||
a(href="https://keyoxide.org/9f0048ac0b23301e1f77e994909f6bd6f80f485d", rel="me", title="Keyoxide profile").u-url Keyoxide profile
|
||||
| /
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", rel="pgpkey publickey authn", type="text/plain", title="PGP public key").u-key public.asc
|
||||
| /
|
||||
a(href="/static/img/qr_fp.png", title="PGP fingerprint QR") QR
|
||||
p
|
||||
| Topics:
|
||||
| #<span class="p-category">FOSS</span>
|
||||
| #<span class="p-category">privacy</span>
|
||||
| #<span class="p-category">encryption</span>
|
||||
| #<span class="p-category">decentralization</span>
|
||||
| #<span class="p-category">music</span>
|
||||
p
|
||||
| Links:
|
||||
a(href="mailto:yarmo@yarmo.eu", rel="me", title="Email").u-email Email
|
||||
| /
|
||||
a(href="xmpp:yarmo@snopyta.org", rel="me", title="XMPP").u-xmpp XMPP
|
||||
| /
|
||||
a(href="https://fosstodon.org/@yarmo", rel="me", title="Fediverse").u-url Fediverse
|
||||
| /
|
||||
a(href="https://git.yarmo.eu/yarmo", rel="me", title="Gitea").u-url Gitea
|
||||
p
|
||||
| Location:
|
||||
span.p-country-name The Netherlands
|
||||
pre(style="display:none").key
|
||||
include ../keys/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc
|
||||
|
@ -1,38 +0,0 @@
|
||||
extends layout
|
||||
|
||||
mixin entry($item)
|
||||
article.longform_list__item.h-entry
|
||||
p
|
||||
a(href="{$item['urlrel']}").p-name.u-url !{$item['title']}
|
||||
br
|
||||
| Posted on
|
||||
time(datetime="{$item['date']}").dt-published !{$item['date_formatted']}
|
||||
|
||||
block content
|
||||
.h-card
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > blog
|
||||
h1
|
||||
| Yarmo's blog
|
||||
nav
|
||||
| Go to:
|
||||
a(href="/about") about me
|
||||
| |
|
||||
a(href="/notes") notes
|
||||
| |
|
||||
a(href="/blogroll") blogroll
|
||||
| |
|
||||
a(href="/feeds") feeds
|
||||
| |
|
||||
a(href="/contact") contact
|
||||
|
||||
main
|
||||
|
||||
div(style="display:none")
|
||||
include id
|
||||
|
||||
.longform_list.h-feed
|
||||
each $item in $posts
|
||||
+entry($item)
|
@ -1,37 +0,0 @@
|
||||
html
|
||||
head
|
||||
meta(http-equiv="Content-Type", content="text/html;charset=UTF-8")
|
||||
meta(name="viewport", content="width=device-width, initial-scale=1.0")
|
||||
meta(name="theme-color", content="#4ab4ab")
|
||||
meta(http-equiv="X-UA-Compatible", content="ie=edge")
|
||||
title #{$title}
|
||||
link(rel="stylesheet", href="/static/dank-mono.css")
|
||||
style
|
||||
dyninclude "static/norm.css"
|
||||
style
|
||||
dyninclude "static/style.css"
|
||||
link(rel="shortcut icon", href="/favicon.png")
|
||||
link(rel="alternate", href="/rss/all", title="RSS blog feed for yarmo.eu", type="application/rss+xml")
|
||||
link(rel="webmention", href="https://webm.yarmo.eu/receive")
|
||||
script(asyc, defer, data-domain="yarmo.eu", src="https://plausible.io/js/plausible.js")
|
||||
body
|
||||
.container
|
||||
block content
|
||||
|
||||
footer
|
||||
p
|
||||
| Ads: no.
|
||||
br
|
||||
| Analytics:
|
||||
a(href="https://plausible.io") Plausible
|
||||
|.
|
||||
br
|
||||
| Open source:
|
||||
a(href="https://git.yarmo.eu/yarmo/yarmo.eu") source code
|
||||
| and
|
||||
a(href="https://drone.yarmo.eu/yarmo/yarmo.eu/") CI/CD pipelines
|
||||
|.
|
||||
br
|
||||
| All content licensed under
|
||||
a(href="https://creativecommons.org/licenses/by-nc-sa/4.0/") CC BY-NC-SA 4.0
|
||||
| unless otherwise stated.
|
@ -1,50 +1,50 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > music
|
||||
h1 Yarmo's music
|
||||
p
|
||||
| Go to:
|
||||
a(href="/vinyl") vinyl
|
||||
| |
|
||||
a(href="/aotw") Album Of The Week
|
||||
header
|
||||
h1 Yarmo's music
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/music") music
|
||||
nav
|
||||
| Go to:
|
||||
a(href="/vinyl") vinyl
|
||||
|
||||
main
|
||||
h3 Instruments
|
||||
ul
|
||||
li Piano / keyboards
|
||||
li Organs
|
||||
li Synths
|
||||
|
||||
main
|
||||
h3 Instruments
|
||||
ul
|
||||
li Piano / keyboards
|
||||
li Organs
|
||||
li Synths
|
||||
|
||||
h3 Releases
|
||||
.wrapper-table
|
||||
table.music
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Band
|
||||
th Title
|
||||
tbody
|
||||
tr
|
||||
td 2013
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/979132997") Sir Jupiter
|
||||
tr
|
||||
td 2015
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/1041392608") EP Roots
|
||||
tr
|
||||
td 2016
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/1151887625") EP Wings
|
||||
tr
|
||||
td 2018
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/1454734101") EP Covered In Snow
|
||||
h3 Releases
|
||||
.wrapper-table
|
||||
table.music
|
||||
thead
|
||||
tr
|
||||
th Years
|
||||
th Band
|
||||
th Title
|
||||
tbody
|
||||
tr
|
||||
td 2013
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/979132997") Sir Jupiter
|
||||
tr
|
||||
td 2015
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/1041392608") EP Roots
|
||||
tr
|
||||
td 2016
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/1151887625") EP Wings
|
||||
tr
|
||||
td 2018
|
||||
td Sir Jupiter
|
||||
td
|
||||
a(href="https://song.link/album/nl/i/1454734101") EP Covered In Snow
|
||||
|
@ -1,21 +1,33 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
mixin entry($item)
|
||||
article.longform_list__item.h-entry
|
||||
p
|
||||
a(href="{$item['urlrel']}").p-nameu-url !{$item['title']}
|
||||
br
|
||||
| Posted on
|
||||
time(datetime="{$item['date']}").dt-published !{$item['date_formatted']}
|
||||
mixin entry(item)
|
||||
article.longform_list__item.h-entry
|
||||
p
|
||||
a(href=item['urlrel']).p-nameu-url !{item['title']}
|
||||
br
|
||||
| Posted on
|
||||
time(datetime=item['date']).dt-published !{item['date_formatted']}
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > notes
|
||||
h1 Yarmo's notes
|
||||
|
||||
main
|
||||
.longform_list
|
||||
each $item in $posts
|
||||
+entry($item)
|
||||
header
|
||||
//nav
|
||||
a(href="/about") about me
|
||||
| > notes
|
||||
h1 Yarmo's notes
|
||||
nav
|
||||
| >>
|
||||
a(href="/notes") notes
|
||||
nav
|
||||
| Go to:
|
||||
a(href="/about") about me
|
||||
| |
|
||||
a(href="/") blog
|
||||
| |
|
||||
a(href="/feeds") feeds
|
||||
| |
|
||||
a(href="/contact") contact
|
||||
|
||||
main
|
||||
.longform_list
|
||||
each item in notes
|
||||
+entry(item)
|
||||
|
@ -1,24 +1,26 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > now
|
||||
h1
|
||||
| Now
|
||||
|
||||
main
|
||||
h3 Working on
|
||||
ul
|
||||
li
|
||||
a(href="https://keyoxide.org") Keyoxide.org
|
||||
li finding a job
|
||||
li finishing a scientific publication
|
||||
li numerous FOSS projects
|
||||
li new music
|
||||
|
||||
h3 Enjoying
|
||||
ul
|
||||
li room renovations
|
||||
li modular synthesis
|
||||
header
|
||||
h1
|
||||
| Now
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/now") now
|
||||
|
||||
main
|
||||
h3 Working on
|
||||
ul
|
||||
li
|
||||
a(href="https://keyoxide.org") Keyoxide.org
|
||||
li finding a job
|
||||
li finishing a scientific publication
|
||||
li numerous FOSS projects
|
||||
li new music
|
||||
|
||||
h3 Enjoying
|
||||
ul
|
||||
li room renovations
|
||||
li modular synthesis
|
||||
|
@ -1,28 +1,30 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > pgp
|
||||
h1
|
||||
| PGP public key
|
||||
header
|
||||
h1
|
||||
| PGP public key
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/pgp") pgp
|
||||
|
||||
main
|
||||
h3#pgp-fingerprint Fingerprint
|
||||
p 9f0048ac0b23301e1f77e994909f6bd6f80f485d
|
||||
|
||||
main
|
||||
h3#pgp-fingerprint Fingerprint
|
||||
p 9f0048ac0b23301e1f77e994909f6bd6f80f485d
|
||||
|
||||
h3 Key servers
|
||||
p
|
||||
a(href="https://keys.openpgp.org/") OpenPGP key server
|
||||
|
||||
h3 Files
|
||||
p
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.pgp", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.pgp", rel="pgpkey", type="application/pgp-keys", title="PGP (binary)") Binary format
|
||||
br
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", rel="pgpkey", type="text/plain", title="PGP (text)") Text format
|
||||
|
||||
h3#pgp-public-key Plaintext
|
||||
pre.select-all
|
||||
code
|
||||
include ../9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc
|
||||
h3 Key servers
|
||||
p
|
||||
a(href="https://keys.openpgp.org/") OpenPGP key server
|
||||
|
||||
h3 Files
|
||||
p
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.pgp", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.pgp", rel="pgpkey", type="application/pgp-keys", title="PGP (binary)") Binary format
|
||||
br
|
||||
a(href="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", download="/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc", rel="pgpkey", type="text/plain", title="PGP (text)") Text format
|
||||
|
||||
h3#pgp-public-key Plaintext
|
||||
pre.select-all
|
||||
code
|
||||
include ../keys/9F0048AC0B23301E1F77E994909F6BD6F80F485D.asc
|
||||
|
134
views/post.pug
@ -1,72 +1,72 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
mixin webmention($item)
|
||||
if (!array_key_exists('type', $item))
|
||||
p
|
||||
if (array_key_exists('title', $item))
|
||||
a(href="{$item['source']}") !{$item['title']}
|
||||
else
|
||||
a(href="{$item['source']}") !{$item['source']}
|
||||
if (array_key_exists('author_name', $item))
|
||||
| by !{$item['author_name']}
|
||||
if (array_key_exists('date', $item))
|
||||
| on !{$item['date']}
|
||||
if (array_key_exists('time', $item))
|
||||
| at !{$item['time']} UTC
|
||||
else if ($item['type'] == "comment")
|
||||
.comment
|
||||
p.quote
|
||||
if (array_key_exists('title', $item))
|
||||
strong !{$item['title']}
|
||||
br
|
||||
if (array_key_exists('content', $item))
|
||||
| !{$item['content']}
|
||||
p.sub
|
||||
a(href="{$item['source']}") Commented
|
||||
if (array_key_exists('author_name', $item))
|
||||
| by !{$item['author_name']}
|
||||
if (array_key_exists('date', $item))
|
||||
| on !{$item['date']}
|
||||
if (array_key_exists('time', $item))
|
||||
| at !{$item['time']} UTC
|
||||
|
||||
mixin discussionLink($item)
|
||||
mixin webmention(item)
|
||||
if (!('type' in item))
|
||||
p
|
||||
a(href="{$item}") !{$item}
|
||||
if ('title' in item)
|
||||
a(href=item.source) !{item.title}
|
||||
else
|
||||
a(href=item.source) !{item.source}
|
||||
if ('author_name' in item)
|
||||
| by !{item.author_name}
|
||||
if ('date' in item)
|
||||
| on !{item.date}
|
||||
if ('time' in item)
|
||||
| at !{item.time}
|
||||
else if (item.type == "comment")
|
||||
.comment
|
||||
p.quote
|
||||
if ('title' in item)
|
||||
strong !{item.title}
|
||||
br
|
||||
if ('content' in item)
|
||||
| !{item.content}
|
||||
p.sub
|
||||
a(href=item.source) Commented
|
||||
if ('author_name' in item)
|
||||
| by !{item.author_name}
|
||||
if ('date' in item)
|
||||
| on !{item.date}
|
||||
if ('time' in item)
|
||||
| at !{item.time} UTC
|
||||
|
||||
mixin discussionLink(item)
|
||||
p
|
||||
a(href=item) !{item}
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| >
|
||||
if ($post["type"] == "blog")
|
||||
a(href="/") blog
|
||||
if ($post["type"] == "note")
|
||||
a(href="/notes") notes
|
||||
| > !{$post['slug']}
|
||||
|
||||
main
|
||||
article.longform.h-entry
|
||||
h1.p-name !{$post['title']}
|
||||
p.longform__header
|
||||
| Posted on
|
||||
a(href="{$post['url']}" datetime="{$post['date']}").u-url.dt-published !{$post['date_formatted']}
|
||||
| by
|
||||
a(href="/" rel="author").p-author.h-card !{$post['author']}
|
||||
|
||||
.longform__content.e-content
|
||||
| !{$post['content']}
|
||||
header
|
||||
nav
|
||||
| >>
|
||||
if post.type == 'blog'
|
||||
a(href="/") blog
|
||||
if post.type == 'note'
|
||||
a(href="/notes") notes
|
||||
| >
|
||||
a(href=post.url)= post.slug
|
||||
|
||||
main
|
||||
article.longform.h-entry
|
||||
h1.p-name !{post.title}
|
||||
p.longform__header
|
||||
| Posted on
|
||||
a(href=post.url datetime="{post.date}").u-url.dt-published !{post.date_formatted}
|
||||
| by
|
||||
a(href="/" rel="author").p-author.h-card !{post.author}
|
||||
|
||||
.longform__content.e-content
|
||||
| !{post.content}
|
||||
|
||||
if ('discussion' in post && post.discussion)
|
||||
.discussion.subsection
|
||||
h2 Join the discussion
|
||||
each item in post.discussion
|
||||
+discussionLink(item)
|
||||
|
||||
if (array_key_exists('discussion', $post) && isset($post['discussion']))
|
||||
.discussion.subsection
|
||||
h2 Join the discussion
|
||||
each $item in $post["discussion"]
|
||||
+discussionLink($item)
|
||||
|
||||
.webmentions.subsection
|
||||
h2 Webmentions
|
||||
if ($post['hasWebmentions'])
|
||||
each $item in $post["webmentions"]
|
||||
+webmention($item)
|
||||
else
|
||||
p This post has not been mentioned yet.
|
||||
.webmentions.subsection
|
||||
h2 Webmentions
|
||||
if post.hasWebmentions
|
||||
each item in post.webmentions
|
||||
+webmention(item)
|
||||
else
|
||||
p This post has not been mentioned yet.
|
||||
|
@ -1,20 +1,20 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
mixin entry($item)
|
||||
article.longform_list__item
|
||||
p
|
||||
a(href="{$item['urlrel']}") !{$item['title']}
|
||||
br
|
||||
| Status: !{$item['status']}
|
||||
article.longform_list__item
|
||||
p
|
||||
a(href="{$item['urlrel']}") !{$item['title']}
|
||||
br
|
||||
| Status: !{$item['status']}
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > projects
|
||||
h1 Yarmo's projects
|
||||
|
||||
main
|
||||
.longform_list
|
||||
each $item in $projects
|
||||
+entry($item)
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > projects
|
||||
h1 Yarmo's projects
|
||||
|
||||
main
|
||||
.longform_list
|
||||
each $item in $projects
|
||||
+entry($item)
|
||||
|
@ -1,32 +1,32 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
mixin webmention($item)
|
||||
p
|
||||
a(href="{$item['source']}") !{$item['title']}
|
||||
| by !{$item['author_name']} on !{$item['date']} at !{$item['time']}
|
||||
p
|
||||
a(href="{$item['source']}") !{$item['title']}
|
||||
| by !{$item['author_name']} on !{$item['date']} at !{$item['time']}
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/projects") projects
|
||||
| > !{$project['slug']}
|
||||
|
||||
main
|
||||
article.longform
|
||||
h1 !{$project['title']}
|
||||
p
|
||||
| Got an idea or a comment?
|
||||
a(href="/contact") Feel free to contact me!
|
||||
p.longform__header
|
||||
.longform__content
|
||||
| !{$project['content']}
|
||||
|
||||
.webmentions.subsection
|
||||
h2 Webmentions
|
||||
if ($project['hasWebmentions'])
|
||||
each $item in $project["webmentions"]
|
||||
+webmention($item)
|
||||
else
|
||||
p This project has not been mentioned yet.
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/projects") projects
|
||||
| > !{$project['slug']}
|
||||
|
||||
main
|
||||
article.longform
|
||||
h1 !{$project['title']}
|
||||
p
|
||||
| Got an idea or a comment?
|
||||
a(href="/contact") Feel free to contact me!
|
||||
p.longform__header
|
||||
.longform__content
|
||||
| !{$project['content']}
|
||||
|
||||
.webmentions.subsection
|
||||
h2 Webmentions
|
||||
if ($project['hasWebmentions'])
|
||||
each $item in $project["webmentions"]
|
||||
+webmention($item)
|
||||
else
|
||||
p This project has not been mentioned yet.
|
||||
|
114
views/reply.pug
@ -1,61 +1,61 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
mixin webmention($item)
|
||||
if (!array_key_exists('type', $item))
|
||||
p
|
||||
if (array_key_exists('title', $item))
|
||||
a(href="{$item['source']}") !{$item['title']}
|
||||
else
|
||||
a(href="{$item['source']}") !{$item['source']}
|
||||
if (array_key_exists('author_name', $item))
|
||||
| by !{$item['author_name']}
|
||||
if (array_key_exists('date', $item))
|
||||
| on !{$item['date']}
|
||||
if (array_key_exists('time', $item))
|
||||
| at !{$item['time']} UTC
|
||||
else if ($item['type'] == "comment")
|
||||
.comment
|
||||
p.quote
|
||||
if (array_key_exists('title', $item))
|
||||
strong !{$item['title']}
|
||||
br
|
||||
if (array_key_exists('content', $item))
|
||||
| !{$item['content']}
|
||||
p.sub
|
||||
a(href="{$item['source']}") Commented
|
||||
if (array_key_exists('author_name', $item))
|
||||
| by !{$item['author_name']}
|
||||
if (array_key_exists('date', $item))
|
||||
| on !{$item['date']}
|
||||
if (array_key_exists('time', $item))
|
||||
| at !{$item['time']} UTC
|
||||
if (!array_key_exists('type', $item))
|
||||
p
|
||||
if (array_key_exists('title', $item))
|
||||
a(href="{$item['source']}") !{$item['title']}
|
||||
else
|
||||
a(href="{$item['source']}") !{$item['source']}
|
||||
if (array_key_exists('author_name', $item))
|
||||
| by !{$item['author_name']}
|
||||
if (array_key_exists('date', $item))
|
||||
| on !{$item['date']}
|
||||
if (array_key_exists('time', $item))
|
||||
| at !{$item['time']} UTC
|
||||
else if ($item['type'] == "comment")
|
||||
.comment
|
||||
p.quote
|
||||
if (array_key_exists('title', $item))
|
||||
strong !{$item['title']}
|
||||
br
|
||||
if (array_key_exists('content', $item))
|
||||
| !{$item['content']}
|
||||
p.sub
|
||||
a(href="{$item['source']}") Commented
|
||||
if (array_key_exists('author_name', $item))
|
||||
| by !{$item['author_name']}
|
||||
if (array_key_exists('date', $item))
|
||||
| on !{$item['date']}
|
||||
if (array_key_exists('time', $item))
|
||||
| at !{$item['time']} UTC
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > !{$reply['slug']}
|
||||
|
||||
main
|
||||
article.longform.h-entry
|
||||
h1.p-name !{$reply['title']}
|
||||
p.longform__header
|
||||
| Posted on
|
||||
a(href="{$reply['url']}" datetime="{$reply['date']}").u-url.dt-published !{$reply['date_formatted']}
|
||||
| by
|
||||
a(href="/" rel="author").p-author.h-card !{$reply['author']}
|
||||
|
||||
.longform__content.e-content
|
||||
| Reply to
|
||||
a(href="{$reply['reply-url']}").in-reply-to !{$reply['reply-title']}
|
||||
| :
|
||||
br
|
||||
| !{$reply['content']}
|
||||
|
||||
.webmentions.subsection
|
||||
h2 Webmentions
|
||||
if ($reply['hasWebmentions'])
|
||||
each $item in $reply["webmentions"]
|
||||
+webmention($item)
|
||||
else
|
||||
p This post has not been mentioned yet.
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > !{$reply['slug']}
|
||||
|
||||
main
|
||||
article.longform.h-entry
|
||||
h1.p-name !{$reply['title']}
|
||||
p.longform__header
|
||||
| Posted on
|
||||
a(href="{$reply['url']}" datetime="{$reply['date']}").u-url.dt-published !{$reply['date_formatted']}
|
||||
| by
|
||||
a(href="/" rel="author").p-author.h-card !{$reply['author']}
|
||||
|
||||
.longform__content.e-content
|
||||
| Reply to
|
||||
a(href="{$reply['reply-url']}").in-reply-to !{$reply['reply-title']}
|
||||
| :
|
||||
br
|
||||
| !{$reply['content']}
|
||||
|
||||
.webmentions.subsection
|
||||
h2 Webmentions
|
||||
if ($reply['hasWebmentions'])
|
||||
each $item in $reply["webmentions"]
|
||||
+webmention($item)
|
||||
else
|
||||
p This post has not been mentioned yet.
|
||||
|
37
views/templates/main.pug
Normal file
@ -0,0 +1,37 @@
|
||||
html
|
||||
head
|
||||
meta(http-equiv="Content-Type", content="text/html;charset=UTF-8")
|
||||
meta(name="viewport", content="width=device-width, initial-scale=1.0")
|
||||
meta(name="theme-color", content="#4ab4ab")
|
||||
meta(http-equiv="X-UA-Compatible", content="ie=edge")
|
||||
title #{title}
|
||||
link(rel="stylesheet", href="/static/dank-mono.css")
|
||||
link(rel="stylesheet", href="/static/norm.css")
|
||||
link(rel="stylesheet", href="/static/style.css")
|
||||
link(rel="shortcut icon", href="/favicon.png")
|
||||
link(rel="alternate", href="/feed/all", title="RSS blog feed for yarmo.eu", type="application/rss+xml")
|
||||
link(rel="alternate", href="/feed/all.atom", title="RSS blog feed for yarmo.eu", type="application/atom+xml")
|
||||
link(rel="alternate", href="/feed/all.json", title="RSS blog feed for yarmo.eu", type="application/feed+json")
|
||||
link(rel="webmention", href="https://webm.yarmo.eu/receive")
|
||||
script(asyc, defer, data-domain="yarmo.eu", src="https://plausible.io/js/plausible.js")
|
||||
body
|
||||
.container
|
||||
block content
|
||||
|
||||
footer
|
||||
p
|
||||
| Ads: no.
|
||||
br
|
||||
| Analytics:
|
||||
a(href="https://plausible.io") Plausible
|
||||
|.
|
||||
br
|
||||
| Open source:
|
||||
a(href="https://git.yarmo.eu/yarmo/yarmo.eu") source code
|
||||
| and
|
||||
a(href="https://drone.yarmo.eu/yarmo/yarmo.eu/") CI/CD pipelines
|
||||
|.
|
||||
br
|
||||
| All content licensed under
|
||||
a(href="https://creativecommons.org/licenses/by-nc-sa/4.0/") CC BY-NC-SA 4.0
|
||||
| unless otherwise stated.
|
218
views/uses.pug
@ -1,112 +1,114 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| > uses
|
||||
h1
|
||||
| Uses
|
||||
header
|
||||
h1
|
||||
| Uses
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/uses") uses
|
||||
|
||||
main
|
||||
h2 Hardware
|
||||
|
||||
main
|
||||
h2 Hardware
|
||||
|
||||
ul
|
||||
li Main dev computer
|
||||
ul
|
||||
li MODEL: custom built
|
||||
li CPU: AMD Ryzen 5 3600
|
||||
li GPU: AMD RX580
|
||||
li RAM: 16GB
|
||||
li OS: Solus & W10
|
||||
li Homelab
|
||||
ul
|
||||
li MODEL: Intel NUC8i5BEK
|
||||
li CPU: Intel Core i5-8259U
|
||||
li RAM: 16GB
|
||||
li OS: Ubuntu 18.04
|
||||
li Laptop
|
||||
ul
|
||||
li MODEL: Lenovo Thinkpad x201i
|
||||
li CPU: Intel Core i3-330M
|
||||
li OS: Solus
|
||||
li Phone
|
||||
ul
|
||||
li MODEL: OnePlus 5T
|
||||
li OS: LineageOS
|
||||
li Network
|
||||
ul
|
||||
li
|
||||
a(href="https://www.asus.com/Networking/Blue-Cave/") ASUS Blue Cave
|
||||
li HiFi
|
||||
ul
|
||||
li
|
||||
a(href="https://www.project-audio.com/en/product/primary-e/") Pro-Ject Primary E
|
||||
li
|
||||
a(href="https://www.wharfedale.co.uk/diamond-11-2/") Wharfedale Diamond 11.2
|
||||
li Music
|
||||
ul
|
||||
li
|
||||
a(href="https://usa.yamaha.com/products/proaudio/speakers/hs_series/index.html") Yamaha HS8
|
||||
li
|
||||
a(href="https://www.zoom.co.jp/products/multi-track-recorder/r16-recorder-interface-controller") Zoom R16
|
||||
li
|
||||
a(href="https://www.nordkeyboards.com/products/nord-electro-5") Nord Electro 5D
|
||||
li
|
||||
a(href="https://www.arturia.com/microbrute-se/overview") Arturia Minibrute SE Pastel Orange
|
||||
li
|
||||
a(href="https://www.arturia.com/drumbrute/overview") Arturia DrumBrute
|
||||
li Custom modular synth
|
||||
ul
|
||||
li Main dev computer
|
||||
ul
|
||||
li MODEL: custom built
|
||||
li CPU: AMD Ryzen 5 3600
|
||||
li GPU: AMD RX580
|
||||
li RAM: 16GB
|
||||
li OS: Solus & W10
|
||||
li Homelab
|
||||
ul
|
||||
li MODEL: Intel NUC8i5BEK
|
||||
li CPU: Intel Core i5-8259U
|
||||
li RAM: 16GB
|
||||
li OS: Ubuntu 18.04
|
||||
li Laptop
|
||||
ul
|
||||
li MODEL: Lenovo Thinkpad x201i
|
||||
li CPU: Intel Core i3-330M
|
||||
li OS: Solus
|
||||
li Phone
|
||||
ul
|
||||
li MODEL: OnePlus 5T
|
||||
li OS: LineageOS
|
||||
li Network
|
||||
ul
|
||||
li
|
||||
a(href="https://www.asus.com/Networking/Blue-Cave/") ASUS Blue Cave
|
||||
li HiFi
|
||||
ul
|
||||
li
|
||||
a(href="https://www.project-audio.com/en/product/primary-e/") Pro-Ject Primary E
|
||||
li
|
||||
a(href="https://www.wharfedale.co.uk/diamond-11-2/") Wharfedale Diamond 11.2
|
||||
li Music
|
||||
ul
|
||||
li
|
||||
a(href="https://usa.yamaha.com/products/proaudio/speakers/hs_series/index.html") Yamaha HS8
|
||||
li
|
||||
a(href="https://www.zoom.co.jp/products/multi-track-recorder/r16-recorder-interface-controller") Zoom R16
|
||||
li
|
||||
a(href="https://www.nordkeyboards.com/products/nord-electro-5") Nord Electro 5D
|
||||
li
|
||||
a(href="https://www.arturia.com/microbrute-se/overview") Arturia Minibrute SE Pastel Orange
|
||||
li
|
||||
a(href="https://www.arturia.com/drumbrute/overview") Arturia DrumBrute
|
||||
li Custom modular synth
|
||||
ul
|
||||
li VCO:
|
||||
a(href="https://www.roland.com/global/products/system-500_512/") Roland System-500 512
|
||||
li VCF-VCA:
|
||||
a(href="https://www.dreadbox-fx.com/eudemonia/") Dreadbox Eudemonia
|
||||
li LFO-MOD:
|
||||
a(href="https://www.dreadbox-fx.com/ataxia/") Dreadbox Ataxia
|
||||
li CV PROC:
|
||||
a(href="https://www.dreadbox-fx.com/utopia/") Dreadbox Utopia
|
||||
li NOISE:
|
||||
a(href="https://www.dreadbox-fx.com/dystopia/") Dreadbox Dystopia
|
||||
li DELAY:
|
||||
a(href="https://www.dreadbox-fx.com/nostalgia/") Dreadbox Nostalgia
|
||||
li LFO:
|
||||
a(href="http://www.doepfer.de/a146.htm") Doepfer A146
|
||||
li CLK DIVIDER:
|
||||
a(href="http://www.doepfer.de/a160.htm") Doepfer A160-1
|
||||
li RM-S&H-SLEW:
|
||||
a(href="http://www.doepfer.de/a1841.htm") Doepfer A184-1
|
||||
|
||||
h2 Software
|
||||
|
||||
ul
|
||||
li General
|
||||
ul
|
||||
li
|
||||
a(href="https://pandoc.org/") Pandoc
|
||||
li Dev
|
||||
ul
|
||||
li
|
||||
a(href="https://atom.io/") Atom
|
||||
li nano
|
||||
li Homelab & selfhosting
|
||||
ul
|
||||
li
|
||||
a(href="https://www.docker.com/") Docker
|
||||
li
|
||||
a(href="https://containo.us/traefik/") Traefik v2
|
||||
li
|
||||
a(href="https://caddyserver.com/") Caddy v2
|
||||
li
|
||||
a(href="https://www.plex.tv/") Plex
|
||||
li
|
||||
a(href="https://mailcow.email/") Mailcow
|
||||
li Science
|
||||
ul
|
||||
li
|
||||
a(href="https://www.mathworks.com/products/matlab.html") Matlab R2010B
|
||||
li Music
|
||||
ul
|
||||
li
|
||||
a(href="https://new.steinberg.net/cubase/") Cubase
|
||||
|
||||
li VCO:
|
||||
a(href="https://www.roland.com/global/products/system-500_512/") Roland System-500 512
|
||||
li VCF-VCA:
|
||||
a(href="https://www.dreadbox-fx.com/eudemonia/") Dreadbox Eudemonia
|
||||
li LFO-MOD:
|
||||
a(href="https://www.dreadbox-fx.com/ataxia/") Dreadbox Ataxia
|
||||
li CV PROC:
|
||||
a(href="https://www.dreadbox-fx.com/utopia/") Dreadbox Utopia
|
||||
li NOISE:
|
||||
a(href="https://www.dreadbox-fx.com/dystopia/") Dreadbox Dystopia
|
||||
li DELAY:
|
||||
a(href="https://www.dreadbox-fx.com/nostalgia/") Dreadbox Nostalgia
|
||||
li LFO:
|
||||
a(href="http://www.doepfer.de/a146.htm") Doepfer A146
|
||||
li CLK DIVIDER:
|
||||
a(href="http://www.doepfer.de/a160.htm") Doepfer A160-1
|
||||
li RM-S&H-SLEW:
|
||||
a(href="http://www.doepfer.de/a1841.htm") Doepfer A184-1
|
||||
|
||||
h2 Software
|
||||
|
||||
ul
|
||||
li General
|
||||
ul
|
||||
li
|
||||
a(href="https://pandoc.org/") Pandoc
|
||||
li Dev
|
||||
ul
|
||||
li
|
||||
a(href="https://atom.io/") Atom
|
||||
li nano
|
||||
li Homelab & selfhosting
|
||||
ul
|
||||
li
|
||||
a(href="https://www.docker.com/") Docker
|
||||
li
|
||||
a(href="https://containo.us/traefik/") Traefik v2
|
||||
li
|
||||
a(href="https://caddyserver.com/") Caddy v2
|
||||
li
|
||||
a(href="https://www.plex.tv/") Plex
|
||||
li
|
||||
a(href="https://mailcow.email/") Mailcow
|
||||
li Science
|
||||
ul
|
||||
li
|
||||
a(href="https://www.mathworks.com/products/matlab.html") Matlab R2010B
|
||||
li Music
|
||||
ul
|
||||
li
|
||||
a(href="https://new.steinberg.net/cubase/") Cubase
|
||||
|
||||
|
@ -1,19 +1,21 @@
|
||||
extends layout
|
||||
extends templates/main
|
||||
|
||||
mixin entry($item)
|
||||
.list__item
|
||||
p !{$item['artist']} - !{$item['title']} (!{$item['year']})
|
||||
mixin entry(item)
|
||||
.list__item
|
||||
p !{item['artist']} - !{item['title']} (!{item['year']})
|
||||
|
||||
block content
|
||||
header
|
||||
nav
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/music") music
|
||||
| > vinyl
|
||||
h1 Yarmo's vinyl collection
|
||||
header
|
||||
h1 Yarmo's vinyl collection
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/music") music
|
||||
| >
|
||||
a(href="/vinyl") vinyl
|
||||
|
||||
main
|
||||
.list
|
||||
each $item in $albums
|
||||
+entry($item)
|
||||
main
|
||||
.list
|
||||
each item in vinyl.albums
|
||||
+entrysitem)
|
||||
|
37
views/work.pug
Normal file
@ -0,0 +1,37 @@
|
||||
extends templates/main
|
||||
|
||||
block content
|
||||
header
|
||||
h1
|
||||
| Work
|
||||
nav
|
||||
| >>
|
||||
a(href="/about") about me
|
||||
| >
|
||||
a(href="/work") work
|
||||
|
||||
main
|
||||
h2 >> Open Source developer
|
||||
|
||||
h3 Projects
|
||||
p
|
||||
| Currently working on
|
||||
a(href="https://keyoxide.org") Keyoxide
|
||||
| .
|
||||
|
||||
h3 VCS accounts
|
||||
.wrapper-table
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Codeberg
|
||||
td
|
||||
a(href="https://codeberg.org/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td GitLab
|
||||
td
|
||||
a(href="https://gitlab.com/yarmo" rel="me") @yarmo
|
||||
tr
|
||||
td Github
|
||||
td
|
||||
a(href="https://github.com/YarmoM" rel="me") @YarmoM
|