54 lines
2.1 KiB
Python
54 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
from sanic import response
|
|
from sqlalchemy import select
|
|
from datetime import datetime
|
|
|
|
from app.core.models.content_v3 import ContentIndexItem
|
|
from app.core.logger import make_log
|
|
|
|
|
|
async def s_api_v1_content_index(request):
|
|
rows = (await request.ctx.db_session.execute(select(ContentIndexItem))).scalars().all()
|
|
items = [{**r.payload, "encrypted_cid": r.encrypted_cid, "sig": r.sig, "_updated_at": (r.updated_at.isoformat() + 'Z') if r.updated_at else None} for r in rows]
|
|
# ETag by max updated_at + count
|
|
max_ts = max((it.get("_updated_at") for it in items if it.get("_updated_at")), default="1970-01-01T00:00:00Z")
|
|
etag = f'W/"{max_ts}.{len(items)}"'
|
|
inm = request.headers.get('If-None-Match')
|
|
if inm and inm == etag:
|
|
resp = response.empty(status=304)
|
|
resp.headers['ETag'] = etag
|
|
return resp
|
|
for it in items:
|
|
it.pop("_updated_at", None)
|
|
make_log("content.index", f"items={len(items)} etag={etag}")
|
|
resp = response.json({"items": items, "schema": "my-network/index@1"})
|
|
resp.headers['ETag'] = etag
|
|
return resp
|
|
|
|
|
|
async def s_api_v1_content_delta(request):
|
|
since = request.args.get('since')
|
|
if not since:
|
|
# No since provided → act as full index
|
|
return await s_api_v1_content_index(request)
|
|
try:
|
|
# basic parse
|
|
_ = datetime.fromisoformat(since.replace('Z', '+00:00'))
|
|
except Exception:
|
|
return response.json({"error": "BAD_SINCE"}, status=400)
|
|
|
|
rows = (await request.ctx.db_session.execute(select(ContentIndexItem))).scalars().all()
|
|
out = []
|
|
max_ts = since
|
|
for r in rows:
|
|
upd = (r.updated_at.isoformat() + 'Z') if r.updated_at else None
|
|
if upd and upd > since:
|
|
out.append({**r.payload, "encrypted_cid": r.encrypted_cid, "sig": r.sig})
|
|
if upd > max_ts:
|
|
max_ts = upd
|
|
resp = response.json({"items": out, "next_since": max_ts, "schema": "my-network/index@1"})
|
|
# Weak ETag for delta response
|
|
resp.headers['ETag'] = f'W/"{max_ts}.{len(out)}"'
|
|
return resp
|