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
+25
-1
@@ -50,6 +50,13 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Historique des versions : HISTORY_KEEP versions par schéma (0 : désactivé),
|
||||
// au plus une toutes les HISTORY_INTERVAL, purgées après HISTORY_MAX_AGE (0 : jamais).
|
||||
if store.History, err = historyPolicy(); err != nil {
|
||||
slog.Error("historique", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
@@ -91,7 +98,8 @@ func main() {
|
||||
}
|
||||
|
||||
go func() {
|
||||
slog.Info("démarrage", "addr", addr, "data", dataDir)
|
||||
slog.Info("démarrage", "addr", addr, "data", dataDir,
|
||||
"history_keep", store.History.Keep, "history_interval", store.History.Interval.String())
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
slog.Error("serveur", "err", err)
|
||||
os.Exit(1)
|
||||
@@ -104,6 +112,22 @@ func main() {
|
||||
slog.Info("arrêt")
|
||||
}
|
||||
|
||||
func historyPolicy() (storage.HistoryPolicy, error) {
|
||||
p := storage.DefaultHistory
|
||||
keep, err := strconv.Atoi(env("HISTORY_KEEP", strconv.Itoa(p.Keep)))
|
||||
if err != nil || keep < 0 {
|
||||
return p, errors.New("HISTORY_KEEP invalide (entier >= 0)")
|
||||
}
|
||||
p.Keep = keep
|
||||
if p.Interval, err = time.ParseDuration(env("HISTORY_INTERVAL", p.Interval.String())); err != nil || p.Interval < 0 {
|
||||
return p, errors.New("HISTORY_INTERVAL invalide (ex. 10m, 0 : à chaque enregistrement)")
|
||||
}
|
||||
if p.MaxAge, err = time.ParseDuration(env("HISTORY_MAX_AGE", "0")); err != nil || p.MaxAge < 0 {
|
||||
return p, errors.New("HISTORY_MAX_AGE invalide (ex. 2160h, 0 : illimité)")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func healthcheck(addr string) int {
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user