I have wanted to redesign my website for a while now. In fact, I have wanted to redesign my website ever since I saw Nathan Wentworth’s website. If you haven’t seen it before, check it out. To this day it remains my favourite personal website on the internet.

My favourite feature on his website has to be the regularly updating feeds; music from https://last.fm, recent posts from https://mastodon.social, and recent bookmarks from https://pinboard.in.

I looked into the codebase for the website and saw that it was being generated using Jekyll. Heartened by the idea that such a website could be achieved using a static website generator, I opened up a new Hugo project (my previous website was also built on Hugo), found a theme that had the right kind of vibe and proceeded to hack away.

I wantonly mixed and matched gohtml partials and shortcodes with md files liberally sprinkled with raw html snippets to attain the right balance of rows and columns. Slowly, the kind of high-information-density design that I have come to love (and contemporary designers love to hate) started to come together.

A few hours later, it was time to bring in my data.

Back in 2020, I grew so frustrated with bookmarking services that I decided to create my own. My big issue with all the incumbents was simple: I didn’t want to bookmark URLs, I wanted to bookmark content.

Whether that content was a quote from an article, a highlight from my Kindle, or a comment on HackerNews, Reddit, or anywhere else, it was the content that I cared about, not the URLs and titles, which are really just pieces of metadata.

Fast-forward to 2023, and I have a number of highlights feeds that I curate on different topics which people either subscribe to using RSS readers or check in on using a browser.

This was the data that I really wanted to show on my new website.

Hugo makes adding this sort of functionality surprisingly simple with the use of user-definable shortcodes.

Here is the shortcode I wrote to display content from my Notado RSS feeds to my website:

// layouts/shortcodes/notado.html
{{ if .Get "url" }}
  {{ $url := .Get "url" }}
  {{ $limit := .Get "limit" }}

  {{ with resources.GetRemote $url | transform.Unmarshal }}
    {{ range first $limit .channel.item }}
      <div style="font-size: small; padding: 10px; background-color: #373641; word-wrap: break-word;">
        <p>
          {{ .description | safeHTML | truncate 300 }}
        </p>
        <p>(<a href="{{ .link }}">View</a>)</p>
      </div>
      <br />
    {{ end }}
  {{ end }}

{{ end }}

This shortcode checks that a url argument has been given, then loops through all the RSS feed items returned by the given URL and displays them on a card with some padding and a link back to the original content, limiting the total number of items rendered using the limit argument.

This shortcode can then be called in the Markdown file for the main page of the website to render the content:

// Remove the spaces between the curly and angle braces
{{ <notado url="https://notado.app/rss/jado/software-development/feed.rss" limit="3"> }}

At this point, it’s possible to embed content from my highlights feeds and this is already a big win! The final step that remains is to have this update automatically; not just every time I push a new commit to the website repository.

My website is currently hosted on Cloudflare Pages, and Cloudflare provides an example of how to use a mixture of JavaScript Workers and Cron Triggers to rebuild and redeploy a Cloudflare Pages website every hour.

const endpoint =
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/{project_name}/deployments";

export default {
  async scheduled(_, env) {
    const init = {
      method: "POST",
      headers: {
        "Content-Type": "application/json;charset=UTF-8",
        Authorization: env.API_TOKEN,
      },
    };

    await fetch(endpoint, init);
  },
};

This feels kind of overkill for me. Fortunately, I have a big old Linux server running NixOS in one of the Hetzner data centers, which I like to use to trigger external API calls at regular intervals.

This is how I can add another systemd timer to my NixOS configuration in order to trigger hourly deployments of my website with the latest data:

systemd.services."update-lgug2z-com" = {
  startAt = "hourly";
  environment = {
    inherit (secrets) CLOUDFLARE_API_TOKEN;
  };
  serviceConfig = {
    Type = "oneshot";
    DynamicUser = true;
    ExecStart = ''
        ${pkgs.httpie}/bin/http POST \
            https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/{project_name}/deployments \
            -A bearer -a ''${CLOUDFLARE_API_TOKEN}
    '';
  };
};

That’s pretty much it. If you landed on this article from a direct link, you can see how this looks when it’s all put together by navigating to the homepage.


Discuss this article on /r/LGUG2Z