feat: Migrate admin UI from Blazor WASM/MudBlazor to Razor Pages/Cookie Auth/Tabler
- Remove QuantEngine.Web.Client from .sln (keep on disk for reference) - Replace Blazor Interactive WebAssembly with server-rendered Razor Pages - Implement Cookie Authentication (HttpOnly, SameSite=Lax, 12h expiry) - Add AuthService with BCrypt password hashing + auto-migration from SHA-256 - Implement IpLockoutService (3 strikes → 15-min ban) - Create Admin folder structure with Layout + shared partials - Implement Dashboard, Collection, Users index pages (base structure) - Remove hardcoded backdoors (master_recovery, dev auth bypass) - Remove hardcoded localhost:5265 URLs - Add Tabler UI base styling (Bootstrap 5 CDN + custom admin.css) - Update CLAUDE.md with new UI standards and auth policies - Build: 0 errors, 0 warnings (ready for dev testing) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
@page
|
||||
@model QuantEngine.Web.Pages.Admin.Users.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "사용자 관리";
|
||||
}
|
||||
|
||||
<div class="page-header d-print-none">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="page-title">사용자 관리</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="/Admin/Users/Create" class="btn btn-primary">새 사용자 추가</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-body">
|
||||
<div class="row row-deck row-cards">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">사용자 목록</h3>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table card-table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>사용자명</th>
|
||||
<th>역할</th>
|
||||
<th>상태</th>
|
||||
<th>생성일</th>
|
||||
<th>작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (Model.Users?.Any() == true)
|
||||
{
|
||||
@foreach (var user in Model.Users)
|
||||
{
|
||||
<tr>
|
||||
<td>@user.Username</td>
|
||||
<td>@(user.Role ?? "Admin")</td>
|
||||
<td>
|
||||
@if (string.Equals(user.IsActive, "true", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
<span class="badge bg-success">활성</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">비활성</span>
|
||||
}
|
||||
</td>
|
||||
<td>@(user.CreatedAt ?? "-")</td>
|
||||
<td>
|
||||
<a href="/Admin/Users/@user.Username/Edit" class="btn btn-sm btn-link">수정</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">사용자가 없습니다</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using QuantEngine.Core.Interfaces;
|
||||
using QuantEngine.Core.Models;
|
||||
using QuantEngine.Web.Services;
|
||||
|
||||
namespace QuantEngine.Web.Pages.Admin.Users;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly IWorkspaceRepository _workspaceRepository;
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
|
||||
public List<WorkspaceAccount>? Users { get; set; }
|
||||
|
||||
public IndexModel(IWorkspaceRepository workspaceRepository, ILogger<IndexModel> logger)
|
||||
{
|
||||
_workspaceRepository = workspaceRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var accounts = await _workspaceRepository.GetAccountsAsync();
|
||||
Users = accounts.ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Users loading failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user