garázs fejlesztés

This commit is contained in:
Roo
2026-07-20 11:17:52 +00:00
parent 2e0abc62a7
commit 4594de6c11
80 changed files with 9135 additions and 8702 deletions

View File

@@ -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,
}