"""运行时登录凭据:内存生效 + 持久化到 .env。""" from __future__ import annotations import re from pathlib import Path from threading import Lock from .config import get_settings _lock = Lock() _username: str | None = None _password: str | None = None def _env_paths() -> list[Path]: here = Path(__file__).resolve() # backend/app/credentials.py -> repo root = parents[2] root = here.parents[2] return [root / ".env", Path.cwd() / ".env", Path.cwd().parent / ".env"] def _ensure_loaded() -> None: global _username, _password if _username is not None and _password is not None: return s = get_settings() _username = s.auth_username _password = s.auth_password def get_credentials() -> tuple[str, str]: with _lock: _ensure_loaded() assert _username is not None and _password is not None return _username, _password def upsert_env_file(key: str, value: str) -> Path | None: """写入第一个已存在的 .env;都不存在则写仓库根 .env。""" paths = _env_paths() target = next((p for p in paths if p.is_file()), paths[0]) target.parent.mkdir(parents=True, exist_ok=True) text = target.read_text(encoding="utf-8") if target.is_file() else "" line = f"{key}={value}" pattern = re.compile(rf"(?m)^{re.escape(key)}=.*$") if pattern.search(text): text = pattern.sub(line, text) else: if text and not text.endswith("\n"): text += "\n" text += line + "\n" target.write_text(text, encoding="utf-8") return target def update_credentials(*, new_username: str, new_password: str) -> None: global _username, _password user = new_username.strip() pwd = new_password if not user or not pwd: raise ValueError("用户名和密码不能为空") with _lock: upsert_env_file("AUTH_USERNAME", user) upsert_env_file("AUTH_PASSWORD", pwd) _username = user _password = pwd # 刷新 Settings 缓存,避免进程内读到旧值 get_settings.cache_clear()