diff --git a/Dockerfile b/Dockerfile
index 9563b70..c0bfc80 100644
--- a/Dockerfile
+++ b/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"]
diff --git a/go.mod b/go.mod
index 5a40b46..adf1768 100644
--- a/go.mod
+++ b/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
diff --git a/go.sum b/go.sum
index 9f6aac6..e97f142 100644
--- a/go.sum
+++ b/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=
diff --git a/internal/logger/logger.go b/internal/logger/logger.go
new file mode 100644
index 0000000..fa8c47d
--- /dev/null
+++ b/internal/logger/logger.go
@@ -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,
+ }))
+}
diff --git a/main.go b/main.go
index f241f43..1c927f9 100644
--- a/main.go
+++ b/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)
diff --git a/package.json b/package.json
index bf8c108..bdde0c6 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,5 @@
"@iconify/tailwind": "^1.0.1",
"daisyui": "^4.7.3",
"tailwindcss": "^3.4.1"
- },
- "dependencies": {
- "simple-icons": "^13.6.0"
}
}
diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh
deleted file mode 100755
index be9bcec..0000000
--- a/scripts/entrypoint.sh
+++ /dev/null
@@ -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
diff --git a/services/bookmark.services.go b/services/bookmark.services.go
index 3d6f912..ba8dcb6 100644
--- a/services/bookmark.services.go
+++ b/services/bookmark.services.go
@@ -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 = ""
}
+
}
}
}
@@ -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, "