Historique des versions : snapshots horodatés, différences, restauration
- Avant d'écraser un schéma, le contenu précédent est copié dans DATA_DIR/.history/<chemin>/<date>.excalidraw, au plus une version par HISTORY_INTERVAL (10 min) ; rétention HISTORY_KEEP (50) et HISTORY_MAX_AGE. Pas de git : l'image reste distroless. - API : GET /api/history, GET /api/history/version, POST /api/history/snapshot, POST /api/history/restore (le contenu remplacé devient une version). - L'historique suit renommages et déplacements, part à la corbeille avec le schéma supprimé. - Dialogue Historique (barre du haut, menu de l'explorateur) : aperçu, différences colorées avec l'état actuel, restaurer, restaurer en copie, créer une version. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H6J7vKwXx7uopxKhWfFJpm
This commit is contained in:
1 parent
88f7750577
commit
9f9ec3b3a4
14 files changed
+1005
-7
No files matched your search
@@ -6,6 +6,7 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@@ -52,6 +53,9 @@ type Entry struct {
|
||||
type Store struct {
|
||||
root string
|
||||
|
||||
// History règle l'historique des versions (DefaultHistory par défaut).
|
||||
History HistoryPolicy
|
||||
|
||||
settingsMu sync.Mutex // sérialise les lectures-modifications de .settings.json
|
||||
}
|
||||
|
||||
@@ -69,7 +73,7 @@ func New(root string) (*Store, error) {
|
||||
if abs, err = filepath.EvalSymlinks(abs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Store{root: abs}, nil
|
||||
return &Store{root: abs, History: DefaultHistory}, nil
|
||||
}
|
||||
|
||||
// Clean normalise un chemin relatif : "/a/../b/" -> "b", "" -> "".
|
||||
@@ -269,6 +273,10 @@ func (s *Store) Write(rel string, data []byte, ifMatch string, create bool) (Ent
|
||||
return Entry{}, "", ErrConflict
|
||||
case err != nil && !errors.Is(err, fs.ErrNotExist):
|
||||
return Entry{}, "", err
|
||||
case err == nil && !bytes.Equal(cur, data):
|
||||
if fi, err := os.Stat(abs); err == nil {
|
||||
s.autoSnapshot(c, cur, fi.ModTime())
|
||||
}
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(abs), 0o755); err != nil {
|
||||
return Entry{}, "", err
|
||||
@@ -363,6 +371,7 @@ func (s *Store) Move(from, to string) (Entry, error) {
|
||||
return Entry{}, mapErr(err)
|
||||
}
|
||||
s.moveThumb(cf, ct, fi.IsDir())
|
||||
s.moveHistory(cf, ct)
|
||||
fi, err = os.Stat(at)
|
||||
if err != nil {
|
||||
return Entry{}, err
|
||||
@@ -463,6 +472,7 @@ func (s *Store) Delete(rel string, recursive bool) error {
|
||||
return err
|
||||
}
|
||||
s.removeThumb(c, fi.IsDir())
|
||||
s.trashHistory(c, trash)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user