Coverage for src / mysingle / core / config.py: 0%
107 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-02 00:58 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-02 00:58 +0900
1"""Common configuration settings for all microservices."""
3from typing import Literal, Self
5from pydantic import EmailStr, Field, computed_field, model_validator
6from pydantic_settings import BaseSettings, SettingsConfigDict
9class CommonSettings(BaseSettings):
10 """Common settings for all microservices."""
12 model_config = SettingsConfigDict(
13 env_file="../../.env",
14 env_file_encoding="utf-8",
15 case_sensitive=True,
16 extra="ignore",
17 )
19 # PROJECT INFORMATION
20 PROJECT_NAME: str = "My Project"
21 ENVIRONMENT: str = "development"
22 DEBUG: bool = True
23 DEV_MODE: bool = True
24 MOCK_DATABASE: bool = False
26 AUDIT_LOGGING_ENABLED: bool = True
28 FRONTEND_URL: str = "http://localhost:3000"
30 # INITIAL SUPERUSER CREDENTIAL SETTINGS
31 SUPERUSER_EMAIL: EmailStr = "your_email@example.com"
32 SUPERUSER_PASSWORD: str = "change-this-admin-password"
33 SUPERUSER_FULLNAME: str = "Admin User"
35 # TEST USER CREDENTIAL SETTINGS (development/local only)
36 TEST_USER_EMAIL: str = "test_user"
37 TEST_USER_PASSWORD: str = "1234"
38 TEST_USER_FULLNAME: str = "Test User"
40 TEST_ADMIN_EMAIL: str = "test_admin"
41 TEST_ADMIN_PASSWORD: str = "1234"
42 TEST_ADMIN_FULLNAME: str = "Test Admin"
44 AUTH_APP_VERSION: str = "0.1.0" # MySingle Auth 패키지 내부용
46 AUTH_PUBLIC_PATHS: list[str] = [
47 "/api/v1/auth/login",
48 "/api/v1/auth/register",
49 "/api/v1/auth/verify-email",
50 "/api/v1/auth/reset-password",
51 "/api/v1/oauth2/google/authorize",
52 "/api/v1/oauth2/google/callback",
53 "/api/v1/oauth2/kakao/authorize",
54 "/api/v1/oauth2/kakao/callback",
55 "/api/v1/oauth2/naver/authorize",
56 "/api/v1/oauth2/naver/callback",
57 ]
59 # DATABASE SETTINGS
60 MONGODB_SERVER: str = "localhost:27017"
61 MONGODB_USERNAME: str = "root"
62 MONGODB_PASSWORD: str = "example"
63 REDIS_URL: str = "redis://localhost:6379/0"
64 REDIS_PASSWORD: str = "change-this-redis-password"
65 # USER CACHE SETTINGS
66 USER_CACHE_TTL_SECONDS: int = 300
67 USER_CACHE_KEY_PREFIX: str = "user"
69 TOKEN_TRANSPORT_TYPE: Literal["bearer", "cookie", "hybrid"] = "hybrid"
70 HTTPONLY_COOKIES: bool = False
71 SAMESITE_COOKIES: Literal["lax", "strict", "none"] = "lax"
72 ALGORITHM: str = "HS256"
73 DEFAULT_AUDIENCE: str = "your-audience"
75 ACCESS_TOKEN_EXPIRE_MINUTES: int = 1440
76 REFRESH_TOKEN_EXPIRE_DAYS: int = 30
77 SERVICE_TOKEN_EXPIRE_MINUTES: int = 10
78 RESET_TOKEN_EXPIRE_MINUTES: int = 60
79 VERIFY_TOKEN_EXPIRE_MINUTES: int = 60
80 EMAIL_TOKEN_EXPIRE_HOURS: int = 48
82 # API Settings
83 CORS_ORIGINS: list[str] = Field(
84 default=["http://localhost:3000", "http://localhost:8000"],
85 description="CORS origins",
86 )
88 # HTTP CLIENT SETTINGS
89 HTTP_CLIENT_TIMEOUT: float = 30.0
90 HTTP_CLIENT_MAX_CONNECTIONS: int = 100
91 HTTP_CLIENT_MAX_KEEPALIVE: int = 20
92 HTTP_CLIENT_MAX_RETRIES: int = 3
93 HTTP_CLIENT_RETRY_DELAY: float = 1.0
95 @property
96 def all_cors_origins(self) -> list[str]:
97 """Get all CORS origins including environment-specific ones."""
98 origins = self.CORS_ORIGINS.copy()
100 # Add localhost variants for development
101 if self.ENVIRONMENT in ["development", "local"]:
102 dev_origins = [
103 "http://localhost:3000",
104 "http://localhost:8000",
105 "http://localhost:8080",
106 "http://127.0.0.1:3000",
107 "http://127.0.0.1:8000",
108 "http://127.0.0.1:8080",
109 ]
110 for origin in dev_origins:
111 if origin not in origins:
112 origins.append(origin)
114 return origins
116 # API GATEWAY SETTINGS
117 USE_API_GATEWAY: bool = True
118 API_GATEWAY_URL: str = "http://localhost:8000"
120 KONG_JWT_SECRET_FRONTEND: str = "change-this-frontend-jwt-secret"
121 KONG_JWT_SECRET_IAM: str = "change-this-iam-service-jwt-secret"
122 KONG_JWT_SECRET_STRATEGY: str = "change-this-strategy-service-jwt-secret"
123 KONG_JWT_SECRET_BACKTEST: str = "change-this-backtest-service-jwt-secret"
124 KONG_JWT_SECRET_INDICATOR: str = "change-this-indicator-service-jwt-secret"
125 KONG_JWT_SECRET_OPTIMIZATION: str = "change-this-optimization-service-jwt-secret"
126 KONG_JWT_SECRET_DASHBOARD: str = "change-this-dashboard-service-jwt-secret"
127 KONG_JWT_SECRET_NOTIFICATION: str = "change-this-notification-service-jwt-secret"
128 KONG_JWT_SECRET_MARKET_DATA: str = "change-this-market-data-service-jwt-secret"
129 KONG_JWT_SECRET_GENAI: str = "change-this-genai-service-jwt-secret"
130 KONG_JWT_SECRET_ML: str = "change-this-ml-service-jwt-secret"
132 # SMTP SETTINGS
133 SMTP_TLS: bool = True
134 SMTP_SSL: bool = False
135 SMTP_PORT: int = 587
136 SMTP_HOST: str = "your_smtp_host"
137 SMTP_USER: str = "your_smtp_user"
138 SMTP_PASSWORD: str | None = None
139 EMAILS_FROM_EMAIL: str = "your_email@example.com"
140 EMAILS_FROM_NAME: str = "Admin Name"
142 @model_validator(mode="after")
143 def _set_default_emails_from(self) -> Self:
144 if not self.EMAILS_FROM_NAME:
145 self.EMAILS_FROM_NAME = self.PROJECT_NAME
146 return self
148 @computed_field
149 def emails_enabled(self) -> bool:
150 return bool(self.SMTP_HOST == "your_smtp_host")
152 # External API Keys
154 # OAUTH2 SETTINGS
155 GOOGLE_CLIENT_ID: str = "your-google-client-id"
156 GOOGLE_CLIENT_SECRET: str = "your-google-client-secret"
157 GOOGLE_OAUTH_SCOPES: list[str] = ["openid", "email", "profile"]
158 OKTA_CLIENT_ID: str = "your-okta-client-id"
159 OKTA_CLIENT_SECRET: str = "your-okta-client-secret"
160 OKTA_DOMAIN: str = "your-okta-domain"
161 KAKAO_CLIENT_ID: str = "your-kakao-client-id"
162 KAKAO_CLIENT_SECRET: str = "your-kakao-client-secret"
163 KAKAO_OAUTH_SCOPES: list[str] = ["profile", "account_email"]
164 NAVER_CLIENT_ID: str = "your-naver-client-id"
165 NAVER_CLIENT_SECRET: str = "your-naver-client-secret"
166 NAVER_OAUTH_SCOPES: list[str] = ["profile", "email"]
169# Global settings instance
170settings = CommonSettings()
173def get_settings() -> CommonSettings:
174 """Get the global settings instance."""
175 return settings
178def get_environment() -> str:
179 """
180 현재 실행 환경 반환
182 Returns:
183 환경 문자열: "development", "testing", "staging", "production"
184 """
185 return settings.ENVIRONMENT.lower()
188def is_production() -> bool:
189 """프로덕션 환경 여부"""
190 return get_environment() == "production"
193def is_development() -> bool:
194 """개발 환경 여부 (development, testing 포함)"""
195 env = get_environment()
196 return env in ["development", "testing", "local"]