feat(v0.11.6): FileBrowser auto-mount sync + UI polish
- Add syncFileBrowserMounts() and generateFileBrowserCompose() to handlers.go - Call syncFileBrowserMounts() after storage path add (storage init) and remove - settings.html: red 'Nincs csatolva!' badge → yellow 'Rendszermeghajtón' (badge-warn) - settings.html: 'Alapértelmezett' button → 'Legyen alapértelmezett' (action clarity) - storage_init.html: replace disk-usage zone gradient bar with clean progress-bar-task - style.css: add .badge-warn and .progress-bar-task CSS classes Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -795,6 +796,8 @@ func (s *Server) settingsStorageRemoveHandler(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
|
||||
s.logger.Printf("[INFO] Storage path removed: %s", path)
|
||||
// Sync FileBrowser mounts after storage path removal
|
||||
go s.syncFileBrowserMounts()
|
||||
http.Redirect(w, r, "/settings?storage_msg=success&storage_detail="+url.QueryEscape("Adattároló eltávolítva: "+path), http.StatusFound)
|
||||
}
|
||||
|
||||
@@ -846,3 +849,112 @@ func (s *Server) settingsStorageLabelHandler(w http.ResponseWriter, r *http.Requ
|
||||
s.logger.Printf("[INFO] Storage label updated: %s → %q", path, label)
|
||||
http.Redirect(w, r, "/settings?storage_msg=success&storage_detail="+url.QueryEscape("Megnevezés módosítva: "+label), http.StatusFound)
|
||||
}
|
||||
|
||||
// syncFileBrowserMounts regenerates FileBrowser's docker-compose.yml
|
||||
// with volume mounts for all registered storage paths, then recreates the container.
|
||||
func (s *Server) syncFileBrowserMounts() {
|
||||
composePath := "/opt/docker/stacks/filebrowser/docker-compose.yml"
|
||||
|
||||
// Check if FileBrowser stack exists
|
||||
if _, err := os.Stat(composePath); os.IsNotExist(err) {
|
||||
s.logger.Printf("[WARN] FileBrowser stack not found at %s — skipping mount sync", composePath)
|
||||
return
|
||||
}
|
||||
|
||||
// Get all active storage paths
|
||||
paths := s.settings.GetStoragePaths()
|
||||
|
||||
// Read domain from .env
|
||||
envPath := "/opt/docker/stacks/filebrowser/.env"
|
||||
domain := ""
|
||||
if data, err := os.ReadFile(envPath); err == nil {
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
if strings.HasPrefix(line, "DOMAIN=") {
|
||||
domain = strings.TrimPrefix(line, "DOMAIN=")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if domain == "" {
|
||||
s.logger.Printf("[WARN] Cannot read DOMAIN from FileBrowser .env — skipping mount sync")
|
||||
return
|
||||
}
|
||||
|
||||
// Build volume mount lines
|
||||
var storageMounts []string
|
||||
for _, sp := range paths {
|
||||
mountName := filepath.Base(sp.Path) // "/mnt/hdd_1" → "hdd_1"
|
||||
line := fmt.Sprintf(" - %s:/srv/%s", sp.Path, mountName)
|
||||
storageMounts = append(storageMounts, line)
|
||||
}
|
||||
|
||||
// Generate compose from template
|
||||
compose := generateFileBrowserCompose(domain, storageMounts)
|
||||
|
||||
// Write compose
|
||||
if err := os.WriteFile(composePath, []byte(compose), 0644); err != nil {
|
||||
s.logger.Printf("[ERROR] Failed to write FileBrowser compose: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Recreate container
|
||||
cmd := exec.Command("docker", "compose", "up", "-d", "--remove-orphans")
|
||||
cmd.Dir = filepath.Dir(composePath)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
s.logger.Printf("[ERROR] Failed to recreate FileBrowser: %s — %v", string(out), err)
|
||||
} else {
|
||||
s.logger.Printf("[INFO] FileBrowser mounts synced — %d storage path(s)", len(paths))
|
||||
}
|
||||
}
|
||||
|
||||
// generateFileBrowserCompose returns a FileBrowser docker-compose.yml string
|
||||
// with the given domain and storage volume mount lines.
|
||||
func generateFileBrowserCompose(domain string, storageMounts []string) string {
|
||||
storageSection := ""
|
||||
if len(storageMounts) > 0 {
|
||||
storageSection = "\n # Storage paths (auto-generated by felhom-controller)\n" +
|
||||
strings.Join(storageMounts, "\n")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`# FileBrowser Quantum — Infrastructure file manager
|
||||
# Domain: files.%s
|
||||
# Deployed by docker-setup.sh — managed by felhom-controller
|
||||
# WARNING: Volume mounts are auto-generated. Manual edits will be overwritten.
|
||||
|
||||
services:
|
||||
filebrowser:
|
||||
image: gtstef/filebrowser:latest
|
||||
container_name: filebrowser
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Budapest
|
||||
volumes:
|
||||
- filebrowser_data:/home/filebrowser/data%s
|
||||
networks:
|
||||
- traefik-public
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256M
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.filebrowser.rule=Host(`+"`"+`files.%s`+"`"+`)"
|
||||
- "traefik.http.routers.filebrowser.entrypoints=websecure"
|
||||
- "traefik.http.routers.filebrowser.tls=true"
|
||||
- "traefik.http.services.filebrowser.loadbalancer.server.port=80"
|
||||
- "traefik.docker.network=traefik-public"
|
||||
|
||||
volumes:
|
||||
filebrowser_data:
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
`, domain, storageSection, domain)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user