Files
web_huakui_web/deploy_prod.sh
T
谢宇宁andClaude Opus 5.5 d2ddb1c321 [Ai] feat: 花魁短剧 Next.js 站点初始提交
- 原生 Next.js App Router + 严格 TypeScript 实现花魁短剧 PC/H5 页面,按 src/features/{catalog,content,playback} 组织,保留完整 SEO(metadata、JSON-LD、canonical、robots、llms、sitemap、IndexNow)。
- 主域名通过 SITE_URL=https://huakuidj.com 强制覆盖后端 foreverUrlAddr(config/*.env、src/server/config.ts、src/server/seo/shared.ts、src/server/services/shared.ts),支持邮箱改为 support@huakuidj.com。
- 移除登录与账户体系:删除 /account 全部页面、登录/注册/充值弹窗、收藏点赞、评论、金币解锁及对应 BFF 接口;旧账户地址 GET/HEAD 统一 301 到首页(src/lib/routes.ts REMOVED_ACCOUNT_PATHS),保留游客会话、反馈、图片上传、观看上报与搜索联想。
- 播放页:评论位改为同类推荐 + 热播榜单,锁定集改为 App 观看遮罩与底部弹层,新增简介标签、分集图例与独立视觉皮肤(src/features/playback、src/styles/pages/playback.css)。
- 卡片:短剧/视频卡状态与时长移至信息行胶囊标签,封面角标移至左上角(src/components/catalog/Cards.tsx、src/styles/foundations/base.css)。
- 同步更新单元测试、浏览器冒烟脚本、路由清单与 README。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 11:33:31 +07:00

