Files
diary-news/scripts/deploy_search_suggestions.sh

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 "================================================"