41 lines
1.2 KiB
Python
41 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
import threading
|
|
from typing import Iterable
|
|
|
|
from app.models import IpEntry
|
|
|
|
|
|
class EntryStorage:
|
|
def __init__(self, file_path: str):
|
|
self._path = Path(file_path)
|
|
self._lock = threading.Lock()
|
|
|
|
def list_entries(self) -> list[IpEntry]:
|
|
with self._lock:
|
|
return self._read_entries_unlocked()
|
|
|
|
def save_entries(self, entries: Iterable[IpEntry]) -> None:
|
|
serializable = [entry.to_dict() for entry in entries]
|
|
with self._lock:
|
|
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
tmp_path = self._path.with_suffix(self._path.suffix + ".tmp")
|
|
tmp_path.write_text(json.dumps(serializable, indent=2, sort_keys=True), encoding="utf-8")
|
|
tmp_path.replace(self._path)
|
|
|
|
def _read_entries_unlocked(self) -> list[IpEntry]:
|
|
if not self._path.exists():
|
|
return []
|
|
|
|
raw = self._path.read_text(encoding="utf-8").strip()
|
|
if not raw:
|
|
return []
|
|
|
|
parsed = json.loads(raw)
|
|
if not isinstance(parsed, list):
|
|
raise ValueError(f"State file must be a list: {self._path}")
|
|
|
|
return [IpEntry.from_dict(item) for item in parsed]
|