Files
server-core/app/config.py
T
Pine a976ce194e refactor(auth): 统一登录=账号密码+手机号验证码, 微信扫码预留(本期未开放)
- auth/status 返回 login_modes([password,phone]) + wechat_login 标志, 供各端渲染(VITE 按此显示)
- wx-login 用 config.AUTH_WECHAT_LOGIN(默认 false) 闸门, 未开放返回 503'微信登录预留中'
- 本期所有端口两种登录模式; 微信扫码后期拓展
2026-08-25 16:25:50 +08:00

153 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""全局配置:端口、数据目录、令牌有效期、认证开关。
所有路径/开关均可通过环境变量覆盖,便于部署和测试:
PINEAGENTS_DEMO_HOST / PINEAGENTS_DEMO_PORT 监听地址(默认 127.0.0.1:8090
PINEAGENTS_DEMO_DATA_DIR JSON 数据目录(默认 <项目根>/data
PINEAGENTS_DEMO_AUTH_ENABLED 认证开关(默认 true)
"""
from __future__ import annotations
import os
from pathlib import Path
# 启动第一步即加载项目根 .env(含 PINEAGENTS_INTERNAL_TOKEN / DATABASE_URL 等),
# 否则后续 os.environ.get 读到空串(.env 后续才会被 app.park/app.training 各自加载)。
# 必须在下方任何 os.environ.get 之前执行。
try: # python-dotenv 为可选依赖;缺失时仍可用系统环境变量
from dotenv import load_dotenv
load_dotenv(Path(__file__).resolve().parent.parent / ".env")
except Exception: # noqa: BLE001
pass
# 项目根目录 = 本文件的上上级(app/config.py -> <根>
BASE_DIR = Path(__file__).resolve().parent.parent
# ---------------------------------------------------------------------------
# 数据目录(serverdata/ 统一承载所有资源:data/logs/files/keys/prompts/uploads
# ---------------------------------------------------------------------------
SERVERDATA_DIR = Path(
os.environ.get("PINEAGENTS_SERVERDATA_DIR", str(BASE_DIR / "serverdata")),
).resolve()
# 数据子目录(缺失自动创建,start.sh 亦负责初始化)
DATA_DIR = SERVERDATA_DIR / "data"
LOG_DIR = SERVERDATA_DIR / "logs"
FILES_DIR = SERVERDATA_DIR / "files"
KEYS_DIR = SERVERDATA_DIR / "keys"
PROMPTS_DIR = SERVERDATA_DIR / "prompts"
UPLOADS_DIR = SERVERDATA_DIR / "uploads"
for _d in (DATA_DIR, LOG_DIR, FILES_DIR, KEYS_DIR, PROMPTS_DIR, UPLOADS_DIR):
_d.mkdir(parents=True, exist_ok=True)
# 数据库连接(异步)。开发默认 SQLite+aiosqlite;生产用 MySQL+asyncmy(环境变量覆盖)。
# 示例 MySQLPINEAGENTS_DEMO_DATABASE_URL=mysql+asyncmy://user:pass@127.0.0.1:3306/opc?charset=utf8mb4
DATABASE_URL = os.environ.get(
"PINEAGENTS_DEMO_DATABASE_URL",
f"sqlite+aiosqlite:///{DATA_DIR / 'app.db'}",
)
# Redis(缓存/会话/限流/分布式锁),空则降级内存。
REDIS_URL = os.environ.get("REDIS_URL", "redis://127.0.0.1:6379/0")
# OSS 对象存储(aioboto3,兼容阿里云 OSS / MinIO);空则文件落 serverdata/uploads。
OSS_ENDPOINT = os.environ.get("OSS_ENDPOINT", "")
OSS_ACCESS_KEY = os.environ.get("OSS_ACCESS_KEY", "")
OSS_SECRET_KEY = os.environ.get("OSS_SECRET_KEY", "")
OSS_BUCKET = os.environ.get("OSS_BUCKET", "opc-files")
USERS_FILE = DATA_DIR / "users.json"
TOKENS_FILE = DATA_DIR / "tokens.json"
AGENTS_FILE = DATA_DIR / "agents.json"
# 智能体初始化 md 模板(镜像本地 md_files 结构;qa 已统一为 PineAgents 品牌)。
# 目录:<PROMPTS_DIR>/<lang>/*.md(通用)、<PROMPTS_DIR>/qa/<lang>/*.md、
# <PROMPTS_DIR>/local/<lang>/*.md
TEMPLATES_DIR = PROMPTS_DIR
# 首次启动时用于初始化 data/ 的种子数据(提交到仓库,数据可读)。
SEED_DIR = BASE_DIR / "data" / "seed"
SEED_USERS_FILE = SEED_DIR / "demo_users.json"
# ---------------------------------------------------------------------------
# 服务监听
# ---------------------------------------------------------------------------
HOST = os.environ.get("PINEAGENTS_DEMO_HOST", "127.0.0.1")
PORT = int(os.environ.get("PINEAGENTS_DEMO_PORT", "8090"))
# ---------------------------------------------------------------------------
# 认证与令牌
# ---------------------------------------------------------------------------
# 演示服务默认开启认证(PineAgents 主后端把 /auth/* 转发到本服务)。
AUTH_ENABLED = (
os.environ.get("PINEAGENTS_DEMO_AUTH_ENABLED", "true").strip().lower()
in ("true", "1", "yes")
)
# 令牌有效期(秒):默认 7 天;-1/0 表示永久(按 100 年封顶)。
DEFAULT_TOKEN_EXPIRY_SECONDS = 7 * 24 * 3600
PERMANENT_TOKEN_SECONDS = 100 * 365 * 24 * 3600
TOKEN_EXPIRY_MAX = PERMANENT_TOKEN_SECONDS
# ---------------------------------------------------------------------------
# JWT
# ---------------------------------------------------------------------------
JWT_SECRET = os.environ.get(
"PINEAGENTS_DEMO_JWT_SECRET",
"dev-insecure-change-me-0123456789abcdef",
)
JWT_ALGORITHM = os.environ.get("PINEAGENTS_DEMO_JWT_ALGORITHM", "HS256")
JWT_ACCESS_EXPIRY_SECONDS = DEFAULT_TOKEN_EXPIRY_SECONDS
# ---------------------------------------------------------------------------
# 算力引擎(compute-engine / new-api)管理接入(loopback,仅服务)
# 模型调用与额度/令牌/流水均经此引擎计量;前端不直连引擎。
# ---------------------------------------------------------------------------
COMPUTE_BASE_URL = os.environ.get(
"PINEAGENTS_COMPUTE_BASE_URL", "http://127.0.0.1:3000",
)
# 受信任内部令牌:server-core 作为唯一入口,无权限限制访问引擎管理/root 端点。
# 与引擎侧 PINEAGENTS_INTERNAL_TOKEN 保持一致;回退兼容旧名 PINEAGENTS_COMPUTE_ADMIN_TOKEN。
COMPUTE_ADMIN_TOKEN = os.environ.get("PINEAGENTS_INTERNAL_TOKEN") or os.environ.get("PINEAGENTS_COMPUTE_ADMIN_TOKEN", "")
# /v1 模型中继转发(relay.py)用的引擎消费令牌:需是引擎 /v1 能识别的真实用户/渠道令牌,
# 与聚合 root 的 COMPUTE_ADMIN_TOKEN 区分开(后者仅用于管理/root 端点)。回退 COMPUTE_ADMIN_TOKEN。
COMPUTE_RELAY_TOKEN = os.environ.get("PINEAGENTS_COMPUTE_RELAY_TOKEN", "")
# 用户端算力中心「基础 API 地址」:对外公网地址(如 https://opc.pinesound.cn),供用户其它工具接入。
# 未设置时回落请求的 base_url。经代理时 request.base_url 可能为内部地址,故用此配置给公网地址。
COMPUTE_PUBLIC_BASE = os.environ.get("PINEAGENTS_COMPUTE_PUBLIC_BASE", "").rstrip("/")
COMPUTE_TIMEOUT = float(os.environ.get("PINEAGENTS_COMPUTE_TIMEOUT", "10"))
COMPUTE_RETRIES = int(os.environ.get("PINEAGENTS_COMPUTE_RETRIES", "2"))
# ---------------------------------------------------------------------------
# 短信验证码(统一登录)
# ---------------------------------------------------------------------------
# stub = 仅打印验证码不真正发送(默认,便于联调);生产接阿里云/腾讯云短信。
SMS_PROVIDER = os.environ.get("PINEAGENTS_SMS_PROVIDER", "stub").strip().lower()
SMS_CODE_TTL_SECONDS = int(os.environ.get("PINEAGENTS_SMS_CODE_TTL_SECONDS", "300"))
SMS_RATE_LIMIT = int(os.environ.get("PINEAGENTS_SMS_RATE_LIMIT", "5"))
# ---------------------------------------------------------------------------
# 微信小程序登录(空 appid/secret 时把 code 直通当作 openid,便于无真实 appid 联调)
# ---------------------------------------------------------------------------
WECHAT_APPID = os.environ.get("PINEAGENTS_WECHAT_APPID", "")
WECHAT_SECRET = os.environ.get("PINEAGENTS_WECHAT_SECRET", "")
# 本期统一登录仅两模式(账号密码/手机号验证码);微信扫码预留后期,未开放(默认 False)。
AUTH_WECHAT_LOGIN = os.environ.get("PINEAGENTS_WECHAT_LOGIN", "false").strip().lower() in ("true", "1", "yes")
def resolve_token_expiry(expires_in: int | None) -> int:
"""把前端传来的 ``expires_in`` 归一化为秒数。
- None → 默认 7 天
- <= 0(含 -1/0)→ 永久(100 年)
- 其他 → 按最大值封顶
"""
if expires_in is None:
return DEFAULT_TOKEN_EXPIRY_SECONDS
if expires_in <= 0:
return PERMANENT_TOKEN_SECONDS
return min(expires_in, TOKEN_EXPIRY_MAX)