diff --git a/controller/internal/web/handlers.go b/controller/internal/web/handlers.go index 36cc699..f32dfcd 100644 --- a/controller/internal/web/handlers.go +++ b/controller/internal/web/handlers.go @@ -1260,10 +1260,11 @@ func (s *Server) settingsStorageLabelHandler(w http.ResponseWriter, r *http.Requ 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. +// syncFileBrowserMounts regenerates FileBrowser's docker-compose.yml and config.yaml +// with volume mounts and sources for all registered storage paths, then recreates the container. func (s *Server) syncFileBrowserMounts() { - composePath := "/opt/docker/stacks/filebrowser/docker-compose.yml" + stackDir := "/opt/docker/stacks/filebrowser" + composePath := stackDir + "/docker-compose.yml" // Check if FileBrowser stack exists if _, err := os.Stat(composePath); os.IsNotExist(err) { @@ -1275,7 +1276,7 @@ func (s *Server) syncFileBrowserMounts() { paths := s.settings.GetStoragePaths() // Read domain from .env - envPath := "/opt/docker/stacks/filebrowser/.env" + envPath := stackDir + "/.env" domain := "" if data, err := os.ReadFile(envPath); err == nil { for _, line := range strings.Split(string(data), "\n") { @@ -1298,10 +1299,16 @@ func (s *Server) syncFileBrowserMounts() { storageMounts = append(storageMounts, line) } - // Generate compose from template - compose := generateFileBrowserCompose(domain, storageMounts) + // Generate and write config.yaml (sources + sidebar entries per drive) + configPath := stackDir + "/config.yaml" + fbConfig := generateFileBrowserConfig(paths) + if err := os.WriteFile(configPath, []byte(fbConfig), 0644); err != nil { + s.logger.Printf("[ERROR] Failed to write FileBrowser config: %v", err) + return + } - // Write compose + // Generate and write compose (includes config.yaml mount) + compose := generateFileBrowserCompose(domain, storageMounts) if err := os.WriteFile(composePath, []byte(compose), 0644); err != nil { s.logger.Printf("[ERROR] Failed to write FileBrowser compose: %v", err) return @@ -1311,11 +1318,11 @@ func (s *Server) syncFileBrowserMounts() { ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() cmd := exec.CommandContext(ctx, "docker", "compose", "up", "-d", "--remove-orphans") - cmd.Dir = filepath.Dir(composePath) + cmd.Dir = stackDir 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)) + s.logger.Printf("[INFO] FileBrowser mounts synced — %d storage path(s), config updated", len(paths)) } } @@ -1341,7 +1348,8 @@ services: environment: - TZ=Europe/Budapest volumes: - - filebrowser_data:/home/filebrowser/data%s + - filebrowser_data:/home/filebrowser/data + - ./config.yaml:/home/filebrowser/data/config.yaml:ro%s networks: - traefik-public deploy: @@ -1370,3 +1378,61 @@ networks: external: true `, domain, storageSection, domain) } + +// generateFileBrowserConfig returns a FileBrowser Quantum config.yaml with +// a separate source per registered storage path. Each source appears as a +// named sidebar entry in the FileBrowser UI. +func generateFileBrowserConfig(paths []settings.StoragePath) string { + var sources string + if len(paths) == 0 { + sources = ` - path: "/srv" +` + } else { + for _, sp := range paths { + mountName := filepath.Base(sp.Path) + label := sp.Label + if label == "" { + label = mountName + } + sources += fmt.Sprintf(" - path: \"/srv/%s\"\n name: %q\n config:\n defaultEnabled: true\n", mountName, label) + } + } + + return fmt.Sprintf(`# FileBrowser Quantum — managed by felhom-controller +# WARNING: This file is auto-generated. Manual edits will be overwritten. + +server: + port: 80 + baseURL: "/" + logging: + - levels: "info|warning|error" + sources: +%suserDefaults: + stickySidebar: true + darkMode: true + viewMode: "normal" + showHidden: false + dateFormat: false + gallerySize: 3 + themeColor: "var(--blue)" + preview: + disableHideSidebar: false + highQuality: true + image: true + video: true + motionVideoPreview: true + office: true + popup: true + autoplayMedia: true + folder: true + permissions: + api: false + admin: false + modify: false + share: false + realtime: false + delete: false + create: false + download: true +`, sources) +}