AppSetting.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. namespace SharedKernel;
  2. public sealed class AppSettings
  3. {
  4. public AppSection App { get; init; } = new();
  5. public ConnectionStringsSection ConnectionStrings { get; init; } = new();
  6. public RedisSection Redis { get; init; } = new();
  7. public SmtpSection SMTP { get; init; } = new();
  8. public JwtSection JWT { get; init; } = new();
  9. public ForwardedHeadersSection ForwardedHeaders { get; init; } = new();
  10. public CorsPolicySection CorsPolicy { get; init; } = new();
  11. public YouTubeSection YouTube { get; init; } = new();
  12. public BackgroundJobsSection BackgroundJobs { get; init; } = new();
  13. public EncryptionSection Encryption { get; init; } = new();
  14. public FeaturesSection Features { get; init; } = new();
  15. public StockDataSection StockData { get; init; } = new();
  16. public KRXCoKrSection KRXCoKr { get; init; } = new();
  17. public OpenDartSection OpenDart { get; init; } = new();
  18. public KoreaEximSection KoreaExim { get; init; } = new();
  19. public OAuthSection OAuth { get; init; } = new();
  20. public sealed class AppSection
  21. {
  22. public string Name { get; init; } = string.Empty;
  23. public string Company { get; init; } = string.Empty;
  24. public string BaseURL { get; init; } = string.Empty;
  25. public string ApiURL { get; init; } = string.Empty;
  26. public string FrontURL { get; init; } = string.Empty;
  27. }
  28. public sealed class ConnectionStringsSection
  29. {
  30. public string DefaultConnection { get; init; } = string.Empty;
  31. public int Timeout { get; init; }
  32. }
  33. public class RedisSection
  34. {
  35. public string DefaultConnection { get; init; } = string.Empty;
  36. public string CachePrefix { get; init; } = string.Empty;
  37. public string AuthTicketPrefix { get; init; } = string.Empty;
  38. public string DataProtectionKey { get; init; } = string.Empty;
  39. public TimeSpan DefaultKeyLifetime { get; init; }
  40. }
  41. public class SmtpSection
  42. {
  43. public string Host { get; init; } = default!;
  44. public int Port { get; init; } = 587;
  45. public string? User { get; init; }
  46. public string? Password { get; init; }
  47. public bool UseStartTls { get; init; } = true;
  48. public string FromEmail { get; init; } = default!;
  49. public string FromName { get; init; } = "no-reply";
  50. }
  51. public class JwtSection
  52. {
  53. public string SecretKey { get; init; } = string.Empty;
  54. public string Issuer { get; init; } = string.Empty;
  55. public string Audience { get; init; } = string.Empty;
  56. public int AccessTokenExpiration { get; init; }
  57. public int RefreshTokenExpiration { get; init; }
  58. }
  59. public class ForwardedHeadersSection
  60. {
  61. public int? ForwardLimit { get; init; } = default;
  62. public List<string> KnownProxies { get; init; } = [];
  63. public List<string> KnownNetworks { get; init; } = [];
  64. }
  65. public class CorsPolicySection
  66. {
  67. public string Name { get; init; } = string.Empty;
  68. public long PreflightMaxAgeSeconds { get; init; } = 0;
  69. public List<string> AllowedOrigins { get; init; } = [];
  70. }
  71. public class YouTubeSection
  72. {
  73. public string HubUrl { get; init; } = "https://pubsubhubbub.appspot.com/subscribe";
  74. public string CallbackUrl { get; init; } = string.Empty;
  75. public string HmacSecret { get; init; } = string.Empty;
  76. public int FeedPollingIntervalMinutes { get; init; } = 3;
  77. }
  78. public class BackgroundJobsSection
  79. {
  80. public bool LiveChat { get; init; } = true;
  81. public bool PubSubRenewal { get; init; } = true;
  82. public bool FeedPolling { get; init; } = true;
  83. public bool LiveViewerPoller { get; init; } = true;
  84. public bool ChannelCacheRefresh { get; init; } = true;
  85. public bool YouTubeDailyAggregator { get; init; } = true;
  86. public bool YouTubeStaleDataPurge { get; init; } = true;
  87. /// <summary>결제 자동 대사 배치 (Toss NeedsReconciliation 재확인 — PaymentReconcileService). Features:Channel 게이트와 무관.</summary>
  88. public bool PaymentReconcile { get; init; } = true;
  89. /// <summary>모의투자 체결·스냅샷 배치 (PaperFillService — d4 M2). Features:Channel 게이트와 무관, 기본 false (StockData 적재 가동 후 활성화).</summary>
  90. public bool PaperFill { get; init; } = false;
  91. /// <summary>예측 채점 배치 (PredictionSettlementService — d2 M4). Web.Api 전용, 기본 false (StockData T+1 종가 적재 가동 후 활성화).</summary>
  92. public bool PredictionSettlement { get; init; } = false;
  93. }
  94. /// <summary>
  95. /// 기능 단위 활성화 플래그. 코드/스키마는 보존하고 노출만 차단하는 soft-off 스위치.
  96. /// Channel=false 시 채널/스튜디오/YouTube 관련 API 와 백그라운드 서비스 전체 비활성 (기본 false).
  97. /// </summary>
  98. public class FeaturesSection
  99. {
  100. public bool Channel { get; init; } = false;
  101. }
  102. /// <summary>
  103. /// 회원 PII(주소/연락처 등) 필드 암호화에 사용하는 AES-GCM 키 묶음.
  104. /// Keys 딕셔너리는 KeyVersion -> Base64(32바이트) 매핑. CurrentKeyVersion 은 신규 암호화에 사용할 버전.
  105. /// 키 회전 시 Keys 에 새 버전 추가 + CurrentKeyVersion 갱신. 복호화는 row 의 KeyVersion 컬럼으로 매핑하여 과거 키 사용.
  106. /// </summary>
  107. public class EncryptionSection
  108. {
  109. public int CurrentKeyVersion { get; init; } = 1;
  110. public Dictionary<string, string> Keys { get; init; } = new();
  111. }
  112. /// <summary>
  113. /// 소셜 로그인 OAuth 설정 (개미투자 D5 — Naver/Kakao). 키는 각 개발자센터 발급 후 채움 (빈 값이면 해당 provider 로그인 비활성).
  114. /// Google 은 DB Config(External.GoogleClientId) 현행 유지 — 여기 포함하지 않는다.
  115. /// </summary>
  116. public class OAuthSection
  117. {
  118. public OAuthProviderSection Naver { get; init; } = new();
  119. public OAuthProviderSection Kakao { get; init; } = new();
  120. public class OAuthProviderSection
  121. {
  122. /// <summary>네이버: Client ID / 카카오: REST API 키</summary>
  123. public string ClientId { get; init; } = string.Empty;
  124. /// <summary>네이버: Client Secret(필수) / 카카오: 콘솔에서 활성화한 경우만 (빈 값이면 미전송)</summary>
  125. public string ClientSecret { get; init; } = string.Empty;
  126. /// <summary>기본 redirect URI — 요청에서 미전달 시 사용. 개발자센터에 등록한 값과 일치해야 함</summary>
  127. public string RedirectUri { get; init; } = string.Empty;
  128. /// <summary>
  129. /// 카카오 Admin 키 — "연결 끊기(unlink)" webhook 요청의 Authorization 헤더 대조에 사용.
  130. /// 카카오 개발자콘솔의 앱 Admin 키를 그대로 넣는다 (빈 값이면 disconnect 검증 비활성 → 항상 거부).
  131. /// 네이버는 사용하지 않음.
  132. /// </summary>
  133. public string AdminKey { get; init; } = string.Empty;
  134. }
  135. }
  136. /// <summary>
  137. /// 주식 데이터 수집 배치 설정 (개미투자 D1). 플래그 기본 false — API 키 발급/운영 결정 후 활성화.
  138. /// Features:Channel 게이트와 무관하게 별도 등록 (AddStockDataServices).
  139. /// </summary>
  140. public class StockDataSection
  141. {
  142. /// <summary>종목 마스터 동기화 배치 (StockMasterSyncService) 활성화</summary>
  143. public bool MasterSync { get; init; } = false;
  144. /// <summary>T+1 일별 시세 수집 배치 (DailyPriceSyncService) 활성화</summary>
  145. public bool DailyPriceSync { get; init; } = false;
  146. /// <summary>마스터 동기화 실행 시각 (KST, "HH:mm")</summary>
  147. public string MasterSyncTime { get; init; } = "07:30";
  148. /// <summary>일별 시세 수집 실행 시각 (KST, "HH:mm") — 금융위 API 는 영업일+1 13시 이후 반영</summary>
  149. public string DailyPriceSyncTime { get; init; } = "13:10";
  150. public DataGoKrSection DataGoKr { get; init; } = new();
  151. /// <summary>공공데이터포털 (data.go.kr) 금융위 API</summary>
  152. public class DataGoKrSection
  153. {
  154. /// <summary>서비스 키 (Decoding 원본 키 — 요청 시 URL 인코딩). 비어 있으면 배치는 로그만 남기고 skip.</summary>
  155. public string ServiceKey { get; init; } = string.Empty;
  156. public string BaseUrl { get; init; } = "https://apis.data.go.kr";
  157. /// <summary>페이지당 행 수 (numOfRows)</summary>
  158. public int PageSize { get; init; } = 1000;
  159. }
  160. }
  161. /// <summary>
  162. /// KRX 한국거래소 OpenAPI (data-dbg.krx.co.kr) 지수 데이터 수집 설정 (개미투자 D1).
  163. /// data.go.kr(StockDataSection)과는 별개 시스템 — 별도 API 키 + AUTH_KEY 헤더 인증.
  164. /// IndexSync 기본 false — API 키 발급/운영 결정 후 활성화. Features:Channel 게이트와 무관하게 AddStockDataServices 에서 등록.
  165. /// </summary>
  166. public class KRXCoKrSection
  167. {
  168. /// <summary>KRX OpenAPI 인증 키 (AUTH_KEY 헤더). 비어 있으면 배치는 로그만 남기고 skip. tracked 파일엔 빈 값만.</summary>
  169. public string ApiKey { get; init; } = string.Empty;
  170. public string BaseUrl { get; init; } = "https://data-dbg.krx.co.kr";
  171. /// <summary>지수 일별시세 수집 배치 (IndexPriceSyncService) 활성화 — KOSPI/KOSDAQ/KRX + 파생상품지수(idx/drvprod_dd_trd) 4개 엔드포인트 한 배치</summary>
  172. public bool IndexSync { get; init; } = false;
  173. /// <summary>지수 수집 실행 시각 (KST, "HH:mm") — KRX 지수는 장 마감(15:30) 이후 확정, 여유 두고 18:30</summary>
  174. public string IndexSyncTime { get; init; } = "18:30";
  175. /// <summary>채권지수 일별시세 수집 배치 (BondIndexPriceSyncService, idx/bon_dd_trd) 활성화 — 응답 shape 이 지수와 달라 별도 배치</summary>
  176. public bool BondIndexSync { get; init; } = false;
  177. /// <summary>채권지수 수집 실행 시각 (KST, "HH:mm") — 지수 수집(18:30) 뒤 여유 두고 18:35</summary>
  178. public string BondIndexSyncTime { get; init; } = "18:35";
  179. /// <summary>주식(종목) 마스터 + 일별매매 수집 배치 (KrxStockMasterSyncService/KrxDailyPriceSyncService) 활성화 — D1 KRX 주식 데이터 수집</summary>
  180. public bool StockSync { get; init; } = false;
  181. /// <summary>종목 마스터 동기화 실행 시각 (KST, "HH:mm") — 장 시작 전 07:40</summary>
  182. public string MasterSyncTime { get; init; } = "07:40";
  183. /// <summary>주식 일별매매 수집 실행 시각 (KST, "HH:mm") — KRX 일별매매는 장 마감(15:30) 이후 확정, 여유 두고 18:10</summary>
  184. public string StockSyncTime { get; init; } = "18:10";
  185. /// <summary>증권상품(ETF/ETN/ELW) 일별매매 수집 배치 (KrxEtpSyncService) 활성화 — KRX 증권상품(ETF/ETN/ELW) 수집</summary>
  186. public bool EtpSync { get; init; } = false;
  187. /// <summary>증권상품(ETF/ETN/ELW) 일별매매 수집 실행 시각 (KST, "HH:mm") — 장 마감(15:30) 이후 확정, 주식 수집(18:10) 뒤 여유 두고 18:20</summary>
  188. public string EtpSyncTime { get; init; } = "18:20";
  189. /// <summary>신주인수권증권/증서 일별매매 수집 배치 (KrxWarrantSyncService) 활성화 — sw_bydd_trd(증권)+sr_bydd_trd(증서)</summary>
  190. public bool WarrantSync { get; init; } = false;
  191. /// <summary>신주인수권증권/증서 일별매매 수집 실행 시각 (KST, "HH:mm") — 증권상품 수집(18:20) 뒤 여유 두고 18:25</summary>
  192. public string WarrantSyncTime { get; init; } = "18:25";
  193. /// <summary>채권(국채전문유통/일반채권/소액채권) 일별매매 수집 배치 (KrxBondSyncService) 활성화 — kts/bnd/smb_bydd_trd 3개 엔드포인트</summary>
  194. public bool BondSync { get; init; } = false;
  195. /// <summary>채권 일별매매 수집 실행 시각 (KST, "HH:mm") — 신주인수권 수집(18:25) 뒤 여유 두고 18:40</summary>
  196. public string BondSyncTime { get; init; } = "18:40";
  197. /// <summary>파생상품(선물 3 + 옵션 3) 일별매매 수집 배치 (KrxDerivativeSyncService) 활성화 — fut/eqsfu/eqkfu + opt/eqsop/eqkop 6개 엔드포인트</summary>
  198. public bool DerivativeSync { get; init; } = false;
  199. /// <summary>파생상품 일별매매 수집 실행 시각 (KST, "HH:mm") — 채권 수집(18:40) 뒤 여유 두고 18:45</summary>
  200. public string DerivativeSyncTime { get; init; } = "18:45";
  201. /// <summary>일반상품(석유/금/배출권) 일별매매 수집 배치 (KrxCommoditySyncService) 활성화 — gen/oil+gold+ets_bydd_trd 3개 엔드포인트</summary>
  202. public bool CommoditySync { get; init; } = false;
  203. /// <summary>일반상품 일별매매 수집 실행 시각 (KST, "HH:mm") — 파생상품 수집(18:45) 뒤 여유 두고 18:50</summary>
  204. public string CommoditySyncTime { get; init; } = "18:50";
  205. /// <summary>ESG 데이터(ESG 증권상품 + ESG 지수 + 사회책임투자채권) 수집 배치 (KrxEsgSyncService) 활성화 — esg/esg_etp_info+esg_index_info+sri_bond_info 3개 엔드포인트</summary>
  206. public bool EsgSync { get; init; } = false;
  207. /// <summary>ESG 데이터 수집 실행 시각 (KST, "HH:mm") — 일반상품 수집(18:50) 뒤 여유 두고 18:55</summary>
  208. public string EsgSyncTime { get; init; } = "18:55";
  209. /// <summary>백필 대상 기간(연) — endDate 로부터 과거로 이만큼 채운다 (기본 3년). KrxBackfill 공용 헬퍼가 사용</summary>
  210. public int BackfillYears { get; init; } = 3;
  211. /// <summary>1회 실행당 최대 백필 fetch 수 — quota 보호용 상한 (기본 60). 여러 날에 걸쳐 나눠 채운다</summary>
  212. public int BackfillMaxPerRun { get; init; } = 60;
  213. }
  214. /// <summary>
  215. /// OpenDART(전자공시, opendart.fss.or.kr) 공시 수집 설정 (개미투자 D1). KRX/data.go.kr 과는 별개 시스템 —
  216. /// 인증은 query param crtfc_key (헤더 아님). DisclosureSync 기본 false — API 키 발급/운영 결정 후 활성화.
  217. /// Features:Channel 게이트와 무관하게 AddStockDataServices 에서 등록. tracked 파일엔 ApiKey 빈 값만.
  218. /// </summary>
  219. public class OpenDartSection
  220. {
  221. /// <summary>OpenDART 인증 키 (crtfc_key query param). 비어 있으면 배치는 로그만 남기고 skip.</summary>
  222. public string ApiKey { get; init; } = string.Empty;
  223. public string BaseUrl { get; init; } = "https://opendart.fss.or.kr";
  224. /// <summary>공시 목록 수집 배치 (DisclosureSyncService) 활성화</summary>
  225. public bool DisclosureSync { get; init; } = false;
  226. /// <summary>공시 수집 실행 시각 (KST, "HH:mm") — 전영업일 공시 반영 여유 두고 08:30</summary>
  227. public string DisclosureSyncTime { get; init; } = "08:30";
  228. /// <summary>백필 대상 기간(일) — today 로부터 과거로 이만큼 접수일 범위를 훑는다 (기본 30일 — 공시는 건수가 많아 최근 30일 권장)</summary>
  229. public int BackfillDays { get; init; } = 30;
  230. }
  231. /// <summary>
  232. /// 한국수출입은행(koreaexim.go.kr) OpenAPI 거시·환율 수집 설정 (개미투자 D1). KRX/data.go.kr/OpenDART 와 별개 시스템 —
  233. /// 인증은 query param authkey, 데이터 종류별(AP01 환율/AP02 대출금리/AP03 국제금리)로 별도 키가 발급된다.
  234. /// MacroSync 기본 false — API 키 발급/운영 결정 후 활성화. Features:Channel 게이트와 무관하게 AddStockDataServices 에서 등록.
  235. /// tracked 파일엔 각 키 빈 값만 (실키는 gitignore 된 appsettings.Development.json / Jenkins Secret File).
  236. /// 주의(koreaexim quirk): 주말/공휴일과 영업일 오전 ~11시 이전엔 빈 배열([])을 반환하고, 키 오류·만료 시 result 코드 2~4 또는 빈 응답이 온다 → 파서가 안전하게 0건 처리.
  237. /// </summary>
  238. public class KoreaEximSection
  239. {
  240. /// <summary>현재환율(AP01) 인증 키 (authkey query param). 비어 있으면 환율 수집 skip.</summary>
  241. public string ExchangeKey { get; init; } = string.Empty;
  242. /// <summary>대출금리(AP02) 인증 키. 비어 있으면 대출금리 수집 skip.</summary>
  243. public string LoanRateKey { get; init; } = string.Empty;
  244. /// <summary>국제금리(AP03) 인증 키. 비어 있으면 국제금리 수집 skip.</summary>
  245. public string IntlRateKey { get; init; } = string.Empty;
  246. public string BaseUrl { get; init; } = "https://www.koreaexim.go.kr/site/program/financial/exchangeJSON";
  247. /// <summary>거시·환율 수집 배치 (MacroDataSyncService) 활성화</summary>
  248. public bool MacroSync { get; init; } = false;
  249. /// <summary>수집 실행 시각 (KST, "HH:mm") — koreaexim 은 영업일 ~11시 이후 반영되므로 여유 두고 11:30</summary>
  250. public string MacroSyncTime { get; init; } = "11:30";
  251. /// <summary>백필 대상 기간(일) — today 로부터 과거로 이만큼 훑으며 미적재 영업일을 채운다 (기본 30일)</summary>
  252. public int MacroBackfillDays { get; init; } = 30;
  253. /// <summary>1회 실행당 최대 백필 fetch 수 — quota 보호용 상한 (기본 60). 여러 날에 걸쳐 나눠 채운다</summary>
  254. public int BackfillMaxPerRun { get; init; } = 60;
  255. }
  256. }