import sqlite3 import threading from datetime import datetime, timezone class SyncState: def __init__(self, db_path="./sync.db"): self.db_path = db_path self._lock = threading.Lock() self._conn = sqlite3.connect(db_path, timeout=30) self._conn.execute("PRAGMA journal_mode=WAL") self._conn.execute(""" CREATE TABLE IF NOT EXISTS events ( uid TEXT PRIMARY KEY, content_hash TEXT NOT NULL, synced_at TIMESTAMP NOT NULL ) """) self._conn.execute(""" CREATE TABLE IF NOT EXISTS ics_cache ( id INTEGER PRIMARY KEY CHECK(id=1), ics_hash TEXT, etag TEXT, fetched_at TIMESTAMP ) """) self._conn.commit() def get_event_uids(self) -> set[str]: with self._lock: cur = self._conn.execute("SELECT uid FROM events") return {row[0] for row in cur.fetchall()} def get_event_hash(self, uid: str) -> str | None: with self._lock: cur = self._conn.execute("SELECT content_hash FROM events WHERE uid = ?", (uid,)) row = cur.fetchone() return row[0] if row else None def upsert_event(self, uid: str, content_hash: str) -> None: with self._lock: self._conn.execute(""" INSERT OR REPLACE INTO events (uid, content_hash, synced_at) VALUES (?, ?, ?) """, (uid, content_hash, datetime.now(timezone.utc).isoformat())) self._conn.commit() def delete_event(self, uid: str) -> None: with self._lock: self._conn.execute("DELETE FROM events WHERE uid = ?", (uid,)) self._conn.commit() def clear_events(self) -> int: with self._lock: cur = self._conn.execute("SELECT COUNT(*) FROM events") count = cur.fetchone()[0] self._conn.execute("DELETE FROM events") self._conn.commit() return count def get_ics_cache(self) -> tuple[str | None, str | None, str | None]: with self._lock: cur = self._conn.execute("SELECT ics_hash, etag, fetched_at FROM ics_cache WHERE id = 1") row = cur.fetchone() if row: return (row[0], row[1], row[2]) return (None, None, None) def set_ics_cache(self, ics_hash: str, etag: str | None) -> None: with self._lock: self._conn.execute(""" INSERT INTO ics_cache (id, ics_hash, etag, fetched_at) VALUES (1, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET ics_hash = ?, etag = ?, fetched_at = ? """, (ics_hash, etag, datetime.now(timezone.utc).isoformat(), ics_hash, etag, datetime.now(timezone.utc).isoformat())) self._conn.commit() def clear_ics_cache(self) -> None: with self._lock: self._conn.execute("DELETE FROM ics_cache") self._conn.commit() def snapshot(self) -> dict: with self._lock: cur = self._conn.execute("SELECT uid, content_hash FROM events") rows = cur.fetchall() return { "uids": [r[0] for r in rows], "hashes": {r[0]: r[1] for r in rows} } def restore_snapshot(self, data: dict) -> None: with self._lock: self._conn.execute("DELETE FROM events") for uid, content_hash in data.get("hashes", {}).items(): self._conn.execute(""" INSERT INTO events (uid, content_hash, synced_at) VALUES (?, ?, ?) """, (uid, content_hash, datetime.now(timezone.utc).isoformat())) self._conn.commit() def close(self) -> None: with self._lock: self._conn.close()