Refactoring to use database for page content, adding Garden

This commit is contained in:
Awstin 2024-07-27 13:20:31 -04:00
parent 649a09c1a8
commit 469b839993
12 changed files with 105 additions and 477 deletions

View file

@ -1,9 +1,34 @@
use axum::{routing::get, Router};
use super::blog::get_articles_as_links_list;
use crate::html::AppState;
use axum::{response::IntoResponse, routing::get, Extension, Router};
pub fn get_router() -> Router {
Router::new().route("/hello", get(hello_from_the_server))
Router::new()
.route("/hello", get(hello_from_the_server))
.route("/articles", get(blogs))
.route("/recentarticles", get(recent_blogs))
}
async fn hello_from_the_server() -> &'static str {
"Hello!"
}
async fn blogs(state: Extension<AppState>) -> impl IntoResponse {
let db_pool = &state.db;
let article_list: Vec<String> = get_articles_as_links_list(db_pool)
.await
.expect("couldn't get articles");
article_list.join("\n")
}
async fn recent_blogs(state: Extension<AppState>) -> impl IntoResponse {
let db_pool = &state.db;
let article_list: Vec<String> = get_articles_as_links_list(db_pool)
.await
.expect("couldn't get articles");
let (article_head, _) = article_list.split_at(5);
article_head.join("\n")
}

View file

