feat: harden multi-account backend and add tests
This commit is contained in:
+41
-2
@@ -760,6 +760,33 @@ class PerAccountContinuousScrapeManager:
|
||||
}
|
||||
store.update(mutate)
|
||||
|
||||
def refresh_config(self) -> None:
|
||||
"""
|
||||
Re-read account state from disk and sync the in-memory config.
|
||||
Call this after external state changes (import, channel add/remove)
|
||||
so the manager picks up new channels or config updates without a restart.
|
||||
"""
|
||||
disk_state = load_account(DATA_DIR, self.account_id)
|
||||
disk_cfg = disk_state.get("continuous_scraping") or {}
|
||||
with self.lock:
|
||||
# Merge disk config into memory, preserving our current running state
|
||||
self.config["enabled"] = bool(disk_cfg.get("enabled", False))
|
||||
self.config["interval_minutes"] = max(1, int(disk_cfg.get("interval_minutes", 1) or 1))
|
||||
self.config["channels"] = [
|
||||
str(item).strip()
|
||||
for item in disk_cfg.get("channels", [])
|
||||
if str(item).strip()
|
||||
]
|
||||
self.config["run_all_tracked"] = bool(disk_cfg.get("run_all_tracked", True))
|
||||
# Sync running state with desired enabled state
|
||||
if self.config["enabled"] and not self.status["running"]:
|
||||
pass # don't auto-start — user must call start()
|
||||
elif not self.config["enabled"] and self.status["running"]:
|
||||
self._log("Continuous disabled via external state change, stopping.", "warn")
|
||||
self.stop_event.set()
|
||||
self.status["running"] = False
|
||||
self._log("Config refreshed from disk.", "debug")
|
||||
|
||||
def _log(self, message: str, level: str = "debug") -> None:
|
||||
timestamp = utc_now_iso()
|
||||
entry = {"timestamp": timestamp, "level": level, "message": message}
|
||||
@@ -840,6 +867,9 @@ class PerAccountContinuousScrapeManager:
|
||||
|
||||
def _run_loop(self) -> None:
|
||||
while not self.stop_event.is_set():
|
||||
# Refresh config from disk so channel / setting changes take effect
|
||||
self.refresh_config()
|
||||
|
||||
# Check auth — don't iterate if account isn't authorized
|
||||
auth_info = auth_status_for(self.account_id)
|
||||
if auth_info.get("status") not in ("ready", "authorized"):
|
||||
@@ -936,6 +966,11 @@ class ContinuousScrapeOrchestrator:
|
||||
result[account_id] = dict(mgr.snapshot())
|
||||
return result
|
||||
|
||||
def refresh_account(self, account_id: str) -> None:
|
||||
"""Re-read account state from disk and sync the continuous manager config."""
|
||||
mgr = self._get_or_create(account_id)
|
||||
mgr.refresh_config()
|
||||
|
||||
def update_for(
|
||||
self,
|
||||
account_id: str,
|
||||
@@ -945,7 +980,9 @@ class ContinuousScrapeOrchestrator:
|
||||
run_all_tracked: bool,
|
||||
) -> Dict[str, Any]:
|
||||
mgr = self._get_or_create(account_id)
|
||||
return mgr.update(enabled, interval_minutes, channels, run_all_tracked)
|
||||
result = mgr.update(enabled, interval_minutes, channels, run_all_tracked)
|
||||
# After explicit update, sync state to disk is already done by mgr.update
|
||||
return result
|
||||
|
||||
def add_account(self, account_id: str, auto_start: bool = True) -> None:
|
||||
acc_state = load_account(DATA_DIR, account_id)
|
||||
@@ -1825,7 +1862,6 @@ class TelegramScraperRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def _legacy_auth_snapshot(self) -> Dict[str, Any]:
|
||||
state = load_state()
|
||||
session_ready = (SESSION_DIR / "session.session").exists()
|
||||
return {
|
||||
"phase": "unknown",
|
||||
"status": "unknown",
|
||||
@@ -2273,6 +2309,7 @@ class TelegramScraperRequestHandler(BaseHTTPRequestHandler):
|
||||
get_global_store(DATA_DIR).update(mutate_global)
|
||||
get_account_store(DATA_DIR, account_id).save(imported_state)
|
||||
self.app.continuous_orchestrator.add_account(account_id, auto_start=False)
|
||||
self.app.continuous_orchestrator.refresh_account(account_id)
|
||||
return self.send_json({"ok": True, "account_id": account_id})
|
||||
|
||||
def _handle_post_account(self, path: str, body: Dict[str, Any]) -> None:
|
||||
@@ -2381,6 +2418,7 @@ class TelegramScraperRequestHandler(BaseHTTPRequestHandler):
|
||||
if name:
|
||||
state.setdefault("channel_names", {})[channel_id] = str(name).strip()
|
||||
store.update(mutate)
|
||||
self.app.continuous_orchestrator.refresh_account(account_id)
|
||||
return self.send_json({"ok": True, "channel_id": channel_id})
|
||||
|
||||
def _handle_account_channel_remove(self, account_id: str, channel_id: str) -> None:
|
||||
@@ -2395,6 +2433,7 @@ class TelegramScraperRequestHandler(BaseHTTPRequestHandler):
|
||||
del chans[channel_id]
|
||||
state.get("channel_names", {}).pop(channel_id, None)
|
||||
store.update(mutate)
|
||||
self.app.continuous_orchestrator.refresh_account(account_id)
|
||||
return self.send_json({"ok": existed[0], "channel_id": channel_id})
|
||||
|
||||
def _handle_account_job_scrape(self, account_id: str, body: Dict[str, Any]) -> None:
|
||||
|
||||
Reference in New Issue
Block a user