Latest project I've been working on is gathering up all odd bits and ends deployed on various PaaSes (Vercel, fly.io, Supabase, ++) and hosting them myself on a VPS. It's been a fun journey learning about setting up a VPS in a way that is reasonable secure to leave exposed to the Internet. One of the things I'm migrating is this site. And, since I'm trying to get rid of a bunch of my dependencies on various providers' free tiers I'm rewriting the site to get off Supabase. It's currently built with SvelteKit with a Supabase backend, but for ease of deployability I have staerted to port it to Go, backed by a SQLite database, and with simple html/template HTML templates that just renders the pages server-side and servers them to the user. I'm also dropping JS entirely (for now at least) and moving away from the SPA feel. This site is pretty basic anyways, and it feels overkill to use a complete fullstack JS framework for something like this. So far I've mostly ported the backend. There are still a few things missing from the backend, but nothing huge. Most of the frontend is also in place, but the styling is incomplete. As is common when rewriting, some parts are changed along the way. So far I've dropped OAuth, opting instead for managing my own users. I'm not going to have open sign-up, but will make it possible to request access (that will have to be manually reviewed by me). I've also dropped a couple of things related to user profile, etc., focusing instead on just getting the basics up and running. Next big step is doing a dump of the Supabase database and writing a migration script so I can get off Mr. Bones' wild ride
Okay, so this is pretty cool. This follow-up is written on an instance no longer served by Vercel and Supabase, but instead running a Go binary backed by a SQLite database on my own VPS! Today I've managed to do a bunch of stuff. First I finished up some styling issues with the rewrite. The old app used tailwind and postcss, so I had to rewrite that into plain CSS. There's still a few bits missing, but nothing on the public pages. Next, I added a bunch of structured logging using the slog package. Makes for easier debugging, and let's me see if there's any activity hitting the application. I've also got the access logs from Caddy, but I think it's nice to get a bit more info on how the app handles it. I also added signal handling of SIGINT and SIGTERM so that the http server waits to finish up any ongoing requests before shutting down. Should make deployments way more graceful. A big piece of the puzzle was migrating data off of Supabase. The old database was PostgreSQL, and used Supabase's row-level security for authorization. Luckily, I was the only one who ever wrote anything, so all posts belonged to me. That made the migration way easier, as I could just create a user in the new database, dump the threads and posts as JSON using row_to_json, and insert them all with the new user as the owner. I've also moved post and thread IDs from UUIDv4 to UUIDv7. Probably a premature optimization, but having them time-sortable on the primary key is a nice property in my opinion. That part was the most finicky, and required me to rip off some of the code from github.com/google/uuid and make it accept a time.Time and set the time-bits based on that (instead of current time). Finally, time came to get it deployed. I created an Ansible role for the app that includes setting up a app-specific user and group with no shell, creating all the necessary directories with the correct permissions, cloning the app repository onto the ansible host (not the remote) and building the binary for the correct OS and architecture. This binary is then pushed to the remote, along with a systemd unit file, and it makes sure to start/restart the service as necessary. The final playbook also includes the Caddy role to make sure that the Caddyfile is updated to let Caddy reverse-proxy requests to the app.
With all that done, the only thing remaining was switching the DNS and all should be good, right? Wrong. As I had configured Caddy before changing DNS, Caddy had started making requests to LetsEncrypt to get a SSL certificate for micronotal.com. But the ACME challenge failed each time, as micronotal.com was still pointing to Vercel. As this certificate dance is done on each reload, and I was doing a bit of trial-and-error-debugging, I quickly hit the LetsEncrypt rate limit on failed authorization requests. After changing the DNS and waiting an hour for the rate-limit window to open it was time to try again. And it failed, again. The error messages were a bit cryptic, so I went for the good ol' if at first it doesn't work, try again. And again. And a few more times. Maybe it works this time? No? Okay, one more time should do it. Unsurprisingly, it didn't magically resolve itself. Went a bit back and forth, but managed to find the cuplrit when I check the record for micronotal.com using dig, and then attempted to curl said IP and getting a response that definitely wasn't from a service of mine. Turns out I left out a '6' in the DNS record ¯\_(ツ)_/¯ Got that fixed, waited 5 mins for the TTL, waited another 5 minutes for the LE rate-limiter, and now we're here!
I think I've actually completed the migration now. I've fixed most if not all of the remaining cosmetic issues, and the site is super snappy which is really fun. I just deleted the old projects on Vercel and Supabase. Feels good!
I've been working on setting up Manjaro Linux (i3) lately, and it's overall been a good experience. Hitting some snags with customizing the terminal (mostly with powerline and nerd fonts), and all my tinkering has led to a bad state that seems hard to recover from. I'm going ahead and doing a clean install, but before that I want to note down the apps and packages I've installed that I actually want to reinstall. In no particular order: * bitwarden * bitwarden-cli * chromium * mpv * neovim * with lazy.nvim package manager * noto-fonts-emoji (if fonts aren't rendering properly in the browser) * py3status * docker * spotifyd * tmux * ufw Some of these might actually be there out of the box, so it's partially a list to remind me to configure them. Finally, a big fat note to self: remember to backup all config files before changing them! Should prevent the need for a clean install if anything breaks
To update, so far I've done: * backup i3 config, cp .i3/config .i3/config.bkup * set resolution, echo 'xrandr -s 5120x1440' >> .i3/config * update pacman mirrors, sudo pacman -f 30 * install brave browser, sudo pacman -S brave-browser I'm partially following these guides: * https://github.com/haraldwb/manjaro-i3wm-post-install-guide * https://jasoneckert.github.io/myblog/configuring-i3/
Changing default browser: * modify .i3/config, set bindsym $mod+F2 exec brave * modify .config/mimeapps.list (:%s/userapp-Pale Moon/brave-browser/g) * set $BROWSER in .profile, export BROWSER=/usr/bin/brave
On the issue of fonts in uxrvt: Seems like the default .Xresources config shipped with Manjaro sets the URxv.font without escaping spaces. The correct thing seems to be to escape them using \
Finally doing a sudo pacman -Syu to update installed packages
Fix for missing emojis: * pacman -S noto-fonts-emoji * add the following to /etc/fonts/conf.d/66-noto-emoji.conf: <?xml version="1.0"?> <!DOCTYPE fontconfig SYSTEM "fonts.dtd"> <fontconfig> <alias> <family>emoji</family> <prefer> <family>Noto Color Emoji</family> </prefer> </alias> </fontconfig>
TODO: get emojis working in the terminal
A few things done based on the aforementioned post-install guide: # Enable fstrim sudo systemctl status fstrim.timer sudo systemctl enable fstrim.timer # Set swappiness sudo vim /etc/sysctl.d/99-swappiness.conf vm.swappiness=10 # Enable ufw sudo ufw enable sudo systemctl status ufw sudo systemctl enable ufw
I think I found a working font and procedure for urxvt: sudo pacman -S ttf-0xproto-nerd edit ~/.Xresources: URxvt.font xfg:0xProto Nerd Font Mono:pixelsize=12 # no escaping xrdb -merge ~/.Xresources Close all running terminals to restart urxvt
AFter all this messing about with getting urxvt correctly rendering fonts, I think I'm going to swap to alacritty anyways (:
Install alacritty: $ sudo pacman -S alacritty Make it the default terminal: $ vim /usr/bin/terminal #!/bin/sh alacritty "$@" Create config file: $ mkdir -p ~/.config/alacritty/alacritty.toml Edit config file, set MesloLGS font: [font] size = 11 normal = { family = "MesloLGS NF", style = "Regular" } bold = { family = "MesloLGS NF", style = "Bold" } italic = { family = "MesloLGS NF", style = "Italic" } bold_italic = { family = "MesloLGS NF", style = "Italic" } Change default shell: $ chsh -s /usr/bin/zsh Configure poweline $ p10k configure
pacman -S mpv
I think I've been fairly successful in setting up neovim with the lazy.nvim package manager. I've got this config structure: $ tree .config/nvim .config/nvim ├── init.lua ├── lazy-lock.json └── lua ├── config │   └── lazy.lua └── plugins ├── cmp.lua ├── init.lua ├── lsp.lua └── treesitter.lua And the follwing plugins installed: cmp-buffer, cmp-cmdline, cmp-nvim-lsp, cmp-path, cmp_luasnip, LuaSnip, nvim-cmp, nvim-lspconfig, nvim-treesitter, nvim-ts-autotag, tokyonight.nvim
Today I went ahead and installed oh-my-zsh. Plugins enabled: git, asdf, dotenv
Embarking on a new adventure, setting up ansible to automate my VPS configuration. I want to manage my python versions with asdf, and I used the zsh asdf plugin to easily install asdf. Just clone the repo and enable the plugin. Next step is installing some dependencies that (I think?) are necessary for python, or at the very least recommended by asdf-python, though they link to pyenv docs for some reason, not digging in to that now: pacman -S --needed base-devel openssl zlib xz tk asdf plugin-add python asdf install python 3.12.6 pacman -S python-pipx pipx install poetry The rest will be put in version control, so I won't bother writing everything down in this thread
It's interesting to see this site still up. I had totally forgotten about it.
Not to mention that it's still working
A previous colleague of mine had a saying, 'Computers don't get tired'. By that he meant developer time is more precious than compute time. In modern day web development, it's generally better to push sub-optimal code that brings features than optimizing until your face turns blue. Would be interesting to figure out how long an optimized piece of code has to run to make up the time (and money!) spent on optimizing it.
If I find some time in the coming days, if like to revamp the profile page. Box in the user info section, add a header before the threads, maybe remove the username link from the threads as we’re already on the page it links to, increase margin between navbar and user info section, etc. Those are just things from the top of my head.
Made most of the aforementioned changes: * Added margins to user info section * Added border to user info section * Added header before thread/note history * Also tweaked padding and margins on threads and posts to separate things a little more and give a little breathing room
Because this is a hobby project, and everything is a nice-to-have, I'll jump on improving the user profile pages instead of ticking anything else of the todo-list. * First of all, logged-in users should se a link in the navbar to their account page as well as their profile. Right now it says 'profile' but links to the account page * Second, user profiles located at /u/<user> should include a list of threads by that user below a summary of user details
Both changes are now deployed. Need to fix ordering of threads on profiles to display latest first
Added some mobile nav bar fixes as well. Feels a bit hacky to add a on:click to all anchor tags on the mobile nav, there’s probably a better way to do it.
Considering rewriting micronotal in SvelteKit... I'm not a frontend developer by any means, and React's useEffect, useCallback, etc. keeps throwing me for a loop. SvelteKit seems to me to be more straightforward, and reactive values/statements are easier to understand when you have automatic dependency detection. As I have barely any experience with any frontend frameworks, I might of course be entirely wrong, and moving to SvelteKit won't magically provide a better developer experience. Pretty sure I'm going to go through with it, though.
So, the rewrite is underway. I've implemented auth, moved some pages and routes around to make things cleaner, and got a frontpage full of threads rendering almost identically to the Next.js implementation. Still a couple of pages left to go before I'm done.
Didn’t get much further today, but I have to say: SvelteKit is so far a much more pleasant experience than Next.js. Way easier to understand what happens client side and what happens server side. Data fetching is a breeze. Automatic typing of load functions. And everything is way snappier! The list goes on.
Writing this from the preview deployment of micronotal written in SvelteKit. Have to say, I'm blown away by how easy SK is to work with compared to Next.
Looking through things, I see that I've got some broken links to profiles now, but that's an easy fix
Adding to the todo-list: * I really should add a max length to post content * I’m also thinking I should do something about the display of posts with a large number of newlines, but that’s a lot trickier to get right. I don’t want to limit creativity; I think posts should be allowed to contain whatever formatting you want, but it can be detrimental to the user experience if posts and threads can offset others by just containing a ton of whitespace.
One possible solution that allows whatever post content you want is crowd-sourced moderation through votes. At scale, this can become an unwieldy tool requiring safeguards against vote manipulation.
Perhaps disabling email login and only supporting social login is part of the answer to prevent botting. Essentially piggybacking on the abuse prevention systems of the large companies.
Unrelated, but also on the todo-list: * use nextjs route groups to deduplicate some layout code
Updated favicon.ico, woo! Current todo-list: * SSR single-thread pages * Decrease horizontal padding on small screens
Done and done ✓
Still zooming in on textarea focus on small screens, will have to investigate other options. Maybe a different max width?
Summer salad: - 1/4 watermelon, diced - 1 pomegranate - feta cheese, diced - handful of cherry tomatoes - handful of basil leaves - handful of mint leaves - baby leaf greens - olive oil, salt, pepper to taste