Completed Tasks (T027-T032): - ✅ FastAPI application with structured logging, CORS, global error handlers - ✅ Pydantic Settings for environment configuration - ✅ SQLAlchemy async engine with session management - ✅ Alembic migration environment setup - ✅ Redis async client with connection pooling - ✅ gRPC SDK Bridge client (placeholder - awaiting protobuf generation) Next: JWT utilities, middleware, database models 🤖 Generated with Claude Code
96 lines
3.0 KiB
Python
96 lines
3.0 KiB
Python
"""
|
|
Configuration management using Pydantic Settings
|
|
Loads configuration from environment variables
|
|
"""
|
|
from pydantic_settings import BaseSettings
|
|
from typing import List
|
|
import os
|
|
|
|
class Settings(BaseSettings):
|
|
"""Application settings loaded from environment variables"""
|
|
|
|
# API Configuration
|
|
API_HOST: str = "0.0.0.0"
|
|
API_PORT: int = 8000
|
|
API_TITLE: str = "Geutebruck Cross-Switching API"
|
|
API_VERSION: str = "1.0.0"
|
|
ENVIRONMENT: str = "development" # development, production
|
|
|
|
# GeViScope SDK Bridge (gRPC)
|
|
SDK_BRIDGE_HOST: str = "localhost"
|
|
SDK_BRIDGE_PORT: int = 50051
|
|
|
|
# GeViServer Connection (used by SDK Bridge)
|
|
GEVISERVER_HOST: str = "localhost"
|
|
GEVISERVER_USERNAME: str = "sysadmin"
|
|
GEVISERVER_PASSWORD: str = "masterkey"
|
|
|
|
# Database (PostgreSQL)
|
|
DATABASE_URL: str = "postgresql+asyncpg://geutebruck:geutebruck@localhost:5432/geutebruck_api"
|
|
DATABASE_POOL_SIZE: int = 20
|
|
DATABASE_MAX_OVERFLOW: int = 10
|
|
|
|
# Redis
|
|
REDIS_HOST: str = "localhost"
|
|
REDIS_PORT: int = 6379
|
|
REDIS_DB: int = 0
|
|
REDIS_PASSWORD: str = ""
|
|
REDIS_MAX_CONNECTIONS: int = 50
|
|
|
|
# JWT Authentication
|
|
JWT_SECRET_KEY: str = "change-this-to-a-secure-random-key-in-production"
|
|
JWT_ALGORITHM: str = "HS256"
|
|
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
|
|
JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = 7
|
|
|
|
# Logging
|
|
LOG_LEVEL: str = "INFO"
|
|
LOG_FORMAT: str = "json" # json or console
|
|
|
|
# Security
|
|
ALLOWED_HOSTS: str = "*"
|
|
CORS_ORIGINS: List[str] = ["http://localhost:3000", "http://localhost:8080"]
|
|
|
|
# Cache Settings
|
|
CACHE_CAMERA_LIST_TTL: int = 60 # seconds
|
|
CACHE_MONITOR_LIST_TTL: int = 60 # seconds
|
|
|
|
# Rate Limiting
|
|
RATE_LIMIT_ENABLED: bool = True
|
|
RATE_LIMIT_PER_MINUTE: int = 60
|
|
|
|
class Config:
|
|
env_file = ".env"
|
|
env_file_encoding = "utf-8"
|
|
case_sensitive = True
|
|
|
|
@property
|
|
def sdk_bridge_url(self) -> str:
|
|
"""Get SDK Bridge gRPC URL"""
|
|
return f"{self.SDK_BRIDGE_HOST}:{self.SDK_BRIDGE_PORT}"
|
|
|
|
@property
|
|
def redis_url(self) -> str:
|
|
"""Get Redis connection URL"""
|
|
if self.REDIS_PASSWORD:
|
|
return f"redis://:{self.REDIS_PASSWORD}@{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
|
|
return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
|
|
|
|
def get_cors_origins(self) -> List[str]:
|
|
"""Parse CORS origins (handles both list and comma-separated string)"""
|
|
if isinstance(self.CORS_ORIGINS, list):
|
|
return self.CORS_ORIGINS
|
|
return [origin.strip() for origin in self.CORS_ORIGINS.split(",")]
|
|
|
|
# Create global settings instance
|
|
settings = Settings()
|
|
|
|
# Validate critical settings on import
|
|
if settings.ENVIRONMENT == "production":
|
|
if settings.JWT_SECRET_KEY == "change-this-to-a-secure-random-key-in-production":
|
|
raise ValueError("JWT_SECRET_KEY must be changed in production!")
|
|
|
|
if settings.GEVISERVER_PASSWORD == "masterkey":
|
|
import warnings
|
|
warnings.warn("Using default GeViServer password in production!")
|