admin frontend elkezdése, járművek tisztázása, frontend fejleszts
This commit is contained in:
179
plans/logic_spec_identity_preserving_soft_delete.md
Normal file
179
plans/logic_spec_identity_preserving_soft_delete.md
Normal 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)
|
||||
Reference in New Issue
Block a user