tinyrelayfile
tinyrelay/tinyclient/avatars.go/

tinyclient/avatars.go raw
1package tinyclient
2
3import (
4 "crypto/sha256"
5 "fmt"
6 "net/http"
7 "regexp"
8 "strings"
9 "time"
10
11 "github.com/FelineStateMachine/tinyrelay/internal/seedmark"
12)
13
14var avatarPath = regexp.MustCompile(`^/avatars/v1/([0-9a-f]{64})\.svg$`)
15
16func avatarURL(pubkey string) string {
17 key := strings.ToLower(pubkey)
18 if !eventIDPattern.MatchString(key) {
19 key = strings.Repeat("0", 64)
20 }
21 return "/avatars/v1/" + key + ".svg"
22}
23
24func roomAvatarURL(relay, id string) string {
25 seed := sha256.Sum256([]byte("tinyrelay:room:v1\x00" + strings.TrimRight(relay, "/") + "\x00" + id))
26 return avatarURL(fmt.Sprintf("%x", seed))
27}
28
29// Avatars depend only on the supplied seed and never look up tenant, room or
30// profile data. Versioned URLs keep each identity's appearance stable.
31func serveAvatar(w http.ResponseWriter, r *http.Request) {
32 if r.Method != http.MethodGet && r.Method != http.MethodHead {
33 w.Header().Set("Allow", "GET, HEAD")
34 http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
35 return
36 }
37 match := avatarPath.FindStringSubmatch(r.URL.Path)
38 if match == nil {
39 http.NotFound(w, r)
40 return
41 }
42 w.Header().Set("Content-Type", "image/svg+xml; charset=utf-8")
43 w.Header().Set("X-Content-Type-Options", "nosniff")
44 w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
45 w.Header().Set("ETag", `"seedmark-v1-`+match[1]+`"`)
46 http.ServeContent(w, r, "avatar.svg", time.Time{}, strings.NewReader(seedmark.Avatar(match[1])))
47}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs