fix: deps.py 修 await chain (3 处 .scalars())
This commit is contained in:
35
scripts/_check_user.py
Normal file
35
scripts/_check_user.py
Normal 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
77
scripts/_final2.py
Normal 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
60
scripts/_final_check.py
Normal 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
40
scripts/_kick3.py
Normal 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()
|
||||
Reference in New Issue
Block a user