116 lines
4.0 KiB
C#
116 lines
4.0 KiB
C#
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<PlainSyncEvent> 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<string>
|
||
{
|
||
"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);
|
||
});
|
||
}
|
||
}
|