garázs fejlesztés
This commit is contained in:
@@ -2,10 +2,11 @@
|
||||
👤 Admin Users API
|
||||
|
||||
Végpontok:
|
||||
GET /admin/users/stats — Felhasználói statisztikák (dashboard)
|
||||
GET /admin/users/{user_id} — Felhasználó részletes adatai (User + Person + Address)
|
||||
PATCH /admin/users/{user_id} — Felhasználó szerkesztése admin által
|
||||
GET /admin/users/{user_id}/memberships — Felhasználó szervezeti tagságai
|
||||
GET /admin/users/stats — Felhasználói statisztikák (dashboard)
|
||||
GET /admin/users/{user_id} — Felhasználó részletes adatai (User + Person + Address)
|
||||
PATCH /admin/users/{user_id} — Felhasználó szerkesztése admin által
|
||||
GET /admin/users/{user_id}/memberships — Felhasználó szervezeti tagságai
|
||||
DELETE /admin/users/{user_id}/hard — [SUPERADMIN] Végleges felhasználó törlés
|
||||
|
||||
Megjegyzés: A GET /admin/users (lista) és POST /admin/users/bulk-action
|
||||
végpontok a admin.py modulban találhatók, mivel az a /admin prefix alá
|
||||
@@ -28,7 +29,9 @@ from app.api import deps
|
||||
from app.db.session import get_db
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.models.identity.address import Address
|
||||
from app.models.identity.identity import Wallet
|
||||
from app.models.marketplace.organization import Organization, OrganizationMember
|
||||
from app.models.system.audit import SecurityAuditLog, FinancialLedger
|
||||
from app.models.vehicle.history import AuditLog, LogSeverity
|
||||
from app.schemas.user import UserResponse
|
||||
from app.schemas.organization import OrganizationMemberResponse
|
||||
@@ -644,3 +647,124 @@ async def get_user_memberships(
|
||||
))
|
||||
|
||||
return membership_list
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Hard Delete Endpoint (Danger Zone)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{user_id}/hard",
|
||||
summary="[SUPERADMIN] Felhasználó végleges törlése",
|
||||
description=(
|
||||
"Véglegesen eltávolít egy felhasználót az adatbázisból. "
|
||||
"KIZÁRÓLAG Superadmin számára elérhető. "
|
||||
"Gatekeeper: Ellenőrzi, hogy a felhasználónak nincs-e pénzügyi rekordja "
|
||||
"(FinancialLedger, Wallet) a törlés előtt. "
|
||||
"Audit trail: SecurityAuditLog-ba rögzíti a műveletet."
|
||||
),
|
||||
)
|
||||
async def hard_delete_user(
|
||||
user_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(deps.get_current_active_user),
|
||||
_ = Depends(deps.RequirePermission("user:manage")),
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
DELETE /admin/users/{user_id}/hard
|
||||
|
||||
Véglegesen töröl egy felhasználót az adatbázisból.
|
||||
Csak Superadmin jogosultsággal érhető el.
|
||||
"""
|
||||
# ── 1. Superadmin ellenőrzés ──────────────────────────────────────────
|
||||
if current_user.role != UserRole.SUPERADMIN:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Only Superadmins can perform hard delete operations."
|
||||
)
|
||||
|
||||
# ── 2. Felhasználó lekérése ───────────────────────────────────────────
|
||||
stmt = select(User).where(User.id == user_id)
|
||||
result = await db.execute(stmt)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"User with ID {user_id} not found."
|
||||
)
|
||||
|
||||
# ── 3. Gatekeeper: Pénzügyi rekordok ellenőrzése ──────────────────────
|
||||
# FinancialLedger ellenőrzés
|
||||
ledger_stmt = select(FinancialLedger).where(FinancialLedger.user_id == user_id).limit(1)
|
||||
ledger_result = await db.execute(ledger_stmt)
|
||||
has_ledger = ledger_result.scalar_one_or_none() is not None
|
||||
|
||||
# Wallet ellenőrzés
|
||||
wallet_stmt = select(Wallet).where(Wallet.user_id == user_id).limit(1)
|
||||
wallet_result = await db.execute(wallet_stmt)
|
||||
has_wallet = wallet_result.scalar_one_or_none() is not None
|
||||
|
||||
if has_ledger or has_wallet:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=(
|
||||
"Cannot hard delete: User has financial or production records. "
|
||||
"Please ensure all financial data is cleared before deletion."
|
||||
)
|
||||
)
|
||||
|
||||
# ── 4. FK referenciák nullázása a törlés előtt ─────────────────────────
|
||||
# Az AuditLog (audit.audit_logs) és SecurityAuditLog (audit.security_audit_logs)
|
||||
# táblák FK constrainttel hivatkoznak identity.users.id-ra, de NINCS
|
||||
# ondelete="SET NULL" beállítva. Ezért manuálisan nullázzuk a referenciákat.
|
||||
user_data = {
|
||||
"user_id": user.id,
|
||||
"email": user.email,
|
||||
"role": str(user.role) if user.role else None,
|
||||
"is_active": user.is_active,
|
||||
"is_deleted": user.is_deleted,
|
||||
}
|
||||
|
||||
# AuditLog.user_id nullázása
|
||||
await db.execute(
|
||||
sa_update(AuditLog)
|
||||
.where(AuditLog.user_id == user_id)
|
||||
.values(user_id=None)
|
||||
)
|
||||
|
||||
# ── 5. Végleges törlés ─────────────────────────────────────────────────
|
||||
await db.delete(user)
|
||||
await db.flush()
|
||||
|
||||
# ── 6. Audit Trail ─────────────────────────────────────────────────────
|
||||
# SecurityAuditLog.target_id FK-val hivatkozik identity.users.id-ra,
|
||||
# de nincs ondelete="SET NULL". Mivel a user már törölve van,
|
||||
# a target_id=None kell legyen, különben ForeignKeyViolationError.
|
||||
# A tényleges user_id a payload_before mezőben kerül tárolásra.
|
||||
audit_log = SecurityAuditLog(
|
||||
actor_id=current_user.id,
|
||||
target_id=None,
|
||||
action="hard_delete_user",
|
||||
is_critical=True,
|
||||
payload_before=user_data,
|
||||
payload_after={
|
||||
"details": f"User #{user_id} ({user.email}) permanently deleted by admin #{current_user.id}."
|
||||
},
|
||||
)
|
||||
db.add(audit_log)
|
||||
|
||||
await db.commit()
|
||||
|
||||
logger.warning(
|
||||
"HARD DELETE: User #%s (%s) permanently deleted by admin #%s",
|
||||
user_id, user.email, current_user.id
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"User #{user_id} has been permanently deleted.",
|
||||
"deleted_user_id": user_id,
|
||||
"deleted_email": user.email,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user