Files
bellsystems-cp/backend/config.py
T
bonaminandClaude Opus 5.5 81365eed89 feat(mqtt-auth): put legacy "vesper" password behind MQTT_ALLOW_LEGACY_PASSWORD
The shared legacy password is still needed for boards on pre-HMAC
firmware, but it was accepted for any username. Now:
- controlled by MQTT_ALLOW_LEGACY_PASSWORD (config.py, default true;
  documented in .env.example) so it can be switched off without a deploy,
- only accepted for device-shaped usernames (uppercase alphanumeric
  segments joined by "-", optional "-kiosk"), never for app_ users or
  any other shape,
- every successful legacy login is logged at WARNING with the username,
  rate-limited to once per username per hour, so the boards still
  depending on it are visible before the flag is turned off.

HMAC auth is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:03:23 +03:00

94 lines
3.2 KiB
Python

from pydantic_settings import BaseSettings
from typing import List, Dict, Any
import json
class Settings(BaseSettings):
# Firebase
firebase_service_account_path: str = "./firebase-service-account.json"
firebase_storage_bucket: str = ""
# JWT
jwt_secret_key: str = "change-me-in-production"
jwt_algorithm: str = "HS256"
jwt_expiration_minutes: int = 480
# MQTT
mqtt_broker_host: str = "localhost"
mqtt_broker_port: int = 1883
mqtt_admin_username: str = "admin"
mqtt_admin_password: str = ""
mqtt_secret: str = "change-me-in-production"
# Accept the old shared "vesper" password for device-shaped usernames
# (boards on pre-HMAC firmware). Turn off once the logs show no users.
mqtt_allow_legacy_password: bool = True
mosquitto_password_file: str = "/etc/mosquitto/passwd"
mqtt_client_id: str = "bellsystems-admin-panel"
# SQLite (local application database)
sqlite_db_path: str = "./data/database.db"
mqtt_data_retention_days: int = 90
# Postgres
database_url: str = "postgresql+asyncpg://bellsystems_user:password@postgres:5432/bellsystems_db"
# Local file storage
built_melodies_storage_path: str = "./storage/built_melodies"
firmware_storage_path: str = "./storage/firmware"
flash_assets_storage_path: str = "./storage/flash_assets"
melody_binaries_storage_path: str = "./storage/melody_binaries"
melody_download_base_url: str = "http://melodies.bellsystems.net/download"
# Email (Resend)
resend_api_key: str = "re_placeholder_change_me"
email_from: str = "noreply@yourdomain.com"
# App
backend_cors_origins: str = '["http://localhost:5173"]'
debug: bool = True
# Nextcloud WebDAV
nextcloud_url: str = ""
nextcloud_username: str = "" # WebDAV login & URL path username
nextcloud_password: str = "" # Use an app password for better security
nextcloud_dav_user: str = "" # Override URL path username if different from login
nextcloud_base_path: str = "BellSystems"
# IMAP/SMTP Email
imap_host: str = ""
imap_port: int = 993
imap_username: str = ""
imap_password: str = ""
imap_use_ssl: bool = True
smtp_host: str = ""
smtp_port: int = 587
smtp_username: str = ""
smtp_password: str = ""
smtp_use_tls: bool = True
email_sync_interval_minutes: int = 15
# Multi-mailbox config (JSON array). If empty, legacy single-account IMAP/SMTP is used.
# Example item:
# {"key":"sales","label":"Sales","email":"sales@bellsystems.gr","imap_host":"...","imap_username":"...","imap_password":"...","smtp_host":"...","smtp_username":"...","smtp_password":"...","sync_inbound":true,"allow_send":true}
mail_accounts_json: str = "[]"
# Auto-deploy (Gitea webhook)
deploy_secret: str = ""
deploy_project_path: str = "/app"
@property
def cors_origins(self) -> List[str]:
return json.loads(self.backend_cors_origins)
@property
def mail_accounts(self) -> List[Dict[str, Any]]:
try:
raw = json.loads(self.mail_accounts_json or "[]")
return raw if isinstance(raw, list) else []
except Exception:
return []
model_config = {"env_file": ".env", "extra": "ignore"}
settings = Settings()