using LiteDB; namespace LehrerApp.Sync.Models; // ── Events ──────────────────────────────────────────────────────────────────── /// Desktop-Event: Payload ist AES-256-GCM verschlüsselt. public class SyncEvent { // LiteDB erkennt nur eine Property namens "Id" automatisch als Primärschlüssel. // Ohne [BsonId] würde ein eigener, von EventId unabhängiger _id vergeben, wodurch // EventQueue.Acknowledge()/_queue.Delete(EventId) niemals etwas löschen würde. [BsonId] public Guid EventId { get; init; } = Guid.NewGuid(); public string DeviceId { get; init; } = ""; public DeviceType DeviceType { get; init; } public DateTime Timestamp { get; init; } = DateTime.UtcNow; public long SequenceNr { get; init; } public string EntityType { get; init; } = ""; public string EntityId { get; init; } = ""; public string Operation { get; init; } = ""; /// Verschlüsselt (Desktop) oder Klartext (Companion/WebApp). public string Payload { get; init; } = ""; /// /// ServerSeq, auf der die lokale Änderung aufbaut (null = Entität wurde hier noch nie /// synchronisiert, z.B. Neuanlage). Wird von SyncEngine.PushAsync erst unmittelbar vor dem /// Senden aus der lokalen Versionsverfolgung (EventQueue) gesetzt, nicht beim Einreihen — /// so verwendet ein zweiter Push desselben Ereignisses (falls der erste abgelehnt wurde) /// automatisch den inzwischen aktualisierten Stand. /// public long? BasedOnServerSeq { get; set; } } /// WebApp/Companion-Event: Payload ist Klartext-JSON. public class PlainSyncEvent { public Guid EventId { get; init; } = Guid.NewGuid(); public string DeviceId { get; init; } = ""; public DeviceType DeviceType { get; init; } = DeviceType.Companion; public DateTime Timestamp { get; init; } = DateTime.UtcNow; public string EntityType { get; init; } = ""; public string EntityId { get; init; } = ""; public string Operation { get; init; } = ""; public string Payload { get; init; } = ""; } // ── Sync API Responses ──────────────────────────────────────────────────────── public class PushResponse { public bool Success { get; init; } public long ServerSequenceNr { get; init; } public List ConflictingEventIds { get; init; } = []; /// Je akzeptiertem Ereignis die tatsächlich vergebene ServerSeq — der Client braucht /// das, um seine lokale Versionsverfolgung je Entität (EventQueue) auf den neuen Stand zu /// bringen. public Dictionary AssignedServerSeqs { get; init; } = []; } public class PullResponse { public List Events { get; init; } = []; public long ServerSequenceNr { get; init; } } public class PlainPushResponse { public bool Success { get; init; } public long ServerSequenceNr { get; init; } public List RejectedEventIds { get; init; } = []; } // ── Snapshot Modelle ────────────────────────────────────────────────────────── /// /// Upload-Request für Device-Pairing. /// EncryptedPayload: LiteDB verschlüsselt mit Sync-Key. /// EncryptedSyncKey: Sync-Key verschlüsselt mit PBKDF2(Code). /// → Empfänger braucht nur den Code um beides zu entschlüsseln. /// public class SnapshotUploadRequest { public string EncryptedPayload { get; init; } = ""; public string EncryptedSyncKey { get; init; } = ""; public DeviceType DeviceType { get; init; } /// Gesetzt beim zweiten Upload-Schritt (Schlüssel nachreichen, siehe SnapshotService. /// CreateAndUploadAsync) — muss dem im ersten Schritt vom Server vergebenen Code entsprechen, /// damit derselbe Eintrag aktualisiert statt ein neuer (mit neuem Code) angelegt wird. public string? Code { get; init; } } public class SnapshotUploadResponse { /// Format: WORT-ZZ-WORT, z.B. "TIGER-42-BLAU". 24h gültig, einmalig. public string Code { get; init; } = ""; public DateTime ExpiresAt { get; init; } } public class SnapshotDownloadResponse { public string EncryptedPayload { get; init; } = ""; public string EncryptedSyncKey { get; init; } = ""; public DateTime CreatedAt { get; init; } public DeviceType SourceDeviceType { get; init; } } // ── Status ──────────────────────────────────────────────────────────────────── public class SyncStatus { public SyncState State { get; set; } = SyncState.Idle; public DateTime? LastSyncAt { get; set; } public int PendingEvents { get; set; } public int ConflictCount { get; set; } public string? ErrorMessage { get; set; } } // ── Enums ───────────────────────────────────────────────────────────────────── public enum DeviceType { Desktop, Companion } public enum SyncState { Idle, Syncing, Error, Offline, IncompatibleVersion }