Files
image-drop/app/config.py
2025-11-23 10:43:16 -07:00

88 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Config loader for the Immich Drop Uploader (Python).
Reads ONLY from .env; there is NO runtime mutation from the UI.
"""
from __future__ import annotations
import os
from dataclasses import dataclass
import secrets
from dotenv import load_dotenv
import hashlib
import binascii
@dataclass
class Settings:
"""App settings loaded from environment variables (.env)."""
admin_password: str
max_concurrent: int
public_upload_page_enabled: bool = False
public_base_url: str = ""
state_db: str = ""
session_secret: str = ""
log_level: str = "INFO"
chunked_uploads_enabled: bool = False
chunk_size_mb: int = 95
timezone: str = "UTC"
def _hash_password(pw: str) -> str:
"""Return PBKDF2-SHA256 hash of a password."""
try:
if not pw:
return ""
salt = os.urandom(16)
iterations = 200_000
dk = hashlib.pbkdf2_hmac('sha256', pw.encode('utf-8'), salt, iterations)
return f"pbkdf2_sha256${iterations}${binascii.hexlify(salt).decode()}${binascii.hexlify(dk).decode()}"
except Exception:
return ""
def load_settings() -> Settings:
"""Load settings from .env, applying defaults when absent."""
# Load environment variables from .env once here so importers dont have to
try:
load_dotenv()
except Exception:
pass
admin_password = os.getenv("ADMIN_PASSWORD", "admin") # Default for convenience, should be changed
if not admin_password.startswith("pbkdf2_sha256$"):
print("="*60)
print("WARNING: ADMIN_PASSWORD is in plaintext.")
print("For better security, use the hashed password below in your .env file:")
hashed_pw = _hash_password(admin_password)
if hashed_pw:
print(f"ADMIN_PASSWORD={hashed_pw}")
print("="*60)
# Safe defaults: disable public uploader and invites unless explicitly enabled
def as_bool(v: str, default: bool = False) -> bool:
if v is None:
return default
return str(v).strip().lower() in {"1","true","yes","on"}
public_upload = as_bool(os.getenv("PUBLIC_UPLOAD_PAGE_ENABLED", "false"), False)
try:
maxc = int(os.getenv("MAX_CONCURRENT", "3"))
except ValueError:
maxc = 3
state_db = os.getenv("STATE_DB", "/data/state.db")
session_secret = os.getenv("SESSION_SECRET") or secrets.token_hex(32)
log_level = os.getenv("LOG_LEVEL", "INFO").upper()
chunked_uploads_enabled = as_bool(os.getenv("CHUNKED_UPLOADS_ENABLED", "false"), False)
try:
chunk_size_mb = int(os.getenv("CHUNK_SIZE_MB", "95"))
except ValueError:
chunk_size_mb = 95
timezone = os.getenv("TIMEZONE", "UTC")
return Settings(
admin_password=admin_password,
max_concurrent=maxc,
public_upload_page_enabled=public_upload,
public_base_url=os.getenv("PUBLIC_BASE_URL", ""),
state_db=state_db,
session_secret=session_secret,
log_level=log_level,
chunked_uploads_enabled=chunked_uploads_enabled,
chunk_size_mb=chunk_size_mb,
timezone=timezone,
)