admin frontend elkezdése, járművek tisztázása, frontend fejleszts

This commit is contained in:
Roo
2026-06-15 18:52:38 +00:00
parent ef8df9608c
commit 213ba3b0f1
80 changed files with 11615 additions and 2304 deletions

View 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()