Initial
This commit is contained in:
30
LehrerApp.Sync/Models/PlainSyncModels.cs
Normal file
30
LehrerApp.Sync/Models/PlainSyncModels.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace LehrerApp.Sync.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Event das die WebApp oder Companion-App erzeugt.
|
||||
/// Payload ist NICHT verschlüsselt – JWT schützt den Transport.
|
||||
/// Der Desktop-Client erkennt PlainEvents am DeviceType
|
||||
/// und wendet sie ohne Entschlüsselung an.
|
||||
/// </summary>
|
||||
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; } = ""; // "Grade", "Exam", ...
|
||||
public string EntityId { get; init; } = "";
|
||||
public string Operation { get; init; } = ""; // "Upsert", "Delete"
|
||||
public string Payload { get; init; } = ""; // JSON, Klartext
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Antwort auf einen Plain-Push.
|
||||
/// </summary>
|
||||
public class PlainPushResponse
|
||||
{
|
||||
public bool Success { get; init; }
|
||||
public long ServerSequenceNr { get; init; }
|
||||
public List<Guid> RejectedEventIds { get; init; } = [];
|
||||
public string? ErrorMessage { get; init; }
|
||||
}
|
||||
61
LehrerApp.Sync/Models/SnapshotModels.cs
Normal file
61
LehrerApp.Sync/Models/SnapshotModels.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
namespace LehrerApp.Sync.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Anfrage zum Upload eines initialen Snapshots.
|
||||
/// Enthält sowohl den verschlüsselten DB-Snapshot
|
||||
/// als auch den mit dem Code verschlüsselten Sync-Schlüssel.
|
||||
/// </summary>
|
||||
public class SnapshotUploadRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// AES-256-GCM verschlüsselter Dump der lokalen LiteDB.
|
||||
/// Verschlüsselt mit dem Sync-Schlüssel des Nutzers.
|
||||
/// Der Server versteht den Inhalt nicht.
|
||||
/// </summary>
|
||||
public string EncryptedPayload { get; init; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Der Sync-Schlüssel, verschlüsselt mit dem aus dem
|
||||
/// Einmal-Code abgeleiteten Key (PBKDF2).
|
||||
/// Ermöglicht dem Empfänger den Schlüssel ohne Vorabübertragung
|
||||
/// zu rekonstruieren – nur der Code wird benötigt.
|
||||
/// </summary>
|
||||
public string EncryptedSyncKey { get; init; } = "";
|
||||
|
||||
public DeviceType DeviceType { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Antwort nach erfolgreichem Snapshot-Upload.
|
||||
/// </summary>
|
||||
public class SnapshotUploadResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Menschenlesbarer Einmal-Code.
|
||||
/// Format: WORT-ZZ-WORT, z.B. "TIGER-42-BLAU"
|
||||
/// Dient gleichzeitig zum Abrufen UND zum Entschlüsseln des Sync-Schlüssels.
|
||||
/// </summary>
|
||||
public string Code { get; init; } = "";
|
||||
|
||||
public DateTime ExpiresAt { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Antwort beim Abrufen eines Snapshots per Code.
|
||||
/// Nach dem ersten Abruf wird der Snapshot vom Server gelöscht.
|
||||
/// </summary>
|
||||
public class SnapshotDownloadResponse
|
||||
{
|
||||
/// <summary>Der verschlüsselte DB-Snapshot.</summary>
|
||||
public string EncryptedPayload { get; init; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Der mit dem Code verschlüsselte Sync-Schlüssel.
|
||||
/// Empfänger: Code → PBKDF2 → Code-Key → EncryptedSyncKey entschlüsseln
|
||||
/// → Sync-Key → EncryptedPayload entschlüsseln.
|
||||
/// </summary>
|
||||
public string EncryptedSyncKey { get; init; } = "";
|
||||
|
||||
public DateTime CreatedAt { get; init; }
|
||||
public DeviceType SourceDeviceType { get; init; }
|
||||
}
|
||||
66
LehrerApp.Sync/Models/SyncModels.cs
Normal file
66
LehrerApp.Sync/Models/SyncModels.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
namespace LehrerApp.Sync.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Ein einzelnes Ereignis im Event-Log.
|
||||
/// Wird verschlüsselt zum Server übertragen – der Server versteht den Payload nicht.
|
||||
/// </summary>
|
||||
public class SyncEvent
|
||||
{
|
||||
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; } // monoton steigend pro Gerät
|
||||
public string EntityType { get; init; } = ""; // "Student", "Exam", "Grade" ...
|
||||
public string EntityId { get; init; } = "";
|
||||
public string Operation { get; init; } = ""; // "Create", "Update", "Delete"
|
||||
public string Payload { get; init; } = ""; // JSON, AES-256 verschlüsselt
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eintrag im lokalen Konflikt-Log – für UI-Anzeige.
|
||||
/// </summary>
|
||||
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; } = ""; // "LocalWon", "RemoteWon", "Pending"
|
||||
public bool Reviewed { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Antwort des Servers auf einen Pull-Request.
|
||||
/// </summary>
|
||||
public class PullResponse
|
||||
{
|
||||
public List<SyncEvent> Events { get; init; } = [];
|
||||
public long ServerSequenceNr { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Antwort des Servers auf einen Push-Request.
|
||||
/// </summary>
|
||||
public class PushResponse
|
||||
{
|
||||
public bool Success { get; init; }
|
||||
public long ServerSequenceNr { get; init; }
|
||||
public List<Guid> ConflictingEventIds { get; init; } = [];
|
||||
}
|
||||
|
||||
public enum DeviceType { Desktop, Companion }
|
||||
|
||||
/// <summary>
|
||||
/// Sync-Status für UI-Anzeige.
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
|
||||
public enum SyncState { Idle, Syncing, Error, Offline }
|
||||
Reference in New Issue
Block a user