fix: standardize log prefixes, remove duplicates, add missing module tags

Second-pass logging cleanup: consistent [LEVEL] [module] format across
all 41 files. Remove stale prefixes ([CF], [SYNC], [SCHED], [API],
[STORAGE], [HEALTH], [ROLLBACK]). Remove 5 duplicate log lines. Gate
ungated DEBUG lines. Fix wrong log levels (restore start WARN→INFO).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 21:20:09 +01:00
parent 8e61cd7ec4
commit af1dd14933
41 changed files with 477 additions and 473 deletions
-3
View File
@@ -152,9 +152,6 @@ func (am *AlertManager) Refresh(report *monitor.HealthReport, cfg *config.Config
am.mu.Lock()
am.alerts = alerts
am.mu.Unlock()
am.logger.Printf("[DEBUG] AlertManager refreshed: %d alerts (%d error, %d warning)",
len(alerts), countLevel(alerts, "error"), countLevel(alerts, "warning"))
}
// GetAlerts returns a copy of the current alerts, optionally excluding specific IDs.
+4 -7
View File
@@ -140,7 +140,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
}
if attempt != nil && attempt.count >= loginMaxAttempts {
s.loginAttemptMu.Unlock()
s.logger.Printf("[WARN] Login rate limited for %s (%d attempts)", ip, attempt.count)
s.logger.Printf("[WARN] [web] Login rate limited for %s (%d attempts)", ip, attempt.count)
s.renderLogin(w, "Túl sok sikertelen próbálkozás, próbálja újra 1 perc múlva", "")
return
}
@@ -148,7 +148,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
effectiveHash := s.effectivePasswordHash()
if err := bcrypt.CompareHashAndPassword([]byte(effectiveHash), []byte(password)); err != nil {
s.logger.Printf("[WARN] Failed login from %s", r.RemoteAddr)
s.logger.Printf("[WARN] [web] Failed login from %s", r.RemoteAddr)
s.loginAttemptMu.Lock()
if s.loginAttempts[ip] == nil {
s.loginAttempts[ip] = &loginAttempt{}
@@ -181,7 +181,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
Secure: isSecure,
})
s.logger.Printf("[INFO] Login from %s", r.RemoteAddr)
s.logger.Printf("[INFO] [web] Login from %s", r.RemoteAddr)
// Redirect to ?next= target if provided, otherwise to dashboard
redirectTo := "/"
@@ -260,9 +260,6 @@ func (s *Server) invalidateAllSessions() {
s.sessions = make(map[string]*session)
s.sessionsMu.Unlock()
s.logger.Printf("[INFO] [web] All sessions invalidated (cleared %d)", count)
if s.isDebug() {
s.logger.Printf("[DEBUG] [web] invalidated all sessions (cleared %d)", count)
}
}
func (s *Server) cleanupSessions() {
@@ -310,7 +307,7 @@ func (s *Server) renderLogin(w http.ResponseWriter, errorMsg, flashMsg string) {
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := s.tmpl.ExecuteTemplate(w, "login", data); err != nil {
s.logger.Printf("[ERROR] Template error (login): %v", err)
s.logger.Printf("[ERROR] [web] Template error (login): %v", err)
http.Error(w, "Internal error", http.StatusInternalServerError)
}
}
+20 -20
View File
@@ -95,14 +95,14 @@ func (s *Server) apiExportEstimate(w http.ResponseWriter, r *http.Request) {
stackName := r.URL.Query().Get("stack")
drive := r.URL.Query().Get("drive")
s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate: stack=%q drive=%q", stackName, drive)
s.logger.Printf("[DEBUG] [web] apiExportEstimate: stack=%q drive=%q", stackName, drive)
if stackName == "" || drive == "" {
jsonError(w, "Missing stack or drive parameter", http.StatusBadRequest)
return
}
if !s.isValidDrivePath(drive) {
s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate: invalid drive path %q", drive)
s.logger.Printf("[DEBUG] [web] apiExportEstimate: invalid drive path %q", drive)
jsonError(w, "Invalid drive path", http.StatusBadRequest)
return
}
@@ -110,12 +110,12 @@ func (s *Server) apiExportEstimate(w http.ResponseWriter, r *http.Request) {
est, err := s.appExporter.EstimateExport(stackName, drive)
if err != nil {
s.logger.Printf("[ERROR] [web] Export estimate failed for %s: %v", stackName, err)
s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate error: %v", err)
s.logger.Printf("[DEBUG] [web] apiExportEstimate error: %v", err)
jsonError(w, err.Error(), http.StatusInternalServerError)
return
}
s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate: total=%s free=%s fits=%v",
s.logger.Printf("[DEBUG] [web] apiExportEstimate: total=%s free=%s fits=%v",
est.TotalSizeHuman, est.DestFreeHuman, est.FitsOnDest)
jsonResponse(w, map[string]interface{}{
"ok": true,
@@ -137,12 +137,12 @@ func (s *Server) apiExportStart(w http.ResponseWriter, r *http.Request) {
StopApp bool `json:"stop_app"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
s.logger.Printf("[DEBUG] [handler_export] apiExportStart: invalid body: %v", err)
s.logger.Printf("[DEBUG] [web] apiExportStart: invalid body: %v", err)
jsonError(w, "Invalid request body", http.StatusBadRequest)
return
}
s.logger.Printf("[DEBUG] [handler_export] apiExportStart: stack=%q drive=%q encrypted=%v stopApp=%v",
s.logger.Printf("[DEBUG] [web] apiExportStart: stack=%q drive=%q encrypted=%v stopApp=%v",
req.StackName, req.DestDrive, req.Password != "", req.StopApp)
if req.StackName == "" || req.DestDrive == "" {
@@ -151,7 +151,7 @@ func (s *Server) apiExportStart(w http.ResponseWriter, r *http.Request) {
}
if !s.isValidDrivePath(req.DestDrive) {
s.logger.Printf("[DEBUG] [handler_export] apiExportStart: invalid drive path %q", req.DestDrive)
s.logger.Printf("[DEBUG] [web] apiExportStart: invalid drive path %q", req.DestDrive)
jsonError(w, "Invalid drive path", http.StatusBadRequest)
return
}
@@ -164,12 +164,12 @@ func (s *Server) apiExportStart(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
s.logger.Printf("[ERROR] [web] Export start failed for %s: %v", req.StackName, err)
s.logger.Printf("[DEBUG] [handler_export] apiExportStart error: %v", err)
s.logger.Printf("[DEBUG] [web] apiExportStart error: %v", err)
jsonError(w, err.Error(), http.StatusConflict)
return
}
s.logger.Printf("[INFO] Export started for %s to %s", req.StackName, req.DestDrive)
s.logger.Printf("[INFO] [web] Export started for %s to %s", req.StackName, req.DestDrive)
jsonResponse(w, map[string]interface{}{"ok": true})
}
@@ -230,23 +230,23 @@ func (s *Server) apiExportManifest(w http.ResponseWriter, r *http.Request) {
return
}
s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: path=%q hasPassword=%v", req.Path, req.Password != "")
s.logger.Printf("[DEBUG] [web] apiExportManifest: path=%q hasPassword=%v", req.Path, req.Password != "")
// Security: validate path is within a registered exports directory
if !s.isValidExportPath(req.Path) {
s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: invalid path %q", req.Path)
s.logger.Printf("[DEBUG] [web] apiExportManifest: invalid path %q", req.Path)
jsonError(w, "Invalid bundle path", http.StatusBadRequest)
return
}
encrypted, _ := appexport.IsEncryptedFAB(req.Path)
s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: encrypted=%v", encrypted)
s.logger.Printf("[DEBUG] [web] apiExportManifest: encrypted=%v", encrypted)
var manifest *appexport.Manifest
var err error
if encrypted {
if req.Password == "" {
s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: encrypted, needs password")
s.logger.Printf("[DEBUG] [web] apiExportManifest: encrypted, needs password")
jsonResponse(w, map[string]interface{}{
"ok": true,
"encrypted": true,
@@ -260,12 +260,12 @@ func (s *Server) apiExportManifest(w http.ResponseWriter, r *http.Request) {
}
if err != nil {
s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: error: %v", err)
s.logger.Printf("[DEBUG] [web] apiExportManifest: error: %v", err)
jsonError(w, err.Error(), http.StatusBadRequest)
return
}
s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: app=%s display=%s size=%d",
s.logger.Printf("[DEBUG] [web] apiExportManifest: app=%s display=%s size=%d",
manifest.AppName, manifest.DisplayName, manifest.TotalSizeBytes)
jsonResponse(w, map[string]interface{}{
"ok": true,
@@ -285,12 +285,12 @@ func (s *Server) apiImportStart(w http.ResponseWriter, r *http.Request) {
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
s.logger.Printf("[DEBUG] [handler_export] apiImportStart: invalid body: %v", err)
s.logger.Printf("[DEBUG] [web] apiImportStart: invalid body: %v", err)
jsonError(w, "Invalid request body", http.StatusBadRequest)
return
}
s.logger.Printf("[DEBUG] [handler_export] apiImportStart: path=%q hasPassword=%v", req.Path, req.Password != "")
s.logger.Printf("[DEBUG] [web] apiImportStart: path=%q hasPassword=%v", req.Path, req.Password != "")
if req.Path == "" {
jsonError(w, "Missing path", http.StatusBadRequest)
@@ -298,7 +298,7 @@ func (s *Server) apiImportStart(w http.ResponseWriter, r *http.Request) {
}
if !s.isValidExportPath(req.Path) {
s.logger.Printf("[DEBUG] [handler_export] apiImportStart: invalid path %q", req.Path)
s.logger.Printf("[DEBUG] [web] apiImportStart: invalid path %q", req.Path)
jsonError(w, "Invalid bundle path", http.StatusBadRequest)
return
}
@@ -309,12 +309,12 @@ func (s *Server) apiImportStart(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
s.logger.Printf("[ERROR] [web] Import start failed for %s: %v", req.Path, err)
s.logger.Printf("[DEBUG] [handler_export] apiImportStart error: %v", err)
s.logger.Printf("[DEBUG] [web] apiImportStart error: %v", err)
jsonError(w, err.Error(), http.StatusConflict)
return
}
s.logger.Printf("[INFO] Import started from %s", req.Path)
s.logger.Printf("[INFO] [web] Import started from %s", req.Path)
jsonResponse(w, map[string]interface{}{"ok": true})
}
+8 -8
View File
@@ -111,7 +111,7 @@ func (s *Server) apiRestoreSkip(w http.ResponseWriter, r *http.Request) {
return
}
s.logger.Println("[INFO] User skipped DR restore — entering normal mode")
s.logger.Println("[INFO] [web] User skipped DR restore — entering normal mode")
s.clearRestoreMode()
jsonResponse(w, map[string]interface{}{
@@ -122,14 +122,14 @@ func (s *Server) apiRestoreSkip(w http.ResponseWriter, r *http.Request) {
// executeAllRestores runs the restore for each pending app sequentially.
func (s *Server) executeAllRestores() {
s.logger.Println("[INFO] Starting DR restore for all apps")
s.logger.Println("[INFO] [web] Starting DR restore for all apps")
restoreStart := time.Now()
s.restoreMu.RLock()
plan := s.restorePlan
s.restoreMu.RUnlock()
if plan == nil {
s.logger.Println("[WARN] Restore plan cleared before execution could start")
s.logger.Println("[WARN] [web] Restore plan cleared before execution could start")
return
}
@@ -155,7 +155,7 @@ func (s *Server) executeAllRestores() {
}
plan.UpdateApp(app.Name, "restoring", "")
s.logger.Printf("[INFO] Restoring app %s (%s)", app.Name, app.DisplayName)
s.logger.Printf("[INFO] [web] Restoring app %s (%s)", app.Name, app.DisplayName)
appStart := time.Now()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
@@ -164,14 +164,14 @@ func (s *Server) executeAllRestores() {
if err != nil {
plan.UpdateApp(app.Name, "failed", err.Error())
s.logger.Printf("[ERROR] Restore failed for %s: %v", app.Name, err)
s.logger.Printf("[ERROR] [web] Restore failed for %s: %v", app.Name, err)
if s.isDebug() {
s.logger.Printf("[DEBUG] [web] executeAllRestores: app=%s failed after %s", app.Name, time.Since(appStart))
}
failCount++
} else {
plan.UpdateApp(app.Name, "done", "")
s.logger.Printf("[INFO] Restore completed for %s", app.Name)
s.logger.Printf("[INFO] [web] Restore completed for %s", app.Name)
if s.isDebug() {
s.logger.Printf("[DEBUG] [web] executeAllRestores: app=%s completed in %s", app.Name, time.Since(appStart))
}
@@ -180,7 +180,7 @@ func (s *Server) executeAllRestores() {
}
plan.SetStatus("done")
s.logger.Println("[INFO] All app restores completed")
s.logger.Println("[INFO] [web] All app restores completed")
if s.isDebug() {
s.logger.Printf("[DEBUG] [web] executeAllRestores: total=%d success=%d fail=%d elapsed=%s", pendingCount, successCount, failCount, time.Since(restoreStart))
}
@@ -193,7 +193,7 @@ func (s *Server) executeAllRestores() {
// Re-scan stacks so dashboard picks up restored apps
if s.stackMgr != nil {
if err := s.stackMgr.ScanStacks(); err != nil {
s.logger.Printf("[WARN] Post-restore stack scan failed: %v", err)
s.logger.Printf("[WARN] [web] Post-restore stack scan failed: %v", err)
}
}
}
+28 -28
View File
@@ -992,7 +992,7 @@ func (s *Server) settingsCrossBackupHandler(w http.ResponseWriter, r *http.Reque
}
}
if !validDest {
s.logger.Printf("[WARN] Cross-drive backup: rejected invalid dest path %q for %s", destPath, name)
s.logger.Printf("[WARN] [web] Cross-drive backup: rejected invalid dest path %q for %s", destPath, name)
http.Redirect(w, r, "/stacks/"+name+"/deploy?flash_error="+url.QueryEscape("Érvénytelen célútvonal: "+destPath), http.StatusFound)
return
}
@@ -1016,12 +1016,12 @@ func (s *Server) settingsCrossBackupHandler(w http.ResponseWriter, r *http.Reque
}
if err := s.settings.SetCrossDriveConfig(name, cfg); err != nil {
s.logger.Printf("[ERROR] Failed to save cross-drive config for %s: %v", name, err)
s.logger.Printf("[ERROR] [web] Failed to save cross-drive config for %s: %v", name, err)
http.Redirect(w, r, "/stacks/"+name+"/deploy?flash_error=Hiba+a+ment%C3%A9si+be%C3%A1ll%C3%ADt%C3%A1s+ment%C3%A9sakor", http.StatusFound)
return
}
s.logger.Printf("[INFO] Cross-drive backup config saved for %s: dest=%s schedule=%s enabled=%v",
s.logger.Printf("[INFO] [web] Cross-drive backup config saved for %s: dest=%s schedule=%s enabled=%v",
name, destPath, schedule, enabled)
http.Redirect(w, r, "/stacks/"+name+"/deploy?flash=Ment%C3%A9si+be%C3%A1ll%C3%ADt%C3%A1s+mentve.", http.StatusFound)
@@ -1047,11 +1047,11 @@ func (s *Server) backupRestoreHandler(w http.ResponseWriter, r *http.Request) {
return
}
s.logger.Printf("[WARN] Restore requested: stack=%s, snapshot=%s from %s", stackName, snapshotID, r.RemoteAddr)
s.logger.Printf("[WARN] [web] Restore requested: stack=%s, snapshot=%s from %s", stackName, snapshotID, r.RemoteAddr)
start := time.Now()
if err := s.backupMgr.RestoreApp(stackName, snapshotID); err != nil {
s.logger.Printf("[ERROR] Restore failed: %v", err)
s.logger.Printf("[ERROR] [web] Restore failed: %v", err)
if s.isDebug() {
s.logger.Printf("[DEBUG] [web] backupRestoreHandler: stack=%s failed after %s", stackName, time.Since(start))
}
@@ -1223,7 +1223,7 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request)
// Generate bcrypt hash
hash, err := bcrypt.GenerateFromPassword([]byte(newPassword), 10)
if err != nil {
s.logger.Printf("[ERROR] Failed to hash new password: %v", err)
s.logger.Printf("[ERROR] [web] Failed to hash new password: %v", err)
data["PasswordError"] = "Belső hiba a jelszó mentésekor"
s.executeTemplate(w, r, "settings", data)
return
@@ -1231,13 +1231,13 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request)
// Save to settings.json
if err := s.settings.SetPasswordHash(string(hash)); err != nil {
s.logger.Printf("[ERROR] Failed to save password to settings.json: %v", err)
s.logger.Printf("[ERROR] [web] Failed to save password to settings.json: %v", err)
data["PasswordError"] = "Belső hiba a jelszó mentésekor"
s.executeTemplate(w, r, "settings", data)
return
}
s.logger.Printf("[INFO] Password changed via settings page from %s", r.RemoteAddr)
s.logger.Printf("[INFO] [web] Password changed via settings page from %s", r.RemoteAddr)
// Invalidate all sessions (force re-login)
s.invalidateAllSessions()
@@ -1297,20 +1297,20 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req
}
if err := s.settings.SetNotificationPrefs(prefs); err != nil {
s.logger.Printf("[ERROR] Failed to save notification prefs: %v", err)
s.logger.Printf("[ERROR] [web] Failed to save notification prefs: %v", err)
data := s.settingsData()
data["NotificationError"] = "Hiba a beállítások mentésekor"
s.executeTemplate(w, r, "settings", data)
return
}
s.logger.Printf("[INFO] Notification preferences updated: email=%s, events=%v", email, enabledEvents)
s.logger.Printf("[INFO] [web] Notification preferences updated: email=%s, events=%v", email, enabledEvents)
// Sync preferences to hub
data := s.settingsData()
if s.notifier != nil && s.notifier.IsEnabled() {
if err := s.notifier.SyncPreferences(email, enabledEvents, cooldownHours); err != nil {
s.logger.Printf("[WARN] Failed to sync preferences to hub: %v", err)
s.logger.Printf("[WARN] [web] Failed to sync preferences to hub: %v", err)
data["NotificationSuccess"] = fmt.Sprintf("Értesítési beállítások mentve (helyi). A központi szinkronizálás sikertelen: %v", err)
} else {
data["NotificationSuccess"] = "Értesítési beállítások mentve."
@@ -1332,7 +1332,7 @@ func (s *Server) settingsNotificationsTestHandler(w http.ResponseWriter, r *http
err := s.notifier.SendTest()
if err != nil {
s.logger.Printf("[ERROR] Test notification failed: %v", err)
s.logger.Printf("[ERROR] [web] Test notification failed: %v", err)
data["NotificationError"] = fmt.Sprintf("Teszt email küldése sikertelen: %v", err)
s.executeTemplate(w, r, "settings", data)
return
@@ -1486,7 +1486,7 @@ func (s *Server) settingsStorageAddHandler(w http.ResponseWriter, r *http.Reques
// 5. Soft warning if not under /mnt/
if !strings.HasPrefix(path, "/mnt/") {
s.logger.Printf("[WARN] Storage path %s is not under /mnt/ — unusual but allowed", path)
s.logger.Printf("[WARN] [web] Storage path %s is not under /mnt/ — unusual but allowed", path)
}
sp := settings.StoragePath{
@@ -1498,13 +1498,13 @@ func (s *Server) settingsStorageAddHandler(w http.ResponseWriter, r *http.Reques
}
if err := s.settings.AddStoragePath(sp); err != nil {
s.logger.Printf("[ERROR] Failed to add storage path: %v", err)
s.logger.Printf("[ERROR] [web] Failed to add storage path: %v", err)
data["StorageError"] = "Hiba a mentés során."
s.executeTemplate(w, r, "settings", data)
return
}
s.logger.Printf("[INFO] Storage path added: %s (%s)", path, label)
s.logger.Printf("[INFO] [web] Storage path added: %s (%s)", path, label)
go s.SyncFileBrowserMounts()
http.Redirect(w, r, "/settings?storage_msg=success&storage_detail="+url.QueryEscape("Adattároló sikeresen hozzáadva: "+path), http.StatusFound)
}
@@ -1549,7 +1549,7 @@ func (s *Server) settingsStorageRemoveHandler(w http.ResponseWriter, r *http.Req
return
}
s.logger.Printf("[INFO] Storage path removed: %s", path)
s.logger.Printf("[INFO] [web] 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)
@@ -1564,7 +1564,7 @@ func (s *Server) settingsStorageDefaultHandler(w http.ResponseWriter, r *http.Re
}
if err := s.settings.SetDefaultStoragePath(path); err != nil {
s.logger.Printf("[ERROR] Failed to set default storage path: %v", err)
s.logger.Printf("[ERROR] [web] Failed to set default storage path: %v", err)
http.Redirect(w, r, "/settings", http.StatusFound)
return
}
@@ -1582,7 +1582,7 @@ func (s *Server) settingsStorageSchedulableHandler(w http.ResponseWriter, r *htt
}
if err := s.settings.SetSchedulable(path, schedulable); err != nil {
s.logger.Printf("[ERROR] Failed to update schedulable: %v", err)
s.logger.Printf("[ERROR] [web] Failed to update schedulable: %v", err)
http.Redirect(w, r, "/settings", http.StatusFound)
return
}
@@ -1607,14 +1607,14 @@ func (s *Server) settingsStorageLabelHandler(w http.ResponseWriter, r *http.Requ
}
if err := s.settings.SetStorageLabel(path, label); err != nil {
s.logger.Printf("[ERROR] Failed to set storage label: %v", err)
s.logger.Printf("[ERROR] [web] Failed to set storage label: %v", err)
data := s.settingsData()
data["StorageError"] = "Hiba a megnevezés mentésekor."
s.executeTemplate(w, r, "settings", data)
return
}
s.logger.Printf("[INFO] Storage label updated: %s → %q", path, label)
s.logger.Printf("[INFO] [web] 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)
}
@@ -1641,7 +1641,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) {
// 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)
s.logger.Printf("[WARN] [web] FileBrowser stack not found at %s — skipping mount sync", composePath)
return
}
@@ -1651,7 +1651,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) {
// Use domain from controller config
domain := s.cfg.Customer.Domain
if domain == "" {
s.logger.Printf("[WARN] Cannot sync FileBrowser mounts — customer domain not configured")
s.logger.Printf("[WARN] [web] Cannot sync FileBrowser mounts — customer domain not configured")
return
}
@@ -1675,7 +1675,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) {
}
if err := os.WriteFile(configPath, []byte(fbConfig), 0644); err != nil {
s.logger.Printf("[ERROR] Failed to write FileBrowser config: %v", err)
s.logger.Printf("[ERROR] [web] Failed to write FileBrowser config: %v", err)
return
}
@@ -1687,7 +1687,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) {
// 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)
s.logger.Printf("[ERROR] [web] Failed to write FileBrowser compose: %v", err)
return
}
@@ -1695,13 +1695,13 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) {
// nuke the data volume so FileBrowser re-reads config.yaml from scratch.
// Normal operations skip this to preserve user accounts, permissions, and share links.
if sourcesChanged && resetDBOnChange {
s.logger.Printf("[INFO] FileBrowser sources changed — resetting database (restore mode)")
s.logger.Printf("[INFO] [web] FileBrowser sources changed — resetting database (restore mode)")
resetCtx, resetCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer resetCancel()
stop := exec.CommandContext(resetCtx, "docker", "compose", "down", "-v")
stop.Dir = stackDir
if out, err := stop.CombinedOutput(); err != nil {
s.logger.Printf("[WARN] FileBrowser down -v: %s — %v", strings.TrimSpace(string(out)), err)
s.logger.Printf("[WARN] [web] FileBrowser down -v: %s — %v", strings.TrimSpace(string(out)), err)
}
}
@@ -1711,9 +1711,9 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) {
cmd := exec.CommandContext(ctx, "docker", "compose", "up", "-d", "--force-recreate", "--remove-orphans")
cmd.Dir = stackDir
if out, err := cmd.CombinedOutput(); err != nil {
s.logger.Printf("[ERROR] Failed to recreate FileBrowser: %s — %v", string(out), err)
s.logger.Printf("[ERROR] [web] Failed to recreate FileBrowser: %s — %v", string(out), err)
} else {
s.logger.Printf("[INFO] FileBrowser mounts synced — %d storage path(s), config updated", len(paths))
s.logger.Printf("[INFO] [web] FileBrowser mounts synced — %d storage path(s), config updated", len(paths))
}
}
+6 -6
View File
@@ -118,11 +118,11 @@ func NewServer(cfg *config.Config, stackMgr *stacks.Manager, cpuCollector *syste
// Log auth source on startup
if sett != nil && sett.GetPasswordHash() != "" {
logger.Printf("[INFO] Auth: using password from settings.json")
logger.Printf("[INFO] [web] Auth: using password from settings.json")
} else if cfg.Web.PasswordHash != "" {
logger.Printf("[INFO] Auth: using password from controller.yaml")
logger.Printf("[INFO] [web] Auth: using password from controller.yaml")
} else {
logger.Printf("[INFO] Auth: no password configured — dashboard is open")
logger.Printf("[INFO] [web] Auth: no password configured — dashboard is open")
}
// Sync FileBrowser config on startup to ensure mounts and sources are current.
@@ -416,7 +416,7 @@ func (s *Server) serveCatchAll(w http.ResponseWriter, r *http.Request, host stri
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
if err := s.tmpl.ExecuteTemplate(w, "catchall", data); err != nil {
s.logger.Printf("[ERROR] Catch-all template error: %v", err)
s.logger.Printf("[ERROR] [web] Catch-all template error: %v", err)
http.Error(w, "Internal error", http.StatusInternalServerError)
}
}
@@ -459,7 +459,7 @@ func (s *Server) primaryHDDPath() string {
func (s *Server) render(w http.ResponseWriter, name string, data interface{}) {
var buf bytes.Buffer
if err := s.tmpl.ExecuteTemplate(&buf, name, data); err != nil {
s.logger.Printf("[ERROR] Template error (%s): %v", name, err)
s.logger.Printf("[ERROR] [web] Template error (%s): %v", name, err)
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
@@ -477,7 +477,7 @@ func (s *Server) executeTemplate(w http.ResponseWriter, r *http.Request, name st
data["CSRFToken"] = s.csrfToken(r)
var buf bytes.Buffer
if err := s.tmpl.ExecuteTemplate(&buf, name, data); err != nil {
s.logger.Printf("[ERROR] Template error (%s): %v", name, err)
s.logger.Printf("[ERROR] [web] Template error (%s): %v", name, err)
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
+28 -28
View File
@@ -206,7 +206,7 @@ func (s *Server) storageScanAPIHandler(w http.ResponseWriter, r *http.Request) {
}
result, err := storage.ScanDisks(s.logger, s.cfg.Logging.Level == "debug")
if err != nil {
s.logger.Printf("[ERROR] storageScan: %v", err)
s.logger.Printf("[ERROR] [web] storageScan: %v", err)
jsonError(w, "Meghajtók keresése sikertelen: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -256,7 +256,7 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) {
return
}
s.logger.Printf("[INFO] Storage init started: device=%s mountName=%s by %s", req.DevicePath, req.MountName, r.RemoteAddr)
s.logger.Printf("[INFO] [web] Storage init started: device=%s mountName=%s by %s", req.DevicePath, req.MountName, r.RemoteAddr)
fmtReq := storage.FormatRequest{
DevicePath: req.DevicePath,
@@ -274,7 +274,7 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) {
if scanResult, scanErr := storage.ScanDisks(s.logger, s.cfg.Logging.Level == "debug"); scanErr == nil {
for _, disk := range scanResult.AvailableDisks {
if disk.Path == req.DevicePath && len(disk.Partitions) == 1 && disk.Partitions[0].FSType == "" {
s.logger.Printf("[INFO] Disk %s has 1 empty partition (%s) — skipping repartition",
s.logger.Printf("[INFO] [web] Disk %s has 1 empty partition (%s) — skipping repartition",
req.DevicePath, disk.Partitions[0].Path)
fmtReq.DevicePath = disk.Partitions[0].Path
fmtReq.CreatePartition = false
@@ -297,7 +297,7 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) {
close(progressCh)
if err != nil {
s.logger.Printf("[ERROR] Storage init failed: %v", err)
s.logger.Printf("[ERROR] [web] Storage init failed: %v", err)
return
}
@@ -314,9 +314,9 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) {
AddedAt: time.Now().UTC().Format(time.RFC3339),
}
if err := s.settings.AddStoragePath(sp); err != nil {
s.logger.Printf("[WARN] Failed to register storage path after init: %v", err)
s.logger.Printf("[WARN] [web] Failed to register storage path after init: %v", err)
} else {
s.logger.Printf("[INFO] Storage path registered: %s (%s)", mountPath, label)
s.logger.Printf("[INFO] [web] Storage path registered: %s (%s)", mountPath, label)
// Sync FileBrowser mounts with new storage path
s.SyncFileBrowserMounts()
}
@@ -505,7 +505,7 @@ func (s *Server) storageMigrateAPIHandler(w http.ResponseWriter, r *http.Request
return
}
s.logger.Printf("[INFO] Migration started: stack=%s from=%s to=%s by %s",
s.logger.Printf("[INFO] [web] Migration started: stack=%s from=%s to=%s by %s",
req.StackName, currentHDDPath, req.TargetPath, r.RemoteAddr)
migrReq := storage.MigrateRequest{
@@ -551,9 +551,9 @@ func (s *Server) storageMigrateAPIHandler(w http.ResponseWriter, r *http.Request
}()
if err := orch.RunEnhancedMigration(migrReq, stopFn, startFn, updateFn, opts, progressCh); err != nil {
s.logger.Printf("[ERROR] Migration failed: stack=%s: %v", req.StackName, err)
s.logger.Printf("[ERROR] [web] Migration failed: stack=%s: %v", req.StackName, err)
} else {
s.logger.Printf("[INFO] Migration complete: stack=%s → %s", req.StackName, req.TargetPath)
s.logger.Printf("[INFO] [web] Migration complete: stack=%s → %s", req.StackName, req.TargetPath)
// Sync FileBrowser mounts (storage paths may now have new app data)
go s.SyncFileBrowserMounts()
}
@@ -879,7 +879,7 @@ func (s *Server) staleDataCleanupHandler(w http.ResponseWriter, r *http.Request)
// Safety: never delete protected top-level dirs
if protected != nil && protected[cleanPath] {
s.logger.Printf("[WARN] Refusing to delete protected HDD path: %s", cleanPath)
s.logger.Printf("[WARN] [web] Refusing to delete protected HDD path: %s", cleanPath)
errors = append(errors, fmt.Sprintf("Védett útvonal, nem törölhető: %s", cleanPath))
continue
}
@@ -893,10 +893,10 @@ func (s *Server) staleDataCleanupHandler(w http.ResponseWriter, r *http.Request)
size := dirSizeInt64(cleanPath)
if err := os.RemoveAll(cleanPath); err != nil {
s.logger.Printf("[ERROR] Failed to remove stale data %s: %v", cleanPath, err)
s.logger.Printf("[ERROR] [web] Failed to remove stale data %s: %v", cleanPath, err)
errors = append(errors, fmt.Sprintf("Törlés sikertelen: %s — %v", cleanPath, err))
} else {
s.logger.Printf("[INFO] Removed stale data: %s (%s) for stack %s", cleanPath, dirSizeBytesHuman(size), req.StackName)
s.logger.Printf("[INFO] [web] Removed stale data: %s (%s) for stack %s", cleanPath, dirSizeBytesHuman(size), req.StackName)
deleted = append(deleted, cleanPath)
totalFreed += size
}
@@ -952,7 +952,7 @@ func (s *Server) storageAttachMountRawHandler(w http.ResponseWriter, r *http.Req
rawPath, err := storage.MountRaw(req.DevicePath)
if err != nil {
s.diskJobMu.Unlock()
s.logger.Printf("[ERROR] storageAttachMountRaw: %v", err)
s.logger.Printf("[ERROR] [web] storageAttachMountRaw: %v", err)
jsonError(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -960,7 +960,7 @@ func (s *Server) storageAttachMountRawHandler(w http.ResponseWriter, r *http.Req
s.activeRawMount = rawPath
s.diskJobMu.Unlock()
s.logger.Printf("[INFO] Raw mount for attach: %s → %s", req.DevicePath, rawPath)
s.logger.Printf("[INFO] [web] Raw mount for attach: %s → %s", req.DevicePath, rawPath)
jsonResponse(w, map[string]interface{}{
"ok": true,
@@ -1026,7 +1026,7 @@ func (s *Server) storageAttachMkdirHandler(w http.ResponseWriter, r *http.Reques
return
}
s.logger.Printf("[INFO] Created directory for attach: %s", createdPath)
s.logger.Printf("[INFO] [web] Created directory for attach: %s", createdPath)
jsonResponse(w, map[string]interface{}{
"ok": true,
@@ -1064,7 +1064,7 @@ func (s *Server) storageAttachAPIHandler(w http.ResponseWriter, r *http.Request)
return
}
s.logger.Printf("[INFO] Storage attach started: device=%s mountName=%s subPath=%s by %s",
s.logger.Printf("[INFO] [web] Storage attach started: device=%s mountName=%s subPath=%s by %s",
req.DevicePath, req.MountName, req.SubPath, r.RemoteAddr)
attachReq := storage.AttachRequest{
@@ -1089,7 +1089,7 @@ func (s *Server) storageAttachAPIHandler(w http.ResponseWriter, r *http.Request)
close(progressCh)
if err != nil {
s.logger.Printf("[ERROR] Storage attach failed: %v", err)
s.logger.Printf("[ERROR] [web] Storage attach failed: %v", err)
return
}
@@ -1111,9 +1111,9 @@ func (s *Server) storageAttachAPIHandler(w http.ResponseWriter, r *http.Request)
AddedAt: time.Now().UTC().Format(time.RFC3339),
}
if err := s.settings.AddStoragePath(sp); err != nil {
s.logger.Printf("[WARN] Failed to register storage path after attach: %v", err)
s.logger.Printf("[WARN] [web] Failed to register storage path after attach: %v", err)
} else {
s.logger.Printf("[INFO] Storage path registered: %s (%s)", mountPath, label)
s.logger.Printf("[INFO] [web] Storage path registered: %s (%s)", mountPath, label)
s.SyncFileBrowserMounts()
}
}()
@@ -1169,9 +1169,9 @@ func (s *Server) storageAttachCancelHandler(w http.ResponseWriter, r *http.Reque
if rawMount != "" {
if err := storage.CleanupRawMount(rawMount); err != nil {
s.logger.Printf("[WARN] Failed to cleanup raw mount %s: %v", rawMount, err)
s.logger.Printf("[WARN] [web] Failed to cleanup raw mount %s: %v", rawMount, err)
} else {
s.logger.Printf("[INFO] Cleaned up raw mount: %s", rawMount)
s.logger.Printf("[INFO] [web] Cleaned up raw mount: %s", rawMount)
}
}
@@ -1217,7 +1217,7 @@ func (s *Server) storageDisconnectHandler(w http.ResponseWriter, r *http.Request
stoppedStacks, err := s.storageWatchdog.SafeDisconnect(r.Context(), req.Path)
if err != nil {
s.logger.Printf("[ERROR] Safe disconnect %s: %v", req.Path, err)
s.logger.Printf("[ERROR] [web] Safe disconnect %s: %v", req.Path, err)
jsonError(w, fmt.Sprintf("Leválasztás sikertelen: %v", err), http.StatusInternalServerError)
return
}
@@ -1260,7 +1260,7 @@ func (s *Server) storageReconnectHandler(w http.ResponseWriter, r *http.Request)
stoppedStacks, err := s.storageWatchdog.Reconnect(r.Context(), req.Path)
if err != nil {
s.logger.Printf("[ERROR] Reconnect %s: %v", req.Path, err)
s.logger.Printf("[ERROR] [web] Reconnect %s: %v", req.Path, err)
jsonError(w, fmt.Sprintf("Csatlakoztatás sikertelen: %v", err), http.StatusInternalServerError)
return
}
@@ -1482,7 +1482,7 @@ func (s *Server) driveMigrateAPIHandler(w http.ResponseWriter, r *http.Request)
return
}
s.logger.Printf("[INFO] Drive migration started: %s → %s by %s", req.SourcePath, req.DestPath, r.RemoteAddr)
s.logger.Printf("[INFO] [web] Drive migration started: %s → %s by %s", req.SourcePath, req.DestPath, r.RemoteAddr)
go func() {
ctx := context.Background()
@@ -1498,9 +1498,9 @@ func (s *Server) driveMigrateAPIHandler(w http.ResponseWriter, r *http.Request)
DestPath: req.DestPath,
}
if err := s.driveMigrator.MigrateDrive(ctx, migrReq, progressCh); err != nil {
s.logger.Printf("[ERROR] Drive migration failed: %v", err)
s.logger.Printf("[ERROR] [web] Drive migration failed: %v", err)
} else {
s.logger.Printf("[INFO] Drive migration complete: %s → %s", req.SourcePath, req.DestPath)
s.logger.Printf("[INFO] [web] Drive migration complete: %s → %s", req.SourcePath, req.DestPath)
go s.SyncFileBrowserMounts()
}
close(progressCh)
@@ -1580,12 +1580,12 @@ func (s *Server) decommissionRemoveHandler(w http.ResponseWriter, r *http.Reques
}
if err := s.settings.RemoveStoragePath(req.Path); err != nil {
s.logger.Printf("[ERROR] Failed to remove decommissioned path %s: %v", req.Path, err)
s.logger.Printf("[ERROR] [web] Failed to remove decommissioned path %s: %v", req.Path, err)
jsonError(w, "Eltávolítás sikertelen: "+err.Error(), http.StatusInternalServerError)
return
}
s.logger.Printf("[INFO] Decommissioned storage path removed: %s", req.Path)
s.logger.Printf("[INFO] [web] Decommissioned storage path removed: %s", req.Path)
// For form submissions, redirect back to settings
if r.Header.Get("Content-Type") != "application/json" {