mirror of
https://github.com/ferdzo/iotDashboard.git
synced 2026-04-05 09:06:26 +00:00
140 lines
3.8 KiB
Python
140 lines
3.8 KiB
Python
"""
|
|
Configuration management for the database writer service.
|
|
Loads settings from environment variables with sensible defaults.
|
|
"""
|
|
|
|
import os
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
import dotenv
|
|
|
|
dotenv.load_dotenv()
|
|
|
|
|
|
@dataclass
|
|
class RedisConfig:
|
|
"""Redis connection configuration"""
|
|
|
|
host: str
|
|
port: int = 6379
|
|
db: int = 0
|
|
password: Optional[str] = None
|
|
|
|
|
|
@dataclass
|
|
class DatabaseConfig:
|
|
"""Database connection configuration"""
|
|
|
|
url: Optional[str] = None
|
|
host: Optional[str] = None
|
|
port: int = 5432
|
|
name: Optional[str] = None
|
|
user: Optional[str] = None
|
|
password: Optional[str] = None
|
|
table_name: str = "sensor_readings"
|
|
enable_timescale: bool = False
|
|
|
|
def get_connection_string(self) -> str:
|
|
"""Build connection string from components or return URL"""
|
|
if self.url:
|
|
return self.url
|
|
|
|
if not all([self.host, self.name, self.user, self.password]):
|
|
raise ValueError("Either DATABASE_URL or all DB_* variables must be set")
|
|
|
|
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.name}"
|
|
|
|
|
|
@dataclass
|
|
class ConsumerConfig:
|
|
"""Redis consumer group configuration"""
|
|
|
|
group_name: str = "db_writer"
|
|
consumer_name: str = "worker-01"
|
|
batch_size: int = 100
|
|
batch_timeout_sec: int = 5
|
|
processing_interval_sec: float = 1.0
|
|
block_time_ms: int = 5000
|
|
|
|
|
|
@dataclass
|
|
class StreamConfig:
|
|
"""Redis stream configuration"""
|
|
|
|
pattern: str = "mqtt_stream:*"
|
|
dead_letter_stream: str = "mqtt_stream:failed"
|
|
max_retries: int = 3
|
|
trim_maxlen: int = 10000 # Keep last N messages in each stream
|
|
|
|
|
|
@dataclass
|
|
class LogConfig:
|
|
"""Logging configuration"""
|
|
|
|
level: str = "INFO"
|
|
format: str = "json" # json or console
|
|
|
|
|
|
class Config:
|
|
"""Main configuration class"""
|
|
|
|
def __init__(self):
|
|
self.redis = RedisConfig(
|
|
host=os.getenv("REDIS_HOST", "localhost"),
|
|
port=int(os.getenv("REDIS_PORT", 6379)),
|
|
db=int(os.getenv("REDIS_DB", 0)),
|
|
password=os.getenv("REDIS_PASSWORD", None) or None,
|
|
)
|
|
|
|
self.database = DatabaseConfig(
|
|
url=os.getenv("DATABASE_URL", None),
|
|
host=os.getenv("DB_HOST", None),
|
|
port=int(os.getenv("DB_PORT", 5432)),
|
|
name=os.getenv("DB_NAME", None),
|
|
user=os.getenv("DB_USER", None),
|
|
password=os.getenv("DB_PASSWORD", None),
|
|
table_name=os.getenv("TABLE_NAME", "sensor_readings"),
|
|
enable_timescale=os.getenv("ENABLE_TIMESCALE", "false").lower() == "true",
|
|
)
|
|
|
|
self.consumer = ConsumerConfig(
|
|
group_name=os.getenv("CONSUMER_GROUP_NAME", "db_writer"),
|
|
consumer_name=os.getenv("CONSUMER_NAME", "worker-01"),
|
|
batch_size=int(os.getenv("BATCH_SIZE", 100)),
|
|
batch_timeout_sec=int(os.getenv("BATCH_TIMEOUT_SEC", 5)),
|
|
processing_interval_sec=float(os.getenv("PROCESSING_INTERVAL_SEC", 1.0)),
|
|
block_time_ms=int(os.getenv("BLOCK_TIME_MS", 5000)),
|
|
)
|
|
|
|
self.stream = StreamConfig(
|
|
max_retries=int(os.getenv("MAX_RETRIES", 3)),
|
|
trim_maxlen=int(os.getenv("TRIM_MAXLEN", 10000)),
|
|
)
|
|
|
|
self.log = LogConfig(
|
|
level=os.getenv("LOG_LEVEL", "INFO"), format=os.getenv("LOG_FORMAT", "json")
|
|
)
|
|
|
|
def validate(self):
|
|
"""Validate configuration"""
|
|
errors = []
|
|
|
|
if not self.redis.host:
|
|
errors.append("REDIS_HOST is required")
|
|
|
|
try:
|
|
self.database.get_connection_string()
|
|
except ValueError as e:
|
|
errors.append(str(e))
|
|
|
|
if self.consumer.batch_size < 1:
|
|
errors.append("BATCH_SIZE must be >= 1")
|
|
|
|
if errors:
|
|
raise ValueError(f"Configuration errors: {', '.join(errors)}")
|
|
|
|
return True
|
|
|
|
|
|
config = Config()
|