using System.Security.Claims; using LehrerApp.Core.Models; using LehrerApp.Sync.Models; using Microsoft.AspNetCore.Mvc; namespace LehrerApp.Api; public static class ReadableSnapshotEndpoints { // ── Readable Snapshot ───────────────────────────────────────────────────── public static void MapReadableSnapshotEndpoints(this WebApplication app) { var snap = app.MapGroup("/api/snapshot/readable") .RequireAuthorization(); // Desktop → Server: aktuellen Snapshot hochladen snap.MapPost("/", ( [FromBody] ReadableSnapshot snapshot, ClaimsPrincipal user, ReadableSnapshotStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); snapshot.ExportedAt = DateTime.UtcNow; store.Store(userId, snapshot); return Results.Ok(new { exportedAt = snapshot.ExportedAt, studentCount = snapshot.Meta.StudentCount, groupCount = snapshot.Meta.GroupCount, }); }); // WebApp → Server: Snapshot laden snap.MapGet("/", ( ClaimsPrincipal user, ReadableSnapshotStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); var snapshot = store.Load(userId); if (snapshot is null) return Results.NotFound( "Kein Snapshot vorhanden. " + "Bitte zuerst den Desktop-Client mit dem Server verbinden."); return Results.Ok(snapshot); }); // Nur Metadaten – für Freshness-Check der WebApp snap.MapGet("/meta", ( ClaimsPrincipal user, ReadableSnapshotStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); var meta = store.LoadMeta(userId); if (meta is null) return Results.NotFound(); return Results.Ok(meta); }); } // ── Plain Sync (WebApp → EventStore) ───────────────────────────────────── public static void MapPlainSyncEndpoints(this WebApplication app) { var sync = app.MapGroup("/api/sync/plain") .RequireAuthorization(); // WebApp schreibt Events (Klartext) – werden beim Desktop-Pull abgeholt sync.MapPost("/push", ( [FromBody] List events, ClaimsPrincipal user, PlainEventStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); // Nur erlaubte EntityTypes für WebApp-Schreibzugriff var allowed = new HashSet { "Grade", "ExamResult", "WorkTask", "Lesson" }; var rejected = events .Where(e => !allowed.Contains(e.EntityType)) .Select(e => e.EventId) .ToList(); var permitted = events .Where(e => allowed.Contains(e.EntityType)) .ToList(); PlainPushResponse result; if (permitted.Count > 0) result = store.Push(userId, permitted); else result = new PlainPushResponse { Success = true }; // Abgelehnte Events mit Grund zurückmelden result = result with { RejectedEventIds = [.. result.RejectedEventIds, .. rejected], }; return Results.Ok(result); }); } }