99 lines
5.5 KiB
Bash
99 lines
5.5 KiB
Bash
#!/bin/bash
|
|
# kr宝塔 运行堵塞 + Node 深度修复 - 宝塔面板「终端」执行
|
|
# 1. 诊断负载/CPU 2. 停 Node 3. 修复 site.db 4. 查日志 5. 批量启动
|
|
|
|
echo "========== kr宝塔 运行堵塞 + Node 深度修复 =========="
|
|
echo "【0】运行堵塞诊断"
|
|
uptime
|
|
echo "--- CPU TOP10 ---"
|
|
ps aux --sort=-%cpu | head -11
|
|
echo "--- 结束异常高 CPU node 进程 ---"
|
|
for pid in $(ps aux | awk '$3>80 && /node|npm|pnpm/ && !/grep/ {print $2}' 2>/dev/null); do
|
|
echo " kill $pid"; kill -9 $pid 2>/dev/null
|
|
done
|
|
sleep 2
|
|
|
|
python3 << 'PY'
|
|
import hashlib,json,os,re,sqlite3,subprocess,time,urllib.request,urllib.parse,ssl
|
|
ssl._create_default_https_context=ssl._create_unverified_context
|
|
P,K="https://127.0.0.1:9988","qcWubCdlfFjS2b2DMT1lzPFaDfmv1cBT"
|
|
def sg(): t=int(time.time()); return {"request_time":t,"request_token":hashlib.md5((str(t)+hashlib.md5(K.encode()).hexdigest()).encode()).hexdigest()}
|
|
def post(p,d=None): pl=sg(); (pl.update(d) if d else None); r=urllib.request.Request(P+p,data=urllib.parse.urlencode(pl).encode()); return json.loads(urllib.request.urlopen(r,timeout=25).read().decode())
|
|
def pids(port):
|
|
try: return {int(x) for x in re.findall(r"pid=(\d+)",subprocess.check_output("ss -tlnp 2>/dev/null | grep ':%s ' || true"%port,shell=True).decode())}
|
|
except: return set()
|
|
def ports(it):
|
|
cfg=it.get("project_config") or {}
|
|
if isinstance(cfg,str): cfg=json.loads(cfg) if cfg else {}
|
|
p=[]; [p.append(int(cfg["port"])) if cfg.get("port") else None]
|
|
p.extend(int(m) for m in re.findall(r"-p\s*(\d+)",str(cfg.get("project_script",""))))
|
|
return sorted(set(p))
|
|
PATH_FB={"玩值大屏":["/www/wwwroot/self/wanzhi/玩值大屏","/www/wwwroot/self/wanzhi/玩值"],"tongzhi":["/www/wwwroot/self/wanzhi/tongzhi","/www/wwwroot/self/wanzhi/tong"],"神射手":["/www/wwwroot/self/kr/kr-use","/www/wwwroot/self/kr/kr-users"],"AITOUFA":["/www/wwwroot/ext/tools/AITOUFA","/www/wwwroot/ext/tools/AITOL"]}
|
|
print("\n【1】停止 Node"); items=post("/project/nodejs/get_project_list").get("data")or post("/project/nodejs/get_project_list").get("list")or[]
|
|
for it in items:
|
|
n=it.get("name")
|
|
if n:
|
|
try:
|
|
for port in ports(it): [subprocess.call("kill -9 %s 2>/dev/null"%pid,shell=True) for pid in pids(port)]
|
|
pf="/www/server/nodejs/vhost/pids/%s.pid"%n
|
|
if os.path.exists(pf): open(pf,"w").write("0")
|
|
post("/project/nodejs/stop_project",{"project_name":n}); print(" 停:",n)
|
|
except: pass
|
|
time.sleep(0.4)
|
|
time.sleep(4)
|
|
print("\n【2】修复 site.db"); db="/www/server/panel/data/db/site.db"; fixed=0
|
|
if os.path.isfile(db):
|
|
c=sqlite3.connect(db); cur=c.cursor(); cur.execute("SELECT id,name,path,project_config FROM sites WHERE project_type='Node'")
|
|
for row in cur.fetchall():
|
|
sid,name,path,cfg=row[0],row[1],row[2],row[3]or"{}"
|
|
path=(path or"").strip(); cfg=json.loads(cfg) if cfg else {}
|
|
proj=cfg.get("path")or cfg.get("project_path")or path
|
|
if not proj or not os.path.isdir(proj):
|
|
for p in PATH_FB.get(name,[]):
|
|
if os.path.isdir(p): proj=p; break
|
|
if not proj or not os.path.isdir(proj): print(" 跳过",name); continue
|
|
cmd="cd %s && (pnpm start 2>/dev/null || npm run start)"%proj
|
|
old=str(cfg.get("project_script")or cfg.get("run_cmd")or"").strip()
|
|
if "cd " not in old or proj not in old:
|
|
cfg["project_script"]=cfg["run_cmd"]=cmd; cfg["path"]=proj
|
|
cur.execute("UPDATE sites SET path=?,project_config=? WHERE id=?",(proj,json.dumps(cfg,ensure_ascii=False),sid)); fixed+=1
|
|
print(" 修复:",name,"->",proj)
|
|
c.commit(); c.close()
|
|
print(" 共修复 %d 个"%fixed)
|
|
print("\n【3】Node 日志"); ld="/www/server/nodejs/vhost"
|
|
for it in items:
|
|
n=it.get("name")
|
|
if not n: continue
|
|
for lp in ["%s/log/%s.log"%(ld,n),"%s/logs/%s.log"%(ld,n)]:
|
|
if os.path.isfile(lp):
|
|
try: tail="".join(open(lp,"r",encoding="utf-8",errors="ignore").readlines()[-5:]).strip(); print("---",n,"---\n%s\n"%(tail[-600:]if tail else"(空)"))
|
|
except: pass
|
|
break
|
|
else: print(n,": 无日志\n")
|
|
print("\n【4】批量启动")
|
|
for rnd in range(3):
|
|
items=post("/project/nodejs/get_project_list").get("data")or post("/project/nodejs/get_project_list").get("list")or[]
|
|
to_start=[it for it in items if it.get("name") and not it.get("run")]
|
|
if not to_start: print(" 全部已运行"); break
|
|
print(" 第%d轮: %d 个"%(rnd+1,len(to_start)))
|
|
for it in to_start:
|
|
n=it.get("name")
|
|
if not n: continue
|
|
try:
|
|
for port in ports(it): [subprocess.call("kill -9 %s 2>/dev/null"%pid,shell=True) for pid in pids(port)]
|
|
pf="/www/server/nodejs/vhost/pids/%s.pid"%n
|
|
if os.path.exists(pf): open(pf,"w").write("0")
|
|
post("/project/nodejs/stop_project",{"project_name":n}); time.sleep(0.5)
|
|
r=post("/project/nodejs/start_project",{"project_name":n})
|
|
print(" %s: %s"%(n,"OK" if r.get("status") or "成功" in str(r.get("msg","")) else "FAIL"))
|
|
except: print(" %s: ERR"%n)
|
|
time.sleep(2)
|
|
time.sleep(10)
|
|
print("\n【5】最终状态"); items2=post("/project/nodejs/get_project_list").get("data")or post("/project/nodejs/get_project_list").get("list")or[]
|
|
run_c=sum(1 for x in items2 if x.get("run")); print(" 运行 %d/%d"%(run_c,len(items2)))
|
|
for it in items2: print(" %s: %s"%(it.get("name"),"运行中" if it.get("run") else "未启动"))
|
|
print("\n--- 修复后负载 ---"); subprocess.call("uptime",shell=True)
|
|
PY
|
|
echo ""
|
|
echo "========== 完成 =========="
|