from fastapi import APIRouter, File, Form, Response, UploadFile from app.api.deps import CurrentUser, ObjectStoreDep, SessionDep, is_instance_owner from app.schemas.user import UserRead, UserSelfPersonUpdate from app.services import account_service, user_service router = APIRouter(prefix="/users", tags=["users"]) def _me(user) -> UserRead: out = UserRead.model_validate(user) out.is_instance_owner = is_instance_owner(user) return out @router.get("/me", response_model=UserRead) async def read_me(current: CurrentUser) -> UserRead: return _me(current) @router.patch("/me/self-person", response_model=UserRead) async def set_self_person( data: UserSelfPersonUpdate, session: SessionDep, current: CurrentUser ) -> UserRead: """Link (or unlink) the Person record that represents this account.""" user = await user_service.set_self_person( session, user=current, person_id=data.self_person_id ) return _me(user) @router.get("/me/export") async def export_account( session: SessionDep, current: CurrentUser, store: ObjectStoreDep ) -> Response: """Download a full backup (JSON + media) of every tree the user owns.""" data = await account_service.export_account(session, store, user=current) return Response( content=data, media_type="application/zip", headers={"Content-Disposition": 'attachment; filename="provenance-export.zip"'}, ) @router.post("/me/import") async def import_account( session: SessionDep, current: CurrentUser, store: ObjectStoreDep, file: UploadFile = File(...), ) -> dict: """Restore a previously-exported backup into new trees (non-destructive).""" raw = await file.read() return await account_service.import_account(session, store, user=current, raw_zip=raw) @router.delete("/me", status_code=204) async def delete_account( session: SessionDep, current: CurrentUser, confirm_email: str = Form(...) ) -> None: """Delete the account: the user, their owned trees, and their sessions. Requires retyping the account email as a guard.""" await account_service.delete_account(session, user=current, confirm_email=confirm_email)