diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..65d0ed0 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,90 @@ +name: Deploy + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: prod-deploy + env: + DEPLOY_BASE: /opt/opc-manager + REPO_URL: https://qiukai:${{ secrets.DEPLOY_TOKEN }}@git.qiukai.me/qiukai/opc-manager.git + SERVICE_NAME: opc-manager + steps: + - name: Clone and deploy + run: | + set -e + RELEASE_ID="${{ github.sha }}" + RELEASE_DIR="${DEPLOY_BASE}/releases/${RELEASE_ID}" + CLONE_DIR="/tmp/opc-deploy-${RELEASE_ID}" + + echo "=== 1. Clone repository ===" + rm -rf "${CLONE_DIR}" + git clone --depth 1 --branch main "${REPO_URL}" "${CLONE_DIR}" + + echo "=== 2. Prepare release directory ===" + rm -rf "${RELEASE_DIR}" + mkdir -p "${RELEASE_DIR}" + + # Copy repo content to release dir (exclude .git, .env, venv, data) + rsync -a --exclude='.git' \ + --exclude='.env' \ + --exclude='.env.local' \ + --exclude='.venv' \ + --exclude='data/uploads' \ + --exclude='data/opc.sqlite' \ + --exclude='__pycache__' \ + --exclude='.gitea' \ + "${CLONE_DIR}/" "${RELEASE_DIR}/" + + echo "=== 3. Link shared resources ===" + # .env from shared dir (not in git) + ln -sfn "${DEPLOY_BASE}/shared/.env" "${RELEASE_DIR}/.env" + # uploads directory from shared (persist across releases) + mkdir -p "${DEPLOY_BASE}/shared/uploads" + ln -sfn "${DEPLOY_BASE}/shared/uploads" "${RELEASE_DIR}/data/uploads" + + echo "=== 4. Setup Python venv ===" + cd "${RELEASE_DIR}" + python3 -m venv .venv + . .venv/bin/activate + pip install --no-cache-dir -r requirements.txt + + echo "=== 5. Restart service ===" + # Update WorkingDirectory in service via symlink approach + # The systemd service points to /opt/opc-manager/current + ln -sfn "${RELEASE_DIR}" "${DEPLOY_BASE}/current" + systemctl restart "${SERVICE_NAME}" + sleep 3 + + echo "=== 6. Health check ===" + for i in 1 2 3 4 5; do + if curl -fsS http://127.0.0.1:5177/api/health >/dev/null 2>&1; then + echo "Health check passed" + break + fi + echo "Attempt $i: waiting for service..." + sleep 2 + done + + # Final verify + if ! curl -fsS http://127.0.0.1:5177/api/health >/dev/null 2>&1; then + echo "ERROR: Health check failed after 5 attempts" + echo "Rolling back to previous release..." + PREV=$(ls -t "${DEPLOY_BASE}/releases" | sed -n '2p') + if [ -n "${PREV}" ]; then + ln -sfn "${DEPLOY_BASE}/releases/${PREV}" "${DEPLOY_BASE}/current" + systemctl restart "${SERVICE_NAME}" + echo "Rolled back to ${PREV}" + fi + exit 1 + fi + + echo "=== 7. Cleanup old releases ===" + find "${DEPLOY_BASE}/releases" -mindepth 1 -maxdepth 1 -type d | sort | head -n -5 | xargs -r rm -rf + + echo "=== 8. Cleanup temp ===" + rm -rf "${CLONE_DIR}" + + echo "=== Deploy complete: ${RELEASE_ID} ===" diff --git a/backend/flask_app.py b/backend/flask_app.py index 2bf15c9..aa0fb26 100644 --- a/backend/flask_app.py +++ b/backend/flask_app.py @@ -1046,7 +1046,7 @@ def delete_file(file_id): @app.route("/api/health") def health(): - return jsonify({"ok": True, "db": str(DB_PATH)}) + return jsonify({"ok": True, "service": "opc-manager"}) init_db() diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..f970f4f --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,193 @@ +# OPC-Manager 自动化部署指南 + +## 架构 + +``` +开发者 push main → Gitea 仓库 (git.qiukai.me) + ↓ + Gitea Actions 触发 + ↓ + Runner(跑在业务服务器 82.157.208.197 上) + ↓ + git clone → rsync 到 release 目录 + ↓ + 创建 venv → pip install → systemctl restart + ↓ + 健康检查 → 切换 current 软链 → 清理旧版本 +``` + +## 服务器目录结构 + +``` +/opt/opc-manager/ +├── releases/ +│ ├── abc1234/ ← 本次发布(commit sha) +│ ├── def5678/ ← 上次发布 +│ └── ... ← 保留最近 5 个 +├── shared/ +│ ├── .env ← 环境变量(不进 git,持久化) +│ └── uploads/ ← 上传的文件(持久化,跨版本共享) +└── current → releases/abc1234 ← 软链,指向当前生效版本 +``` + +## 一次性准备(在业务服务器上执行) + +### 1. 安装 Gitea Actions Runner + +```bash +# 下载 act_runner +cd /opt +wget https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64 -O act_runner +chmod +x act_runner +mv act_runner /usr/local/bin/ + +# 注册 runner(到 git.qiukai.me) +# 先在 Gitea 网页:仓库设置 → Actions → Runners → New runner,获取 token +act_runner register \ + --instance https://git.qiukai.me \ + --token \ + --name prod-deploy \ + --labels prod-deploy \ + --no-interactive + +# 安装为系统服务 +act_runner daemon --config /etc/act_runner/config.yaml & +# 或用 systemd: +cat > /etc/systemd/system/act-runner.service <<'EOF' +[Unit] +Description=Gitea Actions Runner +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/act_runner daemon +Restart=on-failure +Environment=HOME=/root + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable --now act-runner +``` + +### 2. 创建部署目录结构 + +```bash +mkdir -p /opt/opc-manager/{releases,shared/uploads} +``` + +### 3. 创建 .env 文件 + +```bash +cat > /opt/opc-manager/shared/.env <<'EOF' +SECRET_KEY=改成一串随机字符串_至少32位 +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USER=opc +DB_PASSWORD=opc123456 +DB_NAME=opc +FLASK_DEBUG=false +EOF + +chmod 600 /opt/opc-manager/shared/.env +``` + +### 4. 安装 systemd service + +```bash +# 从仓库的 deploy/opc-manager.service 复制 +cat > /etc/systemd/system/opc-manager.service <<'EOF' +[Unit] +Description=OPC Manager (Flask) +After=network.target mysql.service + +[Service] +Type=simple +WorkingDirectory=/opt/opc-manager/current +ExecStart=/opt/opc-manager/current/.venv/bin/gunicorn --preload -w 4 -b 0.0.0.0:5177 backend.flask_app:app +Restart=on-failure +RestartSec=5 +EnvironmentFile=/opt/opc-manager/current/.env + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable opc-manager +``` + +### 5. 配置 Gitea Secret + +在 Gitea 网页操作: +1. 进入仓库 `qiukai/opc-manager` +2. 设置 → Actions → Secrets → New Secret +3. Name: `DEPLOY_TOKEN` +4. Value: 你的 Gitea Personal Access Token(需要有 repo 读权限) + - 生成路径:头像 → 设置 → 应用 → 生成令牌 + +### 6. 首次手动部署 + +push 代码前,先手动跑一次确保目录结构正确: + +```bash +cd /opt/opc-manager +git clone --depth 1 --branch main https://git.qiukai.me/qiukai/opc-manager.git /tmp/opc-init +RELEASE_DIR=/opt/opc-manager/releases/initial +mkdir -p "${RELEASE_DIR}" +rsync -a --exclude='.git' --exclude='.env' --exclude='.venv' /tmp/opc-init/ "${RELEASE_DIR}/" +ln -sfn /opt/opc-manager/shared/.env "${RELEASE_DIR}/.env" +ln -sfn /opt/opc-manager/shared/uploads "${RELEASE_DIR}/data/uploads" +cd "${RELEASE_DIR}" +python3 -m venv .venv +. .venv/bin/activate +pip install -r requirements.txt +ln -sfn "${RELEASE_DIR}" /opt/opc-manager/current +systemctl start opc-manager +curl http://127.0.0.1:5177/api/health +rm -rf /tmp/opc-init +``` + +## 日常使用 + +### 发布新版本 +```bash +# 本地 +git push origin main +# 自动触发 Gitea Actions → 服务器自动部署 +``` + +### 查看部署状态 +```bash +# 在 Gitea 网页:仓库 → Actions 查看部署日志 +# 或在服务器: +systemctl status opc-manager +ls -la /opt/opc-manager/current +``` + +### 回滚 +```bash +# 列出历史版本 +ls -t /opt/opc-manager/releases +# 切换到上一个版本 +PREV=$(ls -t /opt/opc-manager/releases | sed -n '2p') +ln -sfn "/opt/opc-manager/releases/${PREV}" /opt/opc-manager/current +systemctl restart opc-manager +``` + +## Nginx 反代(可选) + +```nginx +server { + listen 80; + server_name opc.yxcowork.vip; + location / { + proxy_pass http://127.0.0.1:5177; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` diff --git a/deploy/opc-manager.service b/deploy/opc-manager.service new file mode 100644 index 0000000..0b15ca5 --- /dev/null +++ b/deploy/opc-manager.service @@ -0,0 +1,14 @@ +[Unit] +Description=OPC Manager (Flask) +After=network.target mysql.service + +[Service] +Type=simple +WorkingDirectory=/opt/opc-manager/current +ExecStart=/opt/opc-manager/current/.venv/bin/gunicorn --preload -w 4 -b 0.0.0.0:5177 backend.flask_app:app +Restart=on-failure +RestartSec=5 +EnvironmentFile=/opt/opc-manager/current/.env + +[Install] +WantedBy=multi-user.target diff --git a/deploy/服务器配置任务提示词.md b/deploy/服务器配置任务提示词.md new file mode 100644 index 0000000..42f118b --- /dev/null +++ b/deploy/服务器配置任务提示词.md @@ -0,0 +1,82 @@ +# OPC-Manager 自动化部署配置任务 + +## 背景 +OPC-Manager 项目(Flask + MySQL,端口 5177)需要配置 Gitea Actions 自动化部署。参考 SalesManager 的模式:push 到 main 分支后自动部署到业务服务器。部署工作流文件已写好(`.gitea/workflows/deploy.yml`),需要在服务器上完成一次性准备。 + +## 服务器信息 +- 业务服务器:82.157.208.197(SSH 别名 `ssh business`) +- Gitea 地址:https://git.qiukai.me +- 仓库:qiukai/opc-manager +- 服务端口:5177 +- MySQL:已安装(8.0.45,系统服务),数据库 `opc`,用户 `opc/opc123456` + +## 需要完成的 5 个步骤 + +### 1. 安装 Gitea Actions Runner +在业务服务器上安装 `act_runner` 并注册到 Gitea: +- 下载地址:https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64 +- 放到 `/usr/local/bin/act_runner` +- 注册时 name=`prod-deploy`,labels=`prod-deploy` +- 注册 token 需要到 Gitea 网页获取:仓库 qiukai/opc-manager → 设置 → Actions → Runners → New runner +- 安装为 systemd 服务(`/etc/systemd/system/act-runner.service`),开机自启 + +### 2. 创建部署目录结构 +```bash +mkdir -p /opt/opc-manager/{releases,shared/uploads} +``` + +### 3. 创建 .env 文件 +在 `/opt/opc-manager/shared/.env` 创建环境变量文件: +``` +SECRET_KEY=随机生成32位以上字符串 +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USER=opc +DB_PASSWORD=opc123456 +DB_NAME=opc +FLASK_DEBUG=false +``` +权限设为 600。 + +### 4. 安装 systemd service +创建 `/etc/systemd/system/opc-manager.service`: +```ini +[Unit] +Description=OPC Manager (Flask) +After=network.target mysql.service + +[Service] +Type=simple +WorkingDirectory=/opt/opc-manager/current +ExecStart=/opt/opc-manager/current/.venv/bin/gunicorn --preload -w 4 -b 0.0.0.0:5177 backend.flask_app:app +Restart=on-failure +RestartSec=5 +EnvironmentFile=/opt/opc-manager/current/.env + +[Install] +WantedBy=multi-user.target +``` +执行 `systemctl daemon-reload && systemctl enable opc-manager`(先不 start,等首次部署后自动启动)。 + +### 5. 配置 Gitea Secret +在 Gitea 网页操作(需要用户在浏览器完成): +- 进入仓库 qiukai/opc-manager → 设置 → Actions → Secrets → New Secret +- Name: `DEPLOY_TOKEN` +- Value: 用户的 Gitea Personal Access Token(需 repo 读权限) + - 生成路径:头像 → 设置 → 应用 → 生成令牌 + +## 首次部署验证 +准备完成后,在本地执行一次 `git push origin main`,观察: +1. Gitea 网页 Actions 页面是否有部署任务在运行 +2. 部署日志是否正常 +3. 部署完成后 `curl http://127.0.0.1:5177/api/health` 是否返回 `{"ok":true,"service":"opc-manager"}` +4. `systemctl status opc-manager` 是否 active + +## 参考文件 +项目的部署工作流在仓库的 `.gitea/workflows/deploy.yml`,systemd 模板在 `deploy/opc-manager.service`,完整说明在 `deploy/README.md`。 + +## 注意事项 +- Gitea Runner 的 token 需要用户在浏览器获取后告诉你,你无法自动获取 +- Gitea Secret (DEPLOY_TOKEN) 也需要用户在浏览器配置 +- 如果服务器没有 python3.11+,需要先安装(OPC-Manager 要求 Python 3.9+) +- 确保服务器已安装 git、rsync、curl(一般都有) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8383a71 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +flask==3.1.3 +mysql-connector-python==9.4.0 +python-dotenv==1.2.1 +werkzeug==3.1.8 +gunicorn==23.0.0