aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/users.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/users.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/users.py20
1 files changed, 16 insertions, 4 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py
index f291f59..a1b436e 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/users.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py
@@ -1079,10 +1079,16 @@ async def unlink_node_key(
# ── Account deletion ─────────────────────────────────────────────────────────
-async def erase_account(db: AsyncSession, user: User) -> dict:
+async def erase_account(db: AsyncSession, user: User, owned_groups: str = "refuse") -> dict:
"""
Erase an account, keeping only what the law asked us to keep.
+ Groups the account owns: `"refuse"` (the owner's own deletion) answers 409
+ with their names, because deleting them strands their members and the
+ owner can hand them over first. `"delete"` (an administrator's) deletes
+ them with the account — an erasure an authority has ordered cannot wait on
+ the person it is about.
+
Gone: credentials, email, node key, group memberships, notifications, refresh
tokens, node registrations, device keys, public-swarm sources. The username
is released.
@@ -1105,7 +1111,8 @@ async def erase_account(db: AsyncSession, user: User) -> dict:
"""
owned = (await db.execute(
select(Group).where(Group.admin_id == user.id))).scalars().all()
- if owned:
+ deleted_groups = [{"id": g.id, "name": g.name} for g in owned]
+ if owned and owned_groups != "delete":
raise HTTPException(
status_code=409,
detail=("This account still owns groups: "
@@ -1113,6 +1120,9 @@ async def erase_account(db: AsyncSession, user: User) -> dict:
+ ". Delete them or hand them over first — deleting the "
"account would strand their members."),
)
+ if owned:
+ from meshbay_hub.db.purge import purge_groups
+ await purge_groups(db, [g["id"] for g in deleted_groups])
await db.execute(delete(UserPreference).where(UserPreference.user_id == user.id))
await db.execute(delete(GroupMember).where(GroupMember.user_id == user.id))
@@ -1138,8 +1148,10 @@ async def erase_account(db: AsyncSession, user: User) -> dict:
user.status = "deleted"
user.role = "user"
await db.commit()
- log.info("Account erased: %s (%s)", username, user.id[:8])
- return {"status": "deleted", "username": username}
+ log.info("Account erased: %s (%s), %d owned group(s) deleted",
+ username, user.id[:8], len(deleted_groups))
+ return {"status": "deleted", "username": username, "user_id": user.id,
+ "groups_deleted": deleted_groups}
class DeleteAccountRequest(BaseModel):