2026.06.05 frontend javítgatás és új belépési logika megvalósítva

This commit is contained in:
Roo
2026-06-05 05:46:04 +00:00
parent 59a30ac428
commit 18524a08f2
38 changed files with 3967 additions and 1028 deletions

View File

@@ -3,6 +3,34 @@ import { ref, computed } from 'vue'
import router from '../router'
import api from '../api/axios'
export interface AddressData {
zip: string | null
city: string | null
street_name: string | null
street_type: string | null
house_number: string | null
stairwell: string | null
floor: string | null
door: string | null
parcel_id: string | null
full_address_text: string | null
}
export interface PersonData {
id: number
first_name: string | null
last_name: string | null
phone: string | null
mothers_last_name: string | null
mothers_first_name: string | null
birth_place: string | null
birth_date: string | null
identity_docs: Record<string, any> | null
ice_contact: Record<string, any> | null
is_active: boolean
address: AddressData | null
}
export interface UserProfile {
id: number
email: string
@@ -16,8 +44,11 @@ export interface UserProfile {
scope_id: string | null
ui_mode: string
active_organization_id: number | null
preferred_language: string
// person_id is set when KYC is complete (Person record exists)
person_id: number | null
// Nested person data with address
person: PersonData | null
}
export const useAuthStore = defineStore('auth', () => {
@@ -42,14 +73,64 @@ export const useAuthStore = defineStore('auth', () => {
})
const userId = computed(() => user.value?.id ?? null)
// ────────────────────────────── Helpers ────────────────────────────
/**
* Extract the raw error code from the backend response.
* Backend returns codes like "[AUTH.INVALID_CREDENTIALS]" or plain strings.
* Strips brackets and returns the clean code, or null if not a code.
*/
function extractErrorCode(err: any): string | null {
const detail = err.response?.data?.detail || err.response?.data?.message || ''
if (!detail) return null
// Strip surrounding brackets: [AUTH.INVALID_CREDENTIALS] -> AUTH.INVALID_CREDENTIALS
const match = detail.match(/^\[?([A-Z]+\.[A-Z_]+)\]?$/)
return match ? match[1] : null
}
/**
* Map a backend error code to a user-friendly i18n key path.
* Returns the i18n key (e.g. 'auth.invalid_credentials') or null if unmapped.
*/
function mapErrorCodeToI18nKey(code: string): string | null {
const map: Record<string, string> = {
'AUTH.INVALID_CREDENTIALS': 'auth.invalid_credentials',
'AUTH.USER_INACTIVE': 'auth.user_inactive',
'AUTH.USER_NOT_FOUND': 'auth.invalid_credentials',
'AUTH.EMAIL_EXISTS': 'auth.email_exists',
'AUTH.INVALID_TOKEN': 'auth.invalid_token',
'AUTH.TOKEN_EXPIRED': 'auth.token_expired',
'AUTH.RATE_LIMIT': 'auth.rate_limit',
}
return map[code] || null
}
/**
* Get a user-friendly error message from an API error response.
* Returns an i18n key path (e.g. 'auth.invalid_credentials') if the error
* matches a known code, or the raw detail string as fallback.
*/
function getErrorMessage(err: any, fallback: string): string {
const code = extractErrorCode(err)
if (code) {
const i18nKey = mapErrorCodeToI18nKey(code)
if (i18nKey) return i18nKey
}
// Fallback to raw detail or message
return err.response?.data?.detail ||
err.response?.data?.message ||
fallback
}
// ────────────────────────────── Actions ────────────────────────────
/**
* Login with email + password.
* The FastAPI /auth/login endpoint uses OAuth2PasswordRequestForm,
* which expects application/x-www-form-urlencoded with `username` and `password`.
* Supports optional device_fingerprint for Device-Hub tracking.
*/
async function login(email: string, password: string) {
async function login(email: string, password: string, deviceFingerprint?: string) {
isLoading.value = true
error.value = null
@@ -57,6 +138,9 @@ export const useAuthStore = defineStore('auth', () => {
const body = new URLSearchParams()
body.append('username', email) // FastAPI OAuth2 form uses `username` key
body.append('password', password)
if (deviceFingerprint) {
body.append('device_fingerprint', deviceFingerprint)
}
const res = await api.post('/auth/login', body, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
@@ -78,12 +162,16 @@ export const useAuthStore = defineStore('auth', () => {
// Immediately fetch the user profile
await fetchUser()
// Clear pending verification email after successful login
localStorage.removeItem('pending_verification_email')
// If KYC is not complete, redirect to KYC page
if (!isKycComplete.value) {
router.push('/complete-kyc')
}
} catch (err: any) {
const message =
err.response?.data?.detail ||
err.response?.data?.message ||
'Bejelentkezés sikertelen. Ellenőrizd az adataidat.'
error.value = message
error.value = getErrorMessage(err, 'auth.loginFailed')
throw err
} finally {
isLoading.value = false
@@ -132,6 +220,9 @@ export const useAuthStore = defineStore('auth', () => {
const res = await api.post('/auth/register', payload)
// Save email to localStorage for verification page auto-fill
localStorage.setItem('pending_verification_email', data.email)
return res.data as {
status: string
message: string
@@ -139,11 +230,26 @@ export const useAuthStore = defineStore('auth', () => {
email: string
}
} catch (err: any) {
const message =
err.response?.data?.detail ||
err.response?.data?.message ||
'Regisztráció sikertelen.'
error.value = message
error.value = getErrorMessage(err, 'auth.registerFailed')
throw err
} finally {
isLoading.value = false
}
}
/**
* Resend the verification email.
* POST /auth/resend-verification with { email }.
*/
async function resendVerification(email: string) {
isLoading.value = true
error.value = null
try {
const res = await api.post('/auth/resend-verification', { email })
return res.data as { status: string; message: string }
} catch (err: any) {
error.value = getErrorMessage(err, 'auth.resendFailed')
throw err
} finally {
isLoading.value = false
@@ -161,11 +267,35 @@ export const useAuthStore = defineStore('auth', () => {
const res = await api.post('/auth/forgot-password', { email })
return res.data as { status: string; message: string }
} catch (err: any) {
const message =
err.response?.data?.detail ||
err.response?.data?.message ||
'Hiba történt a jelszó-visszaállítási kérelem elküldésekor.'
error.value = message
const code = extractErrorCode(err)
if (code === 'AUTH.USER_INACTIVE') {
error.value = 'auth.user_inactive'
} else {
error.value = getErrorMessage(err, 'auth.forgotFailed')
}
throw err
} finally {
isLoading.value = false
}
}
/**
* Reset password using token from email.
* POST /auth/reset-password with { email, token, new_password }.
*/
async function resetPassword(email: string, token: string, newPassword: string) {
isLoading.value = true
error.value = null
try {
const res = await api.post('/auth/reset-password', {
email,
token,
new_password: newPassword,
})
return res.data as { status: string; message: string }
} catch (err: any) {
error.value = getErrorMessage(err, 'auth.resetPasswordFailed')
throw err
} finally {
isLoading.value = false
@@ -198,6 +328,56 @@ export const useAuthStore = defineStore('auth', () => {
}
}
/**
* Verify email account using the token from the activation link (Magic Link).
* POST /auth/verify-email with { token, device_fingerprint }.
* Now returns JWT tokens on success, automatically logging the user in.
* Supports optional device_fingerprint for Device-Hub tracking.
*/
async function verifyAccount(verificationToken: string, deviceFingerprint?: string) {
isLoading.value = true
error.value = null
try {
const payload: Record<string, any> = { token: verificationToken }
if (deviceFingerprint) {
payload.device_fingerprint = deviceFingerprint
}
const res = await api.post('/auth/verify-email', payload)
const data = res.data as {
access_token?: string
refresh_token?: string
token_type?: string
is_active?: boolean
status?: string
message?: string
}
// Magic Link: Ha a backend JWT tokent küld vissza, automatikus bejelentkezés
if (data.access_token) {
// Persist token
localStorage.setItem('access_token', data.access_token)
if (data.refresh_token) {
localStorage.setItem('refresh_token', data.refresh_token)
}
token.value = data.access_token
// Immediately fetch the user profile
await fetchUser()
// Clear pending verification email after successful verification + login
localStorage.removeItem('pending_verification_email')
}
return data
} catch (err: any) {
error.value = getErrorMessage(err, 'auth.verificationFailed')
throw err
} finally {
isLoading.value = false
}
}
/**
* Complete KYC (Know Your Customer) — full profile submission.
* POSTs the KYC data to /auth/complete-kyc, then refreshes the user profile.
@@ -213,11 +393,52 @@ export const useAuthStore = defineStore('auth', () => {
await fetchUser()
return true
} catch (err: any) {
const message =
err.response?.data?.detail ||
err.response?.data?.message ||
'A KYC kitöltése sikertelen. Kérlek ellenőrizd az adatokat.'
error.value = message
error.value = getErrorMessage(err, 'auth.kycFailed')
throw err
} finally {
isLoading.value = false
}
}
/**
* Update the user's Person record (profile data + address).
* PUT /users/me/person with PersonUpdate schema.
* On success, refreshes the local user state with the response.
*/
async function updatePerson(personData: Record<string, any>): Promise<boolean> {
isLoading.value = true
error.value = null
try {
const res = await api.put('/users/me/person', personData)
// Update the local user state with the fresh response
user.value = res.data as UserProfile
return true
} catch (err: any) {
error.value = getErrorMessage(err, 'auth.kycFailed')
throw err
} finally {
isLoading.value = false
}
}
/**
* Change the current user's password.
* PUT /users/me/password with { current_password, new_password }.
* On success, returns { status: 'ok', message: string }.
*/
async function changePassword(currentPassword: string, newPassword: string): Promise<{ status: string; message: string }> {
isLoading.value = true
error.value = null
try {
const res = await api.put('/users/me/password', {
current_password: currentPassword,
new_password: newPassword,
})
return res.data as { status: string; message: string }
} catch (err: any) {
error.value = getErrorMessage(err, 'A jelszó módosítása sikertelen.')
throw err
} finally {
isLoading.value = false
@@ -249,8 +470,13 @@ export const useAuthStore = defineStore('auth', () => {
login,
fetchUser,
register,
resendVerification,
verifyAccount,
forgotPassword,
resetPassword,
completeKyc,
updatePerson,
changePassword,
logout,
init,
clearError,