65 lines
2.4 KiB
Python
65 lines
2.4 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import logging
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
CREDENTIAL_BACKUP_DIR = Path.home() / ".forcepilot" / "qqbot" / "credentials"
|
||
|
|
|
||
|
|
|
||
|
|
class CredentialBackup:
|
||
|
|
def __init__(self, backup_dir: Path | None = None):
|
||
|
|
self._backup_dir = backup_dir or CREDENTIAL_BACKUP_DIR
|
||
|
|
self._backup_dir.mkdir(parents=True, exist_ok=True)
|
||
|
|
|
||
|
|
def _backup_path(self, account_id: str) -> Path:
|
||
|
|
safe_id = account_id.replace("/", "_").replace("\\", "_")
|
||
|
|
return self._backup_dir / f"{safe_id}.json"
|
||
|
|
|
||
|
|
def save(self, account_id: str, app_id: str, client_secret: str) -> None:
|
||
|
|
path = self._backup_path(account_id)
|
||
|
|
data = {
|
||
|
|
"account_id": account_id,
|
||
|
|
"app_id": app_id,
|
||
|
|
"client_secret": client_secret,
|
||
|
|
}
|
||
|
|
try:
|
||
|
|
path.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
||
|
|
logger.debug("Credential backup saved for account '%s'", account_id)
|
||
|
|
except OSError as e:
|
||
|
|
logger.warning("Failed to save credential backup for '%s': %s", account_id, e)
|
||
|
|
|
||
|
|
def load(self, account_id: str) -> dict | None:
|
||
|
|
path = self._backup_path(account_id)
|
||
|
|
if not path.exists():
|
||
|
|
return None
|
||
|
|
try:
|
||
|
|
data = json.loads(path.read_text(encoding="utf-8"))
|
||
|
|
if data.get("app_id") and data.get("client_secret"):
|
||
|
|
logger.debug("Credential backup loaded for account '%s'", account_id)
|
||
|
|
return data
|
||
|
|
except (json.JSONDecodeError, OSError) as e:
|
||
|
|
logger.warning("Failed to load credential backup for '%s': %s", account_id, e)
|
||
|
|
return None
|
||
|
|
|
||
|
|
def exists(self, account_id: str) -> bool:
|
||
|
|
return self._backup_path(account_id).exists()
|
||
|
|
|
||
|
|
def clear(self, account_id: str) -> None:
|
||
|
|
path = self._backup_path(account_id)
|
||
|
|
try:
|
||
|
|
path.unlink(missing_ok=True)
|
||
|
|
logger.debug("Credential backup cleared for account '%s'", account_id)
|
||
|
|
except OSError as e:
|
||
|
|
logger.warning("Failed to clear credential backup for '%s': %s", account_id, e)
|
||
|
|
|
||
|
|
def clear_all(self) -> None:
|
||
|
|
for path in self._backup_dir.glob("*.json"):
|
||
|
|
try:
|
||
|
|
path.unlink()
|
||
|
|
except OSError:
|
||
|
|
pass
|
||
|
|
logger.debug("All credential backups cleared")
|