"""Sync router for BadNote — push/pull notes.""" import json from datetime import datetime, timezone from uuid import uuid4 from fastapi import APIRouter, Depends, status from ..auth import get_current_user from ..database import get_db from ..models import NoteResponse, SyncPullRequest, SyncPushRequest, SyncResponse router = APIRouter() @router.post("/push", response_model=SyncResponse, status_code=status.HTTP_200_OK) async def sync_push( body: SyncPushRequest, user_id: str = Depends(get_current_user), ) -> SyncResponse: """Upsert notes from client.""" db = await get_db() synced = 0 for note in body.notes: now = datetime.now(timezone.utc).isoformat() tags_json = json.dumps(note.tags) existing = await ( await db.execute( "SELECT id FROM notes WHERE id = ? AND user_id = ?", (note.id, user_id) ) ).fetchone() if existing: # Last-writer-wins by timestamp: only apply the client's version if # it is newer than what the server already has, so a stale client # cannot overwrite a more recent note (data loss). await db.execute( """UPDATE notes SET title = ?, tags = ?, strokes_json = ?, updated_at = ? WHERE id = ? AND user_id = ? AND updated_at < ?""", ( note.title, tags_json, note.strokes_json, note.updated_at, note.id, user_id, note.updated_at, ), ) else: await db.execute( """INSERT INTO notes (id, user_id, title, created_at, updated_at, tags, strokes_json) VALUES (?, ?, ?, ?, ?, ?, ?)""", (note.id, user_id, note.title, now, note.updated_at, tags_json, note.strokes_json), ) synced += 1 await db.commit() return SyncResponse(synced_count=synced) @router.post("/pull", status_code=status.HTTP_200_OK) async def sync_pull( body: SyncPullRequest, user_id: str = Depends(get_current_user), ) -> dict: """Pull notes updated since a timestamp.""" db = await get_db() cursor = await db.execute( """SELECT * FROM notes WHERE user_id = ? AND updated_at > ? ORDER BY updated_at""", (user_id, body.since), ) rows = await cursor.fetchall() notes = [ NoteResponse( id=r["id"], user_id=r["user_id"], title=r["title"], created_at=r["created_at"], updated_at=r["updated_at"], tags=json.loads(r["tags"]), strokes_json=r["strokes_json"], ).model_dump() for r in rows ] return {"notes": notes}