using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Gott.Extensions; using Gott.Models; using Gott.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; namespace Gott.Endpoints; public static class SharedEndpoint { public static void MapSharedEndpoints(this IEndpointRouteBuilder app) { // Authentifizierte Endpunkte app.MapPost("/api/messages/{messageId}/share", HandleShareMessage); app.MapGet("/api/shared", HandleListShares); app.MapDelete("/api/shared/{token}", HandleDeleteShare); // Öffentlicher Endpunkt (Auth-Ausnahme in Middleware) app.MapGet("/api/shared/{token}", HandleGetShared); } private static async Task HandleShareMessage(HttpContext ctx, DatabaseService db) { var userId = ctx.GetUserId(); var messageIdStr = ctx.Request.RouteValues["messageId"]?.ToString(); if (!long.TryParse(messageIdStr, out var messageId)) { ctx.Response.StatusCode = 400; await ctx.Response.WriteAsJsonAsync(new { error = "Invalid messageId" }); return; } // Body lesen (optional: includeContext) bool includeContext = false; try { var body = await JsonSerializer.DeserializeAsync(ctx.Request.Body); if (body.TryGetProperty("includeContext", out var ic)) includeContext = ic.GetBoolean(); } catch { /* kein Body oder ungültig – defaults verwenden */ } // Nachricht existiert und gehört dem User? var message = await db.GetMessage(messageId); if (message == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Message not found" }); return; } // Chain holen → Session prüfen var chain = await db.GetChain(message.ChainId); if (chain == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Chain not found" }); return; } var session = await db.GetSession(chain.SessionId); if (session == null || session.UserId != userId) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Not authorized" }); return; } var shared = await db.CreateSharedMessage(messageId, userId, includeContext); await ctx.Response.WriteAsJsonAsync(new { token = shared.ShareToken }); } private static async Task HandleGetShared(HttpContext ctx, DatabaseService db) { var token = ctx.Request.RouteValues["token"]?.ToString(); if (string.IsNullOrEmpty(token)) { ctx.Response.StatusCode = 400; return; } var shared = await db.GetSharedMessage(token); if (shared == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Share not found" }); return; } // Ablaufdatum prüfen if (!string.IsNullOrEmpty(shared.ExpiresAt) && DateTime.TryParse(shared.ExpiresAt, out var expires) && expires < DateTime.UtcNow) { ctx.Response.StatusCode = 410; await ctx.Response.WriteAsJsonAsync(new { error = "Share expired" }); return; } var message = await db.GetMessage(shared.MessageId); if (message == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Message not found" }); return; } // Chain-Info holen var chain = await db.GetChain(message.ChainId); if (shared.IncludeContext) { // Alle Messages der Chain bis zu dieser Nachricht laden var contextMessages = await db.GetChainMessagesUpTo(message.ChainId, shared.MessageId); // Nur text-Messages zurückgeben (keine tool_use/tool_result) var textMessages = contextMessages .Where(m => m.Type == "text" && m.DeletedOn == null) .Select(m => new { m.Id, m.Role, m.Content, m.CreatedAt }) .ToList(); await ctx.Response.WriteAsJsonAsync(new { mode = "context", messages = textMessages, model = chain?.Model, createdAt = shared.CreatedAt }); } else { // Einzelne Nachricht await ctx.Response.WriteAsJsonAsync(new { mode = "single", message = new { message.Id, message.Role, message.Content, message.CreatedAt }, model = chain?.Model, createdAt = shared.CreatedAt }); } } private static async Task HandleListShares(HttpContext ctx, DatabaseService db) { var userId = ctx.GetUserId(); var shares = await db.GetSharedMessagesByUser(userId); await ctx.Response.WriteAsJsonAsync(shares); } private static async Task HandleDeleteShare(HttpContext ctx, DatabaseService db) { var userId = ctx.GetUserId(); var token = ctx.Request.RouteValues["token"]?.ToString(); if (string.IsNullOrEmpty(token)) { ctx.Response.StatusCode = 400; return; } await db.DeleteSharedMessage(token, userId); ctx.Response.StatusCode = 204; } }