admin frontend elkezdése, járművek tisztázása, frontend fejleszts
This commit is contained in:
241
backend/app/scripts/cleanup_user.py
Normal file
241
backend/app/scripts/cleanup_user.py
Normal file
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Hard Delete Cleanup Script — Cascade törlés User és kapcsolódó rekordokra.
|
||||
|
||||
Használat:
|
||||
docker compose exec sf_api python3 /app/backend/app/scripts/cleanup_user.py --email %test%
|
||||
|
||||
Az --email paraméter részleges egyezést is támogat (SQL LIKE, pl. %test%@%.
|
||||
A szkript tranzakcióban törli:
|
||||
- User rekordot
|
||||
- Person rekordot (ha a User volt az egyetlen kapcsolódó)
|
||||
- Organization rekordot (ha a User volt az egyetlen owner)
|
||||
- Session tokeneket, SocialAccount-okat, Wallet-eket, stb.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from sqlalchemy import select, delete, text
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
# Projekt importok
|
||||
from app.database import async_session_factory
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.models.identity.identity import (
|
||||
SocialAccount, Wallet, ActiveVoucher, VerificationToken,
|
||||
UserDeviceLink, UserTrustProfile, OneTimePassword,
|
||||
)
|
||||
from app.models.gamification.gamification import UserStats
|
||||
from app.models.marketplace.service_request import ServiceRequest
|
||||
from app.models.social import ServiceReview
|
||||
from app.models.vehicle.asset import VehicleOwnership
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
logger = logging.getLogger("cleanup_user")
|
||||
|
||||
|
||||
async def cleanup_user(email_pattern: str) -> None:
|
||||
"""
|
||||
Megkeresi a megadott email mintára illeszkedő usereket,
|
||||
és cascade hard delete-et hajt végre rajtuk.
|
||||
"""
|
||||
async with async_session_factory() as db:
|
||||
# 1. Keresés részleges email egyezéssel
|
||||
stmt = (
|
||||
select(User)
|
||||
.where(User.email.ilike(email_pattern))
|
||||
.options(
|
||||
joinedload(User.person),
|
||||
joinedload(User.stats),
|
||||
joinedload(User.wallet),
|
||||
joinedload(User.trust_profile),
|
||||
joinedload(User.ownership_history),
|
||||
joinedload(User.social_accounts),
|
||||
joinedload(User.service_reviews),
|
||||
joinedload(User.service_requests),
|
||||
)
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
users = result.unique().scalars().all()
|
||||
|
||||
if not users:
|
||||
logger.warning("❌ Nem található user a '%s' mintára.", email_pattern)
|
||||
return
|
||||
|
||||
logger.info("🔍 Talált userek (%d db):", len(users))
|
||||
for u in users:
|
||||
logger.info(" - ID=%d, Email=%s, Role=%s", u.id, u.email, u.role.value if hasattr(u.role, 'value') else u.role)
|
||||
|
||||
# 2. Tranzakció indítása
|
||||
async with db.begin():
|
||||
for user in users:
|
||||
user_id = user.id
|
||||
email = user.email
|
||||
logger.info("🗑️ Törlés: User ID=%d, Email=%s", user_id, email)
|
||||
|
||||
# --- Cascade törlés ---
|
||||
|
||||
# UserDeviceLink
|
||||
await db.execute(
|
||||
delete(UserDeviceLink).where(UserDeviceLink.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ UserDeviceLink törölve")
|
||||
|
||||
# VerificationToken
|
||||
await db.execute(
|
||||
delete(VerificationToken).where(VerificationToken.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ VerificationToken törölve")
|
||||
|
||||
# OneTimePassword
|
||||
await db.execute(
|
||||
delete(OneTimePassword).where(OneTimePassword.email == email)
|
||||
)
|
||||
logger.info(" ✓ OneTimePassword törölve")
|
||||
|
||||
# SocialAccount (cascade all, delete-orphan)
|
||||
await db.execute(
|
||||
delete(SocialAccount).where(SocialAccount.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ SocialAccount törölve")
|
||||
|
||||
# UserTrustProfile (cascade all, delete-orphan)
|
||||
await db.execute(
|
||||
delete(UserTrustProfile).where(UserTrustProfile.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ UserTrustProfile törölve")
|
||||
|
||||
# UserStats (gamification)
|
||||
await db.execute(
|
||||
delete(UserStats).where(UserStats.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ UserStats törölve")
|
||||
|
||||
# Wallet + ActiveVoucher (cascade)
|
||||
wallet_stmt = select(Wallet).where(Wallet.user_id == user_id)
|
||||
wallet_result = await db.execute(wallet_stmt)
|
||||
wallet = wallet_result.scalar_one_or_none()
|
||||
if wallet:
|
||||
await db.execute(
|
||||
delete(ActiveVoucher).where(ActiveVoucher.wallet_id == wallet.id)
|
||||
)
|
||||
await db.execute(
|
||||
delete(Wallet).where(Wallet.id == wallet.id)
|
||||
)
|
||||
logger.info(" ✓ Wallet + ActiveVoucher törölve")
|
||||
|
||||
# ServiceReview
|
||||
await db.execute(
|
||||
delete(ServiceReview).where(ServiceReview.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ ServiceReview törölve")
|
||||
|
||||
# ServiceRequest
|
||||
await db.execute(
|
||||
delete(ServiceRequest).where(ServiceRequest.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ ServiceRequest törölve")
|
||||
|
||||
# VehicleOwnership
|
||||
await db.execute(
|
||||
delete(VehicleOwnership).where(VehicleOwnership.user_id == user_id)
|
||||
)
|
||||
logger.info(" ✓ VehicleOwnership törölve")
|
||||
|
||||
# Person rekord kezelése
|
||||
person = user.person
|
||||
if person:
|
||||
# Ellenőrizzük, hogy más User nem hivatkozik-e erre a Person-ra
|
||||
other_users_stmt = (
|
||||
select(User)
|
||||
.where(User.person_id == person.id, User.id != user_id)
|
||||
.limit(1)
|
||||
)
|
||||
other_result = await db.execute(other_users_stmt)
|
||||
other_user = other_result.scalar_one_or_none()
|
||||
|
||||
if not other_user:
|
||||
# Nincs más User ehhez a Person-hoz -> törölhető
|
||||
await db.execute(
|
||||
delete(Person).where(Person.id == person.id)
|
||||
)
|
||||
logger.info(" ✓ Person ID=%d törölve (egyedüli kapcsolat)", person.id)
|
||||
else:
|
||||
logger.info(
|
||||
" ⚠️ Person ID=%d MEGTARTVA (más User is hivatkozik rá: ID=%d)",
|
||||
person.id, other_user.id
|
||||
)
|
||||
|
||||
# Organization kezelése (ha a User volt az egyetlen owner)
|
||||
from app.models.identity.organization import Organization
|
||||
org_stmt = (
|
||||
select(Organization)
|
||||
.where(Organization.owner_id == user_id)
|
||||
)
|
||||
org_result = await db.execute(org_stmt)
|
||||
owned_orgs = org_result.scalars().all()
|
||||
|
||||
for org in owned_orgs:
|
||||
# Ellenőrizzük, hogy más User nem owner-e
|
||||
other_owner_stmt = (
|
||||
select(User)
|
||||
.where(User.id != user_id, User.id.isnot(None))
|
||||
.limit(1)
|
||||
)
|
||||
# Egyszerűbb: csak töröljük, ha nincs más owner
|
||||
# (a gyakorlatban az Organization-nak lehet több tagja)
|
||||
from app.models.identity.organization import OrganizationMember
|
||||
member_stmt = (
|
||||
select(OrganizationMember)
|
||||
.where(
|
||||
OrganizationMember.organization_id == org.id,
|
||||
OrganizationMember.user_id != user_id,
|
||||
)
|
||||
.limit(1)
|
||||
)
|
||||
member_result = await db.execute(member_stmt)
|
||||
other_member = member_result.scalar_one_or_none()
|
||||
|
||||
if not other_member:
|
||||
await db.execute(
|
||||
delete(Organization).where(Organization.id == org.id)
|
||||
)
|
||||
logger.info(" ✓ Organization ID=%d törölve (egyedüli owner)", org.id)
|
||||
else:
|
||||
logger.info(
|
||||
" ⚠️ Organization ID=%d MEGTARTVA (van más tag)",
|
||||
org.id
|
||||
)
|
||||
|
||||
# VÉGÜL: Maga a User rekord törlése
|
||||
await db.execute(
|
||||
delete(User).where(User.id == user_id)
|
||||
)
|
||||
logger.info(" ✅ User ID=%d (%s) törölve.", user_id, email)
|
||||
|
||||
logger.info("🎉 Tranzakció sikeresen végrehajtva.")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Hard Delete Cleanup — User és kapcsolódó rekordok cascade törlése."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--email",
|
||||
required=True,
|
||||
help="Email minta (SQL LIKE, pl. %%test%% vagy teszt%%).",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
import asyncio
|
||||
asyncio.run(cleanup_user(args.email))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user