mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-27 21:36:21 +02:00
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
package audit
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
// PeerBackupPayload is the data sent to trusted peers for log backup.
|
|
type PeerBackupPayload struct {
|
|
PeerID string `json:"peer_id"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
LogHash string `json:"log_hash"` // SHA-256 of decisions.log
|
|
Entries int `json:"entries"` // Number of decision entries
|
|
Snapshot string `json:"snapshot"` // Last N entries for quick restore
|
|
}
|
|
|
|
// PeerBackupResult describes the outcome of a backup attempt.
|
|
type PeerBackupResult struct {
|
|
BackedUp bool `json:"backed_up"`
|
|
FilePath string `json:"file_path,omitempty"`
|
|
Size int64 `json:"size,omitempty"`
|
|
}
|
|
|
|
// CreateBackupSnapshot creates a local backup snapshot of decisions.log
|
|
// that can be sent to trusted peers via P2P transport.
|
|
// Saves to .rlm/decisions_backup.json for peer sync.
|
|
func CreateBackupSnapshot(rlmDir, peerID string, maxEntries int) (*PeerBackupPayload, error) {
|
|
logPath := filepath.Join(rlmDir, decisionsFileName)
|
|
data, err := os.ReadFile(logPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("backup: no decisions.log to back up")
|
|
}
|
|
return nil, fmt.Errorf("backup: read: %w", err)
|
|
}
|
|
|
|
lines := splitLines(string(data))
|
|
// Filter empty lines.
|
|
var entries []string
|
|
for _, l := range lines {
|
|
if l != "" {
|
|
entries = append(entries, l)
|
|
}
|
|
}
|
|
|
|
// Take last N entries for snapshot.
|
|
if maxEntries <= 0 {
|
|
maxEntries = 100
|
|
}
|
|
snapshot := entries
|
|
if len(entries) > maxEntries {
|
|
snapshot = entries[len(entries)-maxEntries:]
|
|
}
|
|
|
|
snapshotJSON, _ := json.Marshal(snapshot)
|
|
|
|
payload := &PeerBackupPayload{
|
|
PeerID: peerID,
|
|
Timestamp: time.Now(),
|
|
Entries: len(entries),
|
|
Snapshot: string(snapshotJSON),
|
|
}
|
|
|
|
// Save backup file locally.
|
|
backupPath := filepath.Join(rlmDir, "decisions_backup.json")
|
|
backupData, _ := json.MarshalIndent(payload, "", " ")
|
|
if err := os.WriteFile(backupPath, backupData, 0o644); err != nil {
|
|
return nil, fmt.Errorf("backup: write: %w", err)
|
|
}
|
|
|
|
return payload, nil
|
|
}
|
|
|
|
// RestoreFromBackup loads a backup snapshot (received from a peer).
|
|
func RestoreFromBackup(rlmDir string) (*PeerBackupPayload, error) {
|
|
backupPath := filepath.Join(rlmDir, "decisions_backup.json")
|
|
data, err := os.ReadFile(backupPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("restore: %w", err)
|
|
}
|
|
var payload PeerBackupPayload
|
|
if err := json.Unmarshal(data, &payload); err != nil {
|
|
return nil, fmt.Errorf("restore: parse: %w", err)
|
|
}
|
|
return &payload, nil
|
|
}
|