Initial commit
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
"""Environment-backed configuration shared by every service client."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from .errors import ConfigurationError
|
||||
|
||||
|
||||
DEFAULT_ENV_FILE = Path(__file__).resolve().parent.parent / ".env"
|
||||
|
||||
|
||||
def _required(name: str) -> str:
|
||||
value = os.getenv(name, "").strip()
|
||||
if not value:
|
||||
raise ConfigurationError(f".env içinde {name} eksik veya boş.")
|
||||
if value.lower() == "change-me":
|
||||
raise ConfigurationError(f".env içindeki {name} placeholder değeri değiştirilmemiş.")
|
||||
return value
|
||||
|
||||
|
||||
def _positive_float(name: str, default: str) -> float:
|
||||
raw_value = os.getenv(name, default).strip()
|
||||
try:
|
||||
value = float(raw_value)
|
||||
except ValueError as exc:
|
||||
raise ConfigurationError(f"{name} sayısal bir değer olmalıdır.") from exc
|
||||
if value <= 0:
|
||||
raise ConfigurationError(f"{name} sıfırdan büyük olmalıdır.")
|
||||
return value
|
||||
|
||||
|
||||
def _non_negative_int(name: str, default: str) -> int:
|
||||
raw_value = os.getenv(name, default).strip()
|
||||
try:
|
||||
value = int(raw_value)
|
||||
except ValueError as exc:
|
||||
raise ConfigurationError(f"{name} tam sayı olmalıdır.") from exc
|
||||
if value < 0:
|
||||
raise ConfigurationError(f"{name} negatif olamaz.")
|
||||
return value
|
||||
|
||||
|
||||
def env_bool(name: str, default: bool = False) -> bool:
|
||||
raw_value = os.getenv(name)
|
||||
if raw_value is None or not raw_value.strip():
|
||||
return default
|
||||
normalized = raw_value.strip().lower()
|
||||
if normalized in {"1", "true", "yes", "on"}:
|
||||
return True
|
||||
if normalized in {"0", "false", "no", "off"}:
|
||||
return False
|
||||
raise ConfigurationError(f"{name} true veya false olmalıdır.")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Settings:
|
||||
env_file: Path
|
||||
base_url: str
|
||||
hmac_secret: str
|
||||
accept_language: str
|
||||
timeout_seconds: float
|
||||
token_expiry_skew_seconds: int
|
||||
max_retries: int
|
||||
retry_base_seconds: float
|
||||
|
||||
@classmethod
|
||||
def from_env(cls, env_file: Path = DEFAULT_ENV_FILE) -> "Settings":
|
||||
env_file = Path(env_file).resolve()
|
||||
if not env_file.is_file():
|
||||
raise ConfigurationError(f".env dosyası bulunamadı: {env_file}")
|
||||
|
||||
load_dotenv(env_file, override=True)
|
||||
environment = _required("IKLIM_API_ENV").lower()
|
||||
url_variables = {
|
||||
"test": "IKLIM_API_TEST_URL",
|
||||
"prod": "IKLIM_API_PROD_URL",
|
||||
}
|
||||
if environment not in url_variables:
|
||||
raise ConfigurationError("IKLIM_API_ENV yalnızca test veya prod olabilir.")
|
||||
|
||||
base_url = _required(url_variables[environment]).rstrip("/")
|
||||
parsed_url = urlsplit(base_url)
|
||||
if parsed_url.scheme != "https" or not parsed_url.netloc:
|
||||
raise ConfigurationError("Seçilen API adresi geçerli bir HTTPS adresi olmalıdır.")
|
||||
|
||||
language = os.getenv("IKLIM_ACCEPT_LANGUAGE", "en").strip().lower() or "en"
|
||||
if language not in {"tr", "en"}:
|
||||
raise ConfigurationError("IKLIM_ACCEPT_LANGUAGE yalnızca tr veya en olabilir.")
|
||||
|
||||
return cls(
|
||||
env_file=env_file,
|
||||
base_url=base_url,
|
||||
hmac_secret=_required("IKLIM_HMAC_SECRET"),
|
||||
accept_language=language,
|
||||
timeout_seconds=_positive_float("IKLIM_TIMEOUT_SECONDS", "30"),
|
||||
token_expiry_skew_seconds=_non_negative_int(
|
||||
"IKLIM_TOKEN_EXPIRY_SKEW_SECONDS", "60"
|
||||
),
|
||||
max_retries=_non_negative_int("IKLIM_MAX_RETRIES", "3"),
|
||||
retry_base_seconds=_positive_float("IKLIM_RETRY_BASE_SECONDS", "2"),
|
||||
)
|
||||
|
||||
def credentials(self) -> tuple[str, str]:
|
||||
return _required("IKLIM_USERNAME"), _required("IKLIM_PASSWORD")
|
||||
Reference in New Issue
Block a user