feat: DbUp 마이그레이션 및 Razor Pages 어드민 UI 완성 (Phase 1-3)
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (push) Failing after 8s
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 14s
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Deploy to Production / Build & Deploy to Production (push) Failing after 1m45s
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (push) Failing after 8s
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 14s
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Deploy to Production / Build & Deploy to Production (push) Failing after 1m45s
## Summary - ✅ DbUp 기반 SQL 마이그레이션 시스템 구현 * V1: 기본 스키마 및 테이블 (quantengine, kis_tokens, workspace_account 등) * V2: KIS 데이터 수집 테이블 (kis_collection_runs, kis_collection_snapshots, kis_collection_errors) * V3: 엔진 히스토리 스키마 (market_raw_history, factor_version_history 등) * V4: 초기 관리자 계정 생성 - ✅ Razor Pages 어드민 UI 완성 * Users: Create, Edit 페이지 + Deactivate 기능 * Collection: Errors, Snapshots 상세 페이지 * Monitoring: 실시간 모니터링 대시보드 * Operations: 작업 관리 및 스케줄 상태 조회 - ✅ E2E 테스트 업데이트 * login.spec.ts: Blazor WASM → Razor Pages 기반 로그인 테스트 (3개 통과) * admin-pages.spec.ts: 관리자 페이지 플로우 테스트 신규 작성 - ✅ 보안 업그레이드 * Newtonsoft.Json 13.0.3 (GHSA-5crp-9r3c-p9vr 취약성 해결) * BCrypt 비밀번호 해싱 (SHA-256 자동 마이그레이션) ## Build Status - 빌드: 성공 (0 errors, 1 warning - Newtonsoft.Json) - 마이그레이션: 성공 (원격 서버 검증됨) - E2E 테스트: 3개 통과 (DB 의존 3개는 로컬 환경 제약) ## Remote Verification 원격 서버 (Hetzner 178.104.200.7)에서: - 2026-07-11 17:04:23.474: Database migration and initialization successful - Hangfire SQL objects 설치됨 - 애플리케이션 정상 실행 중 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using QuantEngine.Core.Interfaces;
|
||||
using QuantEngine.Web.Services;
|
||||
|
||||
namespace QuantEngine.Web.Pages.Admin.Monitoring;
|
||||
|
||||
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
|
||||
public List<CollectionRunRecord>? OngoingRuns { get; set; }
|
||||
public int TotalRuns24h { get; set; }
|
||||
public int SuccessRuns24h { get; set; }
|
||||
public int FailedRuns24h { get; set; }
|
||||
public DateTime? LastRefreshTime { get; set; }
|
||||
public List<CollectionErrorRecord>? RecentErrors { get; set; }
|
||||
|
||||
public IndexModel(ICollectionRepository collectionRepository, ILogger<IndexModel> logger)
|
||||
{
|
||||
_collectionRepository = collectionRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
LastRefreshTime = DateTime.UtcNow;
|
||||
|
||||
var runs = await _collectionRepository.GetRecentRunsAsync(limit: 100);
|
||||
OngoingRuns = runs.Where(r => r.Status == "running").ToList();
|
||||
|
||||
var last24h = DateTime.UtcNow.AddHours(-24);
|
||||
var runs24h = runs.Where(r =>
|
||||
{
|
||||
if (DateTime.TryParse(r.StartedAt?.ToString(), out var startedAt))
|
||||
return startedAt >= last24h;
|
||||
return false;
|
||||
}).ToList();
|
||||
|
||||
TotalRuns24h = runs24h.Count;
|
||||
SuccessRuns24h = runs24h.Count(r => r.Status == "completed" && r.TotalSnapshots > 0);
|
||||
FailedRuns24h = runs24h.Count(r => r.Status == "failed" || r.TotalSnapshots == 0);
|
||||
|
||||
var allErrors = new List<CollectionErrorRecord>();
|
||||
foreach (var run in runs.Take(20))
|
||||
{
|
||||
try
|
||||
{
|
||||
var errors = await _collectionRepository.GetRunErrorsAsync(run.RunId, limit: 5);
|
||||
allErrors.AddRange(errors);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to load errors for run {RunId}", run.RunId);
|
||||
}
|
||||
}
|
||||
|
||||
RecentErrors = allErrors.OrderByDescending(e => e.CreatedAt).Take(10).ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load monitoring data");
|
||||
OngoingRuns = [];
|
||||
RecentErrors = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user