devops: 备案通过回切 https 域名 + 流水线 Ali 单轨
- nginx-jiu-ali.conf:443 ssl+http2 正式入口(HSTS/XFO/nosniff 安全头、 ACME webroot 续期通道、80→443 跳转);8443 明文过渡口拆除 - 客户端构建 URL 全量回切 https://jiu.51yanmei.com(compile×5/local_test/ release-client/notify) - 流水线去 EC2:deploy-client/site 单轨 Ali、manual 回滚与每日备份切 ali、 seed/reset/debug-db 改容器内取密码(SEC-003,退役 DB_PASSWORD secret) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
@@ -18,9 +18,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Dump MySQL to local backup dir
|
- name: Dump MySQL to local backup dir
|
||||||
env:
|
env:
|
||||||
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
|
# 2026-07-03 割接后备份源=阿里机主库
|
||||||
EC2_HOST: ${{ secrets.EC2_HOST }}
|
DEPLOY_SSH_KEY: ${{ secrets.ALI_SSH_KEY }}
|
||||||
EC2_USER: ${{ secrets.EC2_USER }}
|
DEPLOY_HOST: ${{ secrets.ALI_HOST }}
|
||||||
|
DEPLOY_USER: ${{ secrets.ALI_USER }}
|
||||||
run: sh scripts/ci/backup-db.sh
|
run: sh scripts/ci/backup-db.sh
|
||||||
|
|
||||||
- name: Notify (Telegram)
|
- name: Notify (Telegram)
|
||||||
|
|||||||
@@ -10,15 +10,14 @@ jobs:
|
|||||||
- name: Setup SSH
|
- name: Setup SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
printf '%s' "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/ec2_deploy.pem
|
printf '%s' "${{ secrets.ALI_SSH_KEY }}" > ~/.ssh/ec2_deploy.pem
|
||||||
chmod 600 ~/.ssh/ec2_deploy.pem
|
chmod 600 ~/.ssh/ec2_deploy.pem
|
||||||
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
|
ssh-keyscan -H ${{ secrets.ALI_HOST }} >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Query DB
|
- name: Query DB
|
||||||
env:
|
env:
|
||||||
EC2_HOST: ${{ secrets.EC2_HOST }}
|
EC2_HOST: ${{ secrets.ALI_HOST }}
|
||||||
EC2_USER: ${{ secrets.EC2_USER }}
|
EC2_USER: ${{ secrets.ALI_USER }}
|
||||||
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
||||||
run: |
|
run: |
|
||||||
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} bash << EOF
|
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} bash << EOF
|
||||||
echo "=== Docker containers ==="
|
echo "=== Docker containers ==="
|
||||||
@@ -26,20 +25,20 @@ jobs:
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Databases ==="
|
echo "=== Databases ==="
|
||||||
docker exec jiu_mysql mysql -uroot -p${DB_PASSWORD} -e "SHOW DATABASES;" 2>/dev/null \
|
docker exec jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD -e "SHOW DATABASES;"' 2>/dev/null \
|
||||||
|| echo "jiu_mysql container not found or DB_PASSWORD wrong"
|
|| echo "jiu_mysql container not found or MYSQL_ROOT_PASSWORD wrong"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Tables in jiu_db ==="
|
echo "=== Tables in jiu_db ==="
|
||||||
docker exec jiu_mysql mysql -uroot -p${DB_PASSWORD} jiu_db -e "SHOW TABLES;" 2>/dev/null
|
docker exec jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD jiu_db -e "SHOW TABLES;"' 2>/dev/null
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== shops ==="
|
echo "=== shops ==="
|
||||||
docker exec jiu_mysql mysql -uroot -p${DB_PASSWORD} jiu_db -e "SELECT id, name, code FROM shops;" 2>/dev/null
|
docker exec jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD jiu_db -e "SELECT id, name, code FROM shops;"' 2>/dev/null
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== users ==="
|
echo "=== users ==="
|
||||||
docker exec jiu_mysql mysql -uroot -p${DB_PASSWORD} jiu_db -e "SELECT id, shop_id, username, role, is_active FROM users;" 2>/dev/null
|
docker exec jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD jiu_db -e "SELECT id, shop_id, username, role, is_active FROM users;"' 2>/dev/null
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
- name: Cleanup SSH key
|
- name: Cleanup SSH key
|
||||||
|
|||||||
@@ -180,20 +180,9 @@ jobs:
|
|||||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
run: sh scripts/ci/release-client.sh "${{ gitea.ref_name }}"
|
run: sh scripts/ci/release-client.sh "${{ gitea.ref_name }}"
|
||||||
|
|
||||||
- name: Deploy → EC2
|
# 2026-07-03 割接后阿里机即唯一发布目标(EC2 轨移除;EC2 仅保留
|
||||||
env:
|
# jiu.51yanmei.com 反代桥接到 ali,见 baize 台账)。
|
||||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
- name: Deploy → Ali
|
||||||
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
|
||||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
|
||||||
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
|
|
||||||
EC2_HOST: ${{ secrets.EC2_HOST }}
|
|
||||||
EC2_USER: ${{ secrets.EC2_USER }}
|
|
||||||
run: sh scripts/ci/deploy-client.sh "${{ gitea.ref_name }}"
|
|
||||||
|
|
||||||
# 迁移期影子目标:同步 Flutter web + version.yaml + 安装包到阿里机。失败不挡 EC2。
|
|
||||||
- name: Deploy → Ali (migration shadow)
|
|
||||||
if: always()
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
env:
|
||||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
||||||
|
|||||||
@@ -32,20 +32,9 @@ jobs:
|
|||||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
run: sh scripts/ci/release-site.sh "${{ gitea.ref_name }}"
|
run: sh scripts/ci/release-site.sh "${{ gitea.ref_name }}"
|
||||||
|
|
||||||
- name: Deploy → EC2
|
# 2026-07-03 割接后阿里机即唯一发布目标(EC2 轨移除;EC2 仅保留
|
||||||
env:
|
# jiu.51yanmei.com 反代桥接到 ali,见 baize 台账)。
|
||||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
- name: Deploy → Ali
|
||||||
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
|
||||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
|
||||||
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
|
|
||||||
EC2_HOST: ${{ secrets.EC2_HOST }}
|
|
||||||
EC2_USER: ${{ secrets.EC2_USER }}
|
|
||||||
run: sh scripts/ci/deploy-site.sh "${{ gitea.ref_name }}"
|
|
||||||
|
|
||||||
# 迁移期影子目标:同步官网到阿里机。失败不挡 EC2。
|
|
||||||
- name: Deploy → Ali (migration shadow)
|
|
||||||
if: always()
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
env:
|
||||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
||||||
|
|||||||
@@ -28,9 +28,16 @@ jobs:
|
|||||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
||||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
|
# 2026-07-03 割接后回滚目标统一为阿里机
|
||||||
EC2_HOST: ${{ secrets.EC2_HOST }}
|
DEPLOY_TARGET: ali
|
||||||
EC2_USER: ${{ secrets.EC2_USER }}
|
DEPLOY_SSH_KEY: ${{ secrets.ALI_SSH_KEY }}
|
||||||
|
DEPLOY_HOST: ${{ secrets.ALI_HOST }}
|
||||||
|
DEPLOY_USER: ${{ secrets.ALI_USER }}
|
||||||
|
# 兼容:回滚割接前旧 tag 时,旧脚本直接引用 EC2_*(set -u 下缺失即炸)——
|
||||||
|
# 同值指向阿里,旧脚本也会部署到阿里而不是复活 EC2
|
||||||
|
EC2_SSH_KEY: ${{ secrets.ALI_SSH_KEY }}
|
||||||
|
EC2_HOST: ${{ secrets.ALI_HOST }}
|
||||||
|
EC2_USER: ${{ secrets.ALI_USER }}
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ inputs.version }}"
|
TAG="${{ inputs.version }}"
|
||||||
case "$TAG" in
|
case "$TAG" in
|
||||||
|
|||||||
@@ -17,20 +17,19 @@ jobs:
|
|||||||
- name: Setup SSH
|
- name: Setup SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
printf '%s' "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/ec2_deploy.pem
|
printf '%s' "${{ secrets.ALI_SSH_KEY }}" > ~/.ssh/ec2_deploy.pem
|
||||||
chmod 600 ~/.ssh/ec2_deploy.pem
|
chmod 600 ~/.ssh/ec2_deploy.pem
|
||||||
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
|
ssh-keyscan -H ${{ secrets.ALI_HOST }} >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Reset database & reseed
|
- name: Reset database & reseed
|
||||||
env:
|
env:
|
||||||
EC2_HOST: ${{ secrets.EC2_HOST }}
|
EC2_HOST: ${{ secrets.ALI_HOST }}
|
||||||
EC2_USER: ${{ secrets.EC2_USER }}
|
EC2_USER: ${{ secrets.ALI_USER }}
|
||||||
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
||||||
SHOPS: ${{ github.event.inputs.shops }}
|
SHOPS: ${{ github.event.inputs.shops }}
|
||||||
run: |
|
run: |
|
||||||
# 1. 清库重建
|
# 1. 清库重建
|
||||||
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
||||||
"docker exec jiu_mysql mysql -uroot -p${DB_PASSWORD} -e 'DROP DATABASE IF EXISTS jiu_db; CREATE DATABASE jiu_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'"
|
"docker exec jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD -e \"DROP DATABASE IF EXISTS jiu_db; CREATE DATABASE jiu_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\"'"
|
||||||
echo "✓ Database reset"
|
echo "✓ Database reset"
|
||||||
|
|
||||||
# 2. 重启 jiu-server,AutoMigrate 重建表结构
|
# 2. 重启 jiu-server,AutoMigrate 重建表结构
|
||||||
@@ -57,13 +56,13 @@ jobs:
|
|||||||
echo "→ Seeding $SHOP ..."
|
echo "→ Seeding $SHOP ..."
|
||||||
if [ "$FIRST" = "true" ]; then
|
if [ "$FIRST" = "true" ]; then
|
||||||
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
||||||
"docker exec -i jiu_mysql mysql -uroot -p${DB_PASSWORD} --default-character-set=utf8mb4 jiu_db" \
|
"docker exec -i jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD --default-character-set=utf8mb4 jiu_db'" \
|
||||||
< "$FILE"
|
< "$FILE"
|
||||||
FIRST=false
|
FIRST=false
|
||||||
else
|
else
|
||||||
grep -v "^TRUNCATE" "$FILE" | \
|
grep -v "^TRUNCATE" "$FILE" | \
|
||||||
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
||||||
"docker exec -i jiu_mysql mysql -uroot -p${DB_PASSWORD} --default-character-set=utf8mb4 jiu_db"
|
"docker exec -i jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD --default-character-set=utf8mb4 jiu_db'"
|
||||||
fi
|
fi
|
||||||
echo "✓ $SHOP done"
|
echo "✓ $SHOP done"
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -21,15 +21,14 @@ jobs:
|
|||||||
- name: Setup SSH
|
- name: Setup SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
printf '%s' "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/ec2_deploy.pem
|
printf '%s' "${{ secrets.ALI_SSH_KEY }}" > ~/.ssh/ec2_deploy.pem
|
||||||
chmod 600 ~/.ssh/ec2_deploy.pem
|
chmod 600 ~/.ssh/ec2_deploy.pem
|
||||||
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
|
ssh-keyscan -H ${{ secrets.ALI_HOST }} >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Seed databases
|
- name: Seed databases
|
||||||
env:
|
env:
|
||||||
EC2_HOST: ${{ secrets.EC2_HOST }}
|
EC2_HOST: ${{ secrets.ALI_HOST }}
|
||||||
EC2_USER: ${{ secrets.EC2_USER }}
|
EC2_USER: ${{ secrets.ALI_USER }}
|
||||||
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
||||||
SHOPS: ${{ github.event.inputs.shops }}
|
SHOPS: ${{ github.event.inputs.shops }}
|
||||||
CLEAR: ${{ github.event.inputs.clear }}
|
CLEAR: ${{ github.event.inputs.clear }}
|
||||||
run: |
|
run: |
|
||||||
@@ -41,7 +40,7 @@ jobs:
|
|||||||
echo " 清空 ${SHOP}..."
|
echo " 清空 ${SHOP}..."
|
||||||
printf "SET @shop_code='%s';\n" "$SHOP" | cat - backend/seeds/clear_shop.sql | \
|
printf "SET @shop_code='%s';\n" "$SHOP" | cat - backend/seeds/clear_shop.sql | \
|
||||||
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
||||||
"docker exec -i jiu_mysql mysql -uroot -p${DB_PASSWORD} --default-character-set=utf8mb4 jiu_db"
|
"docker exec -i jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD --default-character-set=utf8mb4 jiu_db'"
|
||||||
done
|
done
|
||||||
echo "✓ 清空完成"
|
echo "✓ 清空完成"
|
||||||
fi
|
fi
|
||||||
@@ -56,7 +55,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
echo "→ Seeding $SHOP ..."
|
echo "→ Seeding $SHOP ..."
|
||||||
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} \
|
||||||
"docker exec -i jiu_mysql mysql -uroot -p${DB_PASSWORD} --default-character-set=utf8mb4 jiu_db" \
|
"docker exec -i jiu_mysql sh -c 'exec mysql -uroot -p\$MYSQL_ROOT_PASSWORD --default-character-set=utf8mb4 jiu_db'" \
|
||||||
< "$FILE"
|
< "$FILE"
|
||||||
echo "✓ $SHOP done"
|
echo "✓ $SHOP done"
|
||||||
done
|
done
|
||||||
|
|||||||
+46
-19
@@ -2,34 +2,51 @@
|
|||||||
# 与 nginx-jiu.conf(EC2 pangolin 容器版,listen 127.0.0.1:8445)等价。
|
# 与 nginx-jiu.conf(EC2 pangolin 容器版,listen 127.0.0.1:8445)等价。
|
||||||
# CI deploy-server.sh 的 ali 分支落到 /etc/nginx/conf.d/jiu.conf。
|
# CI deploy-server.sh 的 ali 分支落到 /etc/nginx/conf.d/jiu.conf。
|
||||||
#
|
#
|
||||||
# 过渡期入口 = 8443 明文 HTTP,裸 IP 直连 http://182.92.213.171:8443
|
# 2026-07-03 备案通过回切:443 ssl(jiu.51yanmei.com)为唯一正式入口 + 80→443 跳转。
|
||||||
#(备案未通过前 80/443/8080 被阿里云拦截,走非标端口;不建过渡子域,
|
# 8443 明文过渡口已拆(用户拍板:裸 IP 版 client 从未发过,存量客户端都是 https
|
||||||
# 证书签给域名对不上 IP,故过渡期不做 TLS——登录口令/JWT 明文传输,仅限过渡;
|
# 域名 BASE_URL,DNS 回切后自动恢复,8443 无人使用)。
|
||||||
# 备案通过后改回 listen 443 ssl + 域名 + 80→443 跳转块)。
|
# 证书:/etc/nginx/ssl/jiu.51yanmei.com/(2026-07-03 从 EC2 letsencrypt 同步,
|
||||||
|
# 2026-08-28 到期;续期方案=DNS 已指 ali 后 certbot HTTP-01 webroot,见 baize)。
|
||||||
# 后端上游 = 127.0.0.1:8081(非 8080!ali 上 8080 已被 pay 项目 payd 占用;
|
# 后端上游 = 127.0.0.1:8081(非 8080!ali 上 8080 已被 pay 项目 payd 占用;
|
||||||
# 对应 /opt/jiu/config/production.env 的 SERVER_PORT=8081)。
|
# 对应 /opt/jiu/config/production.env 的 SERVER_PORT=8081)。
|
||||||
limit_req_zone $binary_remote_addr zone=jiu_pub:10m rate=10r/s;
|
limit_req_zone $binary_remote_addr zone=jiu_pub:10m rate=10r/s;
|
||||||
limit_req_status 429;
|
limit_req_status 429;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 8443 default_server;
|
# ali nginx 1.24 无 `http2 on;`(1.25.1+),用 listen 参数旧语法
|
||||||
server_name _;
|
listen 443 ssl http2 default_server; # 正式入口(备案后回切 2026-07-03)
|
||||||
|
server_name jiu.51yanmei.com _;
|
||||||
|
|
||||||
# 备案通过切回 443 时恢复(证书路径仍有效,jiu.51yanmei.com,2026-09-21 到期):
|
# 证书现为 EC2 同步副本(2026-08-28 到期)。ACME 通道已就绪:待批准后在 ali 跑
|
||||||
# listen 443 ssl http2;
|
# `certbot certonly --webroot -w /var/www/acme -d jiu.51yanmei.com` 签新证,
|
||||||
# server_name jiu.51yanmei.com;
|
# 然后把这两行换成 /etc/letsencrypt/live/jiu.51yanmei.com/ 并配 reload 部署钩子。
|
||||||
# ssl_certificate /etc/letsencrypt/live/jiu.51yanmei.com/fullchain.pem;
|
ssl_certificate /etc/nginx/ssl/jiu.51yanmei.com/fullchain.pem;
|
||||||
# ssl_certificate_key /etc/letsencrypt/live/jiu.51yanmei.com/privkey.pem;
|
ssl_certificate_key /etc/nginx/ssl/jiu.51yanmei.com/privkey.pem;
|
||||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# 安全响应头(SEC-P02)。注意 nginx add_header 继承规则:location 内有自己的
|
||||||
|
# add_header 时会整体覆盖 server 级——故 /images、/app/index.html、/downloads
|
||||||
|
# 三处各自补了 HSTS。
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||||
|
add_header X-Frame-Options SAMEORIGIN always;
|
||||||
|
add_header X-Content-Type-Options nosniff always;
|
||||||
|
|
||||||
client_max_body_size 20m;
|
client_max_body_size 20m;
|
||||||
|
|
||||||
|
# certbot HTTP-01 续期通道(webroot 独立目录,不放 marketing——
|
||||||
|
# deploy-site 的 rsync --delete 会清掉站点目录里的挑战文件)
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/acme;
|
||||||
|
}
|
||||||
|
|
||||||
# 商品图片静态文件
|
# 商品图片静态文件
|
||||||
location ^~ /images/ {
|
location ^~ /images/ {
|
||||||
alias /opt/jiu/images/;
|
alias /opt/jiu/images/;
|
||||||
expires 30d;
|
expires 30d;
|
||||||
add_header Cache-Control "public, immutable";
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||||
}
|
}
|
||||||
|
|
||||||
# 文件导入接口(超时更长)
|
# 文件导入接口(超时更长)
|
||||||
@@ -68,6 +85,7 @@ server {
|
|||||||
alias /opt/jiu/web/index.html;
|
alias /opt/jiu/web/index.html;
|
||||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
add_header Pragma "no-cache";
|
add_header Pragma "no-cache";
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||||
expires 0;
|
expires 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +113,7 @@ server {
|
|||||||
alias /opt/jiu/downloads/;
|
alias /opt/jiu/downloads/;
|
||||||
add_header Content-Disposition "attachment";
|
add_header Content-Disposition "attachment";
|
||||||
add_header Cache-Control "no-cache";
|
add_header Cache-Control "no-cache";
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||||
autoindex off;
|
autoindex off;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +124,17 @@ server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# 备案通过、入口切回 443 后再恢复此跳转块(80 未备案期间被拦截,挂了也无意义):
|
# 80 → 443 跳转(仅 jiu 域名;51yanmei.com 门户 80 在 51yanmei.conf 自管)。
|
||||||
# server {
|
# ACME 挑战必须在跳转前放行(HTTP-01 走 80)。
|
||||||
# listen 80;
|
server {
|
||||||
# server_name jiu.51yanmei.com;
|
listen 80;
|
||||||
# return 301 https://$host$request_uri;
|
server_name jiu.51yanmei.com;
|
||||||
# }
|
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/acme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+14
-9
@@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# backup-db.sh — dump the production MySQL (jiu_db) from EC2 and keep a gzip
|
# backup-db.sh — dump the production MySQL (jiu_db) from the production host
|
||||||
# snapshot on the mac (host) runner's local disk (~/jiu-db-backups), retaining
|
# and keep a gzip snapshot on the mac (host) runner's local disk
|
||||||
# the last 30 days. The DB password is read on EC2 from production.env's
|
# (~/jiu-db-backups), retaining the last 30 days. The DB password is read on
|
||||||
# DATABASE_DSN, so no DB-password secret is needed.
|
# the host from production.env's DATABASE_DSN, so no DB-password secret is
|
||||||
|
# needed.
|
||||||
#
|
#
|
||||||
# Requires env (same as deploy-server): EC2_SSH_KEY, EC2_HOST, EC2_USER.
|
# Target host (post-cutover 2026-07-03: Ali is the primary): pass
|
||||||
|
# DEPLOY_SSH_KEY / DEPLOY_HOST / DEPLOY_USER (falls back to EC2_* legacy vars).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# shellcheck source=scripts/ci/lib-forgejo.sh
|
# shellcheck source=scripts/ci/lib-forgejo.sh
|
||||||
@@ -15,15 +17,18 @@ mkdir -p "${BACKUP_DIR}"
|
|||||||
FILENAME="jiu_db_$(date +%Y%m%d_%H%M%S).sql.gz"
|
FILENAME="jiu_db_$(date +%Y%m%d_%H%M%S).sql.gz"
|
||||||
DEST="${BACKUP_DIR}/${FILENAME}"
|
DEST="${BACKUP_DIR}/${FILENAME}"
|
||||||
|
|
||||||
|
TARGET_HOST="${DEPLOY_HOST:-$EC2_HOST}"
|
||||||
|
TARGET_USER="${DEPLOY_USER:-$EC2_USER}"
|
||||||
|
|
||||||
setup_ssh
|
setup_ssh
|
||||||
trap teardown_ssh EXIT
|
trap teardown_ssh EXIT
|
||||||
|
|
||||||
echo "==> backup-db: dumping jiu_db from ${EC2_HOST}"
|
echo "==> backup-db: dumping jiu_db from ${TARGET_HOST}"
|
||||||
# On EC2: parse the DSN password from production.env, then mysqldump the
|
# On the host: parse the DSN password from production.env, then mysqldump the
|
||||||
# container to stdout; stream back over ssh and gzip locally. The heredoc is
|
# container to stdout; stream back over ssh and gzip locally. The heredoc is
|
||||||
# single-quoted, so it runs verbatim on EC2 (no local expansion). ${SSH} carries
|
# single-quoted, so it runs verbatim on the host (no local expansion). ${SSH} carries
|
||||||
# no -t, keeping stdout a clean dump stream.
|
# no -t, keeping stdout a clean dump stream.
|
||||||
${SSH} "${EC2_USER}@${EC2_HOST}" 'bash -s' <<'ENDSSH' | gzip > "${DEST}"
|
${SSH} "${TARGET_USER}@${TARGET_HOST}" 'bash -s' <<'ENDSSH' | gzip > "${DEST}"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
PW=$(python3 -c 'import re;e=open("/opt/jiu/config/production.env").read();d=re.search(r"DATABASE_DSN=(.*)",e).group(1).strip().strip(chr(34)).strip(chr(39));print(re.match(r"[^:]+:([^@]+)@",d).group(1))')
|
PW=$(python3 -c 'import re;e=open("/opt/jiu/config/production.env").read();d=re.search(r"DATABASE_DSN=(.*)",e).group(1).strip().strip(chr(34)).strip(chr(39));print(re.match(r"[^:]+:([^@]+)@",d).group(1))')
|
||||||
exec docker exec -e MYSQL_PWD="${PW}" jiu_mysql mysqldump -uroot --single-transaction --no-tablespaces jiu_db
|
exec docker exec -e MYSQL_PWD="${PW}" jiu_mysql mysqldump -uroot --single-transaction --no-tablespaces jiu_db
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
# is decommissioned — deploying there would resurrect a backend against a stale
|
# is decommissioned — deploying there would resurrect a backend against a stale
|
||||||
# database, so the workflow no longer calls the ec2 branch):
|
# database, so the workflow no longer calls the ec2 branch):
|
||||||
# - ali: stop jiu → swap binary → start jiu → health check (:8081) →
|
# - ali: stop jiu → swap binary → start jiu → health check (:8081) →
|
||||||
# reload the host nginx (transition listen-8443 config).
|
# reload the host nginx (443 ssl jiu.51yanmei.com, 备案回切 2026-07-03).
|
||||||
# - ec2 (legacy default, kept for manual rollback of pre-cutover tags only):
|
# - ec2 (legacy default, kept for manual rollback of pre-cutover tags only):
|
||||||
# stop jiu → swap binary → start jiu → health check → reload nginx
|
# stop jiu → swap binary → start jiu → health check → reload nginx
|
||||||
# inside the pangolin-edge container.
|
# inside the pangolin-edge container.
|
||||||
@@ -56,7 +56,7 @@ fi
|
|||||||
if [ "$DEPLOY_TARGET" = "ali" ]; then
|
if [ "$DEPLOY_TARGET" = "ali" ]; then
|
||||||
# --- Alibaba Cloud (live primary since the 2026-07-02 cutover) ---
|
# --- Alibaba Cloud (live primary since the 2026-07-02 cutover) ---
|
||||||
# Full swap-and-restart: jiu.service runs against the promoted local primary
|
# Full swap-and-restart: jiu.service runs against the promoted local primary
|
||||||
# DB on :8081; nginx serves the transition listen-8443 config.
|
# DB on :8081; nginx serves 443 ssl (jiu.51yanmei.com, 备案回切 2026-07-03).
|
||||||
${SSH} "${TARGET_USER}@${TARGET_HOST}" << 'ENDSSH'
|
${SSH} "${TARGET_USER}@${TARGET_HOST}" << 'ENDSSH'
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
MSG="${ICON} 岩美 ${TAG} ${LABEL}
|
MSG="${ICON} 岩美 ${TAG} ${LABEL}
|
||||||
https://jiu.51yanmei.com"
|
https://jiu.51yanmei.com" # 2026-07-03 备案通过,正式域名入口
|
||||||
|
|
||||||
curl -f -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
|
curl -f -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
|
||||||
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ ver = sys.argv[1]
|
|||||||
notes = sys.argv[2]
|
notes = sys.argv[2]
|
||||||
changelog_file = sys.argv[3]
|
changelog_file = sys.argv[3]
|
||||||
|
|
||||||
base = "https://jiu.51yanmei.com/downloads"
|
base = "https://jiu.51yanmei.com/downloads" # 2026-07-03 备案通过回切 https 域名
|
||||||
mac_url = f"{base}/jiu-macos-x64.zip"
|
mac_url = f"{base}/jiu-macos-x64.zip"
|
||||||
win_url = f"{base}/jiu-windows-x64-setup.exe"
|
win_url = f"{base}/jiu-windows-x64-setup.exe"
|
||||||
android_url = f"{base}/jiu-android.apk"
|
android_url = f"{base}/jiu-android.apk"
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
# sh scripts/local_test.sh # 编译 + 启动
|
# sh scripts/local_test.sh # 编译 + 启动
|
||||||
# sh scripts/local_test.sh --no-open # 只编译不启动
|
# sh scripts/local_test.sh --no-open # 只编译不启动
|
||||||
#
|
#
|
||||||
# 后端固定指向生产 https://jiu.51yanmei.com(与 CI compile-macos.sh 一致)。
|
# 后端固定指向生产 https://jiu.51yanmei.com(与 CI compile-macos.sh 一致;
|
||||||
|
# 2026-07-03 备案通过已回切 https 域名)。
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
export PATH="/opt/homebrew/bin:$PATH"
|
export PATH="/opt/homebrew/bin:$PATH"
|
||||||
|
|||||||
Reference in New Issue
Block a user