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,179 @@
# 🏗️ Logic Spec: Identity-Preserving Soft Delete & Asset Validation
## 1. Modul Célja és Masterbook 2 Illeszkedés
**Mérföldkő:** Identity & Security Hardening
**Masterbook 2 Referencia:** [`docs/v201/05_AUTH_AND_IDENTITY_SPEC.md`](docs/v201/05_AUTH_AND_IDENTITY_SPEC.md) (Soft Delete / Anonymization szekció)
**Masterbook 2 Referencia:** [`docs/v201/18_ASSET_AND_FLEET_SPECIFICATION.md`](docs/v201/18_ASSET_AND_FLEET_SPECIFICATION.md) (Asset specifikáció)
Jelenlegi állapot a Masterbook szerint:
- A Soft Delete már létezik, de **hiányos**: nincs `deleted_at` timestamp, nincs token invalidáció, nem védi a Person rekordot.
- Az Asset modellben nincs `CheckConstraint` a VIN/Rendszám "Vagy-Vagy" szabályhoz.
- A Pydantic sémákban nincs `model_validator` a kötelező azonosító validációhoz.
---
## 2. Adatmodell változások
### 2.1 Asset CheckConstraint (`backend/app/models/vehicle/asset.py`)
**Jelenlegi állapot:** A [`Asset`](backend/app/models/vehicle/asset.py:69) osztály `__table_args__`-ja csak a sémát adja meg:
```python
__table_args__ = {"schema": "vehicle"}
```
**Módosítás:** Bővítsük ki a `__table_args__`-t egy `CheckConstraint`-tel:
```python
__table_args__ = (
CheckConstraint(
"vin IS NOT NULL OR license_plate IS NOT NULL",
name="ck_asset_vin_or_plate_required"
),
{"schema": "vehicle"}
)
```
**Import:** A [`CheckConstraint`](backend/app/models/vehicle/asset.py:7) már elérhető az SQLAlchemy importok között? Ellenőrizendő: jelenleg csak `UniqueConstraint` van importálva. Ki kell egészíteni:
```python
from sqlalchemy import String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, CheckConstraint, BigInteger, Integer, Float
```
### 2.2 deleted_at oszlop a User modellhez (`backend/app/models/identity/identity.py`)
**Jelenlegi állapot:** A [`User`](backend/app/models/identity/identity.py:122) osztályban már van `is_deleted: Mapped[bool]` (149. sor), de hiányzik a `deleted_at`.
**Módosítás:** Add hozzá a `deleted_at` mezőt a `User` osztályhoz a `created_at` után:
```python
# === SOFT DELETE ===
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
```
### 2.3 deleted_at oszlop a Person modellhez (opcionális, struktúra miatt)
**Jelenlegi állapot:** A [`Person`](backend/app/models/identity/identity.py:36) osztályban nincs `deleted_at`.
**Módosítás:** Add hozzá a struktúra konzisztencia miatt:
```python
# === SOFT DELETE (structure only - never delete Person data) ===
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
```
---
## 3. Pydantic Séma Módosítások
### 3.1 AssetCreate séma (`backend/app/schemas/asset.py`)
**Jelenlegi állapot:** A [`license_plate`](backend/app/schemas/asset.py:116) kötelező (`Field(...)`), a `vin` opcionális.
**Módosítás:** Mindkét mező legyen `Optional[str] = None`:
```python
license_plate: Optional[str] = Field(None, min_length=2, max_length=20, description="Rendszám")
vin: Optional[str] = Field(None, min_length=1, max_length=50, description="VIN szám (opcionális)")
```
**Validátor:** Adj hozzá egy `@model_validator` (Pydantic V2 mód) vagy `@root_validator` (Pydantic V1 mód) metódust, ami:
1. Üres string (`""`) átalakítása `None`-ra mindkét mezőnél
2. Ha mindkettő `None`, dobjon `ValueError`-t (422-es HTTP válasz)
### 3.2 AssetUpdate séma (`backend/app/schemas/asset.py`)
**Jelenlegi állapot:** A [`license_plate`](backend/app/schemas/asset.py:203) és `vin` már `Optional[str] = None`.
**Módosítás:** Adj hozzá egy `@model_validator`-t vagy `@root_validator`-t, ami csak akkor dob hibát, ha a felhasználó explicitly `None`-ra akarja állítani mindkettőt.
---
## 4. Backend Service Logika: Person-Preserving Soft Delete
### 4.1 AuthService.soft_delete_user módosítása (`backend/app/services/auth_service.py`)
**Jelenlegi kód:** [`soft_delete_user`](backend/app/services/auth_service.py:486-503)
**Módosítások sorrendben:**
1. **`deleted_at` beállítás:**
```python
user.deleted_at = datetime.now(timezone.utc)
```
2. **E-mail átírás** (már létezik, de pontosítva):
```python
old_email = user.email
timestamp = datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')
user.email = f"deleted_{user.id}_{timestamp}_{old_email}"
```
3. **Person rekord érintetlenül hagyása** (CRITICAL - maradjon ahogy van, NE módosítsuk)
4. **Token invalidáció:** Töröljük az összes aktív Refresh Token-t
5. **Naplózás** bővítése `deleted_at`-tal
---
## 5. API Végpont: DELETE /api/v1/users/me
### 5.1 Új endpoint (`backend/app/api/v1/endpoints/users.py`)
```python
@router.delete("/me", status_code=200)
async def delete_my_account(
reason: Optional[str] = Body(None, description="Törlés oka (opcionális)"),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""
Saját fiók soft-delete.
- Anonimizálja az e-mail címet
- is_active = False, is_deleted = True, deleted_at = timestamp
- Person rekordot NEM bántja
- Érvényteleníti az összes refresh token-t
- Wallet és Gamification adatokat megőrzi (inaktív user miatt freeze)
"""
success = await AuthService.soft_delete_user(
db=db,
user_id=current_user.id,
reason=reason or "user_requested_self_delete",
actor_id=current_user.id
)
if not success:
raise HTTPException(
status_code=400,
detail="A felhasználó már törölve van."
)
return {"status": "ok", "message": "Fiók sikeresen törölve."}
```
---
## 6. Adatbázis Szinkronizáció
A módosítások után futtatni kell a sync_engine-t:
```bash
docker exec -it sf_api python -m app.scripts.sync_engine
```
---
## 7. Tesztterv
### 7.1 Asset validáció tesztelése
1. Hozz létre járművet **csak VIN-nel** → sikeres
2. Hozz létre járművet **csak rendszámmal** → sikeres
3. Hozz létre járművet **mindkettővel** → sikeres
4. Hozz létre járművet **egyik nélkül sem** → 422-es hiba
### 7.2 Soft Delete tesztelése
1. Hozz létre usert, authentikálj
2. DELETE `/api/v1/users/me` hívás
3. Ellenőrizd: `User.is_deleted = True`, `User.is_active = False`, `User.deleted_at` beállítva
4. Ellenőrizd: `Person` rekord adatai változatlanok, `Person.is_active` változatlan
5. Ellenőrizd: Refresh token törölve (régi session nem működik)