admin felület különválasztva
This commit is contained in:
179
frontend_admin/stores/auth.ts
Normal file
179
frontend_admin/stores/auth.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
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
|
||||
subscription_expires_at?: string | null
|
||||
max_vehicles?: number
|
||||
max_garages?: number
|
||||
scope_level: string
|
||||
scope_id: string | null
|
||||
ui_mode: string
|
||||
active_organization_id: number | null
|
||||
preferred_language: string
|
||||
person_id: number | null
|
||||
person: Record<string, any> | null
|
||||
system_capabilities?: Record<string, boolean>
|
||||
org_capabilities?: Record<string, Record<string, boolean>>
|
||||
}
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// ── State ──────────────────────────────────────────────────────────
|
||||
const user = ref<UserProfile | null>(null)
|
||||
const token = ref<string | null>(null)
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const isInitialized = ref(false)
|
||||
|
||||
// ── Getters ────────────────────────────────────────────────────────
|
||||
const isAuthenticated = computed(() => !!token.value && !!user.value)
|
||||
|
||||
const isAdmin = computed(() => {
|
||||
const role = user.value?.role
|
||||
if (!role) return false
|
||||
const staffRoles = ['SUPERADMIN', 'ADMIN', 'MODERATOR', 'SALES_REP', 'SERVICE_MGR']
|
||||
return staffRoles.includes(role.toUpperCase())
|
||||
})
|
||||
|
||||
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 userEmail = computed(() => user.value?.email ?? '')
|
||||
const userRole = computed(() => user.value?.role ?? 'guest')
|
||||
|
||||
// ── Actions ────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Login with email + password.
|
||||
* Calls the FastAPI /auth/login endpoint using OAuth2PasswordRequestForm.
|
||||
* Stores the JWT token in a cookie (via useCookie) for SSR compatibility.
|
||||
*/
|
||||
async function login(email: string, password: string) {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const body = new URLSearchParams()
|
||||
body.append('username', email)
|
||||
body.append('password', password)
|
||||
|
||||
const res = await $fetch<{
|
||||
access_token: string
|
||||
refresh_token?: string
|
||||
token_type: string
|
||||
is_active: boolean
|
||||
}>('/api/v1/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body,
|
||||
})
|
||||
|
||||
// Persist token in a cookie (SSR-safe) and in memory
|
||||
const tokenCookie = useCookie('access_token', {
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
path: '/',
|
||||
sameSite: 'lax',
|
||||
secure: true,
|
||||
})
|
||||
tokenCookie.value = res.access_token
|
||||
token.value = res.access_token
|
||||
|
||||
// Fetch user profile
|
||||
await fetchUser()
|
||||
} catch (err: any) {
|
||||
error.value = err?.data?.detail || err?.message || 'Login failed'
|
||||
throw err
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the current user profile from /auth/me.
|
||||
*/
|
||||
async function fetchUser() {
|
||||
const tokenCookie = useCookie('access_token')
|
||||
const currentToken = tokenCookie.value || token.value
|
||||
if (!currentToken) return
|
||||
|
||||
try {
|
||||
const res = await $fetch<UserProfile>('/api/v1/auth/me', {
|
||||
headers: { Authorization: `Bearer ${currentToken}` },
|
||||
})
|
||||
user.value = res
|
||||
} catch (err: any) {
|
||||
if (err?.response?.status === 401) {
|
||||
logout()
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout — clear token, user state, and cookie.
|
||||
*/
|
||||
function logout() {
|
||||
token.value = null
|
||||
user.value = null
|
||||
error.value = null
|
||||
const tokenCookie = useCookie('access_token')
|
||||
tokenCookie.value = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the store on app load — if a token cookie exists, fetch the user.
|
||||
*/
|
||||
async function init() {
|
||||
const tokenCookie = useCookie('access_token')
|
||||
if (tokenCookie.value) {
|
||||
token.value = tokenCookie.value
|
||||
try {
|
||||
await fetchUser()
|
||||
} catch {
|
||||
logout()
|
||||
}
|
||||
}
|
||||
isInitialized.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any stored error message.
|
||||
*/
|
||||
function clearError() {
|
||||
error.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
user,
|
||||
token,
|
||||
isLoading,
|
||||
error,
|
||||
isInitialized,
|
||||
|
||||
// Getters
|
||||
isAuthenticated,
|
||||
isAdmin,
|
||||
userName,
|
||||
userEmail,
|
||||
userRole,
|
||||
|
||||
// Actions
|
||||
login,
|
||||
fetchUser,
|
||||
logout,
|
||||
init,
|
||||
clearError,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user