Files
yunke/docker-compose.server.yml
T
浅念 6880ff1b97 feat(auth): 登录页(用户名/密码换 API token)+ 前端全链路登录态
后端:
- POST /api/auth/login:admin 账密(Settings 默认 admin/admin123,
  ADMIN_USER/ADMIN_PASSWORD 可覆盖)换取共享 api_token,免 token 中间件
- 防爆破:同 IP 15min 窗口内 5 次失败锁 15min(429),成功登录清零
- 错误信息统一「用户名或密码错误」(防用户名枚举)
- 测试 7 个新增(TDD),全量 500 绿

前端:
- LoginView + 路由守卫(未登录跳 /login,回跳 redirect)
- axios 请求拦截器自动带 Bearer;401 清登录态回登录页
- 媒体直链统一 mediaUrl() 带 ?token=(img/audio 标签发不了 header),
  替换 4 处裸拼;downloadBlob 手动带头
- isLoggedIn 按 key 存在判定 — 后端鉴权关闭时空 token 不锁死登录页
- MainLayout 侧栏底部退出登录

compose:两份编排透传 ADMIN_USER/ADMIN_PASSWORD(默认 admin/admin123)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-03 15:03:20 +08:00

103 lines
3.7 KiB
YAML

# docker-compose.server.yml — 服务器(阿里云 ECS + 1Panel)专用生产编排。
#
# 与 docker-compose.yml(本地五服务开发栈)的差异:
# - 不含 mysql/redis:MySQL 复用 1Panel 已装实例(已建 yunke 库,用户权限
# 「所有区域(%)」,容器经宿主机内网 IP 访问,3306 不对公网);redis 应用
# 尚未接线(config 里只有一个默认 URL),省一份常驻内存——app 将来接
# redis 时记得同步回这里。
# - backend/scheduler 挂到 external 网络 1panel-network(1Panel 的 MySQL
# 容器 1Panel-mysql-YL0F 只绑 127.0.0.1:3306,宿主机网络够不着;加入它
# 所在网络后用容器名直连,不改 MySQL 任何配置)。MYSQL_HOST 填该容器名。
# frontend 不需要——它只跟 backend 说话。
# - 端口只绑 127.0.0.1:frontend 供 1Panel OpenResty(host 网络)反代;
# backend 不映射宿主机(/api 由 frontend 容器内 nginx 反代,见
# yunkes/nginx.conf)。
# - TZ=Asia/Shanghai:调度按北京时间触发(FULL_SYNC_HOUR=2 即北京 02:00)。
#
# 用法(服务器仓库根目录,完整步骤见 docs/DEPLOY.md):
# docker compose -f docker-compose.server.yml --env-file .env up -d --build
# docker compose -f docker-compose.server.yml logs -f scheduler
services:
backend:
build:
context: ./yunke-server
dockerfile: Dockerfile
restart: unless-stopped
command:
- uvicorn
- app.api.main:app
- --host
- 0.0.0.0
- --port
- "8000"
environment:
DB_URL: mysql+asyncmy://${MYSQL_USER:-yunke}:${MYSQL_PASSWORD}@${MYSQL_HOST:?set MYSQL_HOST in .env}:${MYSQL_PORT:-3306}/${MYSQL_DATABASE:-yunke}
MEDIA_DIR: /data/media
YUNKE_BASE_URL: ${YUNKE_BASE_URL:-https://crm2.yunkecn.com}
YUNKE_USERNAME: ${YUNKE_USERNAME:-}
YUNKE_PASSWORD: ${YUNKE_PASSWORD:-}
YK_USERID: ${YK_USERID:-}
YK_COMPANY: ${YK_COMPANY:-}
YK_COOKIE: ${YK_COOKIE:-}
API_TOKEN: ${API_TOKEN:-}
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-}
# 登录页账密(ADMIN_USER/ADMIN_PASSWORD,默认 admin/admin123,可在 .env 覆盖)
ADMIN_USER: ${ADMIN_USER:-admin}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123}
TZ: Asia/Shanghai
networks:
- default
- 1panel
volumes:
# 媒体 bind mount:服务器上 ./yunke-server/media 数据直观可见、好备份
- ./yunke-server/media:/data/media
scheduler:
build:
context: ./yunke-server
dockerfile: Dockerfile
restart: unless-stopped
command:
- python
- -m
- app.sync.cli
- scheduler
environment:
DB_URL: mysql+asyncmy://${MYSQL_USER:-yunke}:${MYSQL_PASSWORD}@${MYSQL_HOST:?set MYSQL_HOST in .env}:${MYSQL_PORT:-3306}/${MYSQL_DATABASE:-yunke}
MEDIA_DIR: /data/media
YUNKE_BASE_URL: ${YUNKE_BASE_URL:-https://crm2.yunkecn.com}
YUNKE_USERNAME: ${YUNKE_USERNAME:-}
YUNKE_PASSWORD: ${YUNKE_PASSWORD:-}
YK_USERID: ${YK_USERID:-}
YK_COMPANY: ${YK_COMPANY:-}
YK_COOKIE: ${YK_COOKIE:-}
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-}
# 调度节奏(北京时间):每晚全量 + 每 4h 消息增量
FULL_SYNC_HOUR: ${FULL_SYNC_HOUR:-2}
MESSAGE_INTERVAL_HOURS: ${MESSAGE_INTERVAL_HOURS:-4}
TZ: Asia/Shanghai
networks:
- default
- 1panel
volumes:
- ./yunke-server/media:/data/media
frontend:
build:
context: ./yunkes
dockerfile: Dockerfile
restart: unless-stopped
depends_on:
- backend
ports:
# 只绑 127.0.0.1:公网流量一律走 1Panel OpenResty 反代(80/443),
# 5173 不暴露到安全组
- "127.0.0.1:5173:80"
networks:
# 1Panel 的 MySQL 容器所在网络(external:只挂靠,不创建不修改)
1panel:
name: 1panel-network
external: true