name: 🇨🇳 国内域名落地页 · 部署 # ═══════════════════════════════════════════════════════════ # 国内域名ICP备案落地页部署工作流 # 编号: ZY-WF-CN-LANDING # 服务器: ZY-SVR-004 · 43.139.217.141 · 广州七区 # 守护: 铸渊 · ICE-GL-ZY001 # 版权: 国作登字-2026-A-00037559 # ═══════════════════════════════════════════════════════════ # # 部署内容: # 1. 国内落地页 (server/sites/cn-landing/) → /opt/zhuyuan-cn/sites/cn-landing/ # 2. Nginx 配置 (server/nginx/cn-domain.conf) → Nginx sites-enabled # 3. LLM API 密钥注入 (四大国内模型) # # 架构: GitHub Actions(美国) → SG(新加坡·跳板) → CN(广州·目标) on: push: branches: [main] paths: - 'server/sites/cn-landing/**' - 'server/nginx/cn-domain.conf' workflow_dispatch: inputs: action: description: '部署动作' required: true default: 'deploy' type: choice options: - deploy - health-check - setup-ssl concurrency: group: cn-landing-deploy cancel-in-progress: false permissions: contents: read jobs: deploy: name: 🚀 部署国内落地页 if: github.event.inputs.action == 'deploy' || github.event.inputs.action == '' || github.event_name == 'push' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: 🔐 配置SSH跳板 (SG→CN) env: SG_SSH_KEY: ${{ secrets.ZY_SERVER_KEY }} CN_SSH_KEY: ${{ secrets.ZY_CN_SERVER_KEY }} CN_SSH_PORT: ${{ secrets.ZY_CN_SSH_PORT }} run: | CN_PORT="${CN_SSH_PORT:-22}" echo "🔧 SSH跳板: GitHub Actions → SG → CN (端口 ${CN_PORT})" mkdir -p ~/.ssh echo "$SG_SSH_KEY" > ~/.ssh/id_sg echo "$CN_SSH_KEY" > ~/.ssh/id_cn chmod 600 ~/.ssh/id_sg ~/.ssh/id_cn for key_file in id_sg id_cn; do if ! head -1 ~/.ssh/${key_file} | grep -q "BEGIN"; then echo "❌ SSH密钥格式异常: ${key_file}" exit 1 fi done echo "✅ SG + CN 密钥格式正确" ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null cat > ~/.ssh/config << EOF Host sg-jump HostName ${{ secrets.ZY_SERVER_HOST }} User ${{ secrets.ZY_SERVER_USER }} IdentityFile ~/.ssh/id_sg StrictHostKeyChecking accept-new BatchMode yes Host cn-target HostName ${{ secrets.ZY_CN_SERVER_HOST }} User ${{ secrets.ZY_CN_SERVER_USER }} Port ${CN_PORT} IdentityFile ~/.ssh/id_cn ProxyJump sg-jump StrictHostKeyChecking accept-new BatchMode yes ConnectTimeout 30 EOF chmod 600 ~/.ssh/config - name: 🔍 SSH连接测试 run: | echo "🔍 测试SSH跳板连接到广州服务器..." ssh cn-target 'echo "✅ SSH连接成功 · $(hostname) · $(date)"' || { echo "❌ SSH跳板连接失败" exit 1 } - name: 📂 准备目标目录 run: | ssh cn-target << 'PREP_CMD' set -e mkdir -p /opt/zhuyuan-cn/sites/cn-landing mkdir -p /opt/zhuyuan-cn/data/logs mkdir -p /opt/zhuyuan-cn/config/nginx echo "✅ 目录结构就绪" PREP_CMD - name: 📦 同步落地页文件 run: | rsync -avz --delete \ server/sites/cn-landing/ \ cn-target:/opt/zhuyuan-cn/sites/cn-landing/ echo "✅ 落地页已同步到 /opt/zhuyuan-cn/sites/cn-landing/" - name: 📦 同步Nginx配置 env: ZY_CN_DOMAIN: ${{ secrets.ZY_CN_DOMAIN }} run: | cp server/nginx/cn-domain.conf /tmp/cn-domain.conf if [ -n "$ZY_CN_DOMAIN" ]; then # 去除可能的 www. 前缀,获取裸域名 BARE_DOMAIN="${ZY_CN_DOMAIN#www.}" sed -i "s/ZY_CN_BARE_DOMAIN_PLACEHOLDER/${BARE_DOMAIN}/g" /tmp/cn-domain.conf echo "✅ 国内域名已注入: ${BARE_DOMAIN} + www.${BARE_DOMAIN}" else echo "⚠️ ZY_CN_DOMAIN 未配置,使用IP直接访问" sed -i "s/ZY_CN_BARE_DOMAIN_PLACEHOLDER www.ZY_CN_BARE_DOMAIN_PLACEHOLDER/_/g" /tmp/cn-domain.conf fi scp -F ~/.ssh/config \ /tmp/cn-domain.conf \ cn-target:/opt/zhuyuan-cn/config/nginx/cn-domain.conf echo "✅ Nginx配置已上传" - name: 🔑 注入LLM API密钥 env: ZY_DEEPSEEK_API_KEY: ${{ secrets.ZY_DEEPSEEK_API_KEY }} ZY_QIANWEN_API_KEY: ${{ secrets.ZY_QIANWEN_API_KEY }} ZY_KIMI_API_KEY: ${{ secrets.ZY_KIMI_API_KEY }} ZY_QINGYAN_API_KEY: ${{ secrets.ZY_QINGYAN_API_KEY }} run: | # 在本地生成密钥文件(变量在此展开) cat > /tmp/.env.llm << ENVEOF ZY_DEEPSEEK_API_KEY=${ZY_DEEPSEEK_API_KEY} ZY_QIANWEN_API_KEY=${ZY_QIANWEN_API_KEY} ZY_KIMI_API_KEY=${ZY_KIMI_API_KEY} ZY_QINGYAN_API_KEY=${ZY_QINGYAN_API_KEY} ENVEOF # 上传到目标服务器 scp -F ~/.ssh/config /tmp/.env.llm cn-target:/opt/zhuyuan-cn/config/.env.llm ssh cn-target 'chmod 600 /opt/zhuyuan-cn/config/.env.llm' rm -f /tmp/.env.llm echo "✅ 四大国内模型API密钥已注入" - name: 🔧 启用Nginx配置 & 重载 env: ZY_CN_DOMAIN: ${{ secrets.ZY_CN_DOMAIN }} run: | BARE_DOMAIN="${ZY_CN_DOMAIN#www.}" ssh cn-target << NGINX_CMD set -e echo "═══ Nginx 配置诊断 ═══" # 诊断: 列出当前所有启用的Nginx站点配置 echo "§1 当前sites-enabled目录:" ls -la /etc/nginx/sites-enabled/ 2>/dev/null || echo " (目录不存在)" echo "" echo "§1.1 当前conf.d目录:" ls -la /etc/nginx/conf.d/ 2>/dev/null || echo " (目录不存在)" # 清理所有冲突配置 — cn-domain.conf 必须是唯一的站点配置 # 此服务器仅服务国内落地页,不应有其他Nginx站点配置 echo "" echo "§2 清理冲突配置..." # 清理 sites-enabled 中的冲突 for conf in /etc/nginx/sites-enabled/*; do if [ -f "\$conf" ] && [ "\$(basename "\$conf")" != "cn-domain.conf" ]; then echo " 🗑️ 移除sites-enabled冲突: \$(basename "\$conf")" sudo rm -f "\$conf" fi done # 清理 conf.d 中可能的冲突 server 块配置 # 保留 conf.d 中的全局配置(如 gzip, ssl_params 等) # 只移除包含 server { 或 proxy_pass 的自定义站点配置 for conf in /etc/nginx/conf.d/*.conf; do if [ -f "\$conf" ] && grep -qE "server[[:space:]]*\{" "\$conf" 2>/dev/null; then echo " 🗑️ 移除conf.d冲突站点配置: \$(basename "\$conf")" sudo rm -f "\$conf" fi done # 复制配置到Nginx目录 sudo cp /opt/zhuyuan-cn/config/nginx/cn-domain.conf /etc/nginx/sites-available/cn-domain.conf sudo ln -sf /etc/nginx/sites-available/cn-domain.conf /etc/nginx/sites-enabled/cn-domain.conf # ─── SSL自动注入: 检测已有Let's Encrypt证书 ─── # 如果之前运行过 setup-ssl,证书文件会存在 # 每次deploy都重新注入SSL配置,避免模板覆盖导致SSL丢失 if [ -d "/etc/letsencrypt/live/${BARE_DOMAIN}" ] && [ -f "/etc/letsencrypt/live/${BARE_DOMAIN}/fullchain.pem" ]; then echo "" echo "§2.5 🔐 检测到SSL证书,注入HTTPS配置..." # 在 listen 80 后添加 listen 443 ssl 和证书配置 sudo sed -i '/listen 80 default_server;/a\\n listen 443 ssl default_server;\n ssl_certificate /etc/letsencrypt/live/'"${BARE_DOMAIN}"'/fullchain.pem;\n ssl_certificate_key /etc/letsencrypt/live/'"${BARE_DOMAIN}"'/privkey.pem;\n include /etc/letsencrypt/options-ssl-nginx.conf;\n ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;' \ /etc/nginx/sites-available/cn-domain.conf echo "✅ SSL配置已注入 · HTTPS已启用" else echo "" echo "§2.5 ℹ️ 未检测到SSL证书 · 仅HTTP模式" echo " 运行 setup-ssl 动作安装Let's Encrypt证书" fi # 确认最终状态 echo "" echo "§3 清理后sites-enabled目录:" ls -la /etc/nginx/sites-enabled/ echo "" echo "§3.1 清理后conf.d目录:" ls -la /etc/nginx/conf.d/ 2>/dev/null || echo " (空)" # 测试并重载Nginx echo "" sudo nginx -t && sudo systemctl reload nginx echo "✅ Nginx配置已生效 · 仅cn-domain.conf生效" NGINX_CMD - name: 💚 健康检查 run: | ssh cn-target << 'HEALTH_CMD' set -e echo "═══ 国内落地页部署验证 ═══" # 检查Nginx if systemctl is-active --quiet nginx; then echo "✅ Nginx: 运行中" else echo "❌ Nginx: 已停止" exit 1 fi # 诊断: 确认Nginx活跃配置 echo "" echo "§ Nginx活跃配置:" ls -la /etc/nginx/sites-enabled/ 2>/dev/null # 检查页面可访问 HTTP_CODE=$(curl -sf -o /dev/null -w '%{http_code}' http://localhost/ 2>/dev/null || echo "000") if [ "$HTTP_CODE" = "200" ]; then echo "✅ 落地页: HTTP 200 OK" else echo "❌ 落地页: HTTP ${HTTP_CODE}" # 不立即退出 · 继续诊断 fi # 检查ICP备案号 · 确认是正确的落地页(非代理页面) if curl -sf http://localhost/ | grep -q "陕ICP备2025071211号"; then echo "✅ ICP备案号: 已显示" else echo "❌ ICP备案号: 未找到 — 可能有冲突配置覆盖了落地页" echo " 诊断: 页面前200字符:" curl -sf http://localhost/ 2>/dev/null | head -c 200 || echo "(无响应)" fi # 检查备案链接 if curl -sf http://localhost/ | grep -q "beian.miit.gov.cn"; then echo "✅ 工信部链接: 已配置" else echo "⚠️ 工信部链接: 未找到" fi # 健康端点 · 验证返回的是cn-landing而不是其他服务 HEALTH=$(curl -sf http://localhost/health 2>/dev/null || echo '{}') echo "✅ 健康探针: ${HEALTH}" # 验证健康端点来自cn-domain.conf而非其他服务 if echo "$HEALTH" | grep -q "cn-landing"; then echo "✅ 健康端点: 来自cn-domain.conf(正确)" elif echo "$HEALTH" | grep -qE "relay|proxy|mcp"; then echo "❌ 健康端点: 来自其他服务 — Nginx配置冲突未完全解决" fi # 确认没有端口冲突: 检查是否有其他服务监听80端口 echo "" echo "§ 端口80监听进程:" sudo ss -tlnp | grep ':80 ' || echo " (无)" echo "" echo "═══ 部署完成 ═══" HEALTH_CMD - name: 🧹 清理SSH密钥 if: always() run: rm -f ~/.ssh/id_sg ~/.ssh/id_cn ~/.ssh/config health-check: name: 💚 国内落地页健康检查 if: github.event.inputs.action == 'health-check' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: 🔐 配置SSH跳板 (SG→CN) env: SG_SSH_KEY: ${{ secrets.ZY_SERVER_KEY }} CN_SSH_KEY: ${{ secrets.ZY_CN_SERVER_KEY }} CN_SSH_PORT: ${{ secrets.ZY_CN_SSH_PORT }} run: | CN_PORT="${CN_SSH_PORT:-22}" mkdir -p ~/.ssh echo "$SG_SSH_KEY" > ~/.ssh/id_sg echo "$CN_SSH_KEY" > ~/.ssh/id_cn chmod 600 ~/.ssh/id_sg ~/.ssh/id_cn ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null cat > ~/.ssh/config << EOF Host sg-jump HostName ${{ secrets.ZY_SERVER_HOST }} User ${{ secrets.ZY_SERVER_USER }} IdentityFile ~/.ssh/id_sg StrictHostKeyChecking accept-new BatchMode yes Host cn-target HostName ${{ secrets.ZY_CN_SERVER_HOST }} User ${{ secrets.ZY_CN_SERVER_USER }} Port ${CN_PORT} IdentityFile ~/.ssh/id_cn ProxyJump sg-jump StrictHostKeyChecking accept-new BatchMode yes ConnectTimeout 30 EOF chmod 600 ~/.ssh/config - name: 💚 执行健康检查 run: | ssh cn-target << 'HEALTH_CMD' echo "═══ 国内落地页健康报告 ═══" echo "" echo "§1 系统状态:" uptime echo "" echo "§2 Nginx 状态:" systemctl is-active nginx && echo "Nginx: 运行中" || echo "Nginx: 已停止" echo "" echo "§3 Nginx sites-enabled 诊断:" ls -la /etc/nginx/sites-enabled/ 2>/dev/null || echo " (目录不存在)" echo "" echo "§4 落地页文件:" ls -la /opt/zhuyuan-cn/sites/cn-landing/ 2>/dev/null || echo "目录不存在" echo "" echo "§5 页面访问测试:" HTTP_CODE=$(curl -sf -o /dev/null -w '%{http_code}' http://localhost/ 2>/dev/null || echo "000") echo "HTTP状态码: ${HTTP_CODE}" echo "" echo "§6 ICP备案号检查:" if curl -sf http://localhost/ | grep -q "陕ICP备2025071211号"; then echo "✅ ICP备案号已显示" else echo "❌ ICP备案号未找到" echo " 页面前200字符:" curl -sf http://localhost/ 2>/dev/null | head -c 200 || echo "(无响应)" fi echo "" echo "§7 健康端点验证:" HEALTH=$(curl -sf http://localhost/health 2>/dev/null || echo '{}') echo "响应: ${HEALTH}" if echo "$HEALTH" | grep -q "cn-landing"; then echo "✅ 来自cn-domain.conf(正确)" elif echo "$HEALTH" | grep -qE "relay|proxy|mcp"; then echo "❌ 来自其他服务 — 存在Nginx配置冲突" fi echo "" echo "§8 LLM API密钥状态:" if [ -f /opt/zhuyuan-cn/config/.env.llm ]; then KEY_COUNT=$(grep -c "API_KEY" /opt/zhuyuan-cn/config/.env.llm 2>/dev/null || echo "0") echo "已配置密钥数: ${KEY_COUNT}" else echo "⚠️ LLM密钥文件不存在" fi echo "" echo "§9 端口80监听进程:" sudo ss -tlnp | grep ':80 ' 2>/dev/null || echo " (无)" # SSL状态检查 echo "" echo "§10 SSL证书状态:" sudo certbot certificates 2>/dev/null || echo " certbot未安装 · 运行setup-ssl动作安装" echo "" echo "§11 HTTPS访问测试:" HTTPS_CODE=$(curl -sf -o /dev/null -w '%{http_code}' https://localhost/ -k 2>/dev/null || echo "000") echo "HTTPS状态码: ${HTTPS_CODE}" HEALTH_CMD - name: 🧹 清理SSH密钥 if: always() run: rm -f ~/.ssh/id_sg ~/.ssh/id_cn ~/.ssh/config setup-ssl: name: 🔐 安装SSL证书 (Let's Encrypt) if: github.event.inputs.action == 'setup-ssl' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: 🔐 配置SSH跳板 (SG→CN) env: SG_SSH_KEY: ${{ secrets.ZY_SERVER_KEY }} CN_SSH_KEY: ${{ secrets.ZY_CN_SERVER_KEY }} CN_SSH_PORT: ${{ secrets.ZY_CN_SSH_PORT }} run: | CN_PORT="${CN_SSH_PORT:-22}" mkdir -p ~/.ssh echo "$SG_SSH_KEY" > ~/.ssh/id_sg echo "$CN_SSH_KEY" > ~/.ssh/id_cn chmod 600 ~/.ssh/id_sg ~/.ssh/id_cn ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null cat > ~/.ssh/config << EOF Host sg-jump HostName ${{ secrets.ZY_SERVER_HOST }} User ${{ secrets.ZY_SERVER_USER }} IdentityFile ~/.ssh/id_sg StrictHostKeyChecking accept-new BatchMode yes Host cn-target HostName ${{ secrets.ZY_CN_SERVER_HOST }} User ${{ secrets.ZY_CN_SERVER_USER }} Port ${CN_PORT} IdentityFile ~/.ssh/id_cn ProxyJump sg-jump StrictHostKeyChecking accept-new BatchMode yes ConnectTimeout 30 EOF chmod 600 ~/.ssh/config - name: 🔐 安装certbot并申请证书 env: ZY_CN_DOMAIN: ${{ secrets.ZY_CN_DOMAIN }} run: | BARE_DOMAIN="${ZY_CN_DOMAIN#www.}" echo "🔐 为 ${BARE_DOMAIN} + www.${BARE_DOMAIN} 申请SSL证书..." ssh cn-target << SSLCMD set -e echo "═══ SSL证书安装 ═══" # 安装certbot (如果未安装) if ! command -v certbot &> /dev/null; then echo "📦 安装certbot..." sudo apt-get update -qq sudo apt-get install -y -qq certbot python3-certbot-nginx else echo "✅ certbot已安装: \$(certbot --version 2>&1)" fi # 确认Nginx正在运行 if ! systemctl is-active --quiet nginx; then echo "❌ Nginx未运行,先启动Nginx" sudo systemctl start nginx fi # 申请证书 (--nginx模式会自动修改Nginx配置) echo "" echo "§1 申请Let's Encrypt证书..." sudo certbot --nginx \ -d ${BARE_DOMAIN} \ -d www.${BARE_DOMAIN} \ --non-interactive \ --agree-tos \ --email admin@${BARE_DOMAIN} \ --redirect \ --no-eff-email # 验证证书 echo "" echo "§2 验证证书..." sudo certbot certificates # 验证HTTPS可访问 echo "" echo "§3 HTTPS访问测试..." HTTP_CODE=\$(curl -sf -o /dev/null -w '%{http_code}' https://localhost/ -k 2>/dev/null || echo "000") echo "HTTPS状态码: \${HTTP_CODE}" # 验证HTTP→HTTPS重定向 echo "" echo "§4 HTTP→HTTPS重定向测试..." REDIR_CODE=\$(curl -sf -o /dev/null -w '%{http_code}' http://localhost/ 2>/dev/null || echo "000") echo "HTTP重定向码: \${REDIR_CODE} (301=正确重定向)" echo "" echo "═══ SSL安装完成 ═══" echo "🌐 现在可以通过 https://${BARE_DOMAIN} 访问" SSLCMD - name: 🧹 清理SSH密钥 if: always() run: rm -f ~/.ssh/id_sg ~/.ssh/id_cn ~/.ssh/config