refactor: 移除前端 Mock API,新增共享组件,优化认证流程
- 删除 mock_api_client、mock_calendar_service、mock_history_service - 新增 fixed_length_code_input、link_button、message_composer 共享组件 - 优化登录/注册/密码重置页面使用新组件 - 简化 injection.dart 移除 mock 分支 - 更新 env.dart 配置(BACKEND_URL 替换 API_URL) - 后端 agentscope 工具和测试更新 - 重构 AGENTS.md 文档结构 - 新增 deploy/ 目录和 protocol 文档
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# 环境变量配置模板(复制到 .env 并填写实际值)
|
||||
# 警告:切勿将包含真实密钥的 .env 提交到代码仓库
|
||||
|
||||
############
|
||||
# 运行时配置
|
||||
############
|
||||
SOCIAL_RUNTIME__ENVIRONMENT=dev
|
||||
SOCIAL_RUNTIME__DEBUG=true
|
||||
SOCIAL_RUNTIME__LOG_LEVEL=INFO
|
||||
SOCIAL_RUNTIME__SQL_LOG_QUERIES=false
|
||||
|
||||
############
|
||||
# Web 服务器配置(Uvicorn)
|
||||
############
|
||||
SOCIAL_WEB__HOST=0.0.0.0
|
||||
SOCIAL_WEB__PORT=5775
|
||||
SOCIAL_WEB__WORKERS=2
|
||||
|
||||
############
|
||||
# LiteLLM Proxy 网关配置
|
||||
############
|
||||
SOCIAL_LITELLM__PORT=3875
|
||||
|
||||
############
|
||||
# Redis 配置
|
||||
############
|
||||
SOCIAL_REDIS__PASSWORD=redis-secure-2026
|
||||
SOCIAL_REDIS__HOST=localhost
|
||||
SOCIAL_REDIS__PORT=6379
|
||||
SOCIAL_REDIS__DB=0
|
||||
|
||||
############
|
||||
# Worker 队列分组配置(显式参数控制)
|
||||
############
|
||||
# critical: 用户同步感知任务(验证码发送、鉴权后置关键动作)
|
||||
# default: 常规异步任务
|
||||
# bulk: 批处理/重计算/可延迟任务
|
||||
SOCIAL_WORKER__GROUPS__CRITICAL__CONCURRENCY=2
|
||||
|
||||
SOCIAL_WORKER__GROUPS__DEFAULT__CONCURRENCY=2
|
||||
|
||||
SOCIAL_WORKER__GROUPS__BULK__CONCURRENCY=1
|
||||
|
||||
############
|
||||
# Taskiq(可选,默认回落到 Redis URL)
|
||||
############
|
||||
# SOCIAL_TASKIQ__BROKER_URL=redis://:password@localhost:6379/0
|
||||
# SOCIAL_TASKIQ__RESULT_BACKEND_URL=redis://:password@localhost:6379/0
|
||||
|
||||
############
|
||||
# Supabase(云模式,后端必需)
|
||||
############
|
||||
SOCIAL_SUPABASE__PUBLIC_URL=https://your-project.supabase.co
|
||||
SOCIAL_SUPABASE__ANON_KEY=
|
||||
SOCIAL_SUPABASE__SERVICE_ROLE_KEY=
|
||||
# 使用阿里云 DescribeInstanceAuthInfo 返回的 JwtSecret
|
||||
SOCIAL_SUPABASE__JWT_SECRET=
|
||||
SOCIAL_SUPABASE__JWT_ALGORITHM=HS256
|
||||
|
||||
# Postgres 连接信息(后端与 Supabase 共用密码)
|
||||
SOCIAL_DATABASE__HOST=localhost
|
||||
SOCIAL_DATABASE__PORT=5434
|
||||
SOCIAL_DATABASE__NAME=postgres
|
||||
SOCIAL_DATABASE__USER=postgres
|
||||
SOCIAL_DATABASE__PASSWORD=change-me-strong-password
|
||||
|
||||
############
|
||||
# Agent Chat 附件存储配置(仅基础设施变量)
|
||||
############
|
||||
SOCIAL_STORAGE__PROVIDER=supabase
|
||||
SOCIAL_STORAGE__BUCKET=agent-chat-attachments
|
||||
SOCIAL_STORAGE__SIGNED_URL_TTL_SECONDS=600
|
||||
SOCIAL_STORAGE__MAX_FILE_SIZE_MB=20
|
||||
SOCIAL_STORAGE__RETENTION_DAYS=30
|
||||
|
||||
######
|
||||
# LLM API KEY
|
||||
SOCIAL_LLM__PROVIDER_KEYS__DASHSCOPE=
|
||||
SOCIAL_LLM__PROVIDER_KEYS__MINIMAX=
|
||||
SOCIAL_LLM__PROVIDER_KEYS__MOONSHOT=
|
||||
SOCIAL_LLM__PROVIDER_KEYS__DEEPSEEK=
|
||||
SOCIAL_LLM__PROVIDER_KEYS__ARK=
|
||||
SOCIAL_LLM__PROVIDER_KEYS__ZAI=
|
||||
@@ -0,0 +1,124 @@
|
||||
# Production Deploy Package
|
||||
|
||||
本目录是单机 `docker compose` 的生产交付包,架构为:
|
||||
|
||||
- 应用层:`litellm + web + worker-critical + worker-default + worker-bulk + init-job`
|
||||
- 中间件:`redis`
|
||||
- 数据与认证:云 Supabase(通过环境变量访问)
|
||||
- 反向代理:由服务器侧 nginx 托管(不在本目录编排)
|
||||
|
||||
## 交付物
|
||||
|
||||
1. `deploy/build-prod-image.sh`:构建并导出生产镜像。
|
||||
2. `deploy/docker-compose.prod.yml`:生产 Docker Compose 编排。
|
||||
3. `deploy/.env.prod.example`:生产环境变量模板。
|
||||
4. `deploy/README.md`:部署说明书。
|
||||
|
||||
## 安全基线
|
||||
|
||||
- `deploy/.env.prod.example` 仅作为模板,真实密钥请在服务器上填写到 `deploy/.env.prod`,不要提交仓库。
|
||||
- Redis 密码必填;为空时容器会启动失败。
|
||||
- 后端镜像默认使用非 root 用户运行。
|
||||
- 容器间通信仅走 Docker 内网(`redis`、`litellm` 服务名)。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```text
|
||||
deploy/
|
||||
├── build-prod-image.sh
|
||||
├── docker-compose.prod.yml
|
||||
├── .env.prod.example
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## 一次性交付:构建生产镜像
|
||||
|
||||
在仓库根目录执行:
|
||||
|
||||
```bash
|
||||
bash deploy/build-prod-image.sh
|
||||
```
|
||||
|
||||
成功后会产出:
|
||||
|
||||
- 镜像名:`social-app-backend:prod`(可用 `SOCIAL_BACKEND_IMAGE` 覆盖)
|
||||
- 归档文件:`deploy/social-app-backend-prod.tar.gz`
|
||||
|
||||
如果你要把镜像拷贝到另一台服务器,传输该 `.tar.gz` 后执行:
|
||||
|
||||
```bash
|
||||
gunzip -c social-app-backend-prod.tar.gz | docker load
|
||||
```
|
||||
|
||||
## 启动流程(生产)
|
||||
|
||||
### 1) 检查环境变量
|
||||
|
||||
先创建生产环境变量文件:
|
||||
|
||||
```bash
|
||||
cp deploy/.env.prod.example deploy/.env.prod
|
||||
```
|
||||
|
||||
确认 `deploy/.env.prod` 至少包含以下关键变量(云 Supabase 与数据库连接):
|
||||
|
||||
- `SOCIAL_SUPABASE__PUBLIC_URL`
|
||||
- `SOCIAL_SUPABASE__ANON_KEY`
|
||||
- `SOCIAL_SUPABASE__SERVICE_ROLE_KEY`
|
||||
- `SOCIAL_SUPABASE__JWT_SECRET`
|
||||
- `SOCIAL_DATABASE__HOST`
|
||||
- `SOCIAL_DATABASE__PORT`
|
||||
- `SOCIAL_DATABASE__NAME`
|
||||
- `SOCIAL_DATABASE__USER`
|
||||
- `SOCIAL_DATABASE__PASSWORD`
|
||||
- `SOCIAL_REDIS__PASSWORD`
|
||||
|
||||
说明:
|
||||
|
||||
- 容器内通信统一使用 Docker 内网:`SOCIAL_REDIS__HOST=redis`、`SOCIAL_LITELLM__HOST=litellm`。
|
||||
- `SOCIAL_WEB__HOST`/`SOCIAL_LITELLM__BIND_HOST` 是容器内监听地址,生产建议保持 `0.0.0.0`。
|
||||
|
||||
### 2) 启动常驻服务
|
||||
|
||||
```bash
|
||||
docker compose --env-file deploy/.env.prod -f deploy/docker-compose.prod.yml up -d redis litellm web worker-critical worker-default worker-bulk
|
||||
```
|
||||
|
||||
### 3) 执行一次性 bootstrap
|
||||
|
||||
```bash
|
||||
docker compose --env-file deploy/.env.prod -f deploy/docker-compose.prod.yml run --rm init-job
|
||||
```
|
||||
|
||||
### 4) 查看状态与日志
|
||||
|
||||
```bash
|
||||
docker compose --env-file deploy/.env.prod -f deploy/docker-compose.prod.yml ps
|
||||
docker compose --env-file deploy/.env.prod -f deploy/docker-compose.prod.yml logs -f web
|
||||
```
|
||||
|
||||
## 停止与重启
|
||||
|
||||
停止:
|
||||
|
||||
```bash
|
||||
docker compose --env-file deploy/.env.prod -f deploy/docker-compose.prod.yml down
|
||||
```
|
||||
|
||||
重启:
|
||||
|
||||
```bash
|
||||
docker compose --env-file deploy/.env.prod -f deploy/docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
## nginx 对接建议
|
||||
|
||||
- 反向代理到 `127.0.0.1:${SOCIAL_WEB__PORT}`(默认 `5775`)。
|
||||
- 仅开放 nginx 对外端口;应用容器仅发布到本机回环地址。
|
||||
- 如果 nginx 运行在宿主机,`web` 需要保留 `127.0.0.1:host_port:container_port` 端口映射。
|
||||
- 如果 nginx 也运行在 Docker 同网络内,可以移除 `web.ports`,改为容器内反向代理(例如 `proxy_pass http://web:5775`)。
|
||||
|
||||
## 已知约束
|
||||
|
||||
- LiteLLM 会在容器启动时动态生成 `/tmp/litellm-proxy-config.yaml`,依赖 `SOCIAL_LLM__PROVIDER_KEYS__*` 已配置。
|
||||
- `init-job` 为一次性任务,不长期驻留。
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
IMAGE_NAME="${SOCIAL_BACKEND_IMAGE:-social-app-backend:prod}"
|
||||
OUTPUT_FILE="$ROOT_DIR/deploy/social-app-backend-prod.tar.gz"
|
||||
|
||||
echo "[1/2] Building image: ${IMAGE_NAME}"
|
||||
docker build -t "${IMAGE_NAME}" -f "$ROOT_DIR/backend/Dockerfile" "$ROOT_DIR"
|
||||
|
||||
docker image inspect "${IMAGE_NAME}" >/dev/null
|
||||
|
||||
echo "[2/2] Exporting image archive: ${OUTPUT_FILE}"
|
||||
docker save "${IMAGE_NAME}" | gzip > "${OUTPUT_FILE}"
|
||||
|
||||
echo "Done"
|
||||
echo "Image: ${IMAGE_NAME}"
|
||||
echo "Archive: ${OUTPUT_FILE}"
|
||||
@@ -0,0 +1,185 @@
|
||||
name: social-app-prod
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: social-prod-redis
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- REDIS_PASSWORD=${SOCIAL_REDIS__PASSWORD}
|
||||
command: >
|
||||
sh -c 'test -n "$$REDIS_PASSWORD" && exec redis-server --appendonly yes --dir /data --requirepass "$$REDIS_PASSWORD" || (echo "REDIS_PASSWORD is required" >&2; exit 1)'
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "sh", "-c", "if [ -n \"$$REDIS_PASSWORD\" ]; then redis-cli -a \"$$REDIS_PASSWORD\" ping; else redis-cli ping; fi"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
|
||||
litellm:
|
||||
image: ${SOCIAL_BACKEND_IMAGE:-social-app-backend:prod}
|
||||
container_name: social-prod-litellm
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./.env.prod
|
||||
environment:
|
||||
- PYTHONPATH=/app/backend/src
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- SOCIAL_REDIS__HOST=redis
|
||||
- SOCIAL_REDIS__PORT=6379
|
||||
command: >
|
||||
sh -c '.venv/bin/python backend/scripts/build_litellm_proxy_config.py --output /tmp/litellm-proxy-config.yaml && .venv/bin/litellm --config /tmp/litellm-proxy-config.yaml --host ${SOCIAL_LITELLM__BIND_HOST:-0.0.0.0} --port ${SOCIAL_LITELLM__PORT:-3875}'
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"python",
|
||||
"-c",
|
||||
"import os,sys,urllib.request;port=os.getenv('SOCIAL_LITELLM__PORT','3875');u=f'http://127.0.0.1:{port}/health';sys.exit(0 if urllib.request.urlopen(u, timeout=3).getcode() < 500 else 1)",
|
||||
]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
web:
|
||||
image: ${SOCIAL_BACKEND_IMAGE:-social-app-backend:prod}
|
||||
container_name: social-prod-web
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./.env.prod
|
||||
environment:
|
||||
- PYTHONPATH=/app/backend/src
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- SOCIAL_RUNTIME__SERVICE_NAME=web
|
||||
- SOCIAL_RUNTIME__ENVIRONMENT=${SOCIAL_RUNTIME__ENVIRONMENT:-prod}
|
||||
- SOCIAL_REDIS__HOST=redis
|
||||
- SOCIAL_REDIS__PORT=6379
|
||||
- SOCIAL_LITELLM__HOST=litellm
|
||||
- SOCIAL_LITELLM__PORT=${SOCIAL_LITELLM__PORT:-3875}
|
||||
command: >
|
||||
sh -c '.venv/bin/uvicorn app:app --host ${SOCIAL_WEB__HOST:-0.0.0.0} --port ${SOCIAL_WEB__PORT:-5775} --workers ${SOCIAL_WEB__WORKERS:-2} --log-level $(printf "%s" "${SOCIAL_RUNTIME__LOG_LEVEL:-info}" | tr "[:upper:]" "[:lower:]")'
|
||||
ports:
|
||||
- "127.0.0.1:${SOCIAL_WEB__PORT:-5775}:${SOCIAL_WEB__PORT:-5775}"
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
litellm:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ../logs:/app/logs
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"python",
|
||||
"-c",
|
||||
"import os,sys,urllib.request;port=os.getenv('SOCIAL_WEB__PORT','5775');u=f'http://127.0.0.1:{port}/health';sys.exit(0 if urllib.request.urlopen(u, timeout=3).getcode() < 500 else 1)",
|
||||
]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
worker-critical:
|
||||
image: ${SOCIAL_BACKEND_IMAGE:-social-app-backend:prod}
|
||||
container_name: social-prod-worker-critical
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./.env.prod
|
||||
environment:
|
||||
- PYTHONPATH=/app/backend/src
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- SOCIAL_RUNTIME__SERVICE_NAME=worker-critical
|
||||
- SOCIAL_RUNTIME__ENVIRONMENT=${SOCIAL_RUNTIME__ENVIRONMENT:-prod}
|
||||
- SOCIAL_REDIS__HOST=redis
|
||||
- SOCIAL_REDIS__PORT=6379
|
||||
- SOCIAL_LITELLM__HOST=litellm
|
||||
- SOCIAL_LITELLM__PORT=${SOCIAL_LITELLM__PORT:-3875}
|
||||
command: >
|
||||
sh -c '.venv/bin/taskiq worker core.taskiq.app:critical_broker core.agentscope.runtime.tasks --workers ${SOCIAL_WORKER__GROUPS__CRITICAL__CONCURRENCY:-2}'
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
litellm:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ../logs:/app/logs
|
||||
|
||||
worker-default:
|
||||
image: ${SOCIAL_BACKEND_IMAGE:-social-app-backend:prod}
|
||||
container_name: social-prod-worker-default
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./.env.prod
|
||||
environment:
|
||||
- PYTHONPATH=/app/backend/src
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- SOCIAL_RUNTIME__SERVICE_NAME=worker-default
|
||||
- SOCIAL_RUNTIME__ENVIRONMENT=${SOCIAL_RUNTIME__ENVIRONMENT:-prod}
|
||||
- SOCIAL_REDIS__HOST=redis
|
||||
- SOCIAL_REDIS__PORT=6379
|
||||
- SOCIAL_LITELLM__HOST=litellm
|
||||
- SOCIAL_LITELLM__PORT=${SOCIAL_LITELLM__PORT:-3875}
|
||||
command: >
|
||||
sh -c '.venv/bin/taskiq worker core.taskiq.app:default_broker core.agentscope.runtime.tasks --workers ${SOCIAL_WORKER__GROUPS__DEFAULT__CONCURRENCY:-2}'
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
litellm:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ../logs:/app/logs
|
||||
|
||||
worker-bulk:
|
||||
image: ${SOCIAL_BACKEND_IMAGE:-social-app-backend:prod}
|
||||
container_name: social-prod-worker-bulk
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./.env.prod
|
||||
environment:
|
||||
- PYTHONPATH=/app/backend/src
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- SOCIAL_RUNTIME__SERVICE_NAME=worker-bulk
|
||||
- SOCIAL_RUNTIME__ENVIRONMENT=${SOCIAL_RUNTIME__ENVIRONMENT:-prod}
|
||||
- SOCIAL_REDIS__HOST=redis
|
||||
- SOCIAL_REDIS__PORT=6379
|
||||
- SOCIAL_LITELLM__HOST=litellm
|
||||
- SOCIAL_LITELLM__PORT=${SOCIAL_LITELLM__PORT:-3875}
|
||||
command: >
|
||||
sh -c '.venv/bin/taskiq worker core.taskiq.app:bulk_broker core.agentscope.runtime.tasks --workers ${SOCIAL_WORKER__GROUPS__BULK__CONCURRENCY:-1}'
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
litellm:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ../logs:/app/logs
|
||||
|
||||
init-job:
|
||||
image: ${SOCIAL_BACKEND_IMAGE:-social-app-backend:prod}
|
||||
container_name: social-prod-init-job
|
||||
restart: "no"
|
||||
env_file:
|
||||
- ./.env.prod
|
||||
environment:
|
||||
- PYTHONPATH=/app/backend/src
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- SOCIAL_RUNTIME__SERVICE_NAME=init-job
|
||||
- SOCIAL_RUNTIME__ENVIRONMENT=${SOCIAL_RUNTIME__ENVIRONMENT:-prod}
|
||||
- SOCIAL_REDIS__HOST=redis
|
||||
- SOCIAL_REDIS__PORT=6379
|
||||
- SOCIAL_LITELLM__HOST=litellm
|
||||
- SOCIAL_LITELLM__PORT=${SOCIAL_LITELLM__PORT:-3875}
|
||||
command: .venv/bin/python -m core.runtime.cli bootstrap
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
litellm:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ../logs:/app/logs
|
||||
profiles:
|
||||
- job
|
||||
|
||||
volumes:
|
||||
redis_data:
|
||||
Reference in New Issue
Block a user