Files
2026-03-29 23:47:31 +02:00

148 lines
5.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<SyncEvent> 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();