diff --git a/.gitignore b/.gitignore index d8eb9c0..cc7efa1 100644 --- a/.gitignore +++ b/.gitignore @@ -196,6 +196,10 @@ Thumbs.db # Env .env +# Deployment docs (contain credentials, not for git) +*部署交接文档.md +*部署文档.md + # Claude CLAUDE.md .claude/ diff --git a/qiji部署交接文档.md b/qiji部署交接文档.md deleted file mode 100644 index 43adf6b..0000000 --- a/qiji部署交接文档.md +++ /dev/null @@ -1,602 +0,0 @@ -# 企迹 (qiji) 部署交接文档 - -## 1. 部署架构总览 - -``` -用户浏览器 → qj.dhdx.fun → 远程服务器(175.178.19.237) - │ - OpenResty (80/443) - │ - ┌──────────────┴──────────────┐ - ▼ ▼ - 前端容器 frp 隧道(18061) - qiji-frontend │ - (18063→80) frps 服务端 - (175.178.19.237) - │ - frp 隧道 - │ - frpc 客户端 - (本机服务器) - │ - ┌──────────┴──────────┐ - ▼ ▼ - FastAPI 后端 Casdoor 认证 - (8002端口) (18000端口, 共享) - │ - ▼ - PostgreSQL (5432, 共享) - MinIO (10.10.10.13:17051) -``` - -**一句话概括:** 用户访问 `http://qj.dhdx.fun`,OpenResty 将前端 `/` 路由到前端容器(18063),将 `/api/` 路由到 FRP 隧道(18061),frp 将流量转发到本机服务器的 qiji 后端(8002)。Casdoor 和 PostgreSQL 复用已有基础设施。 - -> **与 H3ConuMS2 的关系:** qiji 和 H3ConuMS2 共享同一套基础设施(远程服务器、FRP 隧道网格、OpenResty、Casdoor、PostgreSQL),只是各自占用不同的端口和域名。互不干扰。 - ---- - -## 2. 服务器信息 - -### 远程服务器(部署 frps 服务端 + 前端容器 + OpenResty) - -| 项目 | 值 | -|------|-----| -| IP 地址 | 175.178.19.237 | -| SSH 端口 | 7072 | -| SSH 用户 | root | -| SSH 密码 | (见实际交接信息) | -| SSH 密钥 | 已安装(本机 → 远程),可直接免密登录 | -| 系统 | Ubuntu 22.04 | -| 管理面板 | 1Panel (端口 7071) | -| 配置 | 2 vCPU, 3.6GB RAM | - -### 本机服务器(部署 frpc + 后端 + 数据库) - -| 项目 | 值 | -|------|-----| -| 角色 | 本地开发/服务机 | -| FRP 类型 | frpc 原生进程(非 Docker) | -| frpc 配置文件 | `/etc/frp/frpc.toml` | -| frpc 管理地址 | http://localhost:7000 (admin / your-frp-password) | -| 操作系统 | Ubuntu (带 Docker) | - ---- - -## 3. FRP 配置(新增 qiji 隧道) - -### 当前 FRP 状态 - -frpc 已在本机以原生进程运行(`/usr/bin/frpc -c /etc/frp/frpc.toml`),所有 7 条隧道正常运行。 - -**现有隧道列表(勿动):** - -| 隧道名称 | 本地端口 | 远程端口 | 用途 | -|----------|---------|---------|------| -| mysql | 3306 | 13306 | MySQL 远程访问 | -| postgresql | 5432 | 15432 | PostgreSQL 远程访问 | -| redis | 6379 | 16379 | Redis 远程访问 | -| gitea-web | 18003 | 18009 | Gitea | -| h3conums-v2-backend | 8000 | 18060 | H3ConuMS2 后端 | -| casdoor | 18000 | 18010 | Casdoor 认证 | -| gx-gp-notify | 18001 | 18011 | 通知服务 | - -### 新增 qiji 后端隧道 - -编辑 `/etc/frp/frpc.toml`,在文件末尾添加: - -```toml -[[proxies]] -name = "qiji-backend" -type = "tcp" -localIP = "127.0.0.1" -localPort = 8002 -remotePort = 18061 -transport.useEncryption = true -``` - -然后重启 frpc: - -```bash -# frpc 是原生进程,需要 kill 后重启 -# 查看当前进程 -ps aux | grep frpc - -# 重启(二选一) -# 方式一:如果通过 systemd 管理 -sudo systemctl restart frpc - -# 方式二:如果通过 entrypoint 脚本启动 -sudo kill $(pgrep frpc) && sudo /usr/local/bin/entrypoint.d/20-frpc-start.sh & -``` - -重启后验证隧道状态: - -```bash -curl -s http://localhost:7000/api/status -u admin:your-frp-password | python3 -m json.tool | grep -A5 qiji -``` - -应显示 `"status": "running"`。 - -> ⚠️ **注意:** frpc 使用 host 网络模式(原生进程监听 0.0.0.0),所以 `localIP = "127.0.0.1"` 是安全的——frpc 只连接本机回环地址。 - -### frps 服务端(远程,无需修改) - -frps 配置文件在远程 `/opt/1panel/apps/frps/frps/data/frps.toml`,**无需修改**。只要 frpc 请求的 `remotePort` (18061) 没被远程服务器其他服务占用,隧道即可自动建立。 - ---- - -## 4. 前端部署(远程服务器 Docker 容器) - -### 4.1 构建前端镜像(本机操作) - -前端使用 Vue 3 + Vite 构建,产物由 nginx 托管在 Docker 容器中。 - -**项目中的关键文件:** -- `frontend/Dockerfile` — 多阶段构建(node:20-alpine 构建 → nginx:alpine 运行) -- `frontend/nginx.conf` — nginx 配置(SPA try_files + `/api/` 反向代理) - -**构建前置:** 确保 `frontend/.env.production`(或构建时环境变量)中 Casdoor 配置正确。Casdoor 回调地址应为 `https://qj.dhdx.fun`(或实际域名)。如果没有 `.env.production` 文件,Vite 会使用 `.env` 中的值。 - -```bash -# 1. 进入前端目录 -cd /home/v6ole/pyproject/qiji/frontend - -# 2. (可选)创建生产环境变量文件 -cat > .env.production << 'EOF' -VITE_CASDOOR_ENDPOINT=https://casdoor.dhdx.fun -VITE_CASDOOR_CLIENT_ID=your-client-id -EOF - -# 3. 构建 Docker 镜像 -docker build -t qiji-frontend:latest . - -# 4. 验证镜像 -docker images | grep qiji-frontend -``` - -### 4.2 推送镜像到远程服务器 - -```bash -# 保存镜像并远程加载 -docker save qiji-frontend:latest | ssh -p 7072 root@175.178.19.237 "docker load" - -# 验证远程镜像已加载 -ssh -p 7072 root@175.178.19.237 "docker images | grep qiji-frontend" -``` - -### 4.3 在远程服务器启动前端容器 - -```bash -ssh -p 7072 root@175.178.19.237 " - # 停止并删除旧容器(如果存在) - docker stop qiji-frontend 2>/dev/null - docker rm qiji-frontend 2>/dev/null - - # 启动新容器 - # 容器内部 nginx 监听 80,映射到宿主机 18063 - docker run -d \ - --name qiji-frontend \ - --restart always \ - -p 18063:80 \ - qiji-frontend:latest -" -``` - -### 4.4 前端更新流程(日常使用) - -```bash -cd /home/v6ole/pyproject/qiji/frontend - -# 1. 修改代码后重新构建镜像 -docker build -t qiji-frontend:latest . - -# 2. 推送并重启(一键脚本) -docker save qiji-frontend:latest | ssh -p 7072 root@175.178.19.237 "docker load && docker stop qiji-frontend && docker rm qiji-frontend && docker run -d --name qiji-frontend --restart always -p 18063:80 qiji-frontend:latest" - -# 3. 验证 -curl -s http://175.178.19.237:18063/ | head -20 -``` - -### 4.5 查看前端容器日志 - -```bash -ssh -p 7072 root@175.178.19.237 "docker logs --tail 50 qiji-frontend" -``` - ---- - -## 5. OpenResty / Nginx 配置(远程服务器) - -### 5.1 新建站点配置 - -在远程服务器创建 `/opt/1panel/apps/openresty/openresty/conf/conf.d/qj.dhdx.fun.conf`: - -```nginx -server { - listen 80; - listen 443 ssl http2; - server_name qj.dhdx.fun; - index index.html; - - # 基础代理头 - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $server_name; - proxy_set_header X-Real-IP $remote_addr; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $http_connection; - - access_log /www/sites/qj.dhdx.fun/log/access.log main; - error_log /www/sites/qj.dhdx.fun/log/error.log; - - # ACME 验证 - location ^~ /.well-known/acme-challenge { - allow all; - root /usr/share/nginx/html; - } - - # 安全头 - add_header Strict-Transport-Security "max-age=31536000"; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header Referrer-Policy "strict-origin-when-cross-origin" always; - - # 前端(qiji-frontend 容器,端口 18063) - location / { - proxy_pass http://127.0.0.1:18063; - } - - # 后端 API(frp 隧道 → 本机后端 8002) - location /api/ { - proxy_pass http://127.0.0.1:18061; - proxy_read_timeout 3600s; - proxy_send_timeout 3600s; - proxy_connect_timeout 60s; - } - - # HTTP → HTTPS 重定向 - if ($scheme = http) { - return 301 https://$host$request_uri; - } - - # SSL 证书(通过 1Panel 申请 Let's Encrypt 或手动上传) - ssl_certificate /www/sites/qj.dhdx.fun/ssl/fullchain.pem; - ssl_certificate_key /www/sites/qj.dhdx.fun/ssl/privkey.pem; - ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1; - ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED; - ssl_prefer_server_ciphers on; - ssl_session_cache shared:SSL:10m; - ssl_session_timeout 10m; - error_page 497 https://$host$request_uri; - proxy_set_header X-Forwarded-Proto https; -} -``` - -### 5.2 应用配置 - -```bash -# 方式一:通过 SSH 直接创建配置文件 -cat << 'EOF' | ssh -p 7072 root@175.178.19.237 "cat > /opt/1panel/apps/openresty/openresty/conf/conf.d/qj.dhdx.fun.conf" -# (粘贴上面的完整 nginx 配置) -EOF - -# 创建日志目录 -ssh -p 7072 root@175.178.19.237 "mkdir -p /www/sites/qj.dhdx.fun/log /www/sites/qj.dhdx.fun/ssl" - -# 重载 OpenResty 配置(在 1Panel 面板 → OpenResty → 重载配置) -# 或通过命令行 -ssh -p 7072 root@175.178.19.237 "docker restart openresty" -``` - -> ⚠️ **注意:** 建议通过 1Panel 面板(http://175.178.19.237:7071)管理 OpenResty 站点配置和 SSL 证书。直接修改配置文件后需要在面板中**重载配置**才能生效。 - -### 5.3 DNS 配置 - -在域名 DNS 管理后台添加 A 记录: - -| 类型 | 主机记录 | 记录值 | -|------|---------|--------| -| A | qj | 175.178.19.237 | - -> 域名根据实际情况选择,如 `qj.dhdx.fun`、`qiji.dhdx.fun` 或 `weekly.dhdx.fun`。 - -### 5.4 SSL 证书 - -**方式一(推荐):** 通过 1Panel 面板为 `qj.dhdx.fun` 申请 Let's Encrypt 免费证书,自动续期。 - -**方式二:** 自签名证书(仅测试用,浏览器会报警告)。 - -**方式三:** 暂时只使用 HTTP(注释掉 ssl 相关配置行)。 - ---- - -## 6. 后端服务(本机服务器) - -### 6.1 启动方式 - -后端以原生进程运行(不容器化),与 H3ConuMS2 后端部署方式一致。 - -```bash -cd /home/v6ole/pyproject/qiji/backend - -# 确保 .env 文件配置正确 -cp .env.example .env -# 编辑 .env,填入实际值 - -# 启动后端(端口 8002,监听所有网卡) -uvicorn app.main:app --host 0.0.0.0 --port 8002 --reload -# 生产环境去掉 --reload: -# uvicorn app.main:app --host 0.0.0.0 --port 8002 -``` - -### 6.2 环境变量关键配置 - -| 配置项 | 说明 | 当前值 | -|--------|------|--------| -| `DATABASE_URL` | PostgreSQL 连接 | `postgresql+asyncpg://postgres:your-db-password@localhost:5432/qiji` | -| `CASDOOR_ENDPOINT` | Casdoor 地址 | `http://localhost:18000`(本机直连) | -| `CASDOOR_CLIENT_ID` | Casdoor 应用 ID | 与前端一致 | -| `CASDOOR_CLIENT_SECRET` | Casdoor 应用 Secret | 保密 | -| `MINIO_ENDPOINT` | MinIO 地址 | `10.10.10.13:17051` | -| `SECRET_KEY` | JWT 密钥 | 生产环境务必修改 | -| `CORS_ORIGINS` | 允许的前端域名 | 添加 `https://qj.dhdx.fun` | - -### 6.3 生产环境 CORS 配置 - -确保 `.env` 中 `CORS_ORIGINS` 包含生产域名: - -```env -CORS_ORIGINS=["http://localhost:5173","http://localhost:3000","https://qj.dhdx.fun","http://qj.dhdx.fun"] -``` - -### 6.4 后端更新流程 - -```bash -cd /home/v6ole/pyproject/qiji/backend - -# 1. 拉取最新代码 -git pull - -# 2. (如有新增依赖) -pip install -r requirements.txt - -# 3. 重启后端进程 -# 找到并杀掉旧进程 -pkill -f "uvicorn app.main:app" -# 重新启动 -uvicorn app.main:app --host 0.0.0.0 --port 8002 & -``` - -> 建议使用 `systemd` 管理后端进程(见第 10 节)。 - ---- - -## 7. MinIO 文件存储(已共享) - -### 7.1 当前状况 - -MinIO 部署在 `10.10.10.13:17051`(内网地址),qiji 使用 bucket `qiji-photos`。 - -**问题:** 前端通过预签名 URL 直传 MinIO。如果用户从公网(`qj.dhdx.fun`)访问,浏览器收到的预签名 URL 指向 `10.10.10.13:17051`,公网用户无法访问内网地址。 - -### 7.2 解决方案(三选一) - -**方案一(推荐):新增 MinIO FRP 隧道** - -在 `/etc/frp/frpc.toml` 中添加: - -```toml -[[proxies]] -name = "minio" -type = "tcp" -localIP = "10.10.10.13" -localPort = 17051 -remotePort = 18064 -transport.useEncryption = true -``` - -然后在 MinIO 端配置公网地址(或通过环境变量 `MINIO_ENDPOINT=175.178.19.237:18064`),使生成的预签名 URL 指向公网。 - -**方案二:后端代理 MinIO 流量** - -在 qiji 后端添加文件代理路由(如 `/api/files/{bucket}/{object}`),前端不直传 MinIO,而是通过后端中转。简单但增加后端带宽压力。 - -**方案三:仅限内网使用** - -如果 qiji 用户都在内网(通过 VPN 或内网 IP 访问),当前配置无需修改。 - -> **当前建议:** 如果公网用户需要上传/查看照片,选择方案一。如果只在内网使用,暂不处理。 - ---- - -## 8. Casdoor 认证(共享,已运行) - -qiji 与 H3ConuMS2 共用同一个 Casdoor 实例。 - -| 项目 | 值 | -|------|-----| -| 本机端口 | 18000 | -| FRP 远程端口 | 18010 | -| 外部访问 | `https://casdoor.dhdx.fun`(已有 Nginx 配置) | -| Casdoor 应用名 | `qiji-weekly-report` | - -### 配置要点 - -1. **Casdoor 回调地址** 必须添加 `https://qj.dhdx.fun/callback`(或实际域名) -2. 前端 `.env.production` 中 `VITE_CASDOOR_ENDPOINT` 指向 `https://casdoor.dhdx.fun` -3. 后端 `.env` 中 `CASDOOR_ENDPOINT` 指向 `http://localhost:18000`(本机直连,不经 FRP) - ---- - -## 9. 数据库(共享,已运行) - -qiji 使用已有的 PostgreSQL 实例,database 名为 `qiji`(与 H3ConuMS2 的 database 分开)。 - -| 项目 | 值 | -|------|-----| -| 本机端口 | 5432 | -| FRP 远程端口 | 15432 | -| Database | `qiji` | -| 远程连接 | `postgresql://postgres:your-db-password@175.178.19.237:15432/qiji` | - ---- - -## 10. 常见操作速查 - -### 查看 FRP 隧道状态 - -```bash -# 本机 frpc 管理 API -curl -s http://localhost:7000/api/status -u admin:your-frp-password | python3 -m json.tool - -# 只看 qiji 隧道 -curl -s http://localhost:7000/api/status -u admin:your-frp-password | python3 -c "import sys,json; d=json.load(sys.stdin); [print(p['name'],p['status'],p['local_addr'],'→',p['remote_addr']) for p in d.get('tcp',[])]" - -# 远程 frps 管理 API -curl -s http://175.178.19.237:18001/api/status -u admin:your-frp-password | python3 -m json.tool -``` - -### 重启 FRP - -```bash -# 本机 frpc(原生进程) -sudo kill $(pgrep frpc) && sudo /usr/local/bin/entrypoint.d/20-frpc-start.sh & - -# 远程 frps(Docker 容器) -ssh -p 7072 root@175.178.19.237 "docker restart Frps" -``` - -### 查看前端容器状态 - -```bash -ssh -p 7072 root@175.178.19.237 "docker ps --filter name=qiji-frontend" -ssh -p 7072 root@175.178.19.237 "docker logs --tail 30 qiji-frontend" -``` - -### 查看后端运行状态 - -```bash -# 检查后端进程 -ps aux | grep uvicorn | grep 8002 - -# 测试 API 健康 -curl http://localhost:8002/api/health -``` - -### 通过 FRP 隧道测试后端 - -```bash -# 从远程服务器测试 FRP 隧道是否通 -ssh -p 7072 root@175.178.19.237 "curl -s http://127.0.0.1:18061/api/health" - -# 从公网测试(如果 OpenResty 已配置) -curl -s https://qj.dhdx.fun/api/health -``` - -### 通过 1Panel 管理远程服务器 - -- 浏览器访问 `http://175.178.19.237:7071` -- 可视化管理:OpenResty 站点配置、SSL 证书申请、Docker 容器启停 - ---- - -## 11. 端口总览 - -### 远程服务器 (175.178.19.237) 端口 - -| 端口 | 用途 | 所属项目 | -|------|------|---------| -| 22 | SSH(通过 7072 映射) | 基础设施 | -| 7071 | 1Panel 管理面板 | 基础设施 | -| 7072 | SSH(外部访问端口) | 基础设施 | -| 80 | HTTP(OpenResty) | 基础设施 | -| 443 | HTTPS(OpenResty) | 基础设施 | -| 18000 | frps 控制端口 | 基础设施 | -| 18001 | frps 管理面板 | 基础设施 | -| 18002 | Halo 博客 | 其他 | -| 18003 | Speedtest | 其他 | -| 18004-18005 | OpenList | 其他 | -| 18006 | IP 监测 | 其他 | -| 18009 | ← Gitea (frp) | 共享 | -| 18010 | ← Casdoor (frp) | 共享 | -| 18011 | ← 通知服务 (frp) | 共享 | -| 13306 | ← MySQL (frp) | 共享 | -| 15432 | ← PostgreSQL (frp) | 共享 | -| 16379 | ← Redis (frp) | 共享 | -| 18020 | Sun-Panel | 其他 | -| 18051 | Bitwarden | 其他 | -| 18052 | CookieCloud | 其他 | -| 18054 | Bark Server | 其他 | -| 18055 | WeChat API | 其他 | -| 18060 | ← H3ConuMS2 后端 (frp) | H3ConuMS2 | -| **18061** | **← qiji 后端 (frp)** | **qiji (新增)** | -| 18062 | H3ConuMS2 前端容器 | H3ConuMS2 | -| **18063** | **qiji 前端容器** | **qiji (新增)** | -| ~~18064~~ | ~~MinIO (frp)~~ | **qiji (待定)** | - -### 本机服务器端口 - -| 端口 | 用途 | 所属项目 | -|------|------|---------| -| 3306 | MySQL | 共享 | -| 5432 | PostgreSQL | 共享 | -| 6379 | Redis | 共享 | -| 7000 | frpc 管理面板 | 基础设施 | -| 8000 | H3ConuMS2 后端 | H3ConuMS2 | -| **8002** | **qiji 后端** | **qiji** | -| 18000 | Casdoor | 共享 | -| 18001 | 通知服务 | 共享 | -| 18003 | Gitea | 共享 | - ---- - -## 12. 相关项目路径 - -| 路径 | 说明 | 所在服务器 | -|------|------|-----------| -| `/home/v6ole/pyproject/qiji/` | qiji 项目源码 | 本机 | -| `/home/v6ole/pyproject/qiji/backend/` | 后端源码 | 本机 | -| `/home/v6ole/pyproject/qiji/frontend/` | 前端源码 | 本机 | -| `/home/v6ole/pyproject/qiji/frontend/Dockerfile` | 前端镜像构建文件 | 本机 | -| `/home/v6ole/pyproject/qiji/frontend/nginx.conf` | 前端 nginx 配置 | 本机 | -| `/home/v6ole/pyproject/qiji/backend/.env` | 后端环境变量(不提交 git) | 本机 | -| `/etc/frp/frpc.toml` | frpc 配置(含 qiji 隧道) | 本机 | -| `/opt/1panel/apps/frps/frps/data/frps.toml` | frps 配置 | 远程 | -| `/opt/1panel/apps/openresty/openresty/conf/conf.d/qj.dhdx.fun.conf` | qiji 站点 Nginx 配置 | 远程 | -| `/opt/1panel/apps/openresty/openresty/www/sites/qj.dhdx.fun/ssl/` | qiji SSL 证书 | 远程 | -| `/home/v6ole/pyproject/qiji/qiji部署交接文档.md` | 本文档 | 本机 | - ---- - -## 13. 部署检查清单 - -初次部署时,按以下顺序逐项确认: - -- [ ] **后端**:`.env` 配置正确,`uvicorn` 启动在 8002 端口,`curl localhost:8002/api/health` 正常 -- [ ] **FRP 隧道**:`/etc/frp/frpc.toml` 添加 `qiji-backend` 隧道,重启 frpc 后状态为 `running` -- [ ] **FRP 验证**:从远程 `curl 127.0.0.1:18061/api/health` 能访问后端 -- [ ] **前端镜像**:`docker build -t qiji-frontend:latest .` 构建成功 -- [ ] **前端部署**:推送镜像到远程,容器运行在 18063 端口,`curl 175.178.19.237:18063/` 返回 HTML -- [ ] **OpenResty**:创建 `qj.dhdx.fun.conf`,重载配置 -- [ ] **DNS**:添加 A 记录 `qj → 175.178.19.237` -- [ ] **SSL**:通过 1Panel 申请 Let's Encrypt 证书(或自签名测试) -- [ ] **Casdoor**:确认回调地址添加 `https://qj.dhdx.fun/callback` -- [ ] **端到端**:浏览器访问 `https://qj.dhdx.fun`,登录→填报→查看周报全流程通过 -- [ ] **MinIO**:确认照片上传/查看功能正常(如果公网用户需要,配置 MinIO 隧道) - ---- - -## 14. 故障排查 - -| 现象 | 检查项 | -|------|--------| -| 页面打不开 | 远程前端容器是否运行:`docker ps \| grep qiji-frontend` | -| 页面打开但 API 报错 | FRP 隧道是否正常:`curl localhost:7000/api/status -u admin:your-frp-password` | -| API 返回 500 | 后端进程是否存活:`ps aux \| grep uvicorn \| grep 8002` | -| 登录回调失败 | Casdoor 回调地址是否包含生产域名;前端 `.env.production` 中 Casdoor 地址是否正确 | -| 照片上传失败 | MinIO 是否可达;预签名 URL 域名是否为公网可访问地址 | -| FRP 隧道状态不是 running | frpc 日志:`journalctl -u frpc -f` 或 `dmesg \| grep frpc` | - -> 文档创建日期:2026年6月26日 diff --git a/通用部署文档.md b/通用部署文档.md deleted file mode 100644 index 23dc327..0000000 --- a/通用部署文档.md +++ /dev/null @@ -1,520 +0,0 @@ -# 项目部署通用文档(FRP + OpenResty + Docker 平台) - -> **用途:** 本机开发 → 远程服务器(175.178.19.237) 的标准化部署方案。每个新项目复制此文档,填入具体端口和域名即可。 -> -> **最后更新:** 2026-06-26 - ---- - -## 0. 端口规划与分配表 - -### 0.1 分配规则 - -| 资源 | 端口范围 | 数量 | 说明 | -|------|---------|------|------| -| 基础设施 | 18000–18019 | 20 | frp / Casdoor / DB 隧道等 | -| 杂项服务 | 18020–18049 | 30 | Sun-Panel / Bitwarden 等 | -| 项目后端 (frp) | 18060–18079 | 20 | 每个项目占用 1 个 | -| 项目前端 (容器) | 18080–18099 | 20 | 每个项目占用 1 个 | -| 预留 | 18100–18199 | 100 | 未来扩展 | - -> **新项目默认分配:** 后端 frp 取 18060–18079 中最小可用端口,前端容器取 18080–18099 中最小可用端口。 - -### 0.2 当前占用表 - -#### 基础设施 & 共享服务 (18000–18019) - -| 端口 | 资源 | 类型 | 说明 | -|------|------|------|------| -| 18000 | frps 控制 | 基础设施 | FRP 服务端控制端口 | -| 18001 | frps 管理面板 | 基础设施 | frps dashboard | -| 18002 | Halo | 其他 | 博客 | -| 18003 | Speedtest | 其他 | 网速测试 | -| 18004 | OpenList | 其他 | 播放列表 | -| 18005 | OpenList | 其他 | 播放列表 | -| 18006 | IP 监测 | 其他 | 公网 IP 变更监测 | -| 18009 | Gitea (frp) | 共享 | Git 仓库 | -| 18010 | Casdoor (frp) | 共享 | 统一认证 | -| 18011 | 通知服务 (frp) | 共享 | 消息推送 | -| 13306 | MySQL (frp) | 共享 | 数据库远程访问 | -| 15432 | PostgreSQL (frp) | 共享 | 数据库远程访问 | -| 16379 | Redis (frp) | 共享 | 缓存远程访问 | - -#### 杂项服务 (18020–18049) - -| 端口 | 资源 | 说明 | -|------|------|------| -| 18020 | Sun-Panel | 导航面板 | -| 18051 | Bitwarden | 密码管理 | -| 18052 | CookieCloud | Cookie 同步 | -| 18054 | Bark Server | iOS 推送 | -| 18055 | WeChat API | 微信 API 服务 | - -#### 项目后端 (frp 隧道, 18060–18079) - -| 远程端口 | 本机端口 | 项目 | 隧道名称 | 状态 | -|---------|---------|------|---------|------| -| 18060 | 8000 | H3ConuMS2 | h3conums-v2-backend | ✅ 运行中 | -| 18061 | 8002 | 企迹 (qiji) | qiji-backend | ⬜ 待部署 | -| 18062 | — | **可用** | — | — | -| 18063 | — | **可用** | — | — | -| 18064 | — | **可用** | — | — | -| 18065 | — | **可用** | — | — | -| 18066 | — | **可用** | — | — | -| 18067 | — | **可用** | — | — | -| 18068 | — | **可用** | — | — | -| 18069 | — | **可用** | — | — | -| 18070 | — | **可用** | — | — | -| 18071 | — | **可用** | — | — | -| 18072 | — | **可用** | — | — | -| 18073 | — | **可用** | — | — | -| 18074 | — | **可用** | — | — | -| 18075 | — | **可用** | — | — | -| 18076 | — | **可用** | — | — | -| 18077 | — | **可用** | — | — | -| 18078 | — | **可用** | — | — | -| 18079 | — | **可用** | — | — | - -#### 项目前端 (Docker 容器, 18080–18099) - -| 远程端口 | 容器名 | 项目 | 状态 | -|---------|--------|------|------| -| 18062 | h3conums2-frontend | H3ConuMS2 (legacy) | ✅ 运行中 | -| 18063 | qiji-frontend | 企迹 (qiji) | ⬜ 待部署 | -| 18080 | — | **可用** | — | -| 18081 | — | **可用** | — | -| 18082 | — | **可用** | — | -| 18083 | — | **可用** | — | -| 18084 | — | **可用** | — | -| 18085 | — | **可用** | — | -| 18086 | — | **可用** | — | -| 18087 | — | **可用** | — | -| 18088 | — | **可用** | — | -| 18089 | — | **可用** | — | -| 18090 | — | **可用** | — | -| 18091 | — | **可用** | — | -| 18092 | — | **可用** | — | -| 18093 | — | **可用** | — | -| 18094 | — | **可用** | — | -| 18095 | — | **可用** | — | -| 18096 | — | **可用** | — | -| 18097 | — | **可用** | — | -| 18098 | — | **可用** | — | -| 18099 | — | **可用** | — | - -> ⚠️ **注意:** H3ConuMS2 前端用了 18062(属于后端范围),是历史遗留。新项目严格按范围分配:后端 frp 取 18060–18079,前端容器取 18080–18099。 - -### 0.3 本机端口占用 - -| 本机端口 | 服务 | 项目 | -|---------|------|------| -| 3306 | MySQL | 共享 | -| 5432 | PostgreSQL | 共享 | -| 6379 | Redis | 共享 | -| 7000 | frpc 管理面板 | 基础设施 | -| 8000 | FastAPI 后端 | H3ConuMS2 | -| 8002 | FastAPI 后端 | 企迹 (qiji) | -| **8004** | — **可用** | — | -| **8006** | — **可用** | — | -| **8008** | — **可用** | — | -| 18000 | Casdoor | 共享 | -| 18001 | 通知服务 | 共享 | -| 18003 | Gitea | 共享 | - -> **本机后端端口规划:** 新项目从 8004 开始,每次 +2(8004, 8006, 8008, ...),与 FRP 远程端口(18064, 18066, 18068, ...)保持对应关系,便于记忆。 - ---- - -## 1. 部署架构总览 - -``` -用户浏览器 → {DOMAIN} → 远程服务器(175.178.19.237) - │ - OpenResty (80/443) - │ - ┌──────────────┴──────────────┐ - ▼ ▼ - 前端容器 frp 隧道 - {FRONTEND_CONTAINER} ({FRP_REMOTE_PORT}) - (端口映射到宿主机) │ - frps 服务端 - (175.178.19.237) - │ - frp 隧道 - │ - frpc 客户端 - (本机服务器) - │ - ┌───────────┴───────────┐ - ▼ ▼ - FastAPI 后端 Casdoor 认证 - ({BACKEND_PORT}) (18000, 共享) - │ - ▼ - PostgreSQL (5432, 共享) -``` - ---- - -## 2. 服务器信息 - -### 远程服务器(部署 frps + 前端容器 + OpenResty) - -| 项目 | 值 | -|------|-----| -| IP 地址 | 175.178.19.237 | -| SSH 端口 | 7072 | -| SSH 用户 | root | -| SSH 方式 | 本机已配置免密登录 | -| 系统 | Ubuntu 22.04 | -| 管理面板 | 1Panel (http://175.178.19.237:7071) | -| Web 服务器 | OpenResty (1panel/openresty) | -| FRP 服务端 | frps (snowdreamtech/frps:0.69.1, host 网络) | -| frps 配置路径 | `/opt/1panel/apps/frps/frps/data/frps.toml` | -| OpenResty 站点配置 | `/opt/1panel/apps/openresty/openresty/conf/conf.d/` | - -### 本机服务器(部署 frpc + 后端 + 数据库) - -| 项目 | 值 | -|------|-----| -| 角色 | 本地开发/服务机 | -| FRP 类型 | frpc 原生进程(非 Docker) | -| frpc 配置文件 | `/etc/frp/frpc.toml` | -| frpc 管理 API | http://localhost:7000 (admin / 见实际配置) | -| frpc 进程 | `/usr/bin/frpc -c /etc/frp/frpc.toml` | -| Casdoor | localhost:18000(原生进程) | -| PostgreSQL | localhost:5432 | - ---- - -## 3. 新项目部署流程 - -### 3.1 第一步:分配端口 - -从 [0.2 端口占用表](#02-当前占用表) 中选取最小可用端口,记录在此: - -| 配置项 | 值 | 说明 | -|--------|-----|------| -| `PROJECT_NAME` | `_________` | 项目名称(英文短名,用于容器名和隧道名) | -| `DOMAIN` | `_________` | 域名(需配置 DNS) | -| `BACKEND_PORT` | `_________` | 本机后端端口(如 8004) | -| `FRP_REMOTE_PORT` | `_________` | FRP 隧道远程端口(从 18060–18079 选) | -| `FRONTEND_CONTAINER_PORT` | `_________` | 前端容器宿主机端口(从 18080–18099 选) | -| `FRP_TUNNEL_NAME` | `_________` | FRP 隧道名称(如 `xxx-backend`) | -| `CONTAINER_NAME` | `_________` | 前端容器名(如 `xxx-frontend`) | - -> 分配后请更新本文档的 [0.2 端口占用表](#02-当前占用表),标记为已占用。 - -### 3.2 第二步:添加 FRP 隧道 - -编辑 `/etc/frp/frpc.toml`,在末尾添加: - -```toml -[[proxies]] -name = "{FRP_TUNNEL_NAME}" -type = "tcp" -localIP = "127.0.0.1" -localPort = {BACKEND_PORT} -remotePort = {FRP_REMOTE_PORT} -transport.useEncryption = true -``` - -重启 frpc: - -```bash -# frpc 是原生进程,kill 后自动重启(由 entrypoint 脚本守护) -sudo kill $(pgrep frpc) && sudo /usr/local/bin/entrypoint.d/20-frpc-start.sh & -``` - -验证隧道: - -```bash -curl -s http://localhost:7000/api/status -u admin:{FRP_PASSWORD} | python3 -m json.tool | grep {FRP_TUNNEL_NAME} -# 应显示 "status": "running" -``` - -### 3.3 第三步:配置后端 - -确保后端 `.env` 中: - -```env -CORS_ORIGINS=["http://localhost:5173","https://{DOMAIN}","http://{DOMAIN}"] -CASDOOR_ENDPOINT=http://localhost:18000 -``` - -启动后端: - -```bash -cd /home/v6ole/pyproject/{PROJECT_NAME}/backend -uvicorn app.main:app --host 0.0.0.0 --port {BACKEND_PORT} & -``` - -### 3.4 第四步:构建并部署前端 - -前端需要有 `Dockerfile`(多阶段构建:node 构建 + nginx 运行)和 `nginx.conf`。 - -```bash -# 1. 构建镜像 -cd /home/v6ole/pyproject/{PROJECT_NAME}/frontend -docker build -t {CONTAINER_NAME}:latest . - -# 2. 推送到远程 -docker save {CONTAINER_NAME}:latest | ssh -p 7072 root@175.178.19.237 "docker load" - -# 3. 启动容器 -ssh -p 7072 root@175.178.19.237 " - docker stop {CONTAINER_NAME} 2>/dev/null - docker rm {CONTAINER_NAME} 2>/dev/null - docker run -d \ - --name {CONTAINER_NAME} \ - --restart always \ - -p {FRONTEND_CONTAINER_PORT}:80 \ - {CONTAINER_NAME}:latest -" - -# 4. 验证 -curl -s http://175.178.19.237:{FRONTEND_CONTAINER_PORT}/ | head -5 -``` - -### 3.5 第五步:配置 OpenResty - -在远程创建 `/opt/1panel/apps/openresty/openresty/conf/conf.d/{DOMAIN}.conf`: - -```nginx -server { - listen 80; - listen 443 ssl http2; - server_name {DOMAIN}; - index index.html; - - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $server_name; - proxy_set_header X-Real-IP $remote_addr; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $http_connection; - - access_log /www/sites/{DOMAIN}/log/access.log main; - error_log /www/sites/{DOMAIN}/log/error.log; - - # ACME 验证 - location ^~ /.well-known/acme-challenge { - allow all; - root /usr/share/nginx/html; - } - - # 安全头 - add_header Strict-Transport-Security "max-age=31536000"; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header Referrer-Policy "strict-origin-when-cross-origin" always; - - # 前端 - location / { - proxy_pass http://127.0.0.1:{FRONTEND_CONTAINER_PORT}; - } - - # 后端 API - location /api/ { - proxy_pass http://127.0.0.1:{FRP_REMOTE_PORT}; - proxy_read_timeout 3600s; - proxy_send_timeout 3600s; - proxy_connect_timeout 60s; - } - - # HTTP → HTTPS - if ($scheme = http) { - return 301 https://$host$request_uri; - } - - # SSL(通过 1Panel 申请 Let's Encrypt) - ssl_certificate /www/sites/{DOMAIN}/ssl/fullchain.pem; - ssl_certificate_key /www/sites/{DOMAIN}/ssl/privkey.pem; - ssl_protocols TLSv1.3 TLSv1.2; - ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; - ssl_prefer_server_ciphers on; - ssl_session_cache shared:SSL:10m; - ssl_session_timeout 10m; - error_page 497 https://$host$request_uri; - proxy_set_header X-Forwarded-Proto https; -} -``` - -应用配置: - -```bash -# 创建日志和 SSL 目录 -ssh -p 7072 root@175.178.19.237 "mkdir -p /www/sites/{DOMAIN}/log /www/sites/{DOMAIN}/ssl" - -# 上传配置文件 -scp -P 7072 /path/to/{DOMAIN}.conf root@175.178.19.237:/opt/1panel/apps/openresty/openresty/conf/conf.d/ - -# 重载 OpenResty(在 1Panel 面板操作,或命令行) -ssh -p 7072 root@175.178.19.237 "docker restart openresty" -``` - -### 3.6 第六步:配置 DNS + SSL - -1. **DNS:** 在域名管理后台添加 A 记录 `{DOMAIN}` → `175.178.19.237` -2. **SSL:** 通过 1Panel 面板为 `{DOMAIN}` 申请 Let's Encrypt 证书 -3. **Casdoor:** 在 Casdoor 应用中添加回调地址 `https://{DOMAIN}/callback` - -### 3.7 第七步:验证 - -按顺序检查: - -- [ ] 后端进程:`ps aux | grep uvicorn | grep {BACKEND_PORT}` -- [ ] 本机 API:`curl http://localhost:{BACKEND_PORT}/api/health` -- [ ] FRP 隧道:管理 API 中状态为 `running` -- [ ] 远程 API:`ssh -p 7072 root@175.178.19.237 "curl -s http://127.0.0.1:{FRP_REMOTE_PORT}/api/health"` -- [ ] 前端容器:`ssh -p 7072 root@175.178.19.237 "docker ps | grep {CONTAINER_NAME}"` -- [ ] 前端页面:`curl -s http://175.178.19.237:{FRONTEND_CONTAINER_PORT}/ | head -3` -- [ ] OpenResty:`curl -s https://{DOMAIN}/api/health` -- [ ] 完整流程:浏览器访问 `https://{DOMAIN}` → 登录 → 功能验证 - ---- - -## 4. 前端 Dockerfile 模板 - -```dockerfile -FROM node:20-alpine AS build -WORKDIR /app -COPY package*.json ./ -RUN npm ci -COPY . . -RUN npm run build - -FROM nginx:alpine -COPY --from=build /app/dist /usr/share/nginx/html -COPY nginx.conf /etc/nginx/conf.d/default.conf -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] -``` - -> **nginx.conf 说明:** 容器内 nginx 只需处理静态文件 SPA fallback。API 代理由边缘 OpenResty 负责,容器内不需要 `/api/` 代理配置。如需容器独立运行(非本平台),保留 `/api/` 代理指向 `backend` 容器名。 - -### 极简 nginx.conf(平台部署用) - -```nginx -server { - listen 80; - server_name _; - root /usr/share/nginx/html; - index index.html; - - location / { - try_files $uri $uri/ /index.html; - } - - # 可选:健康检查 - location /health { - return 200 "ok"; - } -} -``` - ---- - -## 5. 日常运维速查 - -### 5.1 FRP 管理 - -```bash -# 查看所有隧道状态 -curl -s http://localhost:7000/api/status -u admin:{FRP_PASSWORD} | python3 -c " -import sys,json -d=json.load(sys.stdin) -for p in d.get('tcp',[]): - print(f\"{p['name']:30s} {p['status']:8s} {p['local_addr']:20s} → {p['remote_addr']}\") -" - -# 重启 frpc -sudo kill $(pgrep frpc) && sudo /usr/local/bin/entrypoint.d/20-frpc-start.sh & - -# 远程 frps 日志 -ssh -p 7072 root@175.178.19.237 "docker logs --tail 20 Frps" -``` - -### 5.2 前端容器管理 - -```bash -# 列出所有前端容器 -ssh -p 7072 root@175.178.19.237 "docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}'" - -# 更新前端(构建→推送→重启) -cd /home/v6ole/pyproject/{PROJECT_NAME}/frontend -docker build -t {CONTAINER_NAME}:latest . && \ - docker save {CONTAINER_NAME}:latest | ssh -p 7072 root@175.178.19.237 \ - "docker load && docker stop {CONTAINER_NAME} && docker rm {CONTAINER_NAME} && docker run -d --name {CONTAINER_NAME} --restart always -p {FRONTEND_CONTAINER_PORT}:80 {CONTAINER_NAME}:latest" - -# 查看日志 -ssh -p 7072 root@175.178.19.237 "docker logs --tail 50 {CONTAINER_NAME}" -``` - -### 5.3 OpenResty 管理 - -```bash -# 检查配置语法 -ssh -p 7072 root@175.178.19.237 "docker exec openresty nginx -t" - -# 重载配置(推荐通过 1Panel 面板操作) -ssh -p 7072 root@175.178.19.237 "docker restart openresty" - -# 查看访问日志 -ssh -p 7072 root@175.178.19.237 "tail -50 /www/sites/{DOMAIN}/log/access.log" -``` - -### 5.4 后端管理 - -```bash -# 查看所有后端进程 -ps aux | grep uvicorn | grep -v grep - -# 重启后端 -pkill -f "uvicorn app.main:app.*{BACKEND_PORT}" -cd /home/v6ole/pyproject/{PROJECT_NAME}/backend && uvicorn app.main:app --host 0.0.0.0 --port {BACKEND_PORT} & -``` - ---- - -## 6. 故障排查 - -| 现象 | 检查项 | -|------|--------| -| 页面 502/504 | OpenResty 代理目标可达?前端容器运行?FRP 隧道正常? | -| API 无响应 | `curl localhost:{BACKEND_PORT}/api/health` 本机是否通?FRP 隧道状态? | -| 登录/回调失败 | Casdoor 回调地址是否包含 `https://{DOMAIN}/callback`?前端环境变量 Casdoor 地址是否正确? | -| 静态资源 404 | 前端构建是否成功?nginx try_files 配置是否正确? | -| FRP 隧道 offline | frpc 进程是否存活?远程端口是否被占用?网络是否可达? | -| SSL 证书错误 | 证书是否过期?1Panel 自动续期是否正常? | -| CORS 报错 | 后端 `CORS_ORIGINS` 是否包含生产域名? | - ---- - -## 7. 已有项目索引 - -| 项目 | 域名 | 后端(本机) | FRP(远程) | 前端(远程) | 文档 | -|------|------|-----------|----------|-----------|------| -| H3ConuMS2 | onu.dhdx.fun | 8000 | 18060 | 18062 | H3ConuMS2部署交接文档.md | -| 企迹 (qiji) | qj.dhdx.fun | 8002 | 18061 | 18063 | qiji部署交接文档.md | - ---- - -## 8. 关键注意事项 - -1. **frpc 是原生进程**:修改 `/etc/frp/frpc.toml` 后需手动 kill + 重启,不能用 `systemctl` -2. **frps 是 Docker 容器**:远程 frps 配置在容器内,重启容器即生效 -3. **OpenResty 由 1Panel 管理**:直接改配置文件后需在面板中重载,或重启 openresty 容器 -4. **端口不可冲突**:分配新端口前务必检查 [0.2 端口占用表](#02-当前占用表) -5. **分配后更新本文档**:每次新增项目,更新端口占用表和项目索引 -6. **Casdoor 共享**:所有项目共用同一个 Casdoor 实例(本机 18000) -7. **数据库隔离**:不同项目使用同一 PostgreSQL 实例的不同 database -8. **frp 认证 token** 在 frpc 和 frps 两端必须一致 -9. **所有 frp 隧道已启用加密传输**(`transport.useEncryption = true`) -10. **MinIO** 在内网 `10.10.10.13:17051`,公网项目如需文件上传/查看,需要额外配置 frp 隧道或后端代理 - ---- - -> **模板使用说明:** 新项目部署时复制此文档,将 `{PROJECT_NAME}`、`{DOMAIN}`、`{BACKEND_PORT}`、`{FRP_REMOTE_PORT}`、`{FRONTEND_CONTAINER_PORT}`、`{FRP_TUNNEL_NAME}`、`{CONTAINER_NAME}` 替换为实际值,然后按第三章流程操作。