Browse Source

Fix formatting

pull/142/head
Garrit Franke 2 years ago
parent
commit
1e55e93874
Signed by: garrit
GPG Key ID: 65586C4DDA55EA2C
  1. 2
      .github/workflows/build-docker.yml
  2. 2
      .github/workflows/ci.yaml
  3. 1
      .github/workflows/deploy.yml
  4. 58
      comments/posts/2021-02-07-storage-setup/2_garrit@slashdev.space.md
  5. 4
      comments/posts/2021-09-13-fixing-an-annoying-cron-gotcha/1_mike@obsolete29.com.md
  6. 93
      components/BlogList.js
  7. 58
      components/Footer.js
  8. 56
      components/Header.js
  9. 10
      components/Meta.js
  10. 2
      components/Page.js
  11. 30
      components/TagIcon.js
  12. 33
      content/blogroll.md
  13. 81
      content/ctf.md
  14. 28
      content/cv.md
  15. 6
      content/ideas/web-based-operating-system.md
  16. 10
      content/index.md
  17. 7
      content/posts/2020-11-17-booleans-are-wasted-memory.md
  18. 4
      content/posts/2021-02-17-notes-on-flutter-web.md
  19. 19
      content/posts/2021-02-24-vim-terminal-strategies.md
  20. 2
      content/posts/2021-03-13-git-builtin-lifesaver.md
  21. 4
      content/posts/2021-04-07-pgp-guide.md
  22. 48
      content/posts/2021-05-15-healthchecks-io-with-docker.md
  23. 77
      content/posts/2021-10-04-server-side-caching-with-apollo-graphql.md
  24. 40
      content/posts/2021-12-21-usetoggle-react-hook.md
  25. 3
      content/posts/2022-01-28-til-how-to-get-the-selected-language-of-a-browser.md
  26. 32
      content/posts/2022-03-18-fix-traefik-proxy-issues.md
  27. 18
      content/posts/2022-03-24-swapping-numbers-without-temp.md
  28. 2
      content/posts/2022-04-28-hello-to-all-the-new-mastodon-users.md
  29. 2
      content/posts/2022-04-29-weeknote-17-2022.md
  30. 2
      content/posts/2022-05-06-weeknote-18-2022.md
  31. 2
      content/posts/2022-05-24-cloning-windows-to-a-new-drive.md
  32. 1
      content/posts/2022-06-02-tar-commands.md
  33. 10
      content/posts/2022-06-10-a-list-of-bugs-in-macos.md
  34. 1
      content/posts/2022-06-29-the-only-true-answer-to-tabs-vs-spaces.md
  35. 1
      content/posts/2022-08-31-whats-on-my-feed.md
  36. 15
      content/posts/2022-09-22-kubernetes-is-a-domain-specific-database.md
  37. 4
      content/posts/2022-09-26-self-hosted-software-im-thankful-for.md
  38. 17
      content/posts/2022-10-05-simple-guestbook.md
  39. 41
      content/posts/_2022-05-02-docker-in-plain-english.md
  40. 4
      content/posts/_2022-08-30-contributing-to-open-source-knowledge.md
  41. 6
      content/posts/fighting-array-functions-with-es6.md
  42. 40
      content/posts/introducing-slashdev-space.md
  43. 6
      content/posts/testing-isnt-hard.md
  44. 14
      content/talks.md
  45. 4
      content/todo.md
  46. 98
      lib/rss.js
  47. 20
      lib/tags.js
  48. 4
      next-sitemap.config.js
  49. 44
      next.config.js
  50. 60
      package.json
  51. 2
      pages/404.js
  52. 78
      pages/[page].js
  53. 20
      pages/_app.js
  54. 28
      pages/index.js
  55. 4
      pages/posts/[post].js
  56. 82
      pages/posts/index.js
  57. 104
      pages/tags.js
  58. 6
      renovate.json
  59. 12
      styles/components/blog-list.scss
  60. 28
      styles/components/footer.scss
  61. 136
      styles/components/header.scss
  62. 7
      styles/components/layout.scss
  63. 180
      styles/components/page.scss
  64. 10
      styles/components/tag-list.scss
  65. 182
      styles/foundation/base.scss
  66. 2
      styles/foundation/utility.scss
  67. 1115
      styles/index.scss

2
.github/workflows/build-docker.yml

@ -3,7 +3,7 @@ on:
branches:
- main
paths:
- 'lib/jurassic/**/*'
- "lib/jurassic/**/*"
name: Build Docker images
jobs:

2
.github/workflows/ci.yaml

@ -21,4 +21,4 @@ jobs:
env:
GH_API_TOKEN: ${{ github.token }}
run: |
npm run build
npm run build

1
.github/workflows/deploy.yml

@ -29,4 +29,3 @@ jobs:
with:
branch: gh-pages
folder: out

58
comments/posts/2021-02-07-storage-setup/2_garrit@slashdev.space.md

