from bson import ObjectId from fastapi import APIRouter, Depends, HTTPException, status from pymongo import ReturnDocument from app.database import get_mongo_database from app.models import Account from app.routers.auth import get_current_account from app.schemas import StickyNoteCreate, StickyNoteRead, StickyNoteUpdate, utc_now router = APIRouter(prefix="/sticky-notes", tags=["sticky-notes"]) def serialize_note(document: dict) -> StickyNoteRead: return StickyNoteRead( id=str(document["_id"]), title=document["title"], body=document.get("body", ""), tags=document.get("tags", []), color=document.get("color", "yellow"), created_at=document["created_at"], updated_at=document["updated_at"], ) @router.get("", response_model=list[StickyNoteRead]) async def list_sticky_notes( database=Depends(get_mongo_database), account: Account = Depends(get_current_account), ): cursor = database.sticky_notes.find({"account_id": str(account.id)}).sort( "updated_at", -1 ) return [serialize_note(document) async for document in cursor] @router.post("", response_model=StickyNoteRead, status_code=status.HTTP_201_CREATED) async def create_sticky_note( payload: StickyNoteCreate, database=Depends(get_mongo_database), account: Account = Depends(get_current_account), ): now = utc_now() document = { "account_id": str(account.id), "title": payload.title.strip(), "body": payload.body.strip(), "tags": [tag.strip() for tag in payload.tags if tag.strip()], "color": payload.color, "created_at": now, "updated_at": now, } result = await database.sticky_notes.insert_one(document) document["_id"] = result.inserted_id return serialize_note(document) @router.patch("/{note_id}", response_model=StickyNoteRead) async def update_sticky_note( note_id: str, payload: StickyNoteUpdate, database=Depends(get_mongo_database), account: Account = Depends(get_current_account), ): if not ObjectId.is_valid(note_id): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Invalid note ID.", ) updates = payload.model_dump(exclude_unset=True) if "title" in updates: updates["title"] = updates["title"].strip() if "body" in updates: updates["body"] = updates["body"].strip() if "tags" in updates and updates["tags"] is not None: updates["tags"] = [tag.strip() for tag in updates["tags"] if tag.strip()] if not updates: document = await database.sticky_notes.find_one( {"_id": ObjectId(note_id), "account_id": str(account.id)} ) if document is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found.") return serialize_note(document) updates["updated_at"] = utc_now() document = await database.sticky_notes.find_one_and_update( {"_id": ObjectId(note_id), "account_id": str(account.id)}, {"$set": updates}, return_document=ReturnDocument.AFTER, ) if document is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found.") return serialize_note(document) @router.delete("/{note_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_sticky_note( note_id: str, database=Depends(get_mongo_database), account: Account = Depends(get_current_account), ): if not ObjectId.is_valid(note_id): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Invalid note ID.", ) result = await database.sticky_notes.delete_one( {"_id": ObjectId(note_id), "account_id": str(account.id)} ) if result.deleted_count == 0: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found.")