132 lines
6.8 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 花魁短剧正式服:检查 → 构建 → Linux 运行包 + MD5 → 跳板机 running.sh。
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
cd "$SCRIPT_DIR"
JUMP_HOST="${DEPLOY_JUMP_HOST:-server@116.212.120.2}"
JUMP_DIR="${DEPLOY_JUMP_DIR:-/home/server/web_hkdj/h5/nuxt}"
# 按正式服约定分别设置:SCP 为 22889,触发 SSH 为默认 22。
SCP_PORT="${DEPLOY_SCP_PORT:-22889}"
SSH_PORT="${DEPLOY_SSH_PORT:-22}"
TARGET_ARCH="${DEPLOY_TARGET_ARCH:-x64}"
TARGET_LIBC="${DEPLOY_TARGET_LIBC:-glibc}"
PACKAGE_NAME=web_hkdj_h5.tar.gz
MD5_NAME=web_hkdj_h5.md5
PACKAGE="$SCRIPT_DIR/$PACKAGE_NAME"
MD5_FILE="$SCRIPT_DIR/$MD5_NAME"
RELEASE="$SCRIPT_DIR/dist/production-linux-$TARGET_ARCH-$TARGET_LIBC"
PACKAGE_ONLY=0
fail() { printf '错误:%s\n' "$*" >&2; exit 1; }
step() { printf '\n▸ %s\n' "$*"; }
for argument in "$@"; do
case "$argument" in
--dry-run) DEPLOY_DRY_RUN=1 ;;
--package-only) PACKAGE_ONLY=1 ;;
-h|--help)
printf '用法:%s [--dry-run | --package-only]\n' "$0"
printf '默认上传 server@116.212.120.2:/home/server/web_hkdj/h5/nuxt/,SCP 22889,SSH 22。\n'
printf '可覆盖 DEPLOY_JUMP_HOST、DEPLOY_JUMP_DIR、DEPLOY_SCP_PORT、DEPLOY_SSH_PORT、DEPLOY_TARGET_ARCH、DEPLOY_TARGET_LIBC。\n'
exit 0 ;;
*) fail "不支持的参数 $argument;使用 --help 查看用法" ;;
esac
done
[[ "$JUMP_HOST" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.@-]*$ ]] || fail 'DEPLOY_JUMP_HOST 格式无效'
[[ "$JUMP_DIR" =~ ^/[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*/?$ ]] || fail 'DEPLOY_JUMP_DIR 必须是普通绝对路径'
JUMP_DIR="${JUMP_DIR%/}"
for port in "$SCP_PORT" "$SSH_PORT"; do
[[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ] || fail 'SCP/SSH 端口必须在 1–65535 内'
done
case "$TARGET_ARCH" in x64|arm64) ;; *) fail 'DEPLOY_TARGET_ARCH 仅支持 x64 或 arm64' ;; esac
case "$TARGET_LIBC" in glibc|musl) ;; *) fail 'DEPLOY_TARGET_LIBC 仅支持 glibc 或 musl' ;; esac
for tool in node npm tar; do
command -v "$tool" >/dev/null || fail "缺少命令 $tool"
done
node -e 'const [a,b]=process.versions.node.split(".").map(Number); if(a<20 || a===20 && b<9) process.exit(1)' || fail '需要 Node.js >=20.9,推荐 22'
node -e 'const [a,b,c]=process.argv[1].split(".").map(Number); if(a<10 || a===10 && (b<9 || b===9 && c<2)) process.exit(1)' "$(npm --version)" || fail '跨平台运行依赖打包需要 npm >=10.9.2'
command -v md5sum >/dev/null || command -v md5 >/dev/null || fail '缺少 md5sum 或 macOS md5'
for file in config/production.env config/secrets/m3u8.key package-lock.json scripts/prepare-production-runtime.ts; do
[ -s "$file" ] || fail "缺少 $file"
done
[ -x node_modules/.bin/tsx ] || fail '请先执行 npm ci 安装本地依赖'
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
printf 'DRY RUN:正式环境;Linux %s/%s;只核对,不构建、不连接服务器。\n' "$TARGET_ARCH" "$TARGET_LIBC"
node --input-type=module -e 'import {readFileSync} from "node:fs"; import {parse} from "dotenv"; const c=parse(readFileSync("config/production.env")); console.log(` 服务端口 PORT=${c.PORT};仅打包 config/production.env,不上传本地覆盖。`)'
printf ' npm run check → build → bundle → bundle:linux -- %s %s\n' "$TARGET_ARCH" "$TARGET_LIBC"
printf ' 保留 standalone 裁剪依赖,仅补目标 Linux Sharp/libvips;不安装完整生产依赖。\n'
printf ' tar 根目录:start.mjs、server.js、node_modules、.next、public、config 等\n'
printf ' 生成 %s + %s(纯 MD5 hash)\n' "$PACKAGE_NAME" "$MD5_NAME"
if [ "$PACKAGE_ONLY" = 1 ]; then
printf ' --package-only:生成后结束,不上传。\n'
else
printf ' scp -P %s %s %s %s:%s/\n' "$SCP_PORT" "$PACKAGE_NAME" "$MD5_NAME" "$JUMP_HOST" "$JUMP_DIR"
printf ' ssh -p %s %s %s/running.sh(不带脚本参数)\n' "$SSH_PORT" "$JUMP_HOST" "$JUMP_DIR"
fi
exit 0
fi
if [ "$PACKAGE_ONLY" = 0 ]; then
for tool in ssh scp tee; do command -v "$tool" >/dev/null || fail "缺少命令 $tool"; done
fi
# 同仓库只能有一次构建/打包/上传,避免并发覆盖同名包或提前触发 running.sh。
mkdir -p "$SCRIPT_DIR/dist"
DEPLOY_LOCK="$SCRIPT_DIR/dist/.production-deploy.lock"
mkdir "$DEPLOY_LOCK" 2>/dev/null || fail "已有正式发布进程或残留锁:${DEPLOY_LOCK};请先确认该进程已结束"
trap 'rmdir "$DEPLOY_LOCK" 2>/dev/null || true' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
step '检查类型和单元测试'
npm run check
step '构建并整理正式环境 standalone'
npm run build
npm run bundle
step "准备 standalone 精简运行包,仅补 Linux $TARGET_ARCH/$TARGET_LIBC 图片库"
npm run bundle:linux -- "$TARGET_ARCH" "$TARGET_LIBC"
for file in start.mjs server.js .next/BUILD_ID config/production.env config/secrets/m3u8.key runtime-target.json; do
[ -s "$RELEASE/$file" ] || fail "正式运行包缺少 $file"
done
for directory in node_modules .next/static public; do
[ -d "$RELEASE/$directory" ] || fail "正式运行包缺少目录 $directory"
done
step "打包 $PACKAGE_NAME"
# -C 保持产物直接位于 tar 根部;禁止 AppleDouble、环境覆盖和日志混入。
COPYFILE_DISABLE=1 COPY_EXTENDED_ATTRIBUTES_DISABLE=1 tar \
--no-xattrs \
--exclude='._*' --exclude='.DS_Store' --exclude='.env*' \
--exclude='*.local.env' --exclude='*.log' \
-czf "$PACKAGE" -C "$RELEASE" .
chmod 600 "$PACKAGE"
if command -v md5sum >/dev/null; then
md5sum "$PACKAGE" | awk '{print $1}' > "$MD5_FILE"
else
md5 -q "$PACKAGE" > "$MD5_FILE"
fi
tar -tzf "$PACKAGE" >/dev/null
printf '运行包已生成:%s;校验文件:%s\n' "$PACKAGE_NAME" "$MD5_NAME"
node -e 'console.log(`压缩包体积:${(require("node:fs").statSync(process.argv[1]).size / 1024 / 1024).toFixed(2)} MiB`)' "$PACKAGE"
if [ "$PACKAGE_ONLY" = 1 ]; then exit 0; fi
step "上传到跳板机 $JUMP_HOST:$JUMP_DIR/"
scp -P "$SCP_PORT" "$PACKAGE" "$MD5_FILE" "$JUMP_HOST:$JUMP_DIR/"
step '执行既有正式服 running.sh'
# gatekeeper 按完整 .sh 路径放行,不能附参数、cd、bash 前缀或其它命令。
RUN_LOG=$(mktemp "$SCRIPT_DIR/dist/production-running.XXXXXX")
printf '远端执行日志:%s\n' "$RUN_LOG"
if ! ssh -p "$SSH_PORT" "$JUMP_HOST" "$JUMP_DIR/running.sh" 2>&1 | tee "$RUN_LOG"; then
fail "正式服 running.sh 执行失败;日志:${RUN_LOG}"
fi
# 部分既有服务器脚本没有透传 Supervisor 的失败退出码,不能只依据 SSH 返回 0。
if ! node - "$RUN_LOG" <<'NODE'
const { readFileSync } = require('node:fs');
const output = readFileSync(process.argv[2], 'utf8').replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '');
if (/\b(?:BACKOFF|FATAL|ERROR)\b|\bspawn\s+error\b/i.test(output)) process.exit(1);
NODE
then
fail "正式服输出包含失败状态,不能确认发布成功;日志:${RUN_LOG}"
fi
printf '\n正式服 running.sh 执行完成,未发现失败状态;日志:%s;本地包已保留供排查。\n' "$RUN_LOG"