Use self hosted icons
This commit is contained in:
parent
387b89963d
commit
177e3d0fd3
8 changed files with 81 additions and 108 deletions
35
Dockerfile
35
Dockerfile
|
@ -1,7 +1,7 @@
|
|||
ARG V_GOLANG
|
||||
ARG V_NODE
|
||||
ARG V_ALPINE
|
||||
FROM golang:${V_GOLANG}-alpine AS goBuilder
|
||||
ARG V_GOLANG=1.23
|
||||
ARG V_NODE=20
|
||||
ARG V_ALPINE=3
|
||||
FROM golang:${V_GOLANG}-alpine AS golang
|
||||
WORKDIR /app
|
||||
|
||||
RUN go install github.com/a-h/templ/cmd/templ@latest
|
||||
|
@ -14,7 +14,7 @@ COPY . .
|
|||
RUN templ generate
|
||||
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
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
|
@ -32,22 +32,25 @@ RUN apk add figlet
|
|||
RUN figlet GoDash > logo.txt
|
||||
|
||||
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
|
||||
|
||||
RUN apk add tzdata
|
||||
|
||||
COPY scripts/entrypoint.sh .
|
||||
|
||||
COPY assets/favicon ./assets/favicon
|
||||
COPY --from=logo /app/logo.txt .
|
||||
COPY --from=nodeBuilder /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=nodeBuilder /app/node_modules/simple-icons/_data ./node_modules/simple-icons/_data
|
||||
COPY --from=goBuilder /app/views ./views
|
||||
COPY --from=goBuilder /app/components ./components
|
||||
COPY --from=goBuilder /app/godash .
|
||||
COPY --from=node /app/assets/css/style.css ./assets/css/style.css
|
||||
COPY --from=golang /app/views ./views
|
||||
COPY --from=golang /app/components ./components
|
||||
COPY --from=golang /app/godash .
|
||||
|
||||
ARG 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
1
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/go-playground/validator/v10 v10.22.1
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/lmittmann/tint v1.0.5
|
||||
github.com/r3labs/sse/v2 v2.10.0
|
||||
github.com/shirou/gopsutil/v4 v4.24.8
|
||||
github.com/thanhpk/randstr v1.0.6
|
||||
|
|
2
go.sum
2
go.sum
|
@ -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/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/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/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
|
|
18
internal/logger/logger.go
Normal file
18
internal/logger/logger.go
Normal 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,
|
||||
}))
|
||||
}
|
3
main.go
3
main.go
|
@ -14,10 +14,13 @@ import (
|
|||
|
||||
"gitlab.unjx.de/flohoss/godash/handlers"
|
||||
"gitlab.unjx.de/flohoss/godash/internal/env"
|
||||
"gitlab.unjx.de/flohoss/godash/internal/logger"
|
||||
"gitlab.unjx.de/flohoss/godash/services"
|
||||
)
|
||||
|
||||
func main() {
|
||||
slog.SetDefault(logger.NewLogger())
|
||||
|
||||
env, err := env.Parse()
|
||||
if err != nil {
|
||||
slog.Error("cannot parse environment variables", "err", err)
|
||||
|
|
|
@ -11,8 +11,5 @@
|
|||
"@iconify/tailwind": "^1.0.1",
|
||||
"daisyui": "^4.7.3",
|
||||
"tailwindcss": "^3.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"simple-icons": "^13.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -1,9 +1,10 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -11,8 +12,6 @@ import (
|
|||
"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 iconsFolder = storageFolder + "icons/"
|
||||
const bookmarkFile = storageFolder + "bookmarks.yaml"
|
||||
|
@ -26,11 +25,11 @@ applications:
|
|||
- category: "Code"
|
||||
entries:
|
||||
- name: "GitHub"
|
||||
icon: "si/github.svg"
|
||||
icon: "shi/github.svg"
|
||||
ignore_color: true
|
||||
url: "https://github.com"
|
||||
- name: "Home Assistant"
|
||||
icon: "si/homeassistant.svg"
|
||||
icon: "shi/homeassistant.svg"
|
||||
url: "https://www.home-assistant.io/"`
|
||||
|
||||
func init() {
|
||||
|
@ -48,7 +47,7 @@ func init() {
|
|||
func NewBookmarkService() *BookmarkService {
|
||||
bs := BookmarkService{}
|
||||
bs.parseBookmarks()
|
||||
bs.parseIcons()
|
||||
bs.replaceIconStrings()
|
||||
return &bs
|
||||
}
|
||||
|
||||
|
@ -84,41 +83,51 @@ func (bs *BookmarkService) readBookmarksFile() []byte {
|
|||
return byteValue
|
||||
}
|
||||
|
||||
func (bs *BookmarkService) replaceIconString() {
|
||||
iconsByTitle := make(map[string]string)
|
||||
for _, icon := range bs.SimpleIcons.Icons {
|
||||
iconsByTitle[icon.Title] = icon.Hex
|
||||
}
|
||||
|
||||
func (bs *BookmarkService) replaceIconStrings() {
|
||||
for i, v := range bs.bookmarks.Applications {
|
||||
for j, bookmark := range v.Entries {
|
||||
if filepath.Ext(bookmark.Icon) == ".svg" {
|
||||
var data []byte
|
||||
var err error
|
||||
if strings.HasPrefix(bookmark.Icon, "si/") {
|
||||
title := strings.Replace(bookmark.Icon, "si/", "", 1)
|
||||
data, err = os.ReadFile(simpleIconsFolder + title)
|
||||
ext := filepath.Ext(bookmark.Icon)
|
||||
if ext != ".svg" {
|
||||
slog.Error("icon must be an svg file")
|
||||
continue
|
||||
}
|
||||
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 {
|
||||
slog.Error("failed to get icon", "err", err.Error())
|
||||
continue
|
||||
}
|
||||
color, ok := iconsByTitle[bookmark.Name]
|
||||
if bookmark.OverwriteColor != "" {
|
||||
ok = true
|
||||
color = bookmark.OverwriteColor
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
slog.Error("failed to get icon", "status", resp.Status)
|
||||
continue
|
||||
}
|
||||
if !(bookmark.IgnoreColor || !ok || color == "") {
|
||||
data = []byte(insertColor(string(data), color))
|
||||
}
|
||||
} else {
|
||||
data, err = os.ReadFile(iconsFolder + bookmark.Icon)
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
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
|
||||
}
|
||||
}
|
||||
if data == nil {
|
||||
slog.Error("icon data is null")
|
||||
continue
|
||||
}
|
||||
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 {
|
||||
parts := strings.SplitN(svg, "<svg", 2)
|
||||
if len(parts) != 2 {
|
||||
|
|
Loading…
Reference in a new issue