import os from pathlib import Path from datetime import datetime from zoneinfo import ZoneInfo DATA_DIR = os.getenv("DATA_DIR", "data") # демо-базы и флаг демо-режима DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./data/app.db") PRODUCTION = os.getenv("APP_ENV", "").lower() == "production" # включает отказ запуска со слабыми секретами SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-change-me") ACCESS_TTL_MIN = int(os.getenv("ACCESS_TTL_MIN", "60")) REFRESH_TTL_DAYS = int(os.getenv("REFRESH_TTL_DAYS", "90")) LOGIN_MAX_FAILS = int(os.getenv("LOGIN_MAX_FAILS", "5")) # неудачных попыток LOGIN_WINDOW_SEC = int(os.getenv("LOGIN_WINDOW_SEC", "300")) # за это окно -> блокировка на окно TZ = ZoneInfo(os.getenv("APP_TZ", "Europe/Moscow")) ADMIN_USERNAME = os.getenv("ADMIN_USERNAME", "admin") ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "admin") # APK, поставляемый вместе с образом: ссылка на скачивание работает сразу после деплоя RELEASE_DIR = Path(os.getenv("RELEASE_DIR", str(Path(__file__).resolve().parent.parent / "release"))) PHOTO_DIR = os.getenv("PHOTO_DIR", "data/photos") SCHEDULER_INTERVAL = int(os.getenv("SCHEDULER_INTERVAL", "30")) def iso(dt: datetime | None) -> str | None: """Naive-локальное время -> ISO со смещением (для клиентов).""" return dt.replace(tzinfo=TZ).isoformat() if dt else None def now() -> datetime: """Текущее локальное время (naive). Все даты в БД хранятся как локальное время сервера.""" return datetime.now(TZ).replace(tzinfo=None)