feat: introduce IConceptsMapService abstraction with server and WASM implementations and update knowledge processing flow
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
using FluentResults;
|
||||||
|
using NexusReader.Application.Queries.Concepts;
|
||||||
|
|
||||||
|
namespace NexusReader.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IConceptsMapService
|
||||||
|
{
|
||||||
|
Task<Result<BookConceptsMapResultDto>> GetConceptsMapAsync(Guid bookId);
|
||||||
|
}
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
_isInteractive = true;
|
_isInteractive = true;
|
||||||
if (ViewModel != null)
|
if (ViewModel != null)
|
||||||
{
|
{
|
||||||
await Coordinator.ProcessFullPageAsync(GetFullPageContent());
|
await Coordinator.ProcessFullPageAsync(GetFullPageContent(), ebookId: ViewModel.EbookId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
|
|
||||||
if (_isInteractive)
|
if (_isInteractive)
|
||||||
{
|
{
|
||||||
await Coordinator.ProcessFullPageAsync(GetFullPageContent());
|
await Coordinator.ProcessFullPageAsync(GetFullPageContent(), ebookId: ViewModel.EbookId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
@using NexusReader.Application.Queries.Graph
|
@using NexusReader.Application.Queries.Graph
|
||||||
@using NexusReader.Application.Queries.Concepts
|
@using NexusReader.Application.Queries.Concepts
|
||||||
@using System.Net.Http.Json
|
@using System.Net.Http.Json
|
||||||
@inject HttpClient Http
|
@using NexusReader.Application.Abstractions.Services
|
||||||
|
@inject IConceptsMapService ConceptsMapService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IIdentityService IdentityService
|
@inject IIdentityService IdentityService
|
||||||
@inject ISyncService SyncService
|
@inject ISyncService SyncService
|
||||||
@@ -203,11 +204,11 @@
|
|||||||
|
|
||||||
if (BookId.HasValue && BookId.Value != Guid.Empty)
|
if (BookId.HasValue && BookId.Value != Guid.Empty)
|
||||||
{
|
{
|
||||||
var result = await Http.GetFromJsonAsync<BookConceptsMapResultDto>($"api/book/{BookId}/concepts-map");
|
var result = await ConceptsMapService.GetConceptsMapAsync(BookId.Value);
|
||||||
if (result != null)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
Nodes = result.Nodes;
|
Nodes = result.Value.Nodes;
|
||||||
LastReadBlockId = result.LastReadBlockId;
|
LastReadBlockId = result.Value.LastReadBlockId;
|
||||||
|
|
||||||
if (Nodes.Any())
|
if (Nodes.Any())
|
||||||
{
|
{
|
||||||
@@ -216,7 +217,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_errorMessage = "Brak odpowiedzi od serwera.";
|
_errorMessage = result.Errors.FirstOrDefault()?.Message ?? "Brak odpowiedzi od serwera.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public sealed partial class KnowledgeCoordinator : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ProcessFullPageAsync(string fullContent, string tenantId = "global")
|
public async Task ProcessFullPageAsync(string fullContent, string tenantId = "global", Guid? ebookId = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(fullContent)) return;
|
if (string.IsNullOrWhiteSpace(fullContent)) return;
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ public sealed partial class KnowledgeCoordinator : IDisposable
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await _knowledgeService.GetGraphDataAsync(fullContent, tenantId);
|
var result = await _knowledgeService.GetGraphDataAsync(fullContent, tenantId, ebookId);
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
var packet = result.Value;
|
var packet = result.Value;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ builder.Services.AddCascadingAuthenticationState();
|
|||||||
|
|
||||||
// AI & Content Services
|
// AI & Content Services
|
||||||
builder.Services.AddScoped<IKnowledgeService, WasmKnowledgeService>();
|
builder.Services.AddScoped<IKnowledgeService, WasmKnowledgeService>();
|
||||||
|
builder.Services.AddScoped<IConceptsMapService, WasmConceptsMapService>();
|
||||||
|
|
||||||
builder.Services.AddTransient<NexusReader.Web.Client.Handlers.AuthenticationHeaderHandler>();
|
builder.Services.AddTransient<NexusReader.Web.Client.Handlers.AuthenticationHeaderHandler>();
|
||||||
builder.Services.AddHttpClient("NexusAPI", client =>
|
builder.Services.AddHttpClient("NexusAPI", client =>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Net.Http.Json;
|
||||||
|
using FluentResults;
|
||||||
|
using NexusReader.Application.Abstractions.Services;
|
||||||
|
using NexusReader.Application.Queries.Concepts;
|
||||||
|
|
||||||
|
namespace NexusReader.Web.Client.Services;
|
||||||
|
|
||||||
|
public class WasmConceptsMapService : IConceptsMapService
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
public WasmConceptsMapService(HttpClient httpClient)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<BookConceptsMapResultDto>> GetConceptsMapAsync(Guid bookId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.GetAsync($"api/book/{bookId}/concepts-map");
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<BookConceptsMapResultDto>();
|
||||||
|
return result != null ? Result.Ok(result) : Result.Fail<BookConceptsMapResultDto>("Błąd deserializacji mapy pojęć.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
return Result.Fail<BookConceptsMapResultDto>($"Błąd serwera ({response.StatusCode}): {errorContent}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result.Fail<BookConceptsMapResultDto>(new Error("Błąd sieci przy pobieraniu mapy pojęć.").CausedBy(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,6 +74,7 @@ builder.Services.AddHttpClient("NexusAPI", (sp, client) =>
|
|||||||
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("NexusAPI"));
|
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("NexusAPI"));
|
||||||
|
|
||||||
builder.Services.AddScoped<IIdentityService, NexusReader.Web.Services.ServerIdentityService>();
|
builder.Services.AddScoped<IIdentityService, NexusReader.Web.Services.ServerIdentityService>();
|
||||||
|
builder.Services.AddScoped<IConceptsMapService, NexusReader.Web.Services.ServerConceptsMapService>();
|
||||||
builder.Services.AddCascadingAuthenticationState();
|
builder.Services.AddCascadingAuthenticationState();
|
||||||
|
|
||||||
builder.Services.AddApplication();
|
builder.Services.AddApplication();
|
||||||
@@ -87,6 +88,11 @@ builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(
|
|||||||
// Authorization Policies
|
// Authorization Policies
|
||||||
builder.Services.AddScoped<IAuthorizationHandler, TokenLimitHandler>();
|
builder.Services.AddScoped<IAuthorizationHandler, TokenLimitHandler>();
|
||||||
builder.Services.AddAuthorizationBuilder()
|
builder.Services.AddAuthorizationBuilder()
|
||||||
|
.SetDefaultPolicy(new Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder(
|
||||||
|
IdentityConstants.ApplicationScheme,
|
||||||
|
IdentityConstants.BearerScheme)
|
||||||
|
.RequireAuthenticatedUser()
|
||||||
|
.Build())
|
||||||
.AddPolicy("ProUser", policy => policy.RequireClaim("Plan", SubscriptionPlan.ProName, SubscriptionPlan.EnterpriseName))
|
.AddPolicy("ProUser", policy => policy.RequireClaim("Plan", SubscriptionPlan.ProName, SubscriptionPlan.EnterpriseName))
|
||||||
.AddPolicy("HasAvailableTokens", policy => policy.AddRequirements(new TokenLimitRequirement()));
|
.AddPolicy("HasAvailableTokens", policy => policy.AddRequirements(new TokenLimitRequirement()));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using FluentResults;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using NexusReader.Application.Abstractions.Services;
|
||||||
|
using NexusReader.Application.Queries.Concepts;
|
||||||
|
|
||||||
|
namespace NexusReader.Web.Services;
|
||||||
|
|
||||||
|
public class ServerConceptsMapService : IConceptsMapService
|
||||||
|
{
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
private readonly AuthenticationStateProvider _authStateProvider;
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
|
public ServerConceptsMapService(
|
||||||
|
IMediator mediator,
|
||||||
|
AuthenticationStateProvider authStateProvider,
|
||||||
|
IHttpContextAccessor httpContextAccessor)
|
||||||
|
{
|
||||||
|
_mediator = mediator;
|
||||||
|
_authStateProvider = authStateProvider;
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<BookConceptsMapResultDto>> GetConceptsMapAsync(Guid bookId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var authState = await _authStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var user = authState.User;
|
||||||
|
|
||||||
|
if (user == null || !user.Identity?.IsAuthenticated == true)
|
||||||
|
{
|
||||||
|
user = _httpContextAccessor.HttpContext?.User;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user == null || !user.Identity?.IsAuthenticated == true)
|
||||||
|
{
|
||||||
|
return Result.Fail<BookConceptsMapResultDto>("Użytkownik nie jest uwierzytelniony.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var tenantId = user.FindFirstValue("TenantId") ?? "global";
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(userId))
|
||||||
|
{
|
||||||
|
return Result.Fail<BookConceptsMapResultDto>("Nie znaleziono identyfikatora użytkownika.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _mediator.Send(new GetBookConceptsMapQuery(bookId, userId, tenantId));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result.Fail<BookConceptsMapResultDto>(new Error("Błąd pobierania mapy pojęć na serwerze.").CausedBy(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ using NexusReader.Application.Queries.User;
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using NexusReader.Application.Constants;
|
using NexusReader.Application.Constants;
|
||||||
using NexusReader.Application.Abstractions.Services;
|
using NexusReader.Application.Abstractions.Services;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
|
||||||
namespace NexusReader.Web.Services;
|
namespace NexusReader.Web.Services;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ public class ServerIdentityService : IIdentityService
|
|||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
private readonly INativeStorageService _storageService;
|
private readonly INativeStorageService _storageService;
|
||||||
|
private readonly AuthenticationStateProvider _authStateProvider;
|
||||||
|
|
||||||
public event Func<Task>? OnStateInvalidated;
|
public event Func<Task>? OnStateInvalidated;
|
||||||
|
|
||||||
@@ -27,13 +29,15 @@ public class ServerIdentityService : IIdentityService
|
|||||||
SignInManager<NexusUser> signInManager,
|
SignInManager<NexusUser> signInManager,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
INativeStorageService storageService)
|
INativeStorageService storageService,
|
||||||
|
AuthenticationStateProvider authStateProvider)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_storageService = storageService;
|
_storageService = storageService;
|
||||||
|
_authStateProvider = authStateProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> LoginAsync(string email, string password, bool rememberMe = false)
|
public async Task<Result> LoginAsync(string email, string password, bool rememberMe = false)
|
||||||
@@ -107,7 +111,14 @@ public class ServerIdentityService : IIdentityService
|
|||||||
|
|
||||||
public async Task<Result<UserProfileDto>> GetProfileAsync()
|
public async Task<Result<UserProfileDto>> GetProfileAsync()
|
||||||
{
|
{
|
||||||
var user = _httpContextAccessor.HttpContext?.User;
|
var authState = await _authStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var user = authState.User;
|
||||||
|
|
||||||
|
if (user == null || !user.Identity?.IsAuthenticated == true)
|
||||||
|
{
|
||||||
|
user = _httpContextAccessor.HttpContext?.User;
|
||||||
|
}
|
||||||
|
|
||||||
if (user == null || !user.Identity?.IsAuthenticated == true) return Result.Fail("Not authenticated.");
|
if (user == null || !user.Identity?.IsAuthenticated == true) return Result.Fail("Not authenticated.");
|
||||||
|
|
||||||
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
|
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
|||||||
Reference in New Issue
Block a user