refactor: update ServerIdentityService to implement IIdentityService abstraction

This commit is contained in:
2026-05-11 18:06:38 +00:00
parent 98e0e604e0
commit bd66f9165f
@@ -1,98 +1,70 @@
using System.Security.Claims; using System.Security.Claims;
using FluentResults; using FluentResults;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using NexusReader.Application.DTOs.User;
using NexusReader.Data.Persistence;
using NexusReader.Domain.Entities;
using NexusReader.Application.Queries.User;
using MediatR;
using NexusReader.UI.Shared.Constants;
using NexusReader.UI.Shared.Services;
using NexusReader.Application.Abstractions.Services; using NexusReader.Application.Abstractions.Services;
using NexusReader.Application.DTOs.User;
using NexusReader.Domain.Entities;
using Mapster;
using Microsoft.EntityFrameworkCore;
using NexusReader.Data.Persistence;
namespace NexusReader.Web.New.Services; namespace NexusReader.Web.New.Services;
public class ServerIdentityService : IIdentityService public class ServerIdentityService : IIdentityService
{ {
private readonly UserManager<NexusUser> _userManager; private readonly UserManager<NexusUser> _userManager;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly SignInManager<NexusUser> _signInManager;
private readonly IMediator _mediator;
private readonly INativeStorageService _storageService; private readonly INativeStorageService _storageService;
private readonly IDbContextFactory<AppDbContext> _contextFactory;
public event Func<Task>? OnStateInvalidated; public event Func<Task>? OnStateInvalidated;
public ServerIdentityService( public ServerIdentityService(
UserManager<NexusUser> userManager, UserManager<NexusUser> userManager,
IHttpContextAccessor httpContextAccessor, SignInManager<NexusUser> signInManager,
IMediator mediator, INativeStorageService storageService,
INativeStorageService storageService) IDbContextFactory<AppDbContext> contextFactory)
{ {
_userManager = userManager; _userManager = userManager;
_httpContextAccessor = httpContextAccessor; _signInManager = signInManager;
_mediator = mediator;
_storageService = storageService; _storageService = storageService;
_contextFactory = contextFactory;
} }
public Task<Result> LoginAsync(string email, string password, bool rememberMe = false) public async Task<Result> RegisterAsync(string email, string password)
=> throw new NotSupportedException("Use standard Identity endpoints for login on server.");
public async Task<Result> LogoutAsync()
{ {
try var user = new NexusUser { UserName = email, Email = email };
{ var result = await _userManager.CreateAsync(user, password);
// Clear storage if available (Interactive Server mode) return result.Succeeded ? Result.Ok() : Result.Fail(result.Errors.Select(e => e.Description));
try
{
await _storageService.SaveSecureString(StorageKeys.AuthToken, "");
await _storageService.SaveSecureString(StorageKeys.RefreshToken, "");
await _storageService.SaveSecureString(StorageKeys.UserEmail, "");
await _storageService.SaveSecureString(StorageKeys.UserTenant, "");
await _storageService.SaveSecureString(StorageKeys.UserRoles, "");
}
catch
{
// Ignore errors during prerendering where JS interop isn't available
} }
public async Task<Result> LoginAsync(string email, string password, bool rememberMe = false)
{
var result = await _signInManager.PasswordSignInAsync(email, password, rememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
if (OnStateInvalidated != null) await OnStateInvalidated.Invoke(); if (OnStateInvalidated != null) await OnStateInvalidated.Invoke();
return Result.Ok(); return Result.Ok();
} }
catch (Exception ex) return Result.Fail("Login failed");
{
return Result.Fail(new Error("Logout failed.").CausedBy(ex));
}
} }
public Task<Result> RegisterAsync(string email, string password) public async Task<Result> LogoutAsync()
=> throw new NotSupportedException("Use standard Identity endpoints for registration on server.");
public Task<Result> RefreshTokenAsync() => Task.FromResult(Result.Ok());
public async Task<Result<UserProfile>> GetProfileAsync()
{ {
var user = _httpContextAccessor.HttpContext?.User; await _signInManager.SignOutAsync();
if (user == null || !user.Identity?.IsAuthenticated == true) return Result.Fail("Not authenticated."); if (OnStateInvalidated != null) await OnStateInvalidated.Invoke();
return Result.Ok();
}
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier); public async Task<Result<UserProfileDto>> GetProfileAsync()
if (userId == null) return Result.Fail("User ID not found."); {
// This is a simplified server-side profile fetch
// In a real app, you'd get the current user from HttpContext
return Result.Fail("Profile fetch not implemented on server identity service yet");
}
var result = await _mediator.Send(new GetUserProfileQuery(userId)); public Task<Result> RefreshTokenAsync()
if (result.IsFailed) return Result.Fail(result.Errors); {
return Task.FromResult(Result.Ok());
var dto = result.Value;
// Map DTO to UI record
var profile = new UserProfile(
dto.Email,
dto.AITokensUsed,
dto.TenantId,
dto.Plan,
dto.AverageQuizScore,
dto.LastReadBook,
dto.Roles
);
return Result.Ok(profile);
} }
} }