网页身份改由 auth-hub 做 OAuth2 + PKCE 单点登录,本地邮箱/密码登录与注册整条链路删除 (routes/auth.py、auth.py 的密码哈希、config.py 的 ALLOW_REGISTRATION)。Garmin 账号绑定/ 同步保持完全独立、可选:routes/garmin.py 不再直接查 users 表,Garmin 邮箱回退统一走新增 的 services/garmin.py::get_remembered_email()(优先读 garmin_tokens 当前绑定,兼容早期账号 落在 users.garmin_email 的历史值),彻底把「你是谁」和「你绑没绑 Garmin」两件事拆开。 - db.py: users 表新增 auth_hub_sub/auth_hub_username,MIGRATIONS 补上这两列(此前遗漏导致 已存在的生产 MariaDB 表永远不会自动加列);同时把历史遗留的 garmin_email/ garmin_password_hash NOT NULL 约束在线迁移为可空,因为新账号不再在注册时收集这些字段。 - routes/auth.py: 修掉 /callback 路由重复拼接 /api/auth 前缀导致 404 的 bug。 - client: LoginPage 去掉本地登录/注册标签页,只保留 auth-hub 统一登录;登录成功/失败后都 用 history.replaceState 清理地址栏,修掉 Framework7 browserHistory 读取 /auth/callback?code=... 导致「找不到页面」的问题。 - 新增 test_auth_hub_client.py 锁定 find_or_create_user 按 auth_hub_sub 幂等——生产上曾经因为 这个函数在没有该测试保护时被测试触发,误建过一个空账号,靠手工核对 health_data 计数才发现。 - 生产 auth-hub 侧另行为该项目注册了正式 client(未随本次提交变更,凭证只存在服务器 .env)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
80 lines
3.3 KiB
Plaintext
80 lines
3.3 KiB
Plaintext
# --- Server ---
|
|
# BACKEND_PORT takes precedence over PORT. Prefer it: many tools inject PORT
|
|
# for the frontend, and Flask would otherwise take the React dev server's port.
|
|
BACKEND_PORT=5000
|
|
|
|
# --- Database: sqlite (default) or mariadb ---
|
|
DB_TYPE=sqlite
|
|
# SQLite file (used when DB_TYPE=sqlite)
|
|
DATABASE_PATH=./data/health.db
|
|
|
|
# MariaDB (used when DB_TYPE=mariadb) — production DB on the Oracle server
|
|
# (129.146.26.249, local MariaDB 10.3). Dedicated account over TCP 127.0.0.1;
|
|
# MARIADB_SOCKET is only needed if TCP auth is disabled for the app user.
|
|
# MARIADB_HOST=127.0.0.1
|
|
# MARIADB_PORT=3306
|
|
# MARIADB_USER=garmin
|
|
# MARIADB_PASSWORD=your_production_mariadb_password
|
|
# MARIADB_DATABASE=garmin_health_lab
|
|
|
|
# --- Auth ---
|
|
# CHANGE THIS in production! Used to sign JWTs (7-day expiry by default).
|
|
JWT_SECRET=dev_secret_change_me
|
|
JWT_EXPIRY_DAYS=7
|
|
|
|
# --- auth-hub OAuth2 provider (centralized SSO) ---
|
|
# See docs/AUTH_HUB_INTEGRATION.md for setup instructions.
|
|
#
|
|
# Base URL of the auth-hub service
|
|
AUTH_HUB_BASE_URL=http://129.146.26.249:5300
|
|
#
|
|
# OAuth2 client credentials (obtain from auth-hub.manage_clients create)
|
|
# NOTE: these must be registered against the auth-hub instance AUTH_HUB_BASE_URL
|
|
# actually points to (dev vs prod are separate databases with separate clients).
|
|
# Put the REAL values in backend/.env (gitignored) — never here.
|
|
AUTH_HUB_CLIENT_ID=your_client_id
|
|
AUTH_HUB_CLIENT_SECRET=your_client_secret
|
|
#
|
|
# Callback URL (must exactly match what's registered in auth-hub)
|
|
AUTH_HUB_REDIRECT_URI=http://129.146.26.249:8123/auth/callback
|
|
|
|
# --- CORS (comma-separated allowed front-end origins) ---
|
|
# localhost stays in the production list on purpose: CORS is not an auth
|
|
# boundary — every data route requires a valid JWT — so allowing a developer's
|
|
# dev server costs nothing and saves toggling this on every session.
|
|
CORS_ORIGIN=http://localhost:3000,http://localhost:5173
|
|
|
|
# --- AI models (text-only, large context) ---
|
|
# Put REAL keys in backend/.env — that file is gitignored. Never commit keys.
|
|
# Any model whose credentials are absent is skipped automatically.
|
|
|
|
# Self-hosted AI gateway (model id "gateway"). OpenAI-compatible; it fans out
|
|
# over nvidia/gemini/ollama itself and rotates several Gemini keys, so it
|
|
# absorbs single-vendor quota limits. Reached directly, bypassing any local
|
|
# HTTP proxy. NOTE: its NVIDIA upstream is a large reasoning model — replies
|
|
# can take 2-3 minutes, so set AI_TIMEOUT_SECONDS accordingly.
|
|
AI_GATEWAY_BASE_URL=http://129.146.26.249:5100/v1
|
|
AI_GATEWAY_TOKEN=
|
|
AI_GATEWAY_MODEL=ai-gateway-auto
|
|
|
|
# Google AI Studio -> "gemini-flash". Free-tier quota is small; 429s are common.
|
|
GEMINI_API_KEY=
|
|
|
|
# NVIDIA NIM -> "llama-70b", "nemotron-49b", "mistral-large".
|
|
# Model ids come from that account's live GET /v1/models — do not guess them.
|
|
NVIDIA_API_KEY=
|
|
# NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1
|
|
|
|
# Preference order. The first configured model answers; if it fails or times
|
|
# out, the next is tried. Read per request, so changes need no restart.
|
|
AI_MODEL_CHAIN=gateway,gemini-flash,llama-70b
|
|
|
|
# Max days of history sent (CSV-encoded). Trimmed further per model so the
|
|
# payload always fits that model's own context window.
|
|
AI_DAY_BUDGET=365
|
|
|
|
AI_TIMEOUT_SECONDS=180
|
|
# Output cap. Reasoning models spend part of it thinking before they answer;
|
|
# entries that need more declare their own budget in services/ai.py.
|
|
AI_MAX_TOKENS=1024
|