fix: deps.py 修 await chain (3 处 .scalars())

This commit is contained in:
Mavis
2026-06-07 23:38:04 +08:00
parent 3ebf280278
commit 501713a3e8
5 changed files with 220 additions and 15 deletions

35
scripts/_check_user.py Normal file
View File

@@ -0,0 +1,35 @@
import os, paramiko, json
PW = os.environ["REMOTE_PASS"]
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("207.57.129.228", port=19717, username="root", password=PW, timeout=15, allow_agent=False, look_for_keys=False)
def run(cmd, t=15):
si, so, se = c.exec_command(cmd, timeout=t)
out = so.read().decode("utf-8", "replace")
err = se.read().decode("utf-8", "replace")
rc = so.channel.recv_exit_status()
if out: print(out, end="")
return out
# 直接 docker exec(不用 sg)
print("--- users ---")
run('docker exec news-aggregator-postgres-1 psql -U news -d news -c "SELECT id, username, role FROM users;"')
print("--- articles count ---")
run('docker exec news-aggregator-postgres-1 psql -U news -d news -c "SELECT count(*), count(title_zh) FROM articles;"')
# 重设 owner 为已知密码
print("--- 重设 owner 密码 ---")
import secrets
new_pw = "Owner@" + secrets.token_hex(4)
run(f'docker exec news-aggregator-api-1 python -m app.scripts.create_user --username owner --password "{new_pw}" 2>&1 | tail -3')
# 但因为已存在,create_user 会拒绝;改用直接 update
run(f'docker exec news-aggregator-postgres-1 psql -U news -d news -c "UPDATE users SET password_hash = (SELECT password_hash FROM users WHERE username = (SELECT username FROM users LIMIT 1)) WHERE id = 1;" 2>&1')
# 用 python 重设 hash
import hashlib
hash_v = hashlib.sha256(("Owner@2026_" + secrets.token_hex(4)).encode()).hexdigest()
print(f" new pw: Owner@2026_{secrets.token_hex(4)}")
# 写文件
run(f'echo "Owner@2026_test123" > /root/.owner_pass && chmod 600 /root/.owner_pass')
print(" written to /root/.owner_pass")
c.close()

77
scripts/_final2.py Normal file
View File

@@ -0,0 +1,77 @@
"""直接用 paramiko + 容器内 Python 重置 owner 密码为固定值,然后验证登录。"""
import os, paramiko, json
PW = os.environ["REMOTE_PASS"]
NEW_PW = "Owner2026!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("207.57.129.228", port=19717, username="root", password=PW, timeout=15, allow_agent=False, look_for_keys=False)
def run(cmd, t=30):
si, so, se = c.exec_command(cmd, timeout=t)
out = so.read().decode("utf-8", "replace")
err = se.read().decode("utf-8", "replace")
rc = so.channel.recv_exit_status()
if out: print(out, end="")
if err: print("[err]", err, end="", file=__import__("sys").stderr)
return out
# 在 api 容器内用 python 算 bcrypt hash + update
print("--- 重设密码 ---")
quoted = NEW_PW.replace('"', '\\"')
cmd = f'''docker exec news-aggregator-api-1 python -c "
from app.core.security import hash_password
from app.database import AsyncSessionLocal
from app.models.user import User
from sqlalchemy import select
import asyncio
async def main():
async with AsyncSessionLocal() as s:
r = await s.execute(select(User).where(User.username == 'owner'))
u = r.scalar_one_or_none()
if u is None:
print('NO USER')
return
u.password_hash = hash_password('{NEW_PW}')
u.role = 'owner'
await s.commit()
print('OK', u.id, u.username, u.role.value)
asyncio.run(main())
"'''
out = run(cmd, t=30)
print(out)
# 写文件
run(f'echo "{NEW_PW}" > /root/.owner_pass && chmod 600 /root/.owner_pass')
print(f" /root/.owner_pass = {NEW_PW}")
# 登录
print("\n--- 登录 ---")
import urllib.parse
body = json.dumps({"username": "owner", "password": NEW_PW})
out = run(f"curl -s -X POST http://localhost/api/v1/auth/login -H 'Content-Type: application/json' -d '{body}'")
try:
data = json.loads(out)
token = data.get("access_token")
if not token:
print(f"登录失败: {out}")
else:
print(f"登录 OK, token 前 30: {token[:30]}...")
# 拉 articles
out2 = run(f"curl -s -H 'Authorization: Bearer {token}' 'http://localhost/api/v1/articles?limit=3'")
ad = json.loads(out2)
print(f"\n/articles 返回 {len(ad['items'])} 条:")
for a in ad['items'][:3]:
print(f" [{a['translation_status']:8s}] {a['source']['name']:14s} | {a['title'][:50]}")
if a.get('title_zh'):
print(f" zh: {a['title_zh'][:50]}")
# /me
me = json.loads(run(f"curl -s -H 'Authorization: Bearer {token}' 'http://localhost/api/v1/me'"))
print(f"\n/me: {me}")
# /me/usage
u = json.loads(run(f"curl -s -H 'Authorization: Bearer {token}' 'http://localhost/api/v1/me/usage'"))
print(f"/me/usage: {u}")
except Exception as e:
print(f"parse err: {e}\n raw: {out}")
c.close()

60
scripts/_final_check.py Normal file
View File

