feat: v1.1.1 — 同步间隔可调(5 秒 ~ 1 天,默认 10 秒,改完立即生效)
- 间隔存入 settings.json,带范围校验(5~86400 秒),非法值明确拒绝 - 设置页新增「同步间隔」输入框,附人性化时长提示(秒/分钟/小时) - 同步循环每轮读取最新间隔,保存后下一轮生效,无需重启 - 首页/状态页的间隔文案改为动态显示(原来硬编码 10 秒) - manifest 1.1.0 → 1.1.1
This commit is contained in:
@@ -47,7 +47,9 @@ DEFAULT_REPOS = ["SMB_Android", "DCU_APP", "GD32E50X_BOOT", "DCU_DesktopControll
|
||||
"Motor_v2", "DCU_ECAT_Master", "GD32_BOOT"]
|
||||
LEGACY_GH_USER = "cbb2625274797" # 旧版硬编码账号,仅用于首次运行的迁移
|
||||
LEGACY_GITEA_USER = "cbb" # 旧版硬编码的 Gitea 用户名,仅作默认值
|
||||
INTERVAL = 10
|
||||
DEFAULT_INTERVAL = 10 # 同步间隔默认值(秒)
|
||||
MIN_INTERVAL = 5 # 最小同步间隔(秒)
|
||||
MAX_INTERVAL = 86400 # 最大同步间隔(秒,1 天)
|
||||
PANEL_PORT = int(os.environ.get("PANEL_PORT", "8899"))
|
||||
START_TS = time.time()
|
||||
NAME_RE = re.compile(r"^[A-Za-z0-9._-]{1,100}$")
|
||||
@@ -59,7 +61,24 @@ ENV["GIT_SSH_COMMAND"] = (f"ssh -i {SSH_KEY} -o IdentitiesOnly=yes "
|
||||
ENV["GIT_TERMINAL_PROMPT"] = "0"
|
||||
|
||||
# ---------------- 应用设置(settings.json) ----------------
|
||||
cfg = {"gitea_user": LEGACY_GITEA_USER, "gitea_pass": "", "auto_create": True}
|
||||
cfg = {"gitea_user": LEGACY_GITEA_USER, "gitea_pass": "", "auto_create": True,
|
||||
"interval": DEFAULT_INTERVAL}
|
||||
|
||||
|
||||
def sync_interval():
|
||||
"""当前同步间隔(秒),始终返回合法值"""
|
||||
try:
|
||||
iv = int(cfg.get("interval", DEFAULT_INTERVAL))
|
||||
except Exception:
|
||||
iv = DEFAULT_INTERVAL
|
||||
return min(MAX_INTERVAL, max(MIN_INTERVAL, iv))
|
||||
|
||||
|
||||
def valid_interval(v):
|
||||
try:
|
||||
return MIN_INTERVAL <= int(v) <= MAX_INTERVAL
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def load_settings():
|
||||
@@ -69,6 +88,8 @@ def load_settings():
|
||||
cfg["gitea_user"] = d["gitea_user"]
|
||||
cfg["gitea_pass"] = str(d.get("gitea_pass") or "")
|
||||
cfg["auto_create"] = bool(d.get("auto_create", True))
|
||||
if valid_interval(d.get("interval")):
|
||||
cfg["interval"] = int(d["interval"])
|
||||
except Exception:
|
||||
pass
|
||||
write_gitcreds()
|
||||
@@ -464,7 +485,7 @@ def loop():
|
||||
state["loop_last"] = time.time()
|
||||
if state["enabled"]:
|
||||
run_sync()
|
||||
time.sleep(INTERVAL)
|
||||
time.sleep(sync_interval())
|
||||
|
||||
|
||||
def load_panel_state():
|
||||
@@ -544,14 +565,14 @@ def global_status():
|
||||
now = time.time()
|
||||
next_in = None
|
||||
if state["enabled"] and state["loop_last"]:
|
||||
next_in = max(0, int(state["loop_last"] + INTERVAL - now))
|
||||
next_in = max(0, int(state["loop_last"] + sync_interval() - now))
|
||||
gitea_all = list_gitea_repos()
|
||||
acc = active_account()
|
||||
return {
|
||||
"enabled": state["enabled"],
|
||||
"syncing": state["syncing"],
|
||||
"uptime": fmt_uptime(now - START_TS),
|
||||
"interval": INTERVAL,
|
||||
"interval": sync_interval(),
|
||||
"last_check": state["last_check"],
|
||||
"next_in": next_in,
|
||||
"checks": state["checks"],
|
||||
@@ -707,7 +728,7 @@ PAGE_HOME = """
|
||||
<label class="switch"><input type="checkbox" id="sw" onchange="toggle()"><span class="slider"></span></label>
|
||||
<div>
|
||||
<div class="swlabel" id="swlabel">自动同步:开</div>
|
||||
<div class="swsub">开启后每 10 秒检查一次 Gitea,有新提交自动推送到 GitHub</div>
|
||||
<div class="swsub" id="swsub">开启后定期检查 Gitea,有新提交自动推送到 GitHub</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
@@ -738,9 +759,10 @@ async function refresh(){
|
||||
const s=await jget('api/state');
|
||||
document.getElementById('sw').checked=s.enabled;
|
||||
document.getElementById('swlabel').textContent='自动同步:'+(s.enabled?'开':'关');
|
||||
document.getElementById('swsub').textContent='开启后每 '+(s.interval||10)+' 秒检查一次 Gitea,有新提交自动推送到 GitHub(间隔可在设置页修改)';
|
||||
const p=document.getElementById('pill');
|
||||
p.textContent=s.enabled?'运行中':'已停止';p.className='pill '+(s.enabled?'on':'off');
|
||||
document.getElementById('sEnabled').textContent=s.enabled?'开启(10秒/次)':'已停止';
|
||||
document.getElementById('sEnabled').textContent=s.enabled?('开启('+(s.interval||10)+'秒/次)'):'已停止';
|
||||
document.getElementById('sAccount').textContent=s.account?(s.account.gh_user+'('+s.account.method+')'):'未配置';
|
||||
document.getElementById('sRepos').textContent=s.repo_count+' 个(已同步 '+s.synced_count+')';
|
||||
document.getElementById('sUptime').textContent=s.uptime;
|
||||
@@ -927,6 +949,11 @@ PAGE_SETTINGS = """
|
||||
<div class="swsub">需要当前账号已配置 Token;关闭后添加不存在的仓库会提示手动创建</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="addrow" style="margin-top:16px;align-items:center">
|
||||
<input id="syncInterval" type="number" min="5" max="86400" step="1" placeholder="同步间隔(秒)" style="max-width:200px">
|
||||
<button class="b-mini" id="btnInterval" onclick="saveInterval()">保存间隔</button>
|
||||
<span class="tip" style="margin:0" id="intervalTip"></span>
|
||||
</div>
|
||||
<div class="msg" id="msgCfg"></div>
|
||||
</div>
|
||||
<script>
|
||||
@@ -940,6 +967,10 @@ async function refresh(){
|
||||
document.getElementById('giteaPass').placeholder=s.gitea_pass_set
|
||||
?'Gitea 密码(已设置;留空保存可清除)':'Gitea 密码(私有仓库必填)';
|
||||
document.getElementById('swAuto').checked=!!s.auto_create;
|
||||
document.getElementById('syncInterval').value=s.interval||10;
|
||||
document.getElementById('syncInterval').min=s.min_interval||5;
|
||||
document.getElementById('syncInterval').max=s.max_interval||86400;
|
||||
document.getElementById('intervalTip').textContent='每隔 '+fmtInterval(s.interval||10)+' 检查一次 Gitea 是否有新提交('+(s.min_interval||5)+'~'+(s.max_interval||86400)+' 秒),改完立即生效';
|
||||
document.getElementById('tbAcc').innerHTML=(s.accounts||[]).map(a=>{
|
||||
const cur=a.id===s.active;
|
||||
const ops=cur
|
||||
@@ -997,6 +1028,21 @@ async function saveAuto(){
|
||||
const r=await (await fetch('api/settings/save',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({auto_create})})).json();
|
||||
showMsg('msgCfg',r.msg||JSON.stringify(r),!!r.ok);refresh();
|
||||
}
|
||||
function fmtInterval(s){
|
||||
s=parseInt(s,10)||0;
|
||||
if(s>=3600)return (s/3600)+' 小时';
|
||||
if(s>=60)return Math.round(s/6)/10+' 分钟';
|
||||
return s+' 秒';
|
||||
}
|
||||
async function saveInterval(){
|
||||
const v=parseInt(document.getElementById('syncInterval').value,10);
|
||||
const b=document.getElementById('btnInterval');b.disabled=true;b.textContent='⏳ 保存中…';
|
||||
try{
|
||||
const r=await (await fetch('api/settings/save',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({interval:v})})).json();
|
||||
showMsg('msgCfg',r.msg||JSON.stringify(r),!!r.ok);
|
||||
}catch(e){showMsg('msgCfg','请求失败: '+e,false)}
|
||||
finally{b.disabled=false;b.textContent='保存间隔';refresh();}
|
||||
}
|
||||
refresh();
|
||||
</script>
|
||||
"""
|
||||
@@ -1051,6 +1097,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self._json({"enabled": state["enabled"], "syncing": state["syncing"],
|
||||
"last_check": state["last_check"], "checks": state["checks"],
|
||||
"last_result": state["last_result"],
|
||||
"interval": sync_interval(),
|
||||
"repo_count": len(repos),
|
||||
"synced_count": sum(1 for r in repos if state_path(acc, r).exists()),
|
||||
"uptime": fmt_uptime(time.time() - START_TS),
|
||||
@@ -1063,6 +1110,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"gitea_pass": mask_token(cfg.get("gitea_pass")),
|
||||
"gitea_pass_set": bool(cfg.get("gitea_pass")),
|
||||
"auto_create": bool(cfg.get("auto_create", True)),
|
||||
"interval": sync_interval(),
|
||||
"min_interval": MIN_INTERVAL,
|
||||
"max_interval": MAX_INTERVAL,
|
||||
"gitea_url": GITEA_UI,
|
||||
"active": active_id,
|
||||
"accounts": [account_public(a) for a in accounts]})
|
||||
@@ -1092,6 +1142,10 @@ class Handler(BaseHTTPRequestHandler):
|
||||
new_user = u
|
||||
if "gitea_pass" in data:
|
||||
new_pass = str(data.get("gitea_pass") or "") # 空串 = 清除密码
|
||||
if "interval" in data:
|
||||
if not valid_interval(data.get("interval")):
|
||||
self._json({"ok": False, "msg": f"同步间隔须为 {MIN_INTERVAL} ~ {MAX_INTERVAL} 之间的整数(秒)"})
|
||||
return
|
||||
# 预校验:涉及 Gitea 账号的改动,先用新凭据调一次 /user 验证
|
||||
trial = dict(cfg)
|
||||
if new_user is not None:
|
||||
@@ -1123,6 +1177,11 @@ class Handler(BaseHTTPRequestHandler):
|
||||
log("Gitea 密码已" + ("更新" if new_pass else "清除"))
|
||||
if "auto_create" in data:
|
||||
cfg["auto_create"] = bool(data.get("auto_create"))
|
||||
if "interval" in data:
|
||||
old_iv = sync_interval()
|
||||
cfg["interval"] = int(data.get("interval"))
|
||||
if cfg["interval"] != old_iv:
|
||||
log(f"同步间隔已调整: {old_iv}s -> {cfg['interval']}s")
|
||||
save_settings()
|
||||
if cfg.get("gitea_pass"):
|
||||
msg = "已保存(Gitea 密码验证通过 ✓,可同步私有仓库)"
|
||||
|
||||
Reference in New Issue
Block a user