148 lines
5.2 KiB
Bash
148 lines
5.2 KiB
Bash
#!/bin/bash
|
|
# 部署 search_suggestions 到远程服务器(在能 ssh 进服务器的机器上跑)
|
|
#
|
|
# 用法: bash scripts/deploy_search_suggestions.sh
|
|
#
|
|
# 假设:
|
|
# - 你已经能 ssh news@207.57.129.228 免密登录
|
|
# - 远程路径 /srv/news 是部署根
|
|
# - docker compose 服务名: api / worker / frontend / postgres / redis
|
|
|
|
set -euo pipefail
|
|
|
|
REMOTE="news@207.57.129.228"
|
|
APP_DIR="/srv/news"
|
|
COMPOSE_DIR="$APP_DIR"
|
|
|
|
log() { echo -e "\033[1;36m[$(date +'%H:%M:%S')]\033[0m $*"; }
|
|
ok() { echo -e "\033[1;32m ✓\033[0m $*"; }
|
|
err() { echo -e "\033[1;31m ✗\033[0m $*" >&2; }
|
|
warn(){ echo -e "\033[1;33m !\033[0m $*"; }
|
|
|
|
dc() { ssh "$REMOTE" "cd $APP_DIR && sg docker -c 'docker compose $*'"; }
|
|
|
|
log "=== 1/9 拉代码 ==="
|
|
ssh "$REMOTE" "cd $APP_DIR && git pull origin main"
|
|
ok "git pull done"
|
|
|
|
log "=== 2/9 跑 alembic 迁移 ==="
|
|
dc exec -T api alembic upgrade head
|
|
ok "alembic upgrade head"
|
|
|
|
log "=== 3/9 验证 schema ==="
|
|
dc exec -T postgres psql -U "\$POSTGRES_USER" -d "\$POSTGRES_DB" -c "
|
|
SELECT table_name FROM information_schema.tables
|
|
WHERE table_name IN ('search_keywords', 'search_title_suggestions')
|
|
ORDER BY table_name;
|
|
"
|
|
# 预期: 2 行
|
|
|
|
log "=== 4/9 验证 trigger 挂上 ==="
|
|
dc exec -T postgres psql -U "\$POSTGRES_USER" -d "\$POSTGRES_DB" -c "
|
|
SELECT tgname, tgenabled
|
|
FROM pg_trigger
|
|
WHERE tgrelid = 'articles'::regclass
|
|
AND NOT tgisinternal
|
|
AND tgname LIKE 'trg_articles%';
|
|
"
|
|
# 预期: trg_articles_rebuild_title_suggestions | O
|
|
|
|
log "=== 5/9 重建 worker(让新加的 _refresh_search_keywords job 生效) ==="
|
|
dc up -d --no-deps --force-recreate worker
|
|
sleep 5
|
|
dc ps worker
|
|
ok "worker recreated"
|
|
|
|
log "=== 6/9 重建 api(让 search router 生效) ==="
|
|
dc up -d --no-deps --force-recreate api
|
|
sleep 5
|
|
dc ps api
|
|
ok "api recreated"
|
|
|
|
log "=== 7/9 重建 frontend(让 NAutoComplete 生效) ==="
|
|
dc up -d --no-deps --build frontend
|
|
sleep 10
|
|
dc ps frontend
|
|
ok "frontend rebuilt"
|
|
|
|
log "=== 8/9 回灌历史 articles ==="
|
|
dc exec -T api python -m app.scripts.backfill_search_suggestions
|
|
# 预期: backfill start: N ... backfill done: N rows in X.Xs ... refresh_search_keywords() done
|
|
|
|
log "=== 9/9 端到端 API 测试 ==="
|
|
# 读 owner 密码
|
|
OWNER_PASS=$(ssh "$REMOTE" "sudo cat /root/.owner_pass 2>/dev/null || echo ''")
|
|
if [ -z "$OWNER_PASS" ]; then
|
|
warn "找不到 /root/.owner_pass,改用交互式输入 owner 密码"
|
|
read -s -p "owner password: " OWNER_PASS
|
|
echo
|
|
fi
|
|
|
|
# 登录拿 token
|
|
log " 登录拿 token..."
|
|
TOKEN=$(curl -s --max-time 10 -X POST "http://127.0.0.1/api/v1/auth/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"username\":\"owner\",\"password\":\"$OWNER_PASS\"}" \
|
|
| python -c "import sys,json; d=json.load(sys.stdin); print(d.get('access_token',''))")
|
|
|
|
if [ -z "$TOKEN" ]; then
|
|
err "登录失败,跳过 API 测试(检查密码)"
|
|
exit 1
|
|
fi
|
|
ok "got token (${#TOKEN} chars)"
|
|
|
|
log " 测试 1: 空 q (应当 422 参数校验)"
|
|
curl -s -o /dev/null -w " HTTP %{http_code}\n" \
|
|
"http://127.0.0.1/api/v1/search/suggestions" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
log " 测试 2: 正常 q='美'"
|
|
curl -s --max-time 10 "http://127.0.0.1/api/v1/search/suggestions?q=美&limit=10" \
|
|
-H "Authorization: Bearer $TOKEN" | python -m json.tool
|
|
|
|
log " 测试 3: 正常 q='美联储'"
|
|
curl -s --max-time 10 "http://127.0.0.1/api/v1/search/suggestions?q=%E7%BE%8E%E8%81%94%E5%82%A8&limit=10" \
|
|
-H "Authorization: Bearer $TOKEN" | python -m json.tool
|
|
|
|
log " 测试 4: 长 prefix (>20 字符,应当 422)"
|
|
curl -s -o /dev/null -w " HTTP %{http_code}\n" \
|
|
"http://127.0.0.1/api/v1/search/suggestions?q=this_is_a_very_long_prefix_x" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
log " 测试 5: 不存在的字符 q='zzzzz' (应当返回空)"
|
|
curl -s --max-time 10 "http://127.0.0.1/api/v1/search/suggestions?q=zzzzz&limit=10" \
|
|
-H "Authorization: Bearer $TOKEN" | python -m json.tool
|
|
|
|
log " 测试 6: 未鉴权 (应当 401)"
|
|
curl -s -o /dev/null -w " HTTP %{http_code}\n" \
|
|
"http://127.0.0.1/api/v1/search/suggestions?q=美"
|
|
|
|
log "=== 10/10 worker 日志看 search_keywords 刷新 ==="
|
|
dc logs worker --tail 50 2>&1 | grep -i "search_keywords" || warn "没找到 search_keywords 相关日志(可能要等 10s)"
|
|
|
|
log "=== 11/11 trigger 实际工作: 拿最新文章看 search_title_suggestions ==="
|
|
ARTICLE_ID=$(dc exec -T postgres psql -U "\$POSTGRES_USER" -d "\$POSTGRES_DB" -t -c \
|
|
"SELECT id FROM articles WHERE title_zh IS NOT NULL ORDER BY id DESC LIMIT 1;" \
|
|
| tr -d ' ' | tr -d '\r')
|
|
echo " 最新带 title_zh 的文章 id: $ARTICLE_ID"
|
|
if [ -n "$ARTICLE_ID" ]; then
|
|
dc exec -T postgres psql -U "\$POSTGRES_USER" -d "\$POSTGRES_DB" -c "
|
|
SELECT article_id, title_lang,
|
|
array_length(prefix_keys, 1) AS prefix_count,
|
|
published_at
|
|
FROM search_title_suggestions
|
|
WHERE article_id = $ARTICLE_ID;
|
|
"
|
|
# 预期: 一行,prefix_count 应该是标题字符数
|
|
fi
|
|
|
|
log "=== 12/12 search_keywords 表有数据吗 ==="
|
|
dc exec -T postgres psql -U "\$POSTGRES_USER" -d "\$POSTGRES_DB" -c \
|
|
"SELECT source, count(*) FROM search_keywords GROUP BY source;"
|
|
# 预期: 1 行,ts_stat | N
|
|
|
|
echo
|
|
echo "================================================"
|
|
echo -e "\033[1;32m 部署 + 测试完成!请把上面输出贴回去排查\033[0m"
|
|
echo "================================================"
|