Phase 1: Wire existing backend endpoints to frontend
- Wire garageStore.addVehicle to POST /assets/vehicles (real API call) - Wire authStore.fetchUserProfile to GET /users/me (real API call) - Remove mock data fallback for vehicle creation - Add userProfile state to auth store These changes connect the frontend to verified existing FastAPI endpoints as identified in the backend endpoint audit (#132).
This commit is contained in:
274
frontend/src/stores/authStore.js
Normal file
274
frontend/src/stores/authStore.js
Normal file
@@ -0,0 +1,274 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
const router = useRouter()
|
||||
|
||||
// State
|
||||
const token = ref(localStorage.getItem('token') || '')
|
||||
const isAdmin = ref(localStorage.getItem('is_admin') === 'true')
|
||||
const userEmail = ref(localStorage.getItem('user_email') || '')
|
||||
const userRole = ref(localStorage.getItem('user_role') || '')
|
||||
const userProfile = ref(null) // Full user profile from /users/me
|
||||
|
||||
// Getters
|
||||
const isLoggedIn = computed(() => !!token.value)
|
||||
const isTester = computed(() => userEmail.value === 'tester_pro@profibot.hu' || userRole.value === 'tester')
|
||||
const displayName = computed(() => {
|
||||
if (isTester.value) return 'TESTER PRO'
|
||||
if (isAdmin.value) return 'ADMIN'
|
||||
return 'USER'
|
||||
})
|
||||
|
||||
// Actions
|
||||
const login = async (email, password) => {
|
||||
console.log('AuthStore: Real API login attempt for', email)
|
||||
|
||||
try {
|
||||
// Prepare URL-encoded form data for OAuth2 password grant
|
||||
// FastAPI's OAuth2PasswordRequestForm expects application/x-www-form-urlencoded
|
||||
const params = new URLSearchParams()
|
||||
params.append('username', email)
|
||||
params.append('password', password)
|
||||
|
||||
// Call real backend API
|
||||
const response = await fetch('http://localhost:8000/api/v1/auth/login', {
|
||||
method: 'POST',
|
||||
body: params,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
console.error('AuthStore: Login API error', response.status, errorText)
|
||||
throw new Error(`Login failed: ${response.status} ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
console.log('AuthStore: Login API response', data)
|
||||
|
||||
// Extract token and user info
|
||||
const accessToken = data.access_token
|
||||
const refreshToken = data.refresh_token
|
||||
const tokenType = data.token_type
|
||||
const isActive = data.is_active
|
||||
|
||||
// We need to decode the JWT token to get user role and info
|
||||
// For now, we'll make a separate API call to get user info
|
||||
// Or we can parse the JWT token (simple base64 decode)
|
||||
let userRole = 'user'
|
||||
let isAdmin = false
|
||||
|
||||
try {
|
||||
// Decode JWT token to get payload
|
||||
const tokenParts = accessToken.split('.')
|
||||
if (tokenParts.length === 3) {
|
||||
const payload = JSON.parse(atob(tokenParts[1]))
|
||||
userRole = payload.role || 'user'
|
||||
isAdmin = userRole === 'admin' || userRole === 'superadmin'
|
||||
console.log('AuthStore: Decoded JWT payload', payload)
|
||||
}
|
||||
} catch (decodeError) {
|
||||
console.warn('AuthStore: Could not decode JWT token', decodeError)
|
||||
// Fallback: Make API call to get user info
|
||||
// For now, we'll use a default role
|
||||
}
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem('token', accessToken)
|
||||
localStorage.setItem('refresh_token', refreshToken)
|
||||
localStorage.setItem('is_admin', isAdmin.toString())
|
||||
localStorage.setItem('user_email', email)
|
||||
localStorage.setItem('user_role', userRole)
|
||||
|
||||
// Update store state
|
||||
token.value = accessToken
|
||||
isAdmin.value = isAdmin
|
||||
userEmail.value = email
|
||||
userRole.value = userRole
|
||||
|
||||
console.log('AuthStore: State updated, redirecting to /profile-select')
|
||||
|
||||
// Redirect to profile-select (as per router logic)
|
||||
try {
|
||||
await router.push('/profile-select')
|
||||
console.log('AuthStore: Redirect successful')
|
||||
} catch (error) {
|
||||
console.error('AuthStore: Router redirect failed:', error)
|
||||
throw error
|
||||
}
|
||||
|
||||
return { success: true, token: accessToken, isAdmin, role: userRole }
|
||||
|
||||
} catch (error) {
|
||||
console.error('AuthStore: Login failed', error)
|
||||
|
||||
// Fallback to mock login for development if API is not available
|
||||
if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {
|
||||
console.warn('AuthStore: API unavailable, falling back to mock login for development')
|
||||
|
||||
// Determine user role based on email
|
||||
let mockIsAdmin = false
|
||||
let mockRole = 'user'
|
||||
|
||||
if (email === 'superadmin@profibot.hu') {
|
||||
mockIsAdmin = true
|
||||
mockRole = 'admin'
|
||||
} else if (email === 'tester_pro@profibot.hu') {
|
||||
mockIsAdmin = true // Tester has admin privileges for testing
|
||||
mockRole = 'tester'
|
||||
}
|
||||
|
||||
// Set mock token
|
||||
const mockToken = 'mock_jwt_token_' + Date.now()
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem('token', mockToken)
|
||||
localStorage.setItem('is_admin', mockIsAdmin.toString())
|
||||
localStorage.setItem('user_email', email)
|
||||
localStorage.setItem('user_role', mockRole)
|
||||
|
||||
// Update store state
|
||||
token.value = mockToken
|
||||
isAdmin.value = mockIsAdmin
|
||||
userEmail.value = email
|
||||
userRole.value = mockRole
|
||||
|
||||
// Redirect
|
||||
await router.push('/profile-select')
|
||||
|
||||
return { success: true, token: mockToken, isAdmin: mockIsAdmin, role: mockRole }
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
// Clear localStorage
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('is_admin')
|
||||
localStorage.removeItem('user_email')
|
||||
localStorage.removeItem('user_role')
|
||||
localStorage.removeItem('ui_mode') // Also clear UI mode on logout
|
||||
|
||||
// Reset store state
|
||||
token.value = ''
|
||||
isAdmin.value = false
|
||||
userEmail.value = ''
|
||||
userRole.value = ''
|
||||
userProfile.value = null
|
||||
|
||||
// Redirect to login
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
const fetchUserProfile = async () => {
|
||||
if (!token.value) {
|
||||
throw new Error('Not authenticated')
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:8000/api/v1/users/me', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token.value}`,
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch user profile: ${response.status} ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
console.log('AuthStore: Fetched user profile', data)
|
||||
|
||||
// Update user profile state
|
||||
userProfile.value = data
|
||||
|
||||
// Also update email and role from profile if available
|
||||
if (data.email && !userEmail.value) {
|
||||
userEmail.value = data.email
|
||||
localStorage.setItem('user_email', data.email)
|
||||
}
|
||||
|
||||
if (data.role && !userRole.value) {
|
||||
userRole.value = data.role
|
||||
localStorage.setItem('user_role', data.role)
|
||||
isAdmin.value = data.role === 'admin' || data.role === 'superadmin'
|
||||
localStorage.setItem('is_admin', isAdmin.value.toString())
|
||||
}
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error('AuthStore: Error fetching user profile', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
const checkAuth = () => {
|
||||
// Sync with localStorage on page load
|
||||
const storedToken = localStorage.getItem('token') || ''
|
||||
const storedEmail = localStorage.getItem('user_email') || ''
|
||||
const storedRole = localStorage.getItem('user_role') || ''
|
||||
|
||||
// Try to decode JWT token to get role if not stored
|
||||
let decodedRole = storedRole
|
||||
let decodedIsAdmin = storedRole === 'admin' || storedRole === 'superadmin'
|
||||
|
||||
if (storedToken && !storedRole) {
|
||||
try {
|
||||
// Decode JWT token to get payload
|
||||
const tokenParts = storedToken.split('.')
|
||||
if (tokenParts.length === 3) {
|
||||
const payload = JSON.parse(atob(tokenParts[1]))
|
||||
decodedRole = payload.role || 'user'
|
||||
decodedIsAdmin = decodedRole === 'admin' || decodedRole === 'superadmin'
|
||||
console.log('AuthStore: Decoded JWT on checkAuth', payload)
|
||||
|
||||
// Update localStorage with decoded role
|
||||
localStorage.setItem('user_role', decodedRole)
|
||||
localStorage.setItem('is_admin', decodedIsAdmin.toString())
|
||||
}
|
||||
} catch (decodeError) {
|
||||
console.warn('AuthStore: Could not decode JWT token on checkAuth', decodeError)
|
||||
}
|
||||
} else if (storedToken && storedRole) {
|
||||
// Use stored role
|
||||
decodedIsAdmin = storedRole === 'admin' || storedRole === 'superadmin'
|
||||
}
|
||||
|
||||
token.value = storedToken
|
||||
isAdmin.value = decodedIsAdmin
|
||||
userEmail.value = storedEmail
|
||||
userRole.value = decodedRole || 'user'
|
||||
}
|
||||
|
||||
// Initialize on store creation
|
||||
checkAuth()
|
||||
|
||||
return {
|
||||
// State
|
||||
token,
|
||||
isAdmin,
|
||||
userEmail,
|
||||
userRole,
|
||||
userProfile,
|
||||
|
||||
// Getters
|
||||
isLoggedIn,
|
||||
isTester,
|
||||
displayName,
|
||||
|
||||
// Actions
|
||||
login,
|
||||
logout,
|
||||
checkAuth,
|
||||
fetchUserProfile
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user