guidongui.com

Why I built this blog

The exposing-guidongui repository contains the tech stack of my personal blog, reachable at exposing.guidongui.com. As the subdomain name suggests, I'm not seeking fame, just a public place to talk about my personal projects. Public exposure is a way to do better what I do every day and above all a way to finish the small projects I start and then very often leave in POC state.

I've always wanted a space where I could pour out my ideas, but for deontological reasons, I've always refused to use CMS like Hugo to build one. The day I would do it, it would be built from scratch by me so I could learn something new. The goal is not to look pretty or smart to the world, but to learn by experimenting. I preferred simplicity over flashiness, custom Go libraries over frameworks.

The blog is completely self-developed, self-hosted in my Home Media Lab, which I hope I'll have the chance to talk about in future posts.

From the README:

net/http Standard Library

The server uses Go's standard net/http package without external routing frameworks.

Key patterns:

  • Uses Go 1.22+ method-based routing (GET /path)
  • Path parameters extracted via r.PathValue("id")
  • Handlers return http.HandlerFunc closures with dependency injection for readers
  • http.FileServer serves static CSS and JS from web/templates/

Go Templates

Templates use the text/template package with .gohtml files.

Homepage template (home.gohtml) iterates over posts:

<article>
    <h3><a href="{{.URL}}">{{.Title}}</a></h3>
    <address>{{.Date}}</address>
    <pre>{{.Summary}}</pre>
</article>

Post template (post.gohtml) renders single post data:

<title>{{.Title}}</title>
<pre>{{.Content}}</pre>

Data structures passed to templates:

type HomepageData struct {
    Posts []PostData
}

type PostData struct {
    Title, Author, URL, Date, Summary, Content string
    ViewCount int
}

Templates are parsed once at handler creation and executed per-request via tpl.Execute(w, data).

Goldmark Markdown Parsing

Goldmark converts Markdown posts to HTML.

mdConverter := goldmark.New(
    goldmark.WithExtensions(extension.GFM),  // GitHub Flavored Markdown
)

var buf bytes.Buffer
mdConverter.Convert(mdPost, &buf)
pd.Content = buf.String()

The GFM extension enables:

  • Tables
  • Strikethrough (~~text~~)
  • Task lists (- [x] done)
  • Autolinks

Custom Metadata Parser

Posts use a custom metadata format at the file start:

Asciinema Integration

The homepage displays a terminal recording via a self-hosted Asciinema server.

Frontend integration (home.gohtml):

AsciinemaPlayer.create(
    'https://asciinema.guidongui.com/a/34.cast',
    document.getElementById('asciinema-player'),
    { autoPlay: true, loop: true, theme: 'white', ... }
);

Self-hosted server (deploy/asciinema/deployment.yaml):

  • Runs the official ghcr.io/asciinema/asciinema-server image
  • PostgreSQL database in a sidecar container
  • Exposed at asciinema.guidongui.com via Traefik IngressRoute with TLS
  • Configuration: signups disabled, uploads require auth, public recordings