update
This commit is contained in:
@@ -12,3 +12,6 @@ SYNC_TOKEN = os.getenv("SYNC_TOKEN", "")
|
||||
# files: Dateien der PCs (Claude-Sitzungen), kommen nur zurück, wenn der Client sie anfragt
|
||||
COLLECTIONS = ("events", "todos", "notes", "folders", "files")
|
||||
BASE_COLLECTIONS = COLLECTIONS[:-1]
|
||||
|
||||
# Dateien kommen höchstens in Paketen dieser Größe zurück, der Client holt mit "more" weiter
|
||||
FILES_PAGE_BYTES = int(os.getenv("FILES_PAGE_BYTES", 16 * 1024 * 1024))
|
||||
|
||||
@@ -75,6 +75,21 @@ def since(conn, collection, seq):
|
||||
return [json.loads(r["data"]) for r in rows]
|
||||
|
||||
|
||||
def since_paged(conn, collection, seq, max_bytes):
|
||||
"""Wie since, aber höchstens max_bytes Daten. Gibt (Einträge, letzter seq, mehr da) zurück."""
|
||||
rows = conn.execute(
|
||||
"SELECT data, seq FROM items WHERE collection = ? AND seq > ? ORDER BY seq", (collection, seq)
|
||||
)
|
||||
items, size, last = [], 0, seq
|
||||
for r in rows:
|
||||
if items and size + len(r["data"]) > max_bytes:
|
||||
return items, last, True
|
||||
items.append(json.loads(r["data"]))
|
||||
size += len(r["data"])
|
||||
last = r["seq"]
|
||||
return items, last, False
|
||||
|
||||
|
||||
def alive(conn, collection):
|
||||
rows = conn.execute(
|
||||
"SELECT data FROM items WHERE collection = ? AND deleted = 0 ORDER BY id", (collection,)
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi import FastAPI, Header, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from . import db
|
||||
from .config import BASE_COLLECTIONS, COLLECTIONS, SYNC_TOKEN
|
||||
from .config import BASE_COLLECTIONS, COLLECTIONS, FILES_PAGE_BYTES, SYNC_TOKEN
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
||||
log = logging.getLogger("app")
|
||||
@@ -68,11 +68,19 @@ def sync(body: SyncIn, authorization: str = Header(default="")):
|
||||
else:
|
||||
rejected[collection].append(stored)
|
||||
out = {}
|
||||
cursor = db.cursor(conn)
|
||||
for collection in body.requested:
|
||||
changed = db.since(conn, collection, body.since)
|
||||
if collection == "files":
|
||||
# Dateien sind groß: nur ein Paket, der Stand bleibt beim letzten gelieferten Eintrag
|
||||
changed, last, more = db.since_paged(conn, collection, body.since, FILES_PAGE_BYTES)
|
||||
if more:
|
||||
out["more"] = True
|
||||
cursor = min(cursor, last)
|
||||
else:
|
||||
changed = db.since(conn, collection, body.since)
|
||||
ids = {item["id"] for item in changed}
|
||||
out[collection] = changed + [item for item in rejected[collection] if item["id"] not in ids]
|
||||
out["cursor"] = db.cursor(conn)
|
||||
out["cursor"] = cursor
|
||||
if accepted:
|
||||
log.info("sync: %d Einträge übernommen", accepted)
|
||||
return out
|
||||
|
||||
@@ -9,6 +9,7 @@ from fastapi.testclient import TestClient
|
||||
def client(tmp_path):
|
||||
os.environ["DB_PATH"] = str(tmp_path / "test.db")
|
||||
os.environ["SYNC_TOKEN"] = "geheim"
|
||||
os.environ["FILES_PAGE_BYTES"] = "150"
|
||||
from app import config, db, main
|
||||
importlib.reload(config)
|
||||
importlib.reload(db)
|
||||
@@ -105,3 +106,19 @@ def test_dateien_nur_auf_anfrage(client):
|
||||
# Tombstone gewinnt mit neuerem updated
|
||||
r = sync(client, files=[{"id": "-home-x/s1.jsonl", "updated": "2026-09-22T11:00:00.000Z", "deleted": True}])
|
||||
assert r["files"][-1]["deleted"] is True
|
||||
|
||||
|
||||
def test_dateien_seitenweise(client):
|
||||
for i in range(3):
|
||||
sync(client, files=[{"id": f"s{i}.jsonl", "updated": "2026-09-22T10:00:00.000Z", "content": "x" * 60}])
|
||||
# Jede Antwort höchstens 150 Bytes: ein Eintrag pro Seite, more zeigt an, dass es weitergeht
|
||||
seen, since = [], 0
|
||||
for _ in range(5):
|
||||
r = sync(client, since=since, files=[])
|
||||
seen += [f["id"] for f in r["files"]]
|
||||
since = r["cursor"]
|
||||
if not r.get("more"):
|
||||
break
|
||||
assert seen == ["s0.jsonl", "s1.jsonl", "s2.jsonl"] and since == 3
|
||||
# Basis-Sammlungen bleiben ungeteilt
|
||||
assert "more" not in sync(client)
|
||||
|
||||
Reference in New Issue
Block a user