2026.06.04 frontend építés közben

This commit is contained in:
Roo
2026-06-04 07:26:22 +00:00
parent 7adf6cc3e3
commit 59a30ac428
3302 changed files with 24091 additions and 1771 deletions

258
frontend/src/stores/auth.ts Normal file
View File

@@ -0,0 +1,258 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import router from '../router'
import api from '../api/axios'
export interface UserProfile {
id: number
email: string
first_name: string | null
last_name: string | null
is_active: boolean
role: string
region_code: string
subscription_plan: string
scope_level: string
scope_id: string | null
ui_mode: string
active_organization_id: number | null
// person_id is set when KYC is complete (Person record exists)
person_id: number | null
}
export const useAuthStore = defineStore('auth', () => {
// ────────────────────────────── State ──────────────────────────────
const user = ref<UserProfile | null>(null)
const token = ref<string | null>(localStorage.getItem('access_token'))
const isLoading = ref(false)
const error = ref<string | null>(null)
// ────────────────────────────── Getters ────────────────────────────
const isAuthenticated = computed(() => !!token.value && !!user.value)
const isKycComplete = computed(() => {
// KYC is complete when the user has a linked Person record (person_id is set)
return !!user.value?.person_id
})
const userRole = computed(() => user.value?.role ?? 'guest')
const userName = computed(() => {
if (!user.value) return ''
const { first_name, last_name } = user.value
if (first_name && last_name) return `${first_name} ${last_name}`
return first_name || last_name || user.value.email
})
const userId = computed(() => user.value?.id ?? null)
// ────────────────────────────── Actions ────────────────────────────
/**
* Login with email + password.
* The FastAPI /auth/login endpoint uses OAuth2PasswordRequestForm,
* which expects application/x-www-form-urlencoded with `username` and `password`.
*/
async function login(email: string, password: string) {
isLoading.value = true
error.value = null
try {
const body = new URLSearchParams()
body.append('username', email) // FastAPI OAuth2 form uses `username` key
body.append('password', password)
const res = await api.post('/auth/login', body, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})
const data = res.data as {
access_token: string
refresh_token?: string
token_type: string
is_active: boolean
}
// 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()
} catch (err: any) {
const message =
err.response?.data?.detail ||
err.response?.data?.message ||
'Bejelentkezés sikertelen. Ellenőrizd az adataidat.'
error.value = message
throw err
} finally {
isLoading.value = false
}
}
/**
* Fetch the current user profile from /auth/me.
*/
async function fetchUser() {
if (!token.value) return
try {
const res = await api.get('/auth/me')
user.value = res.data as UserProfile
} catch (err: any) {
// If the token is invalid, clear everything
if (err.response?.status === 401) {
logout()
}
throw err
}
}
/**
* Register a new user.
* The /auth/register endpoint expects JSON with UserLiteRegister schema.
* Default hidden values (region_code, lang, timezone) are appended here.
*/
async function register(data: {
email: string
password: string
first_name: string
last_name: string
}) {
isLoading.value = true
error.value = null
try {
const payload = {
...data,
region_code: 'HU',
lang: 'hu',
timezone: 'Europe/Budapest',
}
const res = await api.post('/auth/register', payload)
return res.data as {
status: string
message: string
user_id: number
email: string
}
} catch (err: any) {
const message =
err.response?.data?.detail ||
err.response?.data?.message ||
'Regisztráció sikertelen.'
error.value = message
throw err
} finally {
isLoading.value = false
}
}
/**
* Request a password reset email.
*/
async function forgotPassword(email: string) {
isLoading.value = true
error.value = null
try {
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
throw err
} finally {
isLoading.value = false
}
}
/**
* Logout — clear token, user state, and localStorage.
*/
function logout() {
token.value = null
user.value = null
error.value = null
localStorage.removeItem('access_token')
localStorage.removeItem('refresh_token')
// Redirect to the landing page
router.push('/')
}
/**
* Initialize the store on app load — if a token exists, fetch the user.
*/
async function init() {
if (token.value) {
try {
await fetchUser()
} catch {
// Token invalid — already cleared by fetchUser → logout
}
}
}
/**
* Complete KYC (Know Your Customer) — full profile submission.
* POSTs the KYC data to /auth/complete-kyc, then refreshes the user profile.
* Returns true on success, throws on failure.
*/
async function completeKyc(kycData: Record<string, any>): Promise<boolean> {
isLoading.value = true
error.value = null
try {
await api.post('/auth/complete-kyc', kycData)
// Refresh the user profile so isKycComplete getter updates
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
throw err
} finally {
isLoading.value = false
}
}
/**
* Clear any stored error message.
*/
function clearError() {
error.value = null
}
return {
// State
user,
token,
isLoading,
error,
// Getters
isAuthenticated,
isKycComplete,
userRole,
userName,
userId,
// Actions
login,
fetchUser,
register,
forgotPassword,
completeKyc,
logout,
init,
clearError,
}
})