82 lines
2.8 KiB
Go
82 lines
2.8 KiB
Go
package settings
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// KeptCopy is one second-drive copy whose source is gone (D25, operator ruling 2026-10-21). The record is what the
|
|
// backups page lists and what „Eltávolítás" removes — by Path, identity-checked against Dev/Ino, never by size or a
|
|
// name pattern (07 §8.2 as amended by D25).
|
|
type KeptCopy struct {
|
|
ID string `json:"id"` // stable: sha256 of Path, first 16 hex
|
|
Kind string `json:"kind"` // "share" | "app"
|
|
Source string `json:"source"` // the share's name, or "<app>: <folder>"
|
|
Drive string `json:"drive"` // the SOURCE drive's label (or its path when the drive is no longer registered)
|
|
Reason string `json:"reason"` // "drive_away" | "drive_gone" | "folder_missing"
|
|
DestBase string `json:"dest_base"` // the copy tree it lives in; Path must stay inside it
|
|
Path string `json:"path"` // the copy's absolute directory
|
|
Dev uint64 `json:"dev"` // identity at record time
|
|
Ino uint64 `json:"ino"` // identity at record time
|
|
SizeBytes int64 `json:"size_bytes"` // at the latest run
|
|
Since string `json:"since"` // RFC3339, first time it was kept
|
|
}
|
|
|
|
// KeptCopyID is the record's id for a copy path.
|
|
func KeptCopyID(path string) string {
|
|
h := sha256.Sum256([]byte(path))
|
|
return hex.EncodeToString(h[:8])
|
|
}
|
|
|
|
// GetKeptCopies returns a copy of the kept-copy records.
|
|
func (s *Settings) GetKeptCopies() []KeptCopy {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return append([]KeptCopy(nil), s.KeptCopies...)
|
|
}
|
|
|
|
// UpsertKeptCopy records a kept copy (or refreshes its size and identity), keeping the first Since.
|
|
func (s *Settings) UpsertKeptCopy(k KeptCopy) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if k.Path == "" || k.DestBase == "" {
|
|
return fmt.Errorf("kept copy: empty path or dest base")
|
|
}
|
|
k.ID = KeptCopyID(k.Path)
|
|
for i := range s.KeptCopies {
|
|
if s.KeptCopies[i].ID == k.ID {
|
|
since := s.KeptCopies[i].Since
|
|
k.Since = since
|
|
if s.KeptCopies[i] == k {
|
|
return nil // nothing changed — no write
|
|
}
|
|
s.KeptCopies[i] = k
|
|
return s.save()
|
|
}
|
|
}
|
|
if k.Since == "" {
|
|
k.Since = time.Now().UTC().Format(time.RFC3339)
|
|
}
|
|
s.KeptCopies = append(s.KeptCopies, k)
|
|
if s.log != nil {
|
|
s.log.Printf("[INFO] [settings] kept copy recorded: kind=%s source=%q reason=%s path=%s", k.Kind, k.Source, k.Reason, k.Path)
|
|
}
|
|
return s.save()
|
|
}
|
|
|
|
// DropKeptCopy forgets the record of a copy path (the source came back, or the household removed the copy). The
|
|
// copy's data is not touched here.
|
|
func (s *Settings) DropKeptCopy(id string) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for i := range s.KeptCopies {
|
|
if s.KeptCopies[i].ID == id {
|
|
s.KeptCopies = append(s.KeptCopies[:i], s.KeptCopies[i+1:]...)
|
|
return s.save()
|
|
}
|
|
}
|
|
return nil
|
|
}
|