# Service Finder Fejlesztési Történet ## 2026-06-04 (B) - Magic Link Implementáció (Auto-Login Email Verification) ### 🎯 Cél Az email verifikációs folyamat átalakítása "Magic Link" élménnyé. A felhasználónak NEM kell manuálisan bejelentkeznie az aktiválás után - a verifikációs link automatikusan belépteti! ### ✅ Implementáció #### 1. Backend: JWT Token Generálás Verifikációnál **Fájl**: [`backend/app/api/v1/endpoints/auth.py`](backend/app/api/v1/endpoints/auth.py:93) - A `/verify-email` végpont most `response_model=Token`-t ad vissza - Sikeres verifikáció után a backend meghívja az [`AuthService.verify_email()`](backend/app/services/auth_service.py:316) metódust - Az `AuthService.verify_email()` mostantól User objektumot ad vissza (nem csak boolean-t) - A végpont generál JWT access és refresh tokent ugyanúgy, mint a `/login` endpoint - A tokenek HTTP-only cookie-ban és JSON response-ban is megjelennek **Változtatások**: ```python # AuthService.verify_email() módosítás - return True # RÉGI + return user # ÚJ: visszaadja az aktivált User objektumot # auth.py verify_email végpont + user = await AuthService.verify_email(db, request.token) + if not user: + raise HTTPException(...) + + # JWT token generálás (ugyanaz a logika, mint login-nál) + token_data = {"sub": str(user.id), "role": ..., "rank": ..., ...} + access, refresh = create_tokens(data=token_data, remember_me=False) + return {"access_token": access, "refresh_token": refresh, ...} ``` #### 2. Frontend Store: Automatikus Bejelentkezés **Fájl**: [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:302) - A `verifyAccount()` action most feldolgozza a backend által küldött JWT tokent - Ha `data.access_token` létezik, akkor: - Elmenti a tokent a `localStorage`-ba - Beállítja a `token.value` state-et - Meghívja a `fetchUser()` metódust (betölti a user profilt) - Törli a `pending_verification_email` értéket **Változtatások**: ```typescript // Új logika a verifyAccount()-ban if (data.access_token) { localStorage.setItem('access_token', data.access_token) if (data.refresh_token) { localStorage.setItem('refresh_token', data.refresh_token) } token.value = data.access_token await fetchUser() localStorage.removeItem('pending_verification_email') } ``` #### 3. Frontend View: Automatikus Átirányítás KYC-re **Fájl**: [`frontend/src/views/VerifyEmailView.vue`](frontend/src/views/VerifyEmailView.vue:28) - **Új UI**: "Sikeres aktiválás és bejelentkezés!" üzenet - **3 másodperces countdown**: "Átirányítás 3 másodperc múlva..." - **Automatikus redirect**: `setInterval()` számolja vissza, majd átirányít `/complete-kyc`-re - **CTA gomb**: "Tovább a Profilhoz" (nem több "Tovább a Belépéshez") - **Cleanup**: `onUnmounted()` törli az interval-t **Változtatások**: ```vue
{{ autoRedirectCountdown > 0 ? `Átirányítás ${autoRedirectCountdown} másodperc múlva...` : 'Átirányítás...' }}
``` ### 🔄 Új Flow (Magic Link) **Régi folyamat**: 1. Regisztráció → Email verify link 2. Verify oldalon aktiválás → "Tovább a Belépéshez" 3. Felhasználó manuálisan bejelentkezik 4. Login után átirányítás KYC-re **Új folyamat (Magic Link)**: 1. Regisztráció → Email verify link 2. Verify oldalon aktiválás → **Backend JWT tokent ad** 3. **Frontend automatikusan bejelentkezteti** a felhasználót 4. **3 másodperc múlva automatikus átirányítás** `/complete-kyc`-re 5. Felhasználó már be van jelentkezve → KYC mentés sikeres! ### 📊 Érintett Fájlok - [`backend/app/api/v1/endpoints/auth.py`](backend/app/api/v1/endpoints/auth.py:93) - verify-email végpont JWT response - [`backend/app/services/auth_service.py`](backend/app/services/auth_service.py:316) - verify_email User visszadás - [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:302) - verifyAccount JWT feldolgozás - [`frontend/src/views/VerifyEmailView.vue`](frontend/src/views/VerifyEmailView.vue:28) - Auto-login UI + countdown ### 🧪 Tesztelési Eredmény 1. ✅ Backend Python szintaxis helyes (`py_compile` sikeres) 2. ✅ Auth service módosítás sikeres (User objektum visszaadás) 3. ✅ Backend újraindítás sikeres, nincsenek hibák a logokban 4. ✅ Frontend TypeScript típusok konzisztensek 5. ✅ Auto-redirect logika implementálva (3 mp countdown) ### 🔗 Gitea - **Feature**: Magic Link Auto-Login on Email Verification - **Típus**: UX Enhancement - **Státusz**: Completed --- ## 2026-06-04 (A) - Onboarding UX & KYC Authentication Flow Javítások ### 🎯 Probléma A felhasználói visszajelzések alapján a regisztrációs és KYC folyamat kritikus UX hibákat tartalmazott: 1. **'Not authenticated' hiba**: Az aktiváló linkre kattintás után a KYC mentés elszállt, mert a felhasználó nem volt bejelentkezve 2. **Email újra begépelése**: A verify oldalon újra be kellett írni az email címet 3. **Tab navigáció**: A Házszám mező után nem a Tovább gombra ugrott a Tab billentyű 4. **Kötelező okmányok**: A rendszer azonnal kérte a személyi igazolványt, ami rossz UX ### ✅ Megoldás #### 1. Backend Módosítások **Fájl**: [`backend/app/schemas/auth.py`](backend/app/schemas/auth.py) - A `DocumentDetail` osztály mezői (`number`, `expiry_date`) most `Optional[str]` és `Optional[date]` - A `UserKYCComplete.identity_docs` mező most `Optional[Dict[str, DocumentDetail]]` - Ez lehetővé teszi a "Soft KYC"-t, ahol a felhasználó bejut a rendszerbe okmányok nélkül is #### 2. Frontend VerifyEmailView Módosítások **Fájl**: [`frontend/src/views/VerifyEmailView.vue`](frontend/src/views/VerifyEmailView.vue:1) - **Sikeres aktiválás után**: A CTA már nem "Tovább a profil kitöltéséhez", hanem "Tovább a Belépéshez" - **Átirányítás**: A `goToKyc()` helyett `goToLogin()` amely a főoldalra irányít - **Email auto-fill**: A `localStorage.getItem('pending_verification_email')` használata a korábbi `saved-email` helyett #### 3. Auth Store Logika **Fájl**: [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:1) - **Regisztráció**: Az email mentésre kerül `localStorage.setItem('pending_verification_email', data.email)` - **Login után KYC ellenőrzés**: Ha `!isKycComplete.value`, automatikus átirányítás a `/complete-kyc` oldalra - **Cleanup**: Sikeres belépés után törlődik a `pending_verification_email` a localStorage-ból #### 4. CompleteKycView Módosítások **Fájl**: [`frontend/src/views/CompleteKycView.vue`](frontend/src/views/CompleteKycView.vue:1) **Tab Order Javítás**: - A Házszám input kapott egy `@keydown.tab.exact="handleHouseNumberTab"` event handlert - A `handleHouseNumberTab` függvény `event.preventDefault()` és `nextTick(() => nextButton.value?.focus())` - A Tovább gomb kapott egy `ref="nextButton"` attribútumot **Opcionális Okmányok**: - A 3. lépés címe: "Okmányok (opcionális)" - A személyi igazolvány vizuálisan is opcionálisként jelenik meg (fehér pont helyett átlátszó, szöveg halvány) - **Validáció eltávolítva**: A `validateStep()` függvény már nem ellenőrzi az ID_CARD mezőket - **Payload logika**: Az `identity_docs` objektum csak akkor kerül a payload-ba, ha legalább egy dokumentum ki van töltve #### 5. Teljes Folyamat Átalakítás **Régi folyamat**: 1. Regisztráció → Email confirm → KYC (nem bejelentkezve) → **HIBA** **Új folyamat**: 1. Regisztráció → Email mentés localStorage-ba 2. Email confirm → "Tovább a Belépéshez" CTA 3. Belépés → Auth store ellenőrzi KYC státuszt 4. Ha KYC hiányzik → Átirányítás `/complete-kyc` (már bejelentkezve!) 5. KYC opcionális okmányokkal → Sikeres mentés ### 📊 Érintett Fájlok - [`backend/app/schemas/auth.py`](backend/app/schemas/auth.py) - Soft KYC support - [`frontend/src/views/VerifyEmailView.vue`](frontend/src/views/VerifyEmailView.vue:28) - Beléptetés flow - [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:102) - KYC redirect logika - [`frontend/src/views/CompleteKycView.vue`](frontend/src/views/CompleteKycView.vue:173) - Tab order + opcionális validáció ### 🧪 Tesztelési Szempontok 1. ✅ Új user regisztráció email mentése 2. ✅ Verify oldalon email auto-fill 3. ✅ Sikeres aktiválás után "Tovább a Belépéshez" gomb 4. ✅ Belépés után KYC ellenőrzés és átirányítás 5. ✅ Házszám → Tab → Tovább gomb fokusz 6. ✅ KYC mentés okmányok nélkül 7. ✅ Backend fogadja az üres `identity_docs`-ot ### 🔗 Gitea - **Issue**: #200 - Fix: Onboarding UX & KYC Authentication Flow - **Státusz**: In Progress --- ## 2026-06-04 (C) - Soft KYC, Trust Profile Extension & Device-Hub Architecture ### 🎯 Cél Teljes stack implementáció a bizalmi infrastruktúra kiterjesztésére: Device Fingerprinting (Device-Hub architektúra), UserTrustProfile bővítés (identity_score, verification_level, verified_channels, identity_risk_flag), Person rekordok merge-elhetősége (merged_into_id), Soft KYC (opcionális címadatok a regisztrációnál), és eszközazonosítás a login folyamatban. ### ✅ Implementáció #### 1. Backend Modellek Bővítése **Fájl**: [`backend/app/models/identity/identity.py`](backend/app/models/identity/identity.py:73) **Person.merged_into_id** (73. sor): - `merged_into_id: Mapped[Optional[int]]` - BigInteger, FK `identity.persons.id`-re, `SET NULL` on delete - Lehetővé teszi Person rekordok összeolvasztását (duplikáció kezelés) **UserTrustProfile bővítés** (263. sor): - `identity_score: Mapped[Optional[float]]` - 0.0 - 1.0 közötti pontszám - `verification_level: Mapped[Optional[str]]` - Szint: `none`, `basic`, `advanced`, `verified` - `verified_channels: Mapped[Optional[dict]]` - JSONB típus, pl. `{"email": true, "phone": false, "id_card": true}` - `identity_risk_flag: Mapped[Optional[bool]]` - Red flag jelzés **Device modell** (294. sor): - `fingerprint_hash: Mapped[str]` - Primary Key, az eszköz ujjlenyomatának hash-e - `risk_score: Mapped[Optional[float]]` - Eszköz kockázati pontszáma - `is_banned: Mapped[bool]` - Kitiltott eszköz flag - `created_at: Mapped[datetime]` - Létrehozás időpontja **UserDeviceLink modell** (309. sor): - `id: Mapped[uuid.UUID]` - UUID Primary Key - `user_id: Mapped[int]` - FK `identity.users.id`-re - `device_hash: Mapped[str]` - FK `identity.devices.fingerprint_hash`-re - `first_seen_at: Mapped[datetime]` - Első látogatás - `last_seen_at: Mapped[Optional[datetime]]` - Utolsó látogatás - `login_count: Mapped[int]` - Bejelentkezések száma (default: 1) #### 2. Adatbázis Szinkronizáció **Parancs**: `docker exec sf_api python -m app.scripts.sync_engine` - **Eredmény**: 7 séma változás sikeresen alkalmazva (1000 OK, 7 Fixed, 0 Shadow Data) - Létrejött: `identity.devices` tábla, `identity.user_device_links` tábla - Bővült: `identity.persons` (merged_into_id), `identity.user_trust_profiles` (4 új oszlop) #### 3. Pydantic Schema Relaxáció (Soft KYC) **Fájl**: [`backend/app/schemas/auth.py`](backend/app/schemas/auth.py:28) - `address_street_name` → `Optional[str] = None` - `address_street_type` → `Optional[str] = None` - `address_house_number` → `Optional[str] = None` - Csak `address_zip` és `address_city` maradt kötelező #### 4. Login Endpoint Device Fingerprint Tracking **Fájl**: [`backend/app/api/v1/endpoints/auth.py`](backend/app/api/v1/endpoints/auth.py:33) - Új paraméter: `device_fingerprint: Optional[str] = Form(None)` - Sikeres autentikáció után: 1. Ellenőrzi, hogy a `Device` létezik-e a hash alapján → ha nem, létrehozza 2. Ellenőrzi a `UserDeviceLink`-et user_id + device_hash alapján 3. Ha létezik: frissíti `last_seen_at` és növeli `login_count` 4. Ha nem létezik: új rekordot hoz létre `first_seen_at` = now, `login_count` = 1 5. Commit #### 5. Frontend FingerprintJS Integráció **Fájl**: [`frontend/src/components/LoginModal.vue`](frontend/src/components/LoginModal.vue:622) - `import FingerprintJS from '@fingerprintjs/fingerprintjs'` - `getDeviceFingerprint()`: Lazy singleton pattern - egyszer inicializálja a FingerprintJS-t, majd gyorsítótárazza a visitorId-t - `handleLogin()`: Meghívja `getDeviceFingerprint()`-et, átadja az eredményt `authStore.login()`-nak **Fájl**: [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:103) - `login()` függvény kibővítve: `deviceFingerprint?: string` paraméter - Ha megadva, `device_fingerprint` mezőként hozzáfűzi a FormData-hoz #### 6. Frontend CompleteKycView Soft KYC **Fájl**: [`frontend/src/views/CompleteKycView.vue`](frontend/src/views/CompleteKycView.vue:1) - Lépés leírás frissítve: "Add meg a lakcímed (csak irányítószám és város kötelező)" - `address_street_name`, `address_street_type`, `address_house_number` validáció eltávolítva - "(opcionális)" címkék hozzáadva az opcionális mezőkhöz - Payload builder: opcionális címmezők csak akkor kerülnek bele, ha ki vannak töltve ### 📊 Érintett Fájlok - [`backend/app/models/identity/identity.py`](backend/app/models/identity/identity.py:73) - Person.merged_into_id, UserTrustProfile bővítés, Device, UserDeviceLink - [`backend/app/schemas/auth.py`](backend/app/schemas/auth.py:28) - UserKYCComplete opcionális címmezők - [`backend/app/api/v1/endpoints/auth.py`](backend/app/api/v1/endpoints/auth.py:33) - Login device_fingerprint tracking - [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:103) - login() deviceFingerprint paraméter - [`frontend/src/components/LoginModal.vue`](frontend/src/components/LoginModal.vue:622) - FingerprintJS integráció - [`frontend/src/views/CompleteKycView.vue`](frontend/src/views/CompleteKycView.vue:1) - Soft KYC UI - [`frontend/package.json`](frontend/package.json) - @fingerprintjs/fingerprintjs dependency ### 🧪 Tesztelési Szempontok 1. ✅ Backend modellek szintaxis helyes (py_compile) 2. ✅ Sync engine 7 séma változást sikeresen alkalmazott 3. ✅ Pydantic schema opcionális mezők (Soft KYC) 4. ✅ Login endpoint device fingerprint fogadása és tárolása 5. ✅ Frontend FingerprintJS betöltés és visitorId küldés 6. ✅ CompleteKycView opcionális címmezők validáció nélkül 7. ✅ Device-Hub architektúra: Device + UserDeviceLink modellek ### 🔗 Gitea - **Issue**: #202 - Soft KYC, Trust Profile Extension & Device-Hub Architecture - **Státusz**: Completed ## 2026-06-04 (C) - Soft KYC Address Constraints Fix ### 🎯 Cél A `system.addresses` tábla `street_name`, `street_type` és `house_number` mezőinek opcionálissá tétele az adatbázis szintjén is a Soft KYC bevezetéséhez. ### ✅ Implementáció #### 1. SQLAlchemy Modell Frissítés **Fájl**: [`backend/app/models/identity/address.py`](backend/app/models/identity/address.py:47) - `street_name`: `Mapped[str]` → `Mapped[Optional[str]]`, `nullable=False` → `nullable=True` - `street_type`: `Mapped[str]` → `Mapped[Optional[str]]`, `nullable=False` → `nullable=True` - `house_number`: `Mapped[str]` → `Mapped[Optional[str]]`, `nullable=False` → `nullable=True` #### 2. Adatbázis ALTER TABLE - `ALTER TABLE system.addresses ALTER COLUMN street_name DROP NOT NULL;` - `ALTER TABLE system.addresses ALTER COLUMN street_type DROP NOT NULL;` - `ALTER TABLE system.addresses ALTER COLUMN house_number DROP NOT NULL;` #### 3. Verifikáció - `information_schema.columns` szerint mindhárom oszlop `is_nullable = YES` ### 🔗 Gitea - **Issue**: #203 - Fix: Soft KYC - Address mezők nullable=True - **Státusz**: Completed ## 2026-06-04 (D) - Fix KYC Data Binding & Magic Link Device Tracking ### 🎯 Cél Két adatfolyam-törés javítása: (1) KYC first_name/last_name nem frissült a Person rekordon, (2) Magic Link verify-email végpont nem támogatta a device_fingerprint-et. ### ✅ Implementáció #### 1. Backend KYC Schema **Fájl**: [`backend/app/schemas/auth.py`](backend/app/schemas/auth.py:28) - `first_name: Optional[str] = None` és `last_name: Optional[str] = None` hozzáadva a `UserKYCComplete` sémához #### 2. Backend KYC Service **Fájl**: [`backend/app/services/auth_service.py`](backend/app/services/auth_service.py:178) - `complete_kyc()` metódusban mindkét ágon (Shadow Identity IF és ELSE) hozzáadva a név frissítés: - `if kyc_in.first_name: p.first_name = kyc_in.first_name` - `if kyc_in.last_name: p.last_name = kyc_in.last_name` #### 3. Frontend KYC Payload **Fájl**: [`frontend/src/views/CompleteKycView.vue`](frontend/src/views/CompleteKycView.vue:459) - `handleSubmit()` payload-ba bekerült: `first_name: kycForm.first_name` és `last_name: kycForm.last_name` #### 4. Backend Magic Link Device Tracking **Fájl**: [`backend/app/api/v1/endpoints/auth.py`](backend/app/api/v1/endpoints/auth.py:137) - `VerifyEmailRequest` sémához hozzáadva: `device_fingerprint: Optional[str] = None` - Sikeres `verify_email()` után teljes Device UPSERT blokk (Device + UserDeviceLink), megegyező logikával, mint a `/login` végpontban #### 5. Frontend Magic Link Tracking - **Fájl**: [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:306) - `verifyAccount()` most fogad `deviceFingerprint` paramétert és küldi a kérésben - **Fájl**: [`frontend/src/views/VerifyEmailView.vue`](frontend/src/views/VerifyEmailView.vue:168) - FingerprintJS integráció (`getDeviceFingerprint()`), a fingerprint hash átadása a `verifyAccount()` hívásnál ### 🔗 Gitea - **Issue**: #204 - Fix KYC Data Binding & Magic Link Device Tracking - **Státusz**: Completed --- ## 2026-06-04 (C) - Nested Profile Response & Editable Profile Card ### 🎯 Cél A GET /users/me végpont UserResponse Pydantic sémájának kibővítése beágyazott Person és Address adatokkal, valamint egy PUT /users/me/person végpont létrehozása a profil inline szerkesztéséhez a frontenden. ### ✅ Implementáció #### 1. Pydantic Schemas (backend/app/schemas/user.py) - **`AddressResponse`**: Beágyazott cím adatok (zip, city, street_name, house_number, stairwell, floor, door, latitude, longitude, stb.) `from_attributes=True` konfigurációval - **`PersonResponse`**: Személyes adatok (first_name, last_name, phone, mothers data, birth data, identity_docs, ice_contact) + `address: Optional[AddressResponse]` beágyazott címmel - **`PersonUpdate`**: PUT végponthoz - tartalmazza a személyes adatokat + `address_*` prefixű cím mezőket - **`UserResponse`**: Kibővítve `person: Optional[PersonResponse] = None` mezővel #### 2. Eager Loading (backend/app/api/deps.py) - `get_current_user()`: `joinedload(User.person).joinedload(Person.address)` láncolt betöltés - Ezzel a SQL egyetlen 4-táblás JOIN-ban tölti be: `users → persons → addresses → geo_postal_codes` #### 3. Person Model - address relationship (backend/app/models/identity/identity.py) - Hozzáadva: `address: Mapped[Optional["Address"]] = relationship("Address", foreign_keys="[Person.address_id]", lazy="joined")` - TYPE_CHECKING import: `from .address import Address` #### 4. PUT /users/me/person végpont (backend/app/api/v1/endpoints/users.py) - Elfogad `PersonUpdate` sémát - Frissíti a Person mezőit (first_name, last_name, phone, stb.) - Cím frissítés/létrehozás a `GeoService.get_or_create_full_address()` segítségével - Visszaadja a frissített `UserResponse`-t beágyazott person+address adatokkal - `_build_user_response()` helper: egységes UserResponse építés minden végponthoz #### 5. Wallet FK Fix (backend/app/models/identity/identity.py) - `Wallet.user_id` FK hozzáadva: `ondelete="CASCADE"` - ezzel a user törlésekor automatikusan törlődik a wallet rekord is #### 6. Frontend ProfileView.vue - Inline Editing - **Read mód**: Megjeleníti a `authStore.user.person.*` adatokat szövegesen - **Edit mód**: `isEditing` ref, `editForm` reactive objektum, input mezők a személyes adatokhoz és címhez - **Mentés**: `authStore.updatePerson(payload)` hívása, sikeres mentés után visszavált read módba - **Cím szekció**: Grid layout zip/city, street/type/number, stairwell/floor/door mezőkkel #### 7. Auth Store (frontend/src/stores/auth.ts) - `AddressData` interface: zip, city, street_name, house_number, stairwell, floor, door - `PersonData` interface: id, first_name, last_name, phone, address: AddressData | null - `UserProfile` kibővítve: `person: PersonData | null` - `updatePerson()` action: PUT /users/me/person hívás, sikeres esetén `user.person` frissítése #### 8. Identity __init__.py javítás - `Device` és `UserDeviceLink` modellek exportálása (hiányoztak, emiatt `ImportError`-t dobott az auth.py) #### 9. auth.py javítás - `from typing import Optional` hozzáadva (hiányzott, `NameError: name 'Optional' is not defined`) ### 🔗 Gitea - **Issue**: #205 - Nested Profile Response & Editable Profile Card - **Státusz**: Completed ## 2026-06-04 (C) - Profile UI Refactor + Identity Docs + Change Password ### 🎯 Cél Profil oldal vizuális felújítása dark card témával, okmányadatok (identity_docs) megjelenítése/szerkesztése, és jelszóváltoztatás implementálása. ### ✅ Implementáció #### 1. Backend: PersonUpdate séma bővítés **Fájl**: [`backend/app/schemas/user.py`](backend/app/schemas/user.py:68) - `identity_docs: Optional[Any] = None` hozzáadva a `PersonUpdate` osztályhoz - Új `ChangePasswordRequest` séma: `current_password` + `new_password` mezőkkel #### 2. Backend: PUT /me/person identity_docs merge logika **Fájl**: [`backend/app/api/v1/endpoints/users.py`](backend/app/api/v1/endpoints/users.py:219) - Ha `identity_docs` érkezik és nem null, merge-el a meglévő adatokkal (`dict.update()`) - Megőrzi a nem küldött mezőket (pl. csak LICENSE küldésekor ID_CARD megmarad) #### 3. Backend: PUT /me/password végpont **Fájl**: [`backend/app/api/v1/endpoints/users.py`](backend/app/api/v1/endpoints/users.py:260) - Jelenlegi jelszó ellenőrzés (`verify_password`) - Új jelszó ≠ régi jelszó validáció - Komplexitás validáció (`AuthService._validate_password_complexity`) - Bcrypt hash-elés és mentés (`get_password_hash`) #### 4. Frontend: ProfileView teljes átírás **Fájl**: [`frontend/src/views/ProfileView.vue`](frontend/src/views/ProfileView.vue:1) - Dark card UI: `bg-black/40 backdrop-blur-xl`, `text-white font-medium`, `#00E5A0` accent - Identity docs szekció: ID_CARD (number, expiry_date) + LICENSE (number, expiry_date, categories) read/edit módban - Change Password modal: Teleport, 3 mező, frontend validáció, API hívás, auto-close 2s után #### 5. Frontend: Auth store bővítés **Fájl**: [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:430) - `changePassword()` action: PUT /users/me/password hívás ### 🔗 Gitea - **Issue**: #206 - Profile UI Refactor + Identity Docs + Change Password - **Státusz**: Completed ## 2026-06-04 (C) - Fix SQLAlchemy JSON Mutation for identity_docs ### 🎯 Cél Az identity_docs JSON mező nem mentődött el a profil szerkesztésnél, mert az SQLAlchemy nem detektálta az in-place dict mutációt. ### ✅ Implementáció #### 1. Backend: flag_modified + copy.deepcopy **Fájl**: [`backend/app/api/v1/endpoints/users.py`](backend/app/api/v1/endpoints/users.py:222) - Importált: `copy` és `sqlalchemy.orm.attributes.flag_modified` - Az identity_docs frissítésnél `copy.deepcopy()`-val új dict példány készül - `flag_modified(person, "identity_docs")` hívás explicit jelzi SQLAlchemy-nek a JSON oszlop változását #### 2. Frontend: Success üzenet frissítés **Fájl**: [`frontend/src/views/ProfileView.vue`](frontend/src/views/ProfileView.vue:692) - Mentés siker üzenet: "A profil adatok és okmányok sikeresen frissítve!" ### 🔗 Gitea - **Issue**: #207 - Fix SQLAlchemy JSON mutation for identity_docs - **Státusz**: Completed --- ## 2026-06-04 (E) - Internal Zip Lookup Endpoint & Frontend Auto-Fill ### 🎯 Cél Külső zippopotam.us API kiváltása belső adatbázis lekérdezéssel. Backend zip-lookup végpont létrehozása a `system.geo_postal_codes` tábla alapján, és frontend auto-fill átállítás a belső API-ra. ### ✅ Implementáció #### 1. Backend: Zip-Lookup Endpoint **Fájl**: [`backend/app/api/v1/endpoints/system_parameters.py`](backend/app/api/v1/endpoints/system_parameters.py:47) - Új `GET /zip-lookup` végpont a system parameters routerben - Paraméterek: `country_code` (default: "HU") és `zip_code` (kötelező) - Lekérdezi a `system.geo_postal_codes` táblát `country_code` + `zip_code` alapján - Visszaadja: `{"city": "Budapest"}` (200) vagy 404 ha nem található - **Kritikus**: A `/zip-lookup` route a `/{key}` catch-all route ELŐTT van regisztrálva, hogy ne a catch-all nyelje el ```python @router.get("/zip-lookup", response_model=Dict[str, Any]) async def zip_lookup( country_code: str = Query("HU", description="Országkód (pl. HU, AT, DE)"), zip_code: str = Query(..., min_length=1, description="Irányítószám"), db: AsyncSession = Depends(get_db), ): if not zip_code.strip(): raise HTTPException(status_code=400, detail="zip_code is required") stmt = select(GeoPostalCode).where( and_( GeoPostalCode.country_code == country_code, GeoPostalCode.zip_code == zip_code.strip(), ) ).limit(1) result = await db.execute(stmt) postal_code = result.scalar_one_or_none() if not postal_code: raise HTTPException(status_code=404, detail=f"No city found...") return {"city": postal_code.city} ``` Teljes URL: `GET /api/v1/system/parameters/zip-lookup?country_code=HU&zip_code=1111` #### 2. Frontend: CompleteKycView.vue - Internal API **Fájl**: [`frontend/src/views/CompleteKycView.vue`](frontend/src/views/CompleteKycView.vue:1) - Külső `https://api.zippopotam.us` fetch kiváltva belső `/api/v1/system/zip-lookup` hívással - Response field változás: `data.places[0]['place name']` → `data.city` #### 3. Frontend: ProfileView.vue - Debounced Zip Lookup **Fájl**: [`frontend/src/views/ProfileView.vue`](frontend/src/views/ProfileView.vue:1) - `watch()` a `editForm.address_zip`-re, 600ms debounce delay - Csak akkor indít lekérdezést, ha a zip hossza ≥ 3 karakter - `isCityLoading` ref a betöltési állapot jelzésére - Axios API hívás: `api.get('/system/zip-lookup', { params: { country_code: 'HU', zip_code: newZip } })` - Sikeres válasz esetén `editForm.address_city` auto-fill #### 4. identity_docs Payload Verifikáció - **Frontend ProfileView.vue**: `buildIdentityDocsPayload()` (636-663. sor) helyesen építi az ID_CARD + LICENSE struktúrát - **savePerson()** (665-701. sor): helyesen hívja `buildIdentityDocsPayload()`-t és belefoglalja a payload-ba - **Backend PersonUpdate**: `identity_docs: Optional[Any] = None` - helyes, JSONB kompatibilis - **Backend PUT /me/person**: `flag_modified` + `copy.deepcopy` már implementálva (#207) - **Read mód display**: `identityDocs?.ID_CARD?.number` pattern helyes ### 📊 Érintett Fájlok - [`backend/app/api/v1/endpoints/system_parameters.py`](backend/app/api/v1/endpoints/system_parameters.py:47) - Új zip-lookup végpont - [`frontend/src/views/CompleteKycView.vue`](frontend/src/views/CompleteKycView.vue:1) - zippopotam.us → internal API - [`frontend/src/views/ProfileView.vue`](frontend/src/views/ProfileView.vue:1) - Debounced zip lookup watcher ### 🧪 Tesztelési Eredmény 1. ✅ Backend zip-lookup endpoint működik: `1111` → `{"city": "Budapest"}` (200) 2. ✅ Ismeretlen irányítószám: 404 hibakód megfelelő üzenettel 3. ✅ Frontend CompleteKycView belső API-t használ 4. ✅ Frontend ProfileView debounced zip lookup implementálva 5. ✅ identity_docs payload már helyesen működik (nem kellett javítás) ### 🔗 Gitea - **Issue**: #208 - Internal Zip Lookup & Identity Docs Payload Fix - **Státusz**: Completed