diff --git a/.containerignore b/.containerignore index 96443e2..b06848b 100644 --- a/.containerignore +++ b/.containerignore @@ -2,15 +2,11 @@ .git .gitignore .gitattributes -scripts-bash -scripts-go +bash-scripts Containerfile -Containerfile.build -binaries README.md xml-template cache etc library watch -releases diff --git a/.gitattributes b/.gitattributes index f10d157..fe2172f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,8 @@ +*.jpg filter=lfs diff=lfs merge=lfs -text *.mp3 filter=lfs diff=lfs merge=lfs -text *.lua filter=lfs diff=lfs merge=lfs -text +*.svg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text *.webp filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text *.webm filter=lfs diff=lfs merge=lfs -text @@ -28,3 +31,7 @@ *.odt filter=lfs diff=lfs merge=lfs -text *.docx filter=lfs diff=lfs merge=lfs -text *.apk filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.JXL filter=lfs diff=lfs merge=lfs -text +*.AVIF filter=lfs diff=lfs merge=lfs -text +*.PNG filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 0a78ee4..e206118 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,3 @@ cache library etc watch -binaries -releases diff --git a/Containerfile b/Containerfile index 7a3c8c8..4412cdc 100644 --- a/Containerfile +++ b/Containerfile @@ -25,11 +25,11 @@ RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build \ -a \ -ldflags="-s -w -linkmode external -extldflags '-static' -X main.GOMEMLIMIT=512MiB -X runtime.defaultGOGC=50" \ -trimpath \ - -o bin/main ./app/gopherbook + -o bin/main app/gopherbook/main.go RUN upx --best --ultra-brute bin/main RUN chmod +x bin/main -FROM git.jester-designs.com/riomoo/alisterbase:1.0.0 +FROM cgr.dev/chainguard/static:latest WORKDIR /app diff --git a/Containerfile.build b/Containerfile.build deleted file mode 100644 index 7747181..0000000 --- a/Containerfile.build +++ /dev/null @@ -1,55 +0,0 @@ -# Multi-platform build container for Gopherbook -FROM golang:alpine AS builder - -RUN apk add --no-cache \ - musl-dev \ - gcc \ - g++ \ - mingw-w64-gcc \ - wget \ - xz \ - git - -# Install UPX for binary compression -RUN wget https://github.com/upx/upx/releases/download/v5.0.2/upx-5.0.2-amd64_linux.tar.xz && \ - tar -xf upx-5.0.2-amd64_linux.tar.xz && \ - mv upx-5.0.2-amd64_linux/upx /usr/local/bin/upx && \ - rm -r upx-5.0.2-amd64_linux upx-5.0.2-amd64_linux.tar.xz - -WORKDIR /app - -COPY go.mod go.sum ./ -RUN go mod download - -COPY . . - -# Build Linux binary -RUN echo "Building Linux binary..." && \ - CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build \ - -a \ - -ldflags="-s -w -linkmode external -extldflags '-static'" \ - -trimpath \ - -o bin/gopherbook-linux ./app/gopherbook && \ - upx --best --ultra-brute bin/gopherbook-linux && \ - chmod +x bin/gopherbook-linux - -# Build Windows binary -RUN echo "Building Windows binary..." && \ - echo 'IDI_ICON1 ICON "./app/gopherbook/static/images/favicon/favicon.ico"' > gopherbook.rc && \ - x86_64-w64-mingw32-windres gopherbook.rc -o ./app/gopherbook/gopherbook.syso && \ - CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc go build \ - -a \ - -ldflags="-s -w" \ - -trimpath \ - -o bin/gopherbook-windows.exe ./app/gopherbook && \ - upx --best --ultra-brute bin/gopherbook-windows.exe && \ - rm ./app/gopherbook/gopherbook.syso gopherbook.rc - -# Verify binaries were created -RUN ls -lh bin/ && \ - echo "Build complete!" && \ - echo "Linux binary size: $(du -h bin/gopherbook-linux | cut -f1)" && \ - echo "Windows binary size: $(du -h bin/gopherbook-windows.exe | cut -f1)" - -# Keep the builder stage as the final stage so we can copy files out -FROM builder diff --git a/Makefile b/Makefile index 797294d..1cd8552 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ build: - go build -o bin/main ./app/gopherbook + go build -o bin/main app/gopherbook/main.go clean: - rm -rf watch etc library cache binaries releases + rm -rf watch etc library cache diff --git a/README.md b/README.md index 23e0389..b333156 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,11 @@ -
- -Description - -
- -
- -## Self-Hosted Comic Library & CBZ/CBT Reader +# Gopherbook – Self-Hosted Comic Library & CBZ/CBT Reader Gopherbook is a lightweight, single-binary, self-hosted web comic reader and library manager written in Go. It is designed for people who want full control over their digital comic collection (CBZ/CBT files), including support for password-protected/encrypted archives, per-user libraries, tagging, automatic organization, and a clean modern reader. -
- ## License -[![Custom badge](https://git.jester-designs.com/riomoo/gopherbook/media/branch/main/docs/images/svgs/PIL.svg)](LICENSE) +[![Custom badge](https://img.shields.io/endpoint?style=for-the-badge&url=https%3A%2F%2Fshare.jester-designs.com%2Fview%2Fpil.json)](LICENSE) ## Features @@ -66,16 +56,7 @@ Then open http://localhost:8080 in your browser. ```bash git clone https://codeberg.org/riomoo/gopherbook.git cd gopherbook -./scripts-bash/run-gb-container/run-podman.sh -``` - -or - -## If you want to use this with docker: -```bash -git clone https://codeberg.org/riomoo/gopherbook.git -cd gopherbook -./scripts-bash/run-gb-container/run-docker.sh +./scripts-bash/run.sh ``` Then open http://localhost:12010 in your browser. @@ -97,15 +78,6 @@ Then open http://localhost:12010 in your browser. ./etc/admin.json ← admin settings (registration toggle) ``` -## Environment Variables -Can be used to set where everything is stored with: -``` -GOPHERBOOK_LIBRARY=$HOME/.config/gopherbook/library -GOPHERBOOK_CACHE=$HOME/.config/gopherbook/covers -GOPHERBOOK_ETC=$HOME/.config/gopherbook/etc -GOPHERBOOK_WATCH=$HOME/.config/gopherbook/watch -``` - ## Watch folder for bulk imports Gopherbook includes an automatic watch folder system that makes bulk importing comics effortless: @@ -205,14 +177,6 @@ Pull requests are welcome! Especially: Please open an issue first for bigger changes. -## If you'd like to know about the new mascot Vinny - -- Check the Wiki! -- **GitHub**: https://github.com/riomoo/gopherbook/wiki/Meet-Vinny -- **GitGud**: https://gitgud.io/riomoo/gopherbook/-/wikis/Meet-Vinny -- **JesterDesigns**: https://git.jester-designs.com/riomoo/gopherbook/wiki/Meet-Vinny - - ## Thanks / Credits - yeka/zip – password-protected ZIP support in pure Go diff --git a/app/gopherbook/config.go b/app/gopherbook/config.go deleted file mode 100644 index 9b70832..0000000 --- a/app/gopherbook/config.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "os" - "path/filepath" -) - -var ( - baseLibraryPath string - baseCachePath string - baseEtcPath string - baseWatchPath string -) - -func init() { - // Store the base paths (before user-specific paths are added) - baseLibraryPath = libraryPath - baseCachePath = cachePath - baseEtcPath = etcPath - baseWatchPath = watchPath - - // Override from environment variables if set - if env := os.Getenv("GOPHERBOOK_LIBRARY"); env != "" { - baseLibraryPath = filepath.Clean(env) - libraryPath = baseLibraryPath - } - if env := os.Getenv("GOPHERBOOK_CACHE"); env != "" { - baseCachePath = filepath.Clean(env) - cachePath = baseCachePath - } - if env := os.Getenv("GOPHERBOOK_ETC"); env != "" { - baseEtcPath = filepath.Clean(env) - etcPath = baseEtcPath - } - if env := os.Getenv("GOPHERBOOK_WATCH"); env != "" { - baseWatchPath = filepath.Clean(env) - watchPath = baseWatchPath - } -} diff --git a/app/gopherbook/main.go b/app/gopherbook/main.go index 9db9fd1..87bab00 100644 --- a/app/gopherbook/main.go +++ b/app/gopherbook/main.go @@ -21,7 +21,6 @@ import ( "regexp" "sort" "strings" - "io/fs" "sync" "time" "runtime" @@ -40,9 +39,6 @@ import ( //go:embed templates/index.html var templateFS embed.FS -//go:embed all:static -var staticFS embed.FS - // ComicInfo represents the standard ComicInfo.xml metadata type ComicInfo struct { XMLName xml.Name `xml:"ComicInfo"` @@ -139,14 +135,6 @@ func main() { loadUsers() initWatchFolders() - // Create static sub-filesystem once - staticSubFS, err := fs.Sub(staticFS, "static") - if err != nil { - log.Fatal(fmt.Errorf("failed to create static sub-filesystem: %w", err)) - } - - // Create handlers once and reuse - staticHandler := http.FileServer(http.FS(staticSubFS)) http.HandleFunc("/api/register", handleRegister) http.HandleFunc("/api/login", handleLogin) @@ -166,7 +154,6 @@ func main() { http.HandleFunc("/api/admin/delete-comic/", authMiddleware(handleDeleteComic)) http.HandleFunc("/api/watch-folder", authMiddleware(handleWatchFolder)) http.HandleFunc("/", serveUI) - http.Handle("/static/", http.StripPrefix("/static/", staticHandler)) go func() { for { @@ -557,8 +544,8 @@ func handleLogin(w http.ResponseWriter, r *http.Request) { currentUser = req.Username key := deriveKey(req.Password) - libraryPath = filepath.Join(baseLibraryPath, currentUser) - cachePath = filepath.Join(baseCachePath, currentUser) + libraryPath = filepath.Join("./library", currentUser) + cachePath = filepath.Join("./cache/covers", currentUser) os.MkdirAll(filepath.Join(libraryPath, "Unorganized"), 0755) os.MkdirAll(cachePath, 0755) @@ -612,8 +599,8 @@ func handleLogout(w http.ResponseWriter, r *http.Request) { passwordsMutex.Unlock() currentEncryptionKey = nil currentUser = "" - libraryPath = baseLibraryPath - cachePath = baseCachePath + libraryPath = "./library" + cachePath = "./cache/covers" http.SetCookie(w, &http.Cookie{ Name: "session", @@ -1612,7 +1599,7 @@ func saveJPEG(img image.Image, path string) error { defer out.Close() // Lower quality = smaller memory footprint during encoding - err = jpeg.Encode(out, img, &jpeg.Options{Quality: 85}) + err = jpeg.Encode(out, img, &jpeg.Options{Quality: 70}) img = nil runtime.GC() @@ -1656,7 +1643,7 @@ func handleTags(w http.ResponseWriter, r *http.Request) { } if req.Color == "" { - req.Color = "#446B6E" + req.Color = "#1f6feb" } tagsMutex.Lock() @@ -1845,7 +1832,7 @@ func handleTryKnownPasswords(w http.ResponseWriter, r *http.Request) { tagData.Count++ tags[tag] = tagData } else { - tags[tag] = Tag{Name: tag, Color: "#446B6E", Count: 1} + tags[tag] = Tag{Name: tag, Color: "#1f6feb", Count: 1} } } tagsMutex.Unlock() @@ -1960,7 +1947,7 @@ func handleSetPassword(w http.ResponseWriter, r *http.Request) { tagData.Count++ tags[tag] = tagData } else { - tags[tag] = Tag{Name: tag, Color: "#446B6E", Count: 1} + tags[tag] = Tag{Name: tag, Color: "#1f6feb", Count: 1} } } tagsMutex.Unlock() @@ -2432,7 +2419,7 @@ func processComic(filePath, filename string, modTime time.Time) Comic { tagData.Count++ tags[tag] = tagData } else { - tags[tag] = Tag{Name: tag, Color: "#446B6E", Count: 1} + tags[tag] = Tag{Name: tag, Color: "#1f6feb", Count: 1} } } tagsMutex.Unlock() @@ -2494,7 +2481,7 @@ func loadComicMetadataLazy(comicID string) error { tagData.Count++ tags[tag] = tagData } else { - tags[tag] = Tag{Name: tag, Color: "#446B6E", Count: 1} + tags[tag] = Tag{Name: tag, Color: "#1f6feb", Count: 1} } } tagsMutex.Unlock() @@ -2710,16 +2697,9 @@ func authMiddleware(next http.HandlerFunc) http.HandlerFunc { next(w, r) } } -func getUsersPath() string { - return filepath.Join(etcPath, "users.json") -} - -func getAdminPath() string { - return filepath.Join(etcPath, "admin.json") -} func loadUsers() { - data, err := os.ReadFile(getUsersPath()) + data, err := os.ReadFile("etc/users.json") if err != nil { return } @@ -2727,7 +2707,7 @@ func loadUsers() { log.Printf("Error unmarshaling users: %v", err) } - adminData, err := os.ReadFile(getAdminPath()) + adminData, err := os.ReadFile("etc/admin.json") if err == nil && len(adminData) > 0 { var adminConfig struct{ RegistrationEnabled bool } if err := json.Unmarshal(adminData, &adminConfig); err == nil { @@ -2738,13 +2718,13 @@ func loadUsers() { func saveUsers() { data, _ := json.MarshalIndent(users, "", " ") - os.WriteFile(getUsersPath(), data, 0644) + os.WriteFile("etc/users.json", data, 0644) } func saveAdminConfig() { config := struct{ RegistrationEnabled bool }{RegistrationEnabled: registrationEnabled} data, _ := json.MarshalIndent(config, "", " ") - os.WriteFile(getAdminPath(), data, 0644) + os.WriteFile("etc/admin.json", data, 0644) } func loadTags() { diff --git a/app/gopherbook/static/images/favicon/favicon.ico b/app/gopherbook/static/images/favicon/favicon.ico deleted file mode 100644 index 415e0c5..0000000 Binary files a/app/gopherbook/static/images/favicon/favicon.ico and /dev/null differ diff --git a/app/gopherbook/static/images/pngs/CutePose2.png b/app/gopherbook/static/images/pngs/CutePose2.png deleted file mode 100644 index d4949e3..0000000 Binary files a/app/gopherbook/static/images/pngs/CutePose2.png and /dev/null differ diff --git a/app/gopherbook/static/images/pngs/LogoPose2.png b/app/gopherbook/static/images/pngs/LogoPose2.png deleted file mode 100644 index 4a96d50..0000000 Binary files a/app/gopherbook/static/images/pngs/LogoPose2.png and /dev/null differ diff --git a/app/gopherbook/templates/index.html b/app/gopherbook/templates/index.html index f2408ce..a501978 100644 --- a/app/gopherbook/templates/index.html +++ b/app/gopherbook/templates/index.html @@ -3,7 +3,6 @@ - Gopherbook