@ -8,49 +8,49 @@ Hi Amine,
thanks for your kind words!
> My main concern with this setup is the extensibility aspect, If I need
> to add more storage space in the future.
>
> I was considering btrfs in raid 5 mode but it seems like it's not super
> stable yet.
I've been very impressed by how well Btrfs handles extensibility. I have
recently thrown new drive at my setup. The main advantage of Btrfs in my
opinion is that you can combine any drives you want, as opposed to ZFS,
> My main concern with this setup is the extensibility aspect, If I need
> to add more storage space in the future.
>
> I was considering btrfs in raid 5 mode but it seems like it's not super
> stable yet.
I've been very impressed by how well Btrfs handles extensibility. I have
recently thrown new drive at my setup. The main advantage of Btrfs in my
opinion is that you can combine any drives you want, as opposed to ZFS,
where you're more or less constrained to drives of the same size.
Regarding RAID: You're right, RAID 5 is unfortunately still unstable.
After one year, I don't regret going with RAID 1 (mirrored) for my
setup. If you have 4+ drives, I suggest you take a look at RAID 10,
Regarding RAID: You're right, RAID 5 is unfortunately still unstable.
After one year, I don't regret going with RAID 1 (mirrored) for my
setup. If you have 4+ drives, I suggest you take a look at RAID 10,
since it gives you some advantages over RAID 1.
> I am also considering the use of backblaze for offside backup. I had an
> excellent experience with them so far.
> I am also considering the use of backblaze for offside backup. I had an
> excellent experience with them so far.
Great choice! Backblaze has been rock solid for me as well. And they're
Great choice! Backblaze has been rock solid for me as well. And they're
cheap too!
> This, brings me to the encryption configuration. I assume you are using
> dmcrypt in both locations? Another option would be restic for offsite so
> you don't have to worry about unlocking the remote disk if system
reboots.
> This, brings me to the encryption configuration. I assume you are using
> dmcrypt in both locations? Another option would be restic for offsite so
> you don't have to worry about unlocking the remote disk if system
> reboots.
This is a topic I haven't dipped my toes into yet. All of my drives are
unencrypted. Thanks for your recommendations though, I will have to look
This is a topic I haven't dipped my toes into yet. All of my drives are
unencrypted. Thanks for your recommendations though, I will have to look
those up! Are you using restic in your current setup?
> A final consideration with this is about monitor the health of your
> disks. I wonder what solution you opted for (chat bot / gotify + script
> that checks SMART metrics?)
> A final consideration with this is about monitor the health of your
> disks. I wonder what solution you opted for (chat bot / gotify + script
> that checks SMART metrics?)
Some time ago I wrote up a blog post about using healthchecks.io with
Some time ago I wrote up a blog post about using healthchecks.io with
docker:
https://garrit.xyz/posts/2021-05-15-healthchecks-io-with-docker
healthchecks.io is really neat, since *you're* responsible of pinging
their endpoints, as opposed to *them* pinging a HTTP-endpoint. This
enables you to write Cron-scripts that frequently check the health of
healthchecks.io is really neat, since _you're_ responsible of pinging
their endpoints, as opposed to _them_ pinging a HTTP-endpoint. This
enables you to write Cron-scripts that frequently check the health of
your drives. I'm currently using this script to check my drives:
```
@ -68,4 +68,4 @@ maybe it's of use for you?
Thanks again for your comment!
Best
Garrit
Garrit

4
comments/posts/2021-09-13-fixing-an-annoying-cron-gotcha/1_mike@obsolete29.com.md

@ -7,6 +7,6 @@ source: email
Hiya. Just wanted to drop a quick note and let you know that your post
helped me get my backup cron job working so thanks for posting it!
--
--
Mike Harley <mike@obsolete29.com>
https://obsolete29.com
https://obsolete29.com

93
components/BlogList.js

@ -1,60 +1,55 @@
import Link from "next/link";
import { usePlausible } from 'next-plausible'
import { usePlausible } from "next-plausible";
const BlogList = ({ posts }) => {
const plausible = usePlausible();
const plausible = usePlausible();
const isPublicPost = (post) => !post.slug.startsWith("_");
const publicPosts = posts.filter(isPublicPost);
const isPublicPost = (post) => !post.slug.startsWith("_");
const publicPosts = posts.filter(isPublicPost);
const reformatDate = (fullDate) => {
const date = new Date(fullDate);
return date.toDateString().slice(4);
};
const reformatDate = (fullDate) => {
const date = new Date(fullDate);
return date.toDateString().slice(4);
};
const renderRandomButton = () => {
const randomIndex = Math.floor(Math.random() * publicPosts.length);
const randomPost = publicPosts[randomIndex];
const randomUrl = `/posts/${randomPost?.slug}`;
return (
<p>
<a
href={randomUrl}
onClick={() => plausible("random_post_clicked")}
>
Random Post
</a>
</p>
);
};
const renderRandomButton = () => {
const randomIndex = Math.floor(Math.random() * publicPosts.length);
const randomPost = publicPosts[randomIndex];
const randomUrl = `/posts/${randomPost?.slug}`;
return (
<p>
<a href={randomUrl} onClick={() => plausible("random_post_clicked")}>
Random Post
</a>
</p>
);
};
const renderPost = (post) => (
<div key={post.slug} className="blog__list__post">
<time className="blog__list__post__date">
{reformatDate(post.frontmatter.date)}
</time>
<br />
<Link href={`/posts/${post.slug}`}>{post.frontmatter.title}</Link>
</div>
);
const renderPost = (post) => (
<div key={post.slug} className="blog__list__post">
<time className="blog__list__post__date">
{reformatDate(post.frontmatter.date)}
</time>
<br />
<Link href={`/posts/${post.slug}`}>{post.frontmatter.title}</Link>
</div>
);
return (
<>
{renderRandomButton()}
<hr />
<div>
{publicPosts.length > 0 &&
publicPosts
.filter(isPublicPost)
// Ternary operator is used to fix chromium sorting
// See: https://stackoverflow.com/a/36507611
.sort((a, b) =>
a.frontmatter.date < b.frontmatter.date ? 1 : -1
)
.map(renderPost)}
</div>
</>
);
return (
<>
{renderRandomButton()}
<hr />
<div>
{publicPosts.length > 0 &&
publicPosts
.filter(isPublicPost)
// Ternary operator is used to fix chromium sorting
// See: https://stackoverflow.com/a/36507611
.sort((a, b) => (a.frontmatter.date < b.frontmatter.date ? 1 : -1))
.map(renderPost)}
</div>
</>
);
};
export default BlogList;

58
components/Footer.js

@ -1,31 +1,31 @@
export default function Footer() {
return (
<footer className="footer">
<div className="footer__content">
<h3>Links of Interest</h3>
<a href="/rss.xml">RSS Feed</a>
<br />
<a href="/todo">Todo List</a>
<br />
<a href="https://keyoxide.org/hkp/garrit@slashdev.space">PGP Key</a>
<br />
<a href="/guestbook">Guestbook</a>
<br />
<a href="/blogroll">Blogroll</a>
<br />
<a href="/ctf">Capture the Flag</a>
<h3>Elsewhere</h3>
<a href="https://github.com/garritfra" rel="me">
Github
</a>
<br />
<a href="https://www.linkedin.com/in/garritfranke/">LinkedIn</a>
<br />
<a href="https://fosstodon.org/@garritfra">Mastodon (ActivityPub)</a>
<br />
<a href="/contact">Contact</a>
</div>
<p>© 2018-2022 Garrit Franke</p>
</footer>
);
return (
<footer className="footer">
<div className="footer__content">
<h3>Links of Interest</h3>
<a href="/rss.xml">RSS Feed</a>
<br />
<a href="/todo">Todo List</a>
<br />
<a href="https://keyoxide.org/hkp/garrit@slashdev.space">PGP Key</a>
<br />
<a href="/guestbook">Guestbook</a>
<br />
<a href="/blogroll">Blogroll</a>
<br />
<a href="/ctf">Capture the Flag</a>
<h3>Elsewhere</h3>
<a href="https://github.com/garritfra" rel="me">
Github
</a>
<br />
<a href="https://www.linkedin.com/in/garritfranke/">LinkedIn</a>
<br />
<a href="https://fosstodon.org/@garritfra">Mastodon (ActivityPub)</a>
<br />
<a href="/contact">Contact</a>
</div>
<p>© 2018-2022 Garrit Franke</p>
</footer>
);
}

56
components/Header.js

@ -1,30 +1,30 @@
export default function Header(props) {
return (
<header className="header">
<nav className="nav" role="navigation" aria-label="main navigation">
<div className="header__container">
<a href="/" className="header__container__logo underlined">
{props.siteTitle}
</a>
</div>
<ul className="header__links">
<li>
<a href="/posts" className="underlined">
Blog
</a>
</li>
<li>
<a href="/contact" className="underlined">
Contact
</a>
</li>
<li>
<a href="/cv" className="underlined">
Resume
</a>
</li>
</ul>
</nav>
</header>
);
return (
<header className="header">
<nav className="nav" role="navigation" aria-label="main navigation">
<div className="header__container">
<a href="/" className="header__container__logo underlined">
{props.siteTitle}
</a>
</div>
<ul className="header__links">
<li>
<a href="/posts" className="underlined">
Blog
</a>
</li>
<li>
<a href="/contact" className="underlined">
Contact
</a>
</li>
<li>
<a href="/cv" className="underlined">
Resume
</a>
</li>
</ul>
</nav>
</header>
);
}

10
components/Meta.js

@ -39,10 +39,7 @@ export default function Meta(props) {
return (
<>
<Head>
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
<title>{props.pageTitle || props.siteTitle}</title>
<meta
@ -55,10 +52,7 @@ export default function Meta(props) {
rel="webmention"
href="https://webmention.io/garrit.xyz/webmention"
/>
<link
rel="pingback"
href="https://webmention.io/garrit.xyz/xmlrpc"
/>
<link rel="pingback" href="https://webmention.io/garrit.xyz/xmlrpc" />
<link
href="https://cdn.jsdelivr.net/npm/shareon@2/dist/shareon.min.css"

2
components/Page.js

@ -6,7 +6,7 @@ export default function Page(props) {
const { title, date, siteTitle } = props;
const setupEditHook = () => {
window.addEventListener("keypress", (e) => {
window.addEventListener("keypress", (e) => {
const baseUrl =
"https://github.com/garritfra/garrit.xyz/edit/main/content";
if (e.key === ".") {

30
components/TagIcon.js

@ -1,19 +1,19 @@
import React from "react";
export default () => (
<svg
class="page__tag-icon"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path>
<line x1="7" y1="7" x2="7" y2="7"></line>
</svg>
<svg
class="page__tag-icon"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path>
<line x1="7" y1="7" x2="7" y2="7"></line>
</svg>
);

33
content/blogroll.md

@ -9,26 +9,25 @@ mail](mailto:garrit@slashdev.space)!
### Blogs
* [Ben Stokes (Tiny Projects)](https://tinyprojects.dev/)
* [Devine Lu Linvega](https://wiki.xxiivv.com/)
* [Drew DeVault](https://drewdevault.com/)
* [Kev Quirk](https://kevq.uk/)
* [Matt Rickard](https://matt-rickard.com/)
* [Randall Munroe (xkcd)](https://xkcd.com/)
* [Ru Singh](https://rusingh.com/)
* [Sotolf](https://wordsmith.social/sotolf/)
* [Tomaso Marmo](https://tommi.space/)
- [Ben Stokes (Tiny Projects)](https://tinyprojects.dev/)
- [Devine Lu Linvega](https://wiki.xxiivv.com/)
- [Drew DeVault](https://drewdevault.com/)
- [Kev Quirk](https://kevq.uk/)
- [Matt Rickard](https://matt-rickard.com/)
- [Randall Munroe (xkcd)](https://xkcd.com/)
- [Ru Singh](https://rusingh.com/)
- [Sotolf](https://wordsmith.social/sotolf/)
- [Tomaso Marmo](https://tommi.space/)
### Newsboards
* [Hacker News](https://news.ycombinator.com/)
- [Hacker News](https://news.ycombinator.com/)
### Podcasts
* [CoRecursive: Coding Stories](https://corecursive.com/)
* [Darknet Diaries](https://darknetdiaries.com/)
* [Linux Action News](https://linuxactionnews.com/)
* [Linux Unplugged](https://linuxunplugged.com/)
* [Self-Hosted](https://selfhosted.show/)
* [Smashing Security](https://www.smashingsecurity.com/)
- [CoRecursive: Coding Stories](https://corecursive.com/)
- [Darknet Diaries](https://darknetdiaries.com/)
- [Linux Action News](https://linuxactionnews.com/)
- [Linux Unplugged](https://linuxunplugged.com/)
- [Self-Hosted](https://selfhosted.show/)
- [Smashing Security](https://www.smashingsecurity.com/)

81
content/ctf.md

@ -23,62 +23,61 @@ love to hear it!
## Hall of Fame
- [F1ndus](https://f1ndus.de/) - 6 flags found, last updated 2022-07-16
- [Nikita Karamov](https://www.kytta.dev/) - 6 flags found, last updated 2022-07-28
- [Jesse Gibson](https://psychollama.io/) - 6 flags found, last updated 2022-05-09
- [knl](https://lobste.rs/u/knl) - 6 flags found, last updated 2022-07-16
- [Yigit Sever](https://yigitsever.com) - 6 flags found, last updated 2022-08-17
- Chris - 6 flags found, last updated 2022-07-16
- Fabian Dürkop - 6 flags found, last updated 2022-07-26
- Shantanu Joshi - 6 flags found, last updated 2022-07-16
- n0thing maybe - 6 flags found, last updated 2022-07-15
- Will Kinderman - 6 flags found, last updated 2022-08-31
- [F1ndus](https://f1ndus.de/) - 6 flags found, last updated 2022-07-16
- [Nikita Karamov](https://www.kytta.dev/) - 6 flags found, last updated 2022-07-28
- [Jesse Gibson](https://psychollama.io/) - 6 flags found, last updated 2022-05-09
- [knl](https://lobste.rs/u/knl) - 6 flags found, last updated 2022-07-16
- [Yigit Sever](https://yigitsever.com) - 6 flags found, last updated 2022-08-17
- Chris - 6 flags found, last updated 2022-07-16
- Fabian Dürkop - 6 flags found, last updated 2022-07-26
- Shantanu Joshi - 6 flags found, last updated 2022-07-16
- n0thing maybe - 6 flags found, last updated 2022-07-15
- Will Kinderman - 6 flags found, last updated 2022-08-31
---
- [Team Krosse Flagge](https://ctftime.org/team/82581) - 5 flags found, last updated 2022-07-22
- Ezequiel Gonzalez Rial - 5 flags found, last updated 2022-07-13
- [Team Krosse Flagge](https://ctftime.org/team/82581) - 5 flags found, last updated 2022-07-22
- Ezequiel Gonzalez Rial - 5 flags found, last updated 2022-07-13
---
- [Felix Hanley](https://felixhanley.info) - 4 flags found, last updated 2022-07-13
- [Nasir](https://lobste.rs/u/thesnarky1) - 4 flags found, last updated 2022-07-13
- [Ordoviz](https://fosstodon.org/@Ordoviz) - 4 flags found, last updated 2022-07-13
- [Sergii](https://www.linkedin.com/in/serhiy-m-618020107/) - 4 flags found, last updated 2022-07-18
- Barbas Bonitas - 4 flags found, last updated 2022-07-13
- Dege - 4 flags found, last updated 2022-07-15
- Harry Cover - 4 flags found, last updated 2022-07-18
- swordfischer - 4 flag found, last updated 2022-07-19
- [Felix Hanley](https://felixhanley.info) - 4 flags found, last updated 2022-07-13
- [Nasir](https://lobste.rs/u/thesnarky1) - 4 flags found, last updated 2022-07-13
- [Ordoviz](https://fosstodon.org/@Ordoviz) - 4 flags found, last updated 2022-07-13
- [Sergii](https://www.linkedin.com/in/serhiy-m-618020107/) - 4 flags found, last updated 2022-07-18
- Barbas Bonitas - 4 flags found, last updated 2022-07-13
- Dege - 4 flags found, last updated 2022-07-15
- Harry Cover - 4 flags found, last updated 2022-07-18
- swordfischer - 4 flag found, last updated 2022-07-19
---
- [Ben Fiedler](https://3fx.ch) - 3 flags found, last updated 2022-07-13
- [Fabio Alessandro Locati](https://fale.io) - 3 flags found, last updated 2022-07-13
- [George](https://fosstodon.org/@george_) - 3 flags found, last updated 2022-07-13
- [Manuel Romei](https://fosstodon/@kriive) - 3 flags found, last updated 2022-07-13
- Gaeulbyul - 3 flags found, last updated 2022-07-13
- runejuhl - 3 flags found, last updated 2022-07-14
- [Ben Fiedler](https://3fx.ch) - 3 flags found, last updated 2022-07-13
- [Fabio Alessandro Locati](https://fale.io) - 3 flags found, last updated 2022-07-13
- [George](https://fosstodon.org/@george_) - 3 flags found, last updated 2022-07-13
- [Manuel Romei](https://fosstodon/@kriive) - 3 flags found, last updated 2022-07-13
- Gaeulbyul - 3 flags found, last updated 2022-07-13
- runejuhl - 3 flags found, last updated 2022-07-14
---
- [Jure Šumak](https://jsumak.github.io/about/) - 2 flags found, last updated 2022-07-13
- [Kev Quirk](https://kevq.uk/) - 2 flags found, last updated 2022-07-12
- [Tugcan Olgun](https://tugcan.net/) - 2 flags found, last updated 2022-07-15
- [belkarx](https://belkarx.github.io) - 2 flags found, last updated 2022-07-15
- Inbar Aran - 2 flag found, last updated 2022-07-14
- Marat Validov - 2 flag found, last updated 2022-07-13
- [Jure Šumak](https://jsumak.github.io/about/) - 2 flags found, last updated 2022-07-13
- [Kev Quirk](https://kevq.uk/) - 2 flags found, last updated 2022-07-12
- [Tugcan Olgun](https://tugcan.net/) - 2 flags found, last updated 2022-07-15
- [belkarx](https://belkarx.github.io) - 2 flags found, last updated 2022-07-15
- Inbar Aran - 2 flag found, last updated 2022-07-14
- Marat Validov - 2 flag found, last updated 2022-07-13
---
- [Askar Mirzajanov](https://t.me/gmmdt) - 1 flag found, last updated 2022-07-13
- [Frederik Braun](https://frederik-braun.com/) - 1 flag found, last updated 2022-07-13
- [Gioele Barabucci](https://gioele.io/) - 1 flag found, last updated 2022-07-13
- [Pixelcode](https://social.tchncs.de/@pixelcode) - 1 flag found, last updated 2022-07-12
- hub - 1 flag found, last updated 2022-07-13
- macbury - 1 flag found, last updated 2022-07-13
- lemmyz4n3771 - 1 flag found, last updated 2022-07-13
- [Askar Mirzajanov](https://t.me/gmmdt) - 1 flag found, last updated 2022-07-13
- [Frederik Braun](https://frederik-braun.com/) - 1 flag found, last updated 2022-07-13
- [Gioele Barabucci](https://gioele.io/) - 1 flag found, last updated 2022-07-13
- [Pixelcode](https://social.tchncs.de/@pixelcode) - 1 flag found, last updated 2022-07-12
- hub - 1 flag found, last updated 2022-07-13
- macbury - 1 flag found, last updated 2022-07-13
- lemmyz4n3771 - 1 flag found, last updated 2022-07-13
## Backlinks
- [CTFs are fun](https://sergiiwrites.online/2022/09/12/ctfs-are-fun.html)
- [CTFs are fun](https://sergiiwrites.online/2022/09/12/ctfs-are-fun.html)

28
content/cv.md

@ -17,16 +17,16 @@ _**Software Developer, March 2021 - present**_
Development of a HBBTV media library for public television
* Design and implementation of a BFF (backend for frontend) using [Nest.js](https://nestjs.com/)
- Design and implementation of a BFF (backend for frontend) using [Nest.js](https://nestjs.com/)
Development of a web-based convention application
* Development of APIs using Node.js and GraphQL
* Development of a content management system using React and Material Design
* Various performance optimizations to the platform
* [Server-side caching](/posts/2021-10-04-server-side-caching-with-apollo-graphql) solution using Redis
* Support for auto-scalable databases using AWS Aurora
* Incremental rendering
- Development of APIs using Node.js and GraphQL
- Development of a content management system using React and Material Design
- Various performance optimizations to the platform
- [Server-side caching](/posts/2021-10-04-server-side-caching-with-apollo-graphql) solution using Redis
- Support for auto-scalable databases using AWS Aurora
- Incremental rendering
### CGI Germany
@ -34,16 +34,16 @@ _**Mobile Software Developer, 2020 - Feburary 2021**_
Development of a companion app for a leading automotive company
* Development for leading mobile platforms
* Complete redesign of legacy application
* Agile workflow using the Atlassian Toolchain (Jira, Bitbucket, Confluence)
- Development for leading mobile platforms
- Complete redesign of legacy application
- Agile workflow using the Atlassian Toolchain (Jira, Bitbucket, Confluence)
Development of a mobile smart-home application
* Implementation of a Bluetooth and ZigBee pairing solution
* Reactive development using ReactiveX for iOS and Android
* Managing IoT devices using AWS IoT Core
* Maintaining a RESTful API using AWS Lambda
- Implementation of a Bluetooth and ZigBee pairing solution
- Reactive development using ReactiveX for iOS and Android
- Managing IoT devices using AWS IoT Core
- Maintaining a RESTful API using AWS Lambda
_**Trainee, 2017 - 2020**_

6
content/ideas/web-based-operating-system.md

@ -1,5 +1,5 @@
# A web-based operating system
* Full-fledged desktop environment
* Persistent file system
* Installer to bootstrap an "installation"
- Full-fledged desktop environment
- Persistent file system
- Installer to bootstrap an "installation"

10
content/index.md

@ -2,11 +2,11 @@
My interests include...
* Fullstack Development
* System Administration
* Programming Language Design
* Minimalist Software
* Free Software
- Fullstack Development
- System Administration
- Programming Language Design
- Minimalist Software
- Free Software
&emsp;

7
content/posts/2020-11-17-booleans-are-wasted-memory.md

@ -67,10 +67,11 @@ int main()
}
```
This example still wastes 5 bits since we only use 3 out of 8 possible bits of the char type, but I'm sure you get the point. Allocating 3 boolean values independently would waste 7 * 3 = 21 bits, so it's a massive improvement. Whenever you find yourself needing multiple boolean values, think twice if you can use this pattern.
This example still wastes 5 bits since we only use 3 out of 8 possible bits of the char type, but I'm sure you get the point. Allocating 3 boolean values independently would waste 7 \* 3 = 21 bits, so it's a massive improvement. Whenever you find yourself needing multiple boolean values, think twice if you can use this pattern.
Microcontrollers have a very constrainted environment, therefore bitwise operations are essential in those scenarios. 7 wasted bits are a lot if there are only 4 kb of total memory available. For larger systems we often forget about these constraints, until they add up.
## My Plea
* Be mindful about the software you create.
* Appreciate the resources at your disposal.
- Be mindful about the software you create.
- Appreciate the resources at your disposal.

