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 推送(通用,任何人都能用自己的