c6a5e93773
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>
150 lines
4.5 KiB
SQL
150 lines
4.5 KiB
SQL
-- V1__Initial_Schema.sql
|
|
-- Create quantengine schema and core tables
|
|
|
|
CREATE SCHEMA IF NOT EXISTS quantengine;
|
|
|
|
-- KIS API Token Cache
|
|
CREATE TABLE IF NOT EXISTS quantengine.kis_tokens (
|
|
account TEXT PRIMARY KEY,
|
|
access_token TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
-- User Account Management
|
|
CREATE TABLE IF NOT EXISTS quantengine.workspace_account (
|
|
ordinal INT NOT NULL,
|
|
username TEXT PRIMARY KEY,
|
|
password_hash TEXT NOT NULL,
|
|
role TEXT NOT NULL DEFAULT 'Admin',
|
|
is_active TEXT NOT NULL DEFAULT 'true',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_account_active ON quantengine.workspace_account(is_active, username);
|
|
|
|
-- Session Management
|
|
CREATE TABLE IF NOT EXISTS quantengine.workspace_session (
|
|
session_token_hash TEXT PRIMARY KEY,
|
|
username TEXT NOT NULL,
|
|
role TEXT NOT NULL DEFAULT 'Admin',
|
|
created_at TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
revoked_at TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_session_username ON quantengine.workspace_session(username, expires_at DESC);
|
|
|
|
-- Collection Runs
|
|
CREATE TABLE IF NOT EXISTS quantengine.collection_runs (
|
|
run_id TEXT PRIMARY KEY,
|
|
collector_name TEXT NOT NULL,
|
|
started_at TEXT NOT NULL,
|
|
finished_at TEXT,
|
|
status TEXT NOT NULL,
|
|
input_source TEXT,
|
|
output_json_path TEXT,
|
|
output_db_path TEXT,
|
|
notes TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Collection Snapshots
|
|
CREATE TABLE IF NOT EXISTS quantengine.collection_snapshots (
|
|
run_id TEXT NOT NULL,
|
|
dataset_name TEXT NOT NULL,
|
|
ticker TEXT NOT NULL,
|
|
name TEXT,
|
|
sector TEXT,
|
|
as_of_date TEXT,
|
|
source_priority TEXT,
|
|
source_status TEXT,
|
|
payload_json TEXT NOT NULL,
|
|
provenance_json TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (run_id, dataset_name, ticker)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_collection_snapshots_ticker_time ON quantengine.collection_snapshots(ticker, created_at DESC);
|
|
|
|
-- Collection Source Errors
|
|
CREATE TABLE IF NOT EXISTS quantengine.collection_source_errors (
|
|
run_id TEXT NOT NULL,
|
|
ticker TEXT,
|
|
source_name TEXT NOT NULL,
|
|
error_kind TEXT NOT NULL,
|
|
error_message TEXT NOT NULL,
|
|
payload_json TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_collection_source_errors_run ON quantengine.collection_source_errors(run_id, source_name);
|
|
|
|
-- Settings
|
|
CREATE TABLE IF NOT EXISTS quantengine.settings (
|
|
ordinal INT NOT NULL,
|
|
key TEXT PRIMARY KEY,
|
|
value_json TEXT NOT NULL,
|
|
note TEXT NOT NULL DEFAULT '',
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Account Snapshots
|
|
CREATE TABLE IF NOT EXISTS quantengine.account_snapshot (
|
|
ordinal INT NOT NULL,
|
|
row_json TEXT NOT NULL,
|
|
captured_at TEXT NOT NULL DEFAULT '',
|
|
account TEXT NOT NULL DEFAULT '',
|
|
account_type TEXT NOT NULL DEFAULT '',
|
|
ticker TEXT NOT NULL DEFAULT '',
|
|
name TEXT NOT NULL DEFAULT '',
|
|
parse_status TEXT NOT NULL DEFAULT '',
|
|
user_confirmed TEXT NOT NULL DEFAULT '',
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_account_snapshot_captured_at ON quantengine.account_snapshot(captured_at);
|
|
CREATE INDEX IF NOT EXISTS idx_account_snapshot_ticker ON quantengine.account_snapshot(ticker);
|
|
|
|
-- Workspace Metadata
|
|
CREATE TABLE IF NOT EXISTS quantengine.workspace_meta (
|
|
key TEXT PRIMARY KEY,
|
|
value_json TEXT NOT NULL
|
|
);
|
|
|
|
-- Workspace Change Log
|
|
CREATE TABLE IF NOT EXISTS quantengine.workspace_change_log (
|
|
id SERIAL PRIMARY KEY,
|
|
domain TEXT NOT NULL,
|
|
action TEXT NOT NULL,
|
|
target_ref TEXT NOT NULL DEFAULT '',
|
|
actor TEXT NOT NULL DEFAULT 'system',
|
|
note TEXT NOT NULL DEFAULT '',
|
|
before_json TEXT NOT NULL DEFAULT 'null',
|
|
after_json TEXT NOT NULL DEFAULT 'null',
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Workspace Approval
|
|
CREATE TABLE IF NOT EXISTS quantengine.workspace_approval_v2 (
|
|
domain TEXT NOT NULL,
|
|
target_ref TEXT NOT NULL DEFAULT '*',
|
|
status TEXT NOT NULL,
|
|
approved_by TEXT NOT NULL DEFAULT '',
|
|
approved_at TEXT NOT NULL DEFAULT '',
|
|
note TEXT NOT NULL DEFAULT '',
|
|
updated_at TEXT NOT NULL,
|
|
PRIMARY KEY (domain, target_ref)
|
|
);
|
|
|
|
-- Workspace Lock
|
|
CREATE TABLE IF NOT EXISTS quantengine.workspace_lock (
|
|
domain TEXT NOT NULL,
|
|
target_ref TEXT NOT NULL DEFAULT '',
|
|
locked_by TEXT NOT NULL DEFAULT '',
|
|
reason TEXT NOT NULL DEFAULT '',
|
|
locked_at TEXT NOT NULL,
|
|
PRIMARY KEY (domain, target_ref)
|
|
);
|