diff --git a/.containerignore b/.containerignore index b06848b..96443e2 100644 --- a/.containerignore +++ b/.containerignore @@ -2,11 +2,15 @@ .git .gitignore .gitattributes -bash-scripts +scripts-bash +scripts-go Containerfile +Containerfile.build +binaries README.md xml-template cache etc library watch +releases diff --git a/.gitignore b/.gitignore index e206118..0a78ee4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ cache library etc watch +binaries +releases diff --git a/Containerfile b/Containerfile index 4412cdc..0e3bbb9 100644 --- a/Containerfile +++ b/Containerfile @@ -25,7 +25,7 @@ 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/main.go + -o bin/main ./app/gopherbook RUN upx --best --ultra-brute bin/main RUN chmod +x bin/main diff --git a/Containerfile.build b/Containerfile.build new file mode 100644 index 0000000..f9fab6a --- /dev/null +++ b/Containerfile.build @@ -0,0 +1,52 @@ +# 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..." && \ + 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 + +# 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 1cd8552..11a28fe 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ build: - go build -o bin/main app/gopherbook/main.go + go build -o bin/main ./app/gopherbook clean: rm -rf watch etc library cache diff --git a/README.md b/README.md index b333156..2766fdc 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,15 @@ 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: diff --git a/app/gopherbook/config.go b/app/gopherbook/config.go new file mode 100644 index 0000000..9b70832 --- /dev/null +++ b/app/gopherbook/config.go @@ -0,0 +1,39 @@ +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 87bab00..7ed7bae 100644 --- a/app/gopherbook/main.go +++ b/app/gopherbook/main.go @@ -544,8 +544,8 @@ func handleLogin(w http.ResponseWriter, r *http.Request) { currentUser = req.Username key := deriveKey(req.Password) - libraryPath = filepath.Join("./library", currentUser) - cachePath = filepath.Join("./cache/covers", currentUser) + libraryPath = filepath.Join(baseLibraryPath, currentUser) + cachePath = filepath.Join(baseCachePath, currentUser) os.MkdirAll(filepath.Join(libraryPath, "Unorganized"), 0755) os.MkdirAll(cachePath, 0755) @@ -599,8 +599,8 @@ func handleLogout(w http.ResponseWriter, r *http.Request) { passwordsMutex.Unlock() currentEncryptionKey = nil currentUser = "" - libraryPath = "./library" - cachePath = "./cache/covers" + libraryPath = baseLibraryPath + cachePath = baseCachePath http.SetCookie(w, &http.Cookie{ Name: "session", @@ -2697,9 +2697,16 @@ 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("etc/users.json") + data, err := os.ReadFile(getUsersPath()) if err != nil { return } @@ -2707,7 +2714,7 @@ func loadUsers() { log.Printf("Error unmarshaling users: %v", err) } - adminData, err := os.ReadFile("etc/admin.json") + adminData, err := os.ReadFile(getAdminPath()) if err == nil && len(adminData) > 0 { var adminConfig struct{ RegistrationEnabled bool } if err := json.Unmarshal(adminData, &adminConfig); err == nil { @@ -2718,13 +2725,13 @@ func loadUsers() { func saveUsers() { data, _ := json.MarshalIndent(users, "", " ") - os.WriteFile("etc/users.json", data, 0644) + os.WriteFile(getUsersPath(), data, 0644) } func saveAdminConfig() { config := struct{ RegistrationEnabled bool }{RegistrationEnabled: registrationEnabled} data, _ := json.MarshalIndent(config, "", " ") - os.WriteFile("etc/admin.json", data, 0644) + os.WriteFile(getAdminPath(), data, 0644) } func loadTags() { diff --git a/scripts-bash/build-release.sh b/scripts-bash/build-release.sh new file mode 100755 index 0000000..a8ed49b --- /dev/null +++ b/scripts-bash/build-release.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +set -e + +IMAGE_NAME="localhost/gopherbook-builder:latest" +CONTAINER_NAME="gopherbook-builder-tmp" +OUTPUT_DIR="./binaries" + +echo "=== Building cross-compilation container ===" +podman build --force-rm -t "$IMAGE_NAME" -f Containerfile.build . + +if [ $? -ne 0 ]; then + echo "Image build failed. Exiting." + exit 1 +fi + +echo "" +echo "=== Creating temporary container ===" +podman create --name "$CONTAINER_NAME" "$IMAGE_NAME" + +echo "" +echo "=== Creating output directory ===" +mkdir -p "$OUTPUT_DIR" +rm -f "$OUTPUT_DIR"/* + +echo "" +echo "=== Extracting Linux binary ===" +podman cp "$CONTAINER_NAME:/app/bin/gopherbook-linux" "$OUTPUT_DIR/gopherbook-linux" +chmod +x "$OUTPUT_DIR/gopherbook-linux" + +echo "" +echo "=== Extracting Windows binary ===" +podman cp "$CONTAINER_NAME:/app/bin/gopherbook-windows.exe" "$OUTPUT_DIR/gopherbook-windows.exe" +chmod +x "$OUTPUT_DIR/gopherbook-windows.exe" + +echo "" +echo "=== Cleaning up temporary container ===" +podman rm "$CONTAINER_NAME" + +echo "" +echo "=== Build complete! ===" +echo "Binaries are in: $OUTPUT_DIR/" +ls -lh "$OUTPUT_DIR/" + +echo "" +echo "=== Binary sizes ===" +du -h "$OUTPUT_DIR"/* + +echo "" +echo "=== Cleaning up builder image ===" +podman rmi "$IMAGE_NAME" + +echo "" +echo "✓ Done! Your binaries are ready:" +echo " • Linux: $OUTPUT_DIR/gopherbook-linux" +echo " • Windows: $OUTPUT_DIR/gopherbook-windows.exe" +echo "" +echo "To run the Linux binary:" +echo " $OUTPUT_DIR/gopherbook-linux" +echo "" +echo "To test the Windows binary (requires wine):" +echo " wine $OUTPUT_DIR/gopherbook-windows.exe" diff --git a/scripts-bash/package-release.sh b/scripts-bash/package-release.sh new file mode 100755 index 0000000..f26c4cd --- /dev/null +++ b/scripts-bash/package-release.sh @@ -0,0 +1,161 @@ +#!/bin/bash + +set -e + +VERSION="${1:-v1.1.000}" +RELEASE_DIR="./releases" +BINARIES_DIR="./binaries" + +echo "=== Packaging Gopherbook $VERSION ===" +echo "" + +# Create release directory +mkdir -p "$RELEASE_DIR" + +# Clean old releases for this version +rm -f "$RELEASE_DIR"/gopherbook-$VERSION-* + +# Check if binaries exist +if [ ! -f "$BINARIES_DIR/gopherbook-linux" ] || [ ! -f "$BINARIES_DIR/gopherbook-windows.exe" ]; then + echo "Error: Binaries not found. Run ./build-and-extract.sh first." + exit 1 +fi + +echo "=== Creating README.txt ===" +cat > /tmp/README.txt << 'EOF' +Gopherbook - Comic Book Reader (CBZ/CBT) +========================================= + +Quick Start: +------------ +1. Run the gopherbook executable +2. Open your browser to http://localhost:8080 +3. Register a new user account +4. Upload your CBZ/CBT comic files + +Features: +--------- +• Supports CBZ (ZIP) and CBT (TAR) formats +• Password-protected archives +• Tag management and filtering +• Bookmark pages +• Auto-organize by artist and story arc +• Watch folder for automatic imports +• Multi-user support with admin controls + +Watch Folder: +------------- +Place CBZ/CBT files in the ./watch// directory +and they will be automatically imported to your library. + +Directory Structure: +-------------------- +./library/ - Your comic library (per user) +./cache/ - Cover image cache +./etc/ - User data and settings +./watch/ - Watch folders for auto-import + +Default Port: 8080 + +For more information, visit: +https://github.com/riomoo/gopherbook +https://codeberg.org/riomoo/gofudge +https://gitgud.io/riomoo/gopherbook + +EOF + +echo "=== Creating Linux package ===" +LINUX_DIR="/tmp/gopherbook-linux" +rm -rf "$LINUX_DIR" +mkdir -p "$LINUX_DIR" + +# Copy Linux binary +cp "$BINARIES_DIR/gopherbook-linux" "$LINUX_DIR/gopherbook" +chmod +x "$LINUX_DIR/gopherbook" + +# Copy documentation +cp /tmp/README.txt "$LINUX_DIR/" + +# Create run script +cat > "$LINUX_DIR/run.sh" << 'EOF' +#!/bin/bash +echo "Starting Gopherbook..." +echo "Open your browser to: http://localhost:8080" +echo "Press Ctrl+C to stop" +echo "" +./gopherbook +EOF +chmod +x "$LINUX_DIR/run.sh" + +# Package Linux +cd /tmp +tar -czf "$LINUX_DIR.tar.gz" gopherbook-linux/ +cd - > /dev/null +mv "/tmp/gopherbook-linux.tar.gz" "$RELEASE_DIR/gopherbook-$VERSION-linux-amd64.tar.gz" +rm -rf "$LINUX_DIR" + +echo "✓ Linux package created" + +echo "" +echo "=== Creating Windows package ===" +WINDOWS_DIR="/tmp/gopherbook-windows" +rm -rf "$WINDOWS_DIR" +mkdir -p "$WINDOWS_DIR" + +# Copy Windows binary +cp "$BINARIES_DIR/gopherbook-windows.exe" "$WINDOWS_DIR/gopherbook.exe" + +# Copy documentation (Windows line endings) +unix2dos < /tmp/README.txt > "$WINDOWS_DIR/README.txt" 2>/dev/null || cp /tmp/README.txt "$WINDOWS_DIR/README.txt" + +# Create batch file +cat > "$WINDOWS_DIR/run.bat" << 'EOF' +@echo off +echo Starting Gopherbook... +echo Open your browser to: http://localhost:8080 +echo Press Ctrl+C to stop +echo. +gopherbook.exe +pause +EOF + +# Create PowerShell script +cat > "$WINDOWS_DIR/run.ps1" << 'EOF' +Write-Host "Starting Gopherbook..." -ForegroundColor Green +Write-Host "Open your browser to: http://localhost:8080" -ForegroundColor Cyan +Write-Host "Press Ctrl+C to stop" -ForegroundColor Yellow +Write-Host "" +.\gopherbook.exe +EOF + +# Package Windows +cd /tmp +zip -q -r "$WINDOWS_DIR.zip" gopherbook-windows/ +cd - > /dev/null +mv "/tmp/gopherbook-windows.zip" "$RELEASE_DIR/gopherbook-$VERSION-windows-amd64.zip" +rm -rf "$WINDOWS_DIR" + +echo "✓ Windows package created" + +echo "" +echo "=== Creating checksums ===" +cd "$RELEASE_DIR" +sha256sum gopherbook-$VERSION-*.tar.gz gopherbook-$VERSION-*.zip > gopherbook-$VERSION-checksums.txt +cd - > /dev/null + +echo "✓ Checksums created" + +echo "" +echo "=== Release packages ready! ===" +echo "" +ls -lh "$RELEASE_DIR"/gopherbook-$VERSION-* +echo "" +echo "Release files:" +echo " • $RELEASE_DIR/gopherbook-$VERSION-linux-amd64.tar.gz" +echo " • $RELEASE_DIR/gopherbook-$VERSION-windows-amd64.zip" +echo " • $RELEASE_DIR/gopherbook-$VERSION-checksums.txt" +echo "" +echo "Upload these files to GitHub Releases!" + +# Cleanup +rm -f /tmp/README.txt