diff --git a/.gitea/workflows/backup.yml b/.gitea/workflows/backup.yml index 1ffc8b9..60dac16 100644 --- a/.gitea/workflows/backup.yml +++ b/.gitea/workflows/backup.yml @@ -18,9 +18,10 @@ jobs: - name: Dump MySQL to local backup dir env: - EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }} - EC2_HOST: ${{ secrets.EC2_HOST }} - EC2_USER: ${{ secrets.EC2_USER }} + # 2026-07-03 割接后备份源=阿里机主库 + DEPLOY_SSH_KEY: ${{ secrets.ALI_SSH_KEY }} + DEPLOY_HOST: ${{ secrets.ALI_HOST }} + DEPLOY_USER: ${{ secrets.ALI_USER }} run: sh scripts/ci/backup-db.sh - name: Notify (Telegram) diff --git a/.gitea/workflows/debug-db.yml b/.gitea/workflows/debug-db.yml index b2f6086..2085c93 100644 --- a/.gitea/workflows/debug-db.yml +++ b/.gitea/workflows/debug-db.yml @@ -10,15 +10,14 @@ jobs: - name: Setup SSH run: | 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 - ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts + ssh-keyscan -H ${{ secrets.ALI_HOST }} >> ~/.ssh/known_hosts - name: Query DB env: - EC2_HOST: ${{ secrets.EC2_HOST }} - EC2_USER: ${{ secrets.EC2_USER }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + EC2_HOST: ${{ secrets.ALI_HOST }} + EC2_USER: ${{ secrets.ALI_USER }} run: | ssh -i ~/.ssh/ec2_deploy.pem ${EC2_USER}@${EC2_HOST} bash << EOF echo "=== Docker containers ===" @@ -26,20 +25,20 @@ jobs: echo "" echo "=== Databases ===" - docker exec jiu_mysql mysql -uroot -p${DB_PASSWORD} -e "SHOW DATABASES;" 2>/dev/null \ - || echo "jiu_mysql container not found or DB_PASSWORD wrong" + 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 MYSQL_ROOT_PASSWORD wrong" echo "" 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 "=== 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 "=== 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 - name: Cleanup SSH key diff --git a/.gitea/workflows/deploy-client.yml b/.gitea/workflows/deploy-client.yml index 84ea62e..79dcb47 100644 --- a/.gitea/workflows/deploy-client.yml +++ b/.gitea/workflows/deploy-client.yml @@ -180,20 +180,9 @@ jobs: GITEA_REPOSITORY: ${{ gitea.repository }} run: sh scripts/ci/release-client.sh "${{ gitea.ref_name }}" - - name: Deploy → EC2 - env: - FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} - 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 + # 2026-07-03 割接后阿里机即唯一发布目标(EC2 轨移除;EC2 仅保留 + # jiu.51yanmei.com 反代桥接到 ali,见 baize 台账)。 + - name: Deploy → Ali env: FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} FORGEJO_URL: ${{ secrets.FORGEJO_URL }} diff --git a/.gitea/workflows/deploy-site.yml b/.gitea/workflows/deploy-site.yml index c7ac282..396ad8f 100644 --- a/.gitea/workflows/deploy-site.yml +++ b/.gitea/workflows/deploy-site.yml @@ -32,20 +32,9 @@ jobs: GITEA_REPOSITORY: ${{ gitea.repository }} run: sh scripts/ci/release-site.sh "${{ gitea.ref_name }}" - - name: Deploy → EC2 - env: - FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} - 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 + # 2026-07-03 割接后阿里机即唯一发布目标(EC2 轨移除;EC2 仅保留 + # jiu.51yanmei.com 反代桥接到 ali,见 baize 台账)。 + - name: Deploy → Ali env: FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} FORGEJO_URL: ${{ secrets.FORGEJO_URL }} diff --git a/.gitea/workflows/manual.yml b/.gitea/workflows/manual.yml index b34e120..8a90027 100644 --- a/.gitea/workflows/manual.yml +++ b/.gitea/workflows/manual.yml @@ -28,9 +28,16 @@ jobs: FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} 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 }} + # 2026-07-03 割接后回滚目标统一为阿里机 + DEPLOY_TARGET: ali + 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: | TAG="${{ inputs.version }}" case "$TAG" in diff --git a/.gitea/workflows/reset-db.yml b/.gitea/workflows/reset-db.yml index 590370d..1b8e98f 100644 --- a/.gitea/workflows/reset-db.yml +++ b/.gitea/workflows/reset-db.yml @@ -17,20 +17,19 @@ jobs: - name: Setup SSH run: | 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 - ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts + ssh-keyscan -H ${{ secrets.ALI_HOST }} >> ~/.ssh/known_hosts - name: Reset database & reseed env: - EC2_HOST: ${{ secrets.EC2_HOST }} - EC2_USER: ${{ secrets.EC2_USER }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + EC2_HOST: ${{ secrets.ALI_HOST }} + EC2_USER: ${{ secrets.ALI_USER }} SHOPS: ${{ github.event.inputs.shops }} run: | # 1. 清库重建 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" # 2. 重启 jiu-server,AutoMigrate 重建表结构 @@ -57,13 +56,13 @@ jobs: echo "→ Seeding $SHOP ..." if [ "$FIRST" = "true" ]; then 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" FIRST=false else grep -v "^TRUNCATE" "$FILE" | \ 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 echo "✓ $SHOP done" done diff --git a/.gitea/workflows/seed.yml b/.gitea/workflows/seed.yml index e6a2f09..b537d0a 100644 --- a/.gitea/workflows/seed.yml +++ b/.gitea/workflows/seed.yml @@ -21,15 +21,14 @@ jobs: - name: Setup SSH run: | 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 - ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts + ssh-keyscan -H ${{ secrets.ALI_HOST }} >> ~/.ssh/known_hosts - name: Seed databases env: - EC2_HOST: ${{ secrets.EC2_HOST }} - EC2_USER: ${{ secrets.EC2_USER }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + EC2_HOST: ${{ secrets.ALI_HOST }} + EC2_USER: ${{ secrets.ALI_USER }} SHOPS: ${{ github.event.inputs.shops }} CLEAR: ${{ github.event.inputs.clear }} run: | @@ -41,7 +40,7 @@ jobs: echo " 清空 ${SHOP}..." printf "SET @shop_code='%s';\n" "$SHOP" | cat - backend/seeds/clear_shop.sql | \ 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 echo "✓ 清空完成" fi @@ -56,7 +55,7 @@ jobs: fi echo "→ Seeding $SHOP ..." 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" echo "✓ $SHOP done" done diff --git a/deploy/nginx-jiu-ali.conf b/deploy/nginx-jiu-ali.conf index 5459cee..ff08dc4 100644 --- a/deploy/nginx-jiu-ali.conf +++ b/deploy/nginx-jiu-ali.conf @@ -2,34 +2,51 @@ # 与 nginx-jiu.conf(EC2 pangolin 容器版,listen 127.0.0.1:8445)等价。 # CI deploy-server.sh 的 ali 分支落到 /etc/nginx/conf.d/jiu.conf。 # -# 过渡期入口 = 8443 明文 HTTP,裸 IP 直连 http://182.92.213.171:8443 -#(备案未通过前 80/443/8080 被阿里云拦截,走非标端口;不建过渡子域, -# 证书签给域名对不上 IP,故过渡期不做 TLS——登录口令/JWT 明文传输,仅限过渡; -# 备案通过后改回 listen 443 ssl + 域名 + 80→443 跳转块)。 +# 2026-07-03 备案通过回切:443 ssl(jiu.51yanmei.com)为唯一正式入口 + 80→443 跳转。 +# 8443 明文过渡口已拆(用户拍板:裸 IP 版 client 从未发过,存量客户端都是 https +# 域名 BASE_URL,DNS 回切后自动恢复,8443 无人使用)。 +# 证书:/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 占用; # 对应 /opt/jiu/config/production.env 的 SERVER_PORT=8081)。 limit_req_zone $binary_remote_addr zone=jiu_pub:10m rate=10r/s; limit_req_status 429; server { - listen 8443 default_server; - server_name _; + # ali nginx 1.24 无 `http2 on;`(1.25.1+),用 listen 参数旧语法 + listen 443 ssl http2 default_server; # 正式入口(备案后回切 2026-07-03) + server_name jiu.51yanmei.com _; - # 备案通过切回 443 时恢复(证书路径仍有效,jiu.51yanmei.com,2026-09-21 到期): - # listen 443 ssl http2; - # server_name jiu.51yanmei.com; - # ssl_certificate /etc/letsencrypt/live/jiu.51yanmei.com/fullchain.pem; - # ssl_certificate_key /etc/letsencrypt/live/jiu.51yanmei.com/privkey.pem; - # ssl_protocols TLSv1.2 TLSv1.3; - # ssl_ciphers HIGH:!aNULL:!MD5; + # 证书现为 EC2 同步副本(2026-08-28 到期)。ACME 通道已就绪:待批准后在 ali 跑 + # `certbot certonly --webroot -w /var/www/acme -d jiu.51yanmei.com` 签新证, + # 然后把这两行换成 /etc/letsencrypt/live/jiu.51yanmei.com/ 并配 reload 部署钩子。 + ssl_certificate /etc/nginx/ssl/jiu.51yanmei.com/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/jiu.51yanmei.com/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + 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; + # certbot HTTP-01 续期通道(webroot 独立目录,不放 marketing—— + # deploy-site 的 rsync --delete 会清掉站点目录里的挑战文件) + location ^~ /.well-known/acme-challenge/ { + root /var/www/acme; + } + # 商品图片静态文件 location ^~ /images/ { alias /opt/jiu/images/; expires 30d; 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; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; + add_header Strict-Transport-Security "max-age=31536000" always; expires 0; } @@ -95,6 +113,7 @@ server { alias /opt/jiu/downloads/; add_header Content-Disposition "attachment"; add_header Cache-Control "no-cache"; + add_header Strict-Transport-Security "max-age=31536000" always; autoindex off; } @@ -105,9 +124,17 @@ server { } } -# 备案通过、入口切回 443 后再恢复此跳转块(80 未备案期间被拦截,挂了也无意义): -# server { -# listen 80; -# server_name jiu.51yanmei.com; -# return 301 https://$host$request_uri; -# } +# 80 → 443 跳转(仅 jiu 域名;51yanmei.com 门户 80 在 51yanmei.conf 自管)。 +# ACME 挑战必须在跳转前放行(HTTP-01 走 80)。 +server { + listen 80; + server_name jiu.51yanmei.com; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/acme; + } + + location / { + return 301 https://$host$request_uri; + } +} diff --git a/scripts/ci/backup-db.sh b/scripts/ci/backup-db.sh index a016749..6e22990 100644 --- a/scripts/ci/backup-db.sh +++ b/scripts/ci/backup-db.sh @@ -1,10 +1,12 @@ #!/usr/bin/env bash -# backup-db.sh — dump the production MySQL (jiu_db) from EC2 and keep a gzip -# snapshot on the mac (host) runner's local disk (~/jiu-db-backups), retaining -# the last 30 days. The DB password is read on EC2 from production.env's -# DATABASE_DSN, so no DB-password secret is needed. +# backup-db.sh — dump the production MySQL (jiu_db) from the production host +# and keep a gzip snapshot on the mac (host) runner's local disk +# (~/jiu-db-backups), retaining the last 30 days. The DB password is read on +# 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 # 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" DEST="${BACKUP_DIR}/${FILENAME}" +TARGET_HOST="${DEPLOY_HOST:-$EC2_HOST}" +TARGET_USER="${DEPLOY_USER:-$EC2_USER}" + setup_ssh trap teardown_ssh EXIT -echo "==> backup-db: dumping jiu_db from ${EC2_HOST}" -# On EC2: parse the DSN password from production.env, then mysqldump the +echo "==> backup-db: dumping jiu_db from ${TARGET_HOST}" +# 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 -# 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. -${SSH} "${EC2_USER}@${EC2_HOST}" 'bash -s' <<'ENDSSH' | gzip > "${DEST}" +${SSH} "${TARGET_USER}@${TARGET_HOST}" 'bash -s' <<'ENDSSH' | gzip > "${DEST}" 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))') exec docker exec -e MYSQL_PWD="${PW}" jiu_mysql mysqldump -uroot --single-transaction --no-tablespaces jiu_db diff --git a/scripts/ci/deploy-server.sh b/scripts/ci/deploy-server.sh index 99406bd..63cf15f 100644 --- a/scripts/ci/deploy-server.sh +++ b/scripts/ci/deploy-server.sh @@ -8,7 +8,7 @@ # is decommissioned — deploying there would resurrect a backend against a stale # database, so the workflow no longer calls the ec2 branch): # - 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): # stop jiu → swap binary → start jiu → health check → reload nginx # inside the pangolin-edge container. @@ -56,7 +56,7 @@ fi if [ "$DEPLOY_TARGET" = "ali" ]; then # --- Alibaba Cloud (live primary since the 2026-07-02 cutover) --- # 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' set -e diff --git a/scripts/ci/notify.sh b/scripts/ci/notify.sh index 81c4254..02861aa 100755 --- a/scripts/ci/notify.sh +++ b/scripts/ci/notify.sh @@ -14,7 +14,7 @@ else fi 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" \ -d "chat_id=${TELEGRAM_CHAT_ID}" \ diff --git a/scripts/ci/release-client.sh b/scripts/ci/release-client.sh index e81eeaf..953d600 100644 --- a/scripts/ci/release-client.sh +++ b/scripts/ci/release-client.sh @@ -28,7 +28,7 @@ ver = sys.argv[1] notes = sys.argv[2] 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" win_url = f"{base}/jiu-windows-x64-setup.exe" android_url = f"{base}/jiu-android.apk" diff --git a/scripts/local_test.sh b/scripts/local_test.sh index 81b2561..22c857b 100755 --- a/scripts/local_test.sh +++ b/scripts/local_test.sh @@ -5,7 +5,8 @@ # sh scripts/local_test.sh # 编译 + 启动 # 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 export PATH="/opt/homebrew/bin:$PATH"