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 @@
-
-
-
-
-
-
-
-
-## 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
-[](LICENSE)
+[](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