This commit is contained in:
Pim Kunis 2023-06-14 16:13:38 +02:00
parent 6a1b772abd
commit 1a5eb48adf
74 changed files with 2 additions and 3221 deletions

View file

@ -1,4 +0,0 @@
FROM nginx:1.24-bullseye
COPY ./site-html /var/www/blog
COPY ./site-repo/nginx.conf /etc/nginx/conf.d/default.conf

View file

@ -1,4 +1,3 @@
# static
# project version
My static website written in Jekyll.
Deployment through Concourse.
This branch tracks the version of the project's image for concourse.

View file

@ -1 +0,0 @@
fly -t home set-pipeline -l secrets.yml -l vars.yml -p static -c pipeline.yml

View file

@ -1,23 +0,0 @@
# vi: ft=yaml
version: '3'
networks:
traefik:
external: true
services:
static:
image: git.pim.kunis.nl/pim/static:TAG
networks:
- traefik
deploy:
placement:
constraints:
- "node.labels.public == true"
labels:
- traefik.enable=true
- traefik.http.routers.static.entrypoints=websecure
- traefik.http.services.static.loadbalancer.server.port=80
- traefik.http.routers.static.rule=Host(`pim.kunis.nl`)
- traefik.http.routers.static.tls=true
- traefik.http.routers.static.tls.certresolver=letsencrypt

6
jekyll/.gitignore vendored
View file

@ -1,6 +0,0 @@
_site/
.sass-cache/
.jekyll-cache/
.jekyll-metadata
_drafts
klise-*.gem

View file

@ -1 +0,0 @@
ruby 3.2.2

View file

@ -1,5 +0,0 @@
---
title: "404"
layout: 404
permalink: "/404.html"
---

View file

@ -1,33 +0,0 @@
source "https://rubygems.org"
# Hello! This is where you manage which Jekyll version is used to run.
# When you want to use a different version, change it below, save the
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
#
# bundle exec jekyll serve
#
# This will help ensure the proper Jekyll version is running.
# Happy Jekylling!
gem "jekyll", "~> 4.1.0"
# This is the default theme for new Jekyll sites. You may change this to anything you like.
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
# uncomment the line below. To upgrade, run `bundle update github-pages`.
# gem "github-pages", group: :jekyll_plugins
# If you have any plugins, put them here!
group :jekyll_plugins do
gem 'jekyll-feed', '~> 0.13'
gem 'jekyll-sitemap', '~> 1.4'
gem 'jekyll-compose', '~> 0.12.0'
gem 'jekyll-postfiles', '~> 3.1'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
# gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
# Performance-booster for watching directories on Windows
# gem "wdm", "~> 0.1.0" if Gem.win_platform?
gem "webrick", "~> 1.7"

View file

@ -1,81 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
colorator (1.1.0)
concurrent-ruby (1.2.0)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.15.5)
forwardable-extended (2.6.0)
http_parser.rb (0.8.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jekyll (4.1.1)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (~> 2.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.1)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (~> 0.4.0)
pathutil (~> 0.9)
rouge (~> 3.0)
safe_yaml (~> 1.0)
terminal-table (~> 1.8)
jekyll-compose (0.12.0)
jekyll (>= 3.7, < 5.0)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-postfiles (3.1.0)
jekyll (>= 3.8.6, < 5)
jekyll-sass-converter (2.2.0)
sassc (> 2.0.1, < 3.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.0.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (3.30.0)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
unicode-display_width (1.8.0)
webrick (1.7.0)
PLATFORMS
x86_64-linux
DEPENDENCIES
jekyll (~> 4.1.0)
jekyll-compose (~> 0.12.0)
jekyll-feed (~> 0.13)
jekyll-postfiles (~> 3.1)
jekyll-sitemap (~> 1.4)
webrick (~> 1.7)
BUNDLED WITH
2.4.5

View file

@ -1 +0,0 @@
`jekyll serve`

View file

@ -1,71 +0,0 @@
title: Pim Kunis
description: There can be only one
lang: en-US
timezone: Europe/Amsterdam
image: assets/img/avatar.jpg
repo: https://git.pim.kunis.nl/pim/pizzeria
mode: light
author:
name: Pim Kunis
bio: There can be only one
username: pim
avatar: /assets/img/avatar.jpg
url: "https://pim.kunis.nl"
baseurl: "/"
permalink: /:title/
collections:
posts:
output: true
markdown: kramdown
highlighter: rouge
kramdown:
syntax_highlighter: rouge
defaults:
- scope:
path: ""
values:
layout: post
comments: false
jekyll_compose:
post_default_front_matter:
modified:
tags: []
description:
draft_default_front_matter:
modified:
tags: []
description:
number_of_posts: 5
sass:
style: compressed
include:
- _redirects
- .htaccess
exclude:
- CNAME
- Gemfile
- Gemfile.lock
- LICENSE
- CHANGELOG.md
- README.md
- node_modules
- CODE_OF_CONDUCT.md
- CONTRIBUTING.md
- lighthouse.png
- klise-*.gem
- klise.gemspec
plugins:
- jekyll-feed
- jekyll-sitemap
- jekyll-postfiles

View file

@ -1,16 +0,0 @@
- title: home
url: /
external: false
- title: archive
url: /archive/
external: false
- title: about
url: /about/
external: false # set true if you using external link, see below
# Example:
# - title: github
# url: https://github.com/piharpi/jekyll-klise
# external: true

View file

@ -1,105 +0,0 @@
{% capture headingsWorkspace %}
{% comment %}
Version 1.0.4
https://github.com/allejo/jekyll-anchor-headings
"Be the pull request you wish to see in the world." ~Ben Balter
Usage:
{% include anchor_headings.html html=content %}
Parameters:
* html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
Optional Parameters:
* beforeHeading (bool) : false - Set to true if the anchor should be placed _before_ the heading's content
* anchorAttrs (string) : '' - Any custom HTML attributes that will be added to the `<a>` tag; you may NOT use `href`, `class` or `title`
* anchorBody (string) : '' - The content that will be placed inside the anchor; the `%heading%` placeholder is available
* anchorClass (string) : '' - The class(es) that will be used for each anchor. Separate multiple classes with a space
* anchorTitle (string) : '' - The `title` attribute that will be used for anchors
* h_min (int) : 1 - The minimum header level to build an anchor for; any header lower than this value will be ignored
* h_max (int) : 6 - The maximum header level to build an anchor for; any header greater than this value will be ignored
* bodyPrefix (string) : '' - Anything that should be inserted inside of the heading tag _before_ its anchor and content
* bodySuffix (string) : '' - Anything that should be inserted inside of the heading tag _after_ its anchor and content
Output:
The original HTML with the addition of anchors inside of all of the h1-h6 headings.
{% endcomment %}
{% assign minHeader = include.h_min | default: 1 %}
{% assign maxHeader = include.h_max | default: 6 %}
{% assign beforeHeading = include.beforeHeading %}
{% assign nodes = include.html | split: '<h' %}
{% capture edited_headings %}{% endcapture %}
{% for _node in nodes %}
{% capture node %}{{ _node | strip }}{% endcapture %}
{% if node == "" %}
{% continue %}
{% endif %}
{% assign nextChar = node | replace: '"', '' | strip | slice: 0, 1 %}
{% assign headerLevel = nextChar | times: 1 %}
<!-- If the level is cast to 0, it means it's not a h1-h6 tag, so let's try to fix it -->
{% if headerLevel == 0 %}
{% if nextChar != '<' and nextChar != '' %}
{% capture node %}<h{{ node }}{% endcapture %}
{% endif %}
{% capture edited_headings %}{{ edited_headings }}{{ node }}{% endcapture %}
{% continue %}
{% endif %}
{% assign _workspace = node | split: '</h' %}
{% assign _idWorkspace = _workspace[0] | split: 'id="' %}
{% assign _idWorkspace = _idWorkspace[1] | split: '"' %}
{% assign html_id = _idWorkspace[0] %}
{% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %}
{% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
<!-- Build the anchor to inject for our heading -->
{% capture anchor %}{% endcapture %}
{% if html_id and headerLevel >= minHeader and headerLevel <= maxHeader %}
{% capture anchor %}href="#{{ html_id }}"{% endcapture %}
{% if include.anchorClass %}
{% capture anchor %}{{ anchor }} class="{{ include.anchorClass }}"{% endcapture %}
{% endif %}
{% if include.anchorTitle %}
{% capture anchor %}{{ anchor }} title="{{ include.anchorTitle | replace: '%heading%', header }}"{% endcapture %}
{% endif %}
{% if include.anchorAttrs %}
{% capture anchor %}{{ anchor }} {{ include.anchorAttrs }}{% endcapture %}
{% endif %}
{% capture anchor %}<a {{ anchor }}>{{ include.anchorBody | replace: '%heading%', header | default: '' }}</a>{% endcapture %}
<!-- In order to prevent adding extra space after a heading, we'll let the 'anchor' value contain it -->
{% if beforeHeading %}
{% capture anchor %}{{ anchor }} {% endcapture %}
{% else %}
{% capture anchor %} {{ anchor }}{% endcapture %}
{% endif %}
{% endif %}
{% capture new_heading %}
<h{{ _hAttrToStrip }}
{{ include.bodyPrefix }}
{% if beforeHeading %}
{{ anchor }}{{ header }}
{% else %}
{{ header }}{{ anchor }}
{% endif %}
{{ include.bodySuffix }}
</h{{ _workspace | last }}
{% endcapture %}
{% capture edited_headings %}{{ edited_headings }}{{ new_heading }}{% endcapture %}
{% endfor %}
{% endcapture %}{% assign headingsWorkspace = '' %}{{ edited_headings | strip }}

View file

@ -1,9 +0,0 @@
<div class="author">
<img
class="author-avatar"
src="{{ site.author.avatar }}"
alt="{{ site.author.username }}"
/>
<h2 class="author-name">{{ site.author.name }}</h2>
<p class="author-bio">{{ site.author.bio }}</p>
</div>

View file

@ -1,10 +0,0 @@
<!-- unnecessary file, however you can still use for comment section, e.g disqus -->
<script
src="https://utteranc.es/client.js"
repo="username/reponame"
issue-term="pathname"
label="✨ comment ✨"
theme="github-light"
crossorigin="anonymous"
async
></script>

View file

@ -1,29 +0,0 @@
<footer class="footer">
<a class="footer_item" href="https://git.pim.kunis.nl/pim"><i class="fa-solid fa-code-branch"></i> Git</a>
<a class="footer_item" rel="me" href="https://social.pizzapim.nl/@pim"><i class="fab fa-mastodon"></i> Mastodon</a>
<span class="footer_item">&copy; {{ site.time | date: "%Y" }}</span>
<small class="footer_copyright">
<!-- Klisé Theme: https://github.com/piharpi/jekyll-klise -->
<a
href="https://github.com/piharpi/jekyll-klise"
target="_blank"
rel="noreferrer noopener"
>klisé</a
>
theme on
<a href="https://jekyllrb.com" target="_blank" rel="noreferrer noopener"
>jekyll</a
>
</small>
</footer>
<script src="/assets/js/main.js" defer="defer"></script>
{%- if page.url == '/archive/' -%}
<script src="/assets/js/search.min.js"></script>
<script>
var sjs = SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('search-results'),
json: '/assets/search.json',
});
</script>
{%- endif -%}

View file

@ -1,165 +0,0 @@
<head prefix="og: http://ogp.me/ns#">
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="application-name" content="{{ site.title }}" />
<meta name="apple-mobile-web-app-status-bar-style" content="#fff" />
<meta name="apple-mobile-web-app-title" content="{{ site.title }}" />
<title>
{% if page.title %}{{ page.title | escape }} - {{ site.title }}{% else %}{{
site.title | escape }}{% endif %}
</title>
<link
rel="alternate"
href="{{
page.url | remove: 'index.html' | remove: '.html' | absolute_url
}}"
hreflang="{{ site.lang }}"
/>
<link
rel="canonical"
href="{{
page.url | remove: 'index.html' | remove: '.html' | absolute_url
}}"
/>
{% if paginator.previous_page %}
<link
rel="prev"
href="{{
paginator.previous_page_path
| remove: 'index.html'
| remove: '.html'
}}"
/>
{% endif %} {% if paginator.next_page %}
<link
rel="next"
href="{{
paginator.next_page_path
| remove: 'index.html'
| remove: '.html'
}}"
/>
{% endif %}
<meta
name="description"
content="{{
page.description
| default: site.description
| strip_html
| normalize_whitespace
| truncate: 200
| escape
}}"
/>
<meta name="referrer" content="no-referrer-when-downgrade" />
<meta property="fb:app_id" content="{{ site.fb_appid }}" />
<meta
property="og:site_name"
content="{% if page.title %}{{ page.title | escape }} | {{
site.author.name
}}{% else %}{{ site.title | escape }}{% endif %}"
/>
<meta
property="og:title"
content="{% if page.title %}{{ page.title | escape }} | {{
site.author.name
}}{% else %}{{ site.title | escape }}{% endif %}"
/>
{% if page.location %}
<meta property="og:type" content="article" />
<meta
property="article:publisher"
content="https://web.facebook.com/{{ site.author.facebook }}"
/>
{% else %}
<meta property="og:type" content="website" />
{% endif %}
<meta
property="og:url"
content="{{
page.url | remove: 'index.html' | remove: '.html' | absolute_url
}}"
/>
<meta
property="og:description"
content="{{
page.description
| default: site.description
| strip_html
| normalize_whitespace
| truncate: 200
| escape
}}"
/>
{% if page.image %}
<meta property="og:image" content="{{ page.image | absolute_url }}" />
{% else %}
<meta property="og:image" content="{{ site.image | absolute_url }}" />
{% endif %}
<meta property="og:image:width" content="640" />
<meta property="og:image:height" content="640" />
<meta name="twitter:card" content="summary" />
<meta
name="twitter:title"
content="{% if page.title %}{{ page.title | escape }} | {{
site.author.twitter
}}{% else %}{{ site.title | escape }}{% endif %}"
/>
<meta
name="twitter:url"
content="{{
page.url | remove: 'index.html' | remove: '.html' | absolute_url
}}"
/>
<meta name="twitter:site" content="@{{ site.author.twitter }}" />
<meta name="twitter:creator" content="@{{ site.author.twitter }}" />
<meta
name="twitter:description"
content="{{
page.description
| default: site.description
| strip_html
| normalize_whitespace
| truncate: 200
| escape
}}"
/>
{% if page.image %}
<meta name="twitter:image" content="{{ page.image | absolute_url }}" />
{% else %}
<meta name="twitter:image" content="{{ site.image | absolute_url }}" />
{% endif %} {% feed_meta %}
<link
rel="apple-touch-icon"
sizes="180x180"
href="/assets/favicons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/assets/favicons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/assets/favicons/favicon-16x16.png"
/>
<link rel="manifest" href="/assets/favicons/site.webmanifest" />
<link
rel="mask-icon"
href="/assets/favicons/safari-pinned-tab.svg"
color="#5bbad5"
/>
<meta name="apple-mobile-web-app-title" content="Jekyll Klise" />
<meta name="application-name" content="Jekyll Klise" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#2c2c2c" />
<link rel="stylesheet" href="/assets/css/style.css" />
<link href="/assets/css/fontawesome.all.min.css" rel="stylesheet" />
</head>