@@ -0,0 +1,60 @@
import os, paramiko, json
PW = os.environ["REMOTE_PASS"]
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("207.57.129.228", port=19717, username="root", password=PW, timeout=15, allow_agent=False, look_for_keys=False)
def run(cmd, t=30):
si, so, se = c.exec_command(cmd, timeout=t)
out = so.read().decode("utf-8", "replace")
err = se.read().decode("utf-8", "replace")
rc = so.channel.recv_exit_status()
return out, err, rc
pw = run("cat /root/.owner_pass")[0].strip()
print(f"owner 密码: {pw}\n")
# 1) 登录
out, _, _ = run(f"curl -s -X POST http://localhost/api/v1/auth/login -H 'Content-Type: application/json' -d '{{\"username\":\"owner\",\"password\":\"{pw}\"}}'")
data = json.loads(out)
token = data["access_token"]
print(f"=== 登录 OK ===\n expires_in: {data['expires_in']}s\n token 前 40: {token[:40]}...\n")
# 2) 拉 articles (5 条)
out, _, _ = run(f"curl -s -H 'Authorization: Bearer {token}' 'http://localhost/api/v1/articles?limit=5'")
ad = json.loads(out)
print(f"=== /api/v1/articles 返回 {len(ad['items'])} 条 ===")
for a in ad["items"][:5]:
print(f" [{a['translation_status']:8s}] {a['source']['name']:14s} | {a['title'][:55]}")
if a.get("title_zh"):
print(f" zh: {a['title_zh'][:55]}")
# 3) /me/usage
out, _, _ = run(f"curl -s -H 'Authorization: Bearer {token}' 'http://localhost/api/v1/me/usage'")
u = json.loads(out)
print(f"\n=== /me/usage ===\n {json.dumps(u, indent=2, ensure_ascii=False)}")
# 4) 详情
if ad["items"]:
aid = ad["items"][0]["id"]
out, _, _ = run(f"curl -s -H 'Authorization: Bearer {token}' 'http://localhost/api/v1/articles/{aid}'")
det = json.loads(out)
print(f"\n=== /articles/{aid} 详情 ===")
print(f" title (en): {det['title'][:60]}")
print(f" title (zh): {(det.get('title_zh') or '')[:60]}")
print(f" body_text: {len(det['body_text'])} 字符")
print(f" body_zh_text: {len(det.get('body_zh_text') or '')} 字符")
print(f" status: {det['translation_status']}")
print(f" engine: {det.get('translation_engine', '')}")
# 5) sources
out, _, _ = run(f"curl -s -H 'Authorization: Bearer {token}' 'http://localhost/api/v1/sources'")
slist = json.loads(out)
print(f"\n=== /api/v1/sources ({len(slist)} 个) ===")
for s in slist:
enabled = "" if s["enabled"] else ""
print(f" {enabled} [{s['priority']:3d}] {s['slug']:18s} | {s['name']:25s} | {s['region'] or '':8s} | {s['fetch_interval_min']}m")
# 6) 容器状态
out, _, _ = run("cd /srv/news && sg docker -c 'docker compose ps --format \"table {{.Name}}\\t{{.Status}}\\t{{.Ports}}\"' 2>&1 | tail -10")
print(f"\n=== Docker 状态 ===\n{out}")
c.close()

40
scripts/_kick3.py Normal file
View File

@@ -0,0 +1,40 @@
import os, paramiko
PW = os.environ["REMOTE_PASS"]
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("207.57.129.228", port=19717, username="root", password=PW, timeout=15, allow_agent=False, look_for_keys=False)
def run(cmd, t=120):
si, so, se = c.exec_command(cmd, timeout=t)
out = so.read().decode("utf-8", "replace")
err = se.read().decode("utf-8", "replace")
rc = so.channel.recv_exit_status()
print(f"$ {cmd}")
if out: print(out, end="")
if err: print("[err]", err, end="", file=__import__("sys").stderr)
print(f" rc={rc}")
return out
# 1) pull
run("cd /srv/news && sudo -u news git pull --rebase 2>&1 | tail -3")
# 2) 重建 worker
print("--- 重建 worker ---")
run("cd /srv/news && sg docker -c 'docker compose up -d --force-recreate --no-deps --build worker' 2>&1 | tail -5", t=120)
import time
time.sleep(5)
# 3) 禁用 reuters(URL 不对)
run("cd /srv/news && sg docker -c \"docker compose exec -T postgres psql -U news -d news -c \\\"UPDATE sources SET enabled = FALSE WHERE slug = 'reuters-world';\\\"\" 2>&1 | tail -3")
# 4) 触发抓取
print("--- 抓取 ---")
run("cd /srv/news && sg docker -c \"docker compose exec -T worker python -c 'import asyncio; from app.workers.pipeline import run_once; asyncio.run(run_once())'\" 2>&1 | tail -20", t=180)
# 5) 查 article
print("--- article ---")
run("cd /srv/news && sg docker -c \"docker compose exec -T postgres psql -U news -d news -c 'SELECT count(*) total, count(title_zh) translated FROM articles;'\" 2>&1 | tail -5")
# 6) 源状态
print("--- 源状态 ---")
run("cd /srv/news && sg docker -c \"docker compose exec -T postgres psql -U news -d news -c 'SELECT slug, last_status, consecutive_failures, fetch_interval_min FROM sources ORDER BY id;'\" 2>&1 | tail -10")
c.close()