package logstorage import ( "encoding/json" "errors" "os" "path/filepath" "tera-clone/services/tera-api/internal/storage/atomicfile" ) type systemClearState struct { Version int `json:"version"` Revision uint64 `json:"revision"` NextSequence uint64 `json:"next_sequence"` AuditNextSequence uint64 `json:"audit_next_sequence,omitempty"` } func (s *Store) systemClearPath() string { return filepath.Join(s.root, "system", "cleared-system-v1.json") } func (s *Store) loadSystemClearLocked() error { info, err := os.Lstat(s.systemClearPath()) if os.IsNotExist(err) { return nil } if err != nil { return err } if !info.Mode().IsRegular() || info.Size() > 1024 { return errors.New("invalid system log deletion checkpoint") } data, err := os.ReadFile(s.systemClearPath()) if err != nil { return err } var state systemClearState if err := json.Unmarshal(data, &state); err != nil { return err } if state.Version != 1 || state.NextSequence == 0 || state.Revision == 0 { return errors.New("invalid system log deletion checkpoint") } s.systemClear = state return nil } func (s *Store) systemRecordCleared(event Envelope) bool { return event.Class == ClassSystem && event.Sequence < s.systemClear.NextSequence || event.Class == ClassAudit && event.Sequence < s.systemClear.AuditNextSequence } func (s *Store) recordCleared(event Envelope) bool { return s.portRecordCleared(event) || s.systemRecordCleared(event) || s.linkRecordCleared(event) } func (s *Store) SystemClearRevision() uint64 { s.mu.Lock() defer s.mu.Unlock() return s.systemClear.Revision } // ClearSystem clears system and audit records. Port and link records are preserved. func (s *Store) ClearSystem() (int, error) { s.mu.Lock() defer s.mu.Unlock() deleted := 0 if err := s.walkClassLocked(ClassSystem, false, func(Envelope) error { deleted++; return nil }); err != nil { return 0, err } if err := s.walkClassLocked(ClassAudit, false, func(Envelope) error { deleted++; return nil }); err != nil { return 0, err } next := systemClearState{Version: 1, Revision: s.systemClear.Revision + 1, NextSequence: s.classes[ClassSystem].NextSequence, AuditNextSequence: s.classes[ClassAudit].NextSequence} data, err := json.Marshal(next) if err != nil { return 0, err } if err := atomicfile.Write(s.systemClearPath(), append(data, '\n'), 0o600); err != nil { _ = s.loadSystemClearLocked() return 0, err } s.systemClear = next if err := s.compactClearedClassLocked(ClassSystem, s.systemRecordCleared); err != nil { return deleted, err } return deleted, s.compactClearedClassLocked(ClassAudit, s.systemRecordCleared) }