View file

@ -1,211 +0,0 @@
<div class="navbar" role="navigation">
<nav class="menu">
<input type="checkbox" id="menu-trigger" class="menu-trigger" />
<label for="menu-trigger">
<span class="menu-icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 512 512"
>
<path
d="M64,384H448V341.33H64Zm0-106.67H448V234.67H64ZM64,128v42.67H448V128Z"
/>
</svg>
</span>
</label>
<a id="mode">
<svg
class="mode-sunny"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 512 512"
>
<title>LIGHT</title>
<line
x1="256"
y1="48"
x2="256"
y2="96"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="256"
y1="416"
x2="256"
y2="464"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="403.08"
y1="108.92"
x2="369.14"
y2="142.86"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="142.86"
y1="369.14"
x2="108.92"
y2="403.08"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="464"
y1="256"
x2="416"
y2="256"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="96"
y1="256"
x2="48"
y2="256"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="403.08"
y1="403.08"
x2="369.14"
y2="369.14"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="142.86"
y1="142.86"
x2="108.92"
y2="108.92"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<circle
cx="256"
cy="256"
r="80"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
</svg>
<svg
class="mode-moon"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 512 512"
>
<title>DARK</title>
<line
x1="256"
y1="48"
x2="256"
y2="96"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="256"
y1="416"
x2="256"
y2="464"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="403.08"
y1="108.92"
x2="369.14"
y2="142.86"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="142.86"
y1="369.14"
x2="108.92"
y2="403.08"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="464"
y1="256"
x2="416"
y2="256"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="96"
y1="256"
x2="48"
y2="256"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="403.08"
y1="403.08"
x2="369.14"
y2="369.14"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<line
x1="142.86"
y1="142.86"
x2="108.92"
y2="108.92"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
<circle
cx="256"
cy="256"
r="80"
style="stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px"
/>
</svg>
</a>
<div class="trigger">
<div class="trigger-container">
{%- assign url = page.url -%}
{%- assign menus = site.data.menus -%}
{%- if menus %}
{%- for menu in menus -%}
{%- if url == menu.url -%}
<a class="menu-link active" href="{{ menu.url }}">{{ menu.title }}</a>
{%- else -%}
{%- if menu.external -%}
<a
class="menu-link"
href="{{ menu.url }}"
target="_blank"
rel="noopener"
>{{ menu.title }}</a
>
{%- else -%}
<a class="menu-link" href="{{ menu.url }}">{{ menu.title }}</a>
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- else -%}
<a class="menu-link {% if url == '/' %}active{% endif %}" href="/">home</a>
<a class="menu-link {% if url == '/about/' %}active{% endif %}" href="/about">about</a>
{%- endif -%}
<a class="menu-link rss" href="/feed.xml">
<svg
xmlns="http://www.w3.org/2000/svg"
width="17"
height="17"
viewBox="0 0 512 512"
fill="#ED812E"
>
<title>RSS</title>
<path
d="M108.56,342.78a60.34,60.34,0,1,0,60.56,60.44A60.63,60.63,0,0,0,108.56,342.78Z"
/>
<path
d="M48,186.67v86.55c52,0,101.94,15.39,138.67,52.11s52,86.56,52,138.67h86.66C325.33,312.44,199.67,186.67,48,186.67Z"
/>
<path
d="M48,48v86.56c185.25,0,329.22,144.08,329.22,329.44H464C464,234.66,277.67,48,48,48Z"
/>
</svg>
</a>
</div>
</div>
</nav>
</div>

View file

@ -1,16 +0,0 @@
<nav class="post-nav">
{% if page.previous %}
<a
class="post-nav-item post-nav-prev"
href="{{ page.previous | relative_url }}"
>
<div class="nav-arrow">Previous</div>
<span class="post-title">{{ page.previous.title }}</span>
</a>
{% endif %} {% if page.next %}
<a class="post-nav-item post-nav-next" href="{{ page.next | relative_url }}">
<div class="nav-arrow">Next</div>
<span class="post-title">{{ page.next.title }}</span>
</a>
{% endif %}
</nav>

View file

@ -1,21 +0,0 @@
<!-- NOTE: unused file, but u can use if necessary -->
<!-- <div class="pagination">
{% if paginator.previous_page %}
<a
class="page-previous"
href="{{ paginator.previous_page_path }}"
class="previous"
>
<span aria-hidden="true"></span> NEWER POSTS
</a>
{% endif %}
<span class="page_number"
>PAGE {{ paginator.page }} OF {{ paginator.total_pages }}</span
>
{% if paginator.next_page %}
<a class="page-next" href="{{ paginator.next_page_path }}" class="next"
>OLDER POSTS
<span aria-hidden="true"></span>
</a>
{% endif %}
</div> -->

