cuatrolabs-pos-ms / app /core /config.py
MukeshKapoor25's picture
refactor(routing): externalize API path configuration for API gateway compatibility
518b9dc
"""
Configuration settings for SCM microservice.
Loads environment variables and provides application settings.
"""
import os
from typing import Optional, List
from pydantic import model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Application settings loaded from environment variables"""
# Application
APP_NAME: str = "POS Microservice"
APP_VERSION: str = "1.0.0"
DEBUG: bool = False
ROOT_PATH: str = os.getenv("ROOT_PATH", "")
# MongoDB Configuration
MONGODB_URI: str = os.getenv("MONGODB_URI", "mongodb://localhost:27017")
MONGODB_DB_NAME: str = os.getenv("MONGODB_DB_NAME", "pos_db")
# PostgreSQL Configuration (for trans schema sync)
POSTGRES_HOST: str = os.getenv("DB_HOST", "ep-sweet-surf-a1qeduoy.ap-southeast-1.aws.neon.tech")
POSTGRES_PORT: int = int(os.getenv("DB_PORT", "5432"))
POSTGRES_DB: str = os.getenv("DB_NAME", "cuatrolabs")
POSTGRES_USER: str = os.getenv("DB_USER", "trans_owner")
POSTGRES_PASSWORD: str = os.getenv("DB_PASSWORD", "BookMyService7")
POSTGRES_MIN_POOL_SIZE: int = int(os.getenv("POSTGRES_MIN_POOL_SIZE", "5"))
POSTGRES_MAX_POOL_SIZE: int = int(os.getenv("POSTGRES_MAX_POOL_SIZE", "20"))
POSTGRES_CONNECT_MAX_RETRIES: int = int(os.getenv("POSTGRES_CONNECT_MAX_RETRIES", "20"))
POSTGRES_CONNECT_INITIAL_DELAY_MS: int = int(os.getenv("POSTGRES_CONNECT_INITIAL_DELAY_MS", "500"))
POSTGRES_CONNECT_BACKOFF_MULTIPLIER: float = float(os.getenv("POSTGRES_CONNECT_BACKOFF_MULTIPLIER", "1.5"))
POSTGRES_SSL_MODE: str = os.getenv("DB_SSLMODE", "disable")
POSTGRES_SSL_ROOT_CERT: Optional[str] = os.getenv("POSTGRES_SSL_ROOT_CERT")
POSTGRES_SSL_CERT: Optional[str] = os.getenv("POSTGRES_SSL_CERT")
POSTGRES_SSL_KEY: Optional[str] = os.getenv("POSTGRES_SSL_KEY")
POSTGRES_URI: Optional[str] = None
# Redis Configuration
REDIS_HOST: str = os.getenv("REDIS_HOST", "localhost")
REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379"))
REDIS_PASSWORD: Optional[str] = os.getenv("REDIS_PASSWORD")
REDIS_DB: int = int(os.getenv("REDIS_DB", "0"))
# JWT Configuration
SECRET_KEY: str = os.getenv("SECRET_KEY", "your-secret-key-change-in-production")
ALGORITHM: str = os.getenv("ALGORITHM", "HS256")
TOKEN_EXPIRATION_HOURS: int = int(os.getenv("TOKEN_EXPIRATION_HOURS", "8"))
# OTP Configuration
OTP_TTL_SECONDS: int = int(os.getenv("OTP_TTL_SECONDS", "600"))
OTP_RATE_LIMIT_MAX: int = int(os.getenv("OTP_RATE_LIMIT_MAX", "10"))
OTP_RATE_LIMIT_WINDOW: int = int(os.getenv("OTP_RATE_LIMIT_WINDOW", "600"))
# Twilio Configuration
TWILIO_ACCOUNT_SID: Optional[str] = os.getenv("TWILIO_ACCOUNT_SID")
TWILIO_AUTH_TOKEN: Optional[str] = os.getenv("TWILIO_AUTH_TOKEN")
TWILIO_PHONE_NUMBER: Optional[str] = os.getenv("TWILIO_PHONE_NUMBER")
# SMTP Configuration
SMTP_HOST: Optional[str] = os.getenv("SMTP_HOST")
SMTP_PORT: int = int(os.getenv("SMTP_PORT", "587"))
SMTP_USERNAME: Optional[str] = os.getenv("SMTP_USERNAME")
SMTP_PASSWORD: Optional[str] = os.getenv("SMTP_PASSWORD")
SMTP_FROM_EMAIL: Optional[str] = os.getenv("SMTP_FROM_EMAIL")
SMTP_USE_TLS: bool = os.getenv("SMTP_USE_TLS", "true").lower() == "true"
# Logging
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
# CORS
CORS_ORIGINS: List[str] = [
"http://localhost:3000",
"http://localhost:8000",
]
# Pydantic v2 config
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=True,
extra="allow", # allows extra environment variables without error
)
@model_validator(mode='after')
def assemble_db_connection(self) -> 'Settings':
from urllib.parse import quote_plus
# Prefer DATABASE_URL and DATABASE_URI like TMS settings.py
env_url = (os.getenv("DATABASE_URL") or os.getenv("DATABASE_URI") or "").strip()
if env_url:
self.POSTGRES_URI = env_url
print(f"[CONFIG] Using provided DATABASE_URL/URI")
return self
# Build DSN from individual parts
if all([self.POSTGRES_USER, self.POSTGRES_PASSWORD, self.POSTGRES_HOST, self.POSTGRES_DB]):
protocol = os.getenv("DB_PROTOCOL", "postgresql+asyncpg")
# Ensure no spaces in connection components
user = self.POSTGRES_USER.strip()
host = self.POSTGRES_HOST.strip()
port = str(self.POSTGRES_PORT).strip()
db = self.POSTGRES_DB.strip()
self.POSTGRES_URI = f"{protocol}://{user}:{quote_plus(self.POSTGRES_PASSWORD)}@{host}:{port}/{db}"
print(f"[CONFIG] Built POSTGRES_URI from components")
print(f"[CONFIG] Protocol: {protocol}")
print(f"[CONFIG] User: {self.POSTGRES_USER}")
print(f"[CONFIG] Host: {self.POSTGRES_HOST}")
print(f"[CONFIG] Port: {self.POSTGRES_PORT}")
print(f"[CONFIG] Database: {self.POSTGRES_DB}")
print(f"[CONFIG] Password: {'SET' if self.POSTGRES_PASSWORD else 'EMPTY'}")
print(f"[CONFIG] SSL Mode: {self.POSTGRES_SSL_MODE}")
else:
self.POSTGRES_URI = None
print(f"[CONFIG] ERROR: Cannot build POSTGRES_URI - missing required components")
print(f"[CONFIG] POSTGRES_USER: {'SET' if self.POSTGRES_USER else 'MISSING'}")
print(f"[CONFIG] POSTGRES_PASSWORD: {'SET' if self.POSTGRES_PASSWORD else 'MISSING'}")
print(f"[CONFIG] POSTGRES_HOST: {'SET' if self.POSTGRES_HOST else 'MISSING'}")
print(f"[CONFIG] POSTGRES_DB: {'SET' if self.POSTGRES_DB else 'MISSING'}")
print(f"[CONFIG] Checking alternative env vars:")
print(f"[CONFIG] DB_USER: {os.getenv('DB_USER', 'NOT SET')}")
print(f"[CONFIG] DB_PASSWORD: {'SET' if os.getenv('DB_PASSWORD') else 'NOT SET'}")
print(f"[CONFIG] DB_HOST: {os.getenv('DB_HOST', 'NOT SET')}")
print(f"[CONFIG] DB_NAME: {os.getenv('DB_NAME', 'NOT SET')}")
return self
# Global settings instance
settings = Settings()