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
+10 -10
View File
@@ -66,18 +66,18 @@ func (r *Router) geoUpdateSettings(w http.ResponseWriter, req *http.Request) {
}
if err := r.sett.SetGeoRestriction(geo); err != nil {
r.logger.Printf("[API] Failed to save geo settings: %v", err)
r.logger.Printf("[ERROR] [api] Failed to save geo settings: %v", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
r.logger.Printf("[API] Geo settings updated: enabled=%v, countries=%v", body.Enabled, body.AllowedCountries)
r.logger.Printf("[INFO] [api] Geo settings updated: enabled=%v, countries=%v", body.Enabled, body.AllowedCountries)
// Trigger async CF sync
if r.geoSync != nil {
go func() {
if err := r.geoSync.Sync(context.Background()); err != nil {
r.logger.Printf("[API] Geo sync after settings update failed: %v", err)
r.logger.Printf("[ERROR] [api] Geo sync after settings update failed: %v", err)
}
}()
}
@@ -93,7 +93,7 @@ func (r *Router) geoTriggerSync(w http.ResponseWriter, _ *http.Request) {
go func() {
if err := r.geoSync.Sync(context.Background()); err != nil {
r.logger.Printf("[API] Manual geo sync failed: %v", err)
r.logger.Printf("[ERROR] [api] Manual geo sync failed: %v", err)
}
}()
@@ -135,18 +135,18 @@ func (r *Router) geoSetAppOverride(w http.ResponseWriter, req *http.Request, app
override := &settings.AppGeoOverride{AllowedCountries: body.AllowedCountries}
if err := r.sett.SetGeoAppOverride(appName, override); err != nil {
r.logger.Printf("[API] Failed to save geo override for %s: %v", appName, err)
r.logger.Printf("[ERROR] [api] Failed to save geo override for %s: %v", appName, err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
r.logger.Printf("[API] Geo override set for %s: countries=%v", appName, body.AllowedCountries)
r.logger.Printf("[INFO] [api] Geo override set for %s: countries=%v", appName, body.AllowedCountries)
// Trigger async CF sync
if r.geoSync != nil {
go func() {
if err := r.geoSync.Sync(context.Background()); err != nil {
r.logger.Printf("[API] Geo sync after app override failed: %v", err)
r.logger.Printf("[ERROR] [api] Geo sync after app override failed: %v", err)
}
}()
}
@@ -161,18 +161,18 @@ func (r *Router) geoRemoveAppOverride(w http.ResponseWriter, _ *http.Request, ap
}
if err := r.sett.RemoveGeoAppOverride(appName); err != nil {
r.logger.Printf("[API] Failed to remove geo override for %s: %v", appName, err)
r.logger.Printf("[ERROR] [api] Failed to remove geo override for %s: %v", appName, err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
r.logger.Printf("[API] Geo override removed for %s", appName)
r.logger.Printf("[INFO] [api] Geo override removed for %s", appName)
// Trigger async CF sync
if r.geoSync != nil {
go func() {
if err := r.geoSync.Sync(context.Background()); err != nil {
r.logger.Printf("[API] Geo sync after override removal failed: %v", err)
r.logger.Printf("[ERROR] [api] Geo sync after override removal failed: %v", err)
}
}()
}
+34 -34
View File
@@ -314,14 +314,14 @@ func (r *Router) listStacks(w http.ResponseWriter, _ *http.Request) {
}
func (r *Router) rescanStacks(w http.ResponseWriter, _ *http.Request) {
r.logger.Printf("[API] Manual stack rescan requested")
r.logger.Printf("[INFO] [api] Manual stack rescan requested")
if err := r.stackMgr.ScanStacks(); err != nil {
r.logger.Printf("[API] Stack rescan failed: %v", err)
r.logger.Printf("[ERROR] [api] Stack rescan failed: %v", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
stackCount := len(r.stackMgr.GetStacks())
r.logger.Printf("[API] Stack rescan completed: %d stacks found", stackCount)
r.logger.Printf("[INFO] [api] Stack rescan completed: %d stacks found", stackCount)
writeJSON(w, http.StatusOK, apiResponse{
OK: true,
Message: fmt.Sprintf("Rescan completed: %d stacks found", stackCount),
@@ -355,7 +355,7 @@ func (r *Router) getDeployFields(w http.ResponseWriter, _ *http.Request, name st
func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name string) {
limitBody(w, req)
r.logger.Printf("[API] Deploy requested for stack: %s", name)
r.logger.Printf("[INFO] [api] Deploy requested for stack: %s", name)
r.dbg("deployStack: name=%s contentLength=%d", name, req.ContentLength)
var body struct {
@@ -373,7 +373,7 @@ func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name stri
warning, err := r.stackMgr.DeployStack(deployReq)
if err != nil {
r.logger.Printf("[API] Deploy failed for %s: %v", name, err)
r.logger.Printf("[ERROR] [api] Deploy failed for %s: %v", name, err)
status := http.StatusInternalServerError
if strings.Contains(err.Error(), "already deployed") {
status = http.StatusConflict
@@ -412,7 +412,7 @@ func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name stri
}
func (r *Router) actionStack(w http.ResponseWriter, action, name string) {
r.logger.Printf("[API] %s requested for stack: %s", action, name)
r.logger.Printf("[INFO] [api] %s requested for stack: %s", action, name)
r.dbg("actionStack: action=%s name=%s", action, name)
// Protected stacks only allow restart — block all other actions
@@ -483,7 +483,7 @@ func (r *Router) actionStack(w http.ResponseWriter, action, name string) {
func (r *Router) updateOptionalConfig(w http.ResponseWriter, req *http.Request, name string) {
limitBody(w, req)
r.logger.Printf("[API] Optional config update requested for stack: %s", name)
r.logger.Printf("[INFO] [api] Optional config update requested for stack: %s", name)
var body struct {
Values map[string]string `json:"values"`
@@ -494,7 +494,7 @@ func (r *Router) updateOptionalConfig(w http.ResponseWriter, req *http.Request,
}
if err := r.stackMgr.UpdateOptionalConfig(name, body.Values); err != nil {
r.logger.Printf("[API] Optional config update failed for %s: %v", name, err)
r.logger.Printf("[ERROR] [api] Optional config update failed for %s: %v", name, err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
@@ -535,11 +535,11 @@ func (r *Router) toggleIntegration(w http.ResponseWriter, req *http.Request, pro
if !body.Enabled {
action = "disable"
}
r.logger.Printf("[API] Integration %s requested: %s:%s", action, provider, target)
r.logger.Printf("[INFO] [api] Integration %s requested: %s:%s", action, provider, target)
state, err := r.integrationMgr.Toggle(req.Context(), provider, target, body.Enabled)
if err != nil {
r.logger.Printf("[API] Integration toggle failed for %s:%s: %v", provider, target, err)
r.logger.Printf("[ERROR] [api] Integration toggle failed for %s:%s: %v", provider, target, err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
@@ -605,7 +605,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri
return
}
limitBody(w, req)
r.logger.Printf("[API] Remove requested for stack: %s", name)
r.logger.Printf("[INFO] [api] Remove requested for stack: %s", name)
r.dbg("removeStack: name=%s", name)
var body struct {
@@ -632,7 +632,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri
resp, err := r.stackMgr.RemoveStack(name, body.RemoveHDDData, backupPaths)
if err != nil {
r.logger.Printf("[API] Remove failed for %s: %v", name, err)
r.logger.Printf("[ERROR] [api] Remove failed for %s: %v", name, err)
status := http.StatusInternalServerError
if strings.Contains(err.Error(), "protected") {
status = http.StatusForbidden
@@ -650,7 +650,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri
// Clean up cross-drive backup config for this stack
if r.sett != nil {
if err := r.sett.SetCrossDriveConfig(name, nil); err != nil {
r.logger.Printf("[WARN] Failed to clean cross-drive config for %s: %v", name, err)
r.logger.Printf("[WARN] [api] Failed to clean cross-drive config for %s: %v", name, err)
}
}
@@ -674,7 +674,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri
func (r *Router) deleteStack(w http.ResponseWriter, req *http.Request, name string) {
limitBody(w, req)
r.logger.Printf("[API] Delete requested for stack: %s", name)
r.logger.Printf("[INFO] [api] Delete requested for stack: %s", name)
var body struct {
RemoveHDDData bool `json:"remove_hdd_data"`
@@ -685,7 +685,7 @@ func (r *Router) deleteStack(w http.ResponseWriter, req *http.Request, name stri
resp, err := r.stackMgr.DeleteStack(name, body.RemoveHDDData)
if err != nil {
r.logger.Printf("[API] Delete failed for %s: %v", name, err)
r.logger.Printf("[ERROR] [api] Delete failed for %s: %v", name, err)
status := http.StatusInternalServerError
if strings.Contains(err.Error(), "protected") {
status = http.StatusForbidden
@@ -704,13 +704,13 @@ func (r *Router) deleteStack(w http.ResponseWriter, req *http.Request, name stri
}
func (r *Router) triggerSync(w http.ResponseWriter, _ *http.Request) {
r.logger.Println("[API] Manual catalog sync requested")
r.logger.Println("[INFO] [api] Manual catalog sync requested")
result := r.syncer.TriggerSync()
if !result.OK {
writeJSON(w, http.StatusTooManyRequests, apiResponse{OK: false, Error: result.Message})
return
}
r.logger.Printf("[API] Catalog sync completed: %s", result.Message)
r.logger.Printf("[INFO] [api] Catalog sync completed: %s", result.Message)
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: result.Message, Data: result})
}
@@ -783,7 +783,7 @@ func (r *Router) triggerBackup(w http.ResponseWriter, _ *http.Request) {
return
}
r.logger.Println("[API] Manual backup triggered")
r.logger.Println("[INFO] [api] Manual backup triggered")
go r.backupMgr.RunFullBackup(context.Background())
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Mentés elindítva"})
@@ -979,12 +979,12 @@ func (r *Router) saveCrossBackupConfig(w http.ResponseWriter, req *http.Request,
}
if err := r.sett.SetCrossDriveConfig(name, cfg); err != nil {
r.logger.Printf("[API] Failed to save cross-drive config for %s: %v", name, err)
r.logger.Printf("[ERROR] [api] Failed to save cross-drive config for %s: %v", name, err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
r.logger.Printf("[API] Cross-drive backup config saved for %s: dest=%s schedule=%s",
r.logger.Printf("[INFO] [api] Cross-drive backup config saved for %s: dest=%s schedule=%s",
name, body.DestinationPath, body.Schedule)
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Cross-drive backup configuration saved"})
}
@@ -999,10 +999,10 @@ func (r *Router) triggerCrossBackup(w http.ResponseWriter, req *http.Request, na
return
}
r.logger.Printf("[API] Cross-drive backup triggered for: %s", name)
r.logger.Printf("[INFO] [api] Cross-drive backup triggered for: %s", name)
go func() {
if err := r.crossDriveRunner.RunAppBackup(context.Background(), name); err != nil {
r.logger.Printf("[API] Cross-drive backup failed for %s: %v", name, err)
r.logger.Printf("[ERROR] [api] Cross-drive backup failed for %s: %v", name, err)
}
if r.OnCrossDriveComplete != nil {
r.OnCrossDriveComplete()
@@ -1037,14 +1037,14 @@ func (r *Router) triggerAllCrossBackups(w http.ResponseWriter, _ *http.Request)
writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "cross-drive runner not available"})
return
}
r.logger.Println("[API] All cross-drive backups triggered")
r.logger.Println("[INFO] [api] All cross-drive backups triggered")
go func() {
ctx := context.Background()
if err := r.crossDriveRunner.RunAllScheduled(ctx, "daily"); err != nil {
r.logger.Printf("[API] Cross-drive run-all error: %v", err)
r.logger.Printf("[ERROR] [api] Cross-drive run-all error: %v", err)
}
if err := r.crossDriveRunner.RunAllScheduled(ctx, "weekly"); err != nil {
r.logger.Printf("[API] Cross-drive run-all weekly error: %v", err)
r.logger.Printf("[ERROR] [api] Cross-drive run-all weekly error: %v", err)
}
if r.OnCrossDriveComplete != nil {
r.OnCrossDriveComplete()
@@ -1150,7 +1150,7 @@ func (r *Router) selfupdateTrigger(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusConflict, apiResponse{OK: false, Error: err.Error()})
return
}
r.logger.Println("[API] Manual self-update triggered")
r.logger.Println("[INFO] [api] Manual self-update triggered")
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Frissítés elindítva"})
}
@@ -1171,7 +1171,7 @@ func (r *Router) configApply(w http.ResponseWriter, req *http.Request) {
// Validate it's parseable YAML by attempting to load it
if _, err := config.LoadFromBytes(body); err != nil {
r.logger.Printf("[API] Config apply rejected: invalid YAML: %v", err)
r.logger.Printf("[WARN] [api] Config apply rejected: invalid YAML: %v", err)
writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: fmt.Sprintf("invalid config YAML: %v", err)})
return
}
@@ -1180,7 +1180,7 @@ func (r *Router) configApply(w http.ResponseWriter, req *http.Request) {
// (os.Rename fails on Docker bind mounts with "device or resource busy")
tmpPath := r.configPath + ".tmp"
if err := os.WriteFile(tmpPath, body, 0644); err != nil {
r.logger.Printf("[ERROR] Config apply: failed to write temp file: %v", err)
r.logger.Printf("[ERROR] [api] Config apply: failed to write temp file: %v", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: "failed to write config"})
return
}
@@ -1189,14 +1189,14 @@ func (r *Router) configApply(w http.ResponseWriter, req *http.Request) {
os.Remove(tmpPath)
// Rename failed (likely Docker bind mount) — write directly
if err := os.WriteFile(r.configPath, body, 0644); err != nil {
r.logger.Printf("[ERROR] Config apply: failed to write config: %v", err)
r.logger.Printf("[ERROR] [api] Config apply: failed to write config: %v", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: "failed to apply config"})
return
}
r.logger.Printf("[API] Config apply: rename failed, wrote directly (bind mount)")
r.logger.Printf("[INFO] [api] Config apply: rename failed, wrote directly (bind mount)")
}
r.logger.Printf("[API] Config applied from Hub (%d bytes), restart needed to take effect", len(body))
r.logger.Printf("[INFO] [api] Config applied from Hub (%d bytes), restart needed to take effect", len(body))
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Config applied. Restart controller to apply changes."})
// Push updated infra backup so Hub has fresh config data immediately
@@ -1231,10 +1231,10 @@ func (r *Router) triggerAssetSync(w http.ResponseWriter, req *http.Request) {
writeJSON(w, http.StatusOK, apiResponse{OK: false, Error: "asset sync not configured"})
return
}
r.logger.Println("[API] Manual asset sync requested")
r.logger.Println("[INFO] [api] Manual asset sync requested")
go func() {
if err := r.assetsSyncer.Sync(context.Background()); err != nil {
r.logger.Printf("[WARN] Manual asset sync failed: %v", err)
r.logger.Printf("[WARN] [api] Manual asset sync failed: %v", err)
}
}()
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Asset sync started"})
@@ -1252,7 +1252,7 @@ func writeJSON(w http.ResponseWriter, status int, v interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(v); err != nil {
log.Printf("[ERROR] Failed to write JSON response: %v", err)
log.Printf("[ERROR] [api] Failed to write JSON response: %v", err)
}
}
+4 -4
View File
@@ -90,7 +90,7 @@ func (s *Syncer) Sync(ctx context.Context) error {
s.mu.Unlock()
}()
s.logger.Println("[INFO] Asset sync starting...")
s.logger.Println("[INFO] [assets] Asset sync starting...")
syncStart := time.Now()
if err := os.MkdirAll(s.assetsDir, 0755); err != nil {
@@ -140,7 +140,7 @@ func (s *Syncer) Sync(ctx context.Context) error {
s.dbg("file %s: downloading (%s, %d bytes)", entry.Filename, reason, entry.Size)
dlStart := time.Now()
if err := s.downloadFile(ctx, entry.Filename); err != nil {
s.logger.Printf("[WARN] Failed to download asset %s: %v", entry.Filename, err)
s.logger.Printf("[WARN] [assets] Failed to download asset %s: %v", entry.Filename, err)
continue
}
s.dbg("file %s: downloaded in %s", entry.Filename, time.Since(dlStart).Round(time.Millisecond))
@@ -154,7 +154,7 @@ func (s *Syncer) Sync(ctx context.Context) error {
path := filepath.Join(s.assetsDir, name)
s.dbg("removing stale file %s", name)
if err := os.Remove(path); err != nil {
s.logger.Printf("[WARN] Failed to remove stale asset %s: %v", name, err)
s.logger.Printf("[WARN] [assets] Failed to remove stale asset %s: %v", name, err)
} else {
removed++
}
@@ -174,7 +174,7 @@ func (s *Syncer) Sync(ctx context.Context) error {
}
s.mu.Unlock()
s.logger.Printf("[INFO] Asset sync complete: %d downloaded, %d unchanged, %d removed (%d total files)",
s.logger.Printf("[INFO] [assets] Asset sync complete: %d downloaded, %d unchanged, %d removed (%d total files)",
downloaded, skipped, removed, len(manifest.Files))
s.dbg("sync completed in %s", time.Since(syncStart).Round(time.Millisecond))
+2
View File
@@ -3,6 +3,7 @@ package backup
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"strings"
@@ -105,6 +106,7 @@ func DiscoverAppData(provider StackDataProvider, discoveredDBs []DiscoveredDB) [
result = append(result, info)
}
log.Printf("[INFO] [backup] Discovered app data: %d apps", len(result))
return result
}
+45 -45
View File
@@ -150,7 +150,7 @@ type BackupStatus struct {
// NewManager creates a new backup manager.
func NewManager(cfg *config.Config, pinger *monitor.Pinger, sett *settings.Settings, logger *log.Logger) *Manager {
if cfg.Paths.SystemDataPath == "" {
logger.Printf("[WARN] SystemDataPath is empty in config — SSD-only apps will not have correct backup paths")
logger.Printf("[WARN] [backup] SystemDataPath is empty in config — SSD-only apps will not have correct backup paths")
}
dataDir := cfg.Paths.DataDir
if dataDir == "" {
@@ -178,7 +178,7 @@ func (m *Manager) GetAppDrivePath(stackName string) string {
}
}
if m.systemDataPath == "" {
m.logger.Printf("[ERROR] systemDataPath is empty — cannot determine drive for %s", stackName)
m.logger.Printf("[ERROR] [backup] systemDataPath is empty — cannot determine drive for %s", stackName)
}
return m.systemDataPath
}
@@ -238,16 +238,16 @@ func (m *Manager) RunDBDumps(ctx context.Context) error {
// runDBDumpsInternal is the implementation of RunDBDumps. Caller must hold the running flag.
func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
start := time.Now()
m.logger.Printf("[INFO] Starting database dump run")
m.logger.Printf("[INFO] [backup] Starting database dump run")
dbs, err := DiscoverDatabases(ctx, m.logger, m.isDebug())
if err != nil {
m.logger.Printf("[ERROR] Database discovery failed: %v", err)
m.logger.Printf("[ERROR] [backup] Database discovery failed: %v", err)
return err
}
if len(dbs) == 0 {
m.logger.Printf("[INFO] No database containers found")
m.logger.Printf("[INFO] [backup] No database containers found")
m.mu.Lock()
m.lastDBDump = &DBDumpStatus{
LastRun: time.Now(),
@@ -258,7 +258,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
return nil
}
m.logger.Printf("[INFO] Discovered %d database(s): %s", len(dbs), dbNames(dbs))
m.logger.Printf("[INFO] [backup] Discovered %d database(s): %s", len(dbs), dbNames(dbs))
// Dump each DB to its app's drive path
var results []DumpResult
@@ -271,12 +271,12 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
// Skip if drive is disconnected or decommissioned
if m.settings != nil && m.settings.IsDisconnected(drivePath) {
m.logger.Printf("[WARN] Skipping DB dump for %s — drive disconnected: %s", db.StackName, drivePath)
m.logger.Printf("[WARN] [backup] Skipping DB dump for %s — drive disconnected: %s", db.StackName, drivePath)
summary = append(summary, fmt.Sprintf("SKIP %s (drive disconnected)", db.ContainerName))
continue
}
if m.settings != nil && m.settings.IsDecommissioned(drivePath) {
m.logger.Printf("[WARN] Skipping DB dump for %s — drive decommissioned: %s", db.StackName, drivePath)
m.logger.Printf("[WARN] [backup] Skipping DB dump for %s — drive decommissioned: %s", db.StackName, drivePath)
summary = append(summary, fmt.Sprintf("SKIP %s (drive decommissioned)", db.ContainerName))
continue
}
@@ -289,7 +289,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
if result.Error != nil {
allOK = false
summary = append(summary, fmt.Sprintf("FAIL %s: %v", result.DB.ContainerName, result.Error))
m.logger.Printf("[ERROR] DB dump failed for %s: %v", result.DB.ContainerName, result.Error)
m.logger.Printf("[ERROR] [backup] DB dump failed for %s: %v", result.DB.ContainerName, result.Error)
} else {
totalSize += result.Size
summary = append(summary, fmt.Sprintf("OK %s (%s)", result.DB.ContainerName, humanizeBytes(result.Size)))
@@ -306,7 +306,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
cache.Error = result.Validation.Error
}
if err := m.settings.SetDBValidation(filename, cache); err != nil {
m.logger.Printf("[WARN] Failed to cache validation for %s: %v", filename, err)
m.logger.Printf("[WARN] [backup] Failed to cache validation for %s: %v", filename, err)
}
}
}
@@ -329,7 +329,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
if allOK {
m.pinger.Ping(uuid, body)
m.logger.Printf("[INFO] DB dump completed: %d databases, %s total (%s)",
m.logger.Printf("[INFO] [backup] DB dump completed: %d databases, %s total (%s)",
len(results), humanizeBytes(totalSize), duration.Round(time.Millisecond))
} else {
m.pinger.Fail(uuid, body)
@@ -352,16 +352,16 @@ func (m *Manager) RunBackup(ctx context.Context) error {
func (m *Manager) runBackupInternal(ctx context.Context) error {
// Skip if a full drive migration is in progress
if m.MigrationActiveCheck != nil && m.MigrationActiveCheck() {
m.logger.Printf("[WARN] Skipping nightly backup — drive migration in progress")
m.logger.Printf("[WARN] [backup] Skipping nightly backup — drive migration in progress")
return nil
}
start := time.Now()
m.logger.Printf("[INFO] Starting restic backup (per-drive)")
m.logger.Printf("[INFO] [backup] Starting restic backup (per-drive)")
driveStacks := m.groupStacksByDrive()
if len(driveStacks) == 0 {
m.logger.Printf("[INFO] No deployed stacks — skipping backup")
m.logger.Printf("[INFO] [backup] No deployed stacks — skipping backup")
return nil
}
@@ -436,7 +436,7 @@ func (m *Manager) runBackupInternal(ctx context.Context) error {
duration.Round(time.Second))
m.pinger.Ping(m.cfg.Monitoring.PingUUIDs.Backup, body)
m.logger.Printf("[INFO] Restic backup completed: %d drives, snapshot %s, %d new, %d changed, %s added (%s)",
m.logger.Printf("[INFO] [backup] Restic backup completed: %d drives, snapshot %s, %d new, %d changed, %s added (%s)",
driveCount, lastResult.SnapshotID, lastResult.FilesNew, lastResult.FilesChanged, lastResult.DataAdded,
duration.Round(time.Millisecond))
}
@@ -454,11 +454,11 @@ func (m *Manager) runBackupInternal(ctx context.Context) error {
func (m *Manager) backupDrive(ctx context.Context, drivePath string, stacks []StackSummary, infraPaths []string) (*SnapshotResult, error) {
// Skip disconnected or decommissioned drives
if m.settings != nil && m.settings.IsDisconnected(drivePath) {
m.logger.Printf("[WARN] Skipping backup for drive %s — disconnected", drivePath)
m.logger.Printf("[WARN] [backup] Skipping backup for drive %s — disconnected", drivePath)
return nil, nil
}
if m.settings != nil && m.settings.IsDecommissioned(drivePath) {
m.logger.Printf("[WARN] Skipping backup for drive %s — decommissioned", drivePath)
m.logger.Printf("[WARN] [backup] Skipping backup for drive %s — decommissioned", drivePath)
return nil, nil
}
@@ -466,7 +466,7 @@ func (m *Manager) backupDrive(ctx context.Context, drivePath string, stacks []St
// Ensure repo is initialized
if err := m.restic.EnsureInitialized(repoPath); err != nil {
m.logger.Printf("[ERROR] Restic init failed for %s: %v", repoPath, err)
m.logger.Printf("[ERROR] [backup] Restic init failed for %s: %v", repoPath, err)
return nil, err
}
@@ -506,19 +506,19 @@ func (m *Manager) backupDrive(ctx context.Context, drivePath string, stacks []St
}
tags := []string{"felhom", m.cfg.Customer.ID, filepath.Base(drivePath)}
m.logger.Printf("[INFO] Backing up drive %s (%d apps, %d paths)", drivePath, len(stacks), len(paths))
m.logger.Printf("[INFO] [backup] Backing up drive %s (%d apps, %d paths)", drivePath, len(stacks), len(paths))
result, err := m.restic.Snapshot(repoPath, paths, tags)
if err != nil {
m.logger.Printf("[ERROR] Restic backup failed for drive %s: %v", drivePath, err)
m.logger.Printf("[ERROR] [backup] Restic backup failed for drive %s: %v", drivePath, err)
return nil, err
}
// Prune check (weekly — Sunday)
if shouldPrune(m.cfg.Backup.PruneSchedule) {
m.logger.Printf("[INFO] Running weekly prune for %s", repoPath)
m.logger.Printf("[INFO] [backup] Running weekly prune for %s", repoPath)
if err := m.restic.Prune(repoPath, m.cfg.Backup.Retention); err != nil {
m.logger.Printf("[WARN] Restic prune failed for %s: %v", repoPath, err)
m.logger.Printf("[WARN] [backup] Restic prune failed for %s: %v", repoPath, err)
}
}
@@ -548,7 +548,7 @@ func (m *Manager) TryRunDriveBackup(ctx context.Context, drivePath string) error
driveStacks := m.groupStacksByDrive()
stacks, ok := driveStacks[drivePath]
if !ok || len(stacks) == 0 {
m.logger.Printf("[INFO] No deployed stacks on drive %s — skipping backup", drivePath)
m.logger.Printf("[INFO] [backup] No deployed stacks on drive %s — skipping backup", drivePath)
return nil
}
@@ -563,7 +563,7 @@ func (m *Manager) TryRunDriveBackup(ctx context.Context, drivePath string) error
}
if result != nil {
m.logger.Printf("[INFO] Single-drive backup for %s: snapshot %s, %d new, %d changed, %s added",
m.logger.Printf("[INFO] [backup] Single-drive backup for %s: snapshot %s, %d new, %d changed, %s added",
drivePath, result.SnapshotID, result.FilesNew, result.FilesChanged, result.DataAdded)
}
@@ -572,12 +572,12 @@ func (m *Manager) TryRunDriveBackup(ctx context.Context, drivePath string) error
// RunIntegrityCheck runs restic check on all primary repos and pings healthchecks.
func (m *Manager) RunIntegrityCheck(ctx context.Context) error {
m.logger.Printf("[INFO] Starting restic integrity check")
m.logger.Printf("[INFO] [backup] Starting restic integrity check")
start := time.Now()
drives := m.activeDrives()
if len(drives) == 0 {
m.logger.Printf("[INFO] No active drives — skipping integrity check")
m.logger.Printf("[INFO] [backup] No active drives — skipping integrity check")
return nil
}
@@ -598,7 +598,7 @@ func (m *Manager) RunIntegrityCheck(ctx context.Context) error {
m.logger.Printf("[DEBUG] RunIntegrityCheck: checking repo %s", repoPath)
}
if err := m.restic.Check(repoPath); err != nil {
m.logger.Printf("[ERROR] Restic check failed for %s: %v", repoPath, err)
m.logger.Printf("[ERROR] [backup] Restic check failed for %s: %v", repoPath, err)
checkErr = err
} else if m.isDebug() {
m.logger.Printf("[DEBUG] RunIntegrityCheck: repo %s OK", repoPath)
@@ -614,12 +614,12 @@ func (m *Manager) RunIntegrityCheck(ctx context.Context) error {
m.mu.Unlock()
if checkErr != nil {
m.logger.Printf("[ERROR] Restic integrity check failed (%s): %v", duration.Round(time.Second), checkErr)
m.logger.Printf("[ERROR] [backup] Restic integrity check failed (%s): %v", duration.Round(time.Second), checkErr)
m.pinger.Fail(uuid, fmt.Sprintf("restic check failed: %v", checkErr))
return checkErr
}
m.logger.Printf("[INFO] Restic integrity check passed (%d repos, %s)", len(drives), duration.Round(time.Second))
m.logger.Printf("[INFO] [backup] Restic integrity check passed (%d repos, %s)", len(drives), duration.Round(time.Second))
m.pinger.Ping(uuid, fmt.Sprintf("restic check passed (%d repos, %s)", len(drives), duration.Round(time.Second)))
return nil
}
@@ -646,7 +646,7 @@ func (m *Manager) RunFullBackup(ctx context.Context) error {
m.logger.Printf("[DEBUG] RunFullBackup: phase 1 — database dumps")
}
if err := m.runDBDumpsInternal(ctx); err != nil {
m.logger.Printf("[WARN] DB dump had errors, continuing with backup anyway")
m.logger.Printf("[WARN] [backup] DB dump had errors, continuing with backup anyway")
}
// Step 2: Restic backup
@@ -713,7 +713,7 @@ func (m *Manager) ListSnapshots(limit int) ([]SnapshotInfo, error) {
}
snapshots, err := m.restic.ListSnapshots(repoPath, 0)
if err != nil {
m.logger.Printf("[WARN] Could not list snapshots from %s: %v", repoPath, err)
m.logger.Printf("[WARN] [backup] Could not list snapshots from %s: %v", repoPath, err)
continue
}
for i := range snapshots {
@@ -744,7 +744,7 @@ func (m *Manager) ListAllSnapshots(limit int) ([]SnapshotInfo, error) {
}
snapshots, err := m.restic.ListSnapshots(repoPath, 0)
if err != nil {
m.logger.Printf("[WARN] Could not list snapshots from %s: %v", repoPath, err)
m.logger.Printf("[WARN] [backup] Could not list snapshots from %s: %v", repoPath, err)
continue
}
for i := range snapshots {
@@ -782,7 +782,7 @@ func (m *Manager) UnlockRepo(ctx context.Context, repoPath string) error {
if err != nil {
return fmt.Errorf("restic unlock: %v (%s)", err, strings.TrimSpace(string(out)))
}
m.logger.Printf("[INFO] Restic repo unlocked: %s", repoPath)
m.logger.Printf("[INFO] [backup] Restic repo unlocked: %s", repoPath)
return nil
}
@@ -819,14 +819,14 @@ func (m *Manager) DumpStackDB(ctx context.Context, stackName string) error {
}
dumpDir := AppDBDumpPath(drivePath, stackName)
m.logger.Printf("[INFO] Running pre-backup DB dump for %s (%d database(s)) → %s", stackName, len(stackDBs), dumpDir)
m.logger.Printf("[INFO] [backup] Running pre-backup DB dump for %s (%d database(s)) → %s", stackName, len(stackDBs), dumpDir)
for _, db := range stackDBs {
result := DumpOne(ctx, db, dumpDir, m.logger, m.isDebug())
if result.Error != nil {
return fmt.Errorf("DB dump failed for %s: %w", result.DB.ContainerName, result.Error)
}
m.logger.Printf("[INFO] Pre-backup DB dump OK: %s (%s)", result.DB.ContainerName, humanizeBytes(result.Size))
m.logger.Printf("[INFO] [backup] Pre-backup DB dump OK: %s (%s)", result.DB.ContainerName, humanizeBytes(result.Size))
// Persist validation to settings
if m.settings != nil && result.FilePath != "" {
@@ -973,16 +973,16 @@ func (m *Manager) saveSnapshotHistory() {
}
data, err := json.Marshal(m.snapshotHistory)
if err != nil {
m.logger.Printf("[WARN] Could not marshal snapshot history: %v", err)
m.logger.Printf("[WARN] [backup] Could not marshal snapshot history: %v", err)
return
}
tmp := m.snapshotHistoryFile + ".tmp"
if err := os.WriteFile(tmp, data, 0644); err != nil {
m.logger.Printf("[WARN] Could not write snapshot history tmp file: %v", err)
m.logger.Printf("[WARN] [backup] Could not write snapshot history tmp file: %v", err)
return
}
if err := os.Rename(tmp, m.snapshotHistoryFile); err != nil {
m.logger.Printf("[WARN] Could not rename snapshot history file: %v", err)
m.logger.Printf("[WARN] [backup] Could not rename snapshot history file: %v", err)
return
}
m.logger.Printf("[INFO] [backup] Saved snapshot history (%d entries)", len(m.snapshotHistory))
@@ -997,13 +997,13 @@ func (m *Manager) loadSnapshotHistoryFromFile() []SnapshotRecord {
data, err := os.ReadFile(m.snapshotHistoryFile)
if err != nil {
if !os.IsNotExist(err) {
m.logger.Printf("[WARN] Could not read snapshot history file: %v", err)
m.logger.Printf("[WARN] [backup] Could not read snapshot history file: %v", err)
}
return nil
}
var records []SnapshotRecord
if err := json.Unmarshal(data, &records); err != nil {
m.logger.Printf("[WARN] Could not parse snapshot history file: %v", err)
m.logger.Printf("[WARN] [backup] Could not parse snapshot history file: %v", err)
return nil
}
return records
@@ -1033,7 +1033,7 @@ func (m *Manager) LoadSnapshotHistory() {
}
snapshots, err := m.restic.ListSnapshots(repoPath, 20)
if err != nil {
m.logger.Printf("[WARN] Could not load snapshot history from %s: %v", repoPath, err)
m.logger.Printf("[WARN] [backup] Could not load snapshot history from %s: %v", repoPath, err)
continue
}
allSnapshots = append(allSnapshots, snapshots...)
@@ -1064,7 +1064,7 @@ func (m *Manager) LoadSnapshotHistory() {
sort.Slice(m.snapshotHistory, func(i, j int) bool {
return m.snapshotHistory[i].Time.Before(m.snapshotHistory[j].Time)
})
m.logger.Printf("[INFO] Loaded %d snapshots from persisted history (merged with %d restic entries)", len(persisted), len(allSnapshots))
m.logger.Printf("[INFO] [backup] Loaded %d snapshots from persisted history (merged with %d restic entries)", len(persisted), len(allSnapshots))
} else {
// No persisted file — fall back to restic-only loading (first run)
for _, s := range allSnapshots {
@@ -1075,7 +1075,7 @@ func (m *Manager) LoadSnapshotHistory() {
Success: true,
})
}
m.logger.Printf("[INFO] Loaded %d historical snapshots from %d restic repos (no persisted history)", len(m.snapshotHistory), len(drives))
m.logger.Printf("[INFO] [backup] Loaded %d historical snapshots from %d restic repos (no persisted history)", len(m.snapshotHistory), len(drives))
}
if len(m.snapshotHistory) > 20 {
@@ -1139,7 +1139,7 @@ func (m *Manager) RefreshCache(nextDBDump, nextBackup time.Time) {
filename := filepath.Base(r.FilePath)
if fv, ok := fileValidation[filename]; ok {
m.lastDBDump.Results[i].Validation = fv
m.logger.Printf("[INFO] Re-validated %s from disk: valid=%v tables=%d",
m.logger.Printf("[INFO] [backup] Re-validated %s from disk: valid=%v tables=%d",
filename, fv.Valid, fv.TableCount)
}
}
@@ -1155,7 +1155,7 @@ func (m *Manager) RefreshCache(nextDBDump, nextBackup time.Time) {
m.cacheTime = time.Now()
m.mu.Unlock()
m.logger.Printf("[INFO] Backup status cache refreshed")
m.logger.Printf("[INFO] [backup] Backup status cache refreshed")
}
// GetFullStatus returns the cached backup status for page rendering.
+20 -19
View File
@@ -117,7 +117,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
})
start := time.Now()
r.logger.Printf("[INFO] Cross-drive backup starting: %s → %s (rsync)",
r.logger.Printf("[INFO] [backup] Cross-drive backup starting: %s → %s (rsync)",
stackName, cfg.DestinationPath)
// Trigger fresh DB dump for this app before cross-drive backup
@@ -126,7 +126,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
r.logger.Printf("[DEBUG] RunAppBackup: triggering pre-backup DB dump for %s", stackName)
}
if err := r.dbDumper.DumpStackDB(ctx, stackName); err != nil {
r.logger.Printf("[WARN] Pre-backup DB dump failed for %s: %v — proceeding with user data backup", stackName, err)
r.logger.Printf("[WARN] [backup] Pre-backup DB dump failed for %s: %v — proceeding with user data backup", stackName, err)
// Non-fatal: user data backup is still valuable without fresh dump
}
}
@@ -156,7 +156,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
duration := time.Since(start)
if runErr != nil {
r.logger.Printf("[ERROR] Cross-drive backup failed: %s: %v", stackName, runErr)
r.logger.Printf("[ERROR] [backup] Cross-drive backup failed: %s: %v", stackName, runErr)
r.updateStatus(stackName, "error", runErr.Error(), duration, "")
return runErr
}
@@ -171,7 +171,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
}
}
r.logger.Printf("[INFO] Cross-drive backup completed: %s (%s)", stackName, duration.Round(time.Second))
r.logger.Printf("[INFO] [backup] Cross-drive backup completed: %s (%s)", stackName, duration.Round(time.Second))
r.updateStatus(stackName, "ok", "", duration, sizeHuman)
return nil
}
@@ -333,14 +333,14 @@ func (r *CrossDriveRunner) ValidateDestination(path string) error {
r.logger.Printf("[DEBUG] ValidateDestination: path=%s, isMountPoint=%v", path, !onSystemDrive)
}
if onSystemDrive {
r.logger.Printf("[WARN] Destination %s is not a separate mount point (system drive) — backup will proceed but data is not protected against drive failure", path)
r.logger.Printf("[WARN] [backup] Destination %s is not a separate mount point (system drive) — backup will proceed but data is not protected against drive failure", path)
}
if !system.IsWritable(path) {
return fmt.Errorf("destination %s is not writable", path)
}
di := system.GetDiskUsage(path)
if di == nil {
r.logger.Printf("[WARN] Cannot determine disk usage for %s — proceeding without space verification", path)
r.logger.Printf("[WARN] [backup] Cannot determine disk usage for %s — proceeding without space verification", path)
return nil
}
if r.debug {
@@ -417,7 +417,9 @@ func (r *CrossDriveRunner) runRsyncBackup(ctx context.Context, stackName, destBa
"--exclude", "backups/*.sql",
"--exclude", "backups/*.dump",
src, dst)
r.logger.Printf("[DEBUG] rsync: %s → %s", src, dst)
if r.debug {
r.logger.Printf("[DEBUG] rsync: %s → %s", src, dst)
}
out, err := cmd.CombinedOutput()
if err != nil {
if r.debug {
@@ -437,7 +439,7 @@ func (r *CrossDriveRunner) runRsyncBackup(ctx context.Context, stackName, destBa
return fmt.Errorf("creating DB dump dest dir: %w", err)
}
if err := r.copyStackDBDumps(stackName, dbDestDir); err != nil {
r.logger.Printf("[WARN] Cross-drive DB dump copy failed for %s: %v", stackName, err)
r.logger.Printf("[WARN] [backup] Cross-drive DB dump copy failed for %s: %v", stackName, err)
// Non-fatal: user data is the primary concern
}
@@ -451,9 +453,11 @@ func (r *CrossDriveRunner) runRsyncBackup(ctx context.Context, stackName, destBa
src := strings.TrimRight(configSrcDir, "/") + "/"
dst := strings.TrimRight(configDestDir, "/") + "/"
cmd := exec.CommandContext(ctx, "rsync", "-a", "--delete", src, dst)
r.logger.Printf("[DEBUG] rsync config: %s → %s", src, dst)
if r.debug {
r.logger.Printf("[DEBUG] rsync config: %s → %s", src, dst)
}
if out, err := cmd.CombinedOutput(); err != nil {
r.logger.Printf("[WARN] Cross-drive config rsync failed for %s: %v (%s)", stackName, err, strings.TrimSpace(string(out)))
r.logger.Printf("[WARN] [backup] Cross-drive config rsync failed for %s: %v (%s)", stackName, err, strings.TrimSpace(string(out)))
// Non-fatal
}
}
@@ -488,9 +492,6 @@ func (r *CrossDriveRunner) copyStackDBDumps(stackName, destDir string) error {
}
copied++
}
if copied > 0 {
r.logger.Printf("[DEBUG] Copied %d DB dump file(s) to %s", copied, destDir)
}
r.logger.Printf("[INFO] [backup] Copied %d DB dumps for %s", copied, stackName)
return nil
}
@@ -514,7 +515,7 @@ func (r *CrossDriveRunner) syncInfraConfig(ctx context.Context) {
for dest := range destDrives {
infraDir := SecondaryInfraPath(dest)
if err := os.MkdirAll(infraDir, 0755); err != nil {
r.logger.Printf("[WARN] Cannot create infra backup dir %s: %v", infraDir, err)
r.logger.Printf("[WARN] [backup] Cannot create infra backup dir %s: %v", infraDir, err)
continue
}
@@ -524,7 +525,7 @@ func (r *CrossDriveRunner) syncInfraConfig(ctx context.Context) {
stacksSrc := strings.TrimRight(r.stacksDir, "/") + "/"
cmd := exec.CommandContext(ctx, "rsync", "-a", "--delete", stacksSrc, stacksDest)
if out, err := cmd.CombinedOutput(); err != nil {
r.logger.Printf("[WARN] Infra rsync (stacks) failed for %s: %v (%s)", dest, err, strings.TrimSpace(string(out)))
r.logger.Printf("[WARN] [backup] Infra rsync (stacks) failed for %s: %v (%s)", dest, err, strings.TrimSpace(string(out)))
}
}
@@ -532,11 +533,11 @@ func (r *CrossDriveRunner) syncInfraConfig(ctx context.Context) {
if _, err := os.Stat(r.controllerYAMLPath); err == nil {
yamlDest := filepath.Join(infraDir, "controller.yaml")
if err := copyFile(r.controllerYAMLPath, yamlDest); err != nil {
r.logger.Printf("[WARN] Cannot copy controller.yaml to %s: %v", yamlDest, err)
r.logger.Printf("[WARN] [backup] Cannot copy controller.yaml to %s: %v", yamlDest, err)
}
}
r.logger.Printf("[INFO] Infrastructure config synced to %s", infraDir)
r.logger.Printf("[INFO] [backup] Infrastructure config synced to %s", infraDir)
}
}
@@ -604,11 +605,11 @@ func (r *CrossDriveRunner) AutoEnableSmallApps() {
Schedule: "daily",
}
if err := r.sett.SetCrossDriveConfig(stack.Name, cfg); err != nil {
r.logger.Printf("[WARN] Auto-enable Tier 2 failed for %s: %v", stack.Name, err)
r.logger.Printf("[WARN] [backup] Auto-enable Tier 2 failed for %s: %v", stack.Name, err)
continue
}
autoEnabled++
r.logger.Printf("[INFO] Auto-enabled Tier 2 backup for %s → %s (no HDD mounts, daily rsync)", stack.Name, destPath)
r.logger.Printf("[INFO] [backup] Auto-enabled Tier 2 backup for %s → %s (no HDD mounts, daily rsync)", stack.Name, destPath)
}
if r.debug && autoEnabled > 0 {
+8 -10
View File
@@ -117,7 +117,7 @@ func DiscoverDatabases(ctx context.Context, logger *log.Logger, debug bool) ([]D
// Get env vars from container
if err := populateDBEnv(ctx, &db); err != nil {
logger.Printf("[WARN] Could not read env vars for %s: %v", name, err)
logger.Printf("[WARN] [backup] Could not read env vars for %s: %v", name, err)
if debug {
logger.Printf("[DEBUG] DiscoverDatabases: skipping %s — env read failed", name)
}
@@ -310,7 +310,7 @@ func DumpOne(ctx context.Context, db DiscoveredDB, dumpDir string, logger *log.L
result.Validation.Valid, result.Validation.TableCount, result.Duration.Round(time.Millisecond))
}
logger.Printf("[INFO] DB dump: %s → %s (%s, %s, %d tables)", db.ContainerName, filename,
logger.Printf("[INFO] [backup] DB dump: %s → %s (%s, %s, %d tables)", db.ContainerName, filename,
humanizeBytes(stat.Size()), result.Duration.Round(time.Millisecond), result.Validation.TableCount)
return result
@@ -318,7 +318,6 @@ func DumpOne(ctx context.Context, db DiscoveredDB, dumpDir string, logger *log.L
// ValidateDump checks a SQL dump file for basic structural integrity.
func ValidateDump(filePath string, dbType DBType) DumpValidation {
log.Printf("[DEBUG] ValidateDump: %s (type=%s)", filePath, dbType)
stat, err := os.Stat(filePath)
if err != nil {
return DumpValidation{Error: fmt.Sprintf("stat failed: %v", err)}
@@ -331,7 +330,7 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
if stat.Size() < 100 {
v.Error = "dump file too small (< 100 bytes)"
log.Printf("[WARN] ValidateDump FAIL: %s — %s", filePath, v.Error)
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s", filePath, v.Error)
return v
}
@@ -340,7 +339,7 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
f, err := os.Open(filePath)
if err != nil {
v.Error = fmt.Sprintf("read failed: %v", err)
log.Printf("[WARN] ValidateDump FAIL: %s — %s", filePath, v.Error)
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s", filePath, v.Error)
return v
}
defer f.Close()
@@ -359,7 +358,7 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
if err != nil {
if err != io.EOF {
v.Error = fmt.Sprintf("hiba az olvasás közben: %v", err)
log.Printf("[WARN] ValidateDump FAIL: %s — read error: %v", filePath, err)
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — read error: %v", filePath, err)
return v
}
break // EOF
@@ -408,18 +407,17 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
case DBTypePostgres:
v.Error = "PostgreSQL dump missing comment header"
}
log.Printf("[WARN] ValidateDump FAIL: %s — %s", filePath, v.Error)
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s", filePath, v.Error)
return v
}
if tableCount == 0 {
v.Error = "no CREATE TABLE statements found"
log.Printf("[WARN] ValidateDump FAIL: %s — %s (header was found, scanned %d lines)", filePath, v.Error, lineNum)
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s (header was found, scanned %d lines)", filePath, v.Error, lineNum)
return v
}
v.Valid = true
log.Printf("[DEBUG] ValidateDump OK: %s — %d tables, header found", filePath, tableCount)
return v
}
@@ -570,7 +568,7 @@ func cleanupTmpFiles(dumpDir string, logger *log.Logger) {
if info.ModTime().Before(cutoff) {
path := filepath.Join(dumpDir, e.Name())
os.Remove(path)
logger.Printf("[INFO] Cleaned up stale tmp file: %s", e.Name())
logger.Printf("[INFO] [backup] Cleaned up stale tmp file: %s", e.Name())
}
}
}
+12 -12
View File
@@ -88,7 +88,7 @@ func (r *ResticManager) EnsureInitialized(repoPath string) error {
// Check if repo is already initialized
configPath := filepath.Join(repoPath, "config")
if _, err := os.Stat(configPath); err == nil {
r.logger.Printf("[INFO] Restic repo already initialized at %s", repoPath)
r.logger.Printf("[INFO] [backup] Restic repo already initialized at %s", repoPath)
return nil
}
@@ -98,7 +98,7 @@ func (r *ResticManager) EnsureInitialized(repoPath string) error {
}
// Initialize repo
r.logger.Printf("[INFO] Initializing restic repository at %s", repoPath)
r.logger.Printf("[INFO] [backup] Initializing restic repository at %s", repoPath)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
@@ -108,7 +108,7 @@ func (r *ResticManager) EnsureInitialized(repoPath string) error {
return fmt.Errorf("restic init failed: %v — %s", err, truncate(string(out), 200))
}
r.logger.Printf("[INFO] Restic repository initialized successfully")
r.logger.Printf("[INFO] [backup] Restic repository initialized successfully")
return nil
}
@@ -134,7 +134,7 @@ func (r *ResticManager) Snapshot(repoPath string, paths []string, tags []string)
if _, err := os.Stat(p); err == nil {
existingPaths = append(existingPaths, p)
} else {
r.logger.Printf("[WARN] Backup path does not exist, skipping: %s", p)
r.logger.Printf("[WARN] [backup] Backup path does not exist, skipping: %s", p)
}
}
@@ -155,10 +155,10 @@ func (r *ResticManager) Snapshot(repoPath string, paths []string, tags []string)
errStr += string(exitErr.Stderr)
}
if strings.Contains(errStr, "lock") || strings.Contains(errStr, "locked") {
r.logger.Printf("[WARN] Restic repo locked — attempting unlock")
r.logger.Printf("[WARN] [backup] Restic repo locked — attempting unlock")
unlockCmd := r.command(ctx, repoPath, "unlock")
if unlockErr := unlockCmd.Run(); unlockErr != nil {
r.logger.Printf("[WARN] Restic unlock failed: %v", unlockErr)
r.logger.Printf("[WARN] [backup] Restic unlock failed: %v", unlockErr)
}
// Retry once with a fresh context (H9 fix — original may be nearly expired).
retryCtx, retryCancel := context.WithTimeout(context.Background(), 30*time.Minute)
@@ -239,7 +239,7 @@ func (r *ResticManager) Prune(repoPath string, retention config.RetentionConfig)
if r.debug {
r.logger.Printf("[DEBUG] [restic] Prune: completed in %s, output=%d bytes", time.Since(start), len(out))
}
r.logger.Printf("[INFO] Restic prune completed for %s", repoPath)
r.logger.Printf("[INFO] [backup] Restic prune completed for %s", repoPath)
return nil
}
@@ -420,19 +420,19 @@ func (r *ResticManager) RestoreAppData(repoPath string, snapshotID string, paths
}
start := time.Now()
r.logger.Printf("[WARN] RESTORE started: repo=%s, snapshot=%s, paths=%v", repoPath, snapshotID, paths)
r.logger.Printf("[INFO] [backup] Restore started: repo=%s, snapshot=%s, paths=%v", repoPath, snapshotID, paths)
cmd := r.command(ctx, repoPath, args...)
output, err := cmd.CombinedOutput()
if err != nil {
r.logger.Printf("[ERROR] Restore failed: %v, output: %s", err, truncate(string(output), 500))
r.logger.Printf("[ERROR] [backup] Restore failed: %v, output: %s", err, truncate(string(output), 500))
return fmt.Errorf("restic restore failed: %w", err)
}
if r.debug {
r.logger.Printf("[DEBUG] [restic] RestoreAppData: completed in %s, output=%d bytes", time.Since(start), len(output))
}
r.logger.Printf("[INFO] RESTORE completed: snapshot=%s, paths=%v", snapshotID, paths)
r.logger.Printf("[INFO] [backup] Restore completed: snapshot=%s, paths=%v", snapshotID, paths)
return nil
}
@@ -483,8 +483,8 @@ func (r *ResticManager) generatePassword() error {
return fmt.Errorf("writing password file: %w", err)
}
r.logger.Printf("[INFO] Generated new restic repository password at %s", r.passwordFile)
r.logger.Printf("[WARN] Save this password externally — losing it means losing access to ALL backups")
r.logger.Printf("[INFO] [backup] Generated new restic repository password at %s", r.passwordFile)
r.logger.Printf("[WARN] [backup] Save this password externally — losing it means losing access to ALL backups")
return nil
}
+3 -3
View File
@@ -64,10 +64,10 @@ func (c *Client) do(ctx context.Context, method, path string, body interface{})
}
bodyReader = bytes.NewReader(data)
if c.debug {
c.logger.Printf("[CF-DEBUG] %s %s body=%s", method, path, string(data))
c.logger.Printf("[DEBUG] [cloudflare] %s %s body=%s", method, path, string(data))
}
} else if c.debug {
c.logger.Printf("[CF-DEBUG] %s %s", method, path)
c.logger.Printf("[DEBUG] [cloudflare] %s %s", method, path)
}
url := apiBase + path
@@ -91,7 +91,7 @@ func (c *Client) do(ctx context.Context, method, path string, body interface{})
}
if c.debug {
c.logger.Printf("[CF-DEBUG] Response %d: %s", resp.StatusCode, string(respBody))
c.logger.Printf("[DEBUG] [cloudflare] Response %d: %s", resp.StatusCode, string(respBody))
}
var apiResp apiResponse
+11 -11
View File
@@ -73,20 +73,20 @@ func (c *Client) GetCustomRulesetID(ctx context.Context, zoneID string) (string,
}
if c.debug {
c.logger.Printf("[CF-DEBUG] GetCustomRulesetID: found %d rulesets for zone %s", len(rulesets), zoneID)
c.logger.Printf("[DEBUG] [cloudflare] GetCustomRulesetID: found %d rulesets for zone %s", len(rulesets), zoneID)
}
for _, rs := range rulesets {
if rs.Phase == wafPhase {
if c.debug {
c.logger.Printf("[CF-DEBUG] GetCustomRulesetID: matched ruleset %s (phase=%s)", rs.ID, wafPhase)
c.logger.Printf("[DEBUG] [cloudflare] GetCustomRulesetID: matched ruleset %s (phase=%s)", rs.ID, wafPhase)
}
return rs.ID, nil
}
}
if c.debug {
c.logger.Printf("[CF-DEBUG] GetCustomRulesetID: no ruleset with phase %s found", wafPhase)
c.logger.Printf("[DEBUG] [cloudflare] GetCustomRulesetID: no ruleset with phase %s found", wafPhase)
}
return "", nil
@@ -112,7 +112,7 @@ func (c *Client) CreateCustomRuleset(ctx context.Context, zoneID string) (string
return "", fmt.Errorf("decode created ruleset: %w", err)
}
c.logger.Printf("[CF] Created custom ruleset %s for zone %s", rs.ID, zoneID)
c.logger.Printf("[INFO] [cloudflare] Created custom ruleset %s for zone %s", rs.ID, zoneID)
return rs.ID, nil
}
@@ -133,7 +133,7 @@ func (c *Client) GetRules(ctx context.Context, zoneID, rulesetID string) ([]rule
}
if c.debug {
c.logger.Printf("[CF-DEBUG] GetRules: %d total rules in ruleset %s", len(rs.Rules), rulesetID)
c.logger.Printf("[DEBUG] [cloudflare] GetRules: %d total rules in ruleset %s", len(rs.Rules), rulesetID)
}
return rs.Rules, nil
@@ -159,7 +159,7 @@ func (c *Client) GetFelhomRules(ctx context.Context, zoneID, rulesetID string) (
}
if c.debug {
c.logger.Printf("[CF-DEBUG] GetFelhomRules: %d felhom rules out of %d total", len(result), len(rules))
c.logger.Printf("[DEBUG] [cloudflare] GetFelhomRules: %d felhom rules out of %d total", len(result), len(rules))
}
return result, nil
@@ -185,13 +185,13 @@ func (c *Client) CreateRule(ctx context.Context, zoneID, rulesetID string, r rul
for _, created := range rs.Rules {
if created.Description == r.Description {
c.logger.Printf("[CF] Created rule %q → %s", r.Description, created.ID)
c.logger.Printf("[INFO] [cloudflare] Created rule %q → %s", r.Description, created.ID)
if c.debug {
expr := r.Expression
if len(expr) > 120 {
expr = expr[:120] + "..."
}
c.logger.Printf("[CF-DEBUG] CreateRule: expression: %s", expr)
c.logger.Printf("[DEBUG] [cloudflare] CreateRule: expression: %s", expr)
}
return created.ID, nil
}
@@ -209,13 +209,13 @@ func (c *Client) UpdateRule(ctx context.Context, zoneID, rulesetID, ruleID strin
c.logger.Printf("[ERROR] [cloudflare] Failed to update WAF rule %s: %v", ruleID, err)
return fmt.Errorf("update rule %s: %w", ruleID, err)
}
c.logger.Printf("[CF] Updated rule %q (%s)", r.Description, ruleID)
c.logger.Printf("[INFO] [cloudflare] Updated rule %q (%s)", r.Description, ruleID)
if c.debug {
expr := r.Expression
if len(expr) > 120 {
expr = expr[:120] + "..."
}
c.logger.Printf("[CF-DEBUG] UpdateRule: expression: %s", expr)
c.logger.Printf("[DEBUG] [cloudflare] UpdateRule: expression: %s", expr)
}
return nil
}
@@ -229,7 +229,7 @@ func (c *Client) DeleteRule(ctx context.Context, zoneID, rulesetID, ruleID strin
c.logger.Printf("[ERROR] [cloudflare] Failed to delete WAF rule %s: %v", ruleID, err)
return fmt.Errorf("delete rule %s: %w", ruleID, err)
}
c.logger.Printf("[CF] Deleted rule %s", ruleID)
c.logger.Printf("[INFO] [cloudflare] Deleted rule %s", ruleID)
return nil
}
+4 -4
View File
@@ -17,12 +17,12 @@ type zone struct {
// It tries the exact domain first, then strips subdomains progressively.
func (c *Client) GetZoneID(ctx context.Context, domain string) (string, error) {
if c.debug {
c.logger.Printf("[CF-DEBUG] GetZoneID: looking up zone for domain %q", domain)
c.logger.Printf("[DEBUG] [cloudflare] GetZoneID: looking up zone for domain %q", domain)
}
// Try exact domain first (e.g., "demo-felhom.eu")
if c.debug {
c.logger.Printf("[CF-DEBUG] GetZoneID: trying exact domain %q", domain)
c.logger.Printf("[DEBUG] [cloudflare] GetZoneID: trying exact domain %q", domain)
}
id, err := c.lookupZone(ctx, domain)
if err != nil {
@@ -40,7 +40,7 @@ func (c *Client) GetZoneID(ctx context.Context, domain string) (string, error) {
break
}
if c.debug {
c.logger.Printf("[CF-DEBUG] GetZoneID: trying parent domain %q", parent)
c.logger.Printf("[DEBUG] [cloudflare] GetZoneID: trying parent domain %q", parent)
}
id, err = c.lookupZone(ctx, parent)
if err != nil {
@@ -73,6 +73,6 @@ func (c *Client) lookupZone(ctx context.Context, name string) (string, error) {
return "", nil
}
c.logger.Printf("[CF] Resolved zone %q → %s", name, zones[0].ID)
c.logger.Printf("[INFO] [cloudflare] Resolved zone %q → %s", name, zones[0].ID)
return zones[0].ID, nil
}
+6 -6
View File
@@ -142,11 +142,11 @@ func (m *Manager) Toggle(ctx context.Context, provider, target string, enable bo
state.Enabled = true
state.Status = "active"
state.EnabledAt = time.Now().UTC().Format(time.RFC3339)
m.logger.Printf("[INFO] Integration %s enabled", key)
m.logger.Printf("[INFO] [integrations] Integration %s enabled", key)
} else {
start := time.Now()
if err := handler.Revoke(ac); err != nil {
m.logger.Printf("[WARN] Integration revoke failed for %s: %v", key, err)
m.logger.Printf("[WARN] [integrations] Integration revoke failed for %s: %v", key, err)
state.LastError = err.Error()
}
if m.isDebug() {
@@ -154,7 +154,7 @@ func (m *Manager) Toggle(ctx context.Context, provider, target string, enable bo
}
state.Enabled = false
state.Status = "disabled"
m.logger.Printf("[INFO] Integration %s disabled", key)
m.logger.Printf("[INFO] [integrations] Integration %s disabled", key)
}
if err := m.sett.SetIntegrationState(key, state); err != nil {
@@ -268,7 +268,7 @@ func (m *Manager) ReapplyConfigForTarget(targetName string) {
ac, err := m.buildApplyContext(provider, target)
if err != nil {
m.logger.Printf("[WARN] Cannot build context for integration %s reapply: %v", key, err)
m.logger.Printf("[WARN] [integrations] Cannot build context for integration %s reapply: %v", key, err)
continue
}
@@ -276,7 +276,7 @@ func (m *Manager) ReapplyConfigForTarget(targetName string) {
ac.RestartStack = func(string) error { return nil }
if err := handler.Apply(ac); err != nil {
m.logger.Printf("[WARN] Integration config reapply failed for %s: %v", key, err)
m.logger.Printf("[WARN] [integrations] Integration config reapply failed for %s: %v", key, err)
if m.isDebug() {
m.logger.Printf("[DEBUG] [integrations] ReapplyConfigForTarget: %s failed: %v", key, err)
}
@@ -292,7 +292,7 @@ func (m *Manager) ReapplyConfigForTarget(targetName string) {
state.Status = "active"
state.LastError = ""
_ = m.sett.SetIntegrationState(key, state)
m.logger.Printf("[INFO] Integration %s config reapplied for %s", key, targetName)
m.logger.Printf("[INFO] [integrations] Integration %s config reapplied for %s", key, targetName)
}
}
@@ -29,8 +29,6 @@ func (h *OnlyOfficeFileBrowserHandler) Apply(ac *ApplyContext) error {
configPath := filepath.Join(ac.StacksDir, "filebrowser", "config.yaml")
officeURL := fmt.Sprintf("https://%s.%s", subdomain, ac.Domain)
ac.Logger.Printf("[DEBUG] [integrations] OnlyOfficeFileBrowser.Apply: jwtSecretPresent=%v subdomain=%s configPath=%s officeURL=%s", jwtSecret != "", subdomain, configPath, officeURL)
configData, err := os.ReadFile(configPath)
if err != nil {
ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser apply: %v", err)
@@ -59,7 +57,7 @@ func (h *OnlyOfficeFileBrowserHandler) Apply(ac *ApplyContext) error {
return fmt.Errorf("config átnevezési hiba: %w", err)
}
ac.Logger.Printf("[INFO] FileBrowser config updated with OnlyOffice integration")
ac.Logger.Printf("[INFO] [integrations] FileBrowser config updated with OnlyOffice integration")
return ac.RestartStack("filebrowser")
}
@@ -94,7 +92,7 @@ func (h *OnlyOfficeFileBrowserHandler) Revoke(ac *ApplyContext) error {
return fmt.Errorf("config átnevezési hiba: %w", err)
}
ac.Logger.Printf("[INFO] FileBrowser config cleaned — OnlyOffice integration removed")
ac.Logger.Printf("[INFO] [integrations] FileBrowser config cleaned — OnlyOffice integration removed")
return ac.RestartStack("filebrowser")
}
@@ -71,16 +71,16 @@ func (h *OnlyOfficeNextcloudHandler) Apply(ac *ApplyContext) error {
cancel()
if err != nil {
if cmd.tolerate != "" && strings.Contains(string(out), cmd.tolerate) {
ac.Logger.Printf("[DEBUG] Nextcloud occ: tolerated — %s", strings.TrimSpace(string(out)))
ac.Logger.Printf("[DEBUG] [integrations] Nextcloud occ: tolerated — %s", strings.TrimSpace(string(out)))
continue
}
ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-Nextcloud apply: occ %s failed: %v", cmd.args[len(cmd.args)-1], err)
return fmt.Errorf("occ parancs sikertelen (%s): %v (kimenet: %s)", cmd.args[len(cmd.args)-1], err, strings.TrimSpace(string(out)))
}
ac.Logger.Printf("[DEBUG] Nextcloud occ %s: ok", strings.Join(cmd.args[7:], " "))
ac.Logger.Printf("[DEBUG] [integrations] Nextcloud occ %s: ok", strings.Join(cmd.args[7:], " "))
}
ac.Logger.Printf("[INFO] OnlyOffice integration applied to Nextcloud")
ac.Logger.Printf("[INFO] [integrations] OnlyOffice integration applied to Nextcloud")
return nil
}
@@ -96,13 +96,13 @@ func (h *OnlyOfficeNextcloudHandler) Revoke(ac *ApplyContext) error {
if strings.Contains(err.Error(), "No such container") ||
strings.Contains(outStr, "not enabled") ||
strings.Contains(outStr, "not installed") {
ac.Logger.Printf("[DEBUG] Nextcloud occ app:disable skipped — %s", strings.TrimSpace(outStr))
ac.Logger.Printf("[DEBUG] [integrations] Nextcloud occ app:disable skipped — %s", strings.TrimSpace(outStr))
return nil
}
ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-Nextcloud revoke: occ app:disable failed: %v", err)
return fmt.Errorf("occ app:disable sikertelen: %v (kimenet: %s)", err, strings.TrimSpace(outStr))
}
ac.Logger.Printf("[INFO] OnlyOffice integration revoked from Nextcloud")
ac.Logger.Printf("[INFO] [integrations] OnlyOffice integration revoked from Nextcloud")
return nil
}
+3 -3
View File
@@ -69,12 +69,12 @@ func (c *MetricsCollector) loop(ctx context.Context) {
func (c *MetricsCollector) sampleWith(ctx context.Context) {
sys := c.sampleSystem()
if err := c.store.InsertSystemMetrics(sys); err != nil {
c.logger.Printf("[WARN] Failed to store system metrics: %v", err)
c.logger.Printf("[WARN] [metrics] Failed to store system metrics: %v", err)
}
containers := c.sampleContainers(ctx)
if err := c.store.InsertContainerMetrics(containers); err != nil {
c.logger.Printf("[WARN] Failed to store container metrics: %v", err)
c.logger.Printf("[WARN] [metrics] Failed to store container metrics: %v", err)
}
}
@@ -104,7 +104,7 @@ func (c *MetricsCollector) sampleContainers(parentCtx context.Context) []Contain
"--format", "{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}")
out, err := cmd.Output()
if err != nil {
c.logger.Printf("[WARN] docker stats failed: %v", err)
c.logger.Printf("[WARN] [metrics] docker stats failed: %v", err)
return nil
}
+2 -2
View File
@@ -75,7 +75,7 @@ func ScanContainerLogs(containerNames []string, since time.Duration, logger *log
elapsed := time.Since(start)
dbg("log scan completed: %d containers in %s", len(containerNames), elapsed.Round(time.Millisecond))
if elapsed > 5*time.Minute && logger != nil {
logger.Printf("[WARN] Log scan took %s (>5min) for %d containers", elapsed.Round(time.Second), len(containerNames))
logger.Printf("[WARN] [metrics] Log scan took %s (>5min) for %d containers", elapsed.Round(time.Second), len(containerNames))
}
return results
@@ -92,7 +92,7 @@ func scanOneContainer(name string, since time.Duration, logger *log.Logger) Cont
output, err := cmd.CombinedOutput()
if err != nil {
if logger != nil {
logger.Printf("[DEBUG] logscanner: docker logs %s: %v", name, err)
logger.Printf("[DEBUG] [metrics] logscanner: docker logs %s: %v", name, err)
}
return summary
}
+1 -1
View File
@@ -39,7 +39,7 @@ func (s *MetricsStore) GetContainerTelemetry(since time.Time) ([]ContainerTeleme
var ct ContainerTelemetry
if err := rows.Scan(&ct.ContainerName, &ct.MemoryAvgMB, &ct.MemoryPeakMB,
&ct.CPUAvgPercent, &ct.SampleCount); err != nil {
log.Printf("[WARN] telemetry row scan failed: %v", err)
log.Printf("[WARN] [metrics] telemetry row scan failed: %v", err)
continue
}
results = append(results, ct)
+16 -16
View File
@@ -38,7 +38,7 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
sysInfo := system.GetInfo(hddPath, cpuCollector)
if debug {
logger.Printf("[DEBUG] [HEALTH] Raw values: disk=%.1f%%, hdd=%.1f%% (configured=%v), mem=%.1f%% (%dMB/%dMB), cpu=%.1f%%, temp=%.1f°C (%s)",
logger.Printf("[DEBUG] [monitor] Raw values: disk=%.1f%%, hdd=%.1f%% (configured=%v), mem=%.1f%% (%dMB/%dMB), cpu=%.1f%%, temp=%.1f°C (%s)",
sysInfo.DiskPercent, sysInfo.HDDPercent, sysInfo.HDDConfigured,
sysInfo.MemPercent, sysInfo.UsedMemMB, sysInfo.TotalMemMB,
sysInfo.CPUPercent, sysInfo.TemperatureCelsius, sysInfo.TemperatureSource)
@@ -52,7 +52,7 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
logger.Printf("[WARN] [monitor] Disk (SSD) threshold breached: %.0f%% (limit: %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskCritPercent)
}
if debug {
logger.Printf("[DEBUG] [HEALTH] SSD disk: CRITICAL (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskCritPercent)
logger.Printf("[DEBUG] [monitor] SSD disk: CRITICAL (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskCritPercent)
}
} else if sysInfo.DiskPercent >= float64(cfg.Monitoring.Thresholds.DiskWarnPercent) {
report.Warnings = append(report.Warnings, fmt.Sprintf("SSD disk usage high: %.0f%%", sysInfo.DiskPercent))
@@ -60,12 +60,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
logger.Printf("[WARN] [monitor] Disk (SSD) threshold breached: %.0f%% (limit: %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskWarnPercent)
}
if debug {
logger.Printf("[DEBUG] [HEALTH] SSD disk: WARN (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskWarnPercent)
logger.Printf("[DEBUG] [monitor] SSD disk: WARN (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskWarnPercent)
}
} else {
report.Info = append(report.Info, fmt.Sprintf("SSD: %.0f%% used", sysInfo.DiskPercent))
if debug {
logger.Printf("[DEBUG] [HEALTH] SSD disk: OK (%.0f%%)", sysInfo.DiskPercent)
logger.Printf("[DEBUG] [monitor] SSD disk: OK (%.0f%%)", sysInfo.DiskPercent)
}
}
}
@@ -93,12 +93,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
logger.Printf("[WARN] [monitor] Memory threshold breached: %.0f%% (limit: %d%%)", sysInfo.MemPercent, cfg.Monitoring.Thresholds.MemoryWarnPercent)
}
if debug {
logger.Printf("[DEBUG] [HEALTH] Memory: WARN (%.0f%% >= %d%%)", sysInfo.MemPercent, cfg.Monitoring.Thresholds.MemoryWarnPercent)
logger.Printf("[DEBUG] [monitor] Memory: WARN (%.0f%% >= %d%%)", sysInfo.MemPercent, cfg.Monitoring.Thresholds.MemoryWarnPercent)
}
} else {
report.Info = append(report.Info, fmt.Sprintf("Memory: %.0f%% used", sysInfo.MemPercent))
if debug {
logger.Printf("[DEBUG] [HEALTH] Memory: OK (%.0f%%)", sysInfo.MemPercent)
logger.Printf("[DEBUG] [monitor] Memory: OK (%.0f%%)", sysInfo.MemPercent)
}
}
}
@@ -111,12 +111,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
logger.Printf("[WARN] [monitor] CPU threshold breached: %.0f%% (limit: %d%%)", sysInfo.CPUPercent, cfg.Monitoring.Thresholds.CPUWarnPercent)
}
if debug {
logger.Printf("[DEBUG] [HEALTH] CPU: WARN (%.0f%% >= %d%%)", sysInfo.CPUPercent, cfg.Monitoring.Thresholds.CPUWarnPercent)
logger.Printf("[DEBUG] [monitor] CPU: WARN (%.0f%% >= %d%%)", sysInfo.CPUPercent, cfg.Monitoring.Thresholds.CPUWarnPercent)
}
} else {
report.Info = append(report.Info, fmt.Sprintf("CPU: %.0f%%", sysInfo.CPUPercent))
if debug {
logger.Printf("[DEBUG] [HEALTH] CPU: OK (%.0f%%)", sysInfo.CPUPercent)
logger.Printf("[DEBUG] [monitor] CPU: OK (%.0f%%)", sysInfo.CPUPercent)
}
}
}
@@ -129,12 +129,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
logger.Printf("[WARN] [monitor] Temperature threshold breached: %.0f°C (limit: %d°C)", sysInfo.TemperatureCelsius, cfg.Monitoring.Thresholds.TemperatureWarnCelsius)
}
if debug {
logger.Printf("[DEBUG] [HEALTH] Temperature: WARN (%.0f°C >= %d°C)", sysInfo.TemperatureCelsius, cfg.Monitoring.Thresholds.TemperatureWarnCelsius)
logger.Printf("[DEBUG] [monitor] Temperature: WARN (%.0f°C >= %d°C)", sysInfo.TemperatureCelsius, cfg.Monitoring.Thresholds.TemperatureWarnCelsius)
}
} else {
report.Info = append(report.Info, fmt.Sprintf("Temperature: %.0f°C", sysInfo.TemperatureCelsius))
if debug {
logger.Printf("[DEBUG] [HEALTH] Temperature: OK (%.0f°C)", sysInfo.TemperatureCelsius)
logger.Printf("[DEBUG] [monitor] Temperature: OK (%.0f°C)", sysInfo.TemperatureCelsius)
}
}
}
@@ -143,18 +143,18 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
if err := checkDocker(); err != nil {
report.Issues = append(report.Issues, fmt.Sprintf("Docker: %v", err))
if debug {
logger.Printf("[DEBUG] [HEALTH] Docker daemon: FAIL (%v)", err)
logger.Printf("[DEBUG] [monitor] Docker daemon: FAIL (%v)", err)
}
} else {
report.Info = append(report.Info, "Docker: reachable")
if debug {
logger.Printf("[DEBUG] [HEALTH] Docker daemon: OK")
logger.Printf("[DEBUG] [monitor] Docker daemon: OK")
}
}
// 6. Protected containers
if debug {
logger.Printf("[DEBUG] [HEALTH] Checking %d protected containers: %v", len(cfg.Stacks.Protected), cfg.Stacks.Protected)
logger.Printf("[DEBUG] [monitor] Checking %d protected containers: %v", len(cfg.Stacks.Protected), cfg.Stacks.Protected)
}
missingProtected := checkProtectedContainers(cfg.Stacks.Protected)
for _, name := range missingProtected {
@@ -162,9 +162,9 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
}
if debug {
if len(missingProtected) > 0 {
logger.Printf("[DEBUG] [HEALTH] Protected containers missing: %v", missingProtected)
logger.Printf("[DEBUG] [monitor] Protected containers missing: %v", missingProtected)
} else {
logger.Printf("[DEBUG] [HEALTH] All protected containers running")
logger.Printf("[DEBUG] [monitor] All protected containers running")
}
}
@@ -185,7 +185,7 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora
}
if debug {
logger.Printf("[DEBUG] [HEALTH] Final status: %s (issues=%d, warnings=%d, info=%d)",
logger.Printf("[DEBUG] [monitor] Final status: %s (issues=%d, warnings=%d, info=%d)",
report.Status, len(report.Issues), len(report.Warnings), len(report.Info))
}
+1 -1
View File
@@ -115,6 +115,6 @@ func (p *Pinger) send(uuid, suffix, body string) error {
lastErr = fmt.Errorf("HTTP %d", resp.StatusCode)
}
p.logger.Printf("[WARN] Health ping failed after 3 attempts (%s): %v", uuid, lastErr)
p.logger.Printf("[WARN] [monitor] Health ping failed after 3 attempts (%s): %v", uuid, lastErr)
return nil // Never let ping failures affect the caller
}
+44 -44
View File
@@ -200,7 +200,7 @@ func (w *StorageWatchdog) handleConnectedProbe(sp settings.StoragePath, state *p
if result.Status == system.ProbeConnected {
if state.consecutiveFailures > 0 {
w.logger.Printf("[DEBUG] [STORAGE] Probe recovered for %s after %d failures", sp.Path, state.consecutiveFailures)
w.logger.Printf("[DEBUG] [storage] Probe recovered for %s after %d failures", sp.Path, state.consecutiveFailures)
}
state.consecutiveFailures = 0
state.lastStatus = "connected"
@@ -209,7 +209,7 @@ func (w *StorageWatchdog) handleConnectedProbe(sp settings.StoragePath, state *p
// Every 60 probes (~5 minutes at 5s interval): emit summary
if state.probeCount >= 60 {
avgLatency := state.totalLatency / time.Duration(state.probeCount)
w.logger.Printf("[DEBUG] [STORAGE] Storage watchdog: %s — %d/%d probes OK (last 5m, avg %dms)",
w.logger.Printf("[DEBUG] [storage] Storage watchdog: %s — %d/%d probes OK (last 5m, avg %dms)",
sp.Path, state.probeOKCount, state.probeCount, avgLatency.Milliseconds())
state.probeCount = 0
state.probeOKCount = 0
@@ -224,11 +224,11 @@ func (w *StorageWatchdog) handleConnectedProbe(sp settings.StoragePath, state *p
// Debug: log immediately on unexpected failure (was connected, now failing)
if w.isDebug() && state.lastStatus == "connected" {
w.logger.Printf("[DEBUG] [STORAGE] Storage probe failed for %s (%d/%d before disconnect): %v",
w.logger.Printf("[DEBUG] [storage] Storage probe failed for %s (%d/%d before disconnect): %v",
sp.Path, state.consecutiveFailures, probeThreshold, result.Err)
}
w.logger.Printf("[WARN] [STORAGE] Probe failed for %s (%d/%d): %v",
w.logger.Printf("[WARN] [storage] Probe failed for %s (%d/%d): %v",
sp.Path, state.consecutiveFailures, probeThreshold, result.Err)
if state.consecutiveFailures >= probeThreshold {
@@ -249,14 +249,14 @@ func (w *StorageWatchdog) handleDisconnect(sp settings.StoragePath, state *pathP
if label == "" {
label = sp.Path
}
w.logger.Printf("[ERROR] [STORAGE] Drive disconnected: %s (%s)", sp.Path, label)
w.logger.Printf("[ERROR] [storage] Drive disconnected: %s (%s)", sp.Path, label)
// 1. Find and stop affected stacks
stoppedStacks := w.stopAffectedStacks(sp.Path)
// 2. Mark disconnected in settings (persists to settings.json)
if err := w.settings.SetDisconnected(sp.Path, true, stoppedStacks); err != nil {
w.logger.Printf("[ERROR] [STORAGE] Failed to mark disconnected: %v", err)
w.logger.Printf("[ERROR] [storage] Failed to mark disconnected: %v", err)
}
// 3. Lazy unmount stale mount (if probe timed out — mount is likely hanging)
@@ -302,7 +302,7 @@ func (w *StorageWatchdog) handleReconnectCheck(ctx context.Context, sp settings.
}
if w.isDebug() {
w.logger.Printf("[DEBUG] [STORAGE] Reconnect check for %s: UUID=%s, mountPath=%s, isAttachWizard=%v",
w.logger.Printf("[DEBUG] [storage] Reconnect check for %s: UUID=%s, mountPath=%s, isAttachWizard=%v",
sp.Path, uuid, mountPath, isAttachWizard)
}
@@ -316,35 +316,35 @@ func (w *StorageWatchdog) handleReconnectCheck(ctx context.Context, sp settings.
if label == "" {
label = sp.Path
}
w.logger.Printf("[INFO] [STORAGE] Drive reconnected (UUID found), attempting remount: %s (%s)", sp.Path, label)
w.logger.Printf("[INFO] [storage] Drive reconnected (UUID found), attempting remount: %s (%s)", sp.Path, label)
if w.isDebug() {
w.logger.Printf("[DEBUG] [STORAGE] UUID %s found at %s, mounting %s (raw=%s, attachWizard=%v)",
w.logger.Printf("[DEBUG] [storage] UUID %s found at %s, mounting %s (raw=%s, attachWizard=%v)",
uuid, uuidPath, sp.Path, rawPath, isAttachWizard)
}
// Attempt remount
if err := w.remount(sp.Path, rawPath, isAttachWizard); err != nil {
w.logger.Printf("[ERROR] [STORAGE] Remount failed for %s: %v", sp.Path, err)
w.logger.Printf("[ERROR] [storage] Remount failed for %s: %v", sp.Path, err)
return // Try again next cycle
}
// Verify with a probe
verifyResult := system.ProbeStoragePath(sp.Path)
if verifyResult.Status != system.ProbeConnected {
w.logger.Printf("[ERROR] [STORAGE] Post-remount probe failed for %s: %v", sp.Path, verifyResult.Err)
w.logger.Printf("[ERROR] [storage] Post-remount probe failed for %s: %v", sp.Path, verifyResult.Err)
if w.isDebug() {
w.logger.Printf("[DEBUG] [STORAGE] Post-mount verification failed for %s: status=%v, err=%v",
w.logger.Printf("[DEBUG] [storage] Post-mount verification failed for %s: status=%v, err=%v",
sp.Path, verifyResult.Status, verifyResult.Err)
}
return
}
if w.isDebug() {
w.logger.Printf("[DEBUG] [STORAGE] Post-mount verification succeeded for %s", sp.Path)
w.logger.Printf("[DEBUG] [storage] Post-mount verification succeeded for %s", sp.Path)
}
w.logger.Printf("[INFO] [STORAGE] Drive successfully remounted: %s (%s)", sp.Path, label)
w.logger.Printf("[INFO] [storage] Drive successfully remounted: %s (%s)", sp.Path, label)
// Clean stale restic locks
w.cleanResticLocks(ctx, sp.Path)
@@ -354,7 +354,7 @@ func (w *StorageWatchdog) handleReconnectCheck(ctx context.Context, sp settings.
// Clear disconnected but preserve StoppedStacks for the restart UI
if err := w.settings.SetDisconnected(sp.Path, false, filteredStacks); err != nil {
w.logger.Printf("[ERROR] [STORAGE] Failed to clear disconnected: %v", err)
w.logger.Printf("[ERROR] [storage] Failed to clear disconnected: %v", err)
}
// Update in-memory state
@@ -400,20 +400,20 @@ func (w *StorageWatchdog) stopAffectedStacks(drivePath string) []string {
// Don't stop protected stacks
if w.cfg.IsProtectedStack(stack.Name) {
w.logger.Printf("[WARN] [STORAGE] Skipping protected stack: %s", stack.Name)
w.logger.Printf("[WARN] [storage] Skipping protected stack: %s", stack.Name)
continue
}
w.logger.Printf("[INFO] [STORAGE] Stopping stack %s (drive disconnected: %s)", stack.Name, drivePath)
w.logger.Printf("[INFO] [storage] Stopping stack %s (drive disconnected: %s)", stack.Name, drivePath)
if err := w.stackProvider.StopStack(stack.Name); err != nil {
w.logger.Printf("[ERROR] [STORAGE] Failed to stop stack %s: %v", stack.Name, err)
w.logger.Printf("[ERROR] [storage] Failed to stop stack %s: %v", stack.Name, err)
continue // Don't add to stopped list if stop failed
}
stopped = append(stopped, stack.Name)
}
if len(stopped) > 0 {
w.logger.Printf("[INFO] [STORAGE] Stopped %d stack(s) due to drive disconnect: %v", len(stopped), stopped)
w.logger.Printf("[INFO] [storage] Stopped %d stack(s) due to drive disconnect: %v", len(stopped), stopped)
}
return stopped
}
@@ -426,18 +426,18 @@ func (w *StorageWatchdog) lazyUnmount(path string) {
// Unmount the bind/main path
cmd := exec.Command("umount", "-l", path)
if out, err := cmd.CombinedOutput(); err != nil {
w.logger.Printf("[WARN] [STORAGE] umount -l %s: %v (%s)", path, err, strings.TrimSpace(string(out)))
w.logger.Printf("[WARN] [storage] umount -l %s: %v (%s)", path, err, strings.TrimSpace(string(out)))
} else {
w.logger.Printf("[INFO] [STORAGE] Lazy unmounted: %s", path)
w.logger.Printf("[INFO] [storage] Lazy unmounted: %s", path)
}
// Then unmount the raw path if it's an attach-wizard drive
if isAttachWizard && rawPath != "" {
cmd = exec.Command("umount", "-l", rawPath)
if out, err := cmd.CombinedOutput(); err != nil {
w.logger.Printf("[WARN] [STORAGE] umount -l %s: %v (%s)", rawPath, err, strings.TrimSpace(string(out)))
w.logger.Printf("[WARN] [storage] umount -l %s: %v (%s)", rawPath, err, strings.TrimSpace(string(out)))
} else {
w.logger.Printf("[INFO] [STORAGE] Lazy unmounted raw: %s", rawPath)
w.logger.Printf("[INFO] [storage] Lazy unmounted raw: %s", rawPath)
}
}
}
@@ -456,19 +456,19 @@ func (w *StorageWatchdog) remount(path, rawPath string, isAttachWizard bool) err
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("mount raw %s: %v (%s)", rawPath, err, strings.TrimSpace(string(out)))
}
w.logger.Printf("[INFO] [STORAGE] Mounted raw: %s", rawPath)
w.logger.Printf("[INFO] [storage] Mounted raw: %s", rawPath)
cmd = exec.Command("mount", "-T", hostFstabPath, path)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("mount bind %s: %v (%s)", path, err, strings.TrimSpace(string(out)))
}
w.logger.Printf("[INFO] [STORAGE] Mounted bind: %s", path)
w.logger.Printf("[INFO] [storage] Mounted bind: %s", path)
} else {
cmd := exec.Command("mount", "-T", hostFstabPath, path)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("mount %s: %v (%s)", path, err, strings.TrimSpace(string(out)))
}
w.logger.Printf("[INFO] [STORAGE] Mounted: %s", path)
w.logger.Printf("[INFO] [storage] Mounted: %s", path)
}
return nil
}
@@ -482,11 +482,11 @@ func (w *StorageWatchdog) cleanResticLocks(ctx context.Context, drivePath string
return // No locks dir or no lock files
}
w.logger.Printf("[INFO] [STORAGE] Found %d restic lock file(s) in %s, running unlock", len(entries), repoPath)
w.logger.Printf("[INFO] [storage] Found %d restic lock file(s) in %s, running unlock", len(entries), repoPath)
if w.unlockRepo != nil {
if err := w.unlockRepo(ctx, repoPath); err != nil {
w.logger.Printf("[WARN] [STORAGE] Restic unlock failed for %s: %v", repoPath, err)
w.logger.Printf("[WARN] [storage] Restic unlock failed for %s: %v", repoPath, err)
}
}
}
@@ -529,7 +529,7 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop
if label == "" {
label = sp.Path
}
w.logger.Printf("[INFO] [STORAGE] Safe disconnect requested: %s (%s)", path, label)
w.logger.Printf("[INFO] [storage] Safe disconnect requested: %s (%s)", path, label)
// 1. Stop affected stacks
stoppedStacks = w.stopAffectedStacks(path)
@@ -544,7 +544,7 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop
cmd := exec.Command("umount", path)
if out, umountErr := cmd.CombinedOutput(); umountErr != nil {
// Try lazy unmount as fallback
w.logger.Printf("[WARN] [STORAGE] umount %s failed, trying lazy: %v", path, umountErr)
w.logger.Printf("[WARN] [storage] umount %s failed, trying lazy: %v", path, umountErr)
cmd = exec.Command("umount", "-l", path)
if out, umountErr = cmd.CombinedOutput(); umountErr != nil {
return stoppedStacks, fmt.Errorf("umount %s failed: %v (%s)", path, umountErr, strings.TrimSpace(string(out)))
@@ -557,14 +557,14 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop
if out, umountErr := cmd.CombinedOutput(); umountErr != nil {
cmd = exec.Command("umount", "-l", rawPath)
if out, umountErr = cmd.CombinedOutput(); umountErr != nil {
w.logger.Printf("[WARN] [STORAGE] umount raw %s failed: %v (%s)", rawPath, umountErr, strings.TrimSpace(string(out)))
w.logger.Printf("[WARN] [storage] umount raw %s failed: %v (%s)", rawPath, umountErr, strings.TrimSpace(string(out)))
}
}
}
// 4. Mark disconnected
if setErr := w.settings.SetDisconnected(path, true, stoppedStacks); setErr != nil {
w.logger.Printf("[ERROR] [STORAGE] Failed to mark disconnected: %v", setErr)
w.logger.Printf("[ERROR] [storage] Failed to mark disconnected: %v", setErr)
}
// 5. Update in-memory state
@@ -587,7 +587,7 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop
go w.pushHubReport()
}
w.logger.Printf("[INFO] [STORAGE] Safe disconnect completed: %s — drive can be removed", path)
w.logger.Printf("[INFO] [storage] Safe disconnect completed: %s — drive can be removed", path)
return stoppedStacks, nil
}
@@ -639,7 +639,7 @@ func (w *StorageWatchdog) Reconnect(ctx context.Context, path string) (stoppedSt
// Clear disconnected, preserve stopped stacks for restart UI
if setErr := w.settings.SetDisconnected(path, false, filteredStacks); setErr != nil {
w.logger.Printf("[ERROR] [STORAGE] Failed to clear disconnected: %v", setErr)
w.logger.Printf("[ERROR] [storage] Failed to clear disconnected: %v", setErr)
}
// Update in-memory state
@@ -661,7 +661,7 @@ func (w *StorageWatchdog) Reconnect(ctx context.Context, path string) (stoppedSt
go w.pushHubReport()
}
w.logger.Printf("[INFO] [STORAGE] Reconnect completed: %s", path)
w.logger.Printf("[INFO] [storage] Reconnect completed: %s", path)
return filteredStacks, nil
}
@@ -678,9 +678,9 @@ func (w *StorageWatchdog) RestartStoppedApps(path string) (started, failed []str
}
for _, name := range stacks {
w.logger.Printf("[INFO] [STORAGE] Starting stack %s (drive reconnected: %s)", name, path)
w.logger.Printf("[INFO] [storage] Starting stack %s (drive reconnected: %s)", name, path)
if err := w.stackProvider.StartStack(name); err != nil {
w.logger.Printf("[ERROR] [STORAGE] Failed to start stack %s: %v", name, err)
w.logger.Printf("[ERROR] [storage] Failed to start stack %s: %v", name, err)
failed = append(failed, name)
} else {
started = append(started, name)
@@ -689,7 +689,7 @@ func (w *StorageWatchdog) RestartStoppedApps(path string) (started, failed []str
// Clear stopped stacks list
if err := w.settings.ClearStoppedStacks(path); err != nil {
w.logger.Printf("[ERROR] [STORAGE] Failed to clear stopped stacks: %v", err)
w.logger.Printf("[ERROR] [storage] Failed to clear stopped stacks: %v", err)
}
return started, failed
@@ -723,7 +723,7 @@ func (w *StorageWatchdog) SimulateDisconnect(ctx context.Context, path string) (
if label == "" {
label = sp.Path
}
w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Simulating disconnect: %s (%s)", path, label)
w.logger.Printf("[INFO] [storage] (simulation) Simulating disconnect: %s (%s)", path, label)
// Mark as simulated so the watchdog skips probing this path
w.simulatedMu.Lock()
@@ -735,7 +735,7 @@ func (w *StorageWatchdog) SimulateDisconnect(ctx context.Context, path string) (
// Step 2: Mark disconnected in settings
if err := w.settings.SetDisconnected(path, true, stoppedStacks); err != nil {
w.logger.Printf("[ERROR] [STORAGE] [DEBUG-SIM] Failed to mark disconnected: %v", err)
w.logger.Printf("[ERROR] [storage] (simulation) Failed to mark disconnected: %v", err)
}
// Step 3: SKIPPED (no lazyUnmount — drive stays physically mounted)
@@ -761,7 +761,7 @@ func (w *StorageWatchdog) SimulateDisconnect(ctx context.Context, path string) (
go w.pushHubReport()
}
w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Disconnect simulated: %s — %d stack(s) stopped", path, len(stoppedStacks))
w.logger.Printf("[INFO] [storage] (simulation) Disconnect simulated: %s — %d stack(s) stopped", path, len(stoppedStacks))
return stoppedStacks, nil
}
@@ -780,7 +780,7 @@ func (w *StorageWatchdog) SimulateReconnect(ctx context.Context, path string) er
if label == "" {
label = sp.Path
}
w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Simulating reconnect: %s (%s)", path, label)
w.logger.Printf("[INFO] [storage] (simulation) Simulating reconnect: %s (%s)", path, label)
// Remove from simulated set
w.simulatedMu.Lock()
@@ -801,7 +801,7 @@ func (w *StorageWatchdog) SimulateReconnect(ctx context.Context, path string) er
// Clear disconnected, preserve stopped stacks for restart UI
if err := w.settings.SetDisconnected(path, false, filteredStacks); err != nil {
w.logger.Printf("[ERROR] [STORAGE] [DEBUG-SIM] Failed to clear disconnected: %v", err)
w.logger.Printf("[ERROR] [storage] (simulation) Failed to clear disconnected: %v", err)
}
// Update in-memory state
@@ -823,7 +823,7 @@ func (w *StorageWatchdog) SimulateReconnect(ctx context.Context, path string) er
go w.pushHubReport()
}
w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Reconnect simulated: %s", path)
w.logger.Printf("[INFO] [storage] (simulation) Reconnect simulated: %s", path)
return nil
}
+5 -5
View File
@@ -38,7 +38,7 @@ func BuildReport(
logger.Printf("[INFO] [report] Building system report")
}
if debug && logger != nil {
logger.Printf("[DEBUG] BuildReport: starting — version=%s, storagePaths=%d", version, len(storagePaths))
logger.Printf("[DEBUG] [report] BuildReport: starting — version=%s, storagePaths=%d", version, len(storagePaths))
}
r := &Report{
@@ -60,7 +60,7 @@ func BuildReport(
h := sha256.Sum256(data)
r.ConfigHash = hex.EncodeToString(h[:])
if debug && logger != nil {
logger.Printf("[DEBUG] BuildReport: configHash=%s (%d bytes)", r.ConfigHash[:12]+"...", len(data))
logger.Printf("[DEBUG] [report] BuildReport: configHash=%s (%d bytes)", r.ConfigHash[:12]+"...", len(data))
}
}
}
@@ -126,9 +126,9 @@ func BuildReport(
}
if debug && logger != nil {
logger.Printf("[DEBUG] BuildReport: system info collected — cpu=%.1f%%, mem=%d/%dMB, temp=%.1fC",
logger.Printf("[DEBUG] [report] BuildReport: system info collected — cpu=%.1f%%, mem=%d/%dMB, temp=%.1fC",
sysInfo.CPUPercent, sysInfo.UsedMemMB, sysInfo.TotalMemMB, sysInfo.TemperatureCelsius)
logger.Printf("[DEBUG] BuildReport: storage entries=%d", len(r.Storage))
logger.Printf("[DEBUG] [report] BuildReport: storage entries=%d", len(r.Storage))
}
// Containers
@@ -175,7 +175,7 @@ func BuildReport(
}
if debug && logger != nil {
logger.Printf("[DEBUG] BuildReport: complete — containers=%d, health=%s, deployed=%d, available=%d, app_telemetry=%d",
logger.Printf("[DEBUG] [report] BuildReport: complete — containers=%d, health=%s, deployed=%d, available=%d, app_telemetry=%d",
r.Containers.Total, r.Health.Status, len(r.Stacks.Deployed), len(r.Stacks.Available), len(r.AppTelemetry))
}
+4 -3
View File
@@ -67,14 +67,14 @@ func BuildInfraBackup(
if data, err := os.ReadFile(settingsPath); err == nil {
ib.SettingsJSONB64 = base64.StdEncoding.EncodeToString(data)
} else if !os.IsNotExist(err) {
logger.Printf("[WARN] Infra backup: could not read settings.json: %v", err)
logger.Printf("[WARN] [report] Infra backup: could not read settings.json: %v", err)
}
// Read primary restic password (important but non-fatal)
if data, err := os.ReadFile(resticPasswordFile); err == nil {
ib.ResticPassword = base64.StdEncoding.EncodeToString(data)
} else if !os.IsNotExist(err) {
logger.Printf("[WARN] Infra backup: could not read restic password file: %v", err)
logger.Printf("[WARN] [report] Infra backup: could not read restic password file: %v", err)
}
// Read encryption key for app.yaml secrets (important but non-fatal)
@@ -82,7 +82,7 @@ func BuildInfraBackup(
if data, err := os.ReadFile(encryptionKeyFile); err == nil {
ib.EncryptionKeyB64 = base64.StdEncoding.EncodeToString(data)
} else if !os.IsNotExist(err) {
logger.Printf("[WARN] Infra backup: could not read encryption key file: %v", err)
logger.Printf("[WARN] [report] Infra backup: could not read encryption key file: %v", err)
}
}
@@ -103,5 +103,6 @@ func BuildInfraBackup(
ib.DeployedStacks = []InfraStack{}
}
logger.Printf("[INFO] [report] InfraBackup built successfully (stacks=%d)", len(ib.DeployedStacks))
return ib, nil
}
+6 -6
View File
@@ -72,7 +72,7 @@ func (p *Pusher) Push(report *Report) error {
url := p.hubURL + "/api/v1/report"
if p.debug {
p.logger.Printf("[DEBUG] Push: url=%s payload=%d bytes", url, len(data))
p.logger.Printf("[DEBUG] [report] Push: url=%s payload=%d bytes", url, len(data))
}
p.statusMu.Lock()
@@ -106,7 +106,7 @@ func (p *Pusher) Push(report *Report) error {
resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
p.logger.Printf("[INFO] Hub report pushed successfully (%d bytes)", len(data))
p.logger.Printf("[INFO] [report] Hub report pushed successfully (%d bytes)", len(data))
p.statusMu.Lock()
p.status.LastSuccess = time.Now()
p.status.LastError = ""
@@ -151,7 +151,7 @@ func (p *Pusher) PushInfraBackup(data []byte) error {
url := p.hubURL + "/api/v1/infra-backup"
if p.debug {
p.logger.Printf("[DEBUG] PushInfraBackup: url=%s payload=%d bytes", url, len(data))
p.logger.Printf("[DEBUG] [report] PushInfraBackup: url=%s payload=%d bytes", url, len(data))
}
var lastErr error
@@ -179,12 +179,12 @@ func (p *Pusher) PushInfraBackup(data []byte) error {
resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
p.logger.Printf("[INFO] Infra backup pushed to Hub (%d bytes)", len(data))
p.logger.Printf("[INFO] [report] Infra backup pushed to Hub (%d bytes)", len(data))
return nil
}
lastErr = fmt.Errorf("HTTP %d", resp.StatusCode)
if p.debug {
p.logger.Printf("[DEBUG] PushInfraBackup: attempt %d failed — HTTP %d", attempt+1, resp.StatusCode)
p.logger.Printf("[DEBUG] [report] PushInfraBackup: attempt %d failed — HTTP %d", attempt+1, resp.StatusCode)
}
}
@@ -221,7 +221,7 @@ func (p *Pusher) PushOnce(report *Report) error {
resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
p.logger.Printf("[INFO] Hub push-once sent (%d bytes)", len(data))
p.logger.Printf("[INFO] [report] Hub push-once sent (%d bytes)", len(data))
return nil
}
return fmt.Errorf("hub push-once: HTTP %d", resp.StatusCode)
+2 -2
View File
@@ -17,7 +17,7 @@ func getBudapestLocation() *time.Location {
budapestLocOnce.Do(func() {
loc, err := time.LoadLocation("Europe/Budapest")
if err != nil {
log.Printf("[ERROR] Cannot load Europe/Budapest timezone: %v — using UTC", err)
log.Printf("[ERROR] [scheduler] Cannot load Europe/Budapest timezone: %v — using UTC", err)
loc = time.UTC
}
budapestLoc = loc
@@ -60,7 +60,7 @@ func (s *Scheduler) SetDebug(on bool) {
func (s *Scheduler) dbg(format string, args ...interface{}) {
if s.debug {
s.logger.Printf("[DEBUG] [sched] "+format, args...)
s.logger.Printf("[DEBUG] [scheduler] "+format, args...)
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ func SaveState(dataDir string, state *UpdateState) error {
func ClearState(dataDir string, logger *log.Logger) {
path := filepath.Join(dataDir, stateFileName)
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
logger.Printf("[WARN] Failed to clear update state file: %v", err)
logger.Printf("[WARN] [selfupdate] Failed to clear update state file: %v", err)
return
}
logger.Printf("[INFO] [selfupdate] Update state cleared")
+17 -17
View File
@@ -91,7 +91,7 @@ func (u *Updater) GetStatus() UpdateStatus {
state, err := LoadState(u.dataDir)
if err != nil {
u.logger.Printf("[WARN] Failed to load update state: %v", err)
u.logger.Printf("[WARN] [selfupdate] Failed to load update state: %v", err)
}
return UpdateStatus{
@@ -123,7 +123,7 @@ func (u *Updater) CheckForUpdate() CheckResult {
latestStr, err := u.queryRegistry()
if err != nil {
result.Error = fmt.Sprintf("Registry lekérdezés sikertelen: %v", err)
u.logger.Printf("[WARN] Registry check failed: %v", err)
u.logger.Printf("[WARN] [selfupdate] Registry check failed: %v", err)
u.mu.Lock()
u.lastCheck = &result
u.mu.Unlock()
@@ -342,7 +342,7 @@ func (u *Updater) TriggerUpdate(initiatedBy string) error {
targetImage := fmt.Sprintf("%s:%s", u.cfg.Image, targetVersion)
previousImage := fmt.Sprintf("%s:%s", u.cfg.Image, u.currentVer)
u.logger.Printf("[INFO] Starting self-update: %s → %s (initiated by: %s)", u.currentVer, targetVersion, initiatedBy)
u.logger.Printf("[INFO] [selfupdate] Starting self-update: %s → %s (initiated by: %s)", u.currentVer, targetVersion, initiatedBy)
u.dbg("TriggerUpdate: target=%s image=%s previousImage=%s", targetVersion, targetImage, previousImage)
go u.performUpdate(targetVersion, targetImage, previousImage, initiatedBy)
@@ -370,13 +370,13 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi
InitiatedBy: initiatedBy,
}
if err := SaveState(u.dataDir, state); err != nil {
u.logger.Printf("[ERROR] Failed to save update state: %v", err)
u.logger.Printf("[ERROR] [selfupdate] Failed to save update state: %v", err)
return
}
// 2. Docker pull
u.dbg("performUpdate: step 2 — docker pull %s", targetImage)
u.logger.Printf("[INFO] Pulling image: %s", targetImage)
u.logger.Printf("[INFO] [selfupdate] Pulling image: %s", targetImage)
pullStart := time.Now()
pullOut, pullErr := runCommand("docker", "pull", targetImage)
if pullErr != nil {
@@ -384,10 +384,10 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi
state.Error = fmt.Sprintf("docker pull failed: %v — %s", pullErr, pullOut)
state.CompletedAt = time.Now().UTC().Format(time.RFC3339)
SaveState(u.dataDir, state)
u.logger.Printf("[ERROR] Docker pull failed: %v — %s", pullErr, pullOut)
u.logger.Printf("[ERROR] [selfupdate] Docker pull failed: %v — %s", pullErr, pullOut)
return
}
u.logger.Printf("[INFO] Image pulled successfully: %s", targetImage)
u.logger.Printf("[INFO] [selfupdate] Image pulled successfully: %s", targetImage)
u.dbg("performUpdate: docker pull completed in %s", time.Since(pullStart).Round(time.Millisecond))
// 3. Update compose file (replace image tag)
@@ -397,14 +397,14 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi
state.Error = fmt.Sprintf("compose update failed: %v", err)
state.CompletedAt = time.Now().UTC().Format(time.RFC3339)
SaveState(u.dataDir, state)
u.logger.Printf("[ERROR] Compose file update failed: %v", err)
u.logger.Printf("[ERROR] [selfupdate] Compose file update failed: %v", err)
return
}
u.logger.Printf("[INFO] Compose file updated with new image: %s", targetImage)
u.logger.Printf("[INFO] [selfupdate] Compose file updated with new image: %s", targetImage)
// 4. Docker compose up -d (this kills the current container)
u.dbg("performUpdate: step 4 — docker compose up -d")
u.logger.Printf("[INFO] Running docker compose up -d — container will restart")
u.logger.Printf("[INFO] [selfupdate] Running docker compose up -d — container will restart")
composeDir := strings.TrimSuffix(u.composePath, "/docker-compose.yml")
upOut, upErr := runCommand("docker", "compose", "-f", u.composePath, "-p", "felhom-controller", "up", "-d")
if upErr != nil {
@@ -412,15 +412,15 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi
state.Error = fmt.Sprintf("docker compose up -d failed: %v — %s", upErr, upOut)
state.CompletedAt = time.Now().UTC().Format(time.RFC3339)
SaveState(u.dataDir, state)
u.logger.Printf("[ERROR] docker compose up -d failed: %v — %s (dir: %s)", upErr, upOut, composeDir)
u.logger.Printf("[ERROR] [selfupdate] docker compose up -d failed: %v — %s (dir: %s)", upErr, upOut, composeDir)
return
}
// If we're still alive after compose up -d, log it.
// Normally this process should be killed when Docker replaces the container.
u.logger.Printf("[WARN] Still running after docker compose up -d — expected to be replaced")
u.logger.Printf("[WARN] [selfupdate] Still running after docker compose up -d — expected to be replaced")
time.Sleep(30 * time.Second)
u.logger.Printf("[WARN] Still alive 30s after docker compose up -d")
u.logger.Printf("[WARN] [selfupdate] Still alive 30s after docker compose up -d")
}
// updateComposeFile reads the compose file, replaces the image tag, and writes it back atomically.
@@ -471,7 +471,7 @@ func (u *Updater) VerifyStartup() *UpdateState {
u.dbg("VerifyStartup: checking update state in %s", u.dataDir)
state, err := LoadState(u.dataDir)
if err != nil {
u.logger.Printf("[WARN] Failed to load update state on startup: %v — clearing", err)
u.logger.Printf("[WARN] [selfupdate] Failed to load update state on startup: %v — clearing", err)
ClearState(u.dataDir, u.logger)
return nil
}
@@ -490,7 +490,7 @@ func (u *Updater) VerifyStartup() *UpdateState {
state.Error = "Version parse error on startup verification"
state.CompletedAt = time.Now().UTC().Format(time.RFC3339)
SaveState(u.dataDir, state)
u.logger.Printf("[WARN] Post-update startup: version parse error (current=%s, target=%s)", u.currentVer, state.TargetVersion)
u.logger.Printf("[WARN] [selfupdate] Post-update startup: version parse error (current=%s, target=%s)", u.currentVer, state.TargetVersion)
return state
}
@@ -499,14 +499,14 @@ func (u *Updater) VerifyStartup() *UpdateState {
state.Status = "success"
state.CompletedAt = time.Now().UTC().Format(time.RFC3339)
SaveState(u.dataDir, state)
u.logger.Printf("[INFO] Post-update startup: update successful (%s → %s)", state.PreviousVersion, state.TargetVersion)
u.logger.Printf("[INFO] [selfupdate] Post-update startup: update successful (%s → %s)", state.PreviousVersion, state.TargetVersion)
} else {
// Version mismatch — update may have failed
state.Status = "failed"
state.Error = fmt.Sprintf("Version mismatch: expected %s, running %s", state.TargetVersion, u.currentVer)
state.CompletedAt = time.Now().UTC().Format(time.RFC3339)
SaveState(u.dataDir, state)
u.logger.Printf("[WARN] Post-update startup: version mismatch (expected %s, running %s)", state.TargetVersion, u.currentVer)
u.logger.Printf("[WARN] [selfupdate] Post-update startup: version mismatch (expected %s, running %s)", state.TargetVersion, u.currentVer)
}
return state
+9 -9
View File
@@ -173,7 +173,7 @@ func Load(path string, logger *log.Logger) (*Settings, error) {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
logger.Printf("[INFO] No settings.json found, using defaults")
logger.Printf("[INFO] [settings] No settings.json found, using defaults")
return s, nil
}
return nil, fmt.Errorf("reading settings file: %w", err)
@@ -201,14 +201,14 @@ func (s *Settings) migrateResticToRsync() {
prefs.CrossDrive.Method = "rsync"
s.AppBackup[name] = prefs
if s.log != nil {
s.log.Printf("[INFO] Migrated cross-drive backup for %s from restic to rsync", name)
s.log.Printf("[INFO] [settings] Migrated cross-drive backup for %s from restic to rsync", name)
}
changed = true
}
}
if changed {
if err := s.save(); err != nil && s.log != nil {
s.log.Printf("[ERROR] Failed to save restic→rsync migration: %v", err)
s.log.Printf("[ERROR] [settings] Failed to save restic→rsync migration: %v", err)
}
}
}
@@ -592,12 +592,12 @@ func (s *Settings) AutoDiscoverStoragePaths(discoveredPaths []string, fallbackHD
if len(s.StoragePaths) > 0 {
if err := s.save(); err != nil {
logger.Printf("[ERROR] Failed to save auto-discovered storage paths: %v", err)
logger.Printf("[ERROR] [settings] Failed to save auto-discovered storage paths: %v", err)
return
}
logger.Printf("[INFO] Auto-discovered %d storage path(s)", len(s.StoragePaths))
logger.Printf("[INFO] [settings] Auto-discovered %d storage path(s)", len(s.StoragePaths))
for _, sp := range s.StoragePaths {
logger.Printf("[INFO] %s (%s) default=%v", sp.Path, sp.Label, sp.IsDefault)
logger.Printf("[INFO] [settings] %s (%s) default=%v", sp.Path, sp.Label, sp.IsDefault)
}
}
}
@@ -619,7 +619,7 @@ func (s *Settings) SetDisconnected(path string, disconnected bool, stoppedStacks
s.log.Printf("[DEBUG] [settings] SetDisconnected path=%q disconnected=%v stopped_stacks=%d", path, disconnected, len(stoppedStacks))
}
if s.log != nil {
s.log.Printf("[INFO] [settings] Node disconnected: %v", disconnected)
s.log.Printf("[INFO] [settings] Storage path %s disconnected=%v", path, disconnected)
}
for i := range s.StoragePaths {
if s.StoragePaths[i].Path == path {
@@ -732,7 +732,7 @@ func (s *Settings) SetDecommissioned(path, migratedTo string) error {
s.log.Printf("[DEBUG] [settings] SetDecommissioned path=%q migrated_to=%q", path, migratedTo)
}
if s.log != nil {
s.log.Printf("[INFO] [settings] Node decommissioned")
s.log.Printf("[INFO] [settings] Storage path %s decommissioned (migrated_to=%s)", path, migratedTo)
}
for i := range s.StoragePaths {
if s.StoragePaths[i].Path == path {
@@ -890,7 +890,7 @@ func (s *Settings) DrainPendingEvents() []PendingEvent {
copy(events, s.PendingEvents)
s.PendingEvents = nil
if err := s.save(); err != nil {
s.log.Printf("[ERROR] Failed to save after draining pending events: %v — restoring events", err)
s.log.Printf("[ERROR] [settings] Failed to save after draining pending events: %v — restoring events", err)
s.PendingEvents = events
return nil
}
+1 -1
View File
@@ -101,7 +101,7 @@ func (s *SetupState) SetStep(step string) {
s.Step = step
s.mu.Unlock()
if err := s.Save(); err != nil {
log.Printf("[WARN] Failed to save setup step %q: %v", step, err)
log.Printf("[WARN] [setup] Failed to save setup step %q: %v", step, err)
}
}
+17 -18
View File
@@ -161,12 +161,12 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) {
reservedMB := m.cfg.System.ReservedMemoryMB
totalMB, usedMB, memErr := system.GetMemoryMB()
if memErr != nil {
m.logger.Printf("[WARN] Cannot read system memory: %v — skipping memory check", memErr)
m.logger.Printf("[WARN] [stacks] Cannot read system memory: %v — skipping memory check", memErr)
} else {
usableMB := totalMB - reservedMB
newReqMB := ParseMemoryMB(meta.Resources.MemRequest)
m.logger.Printf("[INFO] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, real_used=%dMB, new_req=%dMB, remaining=%dMB",
m.logger.Printf("[INFO] [stacks] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, real_used=%dMB, new_req=%dMB, remaining=%dMB",
totalMB, reservedMB, usableMB, usedMB, newReqMB, usableMB-usedMB-newReqMB)
// Hard block: real used + new request exceeds usable memory
@@ -309,7 +309,7 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) {
for k := range env {
envKeys = append(envKeys, k)
}
m.logger.Printf("[INFO] Deploying stack %s with %d env vars: [%s]", req.StackName, len(env), strings.Join(envKeys, ", "))
m.logger.Printf("[INFO] [stacks] Deploying stack %s with %d env vars: [%s]", req.StackName, len(env), strings.Join(envKeys, ", "))
// Check which images are available locally before pulling
if m.isDebug() {
@@ -339,7 +339,7 @@ func (m *Manager) runComposeDeploy(name, stackDir string, env map[string]string,
_, composeErr := m.composeExecWithEnv(stackDir, env, "up", "-d")
if composeErr != nil {
m.logger.Printf("[ERROR] Stack %s deploy failed after %.1fs: %v", name, time.Since(start).Seconds(), composeErr)
m.logger.Printf("[ERROR] [stacks] Stack %s deploy failed after %.1fs: %v", name, time.Since(start).Seconds(), composeErr)
// Revert in-memory and disk state
m.mu.Lock()
if s, ok := m.stacks[name]; ok {
@@ -357,7 +357,7 @@ func (m *Manager) runComposeDeploy(name, stackDir string, env map[string]string,
return
}
m.logger.Printf("[INFO] Stack %s deployed successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logger.Printf("[INFO] [stacks] Stack %s deployed successfully (took %.1fs)", name, time.Since(start).Seconds())
// Clear deploying flag
m.mu.Lock()
@@ -427,7 +427,7 @@ func (m *Manager) UpdateStackConfig(name string, values map[string]string) error
return fmt.Errorf("restarting with new config: %w", err)
}
m.logger.Printf("[INFO] Stack %s config updated and restarted", name)
m.logger.Printf("[INFO] [stacks] Stack %s config updated and restarted", name)
return m.RefreshStatus()
}
@@ -503,13 +503,13 @@ func (m *Manager) UpdateOptionalConfig(stackName string, values map[string]strin
changed := false
for key, val := range values {
if !allowed[key] {
m.logger.Printf("[WARN] Ignoring non-optional env var: %s", key)
m.logger.Printf("[WARN] [stacks] Ignoring non-optional env var: %s", key)
continue
}
if appCfg.Env[key] != val {
appCfg.Env[key] = val
changed = true
m.logger.Printf("[INFO] Updated optional config %s for %s", key, stackName)
m.logger.Printf("[INFO] [stacks] Updated optional config %s for %s", key, stackName)
}
}
@@ -522,12 +522,12 @@ func (m *Manager) UpdateOptionalConfig(stackName string, values map[string]strin
if err := SaveAppConfig(stackDir, appCfg, m.encKey, SensitiveEnvVars(&meta)); err != nil {
return fmt.Errorf("saving app config: %w", err)
}
m.logger.Printf("[INFO] Saved updated app.yaml for %s", stackName)
m.logger.Printf("[INFO] [stacks] Saved updated app.yaml for %s", stackName)
// If deployed, recreate containers to pick up new env vars
// (docker compose restart does NOT pick up new env vars — must use up -d)
if stack.Deployed {
m.logger.Printf("[INFO] Restarting %s to apply new optional config", stackName)
m.logger.Printf("[INFO] [stacks] Restarting %s to apply new optional config", stackName)
env := m.stackEnv(stackDir)
if _, err := m.composeExecCustomEnv(stackDir, env, "up", "-d"); err != nil {
return fmt.Errorf("restart after config update: %w", err)
@@ -591,7 +591,6 @@ func LoadAppConfig(stackDir string) *AppConfig {
cfg := &AppConfig{}
if err := yaml.Unmarshal(data, cfg); err != nil {
log.Printf("[WARN] [stacks] LoadAppConfig: %v", err)
log.Printf("[DEBUG] [stacks] LoadAppConfig: failed to parse %s: %v", path, err)
return nil
}
return cfg
@@ -618,7 +617,7 @@ func SaveAppConfig(stackDir string, cfg *AppConfig, encKey []byte, sensitiveVars
continue
} else {
// H10 fix: log encryption failure — value will be saved in plaintext.
log.Printf("[WARN] Failed to encrypt env var %q: %v — saving as plaintext", k, err)
log.Printf("[WARN] [stacks] Failed to encrypt env var %q: %v — saving as plaintext", k, err)
}
}
saveCfg.Env[k] = v
@@ -763,12 +762,12 @@ func (m *Manager) InjectMissingFields(stackNames []string) {
switch field.Type {
case "secret":
if field.Generate == "" {
m.logger.Printf("[WARN] Stack %s: new secret field %s has no generator — skipping", name, field.EnvVar)
m.logger.Printf("[WARN] [stacks] Stack %s: new secret field %s has no generator — skipping", name, field.EnvVar)
continue
}
value, err := generateValue(field.Generate)
if err != nil {
m.logger.Printf("[ERROR] Stack %s: failed to generate %s: %v", name, field.EnvVar, err)
m.logger.Printf("[ERROR] [stacks] Stack %s: failed to generate %s: %v", name, field.EnvVar, err)
continue
}
appCfg.Env[field.EnvVar] = value
@@ -791,7 +790,7 @@ func (m *Manager) InjectMissingFields(stackNames []string) {
val = meta.Subdomain
}
if val == "" {
m.logger.Printf("[WARN] Stack %s: new subdomain field %s has no default — skipping", name, field.EnvVar)
m.logger.Printf("[WARN] [stacks] Stack %s: new subdomain field %s has no default — skipping", name, field.EnvVar)
continue
}
appCfg.Env[field.EnvVar] = val
@@ -801,16 +800,16 @@ func (m *Manager) InjectMissingFields(stackNames []string) {
injected = append(injected, field.EnvVar)
default:
m.logger.Printf("[WARN] Stack %s: new field %s (type=%s) requires manual configuration", name, field.EnvVar, field.Type)
m.logger.Printf("[WARN] [stacks] Stack %s: new field %s (type=%s) requires manual configuration", name, field.EnvVar, field.Type)
}
}
if len(injected) > 0 {
if err := SaveAppConfig(stackDir, appCfg, m.encKey, SensitiveEnvVars(&meta)); err != nil {
m.logger.Printf("[ERROR] Stack %s: failed to save app.yaml after injection: %v", name, err)
m.logger.Printf("[ERROR] [stacks] Stack %s: failed to save app.yaml after injection: %v", name, err)
continue
}
m.logger.Printf("[SYNC] Stack %s: injected missing fields: %s", name, strings.Join(injected, ", "))
m.logger.Printf("[INFO] [stacks] Stack %s: injected missing fields: %s", name, strings.Join(injected, ", "))
}
}
m.logger.Printf("[INFO] [stacks] InjectMissingFields: processed %d stacks", count)
+29 -30
View File
@@ -96,7 +96,7 @@ func NewManager(cfg *config.Config, logger *log.Logger) (*Manager, error) {
return nil, fmt.Errorf("docker compose not found (tried 'docker compose' and 'docker-compose')")
}
logger.Printf("[INFO] Using compose command: %s", composeCmd)
logger.Printf("[INFO] [stacks] Using compose command: %s", composeCmd)
if err := os.MkdirAll(cfg.Paths.StacksDir, 0755); err != nil {
return nil, fmt.Errorf("creating stacks directory %s: %w", cfg.Paths.StacksDir, err)
@@ -176,14 +176,14 @@ func (m *Manager) MigrateEncryption() {
m.logger.Printf("[DEBUG] [stacks] MigrateEncryption: stack %q needs migration — re-saving with encryption", s.Name)
}
if err := SaveAppConfig(stackDir, appCfg, m.encKey, sensitive); err != nil {
m.logger.Printf("[WARN] Encryption migration failed for %s: %v", s.Name, err)
m.logger.Printf("[WARN] [stacks] Encryption migration failed for %s: %v", s.Name, err)
} else {
migrated++
}
}
}
if migrated > 0 {
m.logger.Printf("[INFO] Encrypted sensitive values in %d app.yaml file(s)", migrated)
m.logger.Printf("[INFO] [stacks] Encrypted sensitive values in %d app.yaml file(s)", migrated)
} else {
m.logger.Printf("[INFO] [stacks] Encryption migration: no stacks needed migration")
}
@@ -316,7 +316,7 @@ func (m *Manager) ScanStacks() error {
}
}
if orphanCount > 0 {
m.logger.Printf("[INFO] Detected %d orphaned stack(s)", orphanCount)
m.logger.Printf("[INFO] [stacks] Detected %d orphaned stack(s)", orphanCount)
}
}
@@ -326,9 +326,8 @@ func (m *Manager) ScanStacks() error {
deployedCount++
}
}
m.logger.Printf("[INFO] Scanned stacks: %d found (%d deployed, %d available)",
m.logger.Printf("[INFO] [stacks] ScanStacks complete: %d stacks found (%d deployed, %d available)",
len(m.stacks), deployedCount, len(m.stacks)-deployedCount)
m.logger.Printf("[INFO] [stacks] ScanStacks complete: %d stacks found", len(m.stacks))
return m.refreshStatusLocked()
}
@@ -628,7 +627,7 @@ func (m *Manager) StartStack(name string) error {
m.logger.Printf("[DEBUG] [stacks] StartStack %s: current state=%s deployed=%v", name, stack.State, stack.Deployed)
}
m.logger.Printf("[INFO] Starting stack: %s", name)
m.logger.Printf("[INFO] [stacks] Starting stack: %s", name)
start := time.Now()
dir := filepath.Dir(stack.ComposePath)
@@ -639,11 +638,11 @@ func (m *Manager) StartStack(name string) error {
}
if _, err := m.composeExecCustomEnv(dir, env, "up", "-d"); err != nil {
m.logger.Printf("[ERROR] Stack %s start failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
m.logger.Printf("[ERROR] [stacks] Stack %s start failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
return fmt.Errorf("starting stack %s: %w", name, err)
}
m.logger.Printf("[INFO] Stack %s started successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logger.Printf("[INFO] [stacks] Stack %s started successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logPostStartStatus(name, dir, env)
// Clear stale health probe so refreshStatus won't re-apply an old unhealthy override.
@@ -671,16 +670,16 @@ func (m *Manager) StopStack(name string) error {
m.logger.Printf("[DEBUG] [stacks] StopStack %s: current state=%s deployed=%v containers=%d", name, stack.State, stack.Deployed, len(stack.Containers))
}
m.logger.Printf("[INFO] Stopping stack: %s", name)
m.logger.Printf("[INFO] [stacks] Stopping stack: %s", name)
start := time.Now()
dir := filepath.Dir(stack.ComposePath)
if _, err := m.composeExec(dir, "down"); err != nil {
m.logger.Printf("[ERROR] Stack %s stop failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
m.logger.Printf("[ERROR] [stacks] Stack %s stop failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
return fmt.Errorf("stopping stack %s: %w", name, err)
}
m.logger.Printf("[INFO] Stack %s stopped successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logger.Printf("[INFO] [stacks] Stack %s stopped successfully (took %.1fs)", name, time.Since(start).Seconds())
return m.RefreshStatus()
}
@@ -694,7 +693,7 @@ func (m *Manager) RestartStack(name string) error {
m.logger.Printf("[DEBUG] [stacks] RestartStack %s: current state=%s deployed=%v containers=%d", name, stack.State, stack.Deployed, len(stack.Containers))
}
m.logger.Printf("[INFO] Restarting stack: %s", name)
m.logger.Printf("[INFO] [stacks] Restarting stack: %s", name)
start := time.Now()
dir := filepath.Dir(stack.ComposePath)
env := m.stackEnv(dir)
@@ -704,11 +703,11 @@ func (m *Manager) RestartStack(name string) error {
// picked up. Plain "docker compose restart" only sends SIGTERM+start
// to existing containers without re-reading the compose file or env.
if _, err := m.composeExecCustomEnv(dir, env, "up", "-d"); err != nil {
m.logger.Printf("[ERROR] Stack %s restart failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
m.logger.Printf("[ERROR] [stacks] Stack %s restart failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
return fmt.Errorf("restarting stack %s: %w", name, err)
}
m.logger.Printf("[INFO] Stack %s restarted successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logger.Printf("[INFO] [stacks] Stack %s restarted successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logPostStartStatus(name, dir, env)
// Clear stale health probe so refreshStatus won't re-apply an old unhealthy override.
@@ -727,7 +726,7 @@ func (m *Manager) UpdateStack(name string) error {
return fmt.Errorf("stack %q not found", name)
}
m.logger.Printf("[INFO] Updating stack: %s", name)
m.logger.Printf("[INFO] [stacks] Updating stack: %s", name)
start := time.Now()
dir := filepath.Dir(stack.ComposePath)
env := m.stackEnv(dir)
@@ -737,16 +736,16 @@ func (m *Manager) UpdateStack(name string) error {
}
if _, err := m.composeExecCustomEnv(dir, env, "pull"); err != nil {
m.logger.Printf("[ERROR] Stack %s update (pull) failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
m.logger.Printf("[ERROR] [stacks] Stack %s update (pull) failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
return fmt.Errorf("pulling images for %s: %w", name, err)
}
if _, err := m.composeExecCustomEnv(dir, env, "up", "-d", "--remove-orphans"); err != nil {
m.logger.Printf("[ERROR] Stack %s update (up) failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
m.logger.Printf("[ERROR] [stacks] Stack %s update (up) failed after %.1fs: %v", name, time.Since(start).Seconds(), err)
return fmt.Errorf("recreating %s: %w", name, err)
}
m.logger.Printf("[INFO] Stack %s updated successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logger.Printf("[INFO] [stacks] Stack %s updated successfully (took %.1fs)", name, time.Since(start).Seconds())
m.logPostStartStatus(name, dir, env)
return m.RefreshStatus()
}
@@ -765,12 +764,11 @@ func (m *Manager) GetLogs(name string, lines int) (string, error) {
}
m.logger.Printf("[INFO] [stacks] Fetching logs for stack %s (tail=%d)", name, lines)
m.logger.Printf("[DEBUG] Fetching logs for %s (tail %d)", name, lines)
dir := filepath.Dir(stack.ComposePath)
output, err := m.composeExec(dir, "logs", "--tail", fmt.Sprintf("%d", lines), "--no-color")
if err != nil {
m.logger.Printf("[WARN] Failed to fetch logs for %s: %v", name, err)
m.logger.Printf("[WARN] [stacks] Failed to fetch logs for %s: %v", name, err)
return "", fmt.Errorf("getting logs for %s: %w", name, err)
}
@@ -860,13 +858,13 @@ func (m *Manager) composeExecCustomEnv(dir string, env []string, args ...string)
if exitErr, ok := err.(*exec.ExitError); ok {
exitCode = exitErr.ExitCode()
}
m.logger.Printf("[ERROR] Command failed: %s %s (in %s) — exit code %d (took %.1fs)",
m.logger.Printf("[ERROR] [stacks] Command failed: %s %s (in %s) — exit code %d (took %.1fs)",
m.composeCmd, strings.Join(args, " "), dir, exitCode, elapsed.Seconds())
if stdoutStr := truncateStr(stdout.String(), 500); stdoutStr != "" {
m.logger.Printf("[ERROR] stdout: %s", stdoutStr)
m.logger.Printf("[ERROR] [stacks] stdout: %s", stdoutStr)
}
if stderrStr := truncateStr(stderr.String(), 500); stderrStr != "" {
m.logger.Printf("[ERROR] stderr: %s", stderrStr)
m.logger.Printf("[ERROR] [stacks] stderr: %s", stderrStr)
}
return stdout.String(), fmt.Errorf("exit code %d\nstderr: %s", exitCode, truncateStr(stderr.String(), 500))
}
@@ -883,6 +881,7 @@ func (m *Manager) execCommand(name string, args ...string) (string, error) {
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
m.logger.Printf("[ERROR] [stacks] execCommand failed: %v", err)
return "", fmt.Errorf("exec %s %s: %w\nstderr: %s", name, strings.Join(args, " "), err, stderr.String())
}
@@ -913,20 +912,20 @@ func (m *Manager) logPostStartStatus(name, stackDir string, env []string) {
output, err := m.composeExecCustomEnv(stackDir, envCopy, "ps", "-a", "--format", "table {{.Name}}\t{{.Image}}\t{{.State}}\t{{.Status}}")
if err != nil {
m.logger.Printf("[WARN] Post-start status check failed for %s: %v", name, err)
m.logger.Printf("[WARN] [stacks] Post-start status check failed for %s: %v", name, err)
return
}
lines := strings.Split(strings.TrimSpace(output), "\n")
if len(lines) <= 1 {
m.logger.Printf("[WARN] Post-start status for %s: no containers found", name)
m.logger.Printf("[WARN] [stacks] Post-start status for %s: no containers found", name)
return
}
m.logger.Printf("[INFO] Stack %s post-start status:", name)
m.logger.Printf("[INFO] [stacks] Stack %s post-start status:", name)
// Skip header line
for _, line := range lines[1:] {
m.logger.Printf("[INFO] %s", line)
m.logger.Printf("[INFO] [stacks] %s", line)
}
}()
}
@@ -962,7 +961,7 @@ func (m *Manager) checkLocalImages(name, stackDir string) {
return
}
m.logger.Printf("[INFO] Deploying stack %s — checking %d images...", name, len(images))
m.logger.Printf("[INFO] [stacks] Deploying stack %s — checking %d images...", name, len(images))
for _, img := range images {
cmd := exec.Command("docker", "image", "inspect", img)
if err := cmd.Run(); err != nil {
@@ -1057,7 +1056,7 @@ func (m *Manager) getCatalogTemplateSlugs() map[string]bool {
cacheDir := filepath.Join(m.cfg.Paths.DataDir, "catalog-cache", "templates")
entries, err := os.ReadDir(cacheDir)
if err != nil {
m.logger.Printf("[WARN] Cannot read catalog cache for orphan detection: %v", err)
m.logger.Printf("[WARN] [stacks] Cannot read catalog cache for orphan detection: %v", err)
return nil
}
slugs := make(map[string]bool, len(entries))
+12 -12
View File
@@ -373,7 +373,7 @@ func (o *MigrateOrchestrator) RunEnhancedMigration(
// to the same drive the app now lives on — no redundancy, so we clear it.
if strings.HasPrefix(cfg.DestinationPath, req.TargetPath) || cfg.DestinationPath == req.TargetPath {
tier2WillClear = true
o.Logger.Printf("[INFO] Migration %s: Tier 2 will be cleared (dest %s is under target %s)",
o.Logger.Printf("[INFO] [storage] Migration %s: Tier 2 will be cleared (dest %s is under target %s)",
req.StackName, cfg.DestinationPath, req.TargetPath)
}
}
@@ -408,13 +408,13 @@ func (o *MigrateOrchestrator) RunEnhancedMigration(
dstDBDumps := backup.AppDBDumpPath(req.TargetPath, req.StackName)
if _, err := os.Stat(srcDBDumps); err == nil {
if err := os.MkdirAll(filepath.Dir(dstDBDumps), 0755); err != nil {
o.Logger.Printf("[WARN] Migration %s: failed to create DB dump dir: %v", req.StackName, err)
o.Logger.Printf("[WARN] [storage] Migration %s: failed to create DB dump dir: %v", req.StackName, err)
} else {
cmd := exec.Command("rsync", "-a", srcDBDumps+"/", dstDBDumps+"/")
if out, err := cmd.CombinedOutput(); err != nil {
o.Logger.Printf("[WARN] Migration %s: DB dump copy failed: %v — %s", req.StackName, err, string(out))
o.Logger.Printf("[WARN] [storage] Migration %s: DB dump copy failed: %v — %s", req.StackName, err, string(out))
} else {
o.Logger.Printf("[INFO] Migration %s: DB dumps copied to %s", req.StackName, dstDBDumps)
o.Logger.Printf("[INFO] [storage] Migration %s: DB dumps copied to %s", req.StackName, dstDBDumps)
}
}
}
@@ -422,9 +422,9 @@ func (o *MigrateOrchestrator) RunEnhancedMigration(
// 2. Clear Tier 2 if conflict
if tier2WillClear {
if err := o.Sett.SetCrossDriveConfig(req.StackName, nil); err != nil {
o.Logger.Printf("[WARN] Migration %s: failed to clear Tier 2 config: %v", req.StackName, err)
o.Logger.Printf("[WARN] [storage] Migration %s: failed to clear Tier 2 config: %v", req.StackName, err)
} else {
o.Logger.Printf("[INFO] Migration %s: Tier 2 cross-drive config cleared (dest was on same drive)", req.StackName)
o.Logger.Printf("[INFO] [storage] Migration %s: Tier 2 cross-drive config cleared (dest was on same drive)", req.StackName)
}
}
@@ -443,18 +443,18 @@ func (o *MigrateOrchestrator) RunEnhancedMigration(
continue
}
if err := os.RemoveAll(srcPath); err != nil {
o.Logger.Printf("[WARN] Migration %s: failed to delete stale data %s: %v", req.StackName, srcPath, err)
o.Logger.Printf("[WARN] [storage] Migration %s: failed to delete stale data %s: %v", req.StackName, srcPath, err)
} else {
o.Logger.Printf("[INFO] Migration %s: deleted stale data %s", req.StackName, srcPath)
o.Logger.Printf("[INFO] [storage] Migration %s: deleted stale data %s", req.StackName, srcPath)
}
}
// Delete DB dumps from source
if _, err := os.Stat(srcDBDumps); err == nil {
if err := os.RemoveAll(srcDBDumps); err != nil {
o.Logger.Printf("[WARN] Migration %s: failed to delete stale DB dumps %s: %v", req.StackName, srcDBDumps, err)
o.Logger.Printf("[WARN] [storage] Migration %s: failed to delete stale DB dumps %s: %v", req.StackName, srcDBDumps, err)
} else {
o.Logger.Printf("[INFO] Migration %s: deleted stale DB dumps %s", req.StackName, srcDBDumps)
o.Logger.Printf("[INFO] [storage] Migration %s: deleted stale DB dumps %s", req.StackName, srcDBDumps)
}
}
}
@@ -469,7 +469,7 @@ func (o *MigrateOrchestrator) RunEnhancedMigration(
}
if err := o.BackupTrigger.TryRunDriveBackup(context.Background(), req.TargetPath); err != nil {
o.Logger.Printf("[WARN] Migration %s: post-migration backup failed: %v", req.StackName, err)
o.Logger.Printf("[WARN] [storage] Migration %s: post-migration backup failed: %v", req.StackName, err)
progress <- MigrateProgress{
Step: "backing_up",
Message: "Biztonsági mentés nem indítható (másik mentés fut)",
@@ -477,7 +477,7 @@ func (o *MigrateOrchestrator) RunEnhancedMigration(
ElapsedSeconds: int(time.Since(start).Seconds()),
}
} else {
o.Logger.Printf("[INFO] Migration %s: post-migration backup completed for %s", req.StackName, req.TargetPath)
o.Logger.Printf("[INFO] [storage] Migration %s: post-migration backup completed for %s", req.StackName, req.TargetPath)
}
}
+16 -14
View File
@@ -91,9 +91,9 @@ func (tx *migrationTx) add(desc string, undoFn func() error) {
func (tx *migrationTx) rollback() {
for i := len(tx.actions) - 1; i >= 0; i-- {
a := tx.actions[i]
tx.logger.Printf("[ROLLBACK] %s", a.description)
tx.logger.Printf("[INFO] [storage] Rollback: %s", a.description)
if err := a.undo(); err != nil {
tx.logger.Printf("[ROLLBACK-ERROR] %s: %v", a.description, err)
tx.logger.Printf("[ERROR] [storage] Rollback failed: %s: %v", a.description, err)
}
}
}
@@ -101,6 +101,8 @@ func (tx *migrationTx) rollback() {
// MigrateDrive performs a full drive migration, moving all apps from source to dest.
func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateRequest, progress chan<- DriveMigrateProgress) error {
start := time.Now()
// TODO: debug should be driven by a dedicated Debug field, not Logger presence.
// Currently always true when Logger is set (which is always in practice).
debug := dm.Logger != nil
dbg := func(format string, args ...interface{}) {
@@ -249,7 +251,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
}
dbg("estimated data: %s (%d bytes), free on dest: %s (%d bytes)", bytesHuman(totalBytes), totalBytes, bytesHuman(freeBytes), freeBytes)
dm.Logger.Printf("[INFO] Drive migration: %s (%s) → %s (%s), %d apps, ~%s data",
dm.Logger.Printf("[INFO] [storage] Drive migration: %s (%s) → %s (%s), %d apps, ~%s data",
req.SourcePath, srcLabel, req.DestPath, dstLabel, len(appsToMigrate), bytesHuman(totalBytes))
tx := &migrationTx{logger: dm.Logger}
@@ -261,7 +263,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
for _, app := range appsToMigrate {
sendDetail("stopping", "Leállítás: "+app.DisplayName, app.Name, 5)
if err := dm.StackProvider.StopStack(app.Name); err != nil {
dm.Logger.Printf("[ERROR] Drive migration: failed to stop %s: %v", app.Name, err)
dm.Logger.Printf("[ERROR] [storage] Drive migration: failed to stop %s: %v", app.Name, err)
// Rollback: restart already stopped apps
send("rolling_back", "Hiba a leállításnál, visszagörgetés...", 0)
for _, name := range stoppedApps {
@@ -274,7 +276,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
tx.add("Restart all stopped apps", func() error {
for _, name := range stoppedApps {
if err := dm.StackProvider.StartStack(name); err != nil {
dm.Logger.Printf("[ROLLBACK-WARN] Failed to restart %s: %v", name, err)
dm.Logger.Printf("[WARN] [storage] Rollback: failed to restart %s: %v", name, err)
}
}
return nil
@@ -365,7 +367,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
if _, err := os.Stat(destAppData); os.IsNotExist(err) {
// appdata might not exist for all apps (SSD-only apps that share the drive)
// Only warn, don't fail
dm.Logger.Printf("[WARN] Drive migration: %s not found on destination (may be SSD-only)", destAppData)
dm.Logger.Printf("[WARN] [storage] Drive migration: %s not found on destination (may be SSD-only)", destAppData)
}
}
@@ -377,7 +379,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
for i, app := range appsToMigrate {
// Guard: verify app still exists
if !dm.StackProvider.StackExists(app.Name) {
dm.Logger.Printf("[WARN] Drive migration: app %s no longer exists, skipping config update", app.Name)
dm.Logger.Printf("[WARN] [storage] Drive migration: app %s no longer exists, skipping config update", app.Name)
continue
}
@@ -386,7 +388,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
oldPath := dm.StackProvider.GetStackHDDPath(app.Name)
if err := dm.StackProvider.UpdateStackHDDPath(app.Name, req.DestPath); err != nil {
dm.Logger.Printf("[ERROR] Drive migration: failed to update HDD_PATH for %s: %v", app.Name, err)
dm.Logger.Printf("[ERROR] [storage] Drive migration: failed to update HDD_PATH for %s: %v", app.Name, err)
send("rolling_back", "Konfiguráció frissítése sikertelen, visszagörgetés...", 0)
// Rollback config changes
for _, name := range configuredApps {
@@ -424,7 +426,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
// Mark source as decommissioned
if err := dm.Sett.SetDecommissioned(req.SourcePath, req.DestPath); err != nil {
dm.Logger.Printf("[WARN] Drive migration: failed to mark source as decommissioned: %v", err)
dm.Logger.Printf("[WARN] [storage] Drive migration: failed to mark source as decommissioned: %v", err)
}
tx.add("Clear decommissioned on source", func() error {
return dm.Sett.ClearDecommissioned(req.SourcePath)
@@ -441,13 +443,13 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
// Apps that moved (source→dest) with Tier 2 pointing to dest: clear (no redundancy)
appHDD := dm.StackProvider.GetStackHDDPath(name)
if appHDD == req.DestPath && cfg.DestinationPath == req.DestPath {
dm.Logger.Printf("[INFO] Drive migration: clearing Tier 2 for %s (dest same as app drive)", name)
dm.Logger.Printf("[INFO] [storage] Drive migration: clearing Tier 2 for %s (dest same as app drive)", name)
_ = dm.Sett.SetCrossDriveConfig(name, nil)
continue
}
// Apps on OTHER drives with Tier 2 pointing to source: redirect to dest
if cfg.DestinationPath == req.SourcePath {
dm.Logger.Printf("[INFO] Drive migration: redirecting Tier 2 for %s from %s to %s", name, req.SourcePath, req.DestPath)
dm.Logger.Printf("[INFO] [storage] Drive migration: redirecting Tier 2 for %s from %s to %s", name, req.SourcePath, req.DestPath)
cfg.DestinationPath = req.DestPath
_ = dm.Sett.SetCrossDriveConfig(name, cfg)
}
@@ -464,7 +466,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
sendDetail("starting", "Indítás: "+app.DisplayName, app.Name, pct)
if err := dm.StackProvider.StartStack(app.Name); err != nil {
dm.Logger.Printf("[WARN] Drive migration: failed to start %s after migration: %v", app.Name, err)
dm.Logger.Printf("[WARN] [storage] Drive migration: failed to start %s after migration: %v", app.Name, err)
// Non-fatal — log but continue
}
}
@@ -476,7 +478,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
if dm.BackupTrigger != nil {
if err := dm.BackupTrigger.TryRunDriveBackup(ctx, req.DestPath); err != nil {
dm.Logger.Printf("[WARN] Drive migration: post-migration backup failed: %v", err)
dm.Logger.Printf("[WARN] [storage] Drive migration: post-migration backup failed: %v", err)
}
}
@@ -497,7 +499,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque
}
elapsed := time.Since(start)
dm.Logger.Printf("[INFO] Drive migration complete: %s → %s, %d apps, %s elapsed",
dm.Logger.Printf("[INFO] [storage] Drive migration complete: %s → %s, %d apps, %s elapsed",
req.SourcePath, req.DestPath, len(appsToMigrate), elapsed.Round(time.Second))
// --- Step 10: Done ---
+21 -21
View File
@@ -77,22 +77,22 @@ func maskRepoURL(url string) string {
// Start begins the periodic sync loop. Call Stop() to terminate.
func (s *Syncer) Start() {
if s.cfg.Git.RepoURL == "" {
s.logger.Println("[SYNC] Git repo URL is empty — sync disabled (manual mode)")
s.logger.Println("[WARN] [sync] Git repo URL is empty — sync disabled (manual mode)")
return
}
interval, err := time.ParseDuration(s.cfg.Git.SyncInterval)
if err != nil {
s.logger.Printf("[SYNC] Invalid sync_interval %q, defaulting to 15m", s.cfg.Git.SyncInterval)
s.logger.Printf("[WARN] [sync] Invalid sync_interval %q, defaulting to 15m", s.cfg.Git.SyncInterval)
interval = 15 * time.Minute
}
s.logger.Printf("[SYNC] Starting catalog sync (repo: %s, interval: %s)", s.cfg.Git.RepoURL, interval)
s.logger.Printf("[INFO] [sync] Starting catalog sync (repo: %s, interval: %s)", s.cfg.Git.RepoURL, interval)
// Initial sync on startup
go func() {
result := s.doSync()
s.logger.Printf("[SYNC] Initial sync: %s", result.Message)
s.logger.Printf("[INFO] [sync] Initial sync: %s", result.Message)
}()
go func() {
@@ -101,11 +101,11 @@ func (s *Syncer) Start() {
for {
select {
case <-s.stopCh:
s.logger.Println("[SYNC] Sync loop stopped")
s.logger.Println("[INFO] [sync] Sync loop stopped")
return
case <-ticker.C:
result := s.doSync()
s.logger.Printf("[SYNC] Periodic sync: %s", result.Message)
s.logger.Printf("[INFO] [sync] Periodic sync: %s", result.Message)
}
}
}()
@@ -207,14 +207,14 @@ func (s *Syncer) doSync() SyncResult {
// Step 3: Trigger rescan if anything changed
if len(newApps) > 0 || len(updated) > 0 {
if err := s.rescanFn(); err != nil {
s.logger.Printf("[SYNC] Rescan after sync failed: %v", err)
s.logger.Printf("[WARN] [sync] Rescan after sync failed: %v", err)
}
}
// Step 4: Inject missing deploy fields for updated stacks
if len(updated) > 0 && s.postSyncHook != nil {
if s.isDebug() {
s.logger.Printf("[DEBUG] [SYNC] Post-sync hook: triggering missing field injection for %d stack(s): %v", len(updated), updated)
s.logger.Printf("[DEBUG] [sync] Post-sync hook: triggering missing field injection for %d stack(s): %v", len(updated), updated)
}
s.postSyncHook(updated)
}
@@ -252,12 +252,12 @@ func (s *Syncer) gitCloneOrPull() error {
gitDir := filepath.Join(s.cacheDir, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
// Clone
s.logger.Printf("[SYNC] Cloning %s → %s", s.cfg.Git.RepoURL, s.cacheDir)
s.logger.Printf("[INFO] [sync] Cloning %s → %s", s.cfg.Git.RepoURL, s.cacheDir)
args := []string{"clone", "--depth", "1", "--branch", s.cfg.Git.Branch}
repoURL := s.buildRepoURL()
args = append(args, repoURL, s.cacheDir)
if s.isDebug() {
s.logger.Printf("[DEBUG] [SYNC] git clone URL: %s, branch: %s, cacheDir: %s", maskRepoURL(repoURL), s.cfg.Git.Branch, s.cacheDir)
s.logger.Printf("[DEBUG] [sync] git clone URL: %s, branch: %s, cacheDir: %s", maskRepoURL(repoURL), s.cfg.Git.Branch, s.cacheDir)
}
return s.runGit(args...)
}
@@ -266,9 +266,9 @@ func (s *Syncer) gitCloneOrPull() error {
s.removeGitLockFiles()
// Pull
s.logger.Printf("[SYNC] Pulling latest from %s (branch: %s)", s.cfg.Git.RepoURL, s.cfg.Git.Branch)
s.logger.Printf("[INFO] [sync] Pulling latest from %s (branch: %s)", s.cfg.Git.RepoURL, s.cfg.Git.Branch)
if s.isDebug() {
s.logger.Printf("[DEBUG] [SYNC] git fetch --depth 1 origin %s in %s", s.cfg.Git.Branch, s.cacheDir)
s.logger.Printf("[DEBUG] [sync] git fetch --depth 1 origin %s in %s", s.cfg.Git.Branch, s.cacheDir)
}
if err := s.runGitInDir(s.cacheDir, "fetch", "--depth", "1", "origin", s.cfg.Git.Branch); err != nil {
return fmt.Errorf("git fetch: %w", err)
@@ -288,9 +288,9 @@ func (s *Syncer) removeGitLockFiles() {
for _, name := range lockFiles {
lockPath := filepath.Join(gitDir, name)
if _, err := os.Stat(lockPath); err == nil {
s.logger.Printf("[SYNC] Removing stale lock file: %s", lockPath)
s.logger.Printf("[WARN] [sync] Removing stale lock file: %s", lockPath)
if err := os.Remove(lockPath); err != nil {
s.logger.Printf("[SYNC] Failed to remove lock file %s: %v", lockPath, err)
s.logger.Printf("[WARN] [sync] Failed to remove lock file %s: %v", lockPath, err)
}
}
}
@@ -328,7 +328,7 @@ func (s *Syncer) copyTemplates() (newApps []string, updated []string, err error)
if _, err := os.Stat(dstDir); os.IsNotExist(err) {
isNew = true
if err := os.MkdirAll(dstDir, 0755); err != nil {
s.logger.Printf("[SYNC] Failed to create stack dir %s: %v", dstDir, err)
s.logger.Printf("[WARN] [sync] Failed to create stack dir %s: %v", dstDir, err)
continue
}
}
@@ -343,7 +343,7 @@ func (s *Syncer) copyTemplates() (newApps []string, updated []string, err error)
if _, err := os.Stat(src); os.IsNotExist(err) {
if s.isDebug() {
s.logger.Printf("[DEBUG] [SYNC] %s/%s: source not found, skipping", appName, filename)
s.logger.Printf("[DEBUG] [sync] %s/%s: source not found, skipping", appName, filename)
}
continue
}
@@ -355,12 +355,12 @@ func (s *Syncer) copyTemplates() (newApps []string, updated []string, err error)
}
if changed {
anyChanged = true
s.logger.Printf("[SYNC] Updated %s/%s", appName, filename)
s.logger.Printf("[INFO] [sync] Updated %s/%s", appName, filename)
if s.isDebug() {
s.logFileHashes(appName, filename, src, dst)
}
} else if s.isDebug() {
s.logger.Printf("[DEBUG] [SYNC] %s/%s: hash match, skipped", appName, filename)
s.logger.Printf("[DEBUG] [sync] %s/%s: hash match, skipped", appName, filename)
}
}
@@ -383,11 +383,11 @@ func (s *Syncer) logFileHashes(appName, filename, src, dst string) {
srcHash := sha256.Sum256(srcData)
dstData, err := os.ReadFile(dst)
if err != nil {
s.logger.Printf("[DEBUG] [SYNC] %s/%s: src=%s, dst=new file", appName, filename, hex.EncodeToString(srcHash[:8]))
s.logger.Printf("[DEBUG] [sync] %s/%s: src=%s, dst=new file", appName, filename, hex.EncodeToString(srcHash[:8]))
return
}
dstHash := sha256.Sum256(dstData)
s.logger.Printf("[DEBUG] [SYNC] %s/%s: src=%s, dst=%s (changed)", appName, filename, hex.EncodeToString(srcHash[:8]), hex.EncodeToString(dstHash[:8]))
s.logger.Printf("[DEBUG] [sync] %s/%s: src=%s, dst=%s (changed)", appName, filename, hex.EncodeToString(srcHash[:8]), hex.EncodeToString(dstHash[:8]))
}
// copyIfChanged copies src to dst only if the content differs.
@@ -430,7 +430,7 @@ func (s *Syncer) runGitInDir(dir string, args ...string) error {
cmd.Stdout = io.Discard
cmd.Stderr = &stderr
s.logger.Printf("[SYNC] Running: git %s", maskRepoURL(strings.Join(args, " ")))
s.logger.Printf("[DEBUG] [sync] Running: git %s", maskRepoURL(strings.Join(args, " ")))
if err := cmd.Run(); err != nil {
return fmt.Errorf("git %s: %w\nstderr: %s", strings.Join(args, " "), err, stderr.String())
-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" {