| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- """配置加载模块"""
- import os
- import secrets
- from pathlib import Path
- import yaml
- class Config:
- """全局配置,从 config.yaml 加载"""
- def __init__(self, config_path: str = "config.yaml"):
- self.config_path = Path(config_path)
- self._data = {}
- if self.config_path.exists():
- with open(self.config_path, "r", encoding="utf-8") as f:
- self._data = yaml.safe_load(f) or {}
- def save(self):
- with open(self.config_path, "w", encoding="utf-8") as f:
- yaml.dump(self._data, f, allow_unicode=True, default_flow_style=False)
- def _resolve(self, p: str) -> Path:
- """将相对路径解析为配置文件所在目录的绝对路径"""
- path = Path(p)
- if not path.is_absolute():
- path = self.config_path.parent / path
- return path.resolve()
- # ── 路径 ──────────────────────────────────────────────
- @property
- def data_dir(self) -> Path:
- return self._resolve(self._get("paths.data_dir", "data"))
- @property
- def problems_dir(self) -> Path:
- return self._resolve(self._get("paths.problems_dir", "data/problems"))
- @property
- def papers_dir(self) -> Path:
- return self._resolve(self._get("paths.papers_dir", "data/papers"))
- @property
- def db_path(self) -> Path:
- return self._resolve(self._get("paths.db_path", "data/bank.db"))
- @property
- def backups_dir(self) -> Path:
- return self._resolve(self._get("paths.backups_dir", "data/backups"))
- @property
- def knowledge_tree_path(self) -> Path:
- return self._resolve(self._get("paths.knowledge_tree", "src/知识点树.md"))
- @property
- def knowledge_map_path(self) -> Path:
- return self._resolve(self._get("paths.knowledge_map", "src/knowledge_map.json"))
- # ── 服务端 ────────────────────────────────────────────
- @property
- def host(self) -> str:
- return self._get("server.host", "0.0.0.0")
- @property
- def port(self) -> int:
- return int(self._get("server.port", 8765))
- @property
- def token(self) -> str:
- t = self._get("server.token", "")
- if not t:
- t = secrets.token_hex(16)
- self._set("server.token", t)
- self.save()
- return t
- def regenerate_token(self) -> str:
- t = secrets.token_hex(16)
- self._set("server.token", t)
- self.save()
- return t
- # ── 默认值 ────────────────────────────────────────────
- @property
- def default_import_type(self) -> str:
- return self._get("defaults.import_type", "精选题")
- @property
- def max_backups(self) -> int:
- return int(self._get("defaults.max_backups", 30))
- # ── WebDAV ────────────────────────────────────────────
- @property
- def webdav_enabled(self) -> bool:
- return bool(self._get("webdav.enabled", False))
- @property
- def webdav_url(self) -> str:
- return self._get("webdav.url", "")
- @property
- def webdav_username(self) -> str:
- return self._get("webdav.username", "")
- @property
- def webdav_password(self) -> str:
- return self._get("webdav.password", "")
- @property
- def webdav_remote_path(self) -> str:
- return self._get("webdav.remote_path", "/tiku-backup")
- def set_webdav(self, url: str, username: str, password: str, remote_path: str = "/tiku-backup"):
- self._set("webdav.enabled", True)
- self._set("webdav.url", url)
- self._set("webdav.username", username)
- self._set("webdav.password", password)
- self._set("webdav.remote_path", remote_path)
- self.save()
- # ── helpers ───────────────────────────────────────────
- def _get(self, dotted_key: str, default=None):
- keys = dotted_key.split(".")
- val = self._data
- for k in keys:
- if isinstance(val, dict):
- val = val.get(k)
- else:
- return default
- return val if val is not None else default
- def _set(self, dotted_key: str, value):
- keys = dotted_key.split(".")
- d = self._data
- for k in keys[:-1]:
- if k not in d:
- d[k] = {}
- d = d[k]
- d[keys[-1]] = value
- # 全局单例,通过 app 的 state 注入
- _config_instance: Config | None = None
- def get_config(config_path: str = "config.yaml") -> Config:
- global _config_instance
- if _config_instance is None:
- _config_instance = Config(config_path)
- return _config_instance
- def set_config(cfg: Config):
- global _config_instance
- _config_instance = cfg
|