From 1d4fc7a6916e3f54acda10ad6fd9caffbc711709 Mon Sep 17 00:00:00 2001 From: 3970260761 <3970260761@qq.com> Date: Sat, 7 Nov 2026 23:24:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=80=E9=94=AE=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=97=81=E5=8A=A0=E5=81=9C=E6=AD=A2=E6=8C=89=E9=92=AE=E5=8F=AF?= =?UTF-8?q?=E4=B8=AD=E6=96=AD+=E5=8F=96=E6=B6=88=E6=A0=87=E8=AE=B0=20v1.3.?= =?UTF-8?q?6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 29 +++++++++++++++++++++++++---- public/index.html | 9 +++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app.py b/app.py index 6674759..6a2c88a 100644 --- a/app.py +++ b/app.py @@ -600,8 +600,8 @@ def api_cpu_advise(): "level": level, "verdict": verdict, "tips": tips, "top_cpu": [{"name": t.get("name"), "cpu": t.get("cpu"), "pid": t.get("pid")} for t in tops]}) -_OPT_JOB = {"running": False, "done": False, "steps": [], "freed": 0, - "available": 0, "started": 0} +_OPT_JOB = {"running": False, "done": False, "cancelled": False, "cancel": False, + "steps": [], "freed": 0, "available": 0, "started": 0} def _optimize_worker(): job = _OPT_JOB @@ -615,6 +615,17 @@ def _optimize_worker(): steps.append("数据落盘(sync)完成") except Exception as e: steps.append("数据落盘跳过: %s" % e) + if job.get("cancel"): + job["steps"] = steps + ["已手动停止,后续步骤取消"] + job["cancelled"] = True + job["done"] = False + job["running"] = False + job["cancel"] = False + try: + job["available"] = psutil.virtual_memory().available + except Exception: + pass + return try: p = os.environ.get("PROCFS_PATH", "/proc") with open(os.path.join(p, "sys", "vm", "drop_caches"), "w") as f: @@ -630,6 +641,7 @@ def _optimize_worker(): job["freed"] = max(0, after_mem - before_mem) job["available"] = after_mem job["done"] = True + job["cancelled"] = False job["running"] = False @app.route("/api/sys/optimize", methods=["POST"]) @@ -639,11 +651,19 @@ def api_sys_optimize(): if _OPT_JOB.get("running"): return jsonify({"ok": False, "running": True, "msg": "优化正在进行中,请稍候"}), 409 - _OPT_JOB.update({"running": True, "done": False, "steps": [], - "freed": 0, "available": 0, "started": time.time()}) + _OPT_JOB.update({"running": True, "done": False, "cancelled": False, "cancel": False, + "steps": [], "freed": 0, "available": 0, "started": time.time()}) threading.Thread(target=_optimize_worker, daemon=True).start() return jsonify({"ok": True, "started": True, "eta": 20}) +@app.route("/api/sys/optimize/cancel", methods=["POST"]) +def api_sys_optimize_cancel(): + """停止优化: 置取消标记,worker 在步骤间隙退出,不阻塞""" + if not _OPT_JOB.get("running"): + return jsonify({"ok": False, "msg": "当前没有正在进行的优化"}) + _OPT_JOB["cancel"] = True + return jsonify({"ok": True, "msg": "已发送停止信号"}) + @app.route("/api/sys/optimize/status") def api_sys_optimize_status(): job = _OPT_JOB @@ -653,6 +673,7 @@ def api_sys_optimize_status(): elapsed = 0 return jsonify({"running": job.get("running", False), "done": job.get("done", False), + "cancelled": job.get("cancelled", False), "steps": job.get("steps", []), "freed": job.get("freed", 0), "available": job.get("available", 0), diff --git a/public/index.html b/public/index.html index e6bc648..0bb65c5 100644 --- a/public/index.html +++ b/public/index.html @@ -56,7 +56,7 @@ button.refresh{background:#20a53a;color:#fff;border:none;border-radius:3px;paddi
磁盘
网络
-v1.3.5 +v1.3.6 资享导航站 @@ -86,11 +86,12 @@ function fmtR(b){return fmtB(b)+'/s'} function esc(s){return String(s==null?'':s).replace(/&/g,'&').replace(/t.name.indexOf('CPU')===0);const oth=j.temps.filter(t=>t.name.indexOf('CPU')!==0);const chip=t=>{const c=t.value>=85?'#f00':(t.value>=70?'#ff9900':'#20a53a');return `${esc(t.name)} ${t.value}°C`};if(cpu.length)h+='
'+cpu.map(chip).join('')+'
';if(oth.length)h+='
'+oth.map(chip).join('')+'
'}if(j.fans&&j.fans.length){h+='
'+j.fans.map(f=>`🌀 ${esc(f.name)} ${f.value} RPM`).join('')+'
'}else{h+='
🌀 风扇转速:宿主未暴露(家用 NAS/主板多无此传感器,属正常)
'}if(j.note&&!j.temps.length)h+=`
${esc(j.note)}
`;el.innerHTML=h}catch(e){el.style.display='';el.innerHTML='
🌡️ 温度读取失败
'}} -async function cpuAdvise(){const el=document.getElementById('cpuadvise');const now=Date.now();if(now-lastAdvise<15000){if(el.innerHTML)el.style.display='';return;}lastAdvise=now;try{const r=await fetch('/api/cpu/advise');const j=await r.json();const col=j.level==='轻松'||j.level==='正常'?'#20a53a':(j.level==='偏高'?'#ff9900':'#f00');document.getElementById('cpuadvise').style.display='';document.getElementById('cpuadvise').innerHTML=`
📊 负载诊断:${j.level} (1分钟负载 ${j.load[0]} ÷ ${j.logical}核 = 单核 ${j.per_core})
${esc(j.verdict)}
如何更流畅:
${j.tips.map(t=>`
· ${esc(t)}
`).join('')}
会做两件事:① 把内存里暂存的数据写回硬盘(sync,防断电丢数据);② 回收系统缓存占用的内存(drop caches,只清缓存、不动正在运行的程序)。全程不重启、不杀进程,页面照常刷新。
`}catch(e){}} +async function cpuAdvise(){const el=document.getElementById('cpuadvise');const now=Date.now();if(now-lastAdvise<15000){if(el.innerHTML)el.style.display='';return;}lastAdvise=now;try{const r=await fetch('/api/cpu/advise');const j=await r.json();const col=j.level==='轻松'||j.level==='正常'?'#20a53a':(j.level==='偏高'?'#ff9900':'#f00');document.getElementById('cpuadvise').style.display='';document.getElementById('cpuadvise').innerHTML=`
📊 负载诊断:${j.level} (1分钟负载 ${j.load[0]} ÷ ${j.logical}核 = 单核 ${j.per_core})
${esc(j.verdict)}
如何更流畅:
${j.tips.map(t=>`
· ${esc(t)}
`).join('')}
会做两件事:① 把内存里暂存的数据写回硬盘(sync,防断电丢数据);② 回收系统缓存占用的内存(drop caches,只清缓存、不动正在运行的程序)。全程不重启、不杀进程,页面照常刷新。
`}catch(e){}} async function kill(pid,name){if(!confirm('结束进程 '+name+' (PID '+pid+') ?'))return;const r=await fetch('/api/kill',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({pid})});const j=await r.json();alert(j.ok?'已发送结束信号':('失败: '+j.error));load(true)} function cmpVal(p,key){ if(key==='name')return String(p.name||'');