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:
Roo
2026-03-24 23:10:31 +00:00
parent cddcd34ba9
commit 89668a9beb
2 changed files with 471 additions and 0 deletions

View 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
}
})