Compare commits
82 Commits
537c1b3d3b
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
|
3f5f647c00 | ||
|
43e110ee5b | ||
|
dff6b8921a | ||
|
8c3d1bdd5b | ||
|
1536ce8ed1 | ||
|
af833d173c | ||
|
3272b67283 | ||
|
4275a6adc4 | ||
|
1ff146f4aa | ||
|
54eb3fc904 | ||
|
842a7ef80a | ||
|
a2d6ebac07 | ||
|
c2fe588fe8 | ||
|
3bf6014537 | ||
|
8d898eb69a | ||
|
2475d2d67e | ||
|
9367f85f5f | ||
|
d839024d95 | ||
|
f9ee17986d | ||
|
953cf64989 | ||
|
c59415d6b3 | ||
|
f8d313309a | ||
|
190e0b2835 | ||
|
8b4902e3e2 | ||
|
824f145e2c | ||
|
7ea7031521 | ||
|
391d164ae9 | ||
|
1aeb6d20b6 | ||
|
0ee904d34f | ||
|
a24bf002ab | ||
|
9da895cd8a | ||
|
dc75012739 | ||
|
adf37e736e | ||
|
f76666c70d | ||
|
a49699a5d6 | ||
|
5d87003de0 | ||
|
37a27723f9 | ||
|
7202c2c37e | ||
|
ac454d75dc | ||
|
2902f5735b | ||
|
54de6a38c8 | ||
|
e89222a64e | ||
|
caac52cc74 | ||
|
f7af260779 | ||
|
eb1136bf90 | ||
|
379e9ce5ff | ||
|
ba0f6170af | ||
|
5880c0d9da | ||
|
8f0cec9739 | ||
|
a56eeeb528 | ||
|
b913e9b4a3 | ||
|
24ad44a9ec | ||
|
f4806ab344 | ||
|
fad71f3265 | ||
|
6a405662e9 | ||
|
c9bd65f2b9 | ||
|
d90d71b9c0 | ||
|
4b1cccc156 | ||
|
c12f2173db | ||
|
29a621a4c3 | ||
|
fdc27aad3a | ||
|
bb5e1f688b | ||
|
e2883bc1e8 | ||
|
413b06bb8a | ||
|
398cde5481 | ||
|
5e3b2d2dce | ||
|
51bd556992 | ||
|
b9926ba634 | ||
|
c7a2b2f4d2 | ||
|
604f3aaa4c | ||
|
a5a09666f5 | ||
|
21f72ae736 | ||
|
37c803f5c7 | ||
|
2cd866adfa | ||
|
19fe9f54d2 | ||
|
6738c889ad | ||
|
d23ef521dc | ||
|
95b7821199 | ||
|
bc76028dc0 | ||
|
a6d1d7aecd | ||
|
9b33229b72 | ||
|
3f549f7576 |
3
.github/workflows/publish.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: "0.121.1"
|
||||
hugo-version: "0.120.3"
|
||||
extended: true
|
||||
|
||||
- run: npm ci
|
||||
@@ -41,3 +41,4 @@ jobs:
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_branch: generated
|
||||
|
6
.gitignore
vendored
@@ -1,4 +1,10 @@
|
||||
### Hugo ###
|
||||
/public/
|
||||
/resources/_gen/
|
||||
/assets/jsconfig.json
|
||||
hugo_stats.json
|
||||
.hugo_build.lock
|
||||
|
||||
.vscode
|
||||
|
||||
### Node ###
|
||||
|
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Dave Gallant
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
31
Makefile
@@ -1,31 +0,0 @@
|
||||
SHELL := bash
|
||||
.SHELLFLAGS := -eu -o pipefail -c
|
||||
.DELETE_ON_ERROR:
|
||||
MAKEFLAGS += --warn-undefined-variables
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
ifeq ($(origin .RECIPEPREFIX), undefined)
|
||||
$(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later)
|
||||
endif
|
||||
.RECIPEPREFIX = >
|
||||
|
||||
build: clean
|
||||
> npm ci
|
||||
> hugo --minify
|
||||
|
||||
clean:
|
||||
> rm -rf public/
|
||||
|
||||
## server: run server locally on port 1313 and open in a browser
|
||||
server:
|
||||
> hugo server --buildDrafts
|
||||
|
||||
|
||||
## help: Print this help message
|
||||
help:
|
||||
> @echo
|
||||
> @echo "Usage:"
|
||||
> @echo
|
||||
> @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' | sort
|
||||
> @echo
|
||||
.PHONY: help
|
@@ -4,6 +4,7 @@ date: "{{ .Date }}"
|
||||
draft: true
|
||||
comments: true
|
||||
toc: false
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": [
|
||||
"../node_modules/prismjs/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
31
config.yaml
@@ -1,14 +1,18 @@
|
||||
baseURL: /
|
||||
googleAnalytics: G-V8WJDERTX9
|
||||
copyright: Dave Gallant
|
||||
title: davegallant.ca
|
||||
enableGitInfo: true
|
||||
enableRobotsTXT: true
|
||||
noJSConfigInAssets: true
|
||||
paginate: 20
|
||||
|
||||
build:
|
||||
noJSConfigInAssets: true
|
||||
writeStats: true
|
||||
|
||||
params:
|
||||
author: Dave Gallant
|
||||
subtitle: Software Engineer
|
||||
favicon: https://davegallant.ca/favicon.ico
|
||||
subtitle:
|
||||
description: "Dave Gallant is a software developer and a computer enthusiast."
|
||||
|
||||
logo:
|
||||
text: davegallant.ca
|
||||
@@ -22,7 +26,7 @@ params:
|
||||
issueTerm: "pathname"
|
||||
github:
|
||||
username: davegallant
|
||||
repository: davegallant.github.io
|
||||
repository: site
|
||||
|
||||
prism:
|
||||
languages:
|
||||
@@ -47,9 +51,12 @@ params:
|
||||
|
||||
menu:
|
||||
main:
|
||||
- name: RSS
|
||||
url: /index.xml
|
||||
- name: Home
|
||||
url: /
|
||||
weight: 1
|
||||
- name: Blog
|
||||
url: /blog
|
||||
weight: 2
|
||||
|
||||
permalinks:
|
||||
post: "/blog/:year/:month/:day/:slug/"
|
||||
@@ -61,15 +68,7 @@ markup:
|
||||
|
||||
module:
|
||||
imports:
|
||||
- path: github.com/davegallant/hugo-theme-gruvbox
|
||||
- path: github.com/schnerring/hugo-mod-json-resume
|
||||
mounts:
|
||||
- source: data
|
||||
target: data
|
||||
- source: layouts
|
||||
target: layouts
|
||||
- source: assets/css/json-resume.css
|
||||
target: assets/css/critical/44-json-resume.css
|
||||
- path: custom-theme
|
||||
mounts:
|
||||
- source: node_modules/simple-icons/icons
|
||||
target: assets/simple-icons
|
||||
|
25
content/_index.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Hello
|
||||
|
||||
👋 I'm a software tinkerer with a passion for infra, security, and self-hosting.
|
||||
|
||||
This is a space where I document my learnings and share them with others. I hope you find something useful here. Continuous improvement is what motivates me to keep learning.
|
||||
|
||||
I choose to host this site, alongside other tools, rather than relying exclusively on larger platforms because I believe in the open web. Interoperability is often not a consideration for popular platforms today and I find that concerning.
|
||||
|
||||
## Connect
|
||||
|
||||
If you would like to connect with me:
|
||||
|
||||
- [Email](mailto:davegallant@proton.me)
|
||||
- [LinkedIn](https://www.linkedin.com/in/dave-gallant)
|
||||
- [Cal.com](https://cal.com/davegallant)
|
||||
- [Mastodon](https://mastodon.social/@davegallant)
|
||||
- [GitHub](https://github.com/davegallant)
|
||||
- [RSS Feed](https://davegallant.ca/index.xml)
|
||||
- [gitea.snake-cloud.ts.net](https://gitea.snake-cloud.ts.net/explore/repos)
|
||||
|
||||
## Credits
|
||||
|
||||
- The site is generated with [hugo](https://gohugo.io/)
|
||||
- The theme is a modified version of [hugo-theme-gruvbox](https://github.com/schnerring/hugo-theme-gruvbox)
|
||||
- The comments system is powered by [utterances](https://github.com/utterance/utterances)
|
5
content/blog/_index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Blog
|
||||
---
|
||||
|
||||
Subscribe via [RSS](https://davegallant.ca/index.xml).
|
95
content/blog/amazon-ebs-csi-driver-terraform/index.md
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "Amazon EBS CSI driver with terraform"
|
||||
date: "2024-04-07T15:20:23-04:00"
|
||||
draft: false
|
||||
comments: true
|
||||
toc: false
|
||||
author: "Dave Gallant"
|
||||
tags: ['aws', 'eks', 'ebs', 'aws-ebs-csi-driver', 'oidc', 'efs', 'aws-efs-csi-driver']
|
||||
---
|
||||
|
||||
I recently configured the Amazon EBS CSI driver and found the setup with terraform to be more effort than expected. I wanted to avoid third-party modules and keep it as simple as possible, while remaining least privilege.
|
||||
|
||||
> UPDATE: This approach can also be used for the aws-efs-csi-driver
|
||||
|
||||
<!--more-->
|
||||
|
||||
The [Amazon EBS CSI driver docs](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html) mention that the following are needed:
|
||||
|
||||
- an existing EKS cluster
|
||||
- IAM role (that allows communication to the EC2 API)
|
||||
- EKS add-on (aws-ebs-csi-driver)
|
||||
- OIDC provider
|
||||
|
||||
This sounded simple enough but I was unable to find a "grab-and-go" terraform example that followed the recommendations in the docs. I saw some suggestions about attaching an `AmazonEBSCSIDriverPolicy` policy to the node groups but did not think this was the best idea since this would allow many pods to potentially have access to the EC2 API.
|
||||
|
||||
After a few minutes of LLM prompting, I was unimpressed with the results. I began to piece together the config myself, and after some trial and error, this is the terraform that I came up with:
|
||||
|
||||
```hcl
|
||||
|
||||
# TLS needed for the thumbprint
|
||||
provider "tls" {}
|
||||
|
||||
data "tls_certificate" "oidc" {
|
||||
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
|
||||
}
|
||||
|
||||
# EKS addon
|
||||
resource "aws_eks_addon" "ebs_csi_driver" {
|
||||
cluster_name = aws_eks_cluster.main.name
|
||||
addon_name = "aws-ebs-csi-driver"
|
||||
addon_version = "v1.29.1-eksbuild.1"
|
||||
service_account_role_arn = aws_iam_role.ebs_csi_driver.arn
|
||||
}
|
||||
|
||||
# AWS Identity and Access Management (IAM) OpenID Connect (OIDC) provider
|
||||
|
||||
resource "aws_iam_openid_connect_provider" "eks" {
|
||||
url = aws_eks_cluster.main.identity.0.oidc.0.issuer
|
||||
client_id_list = ["sts.amazonaws.com"]
|
||||
thumbprint_list = [data.tls_certificate.oidc.certificates[0].sha1_fingerprint]
|
||||
}
|
||||
|
||||
# IAM
|
||||
resource "aws_iam_role" "ebs_csi_driver" {
|
||||
name = "ebs-csi-driver"
|
||||
assume_role_policy = data.aws_iam_policy_document.ebs_csi_driver_assume_role.json
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "ebs_csi_driver_assume_role" {
|
||||
statement {
|
||||
effect = "Allow"
|
||||
|
||||
principals {
|
||||
type = "Federated"
|
||||
identifiers = [aws_iam_openid_connect_provider.eks.arn]
|
||||
}
|
||||
|
||||
actions = [
|
||||
"sts:AssumeRoleWithWebIdentity",
|
||||
]
|
||||
|
||||
condition {
|
||||
test = "StringEquals"
|
||||
variable = "${aws_iam_openid_connect_provider.eks.url}:aud"
|
||||
values = ["sts.amazonaws.com"]
|
||||
}
|
||||
|
||||
condition {
|
||||
test = "StringEquals"
|
||||
variable = "${aws_iam_openid_connect_provider.eks.url}:sub"
|
||||
values = ["system:serviceaccount:kube-system:ebs-csi-controller-sa"]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy_attachment" "AmazonEBSCSIDriverPolicy" {
|
||||
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
|
||||
role = aws_iam_role.ebs_csi_driver.name
|
||||
}
|
||||
```
|
||||
|
||||
The above configuration follows the docs, binding an IAM role to the service account `kube-system/ebs-csi-controller-sa` using an OpenID connect provider.
|
||||
|
||||
After applying the changes above, I deployed [the sample application](https://docs.aws.amazon.com/eks/latest/userguide/ebs-sample-app.html) and noticed that the persistent volume claims were bound to EBS volumes.
|
@@ -5,6 +5,7 @@ lastmod: 2021-09-17T12:48:33-04:00
|
||||
draft: false
|
||||
comments: true
|
||||
tags: ['aws', 'python', 'security', 'aws-vault']
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
Rotating credentials is a security best practice. This morning, I read a question about automatically rotating AWS Access Keys without having to go through the hassle of navigating the AWS console. There are some existing solutions already, but I decided to write a [script](https://gist.github.com/davegallant/2c042686a78684a657fe99e20fa7a924#file-aws_access_key_rotator-py) since it was incredibly simple. The script could be packed up as a systemd/launchd service to continually rotate access keys in the background.
|
@@ -5,6 +5,7 @@ lastmod: 2022-03-13T18:49:10-04:00
|
||||
comments: true
|
||||
draft: false
|
||||
tags: ["synology", "gmail", "backup", "ransomware"]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
I've used gmail since the beta launched touting a whopping 1GB of storage. I thought this was a massive leap in email technology at the time. I was lucky enough to get an invite fairly quickly. Not suprisingly, I have many years of emails, attachments, and photos. I certainly do not want to lose the content of many of these emails. Despite the redundancy of the data that Google secures, I still feel better retaining a copy of this data on my own physical machines.
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
@@ -5,6 +5,7 @@ lastmod: 2021-10-11T10:43:35-04:00
|
||||
draft: false
|
||||
comments: true
|
||||
tags: ["docker", "podman", "containers"]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
There are a number of reasons why you might want to replace docker, especially on macOS. The following feature bundled in Docker Desktop might have motivated you enough to consider replacing docker:
|
@@ -5,6 +5,7 @@ lastmod: 2021-11-14T10:07:03-05:00
|
||||
draft: false
|
||||
comments: true
|
||||
tags: ["k3s", "proxmox", "lxc", "self-hosted"]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
It has been a while since I've actively used Kubernetes and wanted to explore the evolution of tools such as [Helm](https://helm.sh) and [Tekton](https://tekton.dev). I decided to deploy [K3s](https://k3s.io), since I've had success with deploying it on resource-contrained Raspberry Pis in the past. I thought that this time it'd be convenient to have K3s running in a LXC container on Proxmox. This would allow for easy snapshotting of the entire Kubernetes deployment. LXC containers also provide an efficient way to use a machine's resources.
|
After Width: | Height: | Size: 34 KiB |
@@ -6,6 +6,7 @@ lastmod: 2023-12-10T17:22:11-05:00
|
||||
draft: false
|
||||
description: ""
|
||||
tags: ["gitea", "gitea actions", "github actions", "tailscale", "self-hosted"]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
In this post I'll go through the process of setting up Gitea Actions and [Tailscale](https://tailscale.com/), unlocking a simple and secure way to automate workflows.
|
||||
@@ -22,23 +23,24 @@ In this post I'll go through the process of setting up Gitea Actions and [Tailsc
|
||||
|
||||
So what are they? If you've ever used GitHub Actions (and if you're reading this, I imagine you have), these will look familiar. Gitea Actions essentially enable the ability to run github workflows on gitea. Workflows between gitea and github are not completely interopable, but a lot of the same workflow syntax is already compatible on gitea. You can find a documented list of [unsupported workflows syntax](https://docs.gitea.com/usage/actions/comparison#unsupported-workflows-syntax).
|
||||
|
||||
Actions work by using a [custom fork](https://gitea.com/gitea/act) of [nekos/act](https://github.com/nektos/act). Workflows run in a new container for every job. If you specify an action such as 'actions/checkout@v3', it defaults to downloading the scripts from github.com. To avoid internet egress, you could always clone the required actions to your local gitea instance.
|
||||
Actions work by using a [custom fork](https://gitea.com/gitea/act) of [nekos/act](https://github.com/nektos/act). Workflows run in a new container for every job. If you specify an action such as `actions/checkout@v4`, it defaults to downloading the scripts from github.com. To avoid internet egress, you could always clone the required actions to your local gitea instance.
|
||||
|
||||
Actions (gitea's implementation) has me excited because it makes spinning up a network-isolated environment for workflow automation incredibly simple.
|
||||
|
||||
## Integration with Tailscale
|
||||
|
||||
So how does Tailscale help here? Well, more recently I've been exposing my self-hosted services through a combination of traefik and the tailscale (through the tailscale-traefik proxy integration described [here](https://traefik.io/blog/exploring-the-tailscale-traefik-proxy-integration/)). This allows for a nice looking dns name (i.e. gitea.my-tailnet-name.ts.net) and automatic tls certificate management. I can also share this tailscale node securely with other tailscale users without configuring any firewall rules on my router.
|
||||
> **2024-02-10**: I had originally written this post to include [Tailscale-Traefik Proxy Integration](https://traefik.io/blog/exploring-the-tailscale-traefik-proxy-integration/), but have since removed it in favour of Tailscale Serve after learning from this [example](https://github.com/tailscale-dev/docker-guide-code-examples). This simplifies the setup and reduces the number of moving parts.
|
||||
|
||||
So how does Tailscale help here? Well, more recently I've been exposing my self-hosted services using Tailscale [Serve](https://tailscale.com/kb/1312/serve). This allows for a nice looking dns name (i.e. gitea.my-tailnet-name.ts.net), automatic tls certificate management, and optionally allowing the address to be publically accessible (by using [Funnel](https://tailscale.com/kb/1223/funnel)).
|
||||
|
||||
## Deploying Gitea, Traefik, and Tailscale
|
||||
|
||||
In my case, the following is already set up:
|
||||
|
||||
- [docker-compose is installed](https://docs.docker.com/compose/install/linux/)
|
||||
- [tailscale is installed on the gitea host](https://tailscale.com/kb/1017/install/)
|
||||
- [tailscale magic dns is enabled](https://tailscale.com/kb/1081/magicdns/)
|
||||
|
||||
My preferred approach to deploying code in a homelab environment is with docker compose. I have deployed this in a [proxmox lxc container](https://pve.proxmox.com/wiki/Linux_Container) based on debian with a hostname `gitea`. This could be deployed in any environment and with any hostname (as long you updated the tailscale machine name to your preferred subdomain for magic dns).
|
||||
My preferred approach to deploying code in a homelab environment is with docker compose. I have deployed this in a LXC on Proxmox. You could run this on a virtual machine or a physical host as well.
|
||||
|
||||
The `docker-compose.yaml` file looks like:
|
||||
|
||||
@@ -48,6 +50,7 @@ services:
|
||||
gitea:
|
||||
image: gitea/gitea:1.21.1
|
||||
container_name: gitea
|
||||
network_mode: service:ts-gitea
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
@@ -61,67 +64,62 @@ services:
|
||||
- ./data:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
traefik:
|
||||
image: traefik:v3.0.0-beta4
|
||||
container_name: traefik
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
ts-gitea:
|
||||
image: tailscale/tailscale:v1.58
|
||||
container_name: ts-gitea
|
||||
hostname: gitea
|
||||
environment:
|
||||
- TS_AUTHKEY=<FILL THIS IN>
|
||||
- TS_SERVE_CONFIG=/config/gitea.json
|
||||
- TS_STATE_DIR=/var/lib/tailscale
|
||||
volumes:
|
||||
- ./traefik/data/traefik.yaml:/traefik.yaml:ro
|
||||
- ./traefik/data/dynamic.yaml:/dynamic.yaml:ro
|
||||
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
|
||||
- ${PWD}/state:/var/lib/tailscale
|
||||
- ${PWD}/config:/config
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
cap_add:
|
||||
- net_admin
|
||||
- sys_module
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
`traefik/data/traefik.yaml`:
|
||||
Note that you must specify a `TS_AUTHKEY` in the `ts-gitea` service. You can generate an auth key [here](https://login.tailscale.com/admin/settings/keys).
|
||||
|
||||
`config/gitea.json`:
|
||||
|
||||
```yaml
|
||||
entryPoints:
|
||||
https:
|
||||
address: ":443"
|
||||
providers:
|
||||
file:
|
||||
filename: dynamic.yaml
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
tailscale: {}
|
||||
log:
|
||||
level: INFO
|
||||
{
|
||||
"TCP": { "443": { "HTTPS": true } },
|
||||
"Web":
|
||||
{
|
||||
"${TS_CERT_DOMAIN}:443":
|
||||
{ "Handlers": { "/": { "Proxy": "http://127.0.0.1:3000" } } },
|
||||
},
|
||||
"AllowFunnel": { "${TS_CERT_DOMAIN}:443": false }
|
||||
}
|
||||
```
|
||||
|
||||
and finally `traefik/data/dynamic/dynamic.yaml`:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
gitea:
|
||||
rule: Host(`gitea.my-tailnet-name.ts.net`)
|
||||
entrypoints:
|
||||
- "https"
|
||||
service: gitea
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
services:
|
||||
gitea:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://gitea:3000"
|
||||
```
|
||||
|
||||
Something to consider is whether or not you want to use ssh with git. One method to get this to work with containers is to use [ssh container passthrough](https://docs.gitea.com/installation/install-with-docker#ssh-container-passthrough). I decided to keep it simple and not use ssh, since communicating over https is perfectly fine for my use case.
|
||||
|
||||
After adding the above configuration, running `docker compose up -d` should be enough to get an instance up and running. It will be accessible at [https://gitea.my-tailnet-name.ts.net](https://gitea.my-tailnet-name.ts.net) from within the tailnet.
|
||||
|
||||
## Connecting a Runner
|
||||
Something to consider is whether or not you want to use ssh with git. One method to get this to work with containers is to use [ssh container passthrough](https://docs.gitea.com/installation/install-with-docker#ssh-container-passthrough). I decided to keep it simple and not use ssh, since communicating over https is perfectly fine for my use case.
|
||||
|
||||
I installed the runner by [following the docs](https://docs.gitea.com/usage/actions/quickstart#set-up-runner). I opted for installing it on a separate host (another lxc container) as recommended in the docs. I used the systemd unit file to ensure that the runner comes back online after system reboots. I installed tailscale on this gitea runner as well, so that it can have the same "networking privileges" as the main instance.
|
||||
## Theming
|
||||
|
||||
After registering this runner and starting the daemon, it appeared in `/admin/actions/runners`:
|
||||
I discovered some themes for gitea [here](https://git.sainnhe.dev/sainnhe/gitea-themes).
|
||||
|
||||

|
||||
I added the theme by copying [theme-palenight.css](https://git.sainnhe.dev/sainnhe/gitea-themes/raw/branch/master/dist/theme-palenight.css) into `./data/gitea/public/assets/css`. I then added the following to `environment` in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
- GITEA__ui__DEFAULT_THEME=palenight
|
||||
- GITEA__ui__THEMES=palenight
|
||||
```
|
||||
|
||||
After restarting the gitea instance, the default theme was applied.
|
||||
|
||||
## Connecting runners
|
||||
|
||||
I installed the runner by [following the docs](https://docs.gitea.com/usage/actions/quickstart#set-up-runner). I opted for installing it on a separate host as recommended in the docs. I used the systemd unit file to ensure that the runner comes back online after system reboots. I installed tailscale on the gitea runner as well, so that it can be part of the same tailnet as the main instance.
|
||||
|
||||
After registering this runner and starting the daemon, the runner appeared in `/admin/actions/runners`. I added two other runners to help with parallelization.
|
||||
|
||||
## Running a workflow
|
||||
|
||||
@@ -141,19 +139,6 @@ on:
|
||||
jobs:
|
||||
run-ansible-playbook:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
host:
|
||||
- changedetection
|
||||
- homelab
|
||||
- invidious
|
||||
- jackett
|
||||
- ladder
|
||||
- miniflux
|
||||
- plex
|
||||
- qbittorrent
|
||||
- tailscale-exit-node
|
||||
- uptime-kuma
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
@@ -167,7 +152,6 @@ jobs:
|
||||
requirements: requirements.yml
|
||||
options: |
|
||||
--inventory inventory
|
||||
--limit ${{ matrix.host }}
|
||||
- name: Send failure notification
|
||||
uses: dawidd6/action-send-mail@v3
|
||||
if: always() && failure()
|
||||
@@ -177,7 +161,7 @@ jobs:
|
||||
secure: true
|
||||
username: myuser
|
||||
password: ${{ secrets.MAIL_PASSWORD }}
|
||||
subject: ansible runbook '${{ matrix.host }}' failed
|
||||
subject: ansible runbook failed
|
||||
to: me@davegallant.ca
|
||||
from: RFD Notify
|
||||
body: |
|
||||
@@ -186,7 +170,7 @@ jobs:
|
||||
|
||||
And voilà:
|
||||
|
||||
{{< video poster="gitea-workflow" >}}
|
||||

|
||||
|
||||
You may be wondering how the gitea runner is allowed to connect to the other hosts using ansible? Well, the nodes are in the same tailnet and have [tailscale ssh](https://tailscale.com/tailscale-ssh) enabled.
|
||||
|
||||
@@ -199,3 +183,5 @@ One enhancement that I would like to see is the ability to send notifications on
|
||||
Gitea Actions are fast and the resource footprint is minimal. My gitea instance is currently using around 250mb of memory and a small fraction of a single cpu core (and the runner is using a similar amount of resources). This is impressive since many alternatives tend to require substantially more resources. It likely helps that the codebase is largely written in go.
|
||||
|
||||
By combining gitea with the networking marvel that is tailscale, running workflows becomes simple and fun. Whether you are working on a team or working alone, this setup ensures that your workflows are securely accessible from anywhere with an internet connection.
|
||||
|
||||
Check out my gitea instance exposed via Funnel [here](https://gitea.snake-cloud.ts.net).
|
@@ -19,13 +19,13 @@ tags:
|
||||
"socat",
|
||||
"socks",
|
||||
]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
I ran into a roadblock recently where I wanted to be able to conveniently connect to a managed postgres database within Azure that was not running on public subnets. And by conveniently, I mean that I'd rather not have to spin up an ephemeral virtual machine running in the same network and proxy the connection, and I'd like to use a local client (preferably with a GUI). After several web searches, it became evident that Azure does not readily provide much tooling to support this.
|
||||
I ran into a roadblock recently where I wanted to conveniently connect to a managed postgres database within Azure that was not running on public subnets. And by conveniently, I mean that I'd rather not have to spin up an ephemeral virtual machine running in the same network and proxy the connection, and I'd like to use a local client (preferably with a GUI). After several web searches, it became evident that Azure does not readily provide much tooling to support this.
|
||||
|
||||
<!--more-->
|
||||
|
||||
|
||||
## Go Public?
|
||||
|
||||
Should the database be migrated to public subnets? Ideally not, since it is good practice to host internal infrastructure in restricted subnets.
|
||||
@@ -81,4 +81,6 @@ If these stars align, than this solution might work as a stopgap for accessing a
|
||||
|
||||
It would be nice if Azure provided tooling similar to cloud-sql-proxy, so that using private databases would be more of a convenient experience.
|
||||
|
||||
One other thing to note is that some clients (such as [dbeaver](https://dbeaver.io/)) [do not provide DNS resolution over SOCKS](https://github.com/dbeaver/dbeaver/issues/872). So in this case, you won't be able to use DNS as if you were inside the cluster, but instead have to rely on knowing private ip addresses.
|
||||
~~One other thing to note is that some clients (such as [dbeaver](https://dbeaver.io/)) [do not provide DNS resolution over SOCKS](https://github.com/dbeaver/dbeaver/issues/872). So in this case, you won't be able to use DNS as if you were inside the cluster, but instead have to rely on knowing private ip addresses.~~
|
||||
|
||||
> **2025-01-16:**: DNS over SOCKS now works with the latest dbeaver client.
|
@@ -14,6 +14,7 @@ tags:
|
||||
"vlan",
|
||||
"self-hosted",
|
||||
]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
My aging router has been running [OpenWrt](https://en.wikipedia.org/wiki/OpenWrt) for years and for the most part has been quite reliable. OpenWrt is an open-source project used on embedded devices to route network traffic. It supports many different configurations and there exists a [large index of packages](https://openwrt.org/packages/index/start). Ever since I've connected some standalone wireless access points, I've had less of a need for an off-the-shelf all-in-one wireless router combo. I've also recently been experiencing instability with my router (likely the result of a combination of configuration tweaking and firmware updating). OpenWrt has served me well, but it is time to move on!
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 935 KiB After Width: | Height: | Size: 935 KiB |
@@ -13,6 +13,7 @@ tags:
|
||||
"privacy",
|
||||
"self-hosted",
|
||||
]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
I recently stumbled upon [yewtu.be](https://yewtu.be) and found it intriguing. It not only allows you to watch YouTube without _being on YouTube_, but it also allows you to create an account and subscribe to channels without a Google account. What sort of wizardry is going on under the hood? It turns out that it's a hosted instance of [invidious](https://invidious.io/).
|
||||
@@ -82,10 +83,32 @@ After invidious was up and running, I installed [Tailscale](https://tailscale.co
|
||||
|
||||
I figured it would be nice to redirect existing YouTube links that others send me, so that I could seamlessly watch the videos using invidious.
|
||||
|
||||
I went looking for a way to redirect paths at the browser level. I found the lightweight proxy [requestly](https://requestly.io/), which can be used to modify http requests in my browser. I created the following rules:
|
||||
I went looking for a way to redirect paths at the browser level. I found [Redirector](https://github.com/einaregilsson/Redirector), which can be used to modify http requests in the browser. I created the following redirect (exported as json):
|
||||
|
||||

|
||||
```json
|
||||
{
|
||||
"redirects": [
|
||||
{
|
||||
"description": "youtube to invidious",
|
||||
"exampleUrl": "https://www.youtube.com/watch?v=-lz30by8-sU",
|
||||
"exampleResult": "http://invidious:3000/watch?v=-lz30by8-sU",
|
||||
"error": null,
|
||||
"includePattern": "https://*youtube.com/*",
|
||||
"excludePattern": "",
|
||||
"patternDesc": "Any youtube video should redirect to invidious",
|
||||
"redirectUrl": "http://invidious:3000/$2",
|
||||
"patternType": "W",
|
||||
"processMatches": "noProcessing",
|
||||
"disabled": false,
|
||||
"grouped": false,
|
||||
"appliesTo": [
|
||||
"main_frame"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Now the link https://www.youtube.com/watch?v=-lz30by8-sU will redirect to [http://invidious:3000/watch?v=-lz30by8-sU](http://invidious:3000/watch?v=-lz30by8-sU)
|
||||
Now the link <https://www.youtube.com/watch?v=-lz30by8-sU> will redirect to [http://invidious:3000/watch?v=-lz30by8-sU](http://invidious:3000/watch?v=-lz30by8-sU)
|
||||
|
||||
I'm still looking for ways to improve this invidious setup. There doesn't appear to be a way to stream in 4K yet.
|
@@ -12,9 +12,9 @@ A homelab can be an inexpensive way to host a multitude of internal/external ser
|
||||
|
||||
<!--more-->
|
||||
|
||||
Do you want host your own Media server? Ad blocker? Web server?
|
||||
Do you want host your own media server? ad blocker? reverse proxy?
|
||||
Are you interested in learning more about Linux? Virtualization? Networking? Security?
|
||||
Building a homelab can be an entertaining playground to enhance your computer skills.
|
||||
A homelab can be a playground to enhance your computer skills, without worrying about breaking anything important.
|
||||
|
||||
One of the best parts about building a homelab is that it doesn't have to be a large investment in terms of hardware. One of the simplest ways to build a homelab is out of a [refurbished computer](https://ca.refurb.io/products/hp-800-g1-usff-intel-core-i5-4570s-16gb-ram-512gb-ssd-wifi-windows-10-pro?variant=33049503825943).
|
||||
Having multiple machines/nodes provides the advantage of increased redundancy, but starting out with a single node is enough to reap many of the benefits of having a homelab.
|
||||
@@ -30,20 +30,23 @@ A hypervisor such as [Proxmox](https://www.proxmox.com/en/proxmox-ve/get-started
|
||||
|
||||
## Services
|
||||
|
||||
So what are some useful services to deploy?
|
||||
Here is a list of some useful services to consider:
|
||||
|
||||
- [Jellyfin](https://jellyfin.org/) or [Plex](https://www.plex.tv/) - basically a self-hosted Netflix that can be used to stream from multiple devices, and the best part is that you manage the content! Unlike Plex, Jellyfin is open source and can be found [here](https://github.com/jellyfin/jellyfin).
|
||||
- [changedetection](https://github.com/dgtlmoon/changedetection.io) - is a self-hosted equivalent to something like [visualping.io](https://visualping.io/) that will notify you when a webpage changes and keep track of the diffs
|
||||
- [Adguard](https://github.com/AdguardTeam/AdGuardHome) or [Pihole](https://pi-hole.net/) - can block a list of known trackers for all clients on your local network. I've used pihole for a long time, but have recently switched to Adguard since the UI is more modern and it has the ability to toggle on/off a pre-defined list of services, including Netflix (this is useful if you have stealthy young kids). Either of these will speed up your internet experience, simply because you won't need to download all of the extra tracking bloat.
|
||||
- [Gitea](https://gitea.io/) - A lightweight git server. I use this to mirror git repos from GitHub, GitLab, etc.
|
||||
- [Homer](https://github.com/bastienwirtz/homer) - A customizable landing page for services you need to access (including the ability to quickly search).
|
||||
- [Uptime Kuma](https://github.com/louislam/uptime-kuma) - A fancy tool for monitoring the uptime of services.
|
||||
- [Jellyfin](https://jellyfin.org/) or [Plex](https://www.plex.tv/) - a common gateway to self-hosting that enables a "self-hosted Netflix" experience that puts you in control of the content (guaranteed to make your partner and kids happy)
|
||||
- [changedetection](https://github.com/dgtlmoon/changedetection.io) - is a self-hosted equivalent to something like [visualping.io](https://visualping.io/) that can notify you when a webpage changes and keep track of the diffs
|
||||
- [Adguard](https://github.com/AdguardTeam/AdGuardHome) or [Pihole](https://pi-hole.net/) - can block a list of known trackers for all clients on your local network with the added benefit of speeding up web page load times
|
||||
- [gitea](https://gitea.io/) - A lightweight git server that can be used to mirror git repos and host private content
|
||||
- [miniflux](https://github.com/miniflux/v2) - a minimalist RSS reader
|
||||
- [gethomepage](https://github.com/gethomepage/homepage) - A customizable landing page for quick access to services with many supported widgets that can query APIs and display information
|
||||
- [Uptime Kuma](https://github.com/louislam/uptime-kuma) - A tool for monitoring the uptime of services, with notification support
|
||||
- [Speedtest Tracker](https://github.com/alexjustesen/speedtest-tracker) - a way to monitor the performance of your internet connection and/or vpn connection
|
||||
- [Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF) - a self-hosted PDF manipulation tool that will keep your data private
|
||||
|
||||
There is a large number of services you can self-host, including your own applications that you might be developing. [awesome-self-hosted](https://github.com/awesome-selfhosted/awesome-selfhosted) provides a curated list of services that might be of interest to you.
|
||||
There is a large number of services you can self-host, including your own applications that you might be developing. Homelabbing allows you to have control over your data and services, and gives you the opportunity to be a software, network, and infrastructure engineer all at once.
|
||||
|
||||
## VPN
|
||||
|
||||
You could certainly setup and manage your own VPN by using something like [OpenVPN](https://openvpn.net/community-downloads/), but there is also something else you can try: [tailscale](https://tailscale.com/). It is a very quick way to create fully-encrypted connections between clients. With its [MagicDNS](https://tailscale.com/kb/1081/magicdns/), your can reference the names of machines like `homer` rather than using an IP address. By using this mesh-like VPN, you can easily create a secure tunnel to your homelab from anywhere.
|
||||
[Tailscale](https://tailscale.com/) is a quick way to create a flat network for all of your services. With its [MagicDNS](https://tailscale.com/kb/1081/magicdns/), your can reference the names of machines like `changedetection` rather than using an IP address, or managing DNS yourself. By using this mesh-like VPN, you can easily create a secure tunnel to your homelab from anywhere.
|
||||
|
||||
## Monitoring
|
||||
|
||||
@@ -60,3 +63,5 @@ As mentioned above, [Uptime Kuma](https://github.com/louislam/uptime-kuma) is a
|
||||
## In Summary
|
||||
|
||||
Building out a homelab can be a rewarding experience and it doesn't require buying a rack full of expensive servers to get a significant amount of utility. There are many services that you can run that require very minimal setup, making it possible to get a server up and running in a short period of time, with monitoring, and that can be securely connected to remotely.
|
||||
|
||||
If you're looking for a steady stream of ideas for your homelab, check out [selfhosted.show](https://selfhosted.show/).
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
BIN
content/blog/what-to-do-with-a-homelab/proxmox.png
Normal file
After Width: | Height: | Size: 216 KiB |
BIN
content/blog/what-to-do-with-a-homelab/uptime-kuma.png
Normal file
After Width: | Height: | Size: 41 KiB |
@@ -5,6 +5,7 @@ lastmod: 2021-09-08T00:42:33-04:00
|
||||
draft: false
|
||||
comments: true
|
||||
tags: ['nix', 'dotfiles', 'home-manager']
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
Over the years I have collected a number of dotfiles that I have shared across both Linux and macOS machines (`~/.zshrc`, `~/.config/git/config`, `~/.config/tmux/tmux.conf`, etc). I have tried several different ways to manage them, including [bare git repos](https://www.atlassian.com/git/tutorials/dotfiles) and utilities such as [GNU Stow](https://www.gnu.org/software/stow/). These solutions work well enough, but I have since found what I would consider a much better solution for organizing user configuration: [home-manager](https://github.com/nix-community/home-manager).
|
||||
|
||||
@@ -181,3 +182,5 @@ In ways, home-manager can be seen as a gateway to the nix ecosystem. If you have
|
||||
## Wrapping up
|
||||
|
||||
The title of this post is slightly misleading, since it's possible to retain some of your dotfiles and have them intermingle with home-manager by including them alongside nix. The idea of defining user configuration using nix can provide a clean way to maintain your configuration, and allow it to be portable across platforms. Is it worth the effort to migrate away from shell scripts and dotfiles? I'd say so.
|
||||
|
||||
You can find my nix config [here](https://github.com/davegallant/nix-config).
|
40
content/post/opting-out-of-haveibeenpwned.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "Opting out of haveibeenpwned"
|
||||
date: "2025-02-16T21:15:07-05:00"
|
||||
draft: false
|
||||
comments: true
|
||||
toc: false
|
||||
author: "Dave Gallant"
|
||||
tags:
|
||||
[
|
||||
"breach",
|
||||
"haveibeenpwned",
|
||||
"hibp",
|
||||
"privacy",
|
||||
"darkweb",
|
||||
]
|
||||
author: "Dave Gallant"
|
||||
---
|
||||
|
||||
The increasing number of data breaches has become a concern for anyone trying to live a life of relative privacy. Just last month, the PowerSchool Data informed its customers that [hackers stole data of 62 million students](https://www.bleepingcomputer.com/news/security/powerschool-hacker-claims-they-stole-data-of-62-million-students/). Unless you have been practicing [Extreme Privacy](https://inteltechniques.com/book7.html) techniques for decades, you likely have been impacted by at least one data breach.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Understanding Data Breaches
|
||||
|
||||
Data breaches occur when individuals gain access to sensitive information and then share it for public consumption. This sensitive information is dumped on hacker forums and other sites that comprise the [dark web](https://en.wikipedia.org/wiki/Dark_web).
|
||||
|
||||
## Check if you have been impacted
|
||||
|
||||
There are a number of services that can be used to check if you have been impacted by any data breaches, including [Mozilla monitor](https://monitor.mozilla.org), [Google Dark web report](https://myactivity.google.com/dark-web-report/dashboard), and [HIBP](https://haveibeenpwned.com/). Password managers often also offer ways to check your current credentials against known breaches. These services can also be configured to send you notifications on breaches. It is a good idea to become aware of these breaches as soon as you can, so that you can limit the blast radius of these exposures.
|
||||
|
||||
If you have been an email or phone number for any length of time, there is a high probability that some of your data has been exposed somewhere. You can easily check by querying [HIBP](https://haveibeenpwned.com/). Many of the tools that offer breach detection, query this database. Although I think this is a great service (and I recommend using it), it also opens the door for anyone who may be looking to gain more information about your present and past usages of services.
|
||||
|
||||
## Opting out
|
||||
|
||||
If you have an email that you'd like to protect, I'd suggest [opting out of public searchability](https://haveibeenpwned.com/OptOut/). This of course does not undo the data breach that happened, but does it make it more challenging for someone to quickly find out information about an associated email address. This does not impact the ability for you to be [subscribe to breach notifications](https://haveibeenpwned.com/NotifyMe), but it does force you to validate that you have access to the email, before receiving the notifications.
|
||||
|
||||
## Email aliases
|
||||
|
||||
One way to prevent future exposures is to use an email aliasing service such as [Firefox Relay](https://relay.firefox.com), [DuckDuckGo Email Protection](https://duckduckgo.com/email/), or if you use Proton Mail, [hide-my-email aliases](https://proton.me/support/addresses-and-aliases#hide). This will allow you sign up for services using an alias. The service then forwards all emails to your real address that you configure when setting up the alias.
|
||||
|
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 157 KiB |
Before Width: | Height: | Size: 117 KiB |
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"basics": {
|
||||
"name": "Dave Gallant",
|
||||
"image": "",
|
||||
"email": "me@davegallant.ca",
|
||||
"summary": "👋 I'm a software tinkerer with a passion for infra, security and self-hosting.",
|
||||
"profiles": [
|
||||
{
|
||||
"network": "LinkTree",
|
||||
"url": "https://linktr.ee/davegallant"
|
||||
},
|
||||
{
|
||||
"network": "GitHub",
|
||||
"url": "https://github.com/davegallant"
|
||||
},
|
||||
{
|
||||
"network": "Mastodon",
|
||||
"url": "https://mastodon.social/@davegallant"
|
||||
},
|
||||
{
|
||||
"network": "LinkedIn",
|
||||
"url": "https://www.linkedin.com/in/dave-gallant"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
8
go.mod
@@ -1,8 +0,0 @@
|
||||
module davegallant.github.io
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102202609-f31cb2b5c2f4 // indirect
|
||||
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231224014047-e651a547c19a // indirect
|
||||
)
|
16
go.sum
@@ -1,16 +0,0 @@
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102155230-cac2894e8557 h1:rXYdDcfrr/E2k2o3nnuLE6BmDw8K+JSs8hHFCLKzJGo=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102155230-cac2894e8557/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102171330-adc30b08067a h1:muPaq+6iMPFeTPhq7aD1RgAyzpJKEZdAZa+HEX/fDAY=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102171330-adc30b08067a/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102171826-d67d7957b276 h1:YS3mESQh4CBWBF5ubgDxcGlGod+ZgaNorFYu8ZiB/zY=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102171826-d67d7957b276/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102200159-9a3d1dc7bc54 h1:7FzV8p/mlirxME4/V+KRsQp7QxucKV3f3Uj3kqxI37A=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102200159-9a3d1dc7bc54/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102200650-e760163af723 h1:DiYyh60x4rLMDiYTSPGtYnBKLJRd72OcMuVvc9PkQ6Y=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102200650-e760163af723/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102201014-35cc70be186f h1:qiJX+D0Yn75E1rNnEfprPOVSFam5uwr46xg7vNwe7qY=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102201014-35cc70be186f/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102202609-f31cb2b5c2f4 h1:al0gmkt0SgL67AtSFifzlB4WZEP3Vu+ngLp381XJlkI=
|
||||
github.com/davegallant/hugo-theme-gruvbox v0.0.0-20240102202609-f31cb2b5c2f4/go.mod h1:BQehNdf/SB/+bCc031OVsLECIgB9ZaN1dfUFKTeOIuo=
|
||||
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231224014047-e651a547c19a h1:EZRiOf0iW5k9lycVv3LngzSsGUxDRszYS4U7ea2r8RY=
|
||||
github.com/schnerring/hugo-mod-json-resume v0.0.0-20231224014047-e651a547c19a/go.mod h1:5dixHC0WHu0w2Aqb8hsOCrIU1OBYr1w5Q6HZAmTub7Q=
|
10
justfile
Normal file
@@ -0,0 +1,10 @@
|
||||
build: clean
|
||||
npm ci
|
||||
hugo --minify
|
||||
|
||||
clean:
|
||||
rm -rf public/
|
||||
|
||||
server: clean
|
||||
npm ci
|
||||
hugo server --buildDrafts
|
@@ -3,7 +3,7 @@
|
||||
src="https://storage.ko-fi.com/cdn/widget/Widget_2.js"
|
||||
></script>
|
||||
<script type="text/javascript">
|
||||
kofiwidget2.init("Buy me a coffee", "#458588", "F1F2S4LWI");
|
||||
kofiwidget2.init("Buy me a coffee", "#32344a", "F1F2S4LWI");
|
||||
kofiwidget2.draw();
|
||||
</script>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
{{- $utterancesEnabled := $config.utterances.enable -}}
|
||||
|
||||
{{- if $utterancesEnabled -}}
|
||||
<br>
|
||||
<br>
|
||||
<section id='comments' class='comments'>
|
||||
<div class='container sep-before'>
|
||||
<div class='comments'>
|
||||
|
@@ -1,23 +1,19 @@
|
||||
{{- $scriptSrc := "https://utteranc.es/client.js" -}}
|
||||
|
||||
{{- $issueTerm := .Page.Site.Params.comments.utterances.issueTerm -}}
|
||||
{{- $label := .Page.Site.Params.comments.utterances.label -}}
|
||||
{{- $username := .Page.Site.Params.comments.utterances.github.username -}}
|
||||
{{- $repository := .Page.Site.Params.comments.utterances.github.repository -}}
|
||||
{{- $scriptSrc := "https://utteranc.es/client.js" -}} {{- $issueTerm :=
|
||||
.Page.Site.Params.comments.utterances.issueTerm -}} {{- $label :=
|
||||
.Page.Site.Params.comments.utterances.label -}} {{- $username :=
|
||||
.Page.Site.Params.comments.utterances.github.username -}} {{- $repository :=
|
||||
.Page.Site.Params.comments.utterances.github.repository -}}
|
||||
|
||||
<script>
|
||||
// load utteranc comment
|
||||
var getTheme = window.localStorage && window.localStorage.getItem("theme");
|
||||
getTheme = getTheme == null ? 'dark' : getTheme;
|
||||
|
||||
let theme = getTheme === 'dark' ? 'gruvbox-dark' : 'github-light';
|
||||
let s = document.createElement('script');
|
||||
s.src = 'https://utteranc.es/client.js';
|
||||
s.setAttribute('repo', '{{ print $username "/" $repository }}');
|
||||
s.setAttribute('issue-term', '{{ $issueTerm }}');
|
||||
s.setAttribute('theme', theme);
|
||||
s.setAttribute('crossorigin', 'anonymous');
|
||||
s.setAttribute('async', '');
|
||||
document.querySelector('div.comments').innerHTML = '';
|
||||
document.querySelector('div.comments').appendChild(s);
|
||||
// load comments
|
||||
let theme = "dark-blue";
|
||||
let script = document.createElement("script");
|
||||
script.src = "https://utteranc.es/client.js";
|
||||
script.setAttribute("repo", '{{ print $username "/" $repository }}');
|
||||
script.setAttribute("issue-term", "{{ $issueTerm }}");
|
||||
script.setAttribute("theme", theme);
|
||||
script.setAttribute("crossorigin", "anonymous");
|
||||
script.setAttribute("async", "");
|
||||
document.querySelector("div.comments").innerHTML = "";
|
||||
document.querySelector("div.comments").appendChild(script);
|
||||
</script>
|
||||
|
9
layouts/partials/footer_end.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- Cloudflare Web Analytics -->
|
||||
<script
|
||||
defer
|
||||
src="https://static.cloudflareinsights.com/beacon.min.js"
|
||||
data-cf-beacon='{"token": "b96799f53f9940dca6f660e6052ba009"}'
|
||||
></script>
|
||||
<!-- End Cloudflare Web Analytics -->
|
||||
|
||||
{{ template "_internal/google_analytics.html" . }}
|
2
layouts/partials/head/head_end.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<!-- Umami Analytics -->
|
||||
<script defer src="https://umami.snake-cloud.ts.net/script.js" data-website-id="e8adafba-b892-4dad-a139-2bd61fe5fab9"></script>
|
122
package-lock.json
generated
@@ -227,9 +227,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/cascade-layer-name-parser": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.7.tgz",
|
||||
"integrity": "sha512-9J4aMRJ7A2WRjaRLvsMeWrL69FmEuijtiW1XlK/sG+V0UJiHVYUyvj9mY4WAXfU/hGIiGOgL8e0jJcRyaZTjDQ==",
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.9.tgz",
|
||||
"integrity": "sha512-RRqNjxTZDUhx7pxYOBG/AkCVmPS3zYzfE47GEhIGkFuWFTQGJBgWOUUkKNo5MfxIfjDz5/1L3F3rF1oIsYaIpw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -245,8 +245,8 @@
|
||||
"node": "^14 || ^16 || >=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^2.5.0",
|
||||
"@csstools/css-tokenizer": "^2.2.3"
|
||||
"@csstools/css-parser-algorithms": "^2.6.1",
|
||||
"@csstools/css-tokenizer": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/color-helpers": {
|
||||
@@ -319,9 +319,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-parser-algorithms": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.5.0.tgz",
|
||||
"integrity": "sha512-abypo6m9re3clXA00eu5syw+oaPHbJTPapu9C4pzNsJ4hdZDzushT50Zhu+iIYXgEe1CxnRMn7ngsbV+MLrlpQ==",
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz",
|
||||
"integrity": "sha512-ubEkAaTfVZa+WwGhs5jbo5Xfqpeaybr/RvWzvFxRs4jfq16wH8l8Ty/QEEpINxll4xhuGfdMbipRyz5QZh9+FA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -337,13 +337,13 @@
|
||||
"node": "^14 || ^16 || >=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-tokenizer": "^2.2.3"
|
||||
"@csstools/css-tokenizer": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-tokenizer": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.3.tgz",
|
||||
"integrity": "sha512-pp//EvZ9dUmGuGtG1p+n17gTHEOqu9jO+FiCUjNN3BDmyhdA2Jq9QsVeR7K8/2QCK17HSsioPlTW9ZkzoWb3Lg==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.4.tgz",
|
||||
"integrity": "sha512-PuWRAewQLbDhGeTvFuq2oClaSCKPIBmHyIobCV39JHRYN0byDcUWJl5baPeNUcqrjtdMNqFooE0FGl31I3JOqw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -360,9 +360,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/media-query-list-parser": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.7.tgz",
|
||||
"integrity": "sha512-lHPKJDkPUECsyAvD60joYfDmp8UERYxHGkFfyLJFTVK/ERJe0sVlIFLXU5XFxdjNDTerp5L4KeaKG+Z5S94qxQ==",
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.9.tgz",
|
||||
"integrity": "sha512-qqGuFfbn4rUmyOB0u8CVISIp5FfJ5GAR3mBrZ9/TKndHakdnm6pY0L/fbLcpPnrzwCyyTEZl1nUcXAYHEWneTA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -378,8 +378,8 @@
|
||||
"node": "^14 || ^16 || >=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^2.5.0",
|
||||
"@csstools/css-tokenizer": "^2.2.3"
|
||||
"@csstools/css-parser-algorithms": "^2.6.1",
|
||||
"@csstools/css-tokenizer": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/postcss-cascade-layers": {
|
||||
@@ -1607,12 +1607,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -2469,9 +2470,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz",
|
||||
"integrity": "sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
|
||||
"integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"prettier-linter-helpers": "^1.0.0",
|
||||
@@ -2697,10 +2698,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -3217,6 +3219,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -3397,9 +3400,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged": {
|
||||
"version": "15.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.0.tgz",
|
||||
"integrity": "sha512-TFZzUEV00f+2YLaVPWBWGAMq7So6yQx+GG8YRMDeOEIf95Zn5RyiLMsEiX4KTNl9vq/w+NqRJkLA1kPIo15ufQ==",
|
||||
"version": "15.2.2",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz",
|
||||
"integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chalk": "5.3.0",
|
||||
@@ -3407,7 +3410,7 @@
|
||||
"debug": "4.3.4",
|
||||
"execa": "8.0.1",
|
||||
"lilconfig": "3.0.0",
|
||||
"listr2": "8.0.0",
|
||||
"listr2": "8.0.1",
|
||||
"micromatch": "4.0.5",
|
||||
"pidtree": "0.6.0",
|
||||
"string-argv": "0.3.2",
|
||||
@@ -3436,9 +3439,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/listr2": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.0.tgz",
|
||||
"integrity": "sha512-u8cusxAcyqAiQ2RhYvV7kRKNLgUvtObIbhOX2NCXqvp1UU32xIg5CT22ykS2TPKJXZWJwtK3IKLiqAGlGNE+Zg==",
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz",
|
||||
"integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cli-truncate": "^4.0.0",
|
||||
@@ -3815,9 +3818,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3825,6 +3828,7 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
@@ -4066,10 +4070,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@@ -4105,9 +4110,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
|
||||
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
|
||||
"version": "8.5.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
|
||||
"integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4123,10 +4128,11 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
"nanoid": "^3.3.8",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
@@ -4324,9 +4330,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-custom-media": {
|
||||
"version": "10.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.2.tgz",
|
||||
"integrity": "sha512-zcEFNRmDm2fZvTPdI1pIW3W//UruMcLosmMiCdpQnrCsTRzWlKQPYMa1ud9auL0BmrryKK1+JjIGn19K0UjO/w==",
|
||||
"version": "10.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.4.tgz",
|
||||
"integrity": "sha512-Ubs7O3wj2prghaKRa68VHBvuy3KnTQ0zbGwqDYY1mntxJD0QL2AeiAy+AMfl3HBedTCVr2IcFNktwty9YpSskA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4339,10 +4345,10 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@csstools/cascade-layer-name-parser": "^1.0.5",
|
||||
"@csstools/css-parser-algorithms": "^2.3.2",
|
||||
"@csstools/css-tokenizer": "^2.2.1",
|
||||
"@csstools/media-query-list-parser": "^2.1.5"
|
||||
"@csstools/cascade-layer-name-parser": "^1.0.9",
|
||||
"@csstools/css-parser-algorithms": "^2.6.1",
|
||||
"@csstools/css-tokenizer": "^2.2.4",
|
||||
"@csstools/media-query-list-parser": "^2.1.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14 || ^16 || >=18"
|
||||
@@ -5832,10 +5838,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -6410,6 +6417,7 @@
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "davegallant.github.io",
|
||||
"version": "0.1.0"
|
||||
}
|
33
package.json
@@ -1,37 +1,4 @@
|
||||
{
|
||||
"comments": {
|
||||
"dependencies": {
|
||||
"@tabler/icons": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"flexsearch": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"normalize.css": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"prism-themes": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"prismjs": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"simple-icons": "github.com/schnerring/hugo-mod-json-resume",
|
||||
"typeface-fira-code": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"typeface-roboto-slab": "github.com/davegallant/hugo-theme-gruvbox"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fullhuman/postcss-purgecss": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"cssnano": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"eslint": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"eslint-config-prettier": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"eslint-plugin-prettier": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"husky": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"lint-staged": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"markdownlint-cli": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"postcss": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"postcss-cli": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"postcss-custom-media": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"postcss-import": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"postcss-nesting": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"postcss-preset-env": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"postcss-url": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"prettier": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"prettier-plugin-go-template": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"stylelint": "github.com/davegallant/hugo-theme-gruvbox",
|
||||
"stylelint-prettier": "github.com/davegallant/hugo-theme-gruvbox"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@tabler/icons": "^2.44.0",
|
||||
"flexsearch": "^0.7.31",
|
||||
|
9
renovate.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"schedule": [
|
||||
"every weekend"
|
||||
],
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
]
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
div.code-toolbar{position:relative}div.code-toolbar>.toolbar{opacity:0;position:absolute;right:.2em;top:.3em;transition:opacity .3s ease-in-out;z-index:10}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:none;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{background:#f5f2f0;background:hsla(0,0%,88%,.2);border-radius:.5em;box-shadow:0 2px 0 0 rgba(0,0,0,.2);color:#bbb;font-size:.8em;padding:0 .5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;-webkit-text-decoration:none;text-decoration:none}.command-line-prompt{border-right:1px solid #999;display:block;float:left;font-size:100%;letter-spacing:-1px;margin-right:1em;pointer-events:none;text-align:right;-webkit-user-select:none;-moz-user-select:none;user-select:none}.command-line-prompt>span:before{content:" ";display:block;opacity:.7;padding-right:.8em}.command-line-prompt>span[data-user]:before{content:"[" attr(data-user) "@" attr(data-host) "] $"}.command-line-prompt>span[data-user=root]:before{content:"[" attr(data-user) "@" attr(data-host) "] #"}.command-line-prompt>span[data-prompt]:before{content:attr(data-prompt)}.command-line-prompt>span[data-continuation-prompt]:before{content:attr(data-continuation-prompt)}.command-line span.token.output{opacity:.7}
|
||||
|
||||
/*! MIT License | github.com/schnerring/hugo-theme-gruvbox */
|
@@ -1 +0,0 @@
|
||||
{"Target":"css/non-critical.84abe72d2e13924c4967767940c5a6e945550db473f64b927fa091dc697ce443b6d9ff3e2d8f30e3fadfa4142e7282ab04f189ff69c57a23df45dd9ecfb8dff5.css","MediaType":"text/css","Data":{"Integrity":"sha512-hKvnLS4TkkxJZ3Z5QMWm6UVVDbRz9kuSf6CR3Gl85EO22f8+LY8w4/rfpBQucoKrBPGJ/2nFeiPfRd2ez7jf9Q=="}}
|
@@ -1,169 +0,0 @@
|
||||
/*! purgecss start ignore */
|
||||
|
||||
/* Prism Plugins */
|
||||
|
||||
/*prismjs/plugins/toolbar/prism-toolbar.css*/
|
||||
|
||||
div.code-toolbar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: .3em;
|
||||
right: .2em;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
div.code-toolbar:hover > .toolbar {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Separate line b/c rules are thrown out if selector is invalid.
|
||||
IE11 and old Edge versions don't support :focus-within. */
|
||||
|
||||
div.code-toolbar:focus-within > .toolbar {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar > .toolbar-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar > .toolbar-item > a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar > .toolbar-item > button {
|
||||
background: none;
|
||||
border: 0;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
line-height: normal;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
-webkit-user-select: none; /* for button */
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar > .toolbar-item > a,
|
||||
div.code-toolbar > .toolbar > .toolbar-item > button,
|
||||
div.code-toolbar > .toolbar > .toolbar-item > span {
|
||||
color: #bbb;
|
||||
font-size: .8em;
|
||||
padding: 0 .5em;
|
||||
background: #f5f2f0;
|
||||
background: rgba(224, 224, 224, 0.2);
|
||||
box-shadow: 0 2px 0 0 rgba(0,0,0,0.2);
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar > .toolbar-item > a:hover,
|
||||
div.code-toolbar > .toolbar > .toolbar-item > a:focus,
|
||||
div.code-toolbar > .toolbar > .toolbar-item > button:hover,
|
||||
div.code-toolbar > .toolbar > .toolbar-item > button:focus,
|
||||
div.code-toolbar > .toolbar > .toolbar-item > span:hover,
|
||||
div.code-toolbar > .toolbar > .toolbar-item > span:focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*prismjs/plugins/command-line/prism-command-line.css*/
|
||||
|
||||
.command-line-prompt {
|
||||
border-right: 1px solid #999;
|
||||
display: block;
|
||||
float: left;
|
||||
font-size: 100%;
|
||||
letter-spacing: -1px;
|
||||
margin-right: 1em;
|
||||
pointer-events: none;
|
||||
text-align: right;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.command-line-prompt > span:before {
|
||||
opacity: 0.7;
|
||||
content: ' ';
|
||||
display: block;
|
||||
padding-right: 0.8em;
|
||||
}
|
||||
|
||||
.command-line-prompt > span[data-user]:before {
|
||||
content: "[" attr(data-user) "@" attr(data-host) "] $";
|
||||
}
|
||||
|
||||
.command-line-prompt > span[data-user="root"]:before {
|
||||
content: "[" attr(data-user) "@" attr(data-host) "] #";
|
||||
}
|
||||
|
||||
.command-line-prompt > span[data-prompt]:before {
|
||||
content: attr(data-prompt);
|
||||
}
|
||||
|
||||
.command-line-prompt > span[data-continuation-prompt]:before {
|
||||
content: attr(data-continuation-prompt);
|
||||
}
|
||||
|
||||
.command-line span.token.output {
|
||||
/* Make shell output lines a bit lighter to distinguish them from shell commands */
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Prism Font */
|
||||
|
||||
code,
|
||||
kbd,
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
font-family: var(--font-monospace);
|
||||
}
|
||||
|
||||
/*! purgecss end ignore */
|
||||
|
||||
/* Default license header for non-vendor CSS source code that follows */
|
||||
|
||||
/*! MIT License | github.com/schnerring/hugo-theme-gruvbox */
|
||||
|
||||
/*
|
||||
Bootstrap 5 breakpoints
|
||||
See: https://getbootstrap.com/docs/5.0/layout/breakpoints/#available-breakpoints
|
||||
*/
|
||||
|
||||
footer {
|
||||
align-items: center;
|
||||
color: var(--fg3);
|
||||
display: flex;
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 0.8rem;
|
||||
justify-content: center;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.pagination__button {
|
||||
color: var(--primary-alt);
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.pagination__button:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.pagination__button--next {
|
||||
margin-left: auto;
|
||||
}
|
@@ -1 +0,0 @@
|
||||
{"Target":"css/non-critical.css","MediaType":"text/css","Data":{}}
|
@@ -1 +0,0 @@
|
||||
{"Target":".","MediaType":"application/octet-stream","Data":{}}
|
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 21 KiB |