dashboard-nanobot/backend/core/settings.py

151 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import os
from pathlib import Path
from typing import Final
from urllib.parse import urlsplit, urlunsplit
from dotenv import load_dotenv
BACKEND_ROOT: Final[Path] = Path(__file__).resolve().parents[1]
PROJECT_ROOT: Final[Path] = BACKEND_ROOT.parent
# Load env files from nearest to broadest scope.
# Priority (high -> low, with override=False preserving existing values):
# 1) process environment
# 2) backend/.env
# 3) project/.env
# 4) backend/.env.prod
# 5) project/.env.prod
load_dotenv(BACKEND_ROOT / ".env", override=False)
load_dotenv(PROJECT_ROOT / ".env.prod", override=False)
def _env_text(name: str, default: str) -> str:
raw = os.getenv(name)
if raw is None:
return default
return str(raw).replace("\\n", "\n")
def _env_bool(name: str, default: bool) -> bool:
raw = os.getenv(name)
if raw is None:
return default
return str(raw).strip().lower() in {"1", "true", "yes", "on"}
def _env_int(name: str, default: int, min_value: int, max_value: int) -> int:
raw = os.getenv(name)
if raw is None:
return default
try:
value = int(str(raw).strip())
except Exception:
value = default
return max(min_value, min(max_value, value))
def _normalize_dir_path(path_value: str) -> str:
raw = str(path_value or "").strip()
if not raw:
return raw
p = Path(raw)
if p.is_absolute():
return str(p)
return str((BACKEND_ROOT / p).resolve())
DATA_ROOT: Final[str] = _normalize_dir_path(os.getenv("DATA_ROOT", str(PROJECT_ROOT / "data")))
BOTS_WORKSPACE_ROOT: Final[str] = _normalize_dir_path(
os.getenv("BOTS_WORKSPACE_ROOT", str(PROJECT_ROOT / "workspace" / "bots"))
)
def _normalize_database_url(url: str) -> str:
raw = str(url or "").strip()
prefix = "sqlite:///"
if not raw.startswith(prefix):
return raw
path_part = raw[len(prefix) :]
if not path_part or path_part.startswith("/"):
return raw
abs_path = (BACKEND_ROOT / path_part).resolve()
return f"{prefix}{abs_path.as_posix()}"
def _database_engine(url: str) -> str:
raw = str(url or "").strip().lower()
if raw.startswith("sqlite"):
return "sqlite"
if raw.startswith("postgresql"):
return "postgresql"
if raw.startswith("mysql"):
return "mysql"
if "+" in raw:
return raw.split("+", 1)[0]
if "://" in raw:
return raw.split("://", 1)[0]
return "unknown"
def _mask_database_url(url: str) -> str:
raw = str(url or "").strip()
if not raw or raw.startswith("sqlite"):
return raw
try:
parsed = urlsplit(raw)
if parsed.password is None:
return raw
host = parsed.hostname or ""
if parsed.port:
host = f"{host}:{parsed.port}"
auth = parsed.username or ""
if auth:
auth = f"{auth}:***@{host}"
else:
auth = host
netloc = auth
return urlunsplit((parsed.scheme, netloc, parsed.path, parsed.query, parsed.fragment))
except Exception:
return raw
_db_env = str(os.getenv("DATABASE_URL") or "").strip()
DATABASE_URL: Final[str] = _normalize_database_url(
_db_env if _db_env else f"sqlite:///{Path(DATA_ROOT) / 'nanobot_dashboard.db'}"
)
DATABASE_ENGINE: Final[str] = _database_engine(DATABASE_URL)
DATABASE_URL_DISPLAY: Final[str] = _mask_database_url(DATABASE_URL)
DATABASE_ECHO: Final[bool] = _env_bool("DATABASE_ECHO", True)
UPLOAD_MAX_MB: Final[int] = _env_int("UPLOAD_MAX_MB", 100, 1, 2048)
REDIS_ENABLED: Final[bool] = _env_bool("REDIS_ENABLED", False)
REDIS_URL: Final[str] = str(os.getenv("REDIS_URL") or "").strip()
REDIS_PREFIX: Final[str] = str(os.getenv("REDIS_PREFIX") or "dashboard_nanobot").strip() or "dashboard_nanobot"
REDIS_DEFAULT_TTL: Final[int] = _env_int("REDIS_DEFAULT_TTL", 60, 1, 86400)
PANEL_ACCESS_PASSWORD: Final[str] = str(os.getenv("PANEL_ACCESS_PASSWORD") or "").strip()
DEFAULT_AGENTS_MD: Final[str] = _env_text(
"DEFAULT_AGENTS_MD",
"# Agent Instructions\n\n- 优先完成任务目标\n- 操作前先说明意图\n- 输出必须可执行\n\n## 默认输出规范\n\n- 每次执行任务时,在 workspace 中创建新目录保存本次输出。\n- 输出内容默认采用 Markdown.md格式。",
).strip()
DEFAULT_SOUL_MD: Final[str] = _env_text(
"DEFAULT_SOUL_MD",
"# Soul\n\n你是专业的企业数字员工,表达清晰、可执行。",
).strip()
DEFAULT_USER_MD: Final[str] = _env_text(
"DEFAULT_USER_MD",
"# User\n\n- 语言: 中文\n- 风格: 专业\n- 偏好: 简明且有步骤",
).strip()
DEFAULT_TOOLS_MD: Final[str] = _env_text(
"DEFAULT_TOOLS_MD",
"# Tools\n\n- 谨慎使用 shell\n- 修改文件后复核\n- 失败时说明原因并重试策略",
).strip()
DEFAULT_IDENTITY_MD: Final[str] = _env_text(
"DEFAULT_IDENTITY_MD",
"# Identity\n\n- 角色: 企业数字员工\n- 领域: 运维与任务执行",
).strip()