using Avalonia_Common.Core; using Avalonia_EFCore.Database; using Avalonia_EFCore.Models; using Avalonia_Services.Core; using Avalonia_Services.Services.AuthService; using Microsoft.EntityFrameworkCore; using System.Text.Json; namespace Avalonia_API.Authentication { public sealed class ApiAuthEndpointService( AppDataContext db, JwtTokenService jwtTokenService, RefreshTokenService refreshTokenService) : IApiAuthEndpointService { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true, }; public async Task LoginAsync(ServiceEndpointContext ctx) { var request = Deserialize(ctx.Body); if (string.IsNullOrWhiteSpace(request?.Account)) { ctx.StatusCode = 400; return ResponseHelper.Failure(400, "账号不能为空"); } var user = await db.Users.FirstOrDefaultAsync( x => x.Email == request.Account || x.Name == request.Account); if (user is null) { user = new UserEntity { Name = request.Account, Email = request.Account.Contains('@') ? request.Account : null, }; db.Users.Add(user); await db.SaveChangesAsync(); } var roles = NormalizeRoles(request.Roles); var accessToken = jwtTokenService.CreateAccessToken(user, roles); var refreshToken = await refreshTokenService.CreateAsync( user.Id, ctx.GetHeader("User-Agent"), GetRemoteIpAddress(ctx)); return ResponseHelper.Ok(new AuthTokenResponse( accessToken.Token, refreshToken.Token, accessToken.ExpiresAt, refreshToken.Entity.ExpiresAt, roles), "登录成功"); } public async Task RefreshAsync(ServiceEndpointContext ctx) { var request = Deserialize(ctx.Body); var rotated = await refreshTokenService.RotateAsync( request?.RefreshToken, ctx.GetHeader("User-Agent"), GetRemoteIpAddress(ctx)); if (rotated is null) { ctx.StatusCode = 401; return ResponseHelper.Failure(401, "刷新 token 无效或已过期"); } var user = await db.Users.FindAsync(rotated.Value.Entity.UserId); if (user is null) { ctx.StatusCode = 401; return ResponseHelper.Failure(401, "用户不存在"); } var roles = new[] { "Admin" }; var accessToken = jwtTokenService.CreateAccessToken(user, roles); return ResponseHelper.Ok(new AuthTokenResponse( accessToken.Token, rotated.Value.Token, accessToken.ExpiresAt, rotated.Value.Entity.ExpiresAt, roles), "刷新成功"); } public async Task LogoutAsync(ServiceEndpointContext ctx) { var request = Deserialize(ctx.Body); await refreshTokenService.RevokeAsync(request?.RefreshToken); return ResponseHelper.Succeed("退出成功"); } private static T? Deserialize(string? body) { return string.IsNullOrWhiteSpace(body) ? default : JsonSerializer.Deserialize(body, JsonOptions); } private static string? GetRemoteIpAddress(ServiceEndpointContext ctx) { return ctx.Items.TryGetValue("HttpContext", out var value) && value is HttpContext httpContext ? httpContext.Connection.RemoteIpAddress?.ToString() : null; } private static string[] NormalizeRoles(string[]? roles) { var normalized = roles? .Where(role => !string.IsNullOrWhiteSpace(role)) .Select(role => role.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); return normalized is { Length: > 0 } ? normalized : ["Admin"]; } } }