update
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, Header, HTTPException
|
||||
from fastapi import FastAPI, Header, HTTPException, Query
|
||||
from fastapi.responses import Response
|
||||
from pydantic import BaseModel
|
||||
|
||||
from . import db
|
||||
from .config import BASE_COLLECTIONS, COLLECTIONS, FILES_PAGE_BYTES, SYNC_TOKEN
|
||||
from . import db, files
|
||||
from .config import BASE_COLLECTIONS, COLLECTIONS, SYNC_TOKEN
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
||||
log = logging.getLogger("app")
|
||||
@@ -16,6 +17,10 @@ async def lifespan(_app):
|
||||
if not SYNC_TOKEN:
|
||||
log.warning("SYNC_TOKEN ist leer, jede Anfrage wird abgelehnt.")
|
||||
db.init()
|
||||
with db.connect(write=True) as conn:
|
||||
moved = files.migrate(conn)
|
||||
if moved:
|
||||
log.info("%d Dateien aus der Datenbank nach %s verschoben", moved, files.FILES_DIR)
|
||||
yield
|
||||
|
||||
|
||||
@@ -57,30 +62,23 @@ def sync(body: SyncIn, authorization: str = Header(default="")):
|
||||
erkennt sie am gleichen updated und überspringt sie.
|
||||
"""
|
||||
check_token(authorization)
|
||||
with db.connect() as conn:
|
||||
with db.connect(write=True) as conn:
|
||||
accepted = 0
|
||||
rejected = {collection: [] for collection in COLLECTIONS}
|
||||
for collection in COLLECTIONS:
|
||||
merge = files.merge if collection == "files" else lambda c, item: db.merge(c, collection, item)
|
||||
for item in getattr(body, collection) or []:
|
||||
ok, stored = db.merge(conn, collection, item)
|
||||
ok, stored = merge(conn, item)
|
||||
if ok:
|
||||
accepted += 1
|
||||
else:
|
||||
elif stored:
|
||||
rejected[collection].append(stored)
|
||||
out = {}
|
||||
cursor = db.cursor(conn)
|
||||
for collection in body.requested:
|
||||
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)
|
||||
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"] = cursor
|
||||
out["cursor"] = db.cursor(conn)
|
||||
if accepted:
|
||||
log.info("sync: %d Einträge übernommen", accepted)
|
||||
return out
|
||||
@@ -106,3 +104,16 @@ def notes(authorization: str = Header(default="")):
|
||||
check_token(authorization)
|
||||
with db.connect() as conn:
|
||||
return db.alive(conn, "notes")
|
||||
|
||||
|
||||
# Inhalt einer Datei. Mit from und hash (Länge und Hash der letzten 64 KB des eigenen Stands)
|
||||
# kommt nur der Rest ab dieser Position, sonst die ganze Datei. X-From sagt, ab wo.
|
||||
@app.get("/api/files/{file_id:path}")
|
||||
def file_content(file_id: str, from_: int = Query(0, alias="from"), hash: str = "", authorization: str = Header(default="")):
|
||||
check_token(authorization)
|
||||
with db.connect() as conn:
|
||||
meta = files.meta(conn, file_id)
|
||||
if not meta or meta.get("deleted"):
|
||||
raise HTTPException(status_code=404, detail="Datei unbekannt")
|
||||
data, start = files.read_from(file_id, from_, hash)
|
||||
return Response(content=data, media_type="application/octet-stream", headers={"X-From": str(start), "X-Size": str(start + len(data))})
|
||||
|
||||
Reference in New Issue
Block a user