Files
Echo-Tavern/config.py
T
leeexinandClaude Opus 4.6 ae478755be feat(auth,admin): add LinuxDO OAuth login and admin settings
Implement LinuxDO OAuth authorization-code login flow with state validation and local account binding, and add admin APIs/UI to manage OAuth runtime settings with URL validation and secret masking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:14:46 +08:00

107 lines
3.6 KiB
Python

from __future__ import annotations
import json
from pathlib import Path
from typing import Any
BASE_DIR = Path(__file__).resolve().parent
SETTINGS_FILE = BASE_DIR / "settings.json"
DEFAULT_SETTINGS: dict[str, Any] = {
"secret_key": "echo-tavern-dev-secret",
"db_path": "echo_tavern.db",
"deepseek_api_key": "sk-9e94feccd21640ada969990e792cbc11",
"deepseek_base_url": "https://api.deepseek.com",
"deepseek_model": "deepseek-chat",
"deepseek_timeout": 25,
"deepseek_connect_timeout": 8,
"deepseek_read_timeout": 90,
"deepseek_retries": 2,
"max_room_players": 8,
"max_action_length": 280,
"app_host": "0.0.0.0",
"app_port": 8095,
"debug": True,
"linuxdo_oauth": {
"enabled": False,
"client_id": "",
"client_secret": "",
"authorize_url": "https://connect.linux.do/oauth2/authorize",
"token_url": "https://connect.linux.do/oauth2/token",
"userinfo_url": "https://connect.linux.do/api/user",
"scope": "read",
},
}
def _resolve_db_path(db_path: str) -> str:
path = Path(db_path)
if path.is_absolute():
return str(path)
return str((BASE_DIR / path).resolve())
def _load_settings() -> dict[str, Any]:
if not SETTINGS_FILE.exists():
SETTINGS_FILE.write_text(
json.dumps(DEFAULT_SETTINGS, ensure_ascii=False, indent=2),
encoding="utf-8",
)
return dict(DEFAULT_SETTINGS)
try:
payload = json.loads(SETTINGS_FILE.read_text(encoding="utf-8"))
if not isinstance(payload, dict):
return dict(DEFAULT_SETTINGS)
except (OSError, json.JSONDecodeError):
return dict(DEFAULT_SETTINGS)
merged = dict(DEFAULT_SETTINGS)
merged.update(payload)
linuxdo_defaults = DEFAULT_SETTINGS["linuxdo_oauth"]
linuxdo_payload = payload.get("linuxdo_oauth", {})
if isinstance(linuxdo_payload, dict):
merged["linuxdo_oauth"] = {**linuxdo_defaults, **linuxdo_payload}
else:
merged["linuxdo_oauth"] = dict(linuxdo_defaults)
return merged
SETTINGS = _load_settings()
class Config:
SECRET_KEY = str(SETTINGS.get("secret_key", DEFAULT_SETTINGS["secret_key"]))
DB_PATH = _resolve_db_path(str(SETTINGS.get("db_path", DEFAULT_SETTINGS["db_path"])))
DEEPSEEK_API_KEY = str(SETTINGS.get("deepseek_api_key", DEFAULT_SETTINGS["deepseek_api_key"]))
DEEPSEEK_BASE_URL = str(
SETTINGS.get("deepseek_base_url", DEFAULT_SETTINGS["deepseek_base_url"])
)
DEEPSEEK_MODEL = str(SETTINGS.get("deepseek_model", DEFAULT_SETTINGS["deepseek_model"]))
DEEPSEEK_TIMEOUT = float(
SETTINGS.get("deepseek_timeout", DEFAULT_SETTINGS["deepseek_timeout"])
)
DEEPSEEK_CONNECT_TIMEOUT = float(
SETTINGS.get("deepseek_connect_timeout", DEFAULT_SETTINGS["deepseek_connect_timeout"])
)
DEEPSEEK_READ_TIMEOUT = float(
SETTINGS.get("deepseek_read_timeout", DEFAULT_SETTINGS["deepseek_read_timeout"])
)
DEEPSEEK_RETRIES = int(SETTINGS.get("deepseek_retries", DEFAULT_SETTINGS["deepseek_retries"]))
MAX_ROOM_PLAYERS = int(
SETTINGS.get("max_room_players", DEFAULT_SETTINGS["max_room_players"])
)
MAX_ACTION_LENGTH = int(
SETTINGS.get("max_action_length", DEFAULT_SETTINGS["max_action_length"])
)
APP_HOST = str(SETTINGS.get("app_host", DEFAULT_SETTINGS["app_host"]))
APP_PORT = int(SETTINGS.get("app_port", DEFAULT_SETTINGS["app_port"]))
DEBUG = bool(SETTINGS.get("debug", DEFAULT_SETTINGS["debug"]))
LINUXDO_OAUTH = SETTINGS.get("linuxdo_oauth", DEFAULT_SETTINGS["linuxdo_oauth"])
class TestConfig(Config):
TESTING = True
SECRET_KEY = "test-secret"
DEEPSEEK_API_KEY = ""