added settings page with ability to update password, delete user and CRUD and share abilities for associated children
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import secrets
|
||||
from fastapi import APIRouter, HTTPException, Depends, Header
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel
|
||||
|
||||
from baby_monitor.models.auth import (
|
||||
LoginRequest,
|
||||
@@ -11,22 +12,35 @@ from baby_monitor.models.auth import (
|
||||
RegisterResponse,
|
||||
)
|
||||
from baby_monitor.repositories import (
|
||||
get_child_repository,
|
||||
get_diaper_change_repository,
|
||||
get_feeding_repository,
|
||||
get_invitation_repository,
|
||||
get_sleep_repository,
|
||||
get_token_repository,
|
||||
get_user_repository,
|
||||
)
|
||||
from baby_monitor.repositories.dependencies.get_invitation_repository import (
|
||||
get_invitation_repository,
|
||||
)
|
||||
from baby_monitor.repositories.interfaces import (
|
||||
TokenRepositoryInterface,
|
||||
UserRepositoryInterface,
|
||||
InvitationRepositoryInterface,
|
||||
ChildRepositoryInterface,
|
||||
FeedingRepositoryInterface,
|
||||
DiaperChangeRepositoryInterface,
|
||||
SleepRepositoryInterface,
|
||||
)
|
||||
from baby_monitor.utils.password import hash_password, verify_password
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["authentication"])
|
||||
|
||||
|
||||
class ChangePasswordRequest(BaseModel):
|
||||
"""Request model for password change."""
|
||||
|
||||
current_password: str
|
||||
new_password: str
|
||||
|
||||
|
||||
def verify_token(
|
||||
authorization: Annotated[str | None, Header()] = None,
|
||||
token_repo: TokenRepositoryInterface = Depends(get_token_repository),
|
||||
@@ -191,3 +205,97 @@ def get_current_user(
|
||||
"username": user["username"],
|
||||
"is_admin": user.get("is_admin", False),
|
||||
}
|
||||
|
||||
|
||||
@router.post("/change-password")
|
||||
def change_password(
|
||||
request: ChangePasswordRequest,
|
||||
user_id: Annotated[int, Depends(verify_token)],
|
||||
user_repo: Annotated[UserRepositoryInterface, Depends(get_user_repository)], # noqa: E501
|
||||
) -> dict:
|
||||
"""Change password for the current user."""
|
||||
# Get user with credentials
|
||||
user = user_repo.get_by_id(user_id)
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
# Verify current password
|
||||
if not verify_password(request.current_password, user["hashed_password"]):
|
||||
raise HTTPException(status_code=401, detail="Current password is incorrect")
|
||||
|
||||
# Hash new password
|
||||
new_password_hash = hash_password(request.new_password)
|
||||
|
||||
# Update password
|
||||
success = user_repo.update_password(user_id, new_password_hash)
|
||||
if not success:
|
||||
raise HTTPException(status_code=500, detail="Failed to update password")
|
||||
|
||||
return {"message": "Password changed successfully"}
|
||||
|
||||
|
||||
@router.delete("/account")
|
||||
def delete_account(
|
||||
user_id: Annotated[int, Depends(verify_token)],
|
||||
user_repo: Annotated[UserRepositoryInterface, Depends(get_user_repository)], # noqa: E501
|
||||
child_repo: Annotated[ChildRepositoryInterface, Depends(get_child_repository)], # noqa: E501
|
||||
feeding_repo: Annotated[
|
||||
FeedingRepositoryInterface, Depends(get_feeding_repository)
|
||||
], # noqa: E501
|
||||
diaper_repo: Annotated[
|
||||
DiaperChangeRepositoryInterface, Depends(get_diaper_change_repository)
|
||||
], # noqa: E501
|
||||
sleep_repo: Annotated[SleepRepositoryInterface, Depends(get_sleep_repository)], # noqa: E501
|
||||
) -> dict:
|
||||
"""Delete current user's account and cascade delete orphaned children."""
|
||||
# Get user's children
|
||||
children = child_repo.get_by_user_id(user_id)
|
||||
|
||||
deleted_children = []
|
||||
deleted_feedings = 0
|
||||
deleted_diapers = 0
|
||||
deleted_sleeps = 0
|
||||
|
||||
# Process each child
|
||||
for child in children:
|
||||
child_id = child["id"]
|
||||
|
||||
# Remove the parent-child relationship
|
||||
child_repo.remove_parent(child_id, user_id)
|
||||
|
||||
# Check if child has any remaining parents
|
||||
remaining_parents = child_repo.get_parent_ids(child_id)
|
||||
|
||||
if len(remaining_parents) == 0:
|
||||
# Child is orphaned, delete all logs first
|
||||
feedings = feeding_repo.get_by_child_id(child_id)
|
||||
for feeding in feedings:
|
||||
feeding_repo.delete(feeding["id"])
|
||||
deleted_feedings += 1
|
||||
|
||||
diapers = diaper_repo.get_by_child_id(child_id)
|
||||
for diaper in diapers:
|
||||
diaper_repo.delete(diaper["id"])
|
||||
deleted_diapers += 1
|
||||
|
||||
sleeps = sleep_repo.get_by_child_id(child_id)
|
||||
for sleep in sleeps:
|
||||
sleep_repo.delete(sleep["id"])
|
||||
deleted_sleeps += 1
|
||||
|
||||
# Now delete the child
|
||||
child_repo.delete(child_id)
|
||||
deleted_children.append(child["name"])
|
||||
|
||||
# Finally, delete the user
|
||||
user_repo.delete(user_id)
|
||||
|
||||
return {
|
||||
"message": "Account deleted successfully",
|
||||
"deleted_children": deleted_children,
|
||||
"deleted_logs": {
|
||||
"feedings": deleted_feedings,
|
||||
"diaper_changes": deleted_diapers,
|
||||
"sleep_sessions": deleted_sleeps,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user