Use self hosted icons

This commit is contained in:
Florian Hoss 2024-09-30 12:05:12 +02:00
parent 387b89963d
commit 177e3d0fd3
Signed by: flohoss
GPG key ID: 3F35C7F6E6F66F6B
8 changed files with 81 additions and 108 deletions

View file

@ -1,7 +1,7 @@
ARG V_GOLANG ARG V_GOLANG=1.23
ARG V_NODE ARG V_NODE=20
ARG V_ALPINE ARG V_ALPINE=3
FROM golang:${V_GOLANG}-alpine AS goBuilder FROM golang:${V_GOLANG}-alpine AS golang
WORKDIR /app WORKDIR /app
RUN go install github.com/a-h/templ/cmd/templ@latest RUN go install github.com/a-h/templ/cmd/templ@latest
@ -14,7 +14,7 @@ COPY . .
RUN templ generate RUN templ generate
RUN go build -ldflags="-s -w" -o godash main.go RUN go build -ldflags="-s -w" -o godash main.go
FROM node:${V_NODE}-alpine AS nodeBuilder FROM node:${V_NODE}-alpine AS node
WORKDIR /app WORKDIR /app
COPY package.json yarn.lock ./ COPY package.json yarn.lock ./
@ -32,22 +32,25 @@ RUN apk add figlet
RUN figlet GoDash > logo.txt RUN figlet GoDash > logo.txt
FROM alpine:${V_ALPINE} AS final FROM alpine:${V_ALPINE} AS final
RUN apk --no-cache add tzdata ca-certificates dumb-init && \
rm -rf /tmp/* /var/tmp/* /usr/share/man /var/cache/apk/*
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app WORKDIR /app
RUN apk add tzdata
COPY scripts/entrypoint.sh .
COPY assets/favicon ./assets/favicon COPY assets/favicon ./assets/favicon
COPY --from=logo /app/logo.txt . COPY --from=logo /app/logo.txt .
COPY --from=nodeBuilder /app/assets/css/style.css ./assets/css/style.css COPY --from=node /app/assets/css/style.css ./assets/css/style.css
COPY --from=nodeBuilder /app/node_modules/simple-icons/icons ./node_modules/simple-icons/icons COPY --from=golang /app/views ./views
COPY --from=nodeBuilder /app/node_modules/simple-icons/_data ./node_modules/simple-icons/_data COPY --from=golang /app/components ./components
COPY --from=goBuilder /app/views ./views COPY --from=golang /app/godash .
COPY --from=goBuilder /app/components ./components
COPY --from=goBuilder /app/godash .
ARG APP_VERSION ARG APP_VERSION
ENV APP_VERSION=$APP_VERSION ENV APP_VERSION=$APP_VERSION
ENTRYPOINT ["/app/entrypoint.sh"] RUN chown -R appuser:appgroup /app
ENTRYPOINT ["dumb-init", "--"]
USER appuser
CMD ["/app/godash"]

1
go.mod
View file

@ -9,6 +9,7 @@ require (
github.com/go-playground/validator/v10 v10.22.1 github.com/go-playground/validator/v10 v10.22.1
github.com/gorilla/securecookie v1.1.2 github.com/gorilla/securecookie v1.1.2
github.com/gorilla/sessions v1.4.0 github.com/gorilla/sessions v1.4.0
github.com/lmittmann/tint v1.0.5
github.com/r3labs/sse/v2 v2.10.0 github.com/r3labs/sse/v2 v2.10.0
github.com/shirou/gopsutil/v4 v4.24.8 github.com/shirou/gopsutil/v4 v4.24.8
github.com/thanhpk/randstr v1.0.6 github.com/thanhpk/randstr v1.0.6

2
go.sum
View file

@ -40,6 +40,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw=
github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=

18
internal/logger/logger.go Normal file
View file

@ -0,0 +1,18 @@
package logger
import (
"log/slog"
"os"
"time"
"github.com/lmittmann/tint"
)
func NewLogger() *slog.Logger {
w := os.Stderr
return slog.New(tint.NewHandler(w, &tint.Options{
Level: slog.LevelDebug,
TimeFormat: time.Kitchen,
}))
}

View file

@ -14,10 +14,13 @@ import (
"gitlab.unjx.de/flohoss/godash/handlers" "gitlab.unjx.de/flohoss/godash/handlers"
"gitlab.unjx.de/flohoss/godash/internal/env" "gitlab.unjx.de/flohoss/godash/internal/env"
"gitlab.unjx.de/flohoss/godash/internal/logger"
"gitlab.unjx.de/flohoss/godash/services" "gitlab.unjx.de/flohoss/godash/services"
) )
func main() { func main() {
slog.SetDefault(logger.NewLogger())
env, err := env.Parse() env, err := env.Parse()
if err != nil { if err != nil {
slog.Error("cannot parse environment variables", "err", err) slog.Error("cannot parse environment variables", "err", err)

View file

@ -11,8 +11,5 @@
"@iconify/tailwind": "^1.0.1", "@iconify/tailwind": "^1.0.1",
"daisyui": "^4.7.3", "daisyui": "^4.7.3",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.1"
},
"dependencies": {
"simple-icons": "^13.6.0"
} }
} }

View file

@ -1,34 +0,0 @@
#!/bin/sh
cat logo.txt
CMD=./godash
if [ -n "$PUID" ] || [ -n "$PGID" ]; then
USER=appuser
HOME=/app
if ! grep -q "$USER" /etc/passwd; then
# Usage: addgroup [-g GID] [-S] [USER] GROUP
#
# Add a group or add a user to a group
# -g GID Group id
addgroup -g "$PGID" "$USER"
# Usage: adduser [OPTIONS] USER [GROUP]
# Create new user, or add USER to GROUP
# -h DIR Home directory
# -g GECOS GECOS field
# -G GRP Group
# -D Don't assign a password
# -H Don't create home directory
# -u UID User id
adduser -h "$HOME" -g "" -G "$USER" -D -H -u "$PUID" "$USER"
fi
chown "$USER":"$USER" "$HOME" -R
printf "\nUID: %s GID: %s\n\n" "$PUID" "$PGID"
exec su -c - $USER "$CMD"
else
printf "\nWARNING: Running docker as root\n\n"
exec "$CMD"
fi

View file

@ -1,9 +1,10 @@
package services package services
import ( import (
"encoding/json"
"io" "io"
"io/fs"
"log/slog" "log/slog"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -11,8 +12,6 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const simpleIconsFolder = "node_modules/simple-icons/icons/"
const simpleIconsInfo = "node_modules/simple-icons/_data/simple-icons.json"
const storageFolder = "storage/" const storageFolder = "storage/"
const iconsFolder = storageFolder + "icons/" const iconsFolder = storageFolder + "icons/"
const bookmarkFile = storageFolder + "bookmarks.yaml" const bookmarkFile = storageFolder + "bookmarks.yaml"
@ -26,11 +25,11 @@ applications:
- category: "Code" - category: "Code"
entries: entries:
- name: "GitHub" - name: "GitHub"
icon: "si/github.svg" icon: "shi/github.svg"
ignore_color: true ignore_color: true
url: "https://github.com" url: "https://github.com"
- name: "Home Assistant" - name: "Home Assistant"
icon: "si/homeassistant.svg" icon: "shi/homeassistant.svg"
url: "https://www.home-assistant.io/"` url: "https://www.home-assistant.io/"`
func init() { func init() {
@ -48,7 +47,7 @@ func init() {
func NewBookmarkService() *BookmarkService { func NewBookmarkService() *BookmarkService {
bs := BookmarkService{} bs := BookmarkService{}
bs.parseBookmarks() bs.parseBookmarks()
bs.parseIcons() bs.replaceIconStrings()
return &bs return &bs
} }
@ -84,41 +83,51 @@ func (bs *BookmarkService) readBookmarksFile() []byte {
return byteValue return byteValue
} }
func (bs *BookmarkService) replaceIconString() { func (bs *BookmarkService) replaceIconStrings() {
iconsByTitle := make(map[string]string)
for _, icon := range bs.SimpleIcons.Icons {
iconsByTitle[icon.Title] = icon.Hex
}
for i, v := range bs.bookmarks.Applications { for i, v := range bs.bookmarks.Applications {
for j, bookmark := range v.Entries { for j, bookmark := range v.Entries {
if filepath.Ext(bookmark.Icon) == ".svg" { ext := filepath.Ext(bookmark.Icon)
var data []byte if ext != ".svg" {
var err error slog.Error("icon must be an svg file")
if strings.HasPrefix(bookmark.Icon, "si/") { continue
title := strings.Replace(bookmark.Icon, "si/", "", 1) }
data, err = os.ReadFile(simpleIconsFolder + title) if strings.HasPrefix(bookmark.Icon, "shi/") {
title := strings.Replace(bookmark.Icon, "shi/", "", 1)
if title == "" {
slog.Error("icon title is empty")
continue
}
data, err := os.ReadFile(iconsFolder + title)
if os.IsNotExist(err) {
slog.Debug("icon not found, downloading...", "title", title)
resp, err := http.Get("https://cdn.jsdelivr.net/gh/selfhst/icons/" + ext + "/" + title)
if err != nil { if err != nil {
slog.Error("failed to get icon", "err", err.Error())
continue continue
} }
color, ok := iconsByTitle[bookmark.Name] defer resp.Body.Close()
if bookmark.OverwriteColor != "" { if resp.StatusCode != http.StatusOK {
ok = true slog.Error("failed to get icon", "status", resp.Status)
color = bookmark.OverwriteColor continue
} }
if !(bookmark.IgnoreColor || !ok || color == "") { data, err = io.ReadAll(resp.Body)
data = []byte(insertColor(string(data), color))
}
} else {
data, err = os.ReadFile(iconsFolder + bookmark.Icon)
if err != nil { if err != nil {
slog.Error("failed to read icon", "err", err.Error())
continue
}
err = os.WriteFile(iconsFolder+title, data, fs.FileMode(0640))
if err != nil {
slog.Error("failed to write icon", "err", err.Error())
continue continue
} }
} }
if data == nil {
slog.Error("icon data is null")
continue
}
bs.bookmarks.Applications[i].Entries[j].Icon = insertWidthHeight(string(data)) bs.bookmarks.Applications[i].Entries[j].Icon = insertWidthHeight(string(data))
} else {
bs.bookmarks.Applications[i].Entries[j].Icon = "<img title=\"" + bookmark.Name + "\" src=\"/icons/" + bookmark.Icon + "\"/>"
} }
} }
} }
} }
@ -132,32 +141,6 @@ func (bs *BookmarkService) parseBookmarks() {
} }
} }
func (bs *BookmarkService) parseIcons() {
file, err := os.Open(simpleIconsInfo)
if err != nil {
slog.Error(err.Error())
}
defer file.Close()
byteValue, err := io.ReadAll(file)
if err != nil {
slog.Error(err.Error())
}
err = json.Unmarshal(byteValue, &bs.SimpleIcons)
if err != nil {
slog.Error(err.Error())
return
}
bs.replaceIconString()
}
func insertColor(svg, color string) string {
parts := strings.SplitN(svg, "<svg", 2)
if len(parts) != 2 {
return svg
}
return parts[0] + "<svg " + `fill="#` + color + `" ` + parts[1]
}
func insertWidthHeight(svg string) string { func insertWidthHeight(svg string) string {
parts := strings.SplitN(svg, "<svg", 2) parts := strings.SplitN(svg, "<svg", 2)
if len(parts) != 2 { if len(parts) != 2 {