"""Состояние демо-режима (общее для db/security/роутеров). Хранится в файле, чтобы пережить перезапуск. Демо-режим — персональный: отдельная тестовая БД существует рядом с боевой, а в неё «входят» только те пользователи, кто его включил (или кого включил администратор). Остальные продолжают работать с боевыми данными. Какая БД нужна конкретному запросу, определяется по токену (claim mode) — см. REQUEST_DEMO и app/db.py.""" import json import os from contextvars import ContextVar from pathlib import Path from . import config _FLAG = Path(config.DATA_DIR) / "demo_mode.json" # запрос пришёл с демо-токеном (выставляется middleware в main.py, читается get_db, push, user_out и т. п.) REQUEST_DEMO: ContextVar[bool] = ContextVar("request_demo", default=False) class State: active = False # демо-БД существует db_file = "" # имя файла демо-БД в DATA_DIR photo_dir = "" owner_id = None # администратор, создавший демо-набор (его копия в демо-БД имеет тот же id) since = "" users: set = set() # id пользователей БОЕВОЙ БД, которые сейчас работают в демо epoch = 0 # увеличивается при пересоздании/удалении демо-БД: старые демо-токены становятся недействительны def load(): State.active, State.users = False, set() if _FLAG.is_file(): try: d = json.loads(_FLAG.read_text(encoding="utf-8")) State.epoch = int(d.get("epoch", 0)) if d.get("db_file") and (Path(config.DATA_DIR) / d["db_file"]).is_file(): State.active, State.db_file, State.photo_dir = True, d["db_file"], d["photo_dir"] State.owner_id, State.since = d.get("owner_id", d.get("admin_id")), d.get("since", "") State.users = set(d.get("users", [State.owner_id] if State.owner_id else [])) except Exception: pass if not State.active: State.db_file, State.photo_dir, State.owner_id, State.users = "", "", None, set() save() def save(): os.makedirs(config.DATA_DIR, exist_ok=True) tmp = _FLAG.with_suffix(".tmp") tmp.write_text(json.dumps({"db_file": State.db_file, "photo_dir": State.photo_dir, "owner_id": State.owner_id, "since": State.since, "users": sorted(State.users), "epoch": State.epoch}), encoding="utf-8") os.replace(tmp, _FLAG) def clear(): State.active, State.db_file, State.photo_dir, State.owner_id, State.since = False, "", "", None, "" State.users = set() State.epoch += 1 save() def user_in_demo(real_id: int | None) -> bool: return State.active and real_id is not None and real_id in State.users def is_demo_request() -> bool: return State.active and REQUEST_DEMO.get()