diff --git a/GiteaHubSync/app/backend/panel.py b/GiteaHubSync/app/backend/panel.py index b58da7f..9e6d723 100644 --- a/GiteaHubSync/app/backend/panel.py +++ b/GiteaHubSync/app/backend/panel.py @@ -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 = """
自动同步:开
-
开启后每 10 秒检查一次 Gitea,有新提交自动推送到 GitHub
+
开启后定期检查 Gitea,有新提交自动推送到 GitHub
@@ -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 = """
需要当前账号已配置 Token;关闭后添加不存在的仓库会提示手动创建
+
+ + + +
""" @@ -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 密码验证通过 ✓,可同步私有仓库)" diff --git a/GiteaHubSync/manifest b/GiteaHubSync/manifest index b5b2661..ef752d6 100644 --- a/GiteaHubSync/manifest +++ b/GiteaHubSync/manifest @@ -1,5 +1,5 @@ appname = GiteaHubSync -version = 1.1.0 +version = 1.1.1 display_name = Gitea→GitHub 同步助手 desc = 每次推送到飞牛 Gitea 的代码,10 秒内自动镜像到 GitHub 的 7 个私有仓库。内置控制面板:自动同步开关、立即同步、仓库状态、同步日志,一键打开 Gitea。数据全部保存在 NAS 本地。 platform = all diff --git a/README.md b/README.md index 19d8544..e14a65d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GiteaHubSync — Gitea → GitHub 镜像同步(飞牛原生应用) -每次推送到飞牛 Gitea(3000 端口)的代码,**10 秒内自动镜像到 GitHub**(默认 7 个仓库: +每次推送到飞牛 Gitea(3000 端口)的代码,**自动镜像到 GitHub**(默认 10 秒一轮,间隔 5 秒 ~ 1 天可在设置页调整;默认 7 个仓库: SMB_Android / DCU_APP / GD32E50X_BOOT / DCU_DesktopController / Motor_v2 / DCU_ECAT_Master / GD32_BOOT, 可任意增减)。 @@ -13,6 +13,7 @@ NAS 重启自动恢复。控制面板带自动同步开关、立即同步、仓 - **多 GitHub 账号**:设置页可添加多个 GitHub 账号(用户名 + Personal Access Token), 一键切换推送目标;每个账号独立记录同步进度,切回原账号不会重复推送 +- **同步间隔可调**:设置页可改检查间隔(5 秒 ~ 1 天,默认 10 秒),改完立即生效 - **自动创建仓库**:Gitea 上的新仓库加入同步时,若 GitHub 上不存在同名仓库, 自动按账号设置为私有/公开创建(可开关) - **两种推送方式**:配置了 Token 的账号走 HTTPS 推送(通用,任何人都能用自己的