feat(ui): Blazor WebAssembly 마이그레이션 및 API-First 로그인 구현
This commit is contained in:
+7
-9
@@ -1,16 +1,16 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||
using QuantEngine.Web.Client.Services;
|
||||
|
||||
namespace QuantEngine.Web.Infrastructure
|
||||
namespace QuantEngine.Web.Client.Infrastructure
|
||||
{
|
||||
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
private readonly ProtectedLocalStorage _localStorage;
|
||||
private readonly LocalStorageService _localStorage;
|
||||
private readonly ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
private const string StorageKey = "quant_admin_session";
|
||||
|
||||
public CustomAuthenticationStateProvider(ProtectedLocalStorage localStorage)
|
||||
public CustomAuthenticationStateProvider(LocalStorageService localStorage)
|
||||
{
|
||||
_localStorage = localStorage;
|
||||
}
|
||||
@@ -19,11 +19,9 @@ namespace QuantEngine.Web.Infrastructure
|
||||
{
|
||||
try
|
||||
{
|
||||
// ProtectedLocalStorage call will throw an exception during pre-rendering
|
||||
var result = await _localStorage.GetAsync<string>(StorageKey);
|
||||
if (result.Success && !string.IsNullOrEmpty(result.Value))
|
||||
var username = await _localStorage.GetAsync<string>(StorageKey);
|
||||
if (!string.IsNullOrEmpty(username))
|
||||
{
|
||||
var username = result.Value;
|
||||
var identity = new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, username),
|
||||
@@ -36,7 +34,7 @@ namespace QuantEngine.Web.Infrastructure
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Return anonymous state during pre-rendering or if storage reading fails
|
||||
// Return anonymous if localStorage isn't ready
|
||||
}
|
||||
|
||||
return new AuthenticationState(_anonymous);
|
||||
+14
-19
@@ -1,11 +1,10 @@
|
||||
@inherits LayoutComponentBase
|
||||
@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment WebHostEnvironment
|
||||
@inject HttpClient Http
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject NavigationManager NavigationManager
|
||||
@using System.IO
|
||||
@using System.Text.Json
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.FluentUI.AspNetCore.Components
|
||||
@using QuantEngine.Web.Infrastructure
|
||||
@using QuantEngine.Web.Client.Infrastructure
|
||||
|
||||
<FluentStack Orientation="Orientation.Vertical" Class="h-100 w-100">
|
||||
<!-- Header -->
|
||||
@@ -77,25 +76,15 @@
|
||||
private string appVersion = "Local Debug";
|
||||
private string buildTime = "N/A";
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var versionFilePath = Path.Combine(WebHostEnvironment.WebRootPath, "version.json");
|
||||
if (File.Exists(versionFilePath))
|
||||
var versionInfo = await Http.GetFromJsonAsync<VersionInfo>("version.json");
|
||||
if (versionInfo != null)
|
||||
{
|
||||
var jsonContent = File.ReadAllText(versionFilePath);
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(jsonContent);
|
||||
var root = doc.RootElement;
|
||||
|
||||
if (root.TryGetProperty("version", out var versionProp))
|
||||
{
|
||||
appVersion = versionProp.GetString() ?? "Local Debug";
|
||||
}
|
||||
if (root.TryGetProperty("built", out var builtProp))
|
||||
{
|
||||
buildTime = builtProp.GetString() ?? "N/A";
|
||||
}
|
||||
appVersion = versionInfo.Version ?? "Local Debug";
|
||||
buildTime = versionInfo.Built ?? "N/A";
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -110,5 +99,11 @@
|
||||
await customProvider.MarkUserAsLoggedOutAsync();
|
||||
NavigationManager.NavigateTo("login");
|
||||
}
|
||||
|
||||
private class VersionInfo
|
||||
{
|
||||
public string? Version { get; set; }
|
||||
public string? Built { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@page "/collection"
|
||||
@using QuantEngine.Web.Services
|
||||
@using QuantEngine.Web.Client.Services
|
||||
@inject ApiClient ApiClient
|
||||
@inject ILogger<Collection> Logger
|
||||
|
||||
|
||||
+21
-10
@@ -1,6 +1,6 @@
|
||||
@page "/"
|
||||
@using QuantEngine.Core.Infrastructure
|
||||
@inject IWebHostEnvironment Environment
|
||||
@inject HttpClient Http
|
||||
|
||||
<PageTitle>Quant Engine - Dashboard</PageTitle>
|
||||
|
||||
@@ -96,15 +96,26 @@
|
||||
private string RawFeedLabel = "DISCONNECTED";
|
||||
private string ReportPath = "n/a";
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ReportPath = Path.GetFullPath(Path.Combine(Environment.ContentRootPath, "..", "..", "..", "Temp", "operational_report.json"));
|
||||
var report = OperationalReportLoader.Load(ReportPath);
|
||||
Sections.AddRange(report.Sections);
|
||||
SectionCountLabel = report.SectionCount.ToString();
|
||||
GeneratedAtLabel = report.GeneratedAt;
|
||||
SourceLabel = report.SourceJson;
|
||||
ReportStateLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING";
|
||||
ReportChipLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING";
|
||||
try
|
||||
{
|
||||
var report = await Http.GetFromJsonAsync<OperationalReportData>("api/operational-report");
|
||||
if (report != null)
|
||||
{
|
||||
Sections.Clear();
|
||||
Sections.AddRange(report.Sections);
|
||||
SectionCountLabel = report.SectionCount.ToString();
|
||||
GeneratedAtLabel = report.GeneratedAt;
|
||||
SourceLabel = report.SourceJson;
|
||||
ReportStateLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING";
|
||||
ReportChipLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ReportStateLabel = "DATA_MISSING";
|
||||
ReportChipLabel = "DATA_MISSING";
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -2,7 +2,7 @@
|
||||
@attribute [AllowAnonymous]
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IConfiguration Configuration
|
||||
@inject HttpClient Http
|
||||
|
||||
<PageTitle>로그인 - QuantEngine</PageTitle>
|
||||
|
||||
@@ -281,11 +281,9 @@
|
||||
|
||||
try
|
||||
{
|
||||
// Verify against configurations in appsettings.json
|
||||
var expectedUser = Configuration["AdminSettings:Username"] ?? "admin";
|
||||
var expectedPass = Configuration["AdminSettings:Password"] ?? "quant123!";
|
||||
var response = await Http.PostAsJsonAsync("api/auth/login", new { Username, Password });
|
||||
|
||||
if (Username == expectedUser && Password == expectedPass)
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var customProvider = (CustomAuthenticationStateProvider)AuthStateProvider;
|
||||
await customProvider.MarkUserAsAuthenticatedAsync(Username);
|
||||
+23
-13
@@ -1,6 +1,6 @@
|
||||
@page "/operations"
|
||||
@using QuantEngine.Core.Infrastructure
|
||||
@inject IWebHostEnvironment Environment
|
||||
@inject HttpClient Http
|
||||
|
||||
<PageTitle>Quant Engine - Operations</PageTitle>
|
||||
|
||||
@@ -97,19 +97,29 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ReportPath = Path.GetFullPath(Path.Combine(Environment.ContentRootPath, "..", "..", "..", "Temp", "operational_report.json"));
|
||||
try
|
||||
{
|
||||
var report = await Http.GetFromJsonAsync<OperationalReportData>("api/operational-report");
|
||||
if (report != null)
|
||||
{
|
||||
SchemaVersion = report.SchemaVersion;
|
||||
SourceJson = report.SourceJson;
|
||||
GeneratedAt = report.GeneratedAt;
|
||||
|
||||
Sections.Clear();
|
||||
Sections.AddRange(report.Sections);
|
||||
|
||||
var report = OperationalReportLoader.Load(ReportPath);
|
||||
SchemaVersion = report.SchemaVersion;
|
||||
SourceJson = report.SourceJson;
|
||||
GeneratedAt = report.GeneratedAt;
|
||||
Sections.AddRange(report.Sections);
|
||||
HighlightSections.Clear();
|
||||
HighlightSections.AddRange(Sections.Take(4));
|
||||
|
||||
HighlightSections.Clear();
|
||||
HighlightSections.AddRange(Sections.Take(4));
|
||||
|
||||
SectionCountLabel = report.SectionCount.ToString();
|
||||
RenderedSectionCountLabel = Sections.Count.ToString();
|
||||
HealthLabel = Sections.Count > 0 ? "PASS" : "DATA_MISSING";
|
||||
SectionCountLabel = report.SectionCount.ToString();
|
||||
RenderedSectionCountLabel = Sections.Count.ToString();
|
||||
HealthLabel = Sections.Count > 0 ? "PASS" : "DATA_MISSING";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
HealthLabel = "DATA_MISSING";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using QuantEngine.Web.Client.Services;
|
||||
using QuantEngine.Web.Client.Infrastructure;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
|
||||
// Register Fluent UI
|
||||
builder.Services.AddFluentUIComponents();
|
||||
|
||||
// Register LocalStorage for cross-platform session persistence
|
||||
builder.Services.AddScoped<LocalStorageService>();
|
||||
|
||||
// Authentication setup in WebAssembly client
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddCascadingAuthenticationState();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
|
||||
|
||||
// HttpClient register (API-First standard)
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
|
||||
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\QuantEngine.Core\QuantEngine.Core.csproj" />
|
||||
<ProjectReference Include="..\..\QuantEngine.Application\QuantEngine.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.0-preview.2.25120.18" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.0-preview.2.25120.18" />
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components" Version="5.0.0-rc.4-26177.1" />
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="5.0.0-rc.4-26177.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+6
-9
@@ -2,19 +2,16 @@ using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using QuantEngine.Core.Interfaces;
|
||||
|
||||
namespace QuantEngine.Web.Services;
|
||||
namespace QuantEngine.Web.Client.Services;
|
||||
|
||||
public class ApiClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
private readonly ILogger<ApiClient> _logger;
|
||||
private string BaseUrl { get; set; }
|
||||
|
||||
public ApiClient(HttpClient http, ILogger<ApiClient> logger)
|
||||
{
|
||||
_http = http;
|
||||
_logger = logger;
|
||||
BaseUrl = "http://localhost:5001"; // Default for Blazor Server
|
||||
}
|
||||
|
||||
// Collection API Methods
|
||||
@@ -23,7 +20,7 @@ public class ApiClient
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _http.GetFromJsonAsync<CollectionDashboardStateDto>($"{BaseUrl}/api/collection/state");
|
||||
return await _http.GetFromJsonAsync<CollectionDashboardStateDto>("api/collection/state");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -36,7 +33,7 @@ public class ApiClient
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _http.GetFromJsonAsync<CollectionRunsResponse>($"{BaseUrl}/api/collection/runs?limit={limit}");
|
||||
return await _http.GetFromJsonAsync<CollectionRunsResponse>($"api/collection/runs?limit={limit}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -49,7 +46,7 @@ public class ApiClient
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _http.GetFromJsonAsync<CollectionRunSnapshotsResponse>($"{BaseUrl}/api/collection/runs/{runId}/snapshots");
|
||||
return await _http.GetFromJsonAsync<CollectionRunSnapshotsResponse>($"api/collection/runs/{runId}/snapshots");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -62,7 +59,7 @@ public class ApiClient
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _http.GetFromJsonAsync<CollectionRunErrorsResponse>($"{BaseUrl}/api/collection/runs/{runId}/errors?limit={limit}");
|
||||
return await _http.GetFromJsonAsync<CollectionRunErrorsResponse>($"api/collection/runs/{runId}/errors?limit={limit}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -75,7 +72,7 @@ public class ApiClient
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync($"{BaseUrl}/api/collection/run", new { });
|
||||
var response = await _http.PostAsJsonAsync("api/collection/run", new { });
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<CollectionRunStartResponse>();
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace QuantEngine.Web.Client.Services
|
||||
{
|
||||
public class LocalStorageService
|
||||
{
|
||||
private readonly IJSRuntime _js;
|
||||
|
||||
public LocalStorageService(IJSRuntime js)
|
||||
{
|
||||
_js = js;
|
||||
}
|
||||
|
||||
public async Task SetAsync<T>(string key, T value)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(value);
|
||||
await _js.InvokeVoidAsync("localStorage.setItem", key, json);
|
||||
}
|
||||
|
||||
public async Task<T?> GetAsync<T>(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = await _js.InvokeAsync<string?>("localStorage.getItem", key);
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
return JsonSerializer.Deserialize<T>(json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string key)
|
||||
{
|
||||
await _js.InvokeVoidAsync("localStorage.removeItem", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,10 @@
|
||||
@using Microsoft.JSInterop
|
||||
@using Microsoft.FluentUI.AspNetCore.Components
|
||||
@using Microsoft.FluentUI.AspNetCore.Components.Icons
|
||||
@using QuantEngine.Web
|
||||
@using QuantEngine.Web.Components
|
||||
@using QuantEngine.Web.Components.Layout
|
||||
@using QuantEngine.Web.Services
|
||||
@using QuantEngine.Web.Client
|
||||
@using QuantEngine.Web.Client.Pages
|
||||
@using QuantEngine.Web.Client.Layout
|
||||
@using QuantEngine.Web.Client.Infrastructure
|
||||
@using QuantEngine.Web.Client.Services
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
<link rel="stylesheet" href="@Assets["QuantEngine.Web.styles.css"]" />
|
||||
<ImportMap />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<HeadOutlet />
|
||||
<HeadOutlet @rendermode="InteractiveWebAssembly" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<FluentDesignSystemProvider>
|
||||
<Routes />
|
||||
<Routes @rendermode="InteractiveWebAssembly" />
|
||||
<ReconnectModal />
|
||||
</FluentDesignSystemProvider>
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
@using QuantEngine.Web.Client
|
||||
@using QuantEngine.Web.Client.Pages
|
||||
@using QuantEngine.Web.Client.Layout
|
||||
|
||||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
|
||||
<Router AppAssembly="typeof(Dashboard).Assembly" NotFoundPage="typeof(NotFound)">
|
||||
<Found Context="routeData">
|
||||
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
|
||||
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
|
||||
<NotAuthorized>
|
||||
<RedirectToLogin />
|
||||
</NotAuthorized>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using QuantEngine.Web.Components;
|
||||
using QuantEngine.Web.Services;
|
||||
using QuantEngine.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using QuantEngine.Web.Infrastructure;
|
||||
using QuantEngine.Infrastructure.Repositories;
|
||||
using QuantEngine.Infrastructure.Services;
|
||||
using QuantEngine.Core.Interfaces;
|
||||
@@ -10,7 +10,8 @@ using System.Text.Json;
|
||||
using static QuantEngine.Application.Services.DataCollectionService;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using Serilog;
|
||||
using QuantEngine.Web.Infrastructure;
|
||||
using QuantEngine.Web.Client.Infrastructure;
|
||||
using QuantEngine.Web.Client.Services;
|
||||
using QuantEngine.Web.Endpoints;
|
||||
|
||||
// Serilog Configuration with Telegram Sink
|
||||
@@ -25,10 +26,11 @@ builder.Host.UseSerilog();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
.AddInteractiveWebAssemblyComponents();
|
||||
|
||||
// Authentication and Custom State Provider
|
||||
// Authentication and Custom State Provider (Shared client components)
|
||||
builder.Services.AddCascadingAuthenticationState();
|
||||
builder.Services.AddScoped<LocalStorageService>();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
|
||||
builder.Services.AddAuthorizationCore();
|
||||
|
||||
@@ -100,6 +102,32 @@ app.MapStaticAssets();
|
||||
// Collection API Endpoints (must be before MapRazorComponents)
|
||||
app.MapCollectionEndpoints();
|
||||
|
||||
// Login API (API-First for Blazor WASM client authentication)
|
||||
app.MapPost("/api/auth/login", (LoginRequest request, IConfiguration config) =>
|
||||
{
|
||||
var expectedUser = config["AdminSettings:Username"] ?? "admin";
|
||||
var expectedPass = config["AdminSettings:Password"] ?? "quant123!";
|
||||
|
||||
if (request.Username == expectedUser && request.Password == expectedPass)
|
||||
{
|
||||
return Results.Ok(new { success = true, username = request.Username });
|
||||
}
|
||||
return Results.Json(new { success = false, error = "invalid_credentials" }, statusCode: 401);
|
||||
});
|
||||
|
||||
// Operational Report serving API (WASM safe file loading substitute)
|
||||
app.MapGet("/api/operational-report", async (IWebHostEnvironment env) =>
|
||||
{
|
||||
var path = Path.GetFullPath(Path.Combine(env.ContentRootPath, "..", "..", "..", "Temp", "operational_report.json"));
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return Results.NotFound(new { gate = "FAIL", error = "operational_report_missing" });
|
||||
}
|
||||
var json = await File.ReadAllTextAsync(path);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
return Results.Ok(doc.RootElement);
|
||||
});
|
||||
|
||||
app.MapGet("/api/history/{domain}", async (string domain, int? limit, IPostgresqlHistorySnapshotReader reader) =>
|
||||
{
|
||||
var rows = await reader.ReadAsync(domain, limit ?? 500);
|
||||
@@ -144,7 +172,14 @@ app.MapPost("/api/history/{domain}", async (string domain, JsonElement payload,
|
||||
});
|
||||
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
.AddInteractiveWebAssemblyRenderMode()
|
||||
.AddAdditionalAssemblies(typeof(QuantEngine.Web.Client._Imports).Assembly);
|
||||
|
||||
app.Run();
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,22 @@
|
||||
<ProjectReference Include="..\QuantEngine.Infrastructure\QuantEngine.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\QuantEngine.Application\QuantEngine.Application.csproj" />
|
||||
<ProjectReference Include="..\QuantEngine.Core\QuantEngine.Core.csproj" />
|
||||
<ProjectReference Include="Client\QuantEngine.Web.Client.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components" Version="5.0.0-rc.4-26177.1" />
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="5.0.0-rc.4-26177.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.0-preview.2.25120.18" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Exclude client project files from server build to avoid duplicate compilations -->
|
||||
<Compile Remove="Client\**" />
|
||||
<Content Remove="Client\**" />
|
||||
<EmbeddedResource Remove="Client\**" />
|
||||
<None Remove="Client\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
Reference in New Issue
Block a user