Files
gott/Endpoints/AuthEndpoint.cs
T
Meleki 8039b47dec Web-Suche (Agent-Toggle mit Versionswahl), Admin-only Backup, Haiku 4.5
- Agent: Web-Suche als Dropdown (Deaktiviert / 2025 / 2026) statt bool
- AnthropicService: web_search Tool dynamisch nach Version einfügen
- Stream: server_tool_use + web_search_tool_result Events verarbeiten
- Chat-Frontend: web_search_start/end Events mit Suchindikator
- Dashboard: Badge zeigt Web-Suche mit Jahresversion
- UserRecord: IsAdmin Feld, DB-Migration is_admin Spalte
- AuthMiddleware: IsAdmin aus DB laden und in Context setzen
- BackupEndpoint: Admin-Check (403 für Nicht-Admins)
- AuthEndpoint: isAdmin in Login/Me Response
- Frontend: Backup-Section nur für Admins sichtbar
- Haiku 4.5 als Model-Option (Agent-Dropdown + ChatEndpoint)
- TODO: Cronjobs, Haiku ergänzt
2026-03-02 14:05:59 +01:00

100 lines
3.1 KiB
C#

using System.Text.Json;
using Gott.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
namespace Gott.Endpoints;
public static class AuthEndpoint
{
public static void MapAuthEndpoints(this WebApplication app)
{
app.MapPost("/api/auth/login", HandleLogin);
app.MapPost("/api/auth/logout", HandleLogout);
app.MapGet("/api/auth/me", HandleMe);
}
private static async System.Threading.Tasks.Task HandleLogin(HttpContext ctx, DatabaseService db, AuthService auth)
{
var body = await JsonSerializer.DeserializeAsync<JsonElement>(ctx.Request.Body);
var username = body.TryGetProperty("username", out var u) ? u.GetString() : null;
var password = body.TryGetProperty("password", out var p) ? p.GetString() : null;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
ctx.Response.StatusCode = 400;
await ctx.Response.WriteAsJsonAsync(new { error = "Username and password required" });
return;
}
var user = await db.GetUserByUsername(username);
if (user == null || !auth.VerifyPassword(password, user.PasswordHash))
{
ctx.Response.StatusCode = 401;
await ctx.Response.WriteAsJsonAsync(new { error = "Invalid credentials" });
return;
}
var token = auth.CreateToken(user.UserId);
ctx.Response.Cookies.Append("gott_token", token, new CookieOptions
{
HttpOnly = true,
SameSite = SameSiteMode.Strict,
Path = "/",
MaxAge = System.TimeSpan.FromDays(365)
});
await ctx.Response.WriteAsJsonAsync(new
{
userId = user.UserId,
username = user.Username,
displayName = user.DisplayName,
isAdmin = user.IsAdmin
});
}
private static async System.Threading.Tasks.Task HandleLogout(HttpContext ctx, AuthService auth)
{
var token = ctx.Request.Cookies["gott_token"];
auth.RevokeToken(token);
ctx.Response.Cookies.Delete("gott_token", new CookieOptions
{
Path = "/"
});
await ctx.Response.WriteAsJsonAsync(new { ok = true });
}
private static async System.Threading.Tasks.Task HandleMe(HttpContext ctx, DatabaseService db, AuthService auth)
{
var token = ctx.Request.Cookies["gott_token"];
var userId = auth.ValidateToken(token);
if (userId == null)
{
ctx.Response.StatusCode = 401;
await ctx.Response.WriteAsJsonAsync(new { error = "Not authenticated" });
return;
}
var user = await db.GetUser(userId);
if (user == null)
{
ctx.Response.StatusCode = 401;
await ctx.Response.WriteAsJsonAsync(new { error = "User not found" });
return;
}
await ctx.Response.WriteAsJsonAsync(new
{
userId = user.UserId,
username = user.Username,
displayName = user.DisplayName,
isAdmin = user.IsAdmin
});
}
}