View file

@ -1,45 +0,0 @@
---
layout: compress
---
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: " en " }}">
{% include header.html %}
<body data-theme="{{ site.mode }}" class="notransition">
<script>
const body = document.body;
const data = body.getAttribute("data-theme");
const initTheme = (state) => {
if (state === "dark") {
body.setAttribute("data-theme", "dark");
} else if (state === "light") {
body.removeAttribute("data-theme");
} else {
localStorage.setItem("theme", data);
}
};
initTheme(localStorage.getItem("theme"));
setTimeout(() => body.classList.remove("notransition"), 75);
</script>
{% include navbar.html %}
<div class="wrapper">
<main aria-label="Content">
<div class="not-found">
<div class="container">
<div class="title">404</div>
<a class="solution" href="{{ site.url }}">get me the fuck out of here</a>
</div>
</div>
</main>
{% include footer.html %}
</div>
</body>
</html>

View file

@ -1,4 +0,0 @@
---
---
{% if site.compress_html.ignore.envs contains jekyll.environment %}{{ content }}{% else %}{% capture _content %}{{ content }}{% endcapture %}{% if site.compress_html.endings == "all" %}{% assign _endings = "html head body li dt dd p rt rp optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% else %}{% assign _endings = site.compress_html.endings %}{% endif %}{% for _element in _endings %}{% capture _end %}</{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _end %}{% endfor %}{% if site.compress_html.comments.size == 2 %}{% assign _comment_befores = _content | split: site.compress_html.comments.first %}{% for _comment_before in _comment_befores %}{% assign _comment_content = _comment_before | split: site.compress_html.comments.last | first %}{% if _comment_content %}{% capture _comment %}{{ site.compress_html.comments.first }}{{ _comment_content }}{{ site.compress_html.comments.last }}{% endcapture %}{% assign _content = _content | remove: _comment %}{% endif %}{% endfor %}{% endif %}{% assign _pre_befores = _content | split: "<pre" %}{% assign _content = "" %}{% for _pre_before in _pre_befores %}{% assign _pres = _pre_before | split: "</pre>" %}{% case _pres.size %}{% when 2 %}{% capture _content %}{{ _content }}<pre{{ _pres.first }}</pre>{{ _pres.last | split: " " | join: " " }}{% endcapture %}{% when 1 %}{% capture _content %}{{ _content }}{{ _pres.last | split: " " | join: " " }}{% endcapture %}{% endcase %}{% endfor %}{% if site.compress_html.clippings == "all" %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th" | split: " " %}{% else %}{% assign _clippings = site.compress_html.clippings %}{% endif %}{% for _element in _clippings %}{% assign _edges = " <e;<e; </e>;</e>;</e> ;</e>" | replace: "e", _element | split: ";" %}{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}{% endfor %}{{ _content }}{% endif %}

View file

@ -1,38 +0,0 @@
---
layout: compress
---
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: " en " }}">
{% include header.html %}
<body data-theme="{{ site.mode }}" class="notransition">
<script>
const body = document.body;
const data = body.getAttribute("data-theme");
const initTheme = (state) => {
if (state === "dark") {
body.setAttribute("data-theme", "dark");
} else if (state === "light") {
body.removeAttribute("data-theme");
} else {
localStorage.setItem("theme", data);
}
};
initTheme(localStorage.getItem("theme"));
setTimeout(() => body.classList.remove("notransition"), 75);
</script>
{% include navbar.html %}
<div class="wrapper">
{% include author.html %}
<main aria-label="Content">
{{ content }}
</main>
{% include footer.html %}
</div>
</body>
</html>

View file

@ -1,14 +0,0 @@
---
layout: default
home: true
---
<h3 class="posts-item-note" aria-label="Recent Posts">Recent Posts</h3>
{%- for post in site.posts limit: site.number_of_posts -%}
<article class="post-item">
<span class="post-item-date">{{ post.date | date: "%b %d, %Y" }}</span>
<h4 class="post-item-title">
<a href="{{ post.url }}">{{ post.title | escape }}</a>
</h4>
</article>
{%- endfor -%}

View file

@ -1,43 +0,0 @@
---
layout: compress
---
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: " en " }}">
{% include header.html %}
<body data-theme="{{ site.mode }}" class="notransition">
<script>
const body = document.body;
const data = body.getAttribute("data-theme");
const initTheme = (state) => {
if (state === "dark") {
body.setAttribute("data-theme", "dark");
} else if (state === "light") {
body.removeAttribute("data-theme");
} else {
localStorage.setItem("theme", data);
}
};
initTheme(localStorage.getItem("theme"));
setTimeout(() => body.classList.remove("notransition"), 75);
</script>
{% include navbar.html %}
<div class="wrapper">
<header class="header">
<h1 class="header-title center" itemprop="headline">{{ page.title | escape }}.</h1>
</header>
<main class="page-content" aria-label="Content">
{% include anchor_headings.html html=content anchorClass="anchor-head" beforeHeading=true h_min=4 h_max=4 %}
</main>
{% include footer.html %}
</div>
</body>
</html>

View file

@ -1,94 +0,0 @@
---
layout: compress
---
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: "en" }}">
{% include header.html %}
<body data-theme="{{ site.mode }}" class="notransition">
<script>
const body = document.body;
const data = body.getAttribute("data-theme");
const initTheme = (state) => {
if (state === "dark") {
body.setAttribute("data-theme", "dark");
} else if (state === "light") {
body.removeAttribute("data-theme");
} else {
localStorage.setItem("theme", data);
}
};
initTheme(localStorage.getItem("theme"));
setTimeout(() => body.classList.remove("notransition"), 75);
</script>
{% include navbar.html %}
<div class="wrapper post">
<main class="page-content" aria-label="Content">
<article itemscope itemtype="https://schema.org/BlogPosting">
<header class="header">
{% if page.tags and page.tags != empty %}
<div class="tags">
{% assign tags = page.tags %}
<span itemprop="keywords">
{% for tag in tags %}
<a class="tag"
href="/tags/#{{tag | downcase | slugify}}">{{tag | upcase }}</a>{% unless forloop.last %},{% endunless %}
{% endfor %}
</span>
</div>
{% endif %}
<h1 class="header-title" itemprop="headline">{{ page.title | escape }}</h1>
{% if page.date %}
<div class="post-meta">
<time datetime="{{ page.date | date_to_xmlschema }}" itemprop="datePublished">
{{ page.date | date: "%b %d, %Y" }}
</time>
<span itemprop="author" itemscope itemtype="https://schema.org/Person">
<span itemprop="name">{{ site.author.name }}</span>
</span>
<time hidden datetime="{{ page.modified | date_to_xmlschema }}" itemprop="dateModified">
{{ page.date | date: "%b %d, %Y" }}
</time>
<span hidden itemprop="publisher" itemtype="Person">{{ site.author.name }}</span>
<span hidden itemprop="image">{{ page.image }}</span>
<span hidden itemprop="mainEntityOfPage">{{ page.excerpt }}</span>
</div>
{% endif %}
</header>
<div class="page-content" itemprop="articleBody">
{% include anchor_headings.html html=content anchorClass="anchor-head" beforeHeading=true h_min=1 h_max=4 %}
{% if page.tweet %}
<p>Comments this article on
<a href="https://twitter.com/{{site.twitter}}/status/{{page.tweet}}">Twitter</a>.
</p>
{% endif %}
</div>
</article>
{% if page.comments %}
{% include comments.html %}
{% endif %}
</main>
{% if page.modified %}
<small class="post-updated-at">updated_at {{page.modified | date: "%d-%m-%Y"}}</small>
{% endif %}
{% if page.next or page.previous %}
{% include navigation.html %}
{% endif %}
{% include footer.html %}
</div>
</body>
</html>

View file

