Commit inicial
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""Persistencia del estado de migración (migration-state.json).
|
||||
|
||||
El estado permite reanudar una migración interrumpida y evitar volver a
|
||||
copiar mensajes ya migrados. Nunca se almacenan contraseñas aquí.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import threading
|
||||
from datetime import datetime, timezone
|
||||
|
||||
STATE_VERSION = 1
|
||||
|
||||
|
||||
class MigrationState:
|
||||
def __init__(self, path: str, account: str):
|
||||
self.path = path
|
||||
self.account = account
|
||||
self._lock = threading.Lock()
|
||||
self._data = self._load()
|
||||
|
||||
def _load(self) -> dict:
|
||||
if os.path.exists(self.path):
|
||||
with open(self.path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
data.setdefault("folders", {})
|
||||
return data
|
||||
return {
|
||||
"version": STATE_VERSION,
|
||||
"account": self.account,
|
||||
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||
"folders": {},
|
||||
}
|
||||
|
||||
def save(self) -> None:
|
||||
with self._lock:
|
||||
self._data["updated_at"] = datetime.now(timezone.utc).isoformat()
|
||||
target_dir = os.path.dirname(os.path.abspath(self.path)) or "."
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
fd, tmp_path = tempfile.mkstemp(prefix=".migration-state-", dir=target_dir)
|
||||
try:
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||
json.dump(self._data, f, indent=2, ensure_ascii=False)
|
||||
os.replace(tmp_path, self.path)
|
||||
finally:
|
||||
if os.path.exists(tmp_path):
|
||||
try:
|
||||
os.remove(tmp_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def _folder(self, folder: str) -> dict:
|
||||
return self._data["folders"].setdefault(
|
||||
folder,
|
||||
{"fingerprints": {}, "last_scanned_a_uid": 0, "a_uidvalidity": None},
|
||||
)
|
||||
|
||||
def get_uidvalidity(self, folder: str):
|
||||
return self._folder(folder).get("a_uidvalidity")
|
||||
|
||||
def set_uidvalidity(self, folder: str, value) -> None:
|
||||
f = self._folder(folder)
|
||||
if f.get("a_uidvalidity") != value:
|
||||
f["a_uidvalidity"] = value
|
||||
f["last_scanned_a_uid"] = 0
|
||||
|
||||
def get_last_scanned_uid(self, folder: str) -> int:
|
||||
return self._folder(folder).get("last_scanned_a_uid", 0)
|
||||
|
||||
def set_last_scanned_uid(self, folder: str, uid: int) -> None:
|
||||
f = self._folder(folder)
|
||||
f["last_scanned_a_uid"] = max(f.get("last_scanned_a_uid", 0), uid)
|
||||
|
||||
def has_fingerprint(self, folder: str, fingerprint: str) -> bool:
|
||||
return fingerprint in self._folder(folder)["fingerprints"]
|
||||
|
||||
def mark_migrated(self, folder: str, fingerprint: str, a_uid: int, b_uid, size: int) -> None:
|
||||
self._folder(folder)["fingerprints"][fingerprint] = {
|
||||
"a_uid": a_uid,
|
||||
"b_uid": b_uid,
|
||||
"size": size,
|
||||
"migrated_at": datetime.now(timezone.utc).isoformat(),
|
||||
}
|
||||
|
||||
def folders_seen(self) -> list[str]:
|
||||
return list(self._data["folders"].keys())
|
||||
Reference in New Issue
Block a user