diff --git a/tinyweb_forum/handlers.py b/tinyweb_forum/handlers.py index 4baf1d4..9efcfa3 100644 --- a/tinyweb_forum/handlers.py +++ b/tinyweb_forum/handlers.py @@ -211,6 +211,7 @@ class ForumHandlers: f'{search_form}' f' + new' f' mod' + f' sync now' f' {muted_link}' f"" f"
" @@ -502,16 +503,26 @@ class ForumHandlers: auto_discover = self.fdb.get_setting("forum_auto_discover", "1") auto_discover_checked = " checked" if auto_discover == "1" else "" + auto_sync = self.fdb.get_setting("forum_auto_sync", "0") + auto_sync_checked = " checked" if auto_sync == "1" else "" retention_days = self.fdb.get_setting("forum_retention_days", "30") return self._respond( f"{msg}
" + f'' f"Sync not available.
") + count = self.sync.sync_now() + msg = f"Synced with {count} instance{'s' if count != 1 else ''}." + if count == 0: + msg = "No peers to sync with." + return self._respond(f"{msg}
") + def handle_sync_add(self, body): instance = body.get("instance", [""])[0].strip().replace("<", "").replace(">", "") name = body.get("name", [""])[0].strip() @@ -744,6 +771,8 @@ class ForumHandlers: return self._with_csrf(self.handle_unmute(sub[8:]), csrf_token) elif sub.startswith("/blockhash/"): return self._with_csrf(self.handle_block_hash(sub[11:]), csrf_token) + elif sub == "/sync/now": + return self._with_csrf(self.handle_sync_now(), csrf_token) elif method == "POST": if not self._check_csrf(body): return self._with_csrf( @@ -780,6 +809,8 @@ class ForumHandlers: return self._with_csrf(self.handle_auto_discover(body), csrf_token) elif sub == "/storage": return self._with_csrf(self.handle_storage(body), csrf_token) + elif sub == "/auto_sync": + return self._with_csrf(self.handle_auto_sync(body), csrf_token) return self._with_csrf(self._error(404), csrf_token) diff --git a/tinyweb_forum/sync.py b/tinyweb_forum/sync.py index ab4e7b3..99e1985 100644 --- a/tinyweb_forum/sync.py +++ b/tinyweb_forum/sync.py @@ -55,13 +55,41 @@ class ForumSync: if self.fdb.get_setting("forum_auto_discover", "1") == "1": self._enable_announce_handler() self._running = True - self._thread = threading.Thread(target=self._sync_loop, daemon=True) - self._thread.start() + if self.fdb.get_setting("forum_auto_sync", "0") == "1": + self._start_sync_loop() def stop(self): self._running = False self._disable_announce_handler() + def set_auto_sync(self, enabled): + self.fdb.set_setting("forum_auto_sync", "1" if enabled else "0") + if enabled: + self._start_sync_loop() + else: + pass # current cycle finishes, no new one starts + + def sync_now(self): + """Run one sync cycle immediately. Returns count of peers synced.""" + instances = self.fdb.get_synced_instances() + random.shuffle(instances) + count = 0 + for inst in instances[:GOSSIP_FANOUT]: + if not self._running: + break + try: + self._sync_with(inst["instance_hash"]) + count += 1 + except Exception as e: + print(f"[forum] sync error with {inst['instance_hash'][:16]}: {e}") + return count + + def _start_sync_loop(self): + if self._thread and self._thread.is_alive(): + return + self._thread = threading.Thread(target=self._sync_loop, daemon=True) + self._thread.start() + def set_auto_discover(self, enabled): self.fdb.set_setting("forum_auto_discover", "1" if enabled else "0") if enabled: