feat: upgrade tree deletion to soft delete with deleted_at timestamp

Adds deleted_at and deleted_by columns to trees table for proper soft
delete tracking. Supports future 30-day restore window functionality.
The delete endpoint now sets both is_active=False (backward compat) and
deleted_at/deleted_by. Migration backfills existing is_active=False rows.

Fixed ambiguous FK relationship between User/Tree models by adding
explicit foreign_keys to both sides of the author relationship.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-05 23:33:05 -05:00
parent 3a5ac0f201
commit 33368688b2
4 changed files with 54 additions and 3 deletions

View File

@@ -1,3 +1,4 @@
from datetime import datetime, timezone
from typing import Annotated, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status, Query
@@ -508,7 +509,7 @@ async def delete_tree(
db: Annotated[AsyncSession, Depends(get_db)],
current_user: Annotated[User, Depends(require_admin)]
):
"""Soft delete a tree (admin only). Sets is_active to False."""
"""Soft delete a tree (admin only). Sets deleted_at timestamp and is_active=False."""
result = await db.execute(select(Tree).where(Tree.id == tree_id))
tree = result.scalar_one_or_none()
@@ -519,6 +520,8 @@ async def delete_tree(
)
tree.is_active = False
tree.deleted_at = datetime.now(timezone.utc)
tree.deleted_by = current_user.id
await log_audit(db, current_user.id, "tree.delete", "tree", tree.id,
{"tree_name": tree.name})
await db.commit()