Files
TK-BOM-Project/backend/app/config.py
Hyungi Ahn 83b90ef05c
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
feat: 자재 관리 페이지 대규모 개선
- 파이프 수량 계산 로직 수정 (단관 개수가 아닌 실제 길이 기반 계산)
- UI 전면 개편 (DevonThink 스타일의 간결하고 세련된 디자인)
- 자재별 그룹핑 로직 개선:
  * 플랜지: 동일 사양별 그룹핑, WN 스케줄 표시, ORIFICE 풀네임 표시
  * 피팅: 상세 타입 표시 (니플 길이, 엘보 각도/연결, 티 타입, 리듀서 타입 등)
  * 밸브: 동일 사양별 그룹핑, 타입/연결방식/압력 표시
  * 볼트: 크기/재질/길이별 그룹핑 (8SET → 개별 집계)
  * 가스켓: 동일 사양별 그룹핑, 재질/상세내역/두께 분리 표시
  * UNKNOWN: 원본 설명 전체 표시, 동일 항목 그룹핑
- 전체 카테고리 버튼 제거 (표시 복잡도 감소)
- 카테고리별 동적 컬럼 헤더 및 레이아웃 적용
2025-09-09 09:24:45 +09:00

287 lines
11 KiB
Python

"""
TK-MP-Project 설정 관리
환경별 설정을 중앙화하여 관리
"""
import os
from typing import List, Optional, Dict, Any
from pathlib import Path
from pydantic_settings import BaseSettings
from pydantic import Field, validator
import json
class DatabaseSettings(BaseSettings):
"""데이터베이스 설정"""
url: str = Field(
default="postgresql://tkmp_user:tkmp_password_2025@postgres:5432/tk_mp_bom",
description="데이터베이스 연결 URL"
)
pool_size: int = Field(default=10, description="연결 풀 크기")
max_overflow: int = Field(default=20, description="최대 오버플로우")
pool_timeout: int = Field(default=30, description="연결 타임아웃 (초)")
pool_recycle: int = Field(default=3600, description="연결 재활용 시간 (초)")
echo: bool = Field(default=False, description="SQL 로그 출력 여부")
class Config:
env_prefix = "DB_"
class RedisSettings(BaseSettings):
"""Redis 설정"""
url: str = Field(default="redis://redis:6379", description="Redis 연결 URL")
max_connections: int = Field(default=20, description="최대 연결 수")
socket_timeout: int = Field(default=5, description="소켓 타임아웃 (초)")
socket_connect_timeout: int = Field(default=5, description="연결 타임아웃 (초)")
retry_on_timeout: bool = Field(default=True, description="타임아웃 시 재시도")
decode_responses: bool = Field(default=False, description="응답 디코딩 여부")
class Config:
env_prefix = "REDIS_"
class SecuritySettings(BaseSettings):
"""보안 설정"""
cors_origins: List[str] = Field(default=[], description="CORS 허용 도메인")
cors_methods: List[str] = Field(
default=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
description="CORS 허용 메서드"
)
cors_headers: List[str] = Field(
default=["*"],
description="CORS 허용 헤더"
)
cors_credentials: bool = Field(default=True, description="CORS 자격증명 허용")
# 파일 업로드 보안
max_file_size: int = Field(default=50 * 1024 * 1024, description="최대 파일 크기 (bytes)")
allowed_file_extensions: List[str] = Field(
default=['.xlsx', '.xls', '.csv'],
description="허용된 파일 확장자"
)
upload_path: str = Field(default="uploads", description="업로드 경로")
# API 보안
api_key_header: str = Field(default="X-API-Key", description="API 키 헤더명")
rate_limit_per_minute: int = Field(default=100, description="분당 요청 제한")
class Config:
env_prefix = "SECURITY_"
class LoggingSettings(BaseSettings):
"""로깅 설정"""
level: str = Field(default="INFO", description="로그 레벨")
file_path: str = Field(default="logs/app.log", description="로그 파일 경로")
max_file_size: int = Field(default=10 * 1024 * 1024, description="로그 파일 최대 크기")
backup_count: int = Field(default=5, description="백업 파일 수")
format: str = Field(
default="%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s",
description="로그 포맷"
)
date_format: str = Field(default="%Y-%m-%d %H:%M:%S", description="날짜 포맷")
# 환경별 로그 레벨
development_level: str = Field(default="DEBUG", description="개발 환경 로그 레벨")
production_level: str = Field(default="INFO", description="운영 환경 로그 레벨")
test_level: str = Field(default="WARNING", description="테스트 환경 로그 레벨")
class Config:
env_prefix = "LOG_"
class PerformanceSettings(BaseSettings):
"""성능 설정"""
# 캐시 설정
cache_ttl_default: int = Field(default=3600, description="기본 캐시 TTL (초)")
cache_ttl_files: int = Field(default=300, description="파일 목록 캐시 TTL")
cache_ttl_materials: int = Field(default=600, description="자재 목록 캐시 TTL")
cache_ttl_jobs: int = Field(default=1800, description="작업 목록 캐시 TTL")
cache_ttl_classification: int = Field(default=3600, description="분류 결과 캐시 TTL")
cache_ttl_statistics: int = Field(default=900, description="통계 데이터 캐시 TTL")
# 파일 처리 설정
chunk_size: int = Field(default=1000, description="파일 처리 청크 크기")
max_workers: int = Field(default=4, description="최대 워커 수")
memory_limit_mb: int = Field(default=512, description="메모리 제한 (MB)")
class Config:
env_prefix = "PERF_"
class Settings(BaseSettings):
"""메인 애플리케이션 설정"""
# 기본 설정
app_name: str = Field(default="TK-MP BOM Management API", description="애플리케이션 이름")
app_version: str = Field(default="1.0.0", description="애플리케이션 버전")
app_description: str = Field(
default="자재 분류 및 프로젝트 관리 시스템",
description="애플리케이션 설명"
)
debug: bool = Field(default=False, description="디버그 모드")
# 환경 설정
environment: str = Field(
default="development",
description="실행 환경 (development, production, test, synology)"
)
# 서버 설정
host: str = Field(default="0.0.0.0", description="서버 호스트")
port: int = Field(default=8000, description="서버 포트")
reload: bool = Field(default=False, description="자동 재로드")
workers: int = Field(default=1, description="워커 프로세스 수")
# 하위 설정들
database: DatabaseSettings = Field(default_factory=DatabaseSettings)
redis: RedisSettings = Field(default_factory=RedisSettings)
security: SecuritySettings = Field(default_factory=SecuritySettings)
logging: LoggingSettings = Field(default_factory=LoggingSettings)
performance: PerformanceSettings = Field(default_factory=PerformanceSettings)
# 추가 설정
timezone: str = Field(default="Asia/Seoul", description="시간대")
language: str = Field(default="ko", description="기본 언어")
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
case_sensitive = False
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._setup_environment_specific_settings()
self._setup_cors_origins()
self._validate_settings()
@validator('environment')
def validate_environment(cls, v):
"""환경 값 검증"""
allowed_environments = ['development', 'production', 'test', 'synology']
if v not in allowed_environments:
raise ValueError(f'Environment must be one of: {allowed_environments}')
return v
@validator('port')
def validate_port(cls, v):
"""포트 번호 검증"""
if not 1 <= v <= 65535:
raise ValueError('Port must be between 1 and 65535')
return v
def _setup_environment_specific_settings(self):
"""환경별 특정 설정 적용"""
if self.environment == "development":
self.debug = True
self.reload = True
self.database.echo = True
self.logging.level = self.logging.development_level
elif self.environment == "production":
self.debug = False
self.reload = False
self.database.echo = False
self.logging.level = self.logging.production_level
self.workers = max(2, os.cpu_count() or 1)
elif self.environment == "test":
self.debug = False
self.reload = False
self.database.echo = False
self.logging.level = self.logging.test_level
# 테스트용 인메모리 데이터베이스
self.database.url = "sqlite:///:memory:"
elif self.environment == "synology":
self.debug = False
self.reload = False
self.host = "0.0.0.0"
self.port = 10080
def _setup_cors_origins(self):
"""환경별 CORS origins 설정"""
if not self.security.cors_origins:
cors_config = {
"development": [
"http://localhost:3000",
"http://localhost:5173",
"http://localhost:13000",
"http://127.0.0.1:3000",
"http://127.0.0.1:5173",
"http://127.0.0.1:13000"
],
"production": [
"https://your-domain.com",
"https://api.your-domain.com"
],
"synology": [
"http://192.168.0.3:10173",
"http://localhost:10173"
],
"test": [
"http://testserver"
]
}
self.security.cors_origins = cors_config.get(
self.environment,
cors_config["development"]
)
def _validate_settings(self):
"""설정 검증"""
# 로그 디렉토리 생성
log_dir = Path(self.logging.file_path).parent
log_dir.mkdir(parents=True, exist_ok=True)
# 업로드 디렉토리 생성
upload_dir = Path(self.security.upload_path)
upload_dir.mkdir(parents=True, exist_ok=True)
def get_database_url(self) -> str:
"""데이터베이스 URL 반환"""
return self.database.url
def get_redis_url(self) -> str:
"""Redis URL 반환"""
return self.redis.url
def is_development(self) -> bool:
"""개발 환경 여부"""
return self.environment == "development"
def is_production(self) -> bool:
"""운영 환경 여부"""
return self.environment == "production"
def is_test(self) -> bool:
"""테스트 환경 여부"""
return self.environment == "test"
def get_cors_config(self) -> Dict[str, Any]:
"""CORS 설정 반환"""
return {
"allow_origins": self.security.cors_origins,
"allow_methods": self.security.cors_methods,
"allow_headers": self.security.cors_headers,
"allow_credentials": self.security.cors_credentials
}
def to_dict(self) -> Dict[str, Any]:
"""설정을 딕셔너리로 변환"""
return self.dict()
def save_to_file(self, file_path: str):
"""설정을 파일로 저장"""
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(self.to_dict(), f, indent=2, ensure_ascii=False, default=str)
# 전역 설정 인스턴스
settings = Settings()
def get_settings() -> Settings:
"""설정 인스턴스 반환"""
return settings