| 1 | // Package brand serves the suite's app icons from Go, so a page can link |
| 2 | // any sibling app's icon (the app switcher) without vendoring the file. |
| 3 | // The artwork itself is brand/svg/, generated by generate.mjs; see README.md. |
| 4 | package brand |
| 5 | |
| 6 | import ( |
| 7 | "embed" |
| 8 | "net/http" |
| 9 | "strings" |
| 10 | ) |
| 11 | |
| 12 | //go:embed svg/*.svg |
| 13 | var svgFS embed.FS |
| 14 | |
| 15 | // Prefix is the URL path Handler serves under. |
| 16 | const Prefix = "/static/estilo/brand/" |
| 17 | |
| 18 | // files maps a public slug to its master. "meet" is Calls' name before |
| 19 | // the rename, kept so a page still linking it gets the right icon. |
| 20 | var files = map[string]string{ |
| 21 | "oficina": "oficina.svg", |
| 22 | "docs": "docs.svg", |
| 23 | "sheets": "sheets.svg", |
| 24 | "slides": "slides.svg", |
| 25 | "calendar": "calendar.svg", |
| 26 | "calls": "calls.svg", |
| 27 | "meet": "calls.svg", |
| 28 | "memoria": "memoria.svg", |
| 29 | "fichas": "fichas.svg", |
| 30 | "mail": "mail.svg", |
| 31 | "files": "files.svg", |
| 32 | } |
| 33 | |
| 34 | // SVG returns the icon for slug, or nil for an unknown slug. |
| 35 | func SVG(slug string) []byte { |
| 36 | name, ok := files[slug] |
| 37 | if !ok { |
| 38 | return nil |
| 39 | } |
| 40 | body, err := svgFS.ReadFile("svg/" + name) |
| 41 | if err != nil { |
| 42 | return nil |
| 43 | } |
| 44 | return body |
| 45 | } |
| 46 | |
| 47 | // Path is the URL Handler serves slug's icon at. |
| 48 | func Path(slug string) string { return Prefix + slug + ".svg" } |
| 49 | |
| 50 | // Handler serves GET and HEAD for Prefix + "<slug>.svg". Mount it at |
| 51 | // Prefix. Anything else is 404; the icons are public artwork and need no |
| 52 | // session. |
| 53 | func Handler() http.Handler { |
| 54 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 55 | slug, ok := strings.CutSuffix(strings.TrimPrefix(r.URL.Path, Prefix), ".svg") |
| 56 | body := SVG(slug) |
| 57 | if !ok || body == nil || !strings.HasPrefix(r.URL.Path, Prefix) { |
| 58 | http.NotFound(w, r) |
| 59 | return |
| 60 | } |
| 61 | if r.Method != http.MethodGet && r.Method != http.MethodHead { |
| 62 | w.Header().Set("Allow", "GET, HEAD") |
| 63 | http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) |
| 64 | return |
| 65 | } |
| 66 | w.Header().Set("Content-Type", "image/svg+xml") |
| 67 | w.Header().Set("Cache-Control", "public, max-age=86400") |
| 68 | w.Header().Set("X-Content-Type-Options", "nosniff") |
| 69 | if r.Method == http.MethodHead { |
| 70 | return |
| 71 | } |
| 72 | _, _ = w.Write(body) |
| 73 | }) |
| 74 | } |
| 75 | |