using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using LehrerApp.Sync.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; namespace LehrerApp.Api; public static class Endpoints { // ── Auth ────────────────────────────────────────────────────────────────── public static void MapAuthEndpoints(this WebApplication app, string jwtSecret) { app.MapPost("/api/auth/login", (LoginRequest req) => { // TODO: Passwort-Hash gegen DB prüfen if (string.IsNullOrWhiteSpace(req.Username) || string.IsNullOrWhiteSpace(req.Password)) return Results.Unauthorized(); var token = GenerateJwt(req.Username, jwtSecret); return Results.Ok(new { token, userId = req.Username }); }); app.MapPost("/api/auth/register", (RegisterRequest req) => { // TODO: User anlegen, Passwort hashen (BCrypt) if (string.IsNullOrWhiteSpace(req.Username) || req.Password.Length < 12) return Results.BadRequest( "Passwort muss mindestens 12 Zeichen haben."); var token = GenerateJwt(req.Username, jwtSecret); return Results.Ok(new { token, userId = req.Username }); }); } // ── Sync ────────────────────────────────────────────────────────────────── public static void MapSyncEndpoints(this WebApplication app) { var sync = app.MapGroup("/api/sync").RequireAuthorization(); sync.MapPost("/push", ( [FromBody] List events, ClaimsPrincipal user, EventStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); var result = store.Push(userId, events); return Results.Ok(result); }); sync.MapGet("/pull", ( [FromQuery] long since, [FromQuery] string deviceId, ClaimsPrincipal user, EventStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); var result = store.Pull(userId, since, deviceId); return Results.Ok(result); }); sync.MapGet("/status", (ClaimsPrincipal user) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); return Results.Ok(new { userId, timestamp = DateTime.UtcNow }); }); } // ── Snapshot ────────────────────────────────────────────────────────────── public static void MapSnapshotEndpoints(this WebApplication app) { var snap = app.MapGroup("/api/snapshot").RequireAuthorization(); // Sender: Snapshot hochladen → Einmal-Code erhalten snap.MapPost("/upload", ( [FromBody] SnapshotUploadRequest request, ClaimsPrincipal user, SnapshotStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); if (string.IsNullOrWhiteSpace(request.EncryptedPayload)) return Results.BadRequest("Kein Payload."); var result = store.Store(userId, request); return Results.Ok(result); }); // Empfänger: Snapshot per Code abrufen (Einmal-Verwendung) snap.MapGet("/{code}", ( string code, ClaimsPrincipal user, SnapshotStore store) => { var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId is null) return Results.Unauthorized(); var result = store.Retrieve(userId, code); if (result is null) return Results.NotFound( "Snapshot nicht gefunden, abgelaufen oder bereits verwendet."); return Results.Ok(result); }); } // ── JWT ─────────────────────────────────────────────────────────────────── private static string GenerateJwt(string userId, string secret) { var key = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(secret)); var creds = new SigningCredentials( key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( claims: [ new Claim(ClaimTypes.NameIdentifier, userId), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), ], expires: DateTime.UtcNow.AddDays(30), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); } } public record LoginRequest(string Username, string Password); public record RegisterRequest(string Username, string Password, string DisplayName); // Ergänzung – wird unten in der bestehenden Datei angefügt // In Program.cs: app.MapReadableSnapshotEndpoints(); und app.MapPlainSyncEndpoints();