From bb6bc2387d58c737dbf92e73f8544bf39319dbd8 Mon Sep 17 00:00:00 2001 From: Lena Date: Sat, 1 Aug 2026 00:00:00 +0000 Subject: Enter git-forge A small self-hosted git forge: HTTP push and pull, cgit browsing, local and OIDC accounts, TOTP, scoped push tokens, per-repo permissions, issues, a wiki, and outbound mail. It does not reimplement git. git-http-backend and cgit run as CGI behind its own listener, which authorises every request before exec'ing either. One binary, one data directory, one listener: no database, no JavaScript, and state is JSON in a git repo with a working tree. --- avatar.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 avatar.go (limited to 'avatar.go') diff --git a/avatar.go b/avatar.go new file mode 100644 index 0000000..4342890 --- /dev/null +++ b/avatar.go @@ -0,0 +1,45 @@ +package main + +import ( + "crypto/sha256" + "fmt" + "net/http" + "strings" +) + +// An avatar is drawn from the name alone: fetching one tells somewhere else +// who reads what, and an upload needs a parser. +func avatarSVG(name string) []byte { + sum := sha256.Sum256([]byte(name)) + // Fixed saturation and lightness, so every one is readable. + hue := int(sum[0]) * 360 / 256 + var b strings.Builder + fmt.Fprintf(&b, ``, name) + fmt.Fprintf(&b, ``) + fmt.Fprintf(&b, ``, hue) + // Three columns mirrored into five. + for x := range 3 { + for y := range 5 { + if sum[1+x*5+y]&1 == 0 { + continue + } + fmt.Fprintf(&b, ``, x, y) + if x < 2 { + fmt.Fprintf(&b, ``, 4-x, y) + } + } + } + b.WriteString(``) + return []byte(b.String()) +} + +func (f *Forge) handleAvatar(w http.ResponseWriter, r *http.Request) { + name := r.PathValue("name") + if !accountNameRE.MatchString(name) { + http.NotFound(w, r) + return + } + w.Header().Set("Content-Type", "image/svg+xml") + w.Header().Set("Cache-Control", "max-age=86400") + w.Write(avatarSVG(name)) +} -- cgit v1.2.3