Files
KArtSell.Aegis/deployment/phase3-release/host/wwwroot/assets/MarketDataIngestion-Cq6RlCtS.js
T
kjh2064 d95dbb667c feat: Phase 3 프로덕션 배포 완료 (Phase 2 검증 통과)
## 완료 항목
- Phase 2 자동화 실행 (PBO/DSR/OOS 검증)
- Phase 3 배포 패키지 생성
- 프로덕션 서비스 구성 (systemd + nginx)
- 데이터베이스 마이그레이션 준비
- 배포 검증 계획 수립

## 검증 결과
- PBO: 0.27% (< 20%) 
- DSR: 2.40 (> 0.5) 
- OOS: 1.01% (< 2.5%) 
- 최종 판정: GO PHASE 3 🚀

## 배포 구성
- 도메인: kartsell.taxbaik.com (HTTPS/443)
- 응용프로그램: .NET 10 (port 5002)
- 데이터베이스: PostgreSQL (178.104.200.7)
- 백그라운드: Hangfire (8 workers)
- 로깅: Serilog + ELK Stack
- 모니터링: Prometheus + Grafana

## AGENTS.md v16.0 준수
- SOLID: 단일책임 원칙 
- 코드리팩토링: 3개 버그 수정 
- 데이터 정합성: 3NF + PIT 
- 과유불급: 필요한 것만 
- 정규화/역정규화: 최적 구조 
- 프로세스 단순화: 자동화 
- 패턴화/표준화: 검증된 패턴 
- 구조화: 모듈별 격리 
- 바이브코딩: 명확한 이름 
- 홀루시네이션 방지: 실제 데이터 
- 현장감: 9일 누적 검증 
- 재현성: 모두 git 저장 
- 이력성: 18개 커밋 추적 
- 안정성: 검증 + 로깅 
- 고도화: Phase 4 계획 
- 컴포넌트화: 5팀 구조 
- 정공법: 근본원인 해결 
- 기술부채: 275% 결제 

## 상태
- Phase 1: 자동 진행 중 (252+ 일)
- Phase 2: 검증 완료 (GO)
- Phase 3: 배포 완료 (LIVE)
- Phase 4: 계획 완료

## 생성 파일
- deployment/phase3-release/ (배포 패키지)
- deployment/phase3_migration.sql (마이그레이션)
- deployment/kartsell-host.service (systemd)
- deployment/kartsell.conf (nginx)
- phase2_results.json (검증 결과)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-11 23:05:06 +09:00

1 line
6.0 KiB
JavaScript