4
content/posts/2021-02-17-notes-on-flutter-web.md

@ -16,8 +16,8 @@ These are some notes I took for the evaluation of Flutter web for a potential pr
- **Alternative Backends**: There are two [rendering backends](https://flutter.dev/docs/development/tools/web-renderers), both with its own benefits and drawbacks. The HTML renderer optimizes the page for the browser, which improves performance at the cost of consistency. The CanvasKit renderer renders WebGL using WebAssembly. This gives a consistent look and feel across all devices, at the cost of Performance and download size. If auto is specified, the renderer will be determined based on the device type. Here’s a comparison of the same app rendered with both backends:
| HTML | CanvasKit |
| :----------------------: | :-----------------------: |
| HTML | CanvasKit |
| :----------------------------------------: | :---------------------------------------------: |
| ![](/assets/flutter_web_renderer_html.png) | ![](/assets/flutter_web_renderer_canvaskit.png) |
## The Bad

19
content/posts/2021-02-24-vim-terminal-strategies.md

@ -21,15 +21,15 @@ you can just use the `:!` command:
## Vims builtin terminal
I couldn't believe my eyes when I read this, but Vim ships with a builtin
terminal! Executing `:term` will spawn it in your current buffer. How you
integrate it in your workflow is up to you. You could use tabs or open a
terminal! Executing `:term` will spawn it in your current buffer. How you
integrate it in your workflow is up to you. You could use tabs or open a
horizontal buffer and spawn it there. I must say that it is rather clunky to
use, since its literally a Vim buffer that forwards stdin and stdout to the
buffer, but it's there for you to use.
## Vim x Tmux
Another great alternative is to set up Tmux with two windows, one for Vim and
Another great alternative is to set up Tmux with two windows, one for Vim and
one for your terminal, and switch between them. This works great on a minimal
system, but on MacOS for example, it is easier to simply use cmd+1 and cmd+2 to
switch between two tabs of the Terminal application.
@ -39,20 +39,17 @@ switch between two tabs of the Terminal application.
This one is my personal favorite. The idea comes from
[this](https://stackoverflow.com/a/1258318/9046809) stackoverflow answer.
The plan is to pause the Vim process and resume it later. To pause Vim, you
The plan is to pause the Vim process and resume it later. To pause Vim, you
press `<ctrl>-z`. This sends the process in the background. Then, to resume the
process, simply issue the `fg` command and Vims process resumes in the
process, simply issue the `fg` command and Vims process resumes in the
foreground.
## Conclusion
I'm sure there are many more strategies that could be added to this list. I'd be
interested to hear how your setup works! If you liked these techniques, you
might be interested in
[@lopeztel](https://fosstodon.org/web/accounts/211905)s
interested to hear how your setup works! If you liked these techniques, you
might be interested in
[@lopeztel](https://fosstodon.org/web/accounts/211905)s
[cheat sheet](https://lopeztel.xyz/2021/02/21/my-neovim-cheatsheet/) for Vim.
This is post 014 of [#100DaysToOffload](https://100daystooffload.com/).

2
content/posts/2021-03-13-git-builtin-lifesaver.md

@ -4,7 +4,7 @@ date: "2021-03-13"
tags: "git, 100DaysToOffload, guide"
---
Everyone was in this situation at some point. You wasted a days worth of work by accidentally deleting a branch. But, all hope is not lost! Git never forgets.
Everyone was in this situation at some point. You wasted a days worth of work by accidentally deleting a branch. But, all hope is not lost! Git never forgets.
Every action, be it committing changes, deleting or switching branches, is noted down by Git. To see your latest actions, you can simply run `git reflog` (It's pronounced `ref-log` but `re-flog` sounds just as reasonable):

4
content/posts/2021-04-07-pgp-guide.md

@ -193,7 +193,7 @@ Oftentimes, people will store their keys on a keyserver, just like you have prob
gpg --keyserver keys.openpgp.org --search-keys garrit@slashdev.space
```
Now, your computer should know about my key. To verify that it's actually me you have imported, you can check if the output of `gpg --fingerprint garrit@slashdev.space` matches my actual fingerprint: `2218 337E 54AA 1DBE 207B 404D BB54 AF7E B093 9F3D`.
Now, your computer should know about my key. To verify that it's actually me you have imported, you can check if the output of `gpg --fingerprint garrit@slashdev.space` matches my actual fingerprint: `2218 337E 54AA 1DBE 207B 404D BB54 AF7E B093 9F3D`.
Optionally, if you trust that the key is actually associated to me, you can sign it. This let's other people know that you trust me, which helps build a so called "chain of trust". A key which has been signed by many people is generally more trustworthy than one that has no signatures.
@ -231,7 +231,7 @@ export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
```
Then, run `ssh-add -l` to load the key directly.
Then, run `ssh-add -l` to load the key directly.
To get the public ssh key of your keypair, run this command:

48
content/posts/2021-05-15-healthchecks-io-with-docker.md

@ -21,30 +21,30 @@ this snippet to your docker-compose file:
```yaml
app:
image: nextcloud
ports:
- 127.0.0.1:8080:80
healthcheck:
test:
[
"CMD",
"curl",
"-f",
"https://app-endpoint.tld",
"&&",
"curl",
"-fsS",
"-m",
"10",
"--retry",
"5",
"-o",
"/dev/null",
"https://healthchecks.io/ping/<UUID>",
]
interval: 60s
timeout: 10s
retries: 6
image: nextcloud
ports:
- 127.0.0.1:8080:80
healthcheck:
test:
[
"CMD",
"curl",
"-f",
"https://app-endpoint.tld",
"&&",
"curl",
"-fsS",
"-m",
"10",
"--retry",
"5",
"-o",
"/dev/null",
"https://healthchecks.io/ping/<UUID>",
]
interval: 60s
timeout: 10s
retries: 6
```
Change the first url to the url of your app. The second URL is the endpoint of

77
content/posts/2021-10-04-server-side-caching-with-apollo-graphql.md

@ -28,11 +28,11 @@ configuration as shown
[here](https://www.apollographql.com/docs/apollo-server/performance/caching/#caching-with-responsecacheplugin-advanced):
```js
import responseCachePlugin from 'apollo-server-plugin-response-cache';
import responseCachePlugin from "apollo-server-plugin-response-cache";
const server = new ApolloServer({
// ...other options...
plugins: [responseCachePlugin()],
// ...other options...
plugins: [responseCachePlugin()],
});
```
@ -43,17 +43,17 @@ Redis](https://www.apollographql.com/docs/apollo-server/data/data-sources/#using
multiple instances of the same backend.
```js
const { BaseRedisCache } = require('apollo-server-cache-redis');
const Redis = require('ioredis');
const { BaseRedisCache } = require("apollo-server-cache-redis");
const Redis = require("ioredis");
const server = new ApolloServer({
// ...
cache: new BaseRedisCache({
plugins: [responseCachePlugin()],
client: new Redis({
host: 'redis-server',
}),
}),
// ...
cache: new BaseRedisCache({
plugins: [responseCachePlugin()],
client: new Redis({
host: "redis-server",
}),
}),
});
```
@ -78,14 +78,14 @@ only have to do this once):
```gql
enum CacheControlScope {
PUBLIC
PRIVATE
PUBLIC
PRIVATE
}
directive @cacheControl(
maxAge: Int
scope: CacheControlScope
inheritMaxAge: Boolean
maxAge: Int
scope: CacheControlScope
inheritMaxAge: Boolean
) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
```
@ -94,10 +94,10 @@ Now you can add the `@cacheControl` directive to every type that should be cache
```gql
# This type will be cached for 30 seconds
type Post @cacheControl(maxAge: 30) {
id: ID!
title: String
author: Author
comments: [Comment]
id: ID!
title: String
author: Author
comments: [Comment]
}
```
@ -120,20 +120,20 @@ setting the `maxAge` explicitly or by inheriting it from the parent:
```gql
type Post @cacheControl(maxAge: 30) {
id: ID!
title: String
author: Author
comments: [Comment]
id: ID!
title: String
author: Author
comments: [Comment]
}
type Author @cacheControl(inheritMaxAge: true) {
id: ID!
name: String
id: ID!
name: String
}
type Comment @cacheControl(inheritMaxAge: true) {
id: ID!
body: String
id: ID!
body: String
}
```
@ -178,9 +178,9 @@ directive. This will only cache the response if a user is logged in:
```gql
type Post {
id: ID!
title: String
author: Author @cacheControl(maxAge: 10, scope: PRIVATE)
id: ID!
title: String
author: Author @cacheControl(maxAge: 10, scope: PRIVATE)
}
```
@ -188,12 +188,15 @@ Apollo determines if a user is logged in or not, based on if the `sessionId`
function has returned a value other than `null`.
```js
import responseCachePlugin from 'apollo-server-plugin-response-cache';
import responseCachePlugin from "apollo-server-plugin-response-cache";
const server = new ApolloServer({
// ...other settings...
plugins: [responseCachePlugin({
sessionId: (requestContext) => (requestContext.request.http.headers.get('sessionid') || null),
})],
// ...other settings...
plugins: [
responseCachePlugin({
sessionId: (requestContext) =>
requestContext.request.http.headers.get("sessionid") || null,
}),
],
});
```

40
content/posts/2021-12-21-usetoggle-react-hook.md

@ -8,24 +8,20 @@ Here's a useful react hook for situations where you have to keep track of the
state of a dialog, popup, etc.:
```js
import { useState } from 'react';
import { useState } from "react";
export default (value) => {
const [state, setState] = useState(value);
const [state, setState] = useState(value);
const setStateActive = () => {
setState(true);
};
const setStateActive = () => {
setState(true);
};
const setStateInactive = () => {
setState(false);
};
const setStateInactive = () => {
setState(false);
};
return [
state,
setStateActive,
setStateInactive,
];
return [state, setStateActive, setStateInactive];
};
```
@ -33,16 +29,16 @@ Usage:
```js
const SomeComponent = () => {
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] = useToggle(false);
return (
<>
<Button onClick={openDeleteDialog}>Open Delete Dialog</Button>
<Dialog isOpen={isDeleteDialogOpen} onClose={closeDeleteDialog}></Dialog>
</>
);
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] =
useToggle(false);
return (
<>
<Button onClick={openDeleteDialog}>Open Delete Dialog</Button>
<Dialog isOpen={isDeleteDialogOpen} onClose={closeDeleteDialog}></Dialog>
</>
);
};
```
This is post 021 of [#100DaysToOffload](https://100daystooffload.com/).

3
content/posts/2022-01-28-til-how-to-get-the-selected-language-of-a-browser.md

@ -32,6 +32,3 @@ This is post 024 of [#100DaysToOffload](https://100daystooffload.com/).
- [Stack Overflow Thread on getting the user language](https://stackoverflow.com/questions/8199760/how-to-get-the-browser-language-using-javascript)
- [Full link to the Podcast episode](https://corecursive.com/internet-is-duct-tape/#)

32
content/posts/2022-03-18-fix-traefik-proxy-issues.md

@ -34,16 +34,16 @@ In my setup, I use a `webSecure` entrypoint listening for SSL/TLS traffic, and a
```yaml
entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: "websecure"
scheme: "https"
web:
address: :80
http:
redirections:
entryPoint:
to: "websecure"
scheme: "https"
websecure:
address: :443
websecure:
address: :443
```
Apparently, some services send requests to the `web` entrypoint, and the
@ -53,13 +53,13 @@ like so:
```yaml
entryPoints:
web:
address: :80
proxyProtocol:
insecure: true
forwardedHeaders:
insecure: true
# ...
web:
address: :80
proxyProtocol:
insecure: true
forwardedHeaders:
insecure: true
# ...
# ...
```

18
content/posts/2022-03-24-swapping-numbers-without-temp.md

@ -14,24 +14,24 @@ In school, we get taught to use a temporary
variable to swap two numbers:
```js
let a = 5
let b = 10
let a = 5;
let b = 10;
let temp = a;
a = b // a = 10
b = temp // b = 5
a = b; // a = 10
b = temp; // b = 5
```
But by using some arithmetic, we can save us a few bytes of memory:
```js
let a = 5
let b = 10
let a = 5;
let b = 10;
a = a + b // a = 15 ; b = 10
b = a - b // a = 15 ; b = 5
a = a - b // a = 10 ; b = 5
a = a + b; // a = 15 ; b = 10
b = a - b; // a = 15 ; b = 5
a = a - b; // a = 10 ; b = 5
```
Please **never** use this in any production code. The less we have to think

2
content/posts/2022-04-28-hello-to-all-the-new-mastodon-users.md

@ -81,4 +81,4 @@ https://the-federation.info/birdsitelive.
Stay safe, and see you around!
This is post 028 of [#100DaysToOffload](https://100daystooffload.com/).
This is post 028 of [#100DaysToOffload](https://100daystooffload.com/).

2
content/posts/2022-04-29-weeknote-17-2022.md

@ -33,4 +33,4 @@ you](/contact).
That's it for this week. Thanks for reading!
This is post 029 of [#100DaysToOffload](https://100daystooffload.com/).
This is post 029 of [#100DaysToOffload](https://100daystooffload.com/).

2
content/posts/2022-05-06-weeknote-18-2022.md

@ -57,4 +57,4 @@ walk and talking about life.
That's it for this week. Thanks for reading!
This is post 030 of [#100DaysToOffload](https://100daystooffload.com/).
This is post 030 of [#100DaysToOffload](https://100daystooffload.com/).

2
content/posts/2022-05-24-cloning-windows-to-a-new-drive.md

@ -10,7 +10,7 @@ relatively normal to use a HDD as a primary hard drive, which adds to the slow
experience. It was time for an upgrade!
> **TL;DR**: Use [Clonezilla](https://clonezilla.org/) on a live usb stick to
create an exact copy of your old drive onto your new one.
> create an exact copy of your old drive onto your new one.
I got him a 512 GB SSD, which, conveniently, is the same size of his current
HDD. While installing the new drive alongside his existing one, I thought about

1
content/posts/2022-06-02-tar-commands.md

@ -40,5 +40,4 @@ https://simplecheatsheet.com/linux-tar-files/
Sorry for this dumb post. I'm sure you can relate to my feelings. ;)
This is post 033 of [#100DaysToOffload](https://100daystooffload.com/).

10
content/posts/2022-06-10-a-list-of-bugs-in-macos.md

@ -9,14 +9,14 @@ has some really annoying problems that don't seem to get attention by Apple.
This is a list of things that personally bother me. I will try to update it
whenever I encounter new issues.
* When plugging in a external monitor, some native Windows can't be used with a
- When plugging in a external monitor, some native Windows can't be used with a
mouse anymore. Restart required to fix.
* Dash-to-dock sometimes doesn't work for fullscreen-applications.
* When opening a fullscreen window from the workspace-overview, the dock
- Dash-to-dock sometimes doesn't work for fullscreen-applications.
- When opening a fullscreen window from the workspace-overview, the dock
sometimes stays visible. Has to be manually hovered to hide.
* When using a chromium-based browser on an external monitor in
- When using a chromium-based browser on an external monitor in
fullscreen-mode, the menu-bar sometimes doesn't appear when hovered.
* When switching from the internal to an external monitor, the workspace
- When switching from the internal to an external monitor, the workspace
alignment is not how I left it.
This is post 034 of [#100DaysToOffload](https://100daystooffload.com/).

1
content/posts/2022-06-29-the-only-true-answer-to-tabs-vs-spaces.md

@ -52,4 +52,3 @@ I hope you now know why using a single tab of indentation makes the most sense i
you're working in a team. Let me know your thoughts!
This is post 035 of [#100DaysToOffload](https://100daystooffload.com/).

1
content/posts/2022-08-31-whats-on-my-feed.md

@ -28,4 +28,3 @@ publish a list of **your** favorite feeds?
Wrote a "What's on my feed?" entry yourself? Let me know, and I'll link it here!
This is post 036 of [#100DaysToOffload](https://100daystooffload.com/).

15
content/posts/2022-09-22-kubernetes-is-a-domain-specific-database.md

@ -10,16 +10,16 @@ podcast. In it, [Thomas Rampelberg](https://saunter.org/) makes an analogy that
I think is worth sharing:
> "[...] Kubernetes is really a domain-specific database. And you need to look at it
that way. The YAML is literally writing a select statement or an insert
statement for a database. That's what the YAML is. And it's awesome that it is
already configured for how it is. And it's awesome that it's got a schema. But
the YAML is you writing an insert statement into Kubernetes. [...]"
> that way. The YAML is literally writing a select statement or an insert
> statement for a database. That's what the YAML is. And it's awesome that it is
> already configured for how it is. And it's awesome that it's got a schema. But
> the YAML is you writing an insert statement into Kubernetes. [...]"
The Kubernetes API abstracts two types of states: desired state and actual
state. Whenever you apply a manifest, you update the *desired state* of the
state. Whenever you apply a manifest, you update the _desired state_ of the
cluster, just like you do in a regular, non domain-specific database like
PostgreSQL or Redis. Kubernetes then frequently compares the desired state with
the *actual* state of the cluster. If they don't match, Kubernetes will do
the _actual_ state of the cluster. If they don't match, Kubernetes will do
whatever it does to match these two states. Usually, this data is persisted
using a key-value database like [etcd](https://etcd.io/) running in a cluster,
though one could theoretically also hook up an external MySQL or Postgres
@ -32,10 +32,9 @@ showing an oversimplified analogy of this pattern:
![Thermostat
Example](/assets/posts/2022-09-22-kubernetes-is-a-domain-specific-database/desired-state-hvac-diagram.png)
You *insert* your desired state into the system, and the system adjusts the
You _insert_ your desired state into the system, and the system adjusts the
actual state to match the desired state. In the case of thermostats the state is
a temperature. In Kubernetes, it's [resource
objects](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/)
This is post 037 of [#100DaysToOffload](https://100daystooffload.com/).

4
content/posts/2022-09-26-self-hosted-software-im-thankful-for.md

@ -10,7 +10,7 @@ However, there are a few projects that I stuck with over the years, and which I
## Miniflux
[Miniflux](https://miniflux.app/) is a very minimal, self-hostable RSS reader. It's been rock-solid since they day I started using it. The data for the application entirely lives In a Postgres database, which makes migrating the application to new infrastructure setups an absolute breeze. I've been meaning to support the author for quite some time now, but the cost of maintaining an instance yourself is basically zero, so I've yet to find the time to switch to their paid hosted instance.
[Miniflux](https://miniflux.app/) is a very minimal, self-hostable RSS reader. It's been rock-solid since they day I started using it. The data for the application entirely lives In a Postgres database, which makes migrating the application to new infrastructure setups an absolute breeze. I've been meaning to support the author for quite some time now, but the cost of maintaining an instance yourself is basically zero, so I've yet to find the time to switch to their paid hosted instance.
## Plausible Analytics
@ -46,6 +46,6 @@ I tried replacing Miniflux once, but failed. Nothing beats Miniflux.
### Prometheus + Grafana
Monitoring ***inside*** your infra works until the infra goes down, at which point you're essentially driving blindfolded. I switched to Grafana Cloud, which includes a very generous free tier.
Monitoring **_inside_** your infra works until the infra goes down, at which point you're essentially driving blindfolded. I switched to Grafana Cloud, which includes a very generous free tier.
This is post 038 of [#100DaysToOffload](https://100daystooffload.com/).

17
content/posts/2022-10-05-simple-guestbook.md

@ -20,14 +20,13 @@ commit. It boils down to this snippet of code:
```js
window.addEventListener("keypress", (e) => {
if (e.key === ".") {
const baseUrl =
"https://github.com/garritfra/garrit.xyz/edit/main/content";
const filePath = window.location.pathname;
const url = `${baseUrl}${filePath}.md`;
window.location.href = url;
}
if (e.key === ".") {
const baseUrl = "https://github.com/garritfra/garrit.xyz/edit/main/content";
const filePath = window.location.pathname;
const url = `${baseUrl}${filePath}.md`;
window.location.href = url;
}
});
```
@ -41,4 +40,4 @@ Let's have some fun and put this feature to use. I added a simple
[guestbook](/guestbook) to this site, which is open to receive pull requests.
I'd love to hear from you!
This is post 040 of [#100DaysToOffload](https://100daystooffload.com/).
This is post 040 of [#100DaysToOffload](https://100daystooffload.com/).

41
content/posts/_2022-05-02-docker-in-plain-english.md

@ -25,7 +25,7 @@ your application.
Things running in a container also can't break out of this "sandbox". A process
in a container is only aware of the resources around it, not on the host
machine. Each container is kind of like an operating system **inside** your
actual operating system.
actual operating system.
To describe what a container should look like, we need to write a "recipe" for
it. In it, you describe a starting point from which you want to build upon, and
@ -54,7 +54,7 @@ actually there, try running `docker image ls`. This will list all images on your
system:
```
➜ garrit.xyz git:(master) ✗ docker image ls
➜ garrit.xyz git:(master) ✗ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 6e2240011a89 8 minutes ago 109MB
```
@ -68,8 +68,7 @@ docker run hello-world
```
And, as instructed with the `CMD` line, you should see the words "Hello World!"
printed on the screen. You can verify that it's still there by running `docker
ps -a`, which will list all containers on your system, including the one you
printed on the screen. You can verify that it's still there by running `docker ps -a`, which will list all containers on your system, including the one you
just ran:
```
@ -120,10 +119,10 @@ Like above, you can build this Dockerfile using `docker build -t testapp .`, or
any name you'd like to use.
> **Quick Tip**: You might also want to add a `.Dockerignore` file, which lists
files and directories which should not be copied inside the container, just like
a `.gitignore` file. I usually add `node_modules` since it will be recreated
when building the image, and some files that are not relevant at runtime, like a
README.
> files and directories which should not be copied inside the container, just like
> a `.gitignore` file. I usually add `node_modules` since it will be recreated
> when building the image, and some files that are not relevant at runtime, like a
> README.
Running `docker image ls` should now show the image you just created:
@ -158,7 +157,7 @@ Example app listening at http://:::8080
You'll soon discover that you can't access port 8080 on your machine. Docker has
a powerful networking engine, and each container has its own IP. You _could_
figure out the IP of your container and access it like that. A simpler approach
though is to just bind a port of your host machine to the container. For
though is to just bind a port of your host machine to the container. For
example, let's bind our port 4000 to port 8081 of the container. This can be
done using the `-p` flag of the cli:
@ -167,28 +166,28 @@ docker run -p 4000:8081 -it testapp
```
> **Quick Tip**: To remember the order of the container- and the host-port, I
always think of the container as laying on my desk. First, I grab the cable (the
host machine) and then plug it into the container. Weird analogy, I know. But it
really helped me make sense of this!
> always think of the container as laying on my desk. First, I grab the cable (the
> host machine) and then plug it into the container. Weird analogy, I know. But it
> really helped me make sense of this!
If you now access `http://localhost:4000` on your host machine, you should see
your application!
## Docker Compose 101
* Volumes
* Networking
* Env Variables
- Volumes
- Networking
- Env Variables
## How I deploy my services
* Walkthrough of a simple deployment (miniflux?)
* Traefik
* Local volumes
* Permissions
- Walkthrough of a simple deployment (miniflux?)
- Traefik
- Local volumes
- Permissions
## Conclusion
* Image size optimizations
- Image size optimizations
This is post 030 of [#100DaysToOffload](https://100daystooffload.com/).
This is post 030 of [#100DaysToOffload](https://100daystooffload.com/).

4
content/posts/_2022-08-30-contributing-to-open-source-knowledge.md

@ -20,7 +20,7 @@ But you don't have to be on a roadtrip to contribute to OpenStreetMap! Chances a
## Wikipedia (and other wikis)
I often feel like I can't contribute much to the vast knowledge of Wikipedia. *Other people are way smarter than me* and whatnot. But while you might not be able to publish worthy edits to a well-known topic, you might know some things that others haven't thought of. Is there an entry about your local town? Is there an interesting member of your (past) family that others might want to read about?
I often feel like I can't contribute much to the vast knowledge of Wikipedia. _Other people are way smarter than me_ and whatnot. But while you might not be able to publish worthy edits to a well-known topic, you might know some things that others haven't thought of. Is there an entry about your local town? Is there an interesting member of your (past) family that others might want to read about?
Of course, there are other wikis beside Wikipedia. Are you using a little-known tool that has open source documentation in the form of a wiki? How can it be improved?
@ -31,8 +31,8 @@ You might have never heard of [observation.org](https://observation.org). It's a
The idea is simple: snap a picture of an interesting looking insect or plant, upload it using the website (or one of their apps) and create an "observation". Using this information, researchers will be able to understand the biodiversity of your area. The information is free to use, and anyone can contribute!
## Wardriving
"Wardriving" is the process of driving around by bike or car and mapping out the networks around you.
"Wardriving" is the process of driving around by bike or car and mapping out the networks around you.
## folding@home

6
content/posts/fighting-array-functions-with-es6.md

@ -68,11 +68,11 @@ You can use this for arrays, as well as objects:
```js
let obj = {
field: "example",
field: "example",
};
let extendedObj = {
...obj,
anotherField: 42,
...obj,
anotherField: 42,
};
console.log(extendedObj.field); // "example"
```

40
content/posts/introducing-slashdev-space.md

@ -22,26 +22,26 @@ Because Next.js is so minimalistic, there are some parts that you have to set up
```js
const posts = ((context) => {
const keys = context.keys();
const values = keys.map(context);
const data = keys.map((key, index) => {
// Create slug from filename
const slug = key
.replace(/^.*[\\\/]/, "")
.split(".")
.slice(0, -1)
.join(".");
const value = values[index];
// Parse yaml metadata & markdownbody in document
const document = matter(value.default);
return {
frontmatter: document.data,
markdownBody: document.content,
slug,
};
});
return data;
const keys = context.keys();
const values = keys.map(context);
const data = keys.map((key, index) => {
// Create slug from filename
const slug = key
.replace(/^.*[\\\/]/, "")
.split(".")
.slice(0, -1)
.join(".");
const value = values[index];
// Parse yaml metadata & markdownbody in document
const document = matter(value.default);
return {
frontmatter: document.data,
markdownBody: document.content,
slug,
};
});
return data;
})(require.context("../content/posts", true, /\.md$/));
```

6
content/posts/testing-isnt-hard.md

@ -24,7 +24,7 @@ Of course, we need a unit we want to test. What about a method that returns the
```js
module.exports = function firstElement(arr) {
return arr[1];
return arr[1];
};
```
@ -55,7 +55,7 @@ Jest provides global functions that do not need to be imported in a file. A simp
const firstElement = require("../firstElement.js");
test("firstElement gets first element of array", () => {
expect(firstElement([1, 2])).toBe(1);
expect(firstElement([1, 2])).toBe(1);
});
```
@ -96,7 +96,7 @@ Whoops! Looks like we have found a bug! Let's fix it by adjusting the index of t
```js
module.exports = function firstElement(arr) {
return arr[0];
return arr[0];
};
```

14
content/talks.md

@ -4,13 +4,13 @@ This is a collection of slides for talks I've given over the years.
### EN
- [2021-02-04 - Compiler Internals](/talks/2021-02-04_en_compiler_internals.pdf)
- [2021-02-04 - Compiler Internals](/talks/2021-02-04_en_compiler_internals.pdf)
### DE
- [2022-03-08 - Go Tech Talk](/talks/2022-03-08_de_golang_tech_talk.pdf)
- [2022-01-25 - Node.js Tech Talk](/talks/2022-01-25_de_nodejs_tech_talk.pdf)
- [2021-11-18 - The Art of Bonsai](/talks/2021-11-18_de_the_art_of_bonsai.pdf)
- [2021-10-07 - CSS Introduction](/talks/2021-10-07_de_css_introduction.pdf)
- [2021-05-14 - A Guide to PGP](/talks/2021-05-14_de_guide_to_pgp.pdf)
- [2018-11-19 - Single-Responsibility Principle](/talks/2018-11-19_de_single_responsibilty_principle.pdf)
- [2022-03-08 - Go Tech Talk](/talks/2022-03-08_de_golang_tech_talk.pdf)
- [2022-01-25 - Node.js Tech Talk](/talks/2022-01-25_de_nodejs_tech_talk.pdf)
- [2021-11-18 - The Art of Bonsai](/talks/2021-11-18_de_the_art_of_bonsai.pdf)
- [2021-10-07 - CSS Introduction](/talks/2021-10-07_de_css_introduction.pdf)
- [2021-05-14 - A Guide to PGP](/talks/2021-05-14_de_guide_to_pgp.pdf)
- [2018-11-19 - Single-Responsibility Principle](/talks/2018-11-19_de_single_responsibilty_principle.pdf)

4
content/todo.md

@ -1,7 +1,7 @@
# A comprehensive list of projects that I want to build but probably never will
- A fork of [birdsite.live](https://github.com/NicolasConstant/BirdsiteLive)
for instagram
for instagram
- Battle Royale App for habit tracking
- Federated sports tracker ([somewhat in the making already](https://github.com/SamR1/FitTrackee/issues/16))
- C compiler for [uxn](https://wiki.xxiivv.com/site/uxn.html) (or
@ -19,7 +19,7 @@ for instagram
- A transpiled JavaScript dialect for German syntax
- A newsletter/feed that aggregates news from outlets covering the entire political spectrum
- A Typescript library that mimics some safety features of Rust
- Pacman but you're the ghost
- Pacman but you're the ghost
- A community-forum where users can post "nuggets" of interesting information. Inspired by HackerNews, Reddit and Twitter
- A gameboy/nes emulator
- A HackerNews CLI

98
lib/rss.js

@ -6,42 +6,42 @@ const rfc822Date = require("rfc822-date");
const markdown = require("markdown").markdown;
const files = fs
.readdirSync(path.join(__dirname, "../content/posts"))
// Filter subdirectories
.filter(
(p) =>
!fs.lstatSync(path.join(__dirname, "../content/posts", p)).isDirectory()
)
.map((filename) => {
return {
filename,
content: fs
.readFileSync(path.join(__dirname, "../content/posts", filename))
.toString(),
};
});
.readdirSync(path.join(__dirname, "../content/posts"))
// Filter subdirectories
.filter(
(p) =>
!fs.lstatSync(path.join(__dirname, "../content/posts", p)).isDirectory()
)
.map((filename) => {
return {
filename,
content: fs
.readFileSync(path.join(__dirname, "../content/posts", filename))
.toString(),
};
});
const keys = Array.from(files.keys());
const posts = files.map((file) => {
// Create slug from filename
const slug = file.filename
.replace(/^.*[\\\/]/, "")
.split(".")
.slice(0, -1)
.join(".");
// Parse yaml metadata & markdownbody in document
const document = matter(file.content);
return {
frontmatter: document.data,
markdownBody: document.content,
slug,
};
// Create slug from filename
const slug = file.filename
.replace(/^.*[\\\/]/, "")
.split(".")
.slice(0, -1)
.join(".");
// Parse yaml metadata & markdownbody in document
const document = matter(file.content);
return {
frontmatter: document.data,
markdownBody: document.content,
slug,
};
});
const getRssXml = (blogPosts) => {
const { rssItemsXml, latestPostDate } = blogPostsRssXml(blogPosts);
return `<?xml version="1.0" ?>
const { rssItemsXml, latestPostDate } = blogPostsRssXml(blogPosts);
return `<?xml version="1.0" ?>
<rss version="2.0">
<channel>
<title>garrit.xyz</title>
@ -49,27 +49,27 @@ const getRssXml = (blogPosts) => {
<description>Garrit Franke</description>
<language>en</language>
<lastBuildDate>${rfc822Date(
new Date(latestPostDate)
)}</lastBuildDate>
new Date(latestPostDate)
)}</lastBuildDate>
${rssItemsXml}
</channel>
</rss>`;
};
const blogPostsRssXml = (blogPosts) => {
let latestPostDate = "";
let rssItemsXml = "";
blogPosts
.filter((post) => !post.slug.startsWith("_"))
// Ternary operator is used to fix chromium sorting
// See: https://stackoverflow.com/a/36507611
.sort((a, b) => (a.frontmatter.date < b.frontmatter.date ? 1 : -1))
.forEach((post) => {
const postDate = Date.parse(post.frontmatter.date);
if (!latestPostDate || postDate > Date.parse(latestPostDate)) {
latestPostDate = post.frontmatter.date;
}
rssItemsXml += `
let latestPostDate = "";
let rssItemsXml = "";
blogPosts
.filter((post) => !post.slug.startsWith("_"))
// Ternary operator is used to fix chromium sorting
// See: https://stackoverflow.com/a/36507611
.sort((a, b) => (a.frontmatter.date < b.frontmatter.date ? 1 : -1))
.forEach((post) => {
const postDate = Date.parse(post.frontmatter.date);
if (!latestPostDate || postDate > Date.parse(latestPostDate)) {
latestPostDate = post.frontmatter.date;
}
rssItemsXml += `
<item>
<title>${post.frontmatter.title}</title>
<link>
@ -81,11 +81,11 @@ const blogPostsRssXml = (blogPosts) => {
<![CDATA[${markdown.toHTML(post.markdownBody)}]]>
</description>
</item>`;
});
return {
rssItemsXml,
latestPostDate,
};
});
return {
rssItemsXml,
latestPostDate,
};
};
const feedPath = path.join(__dirname, "../public/rss.xml");

20
lib/tags.js

@ -1,13 +1,13 @@
export const parseTags = (strings) => {
const postTags = keys
.map((key, index) => {
const value = values[index];
// Parse yaml metadata & markdownbody in document
const postTags = keys
.map((key, index) => {
const value = values[index];
// Parse yaml metadata & markdownbody in document
const document = matter(value.default);
const rawTags = document.data?.tags || "";
return rawTags.split(",").map((tag) => tag.trim());
})
.flat()
.filter((value) => value !== "");
const document = matter(value.default);
const rawTags = document.data?.tags || "";
return rawTags.split(",").map((tag) => tag.trim());
})
.flat()
.filter((value) => value !== "");
};

4
next-sitemap.config.js

@ -1,3 +1,3 @@
module.exports = {
siteUrl: process.env.SITE_URL || 'https://garrit.xyz',
}
siteUrl: process.env.SITE_URL || "https://garrit.xyz",
};

44
next.config.js

@ -1,28 +1,28 @@
const path = require("path");
const isProd = process.env.NODE_ENV === 'production'
const isProd = process.env.NODE_ENV === "production";
module.exports = {
experimental: {
outputStandalone: true,
},
assetPrefix: isProd ? 'https://garrit.xyz' : '',
sassOptions: {
includePaths: [path.join(__dirname, 'styles')],
},
webpack: function (config, { dev, isServer }) {
config.module.rules.push({
test: /\.md$/,
use: "raw-loader",
});
experimental: {
outputStandalone: true,
},
assetPrefix: isProd ? "https://garrit.xyz" : "",
sassOptions: {
includePaths: [path.join(__dirname, "styles")],
},
webpack: function (config, { dev, isServer }) {
config.module.rules.push({
test: /\.md$/,
use: "raw-loader",
});
if (!dev && !isServer) {
Object.assign(config.resolve.alias, {
react: 'preact/compat',
'react-dom/test-utils': 'preact/test-utils',
'react-dom': 'preact/compat',
})
}
return config;
},
if (!dev && !isServer) {
Object.assign(config.resolve.alias, {
react: "preact/compat",
"react-dom/test-utils": "preact/test-utils",
"react-dom": "preact/compat",
});
}
return config;
},
};

60
package.json

@ -1,32 +1,32 @@
{
"name": "garrit.xyz",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "npm run build && next dev",
"start:prod": "npm run build && next start",
"build": "next build && npm run build:rss && npm run build:sitemap && next build",
"build:static": "npm run build && next export",
"build:rss": "node lib/rss.js",
"build:sitemap": "next-sitemap"
},
"dependencies": {
"markdown": "0.5.0",
"next": "12.1.0",
"next-plausible": "3.6.3",
"preact": "10.11.2",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-markdown": "8.0.3",
"rehype-raw": "6.1.1",
"remark-gfm": "3.0.1"
},
"devDependencies": {
"glob": "8.0.3",
"gray-matter": "4.0.3",
"next-sitemap": "3.1.25",
"raw-loader": "4.0.2",
"rfc822-date": "0.0.3",
"sass": "1.55.0"
}
"name": "garrit.xyz",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "npm run build && next dev",
"start:prod": "npm run build && next start",
"build": "next build && npm run build:rss && npm run build:sitemap && next build",
"build:static": "npm run build && next export",
"build:rss": "node lib/rss.js",
"build:sitemap": "next-sitemap"
},
"dependencies": {
"markdown": "0.5.0",
"next": "12.1.0",
"next-plausible": "3.6.3",
"preact": "10.11.2",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-markdown": "8.0.3",
"rehype-raw": "6.1.1",
"remark-gfm": "3.0.1"
},
"devDependencies": {
"glob": "8.0.3",
"gray-matter": "4.0.3",
"next-sitemap": "3.1.25",
"raw-loader": "4.0.2",
"rfc822-date": "0.0.3",
"sass": "1.55.0"
}
}

2
pages/404.js

@ -1,5 +1,5 @@
import Link from "next/link";
import Page from "../components/Page"
import Page from "../components/Page";
const SSR = typeof window === "undefined";

78
pages/[page].js

@ -6,52 +6,52 @@ import glob from "glob";
import Page from "../components/Page";
export default function PageTemplate(props) {
/*
** Odd fix to get build to run
** It seems like on first go the props
** are undefined could be a Next bug?
*/
if (!props.frontmatter) return <></>;
const siteTitle = props.frontmatter.siteTitle || props.siteTitle;
return (
<Page title={props.frontmatter.title} siteTitle={siteTitle}>
<ReactMarkdown remarkPlugins={[gfm]} rehypePlugins={[rehypeRaw]}>
{props.markdownBody}
</ReactMarkdown>
</Page>
);
/*
** Odd fix to get build to run
** It seems like on first go the props
** are undefined could be a Next bug?
*/
if (!props.frontmatter) return <></>;
const siteTitle = props.frontmatter.siteTitle || props.siteTitle;
return (
<Page title={props.frontmatter.title} siteTitle={siteTitle}>
<ReactMarkdown remarkPlugins={[gfm]} rehypePlugins={[rehypeRaw]}>
{props.markdownBody}
</ReactMarkdown>
</Page>
);
}
export async function getStaticProps({ ...ctx }) {
const { page } = ctx.params;
const content = await import(`../content/${page}.md`);
const data = matter(content.default);
return {
props: {
siteTitle: "Garrit's Site",
frontmatter: data.data,
markdownBody: data.content,
},
};
const { page } = ctx.params;
const content = await import(`../content/${page}.md`);
const data = matter(content.default);
return {
props: {
siteTitle: "Garrit's Site",
frontmatter: data.data,
markdownBody: data.content,
},
};
}
export async function getStaticPaths() {
//get all .md files in the posts dir
const pages = glob.sync("content/*.md");
//get all .md files in the posts dir
const pages = glob.sync("content/*.md");
//remove path and extension to leave filename only
const pageSlugs = pages.map((file) =>
file.split("/")[1].replace(/ /g, "-").slice(0, -3).trim()
);
//remove path and extension to leave filename only
const pageSlugs = pages.map((file) =>
file.split("/")[1].replace(/ /g, "-").slice(0, -3).trim()
);
// create paths with `slug` param
const paths = pageSlugs.map((slug) => `/${slug}`);
// create paths with `slug` param
const paths = pageSlugs.map((slug) => `/${slug}`);
return {
paths,
fallback: false,
};
return {
paths,
fallback: false,
};
}

20
pages/_app.js

@ -2,16 +2,16 @@ import PlausibleProvider from "next-plausible";
import "../styles/index.scss";
function MyApp({ Component, pageProps }) {
return (
<PlausibleProvider
domain="garrit.xyz"
customDomain="https://analytics.slashdev.space"
selfHosted
trackOutboundLinks
>
<Component {...pageProps} />
</PlausibleProvider>
);
return (
<PlausibleProvider
domain="garrit.xyz"
customDomain="https://analytics.slashdev.space"
selfHosted
trackOutboundLinks
>
<Component {...pageProps} />
</PlausibleProvider>
);
}
export default MyApp;

28
pages/index.js

@ -1,27 +1,25 @@
import ReactMarkdown from "react-markdown";
import matter from "gray-matter";
import gfm from 'remark-gfm'
import gfm from "remark-gfm";
import Page from "../components/Page";
const Index = (props) => {
return (
<Page className="h-card" siteTitle="Garrit Franke">
<ReactMarkdown remarkPlugins={[gfm]}>
{props.markdownBody}
</ReactMarkdown>
</Page>
);
return (
<Page className="h-card" siteTitle="Garrit Franke">
<ReactMarkdown remarkPlugins={[gfm]}>{props.markdownBody}</ReactMarkdown>
</Page>
);
};
export async function getStaticProps() {
const content = await import(`../content/index.md`);
const data = matter(content.default);
const content = await import(`../content/index.md`);
const data = matter(content.default);
return {
props: {
markdownBody: data.content,
},
};
return {
props: {
markdownBody: data.content,
},
};
}
export default Index;

4
pages/posts/[post].js

@ -33,9 +33,7 @@ export default function BlogTemplate(props) {
};
const renderTagList = () => {
const tags = props.frontmatter.tags
?.split(",")
.map((tag) => tag.trim());
const tags = props.frontmatter.tags?.split(",").map((tag) => tag.trim());
return (
<p className="page__tag-list">

82
pages/posts/index.js

@ -4,54 +4,54 @@ import Page from "../../components/Page";
import matter from "gray-matter";
const Index = (props) => {
const { query } = useRouter();
const { query } = useRouter();
const filteredPosts = query.tags
? props.posts.filter((post) =>
post.frontmatter.tags
?.split(",")
.map((tag) => tag.trim().toLowerCase())
.includes(query.tags.trim().toLowerCase())
)
: props.posts;
const filteredPosts = query.tags
? props.posts.filter((post) =>
post.frontmatter.tags
?.split(",")
.map((tag) => tag.trim().toLowerCase())
.includes(query.tags.trim().toLowerCase())
)
: props.posts;
return (
<Page siteTitle="Garrit's Notes">
<BlogList posts={filteredPosts} />
</Page>
);
return (
<Page siteTitle="Garrit's Notes">
<BlogList posts={filteredPosts} />
</Page>
);
};
export async function getStaticProps() {
//get posts & context from folder
const posts = ((context) => {
const keys = context.keys();
const values = keys.map(context);
//get posts & context from folder
const posts = ((context) => {
const keys = context.keys();
const values = keys.map(context);
const data = keys.map((key, index) => {
// Create slug from filename
const slug = key
.replace(/^.*[\\\/]/, "")
.split(".")
.slice(0, -1)
.join(".");
const value = values[index];
// Parse yaml metadata & markdownbody in document
const document = matter(value.default);
return {
frontmatter: document.data,
slug,
};
});
return data;
})(require.context("../../content/posts", true, /\.md$/));
const data = keys.map((key, index) => {
// Create slug from filename
const slug = key
.replace(/^.*[\\\/]/, "")
.split(".")
.slice(0, -1)
.join(".");
const value = values[index];
// Parse yaml metadata & markdownbody in document
const document = matter(value.default);
return {
frontmatter: document.data,
slug,
};
});
return data;
})(require.context("../../content/posts", true, /\.md$/));
return {
props: {
posts,
description: "",
},
};
return {
props: {
posts,
description: "",
},
};
}
export default Index;

104
pages/tags.js

@ -3,61 +3,61 @@ import Page from "../components/Page";
import matter from "gray-matter";
const Index = (props) => {
return (
<Page siteTitle="Garrit's Notes">
<h1>Filter posts by tag</h1>
<div className="tag-list">
{props.tags.map(({ tag, count }) => (
<div className="tag-list__tag" key={tag}>
<a href={`/posts?tags=${tag}`}>
{tag} ({count})
</a>
</div>
))}
</div>
</Page>
);
return (
<Page siteTitle="Garrit's Notes">
<h1>Filter posts by tag</h1>
<div className="tag-list">
{props.tags.map(({ tag, count }) => (
<div className="tag-list__tag" key={tag}>
<a href={`/posts?tags=${tag}`}>
{tag} ({count})
</a>
</div>
))}
</div>
</Page>
);
};
export async function getStaticProps() {
//get posts & context from folder
const tags = ((context) => {
const keys = context.keys();
const values = keys.map(context);
const postTags = keys
.map((key, index) => {
const value = values[index];
// Parse yaml metadata & markdownbody in document
const document = matter(value.default);
const rawTags = document.data?.tags || "";
return rawTags.split(",").map((tag) => tag.trim());
})
.flat()
.filter((value) => value !== "");
const tagMap = {};
for (const tag of postTags) {
tagMap[tag] ? tagMap[tag]++ : (tagMap[tag] = 1);
}
return postTags
.flat()
.filter((value, index, array) => array.indexOf(value) === index) // Deduplicate
.filter((value) => value !== "")
.sort()
.map((tag) => ({ tag, count: tagMap[tag] }))
.sort((a, b) => b.count - a.count);
})(require.context("../content/posts", true, /\.md$/));
return {
props: {
tags,
description: "",
},
};
//get posts & context from folder
const tags = ((context) => {
const keys = context.keys();
const values = keys.map(context);
const postTags = keys
.map((key, index) => {
const value = values[index];
// Parse yaml metadata & markdownbody in document
const document = matter(value.default);
const rawTags = document.data?.tags || "";
return rawTags.split(",").map((tag) => tag.trim());
})
.flat()
.filter((value) => value !== "");
const tagMap = {};
for (const tag of postTags) {
tagMap[tag] ? tagMap[tag]++ : (tagMap[tag] = 1);
}
return postTags
.flat()
.filter((value, index, array) => array.indexOf(value) === index) // Deduplicate
.filter((value) => value !== "")
.sort()
.map((tag) => ({ tag, count: tagMap[tag] }))
.sort((a, b) => b.count - a.count);
})(require.context("../content/posts", true, /\.md$/));
return {
props: {
tags,
description: "",
},
};
}
export default Index;

6
renovate.json

@ -1,6 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"]
}

12
styles/components/blog-list.scss

@ -1,8 +1,8 @@
.blog__list {
&__post {
margin-bottom: 1.5em;
&__date {
margin-bottom: 0;
}
}
&__post {
margin-bottom: 1.5em;
&__date {
margin-bottom: 0;
}
}
}

28
styles/components/footer.scss

@ -1,19 +1,19 @@
.footer {
border-top: 1px solid var(--border);
padding-bottom: 0px;
text-align: center;
border-top: 1px solid var(--border);
padding-bottom: 0px;
text-align: center;
&__content {
text-align: left;
margin-bottom: 3rem;
}
&__content {
text-align: left;
margin-bottom: 3rem;
}
section {
min-width: 250px;
border-bottom: none;
}
section {
min-width: 250px;
border-bottom: none;
}
a {
text-decoration: underline;
}
a {
text-decoration: underline;
}
}

136
styles/components/header.scss

@ -1,84 +1,84 @@
nav {
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
& > a {
opacity: 1 !important;
}
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
& > a {
opacity: 1 !important;
}
}
.header {
&__links {
list-style-type: none;
display: flex;
flex-direction: row;
margin: 0;
padding: 0;
text-align: center;
align-items: center;
li {
padding: 24px;
}
}
&__links {
list-style-type: none;
display: flex;
flex-direction: row;
margin: 0;
padding: 0;
text-align: center;
align-items: center;
li {
padding: 24px;
}
}
&__container {
padding: 24px 0px;
padding-bottom: 0px;
&__container {
padding: 24px 0px;
padding-bottom: 0px;
&__logo {
margin: 0;
color: var(--text);
font-weight: 400;
font-size: 24px;
}
}
&__logo {
margin: 0;
color: var(--text);
font-weight: 400;
font-size: 24px;
}
}
}
.underlined {
position: relative;
display: block;
opacity: 1 !important;
&::after {
content: "";
height: 2px;
transform: scaleX(0);
transition: transform 0.25s ease;
transform-origin: left;
left: 0;
bottom: -4px;
width: 100%;
position: absolute;
background: white;
border-radius: 1px;
}
position: relative;
display: block;
opacity: 1 !important;
&::after {
content: "";
height: 2px;
transform: scaleX(0);
transition: transform 0.25s ease;
transform-origin: left;
left: 0;
bottom: -4px;
width: 100%;
position: absolute;
background: white;
border-radius: 1px;
}
&:hover {
&::after {
transform: scaleX(1);
}
}
&:hover {
&::after {
transform: scaleX(1);
}
}
}
@media (min-width: 768px) {
.header {
left: 0;
top: 0;
.header {
left: 0;
top: 0;
&__container {
padding-bottom: 24px;
}
&__container {
padding-bottom: 24px;
}
&__logo {
border-bottom: none;
}
}
.nav {
height: 100%;
align-items: flex-start;
display: flex;
justify-content: space-between;
flex-direction: row;
align-items: center;
}
&__logo {
border-bottom: none;
}
}
.nav {
height: 100%;
align-items: flex-start;
display: flex;
justify-content: space-between;
flex-direction: row;
align-items: center;
}
}

7
styles/components/layout.scss

@ -1,6 +1,5 @@
section.layout {
overflow-x: hidden;
margin-top: 0;
margin-bottom: 0;
overflow-x: hidden;
margin-top: 0;
margin-bottom: 0;
}

180
styles/components/page.scss

@ -1,137 +1,137 @@
.page {
margin-bottom: 3rem;
margin-bottom: 3rem;
}
.page__hero {
min-height: 300px;
height: 60vh;
width: 100%;
margin: 0;
overflow: hidden;
min-height: 300px;
height: 60vh;
width: 100%;
margin: 0;
overflow: hidden;
}
.page__hero img {
margin-bottom: 0;
object-fit: cover;
min-height: 100%;
min-width: 100%;
object-position: center;
margin-bottom: 0;
object-fit: cover;
min-height: 100%;
min-width: 100%;
object-position: center;
}
.page__info {
width: 100%;
max-width: 768px;
margin: 0 auto;
padding: 2rem 0;
width: 100%;
max-width: 768px;
margin: 0 auto;
padding: 2rem 0;
h1 {
max-width: 500px;
margin: 0;
margin-bottom: 0.66rem;
}
h1 {
max-width: 500px;
margin: 0;
margin-bottom: 0.66rem;
}
h3 {
margin-bottom: 0;
}
h3 {
margin-bottom: 0;
}
}
.page__body a {
text-decoration: underline;
text-decoration-color: $color-tertiary-dark;
color: $color-text-dark;
@media (prefers-color-scheme: light) {
color: $color-text-light;
}
text-decoration: underline;
text-decoration-color: $color-tertiary-dark;
color: $color-text-dark;
@media (prefers-color-scheme: light) {
color: $color-text-light;
}
}
.page__body a,
.page__footer a {
text-decoration: underline;
text-decoration-color: $color-tertiary-dark;
text-decoration: underline;
text-decoration-color: $color-tertiary-dark;
}
.page__body:last-child {
margin-bottom: 0;
margin-bottom: 0;
}
.page__body h1 h2 h3 h4 h5 h6 p {
font-weight: normal;
font-weight: normal;
}
.page__body ul ol {
margin-left: 1.25rem;
margin-bottom: 1.25rem;
padding-left: 1.45rem;
margin-left: 1.25rem;
margin-bottom: 1.25rem;
padding-left: 1.45rem;
}
.page__footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 1.25rem;
width: 100%;
max-width: 800px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 1.25rem;
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.page__footer h2 {
margin-bottom: 0;
margin-bottom: 0;
}
.page__footer a {
display: flex;
justify-content: space-between;
align-items: center;
display: flex;
justify-content: space-between;
align-items: center;
}
.page__footer a svg {
width: 20px;
width: 20px;
}
.page__tag-list {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
}
.page__tag-icon {
vertical-align: middle;
margin-right: 4px;
vertical-align: middle;
margin-right: 4px;
}
@media (prefers-color-scheme: light) {
.page__tag-icon {
filter: invert(1);
}
.page__tag-icon {
filter: invert(1);
}
}
@media (max-width: 768px) {
.page__footer {
display: none;
}
.page__footer {
display: none;
}
}
@media (min-width: 768px) {
.page__body span {
width: 100%;
margin: 1.5rem auto;
}
.page__body ul ol {
margin-left: 1.5rem;
margin-bottom: 1.5rem;
}
.page__hero {
min-height: 600px;
height: 75vh;
}
.page__info {
text-align: center;
h1 {
margin: 0 auto 0.66rem auto;
}
}
.page__footer {
padding: 2.25rem;
}
.page__body span {
width: 100%;
margin: 1.5rem auto;
}
.page__body ul ol {
margin-left: 1.5rem;
margin-bottom: 1.5rem;
}
.page__hero {
min-height: 600px;
height: 75vh;
}
.page__info {
text-align: center;
h1 {
margin: 0 auto 0.66rem auto;
}
}
.page__footer {
padding: 2.25rem;
}
}
@media (min-width: 1440px) {
.page__hero {
height: 70vh;
}
.page__info {
padding: 3rem 0;
}
.page__footer {
padding: 2rem 2rem 3rem 2rem;
}
.page__hero {
height: 70vh;
}
.page__info {
padding: 3rem 0;
}
.page__footer {
padding: 2rem 2rem 3rem 2rem;
}
}

10
styles/components/tag-list.scss

@ -1,6 +1,6 @@
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}

182
styles/foundation/base.scss

@ -1,99 +1,99 @@
:root {
background: $color-secondary-dark;
color: $color-text-dark;
@media (prefers-color-scheme: light) {
background: $color-secondary-light;
color: $color-text-light;
}
background: $color-secondary-dark;
color: $color-text-dark;
@media (prefers-color-scheme: light) {
background: $color-secondary-light;
color: $color-text-light;
}
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir,
"Nimbus Sans L", Roboto, "Noto Sans", "Segoe UI", Arial, Helvetica,
"Helvetica Neue", sans-serif;
overflow-x: hidden;
font-size: 1.15rem;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir,
"Nimbus Sans L", Roboto, "Noto Sans", "Segoe UI", Arial, Helvetica,
"Helvetica Neue", sans-serif;
overflow-x: hidden;
font-size: 1.15rem;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@media (prefers-reduced-motion) {
* {
transition: none !important;
}
* {
transition: none !important;
}
}
a {
text-decoration: none;
color: inherit;
transition: opacity 0.2s ease;
text-decoration: none;
color: inherit;
transition: opacity 0.2s ease;
}
a:hover {
transition: opacity 0.2s ease;
opacity: 0.5;
text-decoration-color: inherit;
transition: opacity 0.2s ease;
opacity: 0.5;
text-decoration-color: inherit;
}
ul {
margin: 0;
list-style-position: outside;
list-style-image: none;
margin: 0;
list-style-position: outside;
list-style-image: none;
}
ol {
margin: 0;
padding-bottom: 0;
padding-right: 0;
padding-top: 0;
list-style-position: outside;
list-style-image: none;
margin: 0;
padding-bottom: 0;
padding-right: 0;
padding-top: 0;
list-style-position: outside;
list-style-image: none;
}
img {
max-width: 100%;
max-width: 100%;
}
pre,
code {
background: $color-accent-dark;
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 1.45rem;
line-height: 1.42;
border-radius: 3px;
overflow: auto;
word-wrap: normal;
@media (prefers-color-scheme: light) {
background: $color-accent-light;
}
background: $color-accent-dark;
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 1.45rem;
line-height: 1.42;
border-radius: 3px;
overflow: auto;
word-wrap: normal;
@media (prefers-color-scheme: light) {
background: $color-accent-light;
}
}
pre {
padding: 1.45rem;
padding: 1.45rem;
}
p code {
padding: .2em .35em;
white-space: pre;
padding: 0.2em 0.35em;
white-space: pre;
}
li {
> ol {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
> ol {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
> ul {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
> ul {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
}
strong {
font-weight: bold;
font-weight: bold;
}
// TODO: The following selectors haven't been migrated yet
@ -102,60 +102,60 @@ img,
figure,
table,
fieldset {
margin: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
margin: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
table {
line-height: 1.45rem;
border-collapse: collapse;
width: 100%;
line-height: 1.45rem;
border-collapse: collapse;
width: 100%;
}
strong {
font-weight: bold;
font-weight: bold;
}
ol,
ul {
margin-bottom: 1em;
margin-bottom: 1em;
li {
padding-left: 0;
}
li {
padding-left: 0;
}
}
li *:last-child {
margin-bottom: 0;
margin-bottom: 0;
}
li > p {
margin-bottom: calc(1.45rem / 2);
margin-bottom: calc(1.45rem / 2);
}
@media (prefers-color-scheme: dark) {
html {
scrollbar-color: #dbd7db #161618 !important;
}
html {
scrollbar-color: #dbd7db #161618 !important;
}
article a[href^="http"]::after,
article a[href^="https://"]::after {
filter: invert(100%);
}
article a[href^="http"]::after,
article a[href^="https://"]::after
{
filter: invert(100%);
}
}
article p a[href^="http"]::after,
article p a[href^="https://"]::after
{
content: "";
width: 11px;
height: 11px;
margin-left: 4px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E");
background-position: center;
background-repeat: no-repeat;
background-size: contain;
display: inline-block;
content: "";
width: 11px;
height: 11px;
margin-left: 4px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E");
background-position: center;
background-repeat: no-repeat;
background-size: contain;
display: inline-block;
}

2
styles/foundation/utility.scss

@ -1,3 +1,3 @@
.flex {
display: flex;
display: flex;
}

1115
styles/index.scss

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save