from pathlib import Path from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict PROJECT_DIR = Path(__file__).resolve().parent # 미디어 파일 저장 디렉토리 MEDIA_ROOT = PROJECT_DIR / "media" MEDIA_ROOT.mkdir(exist_ok=True) _base_config = SettingsConfigDict( env_file=PROJECT_DIR / ".env", env_ignore_empty=True, extra="ignore", ) class ProjectSettings(BaseSettings): PROJECT_NAME: str = Field(default="CastAD") PROJECT_DOMAIN: str = Field(default="localhost:8000") VERSION: str = Field(default="0.1.0") DESCRIPTION: str = Field(default="FastAPI 기반 CastAD 프로젝트") ADMIN_BASE_URL: str = Field(default="/admin") DEBUG: bool = Field(default=True) model_config = _base_config class APIKeySettings(BaseSettings): CHATGPT_API_KEY: str = Field(default="your-chatgpt-api-key") # 기본값 추가 SUNO_API_KEY: str = Field(default="your-suno-api-key") # Suno API 키 SUNO_CALLBACK_URL: str = Field( default="https://example.com/api/suno/callback" ) # Suno 콜백 URL (필수) CREATOMATE_API_KEY: str = Field(default="your-creatomate-api-key") # Creatomate API 키 model_config = _base_config class CORSSettings(BaseSettings): # CORS (Cross-Origin Resource Sharing) 설정 # 요청을 허용할 출처(Origin) 목록 # ["*"]: 모든 출처 허용 (개발 환경용, 프로덕션에서는 구체적인 도메인 지정 권장) # 예: ["https://example.com", "https://app.example.com"] CORS_ALLOW_ORIGINS: list[str] = ["*"] # 자격 증명(쿠키, Authorization 헤더 등) 포함 요청 허용 여부 # True: 클라이언트가 credentials: 'include'로 요청 시 쿠키/인증 정보 전송 가능 # 주의: CORS_ALLOW_ORIGINS가 ["*"]일 때는 보안상 False 권장 CORS_ALLOW_CREDENTIALS: bool = True # 허용할 HTTP 메서드 목록 # ["*"]: 모든 메서드 허용 (GET, POST, PUT, DELETE, PATCH, OPTIONS 등) # 구체적 지정 예: ["GET", "POST", "PUT", "DELETE"] CORS_ALLOW_METHODS: list[str] = ["*"] # 클라이언트가 요청 시 사용할 수 있는 HTTP 헤더 목록 # ["*"]: 모든 헤더 허용 # 구체적 지정 예: ["Content-Type", "Authorization", "X-Custom-Header"] CORS_ALLOW_HEADERS: list[str] = ["*"] # 브라우저의 JavaScript에서 접근 가능한 응답 헤더 목록 # []: 기본 안전 헤더(Cache-Control, Content-Language, Content-Type, # Expires, Last-Modified, Pragma)만 접근 가능 # 추가 노출 필요 시: ["X-Total-Count", "X-Request-Id", "X-Custom-Header"] CORS_EXPOSE_HEADERS: list[str] = [] # Preflight 요청(OPTIONS) 결과를 캐시하는 시간(초) # 600: 10분간 캐시 (이 시간 동안 동일 요청에 대해 preflight 생략) # 0으로 설정 시 매번 preflight 요청 발생 CORS_MAX_AGE: int = 600 model_config = _base_config class DatabaseSettings(BaseSettings): # MySQL 연결 설정 (기본값: 테스트 계정 및 poc DB) MYSQL_HOST: str = Field(default="localhost") MYSQL_PORT: int = Field(default=3306) MYSQL_USER: str = Field(default="test") MYSQL_PASSWORD: str = Field(default="") # 환경변수에서 로드 MYSQL_DB: str = Field(default="poc") # Redis 설정 REDIS_HOST: str = "localhost" REDIS_PORT: int = 6379 model_config = _base_config @property def MYSQL_URL(self) -> str: """비동기 MySQL URL 생성 (asyncmy 드라이버 사용, SQLAlchemy 통합 최적화)""" return f"mysql+asyncmy://{self.MYSQL_USER}:{self.MYSQL_PASSWORD}@{self.MYSQL_HOST}:{self.MYSQL_PORT}/{self.MYSQL_DB}" def REDIS_URL(self, db: int = 0) -> str: """Redis URL 생성 (db 인수로 기본값 지원)""" return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{db}" class CrawlerSettings(BaseSettings): NAVER_COOKIES: str = Field(default="") model_config = _base_config class AzureBlobSettings(BaseSettings): """Azure Blob Storage 설정""" AZURE_BLOB_SAS_TOKEN: str = Field( default="", description="Azure Blob Storage SAS 토큰", ) AZURE_BLOB_BASE_URL: str = Field( default="https://ado2mediastoragepublic.blob.core.windows.net/ado2-media-public-access/ado2-media-original", description="Azure Blob Storage 기본 URL", ) model_config = _base_config class CreatomateSettings(BaseSettings): """Creatomate 템플릿 설정""" # 세로형 템플릿 (기본값) TEMPLATE_ID_VERTICAL: str = Field( default="e8c7b43f-de4b-4ba3-b8eb-5df688569193", description="Creatomate 세로형 템플릿 ID", ) TEMPLATE_DURATION_VERTICAL: float = Field( default=90.0, description="세로형 템플릿 기본 duration (초)", ) # 가로형 템플릿 TEMPLATE_ID_HORIZONTAL: str = Field( default="0f092a6a-f526-4ef0-9181-d4ad4426b9e7", description="Creatomate 가로형 템플릿 ID", ) TEMPLATE_DURATION_HORIZONTAL: float = Field( default=30.0, description="가로형 템플릿 기본 duration (초)", ) model_config = _base_config class PromptSettings(BaseSettings): PROMPT_FOLDER_ROOT : str = Field(default="./app/utils/prompts") MARKETING_PROMPT_NAME : str = Field(default="marketing_prompt") SUMMARIZE_PROMPT_NAME : str = Field(default="summarize_prompt") LYLIC_PROMPT_NAME : str = Field(default="lyric_prompt") model_config = _base_config class RecoverySettings(BaseSettings): """외부 API 복구 및 타임아웃 설정 ChatGPT, Suno, Creatomate API의 타임아웃 및 재시도 설정을 관리합니다. """ # ============================================================ # ChatGPT API 설정 # ============================================================ CHATGPT_TIMEOUT: float = Field( default=600.0, description="ChatGPT API 타임아웃 (초). OpenAI Python SDK 기본값: 600초 (10분)", ) CHATGPT_MAX_RETRIES: int = Field( default=1, description="ChatGPT API 응답 실패 시 최대 재시도 횟수", ) # ============================================================ # Suno API 설정 # ============================================================ SUNO_DEFAULT_TIMEOUT: float = Field( default=30.0, description="Suno API 기본 요청 타임아웃 (초) - 음악 생성 요청, 상태 조회 등", ) SUNO_LYRIC_TIMEOUT: float = Field( default=120.0, description="Suno API 가사 타임스탬프 요청 타임아웃 (초) - 가사 동기화 처리에 더 긴 시간 필요", ) SUNO_MAX_RETRIES: int = Field( default=2, description="Suno API 응답 실패 시 최대 재시도 횟수", ) # ============================================================ # Creatomate API 설정 # ============================================================ CREATOMATE_DEFAULT_TIMEOUT: float = Field( default=30.0, description="Creatomate API 기본 요청 타임아웃 (초) - 템플릿 조회, 상태 조회 등 일반 API 호출", ) CREATOMATE_RENDER_TIMEOUT: float = Field( default=60.0, description="Creatomate API 렌더링 요청 타임아웃 (초) - 영상 렌더링 요청 시 더 긴 시간 필요", ) CREATOMATE_CONNECT_TIMEOUT: float = Field( default=10.0, description="Creatomate API 연결 타임아웃 (초) - 서버 연결 수립까지의 대기 시간", ) CREATOMATE_MAX_RETRIES: int = Field( default=2, description="Creatomate API 응답 실패 시 최대 재시도 횟수", ) model_config = _base_config class KakaoSettings(BaseSettings): """카카오 OAuth 설정""" KAKAO_CLIENT_ID: str = Field(default="", description="카카오 REST API 키") KAKAO_CLIENT_SECRET: str = Field(default="", description="카카오 Client Secret (선택)") KAKAO_REDIRECT_URI: str = Field( default="http://localhost:8000/user/auth/kakao/callback", description="카카오 로그인 후 리다이렉트 URI", ) model_config = _base_config class JWTSettings(BaseSettings): """JWT 토큰 설정""" JWT_SECRET: str = Field( default="your-super-secret-key-must-be-at-least-32-characters-long", description="JWT 서명 비밀키 (최소 32자)", ) JWT_ALGORITHM: str = Field(default="HS256", description="JWT 알고리즘") JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = Field( default=60, description="액세스 토큰 만료 시간 (분)" ) JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = Field( default=7, description="리프레시 토큰 만료 시간 (일)" ) model_config = _base_config class LogSettings(BaseSettings): """ 로깅 설정 클래스 애플리케이션의 로깅 동작을 제어하는 설정들을 관리합니다. 모든 설정은 .env 파일 또는 환경변수로 오버라이드 가능합니다. 사용 예시 (.env 파일): LOG_LEVEL=INFO LOG_CONSOLE_LEVEL=WARNING LOG_FILE_LEVEL=DEBUG LOG_DIR=/var/log/myapp """ # ============================================================ # 로그 디렉토리 설정 # ============================================================ # 로그 파일이 저장될 디렉토리 경로입니다. # - 기본값: 프로젝트 루트의 logs 폴더 # - 운영 환경에서는 /www/log/uvicorn 또는 /var/log/app 등으로 설정 권장 # - 디렉토리가 존재하지 않으면 자동으로 생성됩니다. # - .env 파일에서 LOG_DIR 환경변수로 오버라이드 가능 LOG_DIR: str = Field( default="logs", description="로그 파일 저장 디렉토리 (절대 경로 또는 상대 경로)", ) # ============================================================ # 로그 출력 대상 설정 # ============================================================ # 콘솔 출력 활성화 여부 # - True: 터미널/콘솔에 로그 출력 # - False: 콘솔 출력 비활성화 (파일에만 기록) LOG_CONSOLE_ENABLED: bool = Field( default=True, description="콘솔 로그 출력 활성화 여부", ) # 파일 출력 활성화 여부 # - True: 로그 파일에 기록 (app.log, error.log) # - False: 파일 출력 비활성화 (콘솔에만 출력) LOG_FILE_ENABLED: bool = Field( default=True, description="파일 로그 출력 활성화 여부", ) # ============================================================ # 로그 레벨 설정 # ============================================================ # 로그 레벨 우선순위 (낮음 → 높음): # DEBUG < INFO < WARNING < ERROR < CRITICAL # # 설정된 레벨 이상의 로그만 출력됩니다. # 예: INFO로 설정 시 DEBUG는 무시되고, INFO, WARNING, ERROR, CRITICAL만 출력 # ============================================================ # 기본 로그 레벨 # - 로거 자체의 최소 로그 레벨을 설정합니다. # - 이 레벨보다 낮은 로그는 핸들러(콘솔/파일)로 전달되지 않습니다. # - 가능한 값: DEBUG, INFO, WARNING, ERROR, CRITICAL # - DEBUG: 개발 시 상세 디버깅 정보 (변수 값, 흐름 추적 등) # - INFO: 일반적인 작업 진행 상황 (요청 시작/완료 등) # - WARNING: 잠재적 문제 또는 주의가 필요한 상황 # - ERROR: 오류 발생, 하지만 애플리케이션은 계속 실행 # - CRITICAL: 심각한 오류, 애플리케이션 중단 가능성 LOG_LEVEL: str = Field( default="DEBUG", description="기본 로그 레벨", ) # 콘솔 출력 로그 레벨 # - 터미널/콘솔에 출력되는 로그의 최소 레벨을 설정합니다. # - 개발 환경: DEBUG 권장 (모든 로그 확인) # - 운영 환경: INFO 또는 WARNING 권장 (중요한 정보만 출력) # - LOG_LEVEL보다 낮게 설정해도 LOG_LEVEL이 우선 적용됩니다. LOG_CONSOLE_LEVEL: str = Field( default="DEBUG", description="콘솔 출력 로그 레벨", ) # 파일 출력 로그 레벨 # - 로그 파일에 기록되는 로그의 최소 레벨을 설정합니다. # - 파일에는 더 상세한 로그를 남기고 싶을 때 DEBUG로 설정 # - 파일 저장 위치: logs/{날짜}_{모듈명}.log # - 에러 로그는 별도로 logs/{날짜}_error.log에도 기록됩니다. LOG_FILE_LEVEL: str = Field( default="DEBUG", description="파일 출력 로그 레벨", ) # ============================================================ # 로그 파일 관리 설정 # ============================================================ # 로그 파일 최대 크기 (MB) # - 파일이 이 크기를 초과하면 자동으로 새 파일로 롤오버됩니다. # - 기존 파일은 .1, .2 등의 접미사가 붙어 백업됩니다. # - 예: 15MB 설정 시, 파일이 15MB를 넘으면 새 파일 생성 LOG_MAX_SIZE_MB: int = Field( default=15, description="로그 파일 최대 크기 (MB)", ) # 로그 파일 백업 개수 # - 롤오버 시 보관할 백업 파일의 최대 개수입니다. # - 이 개수를 초과하면 가장 오래된 백업 파일이 삭제됩니다. # - 예: 30 설정 시, 최대 30개의 백업 파일 유지 # - 디스크 용량 관리를 위해 적절한 값 설정 권장 LOG_BACKUP_COUNT: int = Field( default=30, description="로그 파일 백업 개수", ) # ============================================================ # 로그 포맷 설정 # ============================================================ # 사용 가능한 포맷 변수: # {asctime} - 로그 발생 시간 (LOG_DATE_FORMAT 형식) # {levelname} - 로그 레벨 (DEBUG, INFO 등) # {name} - 로거 이름 (home, song 등) # {filename} - 소스 파일명 # {funcName} - 함수명 # {lineno} - 라인 번호 # {message} - 로그 메시지 # {module} - 모듈명 # {pathname} - 파일 전체 경로 # # 포맷 예시: # "[{asctime}] {levelname} {message}" # 출력: [2024-01-14 15:30:00] INFO 서버 시작 # ============================================================ # 콘솔 로그 포맷 # - 터미널에 출력되는 로그의 형식을 지정합니다. # - [{levelname}]은 로그 레벨을 대괄호로 감싸서 출력합니다. LOG_CONSOLE_FORMAT: str = Field( default="[{asctime}] [{levelname}] [{name}:{funcName}:{lineno}] {message}", description="콘솔 로그 포맷", ) # 파일 로그 포맷 # - 파일에 기록되는 로그의 형식을 지정합니다. # - 파일에는 더 상세한 정보(filename 등)를 포함할 수 있습니다. LOG_FILE_FORMAT: str = Field( default="[{asctime}] [{levelname}] [{filename}:{name} -> {funcName}():{lineno}] {message}", description="파일 로그 포맷", ) # 날짜 포맷 # - {asctime}에 표시되는 시간의 형식을 지정합니다. # - Python strftime 형식을 따릅니다. # - 예시: # "%Y-%m-%d %H:%M:%S" -> 2024-01-14 15:30:00 # "%Y/%m/%d %H:%M:%S.%f" -> 2024/01/14 15:30:00.123456 # "%d-%b-%Y %H:%M:%S" -> 14-Jan-2024 15:30:00 LOG_DATE_FORMAT: str = Field( default="%Y-%m-%d %H:%M:%S", description="로그 날짜 포맷", ) model_config = _base_config def get_log_dir(self) -> Path: """ 로그 디렉토리 경로를 반환합니다. 우선순위: 1. .env의 LOG_DIR 설정값 (절대 경로인 경우) 2. /www/log/uvicorn 폴더가 존재하면 사용 (운영 서버) 3. 프로젝트 루트의 logs 폴더 (개발 환경 기본값) Returns: Path: 로그 디렉토리 경로 (존재하지 않으면 자동 생성) """ # 1. .env에서 설정한 경로가 절대 경로인 경우 우선 사용 log_dir_path = Path(self.LOG_DIR) if log_dir_path.is_absolute(): log_dir_path.mkdir(parents=True, exist_ok=True) return log_dir_path # 2. 운영 서버 경로 확인 (/www/log/uvicorn) production_log_dir = Path("/www/log/uvicorn") if production_log_dir.exists(): return production_log_dir # 3. 기본값: 프로젝트 루트의 logs 폴더 default_log_dir = PROJECT_DIR / self.LOG_DIR default_log_dir.mkdir(parents=True, exist_ok=True) return default_log_dir prj_settings = ProjectSettings() apikey_settings = APIKeySettings() db_settings = DatabaseSettings() cors_settings = CORSSettings() crawler_settings = CrawlerSettings() azure_blob_settings = AzureBlobSettings() creatomate_settings = CreatomateSettings() prompt_settings = PromptSettings() log_settings = LogSettings() kakao_settings = KakaoSettings() jwt_settings = JWTSettings() recovery_settings = RecoverySettings()