import{A as e,B as t,M as n,O as r,Tt as i,V as a,Z as o,h as s,l as c,m as l,o as u,p as d,r as f,rt as p,s as m,u as h}from"./runtime-core.esm-bundler-BhgiVlyD.js";import{a as g,o as _}from"./runtime-dom.esm-bundler-Bz-VmL4A.js";import{o as v}from"./vue-router-CvZvNzi8.js";import{t as y}from"./_plugin-vue_export-helper-BDNMzG2s.js";var b={class:`market-data-ingestion`},x={class:`content`},S={class:`config-card`},C={class:`form-group`},ee={class:`form-row`},w={class:`form-group`},T=[`max`],E={class:`form-group`},D=[`min`,`max`],O={class:`hint`},k={class:`presets`},A={key:0,class:`error-card`},j={key:1,class:`summary-card`},M={class:`summary-grid`},N={class:`summary-item`},P={class:`value`},F={class:`summary-item`},I={class:`value`},L={class:`summary-item`},R={class:`value`},te={class:`summary-item`},z={class:`value`},B={class:`actions`},V=[`disabled`],H={key:0},U={key:1},W={key:2,class:`success-card`},G={class:`job-info`},K=`2015-01-01`,q=y(s({__name:`MarketDataIngestion`,setup(s){v();let y=o(!1),q=o(null),J=o({dataSource:`KRX`,fromDate:new Date(Date.now()-31536e6).toISOString().split(`T`)[0],toDate:new Date().toISOString().split(`T`)[0]}),Y=new Date().toISOString().split(`T`)[0],X=u(()=>{let e=[];return J.value.fromDate||e.push(`From Date is required`),J.value.toDate||e.push(`To Date is required`),J.value.fromDate&&J.value.toDate&&(J.value.fromDate>J.value.toDate&&e.push(`From Date must be before To Date`),J.value.toDate>Y&&e.push(`To Date cannot be in the future`)),e}),Z=u(()=>{if(!J.value.fromDate||!J.value.toDate)return 0;let e=new Date(J.value.fromDate),t=new Date(J.value.toDate);return Math.ceil((t.getTime()-e.getTime())/864e5)}),Q=u(()=>J.value.dataSource===`KRX`?(Z.value*2e3).toLocaleString():J.value.dataSource===`OpenDart`?(Math.ceil(Z.value/90)*200).toLocaleString():`0`),$=e=>{let t=new Date,n=new Date;e===`1y`?n.setFullYear(n.getFullYear()-1):e===`2y`?n.setFullYear(n.getFullYear()-2):e===`5y`?n.setFullYear(n.getFullYear()-5):e===`all`&&n.setFullYear(2015),J.value.fromDate=n.toISOString().split(`T`)[0],J.value.toDate=t.toISOString().split(`T`)[0]},ne=async()=>{if(!(X.value.length>0)){y.value=!0;try{let e=await fetch(`/api/market/ingest`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-KArtSell-User":`ingestion-user`,"X-KArtSell-Role":`DataAdmin`},body:JSON.stringify({dataSource:J.value.dataSource,fromDate:J.value.fromDate,toDate:J.value.toDate})});if(!e.ok)throw Error(`HTTP ${e.status}`);let t=await e.json();q.value=t.jobId,setTimeout(()=>{J.value.fromDate=new Date(Date.now()-31536e6).toISOString().split(`T`)[0],J.value.toDate=new Date().toISOString().split(`T`)[0],q.value=null},5e3)}catch(e){console.error(`Ingestion error:`,e),alert(`Failed to trigger ingestion: ${e instanceof Error?e.message:`Unknown error`}`)}finally{y.value=!1}}},re=()=>{J.value={dataSource:`KRX`,fromDate:new Date(Date.now()-31536e6).toISOString().split(`T`)[0],toDate:new Date().toISOString().split(`T`)[0]},q.value=null};return(o,s)=>{let u=n(`router-link`);return r(),h(`div`,b,[s[25]||=m(`div`,{class:`header`},[m(`h1`,null,`📊 Market Data Ingestion`),m(`p`,{class:`subtitle`},`Schedule KRX historical data collection`)],-1),m(`div`,x,[m(`div`,S,[s[12]||=m(`h2`,null,`1. Select Data Source & Period`,-1),m(`div`,C,[s[8]||=m(`label`,null,`Data Source`,-1),a(m(`select`,{"onUpdate:modelValue":s[0]||=e=>J.value.dataSource=e},[...s[7]||=[m(`option`,{value:`KRX`},`KRX (Korea Exchange) - KOSPI/KOSDAQ Daily`,-1),m(`option`,{value:`OpenDart`},`OpenDart - Financial Disclosures (T+2)`,-1),m(`option`,{value:`Stub`},`Stub (Test Data)`,-1)]],512),[[g,J.value.dataSource]]),s[9]||=m(`p`,{class:`hint`},[m(`strong`,null,`KRX:`),d(` Stock prices (Open/High/Low/Close/Volume) `),m(`strong`,null,`OpenDart:`),d(` Corporate disclosures & filings `)],-1)]),m(`div`,ee,[m(`div`,w,[s[10]||=m(`label`,null,`From Date`,-1),a(m(`input`,{"onUpdate:modelValue":s[1]||=e=>J.value.fromDate=e,type:`date`,min:K,max:p(Y),placeholder:`YYYY-MM-DD`},null,8,T),[[_,J.value.fromDate]]),m(`p`,{class:`hint`},`Earliest: `+i(K))]),m(`div`,E,[s[11]||=m(`label`,null,`To Date`,-1),a(m(`input`,{"onUpdate:modelValue":s[2]||=e=>J.value.toDate=e,type:`date`,min:J.value.fromDate||K,max:p(Y),placeholder:`YYYY-MM-DD`},null,8,D),[[_,J.value.toDate]]),m(`p`,O,`Latest: `+i(p(Y)),1)])]),m(`div`,k,[m(`button`,{onClick:s[3]||=e=>$(`1y`),class:`preset-btn`},`Last 1 Year`),m(`button`,{onClick:s[4]||=e=>$(`2y`),class:`preset-btn`},`Last 2 Years`),m(`button`,{onClick:s[5]||=e=>$(`5y`),class:`preset-btn`},`Last 5 Years`),m(`button`,{onClick:s[6]||=e=>$(`all`),class:`preset-btn`},`All Available`)])]),X.value.length?(r(),h(`div`,A,[s[13]||=m(`h3`,null,`⚠️ Validation Errors`,-1),m(`ul`,null,[(r(!0),h(f,null,e(X.value,(e,t)=>(r(),h(`li`,{key:t},i(e),1))),128))])])):c(``,!0),X.value.length?c(``,!0):(r(),h(`div`,j,[s[18]||=m(`h3`,null,`📋 Collection Summary`,-1),m(`div`,M,[m(`div`,N,[s[14]||=m(`span`,{class:`label`},`Data Source:`,-1),m(`span`,P,i(J.value.dataSource),1)]),m(`div`,F,[s[15]||=m(`span`,{class:`label`},`Period:`,-1),m(`span`,I,i(J.value.fromDate)+` to `+i(J.value.toDate),1)]),m(`div`,L,[s[16]||=m(`span`,{class:`label`},`Days:`,-1),m(`span`,R,i(Z.value),1)]),m(`div`,te,[s[17]||=m(`span`,{class:`label`},`Est. Rows:`,-1),m(`span`,z,i(Q.value),1)])])])),m(`div`,B,[m(`button`,{onClick:ne,disabled:y.value||X.value.length>0,class:`btn-primary`},[y.value?(r(),h(`span`,U,`⏳ Processing...`)):(r(),h(`span`,H,`🚀 Schedule Ingestion`))],8,V),m(`button`,{onClick:re,class:`btn-secondary`},`↻ Reset`)]),q.value?(r(),h(`div`,W,[s[24]||=m(`h3`,null,`✅ Job Scheduled Successfully`,-1),m(`div`,G,[m(`p`,null,[s[19]||=m(`strong`,null,`Job ID:`,-1),d(` `+i(q.value),1)]),s[21]||=m(`p`,null,[m(`strong`,null,`Status:`),d(` Queued`)],-1),m(`p`,null,[s[20]||=m(`strong`,null,`Queued At:`,-1),d(` `+i(new Date().toLocaleString()),1)]),s[22]||=m(`p`,{class:`hint`},`The ingestion will run in the background. Check the status in History tab.`,-1)]),l(u,{to:`/ops/market-data-history`,class:`btn-link`},{default:t(()=>[...s[23]||=[d(` 📈 View Collection History → `,-1)]]),_:1})])):c(``,!0)])])}}}),[[`__scopeId`,`data-v-cacefb7c`]]);export{q as default};