@ -1,184 +0,0 @@
---
layout: post
title: Homebrew SSH Certificate Authority for the Terraform Libvirt Provider
date: 2023-05-23 11:14:00 Europe/Amsterdam
categories: ssh terraform ansible
---
Ever SSH'ed into a freshly installed server and gotten the following annoying message?
```
The authenticity of host 'host.tld (1.2.3.4)' can't be established.
ED25519 key fingerprint is SHA256:eUXGdm1YdsMAS7vkdx6dOJdOGHdem5gQp4tadCfdLB8.
Are you sure you want to continue connecting (yes/no)?
```
Or even more annoying:
```
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:eUXGdm1YdsMAS7vkdx6dOJdOGHdem5gQp4tadCfdLB8.
Please contact your system administrator.
Add correct host key in /home/user/.ssh/known_hosts to get rid of this message.
Offending ED25519 key in /home/user/.ssh/known_hosts:3
remove with:
ssh-keygen -f "/etc/ssh/ssh_known_hosts" -R "1.2.3.4"
ED25519 host key for 1.2.3.4 has changed and you have requested strict checking.
Host key verification failed.
```
Could it be that the programmers at OpenSSH simply like to annoy us with these confusing messages?
Maybe, but these warnings also serve as a way to notify users of a potential Man-in-the-Middle (MITM) attack.
I won't go into the details of this problem, but I refer you to [this excellent blog post](https://blog.g3rt.nl/ssh-host-key-validation-strict-yet-user-friendly.html).
Instead, I would like to talk about ways to solve these annoying warnings.
One obvious solution is simply to add each host to your `known_hosts` file.
This works okay when managing a handful of servers, but becomes unbearable when managing many servers.
In my case, I wanted to quickly spin up virtual machines using Duncan Mac-Vicar's [Terraform Libvirt provider](https://registry.terraform.io/providers/dmacvicar/libvirt/latest/docs), without having to accept their host key before connecting.
The solution? Issuing SSH host certificates using an SSH certificate authority.
## SSH Certificate Authorities vs. the Web
The idea of an SSH certificate authority (CA) is quite easy to grasp, if you understand the web's Public Key Infrastructure (PKI).
Just like with the web, a trusted party can issue certificates that are offered when establishing a connection.
The idea is, just by trusting the trusted party, you trust every certificate they issue.
In the case of the web's PKI, this trusted party is bundled and trusted by [your browser](https://wiki.mozilla.org/CA) or operating system.
However, in the case of SSH, the trusted party is you! (Okay you can also trust your own web certificate authority)
With this great power, comes great responsibility which we will abuse heavily in this article.
## SSH Certificate Authority for Terraform
So, let's start with a plan.
I want to spawn virtual machines with Terraform which which are automatically provisioned with a SSH host certificate issued by my CA.
This CA will be another host on my private network, issuing certificates over SSH.
### Fetching the SSH Host Certificate
First we generate an SSH key pair in Terraform.
Below is the code for that:
```terraform
resource "tls_private_key" "debian" {
algorithm = "ED25519"
}
data "tls_public_key" "debian" {
private_key_pem = tls_private_key.debian.private_key_pem
}
```
Now that we have an SSH key pair, we need to somehow make Terraform communicate this with the CA.
Lucky for us, there is a way for Terraform to execute an arbitrary command with the `external` data feature.
We call this script below:
```terraform
data "external" "cert" {
program = ["bash", "${path.module}/get_cert.sh"]
query = {
pubkey = trimspace(data.tls_public_key.debian.public_key_openssh)
host = var.name
cahost = var.ca_host
cascript = var.ca_script
cakey = var.ca_key
}
}
```
These query parameters will end up in the script's stdin in JSON format.
We can then read these parameters, and send them to the CA over SSH.
The result must as well be in JSON format.
```bash
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
# Read the query parameters
eval "$(jq -r '@sh "PUBKEY=\(.pubkey) HOST=\(.host) CAHOST=\(.cahost) CASCRIPT=\(.cascript) CAKEY=\(.cakey)"')"
# Fetch certificate from the CA
# Warning: extremely ugly code that I am to lazy to fix
CERT=$(ssh -o ConnectTimeout=3 -o ConnectionAttempts=1 root@$CAHOST '"'"$CASCRIPT"'" host "'"$CAKEY"'" "'"$PUBKEY"'" "'"$HOST"'".dmz')
jq -n --arg cert "$CERT" '{"cert":$cert}'
```
We see that a script is called on the remote host that issues the certificate.
This is just a simple wrapper around `ssh-keygen`, which you can see below.
```bash
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
host() {
CAKEY="$2"
PUBKEY="$3"
HOST="$4"
echo "$PUBKEY" > /root/ca/"$HOST".pub
ssh-keygen -h -s /root/ca/keys/"$CAKEY" -I "$HOST" -n "$HOST" /root/ca/"$HOST".pub
cat /root/ca/"$HOST"-cert.pub
rm /root/ca/"$HOST"*.pub
}
"$1" "$@"
```
### Appeasing the Terraform Gods
So nice, we can fetch the SSH host certificate from the CA.
We should just be able to use it right?
We can, but it brings a big annoyance with it: Terraform will fetch a new certificate every time it is run.
This is because the `external` feature of Terraform is a data source.
If we were to use this data source for a Terraform resource, it would need to be updated every time we run Terraform.
I have not been able to find a way to avoid fetching the certificate every time, except for writing my own resource provider which I'd rather not.
I have, however, found a way to hack around the issue.
The idea is as follows: we can use Terraform's `ignore_changes` to, well, ignore any changes of a resource.
Unfortunately, we cannot use this for a `data` source, so we must create a glue `null_resource` that supports `ignore_changes`.
This is shown in the code snipppet below.
We use the `triggers` property simply to copy the certificate in; we don't use it for it's original purpose.
```terraform
resource "null_resource" "cert" {
triggers = {
cert = data.external.cert.result["cert"]
}
lifecycle {
ignore_changes = [
triggers
]
}
}
```
And voilà, we can now use `null_resource.cert.triggers["cert"]` as our certificate, that won't trigger replacements in Terraform.
### Setting the Host Certificate with Cloud-Init
Terraform's Libvirt provider has native support for Cloud-Init, which is very handy.
We can give the host certificate directly to Cloud-Init and place it on the virtual machine.
Inside the Cloud-Init configuration, we can set the `ssh_keys` property to do this:
```yml
ssh_keys:
ed25519_private: |
${indent(4, private_key)}
ed25519_certificate: "${host_cert}"
```
I hardcoded this to ED25519 keys, because this is all I use.
This works perfectly, and I never have to accept host certificates from virtual machines again.
### Caveats
A sharp eye might have noticed the lifecycle of these host certificates is severely lacking.
Namely, the deployed host certificates have no expiration date nore is there revocation function.
There are ways to implement these, but for my home lab I did not deem this necessary at this point.
In a more professional environment, I would suggest using [Hashicorp's Vault](https://www.vaultproject.io/).
This project did teach me about the limits and flexibility of Terraform, so all in all a success!
All code can be found on the git repository [here](https://git.pim.kunis.nl/home/tf-modules/src/branch/master/debian).

View file

@ -1,61 +0,0 @@
---
layout: post
title: My Experiences with virtio-9p
date: 2023-05-31 14:18:00 Europe/Amsterdam
categories: libvirt virtio 9p
---
When I was scaling up my home lab, I started thinking more about data management.
I hadn't (and still haven't) set up any form of network storage.
I have, however, set up a backup mechanism using [Borg](https://borgbackup.readthedocs.io/en/stable/).
Still, I want to operate lots of virtual machines, and backing up each one of them separately seemed excessive.
So I started thinking, what if I just let the host machines back up the data?
After all, the amount of physical hosts I have in my home lab is unlikely to increase drastically.
# The Use Case for Sharing Directories
I started working out this idea further.
Without network storage, I needed a way for guest VMs to access the host's disks.
Here there are two possibilities, either expose some block device or a file system.
Creating a whole virtual disk for just the data of some VMs seemed wasteful, and from my experiences also increases backup times dramatically.
I therefore searched for a way to mount a directory from the host OS on the guest VM.
This is when I stumbled upon [this blog](https://rabexc.org/posts/p9-setup-in-libvirt) post talking about sharing directories with virtual machines.
# Sharing Directories with virtio-9p
virtio-9p is a way to map a directory on the host OS to a special device on the virtual machine.
In `virt-manager`, it looks like the following:
![picture showing virt-manager configuration to map a directory to a VM](virt-manager.png)
Under the hood, virtio-9p uses the 9pnet protocol.
Originally developed at Bell Labs, support for this is available in all modern Linux kernels.
If you share a directory with a VM, you can then mount it.
Below is an extract of my `/etc/fstab` to automatically mount the directory:
```
data /mnt/data 9p trans=virtio,rw 0 0
```
The first argument (`data`) refers to the name you gave this share from the host
With the `trans` option we specify that this is a virtio share.
# Problems with virtio-9p
At first I had no problems with my setup, but I am now contemplating just moving to a network storage based setup because of two problems.
The first problem is that some files have suddenly changed ownership from `libvirt-qemu` to `root`.
If the file is owned by `root`, the guest OS can still see it, but cannot access it.
I am not entirely sure the problem lies with virtio, but I suspect it is.
For anyone experiencing this problem, I wrote a small shell script to revert ownership to the `libvirt-qemu` user:
```shell
find -printf "%h/%f %u\n" | grep root | cut -d ' ' -f1 | xargs chown libvirt-qemu:libvirt-qemu
```
Another problem that I have experienced, is guests being unable to mount the directory at all.
I have only experienced this problem once, but it was highly annoying.
To fix it, I had to reboot the whole physical machine.
# Alternatives
virtio-9p seemed like a good idea, but as discussed, I had some problems with it.
It seems [virtioFS](https://virtio-fs.gitlab.io/) might be a an interesting alternative as it is designed specifically for sharing directories with VMs.
As for me, I will probably finally look into deploying network storage either with NFS or SSHFS.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,368 +0,0 @@
// Reset some basic elements
* {
-webkit-transition: background-color 75ms ease-in, border-color 75ms ease-in;
-moz-transition: background-color 75ms ease-in, border-color 75ms ease-in;
-ms-transition: background-color 75ms ease-in, border-color 75ms ease-in;
-o-transition: background-color 75ms ease-in, border-color 75ms ease-in;
transition: background-color 75ms ease-in, border-color 75ms ease-in;
}
.notransition {
-webkit-transition: none;
-moz-transition: none;
-ms-transition: none;
-o-transition: none;
transition: none;
}
html {
overflow-x: hidden;
width: 100%;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
hr,
dl,
dd,
ol,
ul,
figure {
margin: 0;
padding: 0;
}
// Basic styling
body {
min-height: 100vh;
overflow-x: hidden;
position: relative;
color: $text-base-color;
background-color: $white;
font: $normal-weight #{$base-font-size}/#{$base-line-height} $sans-family;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-webkit-font-feature-settings: "kern" 1;
-moz-font-feature-settings: "kern" 1;
-o-font-feature-settings: "kern" 1;
font-feature-settings: "kern" 1;
font-kerning: normal;
box-sizing: border-box;
}
// Set `margin-bottom` to maintain vertical rhythm
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
ul,
ol,
dl,
figure,
%vertical-rhythm {
margin-top: $spacing-full - 20;
margin-bottom: $spacing-full - 20;
}
// strong | bold
strong,
b {
font-weight: $bold-weight;
color: $black;
}
// horizontal rule
hr {
border-bottom: 0;
border-style: solid;
border-color: $light;
}
// kbd tag
kbd {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border: 1px solid $light;
border-radius: 2px;
color: $black;
display: inline-block;
font-size: $small-font-size;
line-height: 1.4;
font-family: $mono-family;
margin: 0 0.1em;
font-weight: $bold-weight;
padding: 0.01em 0.4em;
text-shadow: 0 1px 0 $white;
}
// Image
img {
max-width: 100%;
vertical-align: middle;
-webkit-user-drag: none;
margin: 0 auto;
text-align: center;
}
// Figure
figure {
position: relative;
}
// Image inside Figure tag
figure > img {
display: block;
position: relative;
}
// Image caption
figcaption {
font-size: 13px;
text-align: center;
}
// List
ul {
list-style: none;
li {
display: list-item;
text-align: -webkit-match-parent;
}
li::before {
content: "\FE63";
display: inline-block;
top: -1px;
width: 1.2em;
position: relative;
margin-left: -1.3em;
font-weight: 700;
}
}
ol {
list-style: none;
counter-reset: li;
li {
position: relative;
counter-increment: li;
&::before {
content: counter(li);
display: inline-block;
width: 1em;
margin-right: 0.5em;
margin-left: -1.6em;
text-align: right;
direction: rtl;
font-weight: $bold-weight;
font-size: $small-font-size;
}
}
}
ul,
ol {
margin-top: 0;
margin-left: $spacing-full;
}
li {
padding-bottom: 1px;
padding-top: 1px;
&:before {
color: $black;
}
> ul,
> ol {
margin-bottom: 2px;
margin-top: 0;
}
}
// Headings
h1,
h2,
h3,
h4,
h5,
h6 {
color: $black;
font-weight: $bold-weight;
& + ul,
& + ol {
margin-top: 10px;
}
@include media-query($on-mobile) {
scroll-margin-top: 65px;
}
}
// Headings with link
h1 > a,
h2 > a,
h3 > a,
h4 > a,
h5 > a,
h6 > a {
text-decoration: none;
border: none;
&:hover {
text-decoration: none;
border: none;
}
}
// Link
a {
color: inherit;
text-decoration-color: $smoke;
&:hover {
color: $text-link-blue;
}
&:focus {
outline: 3px solid rgba(0, 54, 199, 0.6);
outline-offset: 2px;
}
}
// Del
del {
color: inherit;
}
// Em
em {
color: inherit;
}
// Blockquotes
blockquote {
color: $gray;
font-style: italic;
text-align: center;
opacity: 0.9;
border-top: 1px solid $light;
border-bottom: 1px solid $light;
padding: 10px;
margin-left: 10px;
margin-right: 10px;
font-size: 1em;
> :last-child {
margin-bottom: 0;
margin-top: 0;
}
}
// Wrapper
.wrapper {
max-width: -webkit-calc(#{$narrow-size} - (#{$spacing-full} * 2));
max-width: calc(#{$narrow-size} - (#{$spacing-full} * 2));
position: relative;
margin-right: auto;
margin-left: auto;
padding-right: $spacing-full;
padding-left: $spacing-full;
@extend %clearfix;
@include media-query($on-mobile) {
max-width: -webkit-calc(#{$narrow-size} - (#{$spacing-full}));
max-width: calc(#{$narrow-size} - (#{$spacing-full}));
padding-right: $spacing-full - 10;
padding-left: $spacing-full - 10;
&.blurry {
animation: 0.2s ease-in forwards blur;
-webkit-animation: 0.2s ease-in forwards blur;
}
}
}
// Underline
u {
text-decoration-color: #d2c7c7;
}
// Small
small {
font-size: $small-font-size;
}
// Superscript
sup {
border-radius: 10%;
top: -3px;
left: 2px;
font-size: small;
position: relative;
margin-right: 2px;
}
// Table
.overflow-table {
overflow-x: auto;
}
table {
width: 100%;
margin-top: $spacing-half;
border-collapse: collapse;
font-size: $small-font-size;
thead {
font-weight: $bold-weight;
color: $black;
border-bottom: 1px solid $light;
}
th,
td,
tr {
border: 1px solid $light;
padding: 2px 7px;
}
}
// Clearfix
%clearfix:after {
content: "";
display: table;
clear: both;
}
// When mouse block a text set this color
mark,
::selection {
background: #fffba0;
color: $black;
}
// Github Gist clear border
.gist {
table {
border: 0;
tr,
td {
border: 0;
}
}
}

View file

@ -1,247 +0,0 @@
body[data-theme="dark"] {
color: $dark-text-base-color;
background-color: $dark-black;
// Heading
h1,
h2,
h3,
h4,
h5,
h6 {
color: $dark-white;
}
// Table
table {
thead {
color: $dark-white;
border-color: $dark-light;
}
th,
td,
tr {
border-color: $dark-light;
}
}
// Post
.page-content {
a {
color: $dark-text-link-blue;
&:hover,
&:active,
&:focus {
color: $dark-text-link-blue-active;
}
}
h3 {
border-color: $dark-light;
}
h1,
h2,
h3,
h4,
h5,
h6 {
.anchor-head {
color: $dark-text-link-blue;
}
}
}
// Syntax
code {
&.highlighter-rouge {
background-color: $dark-light;
}
}
// kbd tag
kbd {
border-color: $dark-light;
color: $dark-white;
text-shadow: 0 1px 0 $dark-black;
}
// horizontal rule
hr {
border-color: $dark-light;
}
// Post Meta
.post-meta {
color: $dark-gray;
time {
&::after {
background-color: $dark-light;
}
}
span[itemprop="author"] {
border-color: $dark-light;
}
}
// Link
a {
color: inherit;
text-decoration-color: $dark-smoke;
&:hover {
color: $dark-text-link-blue;
}
&:focus {
outline-color: rgba(255, 82, 119, 0.6);
}
}
// List
li {
&:before {
color: $dark-white;
}
}
// Blockquote
blockquote {
color: $dark-gray;
border-color: $dark-light;
}
// Strong, Bold
strong,
b {
color: $dark-white;
}
// Navbar
.navbar {
border-color: $dark-light;
.menu {
a#mode {
.mode-sunny {
display: block;
}
.mode-moon {
display: none;
}
}
.menu-link {
color: $dark-white;
}
@include media-query($on-mobile) {
background-color: $dark-black;
border-color: $dark-light;
.menu-icon {
> svg {
fill: $dark-white;
}
}
input[type="checkbox"]:checked ~ .trigger {
background: $dark-black;
}
}
}
}
// Post Item
.post-item {
&:not(:first-child) {
border-color: $dark-light;
}
.post-item-date {
color: $dark-white;
}
.post-item-title {
a {
color: $dark-text-base-color;
&:hover,
&focus {
color: $dark-white;
}
}
}
}
// Post Navigation
.post-nav {
border-color: $dark-light;
.post-nav-item {
font-weight: $bold-weight;
.post-title {
color: $dark-white;
opacity: 0.9;
}
&:hover,
&:focus {
.post-title {
color: $dark-text-link-blue-active;
}
}
.nav-arrow {
color: $dark-gray;
}
}
@include media-query($on-mobile) {
.post-nav-item:nth-child(even) {
border-color: $dark-light;
}
}
}
// Footer
.footer {
span.footer_item {
color: $dark-white;
}
a.footer_item:not(:last-child) {
color: $dark-white;
}
.footer_copyright {
color: $dark-gray;
opacity: 1;
}
}
// 404 Page
.not-found {
.title {
color: $dark-white;
text-shadow: 1px 0px 0px $dark-text-link-blue;
}
.phrase {
color: $dark-text-base-color;
}
.solution {
color: $dark-text-link-blue;
}
.solution:hover {
color: $dark-text-link-blue-active;
}
}
.search-article {
input[type="search"] {
color: $dark-text-base-color;
&::-webkit-input-placeholder {
color: rgba(128,128,128,0.8);
}
}
}
}

View file

@ -1 +0,0 @@
@charset "utf-8";

View file

@ -1,380 +0,0 @@
// Navbar
.navbar {
height: auto;
max-width: calc(#{$wide-size} - (#{$spacing-full} * 2));
max-width: -webkit-calc(#{$wide-size} - (#{$spacing-full} * 2));
position: relative;
margin-right: auto;
margin-left: auto;
border-bottom: 1px solid $light;
padding: $spacing-full - 15px $spacing-full;
@extend %clearfix;
}
// Navigation
.menu {
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
a#mode {
float: left;
left: 8px;
top: 6px;
position: relative;
clear: both;
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
opacity: 0.7;
z-index: 1;
&:hover {
cursor: pointer;
opacity: 1;
}
&:active {
-webkit-transform: scale(0.9, 0.9);
transform: scale(0.9, 0.9);
}
.mode-moon {
display: block;
line {
stroke: $black;
fill: none;
}
circle {
fill: $black;
stroke: $black;
}
}
.mode-sunny {
display: none;
line {
stroke: $dark-white;
fill: none;
}
circle {
fill: none;
stroke: $dark-white;
}
}
}
.trigger {
float: right;
}
.menu-trigger {
display: none;
}
.menu-icon {
display: none;
}
.menu-link {
color: $black;
line-height: $base-line-height + 0.4;
text-decoration: none;
padding: 5px 8px;
opacity: 0.7;
letter-spacing: 0.3px;
&:hover {
opacity: 1;
}
&:not(:last-child) {
margin-right: 5px;
}
&.rss {
position: relative;
bottom: -3px;
outline: none;
}
@include media-query($on-mobile) {
opacity: 0.8;
}
}
.menu-link.active {
opacity: 1;
font-weight: 600;
}
@include media-query($on-mobile) {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 2;
text-align: center;
height: 50px;
background-color: $white;
border-bottom: 1px solid $light;
a#mode {
left: 10px;
top: 12px;
}
.menu-icon {
display: block;
position: absolute;
right: 0;
width: 50px;
height: 23px;
line-height: 0;
padding-top: 13px;
padding-bottom: 15px;
cursor: pointer;
text-align: center;
z-index: 1;
> svg {
fill: $black;
opacity: 0.7;
}
&:hover {
> svg {
opacity: 1;
}
}
&:active {
-webkit-transform: scale(0.9, 0.9);
transform: scale(0.9, 0.9);
}
}
input[type="checkbox"]:not(:checked) ~ .trigger {
clear: both;
visibility: hidden;
}
input[type="checkbox"]:checked ~ .trigger {
position: fixed;
animation: 0.2s ease-in forwards fadein;
-webkit-animation: 0.2s ease-in forwards fadein;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: $white;
height: 100vh;
width: 100%;
top: 0;
}
.menu-link {
display: block;
box-sizing: border-box;
font-size: 1.1em;
&:not(:last-child) {
margin: 0;
padding: 2px 0;
}
}
}
}
// Author
.author {
margin-top: 6.3rem;
margin-bottom: 7.2rem;
text-align: center;
@include media-query($on-mobile) {
margin-bottom: 3em;
}
.author-avatar {
width: 70px;
height: 70px;
border-radius: 100%;
user-select: none;
background-color: $black;
-ms-user-select: none;
-webkit-user-select: none;
-webkit-animation: 0.5s ease-in forwards fadein;
animation: 0.5s ease-in forwards fadein;
opacity: 1;
}
.author-name {
font-size: 1.7em;
margin-bottom: 2px;
}
.author-bio {
margin: 0 auto;
opacity: 0.9;
max-width: 393px;
line-height: 1.688;
}
}
// Content
.posts-item-note {
font-size: $base-font-size;
font-weight: 700;
margin-bottom: 5px;
color: $black;
}
// List of posts
.post-item {
display: flex;
padding-top: 5px;
padding-bottom: 6px;
@extend %clearfix;
&:not(:first-child) {
border-top: 1px solid $light;
}
.post-item-date {
min-width: 96px;
color: $black;
font-weight: 700;
padding-right: 10px;
@include media-query($on-mobile) {
font-size: 16px;
}
}
.post-item-title {
margin: 0;
border: 0;
padding: 0;
font-size: $base-font-size;
font-weight: normal;
letter-spacing: 0.1px;
a {
color: $text-base-color;
&:hover,
&focus {
color: $black;
}
}
}
}
// Footer
.footer {
margin-top: 8em;
margin-bottom: 2em;
text-align: center;
@include media-query($on-mobile) {
margin-top: 3em;
}
span.footer_item {
color: $black;
opacity: 0.8;
font-weight: $bold-weight;
font-size: $small-font-size;
}
a.footer_item {
color: $black;
opacity: 0.8;
text-decoration: none;
&:not(:last-child) {
margin-right: 10px;
&:hover {
opacity: 1;
}
}
}
.footer_copyright {
font-size: $small-font-size - 1;
margin-top: 3px;
display: block;
color: $gray;
opacity: 0.8;
}
}
.not-found {
text-align: center;
display: flex;
justify-content: center;
flex-direction: column;
height: 75vh;
.title {
font-size: 5em;
font-weight: $bold-weight;
line-height: 1.1;
color: $black;
text-shadow: 1px 0px 0px $text-link-blue;
}
.phrase {
color: $text-base-color;
}
.solution {
color: $text-link-blue;
letter-spacing: 0.5px;
}
.solution:hover {
color: $text-link-blue-active;
}
}
.search-article {
position: relative;
margin-bottom: 50px;
label[for="search-input"] {
position: relative;
top: 10px;
left: 11px;
}
input[type="search"] {
top: 0;
left: 0;
border: 0;
width: 100%;
height: 40px;
outline: none;
position: absolute;
border-radius: 5px;
padding: 10px 10px 10px 35px;
color: $text-base-color;
-webkit-appearance: none;
font-size: $base-font-size;
background-color: rgba(128, 128, 128, 0.1);
border: 1px solid rgba(128, 128, 128, 0.1);
&::-webkit-input-placeholder {
color: #808080;
}
&::-webkit-search-decoration,
&::-webkit-search-results-decoration {
display: none;
}
}
}
#search-results {
text-align: center;
li {
text-align: left;
}
}
.archive-tags {
height: auto;
.tag-item {
padding: 1px 3px;
border-radius: 2px;
border: 1px solid rgba(128, 128, 128, 0.1);
background-color: rgba(128, 128, 128, 0.1);
}
}

View file

@ -1,41 +0,0 @@
// Animation fade-in
@keyframes fadein {
0% {
opacity: 0.2;
}
100% {
opacity: 0.8;
}
}
// Animation blur
@keyframes blur {
0% {
filter: blur(0px);
}
100% {
filter: blur(4px);
}
}
// Responsive embed video
.embed-responsive {
height: 0;
max-width: 100%;
overflow: hidden;
position: relative;
padding-bottom: 56.25%;
margin-top: 20px;
iframe,
object,
embed {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
}

View file

@ -1,261 +0,0 @@
// Post wrapper
.wrapper.post {
@include media-query($on-mobile) {
padding-left: $spacing-half;
padding-right: $spacing-half;
}
}
// Post title
.header {
margin-top: 7.8em;
margin-bottom: 3em;
.tags {
margin-left: 3px;
letter-spacing: 0.5px;
.tag {
font-weight: $bold-weight;
font-size: $small-font-size - 2;
&:hover {
text-decoration: none;
}
}
}
.header-title {
font-size: 2em;
line-height: 1.2;
margin-top: 10px;
margin-bottom: 20px;
&.center {
text-align: center;
}
@include media-query($on-mobile) {
font-size: 1.9em;
}
}
}
// Post meta
.post-meta {
padding-top: 3px;
line-height: 1.3;
color: $gray;
time {
position: relative;
margin-right: 1.5em;
&::after {
background: $light;
bottom: 1px;
content: " ";
height: 2px;
position: absolute;
right: -20px;
width: 12px;
}
}
span[itemprop="author"] {
border-bottom: 1px dotted $light;
}
}
// Post content
.page-content {
padding-top: 8px;
iframe {
text-align: center;
}
figure {
img {
border-radius: 2px;
}
figcaption {
margin-top: 5px;
font-style: italic;
font-size: $small-font-size;
}
}
a {
color: $text-link-blue;
text-decoration: none;
&[target="_blank"]::after {
content: " \2197";
font-size: $small-font-size;
line-height: 0;
position: relative;
bottom: 5px;
vertical-align: baseline;
}
&:hover {
color: $text-link-blue-active;
}
&:focus {
color: $text-link-blue;
}
}
> p {
margin: 0;
padding-top: $spacing-full - 15;
padding-bottom: $spacing-full - 15;
}
ul.task-list {
list-style: none;
margin: 0;
li::before {
content: "";
}
li input[type="checkbox"] {
margin-right: 10px;
}
}
dl dt {
font-weight: $bold-weight;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: $black;
font-weight: $bold-weight;
margin-top: $spacing-full;
margin-bottom: 0;
&:hover {
.anchor-head {
color: $text-link-blue;
opacity: 1;
}
}
.anchor-head {
position: relative;
opacity: 0;
outline: none;
&::before {
content: "#";
position: absolute;
right: -3px;
width: 1em;
font-weight: $bold-weight;
}
}
}
h1 {
@include relative-font-size(1.5);
}
h2 {
@include relative-font-size(1.375);
}
h3 {
@include relative-font-size(1.25);
border-bottom: 1px solid $light;
padding-bottom: 4px;
}
h4 {
@include relative-font-size(1.25);
}
h5 {
@include relative-font-size(1);
}
h6 {
@include relative-font-size(0.875);
}
}
.post-nav {
display: flex;
position: relative;
margin-top: 5em;
border-top: 1px solid $light;
line-height: 1.4;
.post-nav-item {
border-bottom: 0;
font-weight: $bold-weight;
padding-bottom: 10px;
.post-title {
color: $black;
}
&:hover,
&:focus {
.post-title {
color: $text-link-blue-active;
opacity: 0.9;
}
}
.nav-arrow {
font-weight: $normal-weight;
font-size: $small-font-size;
color: $gray;
margin-bottom: 3px;
}
width: 50%;
padding-top: 10px;
text-decoration: none;
box-sizing: border-box;
&:nth-child(odd) {
padding-left: 0;
padding-right: 20px;
}
&:nth-child(even) {
text-align: right;
padding-right: 0;
padding-left: 20px;
}
}
@include media-query($on-mobile) {
display: block;
font-size: $small-font-size;
.post-nav-item {
display: block;
width: 100%;
}
.post-nav-item:nth-child(even) {
border-left: 0;
padding-left: 0;
border-top: 1px solid $light;
}
}
}
.post-updated-at {
font-family: "Ubuntu mono", "monospace";
}

View file

@ -1,185 +0,0 @@
// Code
code {
font-family: $mono-family;
text-rendering: optimizeLegibility;
font-feature-settings: "calt" 1;
font-variant-ligatures: normal;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
font-size: inherit;
&.highlighter-rouge {
padding: 1px 3px;
position: relative;
top: -1px;
background-color: #f6f6f6;
border-radius: 2px;
border: 1px solid rgba(128,128,128,0.1);
}
}
// Codeblock Theme
pre.highlight, pre {
margin: 0 -27px;
@include media-query($on-mobile) {
margin: 0 calc(51% - 51vw);
padding-left: 20px;
}
border: 1px solid rgba(128,128,128,0.1);
background-color: #1a1b21;
border-radius: 2px;
padding: 10px;
display: block;
overflow-x: auto;
> code {
width: 100%;
max-width: 50rem;
margin-left: auto;
margin-right: auto;
line-height: 1.5;
display: block;
border: 0;
}
}
.highlight table td {
padding: 5px;
}
.highlight table pre {
margin: 0;
}
.highlight,
.highlight .w {
color: #fbf1c7;
// background-color: #1a1b21;
}
.highlight .err {
color: #fb4934;
// background-color: #1a1b21;
font-weight: bold;
}
.highlight .c,
.highlight .cd,
.highlight .cm,
.highlight .c1,
.highlight .cs {
color: #928374;
font-style: italic;
}
.highlight .cp {
color: #8ec07c;
}
.highlight .nt {
color: #fb4934;
}
.highlight .o,
.highlight .ow {
color: #fbf1c7;
}
.highlight .p,
.highlight .pi {
color: #fbf1c7;
}
.highlight .gi {
color: #b8bb26;
background-color: #282828;
}
.highlight .gd {
color: #fb4934;
background-color: #282828;
}
.highlight .gh {
color: #b8bb26;
font-weight: bold;
}
.highlight .k,
.highlight .kn,
.highlight .kp,
.highlight .kr,
.highlight .kv {
color: #fb4934;
}
.highlight .kc {
color: #d3869b;
}
.highlight .kt {
color: #fabd2f;
}
.highlight .kd {
color: #fe8019;
}
.highlight .s,
.highlight .sb,
.highlight .sc,
.highlight .sd,
.highlight .s2,
.highlight .sh,
.highlight .sx,
.highlight .s1 {
color: #b8bb26;
font-style: italic;
}
.highlight .si {
color: #b8bb26;
font-style: italic;
}
.highlight .sr {
color: #b8bb26;
font-style: italic;
}
.highlight .se {
color: #fe8019;
}
.highlight .nn {
color: #8ec07c;
}
.highlight .nc {
color: #8ec07c;
}
.highlight .no {
color: #d3869b;
}
.highlight .na {
color: #b8bb26;
}
.highlight .m,
.highlight .mf,
.highlight .mh,
.highlight .mi,
.highlight .il,
.highlight .mo,
.highlight .mb,
.highlight .mx {
color: #d3869b;
}
.highlight .ss {
color: #83a598;
}

View file

@ -1,63 +0,0 @@
// Fonts preferences
$sans-family: Roboto, sans-serif;
$mono-family: Consolas, monospace;
$base-font-size: 16px;
$medium-font-size: $base-font-size * 0.938;
$small-font-size: $base-font-size * 0.875;
$base-line-height: 1.85;
// Font weight
// $light-weight: 300; // uncomment if necessary
$normal-weight: 400;
$bold-weight: 700;
// $black-weight: 900; // uncomment if necessary
//Light Colors
$text-base-color: #434648;
$text-link-blue: #003fff;
$text-link-blue-active: #0036c7;
$black: #0d122b;
$light: #ececec;
$smoke: #d2c7c7;
$gray: #6b7886;
$white: #fff;
// Dark Colors
$dark-text-base-color: #c7bebe;
$dark-text-link-blue: #ff5277;
$dark-text-link-blue-active: #ff2957;
$dark-black: #131418;
$dark-white: #eaeaea;
$dark-light: #1b1d25;
$dark-smoke: #4a4d56;
$dark-gray: #767f87;
// Width of the content area
$wide-size: 890px;
$narrow-size: 720px;
// Padding unit
$spacing-full: 30px;
$spacing-half: $spacing-full / 2;
// State of devices
$on-mobile: 768px;
$on-tablet: 769px;
$on-desktop: 1024px;
$on-widescreen: 1152px;
@mixin media-query($device) {
@media screen and (max-width: $device) {
@content;
}
}
@mixin relative-font-size($ratio) {
font-size: $base-font-size * $ratio;
}
// Import sass files
@import "klise/fonts", "klise/base", "klise/layout", "klise/post",
"klise/miscellaneous", "klise/syntax", "klise/dark";

View file

@ -1,9 +0,0 @@
---
title: Me
permalink: /about/
layout: page
excerpt: Free PIIs
comments: false
---
Here I might post some personally identifiable information.

View file

@ -1,29 +0,0 @@
---
title: Archive
permalink: /archive/
layout: page
excerpt: All posts.
comments: false
---
<div class="search-article">
<label for="search-input" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="rgba(128,128,128,0.8)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
</label>
<input type="search" id="search-input" placeholder="Find some articles here" aria-label="Search">
</div>
<ul id="search-results"></ul>
{%- for post in site.posts -%}
{%- capture current_year -%}{{ post.date | date: "%Y" }}{%- endcapture -%}
{%- unless current_year == previous_year -%}
<h2>{{ current_year }}</h2>
{%- assign previous_year = current_year -%}
{%- endunless -%}
<article class="post-item">
<h3 class="post-item-title">
<a href="{{ post.url }}">{{ post.title | escape }}</a>
</h3>
</article>
{%- endfor -%}

File diff suppressed because one or more lines are too long

View file

@ -1,4 +0,0 @@
---
---
@import "main";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#603cba</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1,25 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="500.000000pt" height="500.000000pt" viewBox="0 0 500.000000 500.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,500.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2230 4984 c-106 -12 -129 -16 -230 -35 -89 -17 -92 -17 -201 -50
-296 -87 -565 -225 -829 -423 -108 -81 -329 -297 -412 -401 -198 -252 -372
-577 -448 -840 -26 -93 -59 -223 -65 -260 -4 -22 -9 -52 -11 -66 -22 -127 -31
-269 -29 -454 2 -115 5 -219 8 -230 2 -11 8 -42 11 -70 12 -82 16 -106 37
-200 12 -49 22 -94 23 -100 10 -45 76 -238 107 -314 125 -299 301 -562 539
-801 358 -361 775 -585 1280 -691 97 -20 128 -25 260 -39 80 -8 475 -6 500 3
8 3 34 8 58 11 109 15 134 19 234 41 276 62 582 193 820 353 474 318 826 783
998 1319 31 97 36 114 46 163 3 14 9 41 14 60 39 151 55 312 55 550 -1 242
-22 415 -79 635 -16 62 -28 103 -36 120 -5 11 -13 36 -19 55 -54 203 -267 576
-452 795 -37 44 -224 232 -281 282 -68 60 -234 180 -326 237 -97 59 -352 186
-375 186 -7 0 -17 4 -22 9 -25 22 -319 108 -435 126 -25 4 -52 9 -60 10 -8 1
-37 6 -65 9 -27 4 -66 9 -85 12 -75 12 -429 11 -530 -2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,19 +0,0 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View file

@ -1 +0,0 @@
!function(t,e,n){"use strict";var o=function(t,e){var n,o;return function(){var i=this,r=arguments,d=+new Date;n&&d<n+t?(clearTimeout(o),o=setTimeout(function(){n=d,e.apply(i,r)},t)):(n=d,e.apply(i,r))}},i=!1,r=!1,d=!1,s=!1,a="unloaded",u=!1,l=function(){if(!u||!e.body.contains(u)||"loaded"==u.disqusLoaderStatus)return!0;var n,o,i=t.pageYOffset,l=(n=u,o=n.getBoundingClientRect(),{top:o.top+e.body.scrollTop,left:o.left+e.body.scrollLeft}).top;if(l-i>t.innerHeight*r||i-l-u.offsetHeight-t.innerHeight*r>0)return!0;var c,f,p,y=e.getElementById("disqus_thread");y&&y.removeAttribute("id"),u.setAttribute("id","disqus_thread"),u.disqusLoaderStatus="loaded","loaded"==a?DISQUS.reset({reload:!0,config:d}):(t.disqus_config=d,"unloaded"==a&&(a="loading",c=s,f=function(){a="loaded"},(p=e.createElement("script")).src=c,p.async=!0,p.setAttribute("data-timestamp",+new Date),p.addEventListener("load",function(){"function"==typeof f&&f()}),(e.head||e.body).appendChild(p)))};t.addEventListener("scroll",o(i,l)),t.addEventListener("resize",o(i,l)),t.disqusLoader=function(t,n){n=function(t,e){var n,o={};for(n in t)Object.prototype.hasOwnProperty.call(t,n)&&(o[n]=t[n]);for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(o[n]=e[n]);return o}({laziness:1,throttle:250,scriptUrl:!1,disqusConfig:!1},n),r=n.laziness+1,i=n.throttle,d=n.disqusConfig,s=!1===s?n.scriptUrl:s,(u="string"==typeof t?e.querySelector(t):"number"==typeof t.length?t[0]:t)&&(u.disqusLoaderStatus="unloaded"),l()}}(window,document);

View file

@ -1,31 +0,0 @@
(() => {
// Theme switch
const body = document.body;
const lamp = document.getElementById("mode");
const toggleTheme = (state) => {
if (state === "dark") {
localStorage.setItem("theme", "light");
body.removeAttribute("data-theme");
} else if (state === "light") {
localStorage.setItem("theme", "dark");
body.setAttribute("data-theme", "dark");
} else {
initTheme(state);
}
};
lamp.addEventListener("click", () =>
toggleTheme(localStorage.getItem("theme"))
);
// Blur the content when the menu is open
const cbox = document.getElementById("menu-trigger");
cbox.addEventListener("change", function () {
const area = document.querySelector(".wrapper");
this.checked
? area.classList.add("blurry")
: area.classList.remove("blurry");
});
})();

View file

@ -1,6 +0,0 @@
/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
!function(){"use strict";var i={compile:function(r){return o.template.replace(o.pattern,function(t,e){var n=o.middleware(e,r[e],o.template);return void 0!==n?n:r[e]||t})},setOptions:function(t){o.pattern=t.pattern||o.pattern,o.template=t.template||o.template,"function"==typeof t.middleware&&(o.middleware=t.middleware)}},o={};o.pattern=/\{(.*?)\}/g,o.template="",o.middleware=function(){};var n=function(t,e){var n=e.length,r=t.length;if(n<r)return!1;if(r===n)return t===e;t:for(var i=0,o=0;i<r;i++){for(var u=t.charCodeAt(i);o<n;)if(e.charCodeAt(o++)===u)continue t;return!1}return!0},e=new function(){this.matches=function(t,e){return n(e.toLowerCase(),t.toLowerCase())}};var r=new function(){this.matches=function(e,t){return!!e&&(e=e.trim().toLowerCase(),(t=t.trim().toLowerCase()).split(" ").filter(function(t){return 0<=e.indexOf(t)}).length===t.split(" ").length)}};var u={put:function(t){if(f(t))return p(t);if(function(t){return Boolean(t)&&"[object Array]"===Object.prototype.toString.call(t)}(t))return function(t){var e=[];l();for(var n=0,r=t.length;n<r;n++)f(t[n])&&e.push(p(t[n]));return e}(t);return undefined},clear:l,search:function(t){return t?function(t,e,n,r){for(var i=[],o=0;o<t.length&&i.length<r.limit;o++){var u=function(t,e,n,r){for(var i in t)if(!function(t,e){for(var n=!1,r=0,i=(e=e||[]).length;r<i;r++){var o=e[r];!n&&new RegExp(t).test(o)&&(n=!0)}return n}(t[i],r.exclude)&&n.matches(t[i],e))return t}(t[o],e,n,r);u&&i.push(u)}return i}(s,t,c.searchStrategy,c).sort(c.sort):[]},setOptions:function(t){(c=t||{}).fuzzy=t.fuzzy||!1,c.limit=t.limit||10,c.searchStrategy=t.fuzzy?e:r,c.sort=t.sort||a}};function a(){return 0}var s=[],c={};function l(){return s.length=0,s}function f(t){return Boolean(t)&&"[object Object]"===Object.prototype.toString.call(t)}function p(t){return s.push(t),s}c.fuzzy=!1,c.limit=10,c.searchStrategy=c.fuzzy?e:r,c.sort=a;var d={load:function(t,e){var n=window.XMLHttpRequest?new window.XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");n.open("GET",t,!0),n.onreadystatechange=function(e,n){return function(){if(4===e.readyState&&200===e.status)try{n(null,JSON.parse(e.responseText))}catch(t){n(t,null)}}}(n,e),n.send()}};var h={merge:function(t,e){var n={};for(var r in t)n[r]=t[r],"undefined"!=typeof e[r]&&(n[r]=e[r]);return n},isJSON:function(t){try{return t instanceof Object&&JSON.parse(JSON.stringify(t))?!0:!1}catch(e){return!1}}};var t,m,v,w;function y(t){u.put(t),m.searchInput.addEventListener("input",function(t){-1===[13,16,20,37,38,39,40,91].indexOf(t.which)&&(g(),z(t.target.value))})}function g(){m.resultsContainer.innerHTML=""}function O(t){m.resultsContainer.innerHTML+=t}function z(t){var e;(e=t)&&0<e.length&&(g(),function(t,e){var n=t.length;if(0===n)return O(m.noResultsText);for(var r=0;r<n;r++)t[r].query=e,O(i.compile(t[r]))}(u.search(t),t))}function S(t){throw new Error("SimpleJekyllSearch --- "+t)}t=window,m={searchInput:null,resultsContainer:null,json:[],success:Function.prototype,searchResultTemplate:'<li><a href="{url}" title="{description}">{title}</a></li>',templateMiddleware:Function.prototype,sortMiddleware:function(){return 0},noResultsText:"No results found",limit:10,fuzzy:!1,exclude:[]},w=function j(t){if(!((e=t)&&"undefined"!=typeof e.required&&e.required instanceof Array))throw new Error("-- OptionsValidator: required options missing");var e;if(!(this instanceof j))return new j(t);var r=t.required;this.getRequiredOptions=function(){return r},this.validate=function(e){var n=[];return r.forEach(function(t){"undefined"==typeof e[t]&&n.push(t)}),n}}({required:v=["searchInput","resultsContainer","json"]}),t.SimpleJekyllSearch=function(t){var n;0<w.validate(t).length&&S("You must specify the following required options: "+v),m=h.merge(m,t),i.setOptions({template:m.searchResultTemplate,middleware:m.templateMiddleware}),u.setOptions({fuzzy:m.fuzzy,limit:m.limit,sort:m.sortMiddleware}),h.isJSON(m.json)?y(m.json):(n=m.json,d.load(n,function(t,e){t&&S("failed to get JSON ("+n+")"),y(e)}));var e={search:z};return"function"==typeof m.success&&m.success.call(e),e}}();

View file

@ -1,15 +0,0 @@
---
layout: none
---
[
{% for post in site.posts %}
{
"title" : "{{ post.title | escape }}",
"description" : "{{ post.description | escape }}",
"tags" : "{{ post.tags | join: ', ' }}",
"url" : "{{ site.baseurl }}{{ post.url }}",
"date" : "{{ post.date }}"
} {% unless forloop.last %},{% endunless %}
{% endfor %}
]

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/assets/favicons/mstile-150x150.png"/>
<TileColor>#2c2c2c</TileColor>
</tile>
</msapplication>
</browserconfig>

View file

@ -1,3 +0,0 @@
---
layout: home
---

View file

@ -1,25 +0,0 @@
# frozen_string_literal: true
Gem::Specification.new do |spec|
spec.name = "klise"
spec.version = "1.0.1"
spec.authors = ["Mahendrata Harpi"]
spec.email = ["justharpi@gmail.com"]
spec.summary = "🏖 Klisé is a minimalist Jekyll theme for running a personal site or blog, light & dark mode support."
spec.homepage = "https://github.com/piharpi/jekyll-klise"
spec.license = "MIT"
spec.metadata["plugin_type"] = "theme"
spec.files = `git ls-files -z`.split("\x0").select do |f|
f.match(%r{^(_(includes|layouts|sass)/|(assets|LICENSE|README)((\.(txt|md|markdown|yml)|$)))}i)
end
spec.add_runtime_dependency "jekyll", "~> 4.1"
spec.add_runtime_dependency 'jekyll-feed', '~> 0.13'
spec.add_runtime_dependency 'jekyll-sitemap', '~> 1.4'
spec.add_runtime_dependency 'jekyll-compose', '~> 0.12.0'
spec.add_runtime_dependency 'jekyll-postfiles', '~> 3.1'
spec.add_development_dependency "bundler", "~> 2.1"
end

View file

@ -1,7 +0,0 @@
{
"version": 2,
"routes": [
{ "handle": "filesystem" },
{ "src": "/.*", "status": 404, "dest": "404.html" }
]
}

View file

@ -1,27 +0,0 @@
---
title: Tags
permalink: /tags/
layout: page
excerpt: Sorted article by tags.
---
<div class="archive-tags">
<a class="tag-item" href="#">all</a>
{%- for tag in site.tags -%}
{% capture name %}{{ tag | first }}{% endcapture %}
<a class="tag-item" href="#{{name}}">{{ name }}</a>
{%- endfor -%}
</div>
{%- for tag in site.tags -%}
{%- capture name -%}{{ tag | first }}{%- endcapture -%}
<h2 id="{{ name }}">{{ name | upcase }}</h2>
{%- for post in site.tags[name] -%}
<article class="post-item" id="results-container">
<span class="post-item-date">{{ post.date | date: "%b %d, %Y" }}</span>
<h3 class="post-item-title">
<a href="{{ post.url }}">{{ post.title | escape }}</a>
</h3>
</article>
{%- endfor -%}
{%- endfor -%}

4
labels
View file

@ -1,4 +0,0 @@
org.opencontainers.image.authors=Pim Kunis
org.opencontainers.image.source=https://git.pim.kunis.nl/pim/static
org.opencontainers.image.documentation=https://git.pim.kunis.nl/pim/static/src/branch/master/README.md
org.opencontainers.image.description=My static website written in Jekyll.

View file

@ -1,18 +0,0 @@
server {
listen 80;
server_name pim.kunis.nl;
index index.html index.htm;
root /var/www/blog;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
}
error_page 404 /404.html;
}

View file

@ -1,131 +0,0 @@
---
resource_types:
- name: apprise
type: registry-image
source:
repository: git.pim.kunis.nl/pim/concourse-apprise-notifier
tag: 1.1
resources:
- name: site-repo
type: git
source:
uri: https://git.pim.kunis.nl/pim/static.git
icon: git
- name: site-registry-image
type: registry-image
source:
repository: git.pim.kunis.nl/pim/static
username: pim
password: ((api_key))
tag: ((image_version))
icon: docker
- name: apprise-notification
type: apprise
source:
host: https://apprise.pim.kunis.nl:444
key: concourse
icon: bell
notify-failed: &notify-failed
put: apprise-notification
params:
body: "Failed to deploy static website!"
jobs:
- name: build-static-website
plan:
- get: site-repo
trigger: true
- task: build-site
config:
platform: linux
image_resource:
type: registry-image
source:
repository: jekyll/builder
inputs:
- name: site-repo
outputs:
- name: site-html
params:
JEKYLL_ENV: production
run:
path: sh
args:
- -exc
- |
ls -lash
chown jekyll:jekyll site-html
cd site-repo/jekyll
bundle install
bundle exec jekyll build --disable-disk-cache --destination ../../site-html
on_failure:
<< : *notify-failed
- task: build-image
privileged: true
config:
platform: linux
image_resource:
type: registry-image
source:
repository: concourse/oci-build-task
inputs:
- name: site-repo
- name: site-html
outputs:
- name: image
params:
DOCKERFILE: site-repo/Dockerfile
LABELS_FILE: site-repo/labels
run:
path: sh
args:
- -exc
- |
build
ls image
on_failure:
<< : *notify-failed
- put: site-registry-image
params:
image: image/image.tar
on_failure:
<< : *notify-failed
- name: deploy-static-website
plan:
- get: site-registry-image
trigger: true
passed: [build-static-website]
- get: site-repo
- task: deploy-site
config:
platform: linux
image_resource:
type: registry-image
source:
repository: raesene/alpine-containertools
params:
DOCKER_HOST: ssh://root@maestro.dmz
inputs:
- name: site-repo
- name: site-registry-image
run:
path: sh
args:
- -exc
- |
ls -lash
mkdir $HOME/.ssh
cp site-repo/ssh_config $HOME/.ssh/config
echo "((private_key))" > $HOME/.ssh/id_ed25519
chmod 600 $HOME/.ssh/id_ed25519
sed "s/TAG/((image_version))/g" "site-repo/docker-stack.yml.template" > docker-stack.yml
docker stack deploy --compose-file docker-stack.yml static
on_success:
put: apprise-notification
params:
body: "Static website deployed!"
no_get: true
on_failure:
<< : *notify-failed

View file

@ -1,2 +0,0 @@
Host maestro.dmz
StrictHostKeyChecking no

View file

@ -1 +0,0 @@
image_version: "1.1"