Files
web_huakui_web/deploy_test.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

125 lines
6.1 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
# 花魁短剧测试服:检查 → 构建 → dist.zip → 上传 → Supervisor 重启/健康检查。
# DEPLOY_DRY_RUN=1 ./deploy_test.sh 仅核对,不构建、不连接服务器。
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
cd "$SCRIPT_DIR"
HOST="${DEPLOY_HOST:-root@172.104.181.99}"
SSH_PORT="${DEPLOY_SSH_PORT:-22889}"
REMOTE_DIR="${DEPLOY_DIR:-/server/hkdj_net/h5}"
CHECK_URL="${DEPLOY_CHECK_URL:-https://hkdjneth5.remoces.com}"
PACKAGE="$SCRIPT_DIR/dist.zip"
CHECKSUM="$SCRIPT_DIR/dist.zip.sha256"
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 ;;
-h|--help)
printf '用法:%s [--dry-run]\n默认目标 root@172.104.181.99:22889 /server/hkdj_net/h5/\n可覆盖 DEPLOY_HOST、DEPLOY_SSH_PORT、DEPLOY_DIR、DEPLOY_CHECK_URL;DEPLOY_DRY_RUN=1 等同 --dry-run。\n' "$0"
exit 0 ;;
*) fail "不支持的参数 $argument;使用 --help 查看用法" ;;
esac
done
# 这些值会作为远端路径/命令使用,仅允许普通 SSH 目标和绝对路径。
[[ "$HOST" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.@-]*$ ]] || fail "DEPLOY_HOST 格式无效"
[[ "$SSH_PORT" =~ ^[0-9]+$ ]] && [ "$SSH_PORT" -ge 1 ] && [ "$SSH_PORT" -le 65535 ] || fail "DEPLOY_SSH_PORT 无效"
[[ "$REMOTE_DIR" =~ ^/[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*/?$ ]] || fail "DEPLOY_DIR 必须是普通绝对路径"
for tool in node npm zip unzip ssh scp; 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,推荐 Node.js 22"
node -e 'try { const u=new URL(process.argv[1]); if (!["http:","https:"].includes(u.protocol) || u.username || u.password || u.search || u.hash || u.pathname!=="/") process.exit(1); } catch { process.exit(1); }' "$CHECK_URL" || fail "DEPLOY_CHECK_URL 必须是 HTTP(S) 站点根地址"
for file in config/test.env config/secrets/m3u8.key deploy/up_next_h5.sh package-lock.json; do
[ -s "$file" ] || fail "缺少 $file"
done
bash -n deploy/up_next_h5.sh
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
printf 'DRY RUN:测试配置 config/test.env;上游与端口可由服务器 config/test.local.env 覆盖。\n'
# 不输出本地覆盖/进程配置中的凭证;只展示仓库公共配置。
node --input-type=module -e 'import {readFileSync} from "node:fs"; import {parse} from "dotenv"; const c=parse(readFileSync("config/test.env")); console.log(` API=${c.UPSTREAM_API_URL} PORT=${c.PORT}`)'
printf ' npm run check → npm run build:test → npm run bundle:test\n'
printf ' dist.zip(不含 node_modules / 本地覆盖)+ SHA-256 → %s:%s/(SSH %s)\n' "$HOST" "$REMOTE_DIR" "$SSH_PORT"
printf ' ssh 执行 %s/up_next_h5.sh:安装 Linux 运行依赖 → next/ → Supervisor hkdj_next_h5 → 健康检查\n' "$REMOTE_DIR"
printf ' 公网检查 %s:/healthz(test)+ 首页(Next HTML);此模式不访问域名。\n' "$CHECK_URL"
exit 0
fi
step '检查类型和单元测试'
npm run check
step '构建并整理测试环境 standalone'
npm run build:test
npm run bundle:test
for file in start.mjs server.js .next-test/BUILD_ID config/test.env config/secrets/m3u8.key; do
[ -s "dist/test/$file" ] || fail "测试包缺少 $file"
done
[ -d dist/test/.next-test/static ] && [ -d dist/test/public ] || fail '测试包缺少静态资源'
# Next 本机的原生模块不能直接用于 Linux:远端用 lockfile 安装运行依赖。
# dotenv 属于 dependencies,start.mjs 在 npm ci --omit=dev 后仍可加载配置。
cp package.json package-lock.json dist/test/
step '生成 dist.zip 和 SHA-256(保留本地包便于排查)'
rm -f "$PACKAGE" "$CHECKSUM"
(
cd dist/test
COPYFILE_DISABLE=1 zip -q -r "$PACKAGE" . \
-x 'node_modules/*' '*/node_modules/*' '*.local.env' '.env*' '*/.env*' '._*' '*/._*' '*.log'
)
chmod 600 "$PACKAGE"
if command -v sha256sum >/dev/null; then
sha256sum dist.zip > "$CHECKSUM"
else
shasum -a 256 dist.zip > "$CHECKSUM"
fi
unzip -tq "$PACKAGE"
step "上传到 $HOST:$REMOTE_DIR/"
ssh -p "$SSH_PORT" "$HOST" "mkdir -p '$REMOTE_DIR'"
scp -P "$SSH_PORT" "$PACKAGE" "$CHECKSUM" deploy/up_next_h5.sh "$HOST:$REMOTE_DIR/"
step '执行服务器更新脚本'
ssh -p "$SSH_PORT" "$HOST" "/bin/bash '$REMOTE_DIR/up_next_h5.sh'"
step "检查公网测试站 $CHECK_URL"
if ! node --input-type=module - "$CHECK_URL" <<'NODE'
const origin = process.argv[2];
async function read(path) {
const response = await fetch(new URL(path, origin), { signal: AbortSignal.timeout(4000), redirect: 'error', cache: 'no-store' });
if (response.status !== 200) throw new Error(`${path} HTTP ${response.status}`);
let text = '', size = 0;
const decoder = new TextDecoder();
for await (const part of response.body) {
size += part.length;
if (size > 262144) throw new Error(`${path} 响应超过检查上限`);
text += decoder.decode(part, { stream: true });
if (path === '/' && text.includes('/_next/')) break;
}
return { text, type: response.headers.get('content-type') || '' };
}
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const [health, home] = await Promise.all([read('/healthz'), read('/')]);
let status;
try { status = JSON.parse(health.text); } catch { throw new Error('/healthz 返回无效 JSON'); }
if (status?.status !== 'ok' || status?.environment !== 'test') throw new Error('/healthz 未命中 Next 测试服务');
if (!/^text\/html(?:;|$)/i.test(home.type) || !home.text.includes('/_next/')) throw new Error('首页不是 Next HTML');
console.log('公网 /healthz 与 Next 首页检查通过');
process.exit(0);
} catch (error) {
console.error(`公网检查 ${attempt}/3 失败:${error.message}`);
if (attempt < 3) await new Promise(resolve => setTimeout(resolve, 1000));
}
}
process.exit(1);
NODE
then
fail '服务器服务已更新,但域名检查失败,请检查 Nginx 和 Cloudflare 回源配置。'
fi
printf '\n测试服更新完成:%s:%s/next(Supervisor:hkdj_next_h5)\n' "$HOST" "$REMOTE_DIR"