@ -6,7 +6,7 @@ use axum::{
use sqlx::PgPool;
use std::{collections::HashMap, error::Error};
use super::{root::AppState, ArticleTemplate, HtmlTemplate};
use super::{get_page, AppState, ArticleTemplate, HtmlTemplate};
pub fn get_router() -> Router {
Router::new()
@ -14,21 +14,8 @@ pub fn get_router() -> Router {
.route("/:article", get(article))
}
async fn blog(state: Extension<AppState>) -> impl IntoResponse {
let db_pool = &state.db;
let list: Vec<String> = get_articles_as_links_list(db_pool)
.await
.expect("couldn't get articles");
let template = BlogTemplate {
article_list: list.join("\n"),
};
HtmlTemplate(template)
}
#[derive(Template)]
#[template(path = "blog.html")]
struct BlogTemplate {
article_list: String,
async fn blog(state: Extension<AppState>) -> Result<impl IntoResponse, StatusCode> {
get_page(&state.db, "blog").await
}
#[derive(Template)]

24
src/html/garden.rs Normal file
View file

@ -0,0 +1,24 @@
use axum::{
extract::{Extension, Path}, http::StatusCode, response::IntoResponse, routing::{get, Router}
};
use std::collections::HashMap;
use super::{get_page, AppState};
pub fn get_router() -> Router {
Router::new()
.route("/", get(garden))
.route("/:page", get(page))
}
async fn garden(state: Extension<AppState>) -> Result<impl IntoResponse, StatusCode> {
get_page(&state.db, "garden").await
}
async fn page(
state: Extension<AppState>,
Path(params): Path<HashMap<String, String>>,
) -> Result<impl IntoResponse, StatusCode> {
let page_id: &String = params.get("page").unwrap();
get_page(&state.db, page_id).await
}

View file

@ -1,12 +1,20 @@
use askama::Template;
use achubb_database::data::page::Page;
use axum::{
http::StatusCode,
response::{Html, IntoResponse, Response},
};
pub mod root;
pub mod blog;
pub mod projects;
use sqlx::PgPool;
pub mod api;
pub mod blog;
pub mod garden;
pub mod root;
#[derive(Clone)]
pub struct AppState {
pub db: PgPool,
}
/// A wrapper type that we'll use to encapsulate HTML parsed by askama into valid HTML for axum to serve.
pub struct HtmlTemplate<T>(pub T);
@ -37,3 +45,23 @@ pub struct ArticleTemplate {
footer: String,
content: String,
}
#[derive(Template)]
#[template(path = "page.html")]
pub struct PageTemplate {
content: String,
}
pub async fn get_page(db: &PgPool, path: &str) -> Result<impl IntoResponse, StatusCode> {
let reference: String = path.to_string();
let page: Page = match Page::read_by_reference(db, &reference).await {
Ok(a) => *a,
Err(_) => return Err(StatusCode::NOT_FOUND),
};
let template = PageTemplate {
content: page.content,
};
Ok(HtmlTemplate(template))
}

View file

@ -1,6 +1,6 @@
use crate::html::{api, blog, projects, HtmlTemplate};
use askama::Template;
use crate::html::{api, blog, AppState};
use axum::{
http::StatusCode,
response::{IntoResponse, Redirect},
routing::{get, Router},
Extension,
@ -8,12 +8,7 @@ use axum::{
use sqlx::PgPool;
use tower_http::services::ServeDir;
use super::{blog::get_articles_as_links_list, projects::get_projects_as_links_list};
#[derive(Clone)]
pub struct AppState {
pub db: PgPool,
}
use super::{garden, get_page};
pub fn get_router(pool: PgPool) -> Router {
let assets_path = std::env::current_dir().unwrap();
@ -21,7 +16,7 @@ pub fn get_router(pool: PgPool) -> Router {
Router::new()
.nest("/api", api::get_router())
.nest("/blog", blog::get_router())
.nest("/projects", projects::get_router())
.nest("/garden", garden::get_router())
.nest_service(
"/assets",
ServeDir::new(format!("{}/assets", assets_path.to_str().unwrap())),
@ -38,66 +33,22 @@ pub fn get_router(pool: PgPool) -> Router {
.layer(Extension(state))
}
async fn home(state: Extension<AppState>) -> impl IntoResponse {
let db_pool = &state.db;
let article_list: Vec<String> = get_articles_as_links_list(db_pool)
.await
.expect("couldn't get articles");
let (article_head, _) = article_list.split_at(5);
let project_list: Vec<String> = get_projects_as_links_list(db_pool)
.await
.expect("Couldn't get projects");
let (project_head, _) = project_list.split_at(5);
let template = HomeTemplate {
recent_blogs: article_head.join("\n"),
recent_projects: project_head.join("\n"),
};
HtmlTemplate(template)
async fn home(state: Extension<AppState>) -> Result<impl IntoResponse, StatusCode> {
get_page(&state.db, "home").await
}
#[derive(Template)]
#[template(path = "home.html")]
struct HomeTemplate {
recent_blogs: String,
recent_projects: String,
async fn now(state: Extension<AppState>) -> Result<impl IntoResponse, StatusCode> {
get_page(&state.db, "now").await
}
async fn now() -> impl IntoResponse {
let template = NowTemplate {};
HtmlTemplate(template)
async fn about(state: Extension<AppState>) -> Result<impl IntoResponse, StatusCode> {
get_page(&state.db, "about").await
}
#[derive(Template)]
#[template(path = "now.html")]
struct NowTemplate {}
async fn about() -> impl IntoResponse {
let template = AboutTemplate {};
HtmlTemplate(template)
async fn contact(state: Extension<AppState>) -> Result<impl IntoResponse, StatusCode> {
get_page(&state.db, "contact").await
}
#[derive(Template)]
#[template(path = "about.html")]
struct AboutTemplate {}
async fn contact() -> impl IntoResponse {
let template = ContactTemplate {};
HtmlTemplate(template)
async fn uses(state: Extension<AppState>) -> Result<impl IntoResponse, StatusCode> {
get_page(&state.db, "uses").await
}
#[derive(Template)]
#[template(path = "contact.html")]
struct ContactTemplate {}
async fn uses() -> impl IntoResponse {
let template = UsesTemplate {};
HtmlTemplate(template)
}
#[derive(Template)]
#[template(path = "uses.html")]
struct UsesTemplate {}

View file

@ -1,60 +0,0 @@
<!-- prettier-ignore -->
{% extends "base.html" %}
{% block content %}
<h2>General</h2>
<p>
My greatest joy in life is learning new things.
Early on I leaned almost exclusively toward the sciences.
I still trend that way but my interests have broadened considerably in recent years.
Philosophy, science, business, programming, religion, psychology, anything that helps me understand the world better.
My specific fascination for the last four years has been computers.
They are an unbelievably amazing intersection of so many sciences both physically and in the logic of programming.
They also provide the perfect platform for learning and creating.
</p>
<p>
A close second is making things and tinkering.
There is a satisfaction that comes from making or changing something with your own hands that is unique.
That is part of the reason that I switched my website from WordPress to something written by hand.
Even if I can't make something as good as what I could purchase, it is still mine.
I know it intimately and can build or tweak whatever it is to fit my needs exactly.
</p>
<p>
I read extensively and while my reading list is only getting longer I am always looking for new book recommendations.
I listen to a lot of music, almost all styles these days.
With a preference for instumentally complex music.
</p>
<p>
I am quite introverted and enjoy interacting with others one on one.
I find that my focus on learning things deeply extends to how I interact.
One long deep conversation is more pleasant to me than a dozen short ones.
I am more interested in talking about each other and our own interests than current events.
It does make interactions more work but personally I find it very fulfilling.
</p>
<p>
I have recently discovered just how important independence is to me.
I think that removing the worry of money from day to day life has allowed me to focus on what is important to me in a broader sense.
I think that it is too easy to give up control of things that are important.
To rely on external things that I have no control over.
</p>
<p>
I have been working lately to slowly move my digital life away from cloud services.
Hosting my own calendar, contacts, email, backups.
There is a sense of peace with that.
Knowing that I have control over things that are important to me.
It a stress that I had gotten used to and barely noticed.
Like not sleeping quite enough for a long time.
Didn't notice the effect until it was gone.
</p>
<p>
I would like to do the same with more of life eventually.
Work, food, electricity.
Try to make and fix things instead of buying or replacing them.
That thought feels right.
</p>
<p>
It and other feelings about the best way for me to live are things that I am constantly exploring.
Trying to live more in line with what feels right internally.
A wonderful exploration and adventure.
</p>
{% endblock %}

View file

@ -4,6 +4,7 @@
<head>
<link href="/assets/main.css" rel="stylesheet" />
<link rel="shortcut icon" href="/assets/favicon.ico">
<script src="/assets/htmx.min.js"></script>
<title>Awstin</title>
{% block head %}{% endblock %}
</head>
@ -35,13 +36,13 @@
</svg>
</a>
</li>
<li><a id="projects" href="/projects">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>Projects</title>
<li><a id="garden" href="/garden">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#b8bb26">
<title>Garden</title>
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path d="M13 15H16" stroke="#d3869b" stroke-width="2" stroke-linecap="round"></path> <path d="M8 15L10.5 12.5V12.5C10.7761 12.2239 10.7761 11.7761 10.5 11.5V11.5L8 9" stroke="#d3869b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M3 8C3 6.11438 3 5.17157 3.58579 4.58579C4.17157 4 5.11438 4 7 4H12H17C18.8856 4 19.8284 4 20.4142 4.58579C21 5.17157 21 6.11438 21 8V12V16C21 17.8856 21 18.8284 20.4142 19.4142C19.8284 20 18.8856 20 17 20H12H7C5.11438 20 4.17157 20 3.58579 19.4142C3 18.8284 3 17.8856 3 16V12V8Z" stroke="#d3869b" stroke-width="2" stroke-linejoin="round"></path>
<path d="M10.3412 9.5C9.53284 8.96254 9.00008 8.04349 9.00008 7C9.00008 5.34315 10.3432 4 12.0001 4C13.5954 4 14.8999 5.24523 14.9946 6.81674M9.00558 12.1867C8.13595 12.618 7.07365 12.6199 6.16996 12.0981C4.73509 11.2697 4.24346 9.43495 5.07189 8.00007C5.86955 6.61848 7.60018 6.11139 9.0085 6.81513M10.6644 14.6867C10.6032 15.6555 10.0736 16.5764 9.16993 17.0981C7.73506 17.9266 5.90029 17.4349 5.07186 16.0001C4.2742 14.6185 4.70036 12.8662 6.01398 11.9984M13.3357 9.31328C13.397 8.3445 13.9265 7.4236 14.8302 6.90186C16.2651 6.07343 18.0998 6.56505 18.9283 7.99993C19.7259 9.38152 19.2998 11.1338 17.9862 12.0016M14.9946 11.8133C15.8642 11.382 16.9265 11.3801 17.8302 11.9019C19.2651 12.7303 19.7567 14.5651 18.9283 15.9999C18.1306 17.3815 16.4 17.8886 14.9917 17.1849M13.659 14.5C14.4674 15.0375 15.0001 15.9565 15.0001 17C15.0001 18.6569 13.657 20 12.0001 20C10.4048 20 9.10032 18.7548 9.00562 17.1833M15.0001 12C15.0001 13.6569 13.6569 15 12.0001 15C10.3432 15 9.00008 13.6569 9.00008 12C9.00008 10.3431 10.3432 9 12.0001 9C13.6569 9 15.0001 10.3431 15.0001 12Z" stroke="#b8bb26" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</svg>
</a>

View file

@ -1,9 +0,0 @@
<!-- prettier-ignore -->
{% extends "base.html" %}
{% block content %}
<p>
I am always happy to meet new people.
I can be contacted by email at <a href="mailto:awstin@achubb.com">awstin@achubb.com</a>.
</p>
{% endblock %}

View file

@ -1,71 +0,0 @@
<!-- prettier-ignore -->
{% extends "base.html" %}
{% block content %}
<section id="about">
<h1>Awstin Chubb</h1>
<h2>About me</h2>
<h3>Short version</h3>
<p>
I have been a gymnastics coach, hot tub technician, carpenter (saunas), chemist, and now software developer work wise.
I am an athlete, tinkerer, and lifelong student of all things.
</p>
<p>
I have a deep and abiding interest in how the world works and how best I can live within it.
Over the last year I have really discovered the importance of independence to me.
Of having minimal reliance on external systems that I don't have any control over.
Much of my time is slowly working to bring more of this into my life.
</p>
<p>
Born in Ottawa and living in Toronto, Canada.
</p>
<p>
If you are interested in reading more see my <a href="/about">about</a> page.
</p>
</section><br>
<section id="now">
<a href="/now">
<h2>What am I up to?</h2>
</a>
</section><br>
<section id="blog">
<h2>Blog</h2>
<h3>Most Recent</h3>
<ul class="no-bul">
{{recent_blogs|safe}}
</ul>
<p>
For the full list see my <a href="/blog">Blog.</a>
</p>
</section><br>
<section id="projects">
<h2>Projects</h2>
<h3>Recent</h3>
<ul class="no-bul">
{{recent_projects|safe}}
</ul>
<p>
See my <a href="/projects">projects</a> page for all my projects.
</p>
</section><br>
<section id="uses">
<h2>Uses</h2>
<p>
Some things that I <a href="/uses">use</a> on a regular basis if not daily.
</p>
</section><br>
<section id="contact">
<h2>Contact Me</h2>
<p>
Here is my <a href="/contact">contact</a> information.
</p>
</section><br>
{% endblock %}

View file

@ -1,82 +0,0 @@
<!-- prettier-ignore -->
{% extends "base.html" %}
{% block content %}
<p>
Last updated: 2024-07-11
</p>
<h2>Work</h2>
<p>
Still in Toronto, still working in software at Amazon.
My team writes tools to help automate and test new machine setups.
Just got a promotion from L4 (entry level software engineer) to L5 (regular software engineer).
Was not something that I chased but there is an "up or out" when in the L4 position so it is really nice to have that worry off my mind.
</p>
<h2>Life</h2>
<p>
I am engaged and living with the most wonderful woman I have ever met.
Planning our wedding (slowly bit by bit) for next spring.
</p>
<p>
My brother is getting married soon, so we are preparing to travel back to my hometown for that in a week.
He is the happiest I have ever seen him and I am so grateful for that.
</p>
<p>
Actually planning for the long term financially now that I am lucky enough to have a job that leaves me with money to save and invest after the bills are payed.
It still feels new to have that cushion and to plan long term, definitely enjoying it.
</p>
<h2>Brazilian Jiu-Jitsu</h2>
<p>
Got my blue belt just before the new year.
Still training as much as I can.
The team here at GB Toronto has become an external family.
I am so grateful for the community.
</p>
<p>
Competiton is this Saturday.
I have made the weight, however they opened the weigh in on Friday evening which is unusual.
Means that I can cut water weight aggressively and re-hydrate before the first match so I don't feel terrible.
So putting a little of it back on.
</p>
<p>
We have a lot of people from the school going.
It is going to be a very fun day.
</p>
<h2>Learning</h2>
<p>
I am working to learn Japanese and Portuguese on Duolingo.
I love the program and it is 100% worth paying for, but the fact that even when paying it keeps asking for ratings, putting widgets on my home screen, or turning on notifications bothers me.
You already have my money now please don't bother me.
Finding the lessons really helpful.
</p>
<p>
I started studying for the CompTIA ITF+ certification test but that has fallen off in the last little bit.
I have been learning hardware and networking playing with my home network and a small homelab consisting of my NAS and a few raspberry pis.
Just not very stoked on going for certifications and much prefer to learn this stuff by just playing around with it than reading textbooks and doing practice questions.
</p>
<h2>Tinkering</h2>
<p>
Have just finished setting up some self hosted software.
Firefly for budgeting and personal finance.
Pihole for local network addblocking and as a DNS server.
Syncthing to keep all my devices synchronized.
Slowly working to have more and more of my digital life hosted myself, and it feels good to have control over these things even though it is more work.
</p>
<p>
This website is the main thing that I have been working with.
I rebuilt it using HTMX and a Rust backend.
Added a <a href="/uses">uses page</a> recently with just a list of the common things that I use every day.
Going to add links and other curated stuff next.
I love these pages on other personal websites and want to contribute to that.
</p>
<p>
I am looking at restructuring the site into more of a <a href="https://hapgood.us/2015/10/17/the-garden-and-the-stream-a-technopastoral/">garden than a stream.</a>
It is a structure that I find much more interesting and fits with how I want to build much more.
Treating most things as timeless and growing as opposed to a strict timeline.
It is how I keep most of my personal notes, dates more of as reference than things needing to be in order.
Will see how that works.
</p>
<p>
Awstin
</p>
{% endblock %}

View file

@ -2,7 +2,5 @@
{% extends "base.html" %}
{% block content %}
<ul class="no-bul">
{{project_list|safe}}
</ul>
{{content|safe}}
{% endblock %}

View file

@ -1,164 +0,0 @@
<!-- prettier-ignore -->
{% extends "base.html" %}
{% block content %}
<h2>Things I use</h2>
<h3>Analog stuff</h3>
<ul>
<li>
<b>Bike:</b> Trek... something. Hybrid. I have had it for almost 15 years and it shows no signs of slowing down as long as I periodically replace some parts.
</li>
<li>
<b>Bag:</b> <a href="https://kifaru.net/en-ca/products/checkpoint">Kifaru Checkpoint</a>. Never thought that an expensive backpack was worth the investment. I was so wrong. I tend to fill my bag pretty full for daily use and the fitted backplate makes it so that I notice almost no difference carrying it no matter what I have packed.
</li>
<li>
<b>Chair:</b> <a href="https://www.ergocentric.com/product-category/tcentric-hybrid-task-chair/?filter_series=tcentric-hybrid">ErgoCentric t-centric hybrid</a>. Canadian ergonomic chair company. Was able to go and try it out in the showroom and get fitted for the chair. Fantastic and fully worth the price, especially with how much time I spend sitting in it.
</li>
<li>
<b>Home Fitness:</b> <a href="https://treadmillfactory.ca/collections/cast-iron-kettlebells">Extreme Monkey Kettlebells</a>, yoga mat blocks and straps, <a href="https://www.ironmind.com/product-info/ironmind-grippers/captains-of-crush-grippers/">Captains of crush grippers</a>, various therabands and other elastics.
</li>
</ul>
<h3>Hardware</h3>
<ul>
<li>
<b>Desktop:</b> My desktop is one that I built myself quite a while ago. AMD RX580 GPU and recently upgraded the cpu and memory to Ryzen 5 4500 and 32G of Crucial DDR4. 2.5Gb network card
</li>
<li>
<b>Network Attached Storage:</b> Built it myself with a Jonsbo N1 mini ITX case, 32G RAM and a Ryzen 3 4100 cpu. 2 12TB Seagate Ironwolf Hard drives and a 500Gb NVME SSD. 2.5Gb network card
</li>
<li>
<b>Laptop:</b> 2018 Asus Zenbook 3, have replaced the battery in it so is still going strong. If I was to buy a laptop today I would certianly go for a <a href="https://frame.work/ca/en">Framework</a>. Really believe in their mission and value being able to fix and upgrade my stuff.
</li>
<li>
<b>Phone:</b> Samsung Galaxy A20, got it used. Gets the job done.
</li>
<li>
<b>Keyboard:</b> Built a <a href="https://github.com/davidphilipbarr/Sweep">Ferriss Sweep</a>, looking at building another so that I can keep one at work and one at home. Has made such a difference to my comfort when typing. Kalih Choc blue switches.
</li>
<li>
<b>Mouse:</b> SteelSeries something, currently using a wired mouse, have a logitech MxMaster and logitech MxAnywhere but the logitech univesal receiver dongle does not work through the KVM so don't use them at home. On my list to see if I can figure that out but not a high priority as I don't use my mouse all that much.
</li>
<li>
<b><a href="https://www.raspberrypi.com/">Raspberry Pis</a>:</b> I have 2 raspberry pis in use at the moment and 1 more that I have yet to decide what to do with.
<ul>
<li>
2GB Pi4 runs my vpn, dns, network wide ad blocking
</li>
<li>
8GB Pi4 runs some of my own scripts and programs, will also be hosting a home website when I get around to writing that
</li>
<li>
8GB Pi5 still unused until I both decide what to use it for and take the time to get it set up
</li>
</ul>
</li>
<li>
<b>General gadgets:</b>
<ul>
<li>
<a href="https://www.amazon.ca/gp/product/B081251PBW">KVM</a> to switch between desktop and work computer when I am working from home
</li>
<li>
<a href="https://www.amazon.ca/gp/product/B08XWK4HNT">5 port 2.5Gb switch</a>, and <a href="https://www.amazon.ca/gp/product/B00A128S24">5 port 1Gb switch</a> so that all of my devices that can be hard wired are. The 2.5Gb switch is to have that bandwidth between my desktop and NAS for fast file transfer.
</li>
</ul>
</li>
<li>
<b>E-reader:</b> <a href="https://www.kobo.com/">Kobo</a> touch 2.0, reliable, though the screen has taken some damage over the years, might be time to replace. I like the Kobo because of they support epub format which is open and I think should be the default for all ebooks.
</li>
</ul>
<h3>Software</h3>
<ul>
<li>
<b>Writing/Programming:</b> <a href="https://neovim.io/">Neovim</a> with a fair few plugins. <a href="https://github.com/nvim-telescope/telescope.nvim">Telescope</a> and <a href="https://github.com/ThePrimeagen/harpoon/tree/harpoon2">Harpoon</a> for file navigation, <a href="https://en.wikipedia.org/wiki/Language_Server_Protocol">lsp</a> and <a href="https://github.com/nvim-treesitter/nvim-treesitter">Treesitter</a> for coding, <a href="https://github.com/folke/zen-mode.nvim">Zenmode</a> for minimal distraction writing.
</li>
<li>
<b>Finance:</b> <a href="https://www.firefly-iii.org/">Firefly</a> hosted on my NAS to budget and track my finances. <a href="https://github.com/victorbalssa/abacus">Abacus</a> on my phone to interact with Firefly on the go.
</li>
<li>
<b>Media:</b>
<ul>
<li>
<a href="https://jellyfin.org/">Jellyfin</a> hosted on my NAS for movies, shows, and music/audiobooks
</li>
<li>
<a href="https://github.com/jmshrv/finamp">Finamp</a> on my phone to stream music/audiobooks from Jellyfin
</li>
<li>
<a href="https://calibre-ebook.com/">Calibre</a> for managing my ebook library
</li>
<li>
<a href="https://lzone.de/liferea">Liferea</a> RSS feed reader for blogs and webcomics that I follow
</li>
<li>
<a href="https://freetubeapp.io/">Freetube</a> for watching Youtube without sign in, tracking etc.
</li>
</ul>
</li>
<li>
<b>Synchronization:</b> <a href="https://syncthing.net/">SyncThing</a> hosted on my NAS to keep files synced on my laptop, desktop, and phone. Lets my laptop and desktop function as essentially one computer
</li>
<li>
<b>Browser:</b> <a href="https://www.mozilla.org/en-CA/firefox/new/">Firefox</a> browser with <a href="https://ublockorigin.com/">Ublock Origin</a>, <a href="https://privacybadger.org/">Privacy Badger</a>, and <a href="https://lydell.github.io/LinkHints/">Link hints</a>, plugins
</li>
<li>
<b>Operating Systems:</b>
<ul>
<li>
Desktop, laptop, and cloud server: <a href="https://archlinux.org/">Arch linux</a>
</li>
<li>
NAS: <a href="https://www.truenas.com/truenas-scale/">TrueNas Scale</a>
</li>
</ul>
</li>
<li>
<b>Window Manager:</b> <a href="https://i3wm.org/">i3 tiling window manager</a>
</li>
<li>
<b>Terminal:</b> <a href="https://github.com/alacritty/alacritty">Alacritty</a>
</li>
<li>
<b>Shell:</b> <a href="https://fishshell.com/">Fish</a>
</li>
<li>
<b>Terminal Multiplexer:</b> <a href="https://zellij.dev/">Zellij</a>
</li>
<li>
<b>Programming Languages:</b> <a href="https://www.rust-lang.org/">Rust</a>
</li>
<li>
<b>Memory:</b> <a href="https://apps.ankiweb.net/">Anki</a> flashcard app
</li>
</ul>
<h3>Services</h3>
<p>
I try to pay for the services that I use. I find that "free" services don't often respect the user very much and have been getting worse pretty quickly lately.
</p>
<ul>
<li>
<b>Password Manager:</b> <a href="https://1password.com/">1Password</a>
</li>
<li>
<b>Music:</b> <a href="https://spotify.com/">Spotify</a>
</li>
<li>
<b>Server:</b> <a href="https://www.vultr.com/">Vultr</a> minimal size instance
</li>
<li>
<b>Domain Registrar:</b> <a href="https://porkbun.com/">PorkBun</a>
</li>
<li>
<b>Backups:</b> <a href="https://www.backblaze.com/">Backblaze</a>, encrypted locally before uploading, periodic backup from my NAS.
</li>
<li>
<b>Email:</b> <a href="https://www.fastmail.com/">FastMail</>
</li>
<li>
<b>Search:</b> <a href="https://kagi.com/">Kagi</a> payed alternative to Google. So much different, any time that I use Google on another device I am reminded how bad the search has gotten
</li>
<li>
<b>Language Learning:</b> <a href="https://www.duolingo.com/learn">Duolingo</a>
</li>
</ul>
{% endblock %}