using LiteDB; using LehrerApp.Sync.Models; namespace LehrerApp.Sync; /// /// Lokale Event-Queue in LiteDB. Puffert Events bis sie /// erfolgreich zum Server gepusht wurden. /// public class EventQueue : IDisposable { private readonly LiteDatabase _db; private readonly ILiteCollection _queue; private readonly ILiteCollection _meta; private readonly ILiteCollection _conflicts; private long _currentSeq; public EventQueue(string path) { _db = new LiteDatabase(path); _queue = _db.GetCollection("queue"); _meta = _db.GetCollection("meta"); _conflicts = _db.GetCollection("conflicts"); _queue.EnsureIndex(x => x.SequenceNr); _currentSeq = _meta.FindById("seq")?.Value ?? 0; } public SyncEvent Enqueue(string deviceId, DeviceType deviceType, string entityType, string entityId, string operation, string payload) { var evt = new SyncEvent { DeviceId = deviceId, DeviceType = deviceType, Timestamp = DateTime.UtcNow, SequenceNr = ++_currentSeq, EntityType = entityType, EntityId = entityId, Operation = operation, Payload = payload, }; _queue.Insert(evt); _meta.Upsert(new SyncMeta { Id = "seq", Value = _currentSeq }); return evt; } public List GetPending(int max = 200) => _queue.Find(Query.All(nameof(SyncEvent.SequenceNr))).Take(max).ToList(); public int PendingCount() => _queue.Count(); public void Acknowledge(IEnumerable ids) { foreach (var id in ids) _queue.Delete(id); } public long GetLastServerSeq() => _meta.FindById("serverSeq")?.Value ?? 0; public void SetLastServerSeq(long nr) => _meta.Upsert(new SyncMeta { Id = "serverSeq", Value = nr }); public DateTime? GetLastSyncAt() => _meta.FindById("lastSync")?.Timestamp; public void SetLastSyncAt(DateTime dt) => _meta.Upsert(new SyncMeta { Id = "lastSync", Value = 0, Timestamp = dt }); public void AddConflict(ConflictEntry c) => _conflicts.Insert(c); public List GetUnreviewed() => _conflicts.Find(c => !c.Reviewed).ToList(); public int ConflictCount() => _conflicts.Count(c => !c.Reviewed); public void Dispose() => _db.Dispose(); } public class ConflictEntry { public Guid Id { get; init; } = Guid.NewGuid(); public DateTime DetectedAt { get; init; } = DateTime.UtcNow; public SyncEvent LocalEvent { get; init; } = null!; public SyncEvent RemoteEvent { get; init; } = null!; public string Resolution { get; init; } = ""; public bool Reviewed { get; set; } } internal class SyncMeta { public string Id { get; set; } = ""; public long Value { get; set; } public DateTime? Timestamp { get; set; } }