Files
BadNote/server/badnote_server/routers/notes_router.py
T

117 lines
3.7 KiB
Python
Raw Normal View History

"""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}