fix: DB password — read from secrets file, bypasses Docker Compose interpolation
- Add secrets/db_password file support to docker-compose.yml (Docker secrets mount) - config.py reads POSTGRES_PASSWORD_FILE if set, builds DATABASE_URL with proper URL encoding - Remove inline DATABASE_URL construction from docker-compose.yml (was subject to $VAR interpolation) - Any password with any characters now works — no escaping needed Authored by: Jack Levy
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from urllib.parse import quote as urlquote
|
||||
from pydantic import model_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
@@ -19,9 +21,18 @@ class Settings(BaseSettings):
|
||||
# Falls back to JWT_SECRET_KEY derivation if not set (not recommended for production)
|
||||
ENCRYPTION_SECRET_KEY: str = ""
|
||||
|
||||
# Database
|
||||
DATABASE_URL: str = "postgresql+asyncpg://congress:congress@postgres:5432/pocketveto"
|
||||
SYNC_DATABASE_URL: str = "postgresql://congress:congress@postgres:5432/pocketveto"
|
||||
# Database — built automatically from components (supports any characters in password)
|
||||
POSTGRES_USER: str = "congress"
|
||||
POSTGRES_PASSWORD: str = "congress"
|
||||
POSTGRES_DB: str = "pocketveto"
|
||||
POSTGRES_HOST: str = "postgres"
|
||||
POSTGRES_PORT: int = 5432
|
||||
# Path to a file containing the raw DB password (any chars, no escaping needed).
|
||||
# When set, this takes priority over POSTGRES_PASSWORD.
|
||||
POSTGRES_PASSWORD_FILE: str = ""
|
||||
# Override these only if connecting to an external DB not managed by Docker Compose
|
||||
DATABASE_URL: str = ""
|
||||
SYNC_DATABASE_URL: str = ""
|
||||
|
||||
# Redis
|
||||
REDIS_URL: str = "redis://redis:6379/0"
|
||||
@@ -61,12 +72,22 @@ class Settings(BaseSettings):
|
||||
PYTRENDS_ENABLED: bool = True
|
||||
|
||||
@model_validator(mode="after")
|
||||
def check_secrets(self) -> "Settings":
|
||||
def check_secrets_and_build_db_url(self) -> "Settings":
|
||||
if self.JWT_SECRET_KEY == "change-me-in-production":
|
||||
raise ValueError(
|
||||
"JWT_SECRET_KEY must be set to a secure random value in .env. "
|
||||
"Generate one with: python -c \"import secrets; print(secrets.token_hex(32))\""
|
||||
)
|
||||
if not self.DATABASE_URL:
|
||||
if self.POSTGRES_PASSWORD_FILE and os.path.isfile(self.POSTGRES_PASSWORD_FILE):
|
||||
with open(self.POSTGRES_PASSWORD_FILE) as f:
|
||||
raw_pw = f.read().strip()
|
||||
else:
|
||||
raw_pw = self.POSTGRES_PASSWORD
|
||||
pw = urlquote(raw_pw, safe="")
|
||||
base = f"{self.POSTGRES_USER}:{pw}@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
|
||||
self.DATABASE_URL = f"postgresql+asyncpg://{base}"
|
||||
self.SYNC_DATABASE_URL = f"postgresql://{base}"
|
||||
return self
|
||||
|
||||
# SMTP (Email notifications)
|
||||
|
||||
Reference in New Issue
Block a user