"""Notes CRUD router for BadNote.""" import json from datetime import datetime, timezone from uuid import uuid4 from fastapi import APIRouter, Depends, HTTPException, Query, status from ..auth import get_current_user from ..database import get_db from ..models import NoteCreate, NoteResponse, NoteUpdate router = APIRouter() def _row_to_note(row) -> NoteResponse: return NoteResponse( id=row["id"], user_id=row["user_id"], title=row["title"], created_at=row["created_at"], updated_at=row["updated_at"], tags=json.loads(row["tags"]), strokes_json=row["strokes_json"], ) @router.get("", response_model=list[NoteResponse]) async def list_notes( since: str | None = Query(None, description="ISO8601 timestamp filter"), user_id: str = Depends(get_current_user), ) -> list[NoteResponse]: """List notes, optionally filtered by updated_at > since.""" db = await get_db() if since: cursor = await db.execute( "SELECT * FROM notes WHERE user_id = ? AND updated_at > ? ORDER BY updated_at", (user_id, since), ) else: cursor = await db.execute( "SELECT * FROM notes WHERE user_id = ? ORDER BY updated_at", (user_id,), ) rows = await cursor.fetchall() return [_row_to_note(r) for r in rows] @router.get("/{note_id}", response_model=NoteResponse) async def get_note( note_id: str, user_id: str = Depends(get_current_user), ) -> NoteResponse: """Get a single note by ID.""" db = await get_db() cursor = await db.execute( "SELECT * FROM notes WHERE id = ? AND user_id = ?", (note_id, user_id) ) row = await cursor.fetchone() if row is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found") return _row_to_note(row) @router.post("", response_model=NoteResponse, status_code=status.HTTP_201_CREATED) async def upsert_note( body: NoteCreate, user_id: str = Depends(get_current_user), ) -> NoteResponse: """Create or update a note (upsert by id).""" db = await get_db() now = datetime.now(timezone.utc).isoformat() tags_json = json.dumps(body.tags) existing = await ( await db.execute( "SELECT id FROM notes WHERE id = ? AND user_id = ?", (body.id, user_id) ) ).fetchone() if existing: await db.execute( """UPDATE notes SET title = ?, tags = ?, strokes_json = ?, updated_at = ? WHERE id = ? AND user_id = ?""", (body.title, tags_json, body.strokes_json, now, body.id, user_id), ) else: await db.execute( """INSERT INTO notes (id, user_id, title, created_at, updated_at, tags, strokes_json) VALUES (?, ?, ?, ?, ?, ?, ?)""", (body.id, user_id, body.title, now, now, tags_json, body.strokes_json), ) await db.commit() cursor = await db.execute( "SELECT * FROM notes WHERE id = ? AND user_id = ?", (body.id, user_id) ) row = await cursor.fetchone() return _row_to_note(row) @router.delete("/{note_id}", status_code=status.HTTP_200_OK) async def delete_note( note_id: str, user_id: str = Depends(get_current_user), ) -> dict: """Delete a note.""" db = await get_db() cursor = await db.execute( "SELECT id FROM notes WHERE id = ? AND user_id = ?", (note_id, user_id) ) if await cursor.fetchone() is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found") await db.execute("DELETE FROM notes WHERE id = ? AND user_id = ?", (note_id, user_id)) await db.commit() return {"deleted": note_id}