import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { jwtDecode } from 'jwt-decode' export interface JwtPayload { sub: string role: string rank: number scope_level: string region_code?: string scope_id?: number exp: number iat: number } export interface User { id: string email: string role: string rank: number scope_level: string region_code?: string scope_id?: number permissions: string[] } export const useAuthStore = defineStore('auth', () => { // State const token = ref(null) const user = ref(null) const isAuthenticated = computed(() => !!token.value && !isTokenExpired()) const isLoading = ref(false) const error = ref(null) // Initialize token from localStorage only on client side if (typeof window !== 'undefined') { token.value = localStorage.getItem('admin_token') } // Getters const getUserRole = computed(() => user.value?.role || '') const getUserRank = computed(() => user.value?.rank || 0) const getScopeLevel = computed(() => user.value?.scope_level || '') const getRegionCode = computed(() => user.value?.region_code || '') const getScopeId = computed(() => user.value?.scope_id || 0) const getPermissions = computed(() => user.value?.permissions || []) // Check if token is expired function isTokenExpired(): boolean { if (!token.value) return true try { const decoded = jwtDecode(token.value) return Date.now() >= decoded.exp * 1000 } catch { return true } } // Parse token and set user function parseToken(): void { if (!token.value) { user.value = null return } try { const decoded = jwtDecode(token.value) // Map JWT claims to user object user.value = { id: decoded.sub, email: decoded.sub, // Assuming sub is email role: decoded.role, rank: decoded.rank, scope_level: decoded.scope_level, region_code: decoded.region_code, scope_id: decoded.scope_id, permissions: generatePermissions(decoded.role, decoded.rank) } error.value = null } catch (err) { console.error('Failed to parse token:', err) error.value = 'Invalid token format' user.value = null } } // Generate permissions based on role and rank function generatePermissions(role: string, rank: number): string[] { const permissions: string[] = [] // Base permissions based on role switch (role) { case 'superadmin': permissions.push('*') break case 'admin': permissions.push('view:dashboard', 'manage:users', 'manage:services', 'view:finance') if (rank >= 5) permissions.push('manage:settings') break case 'moderator': permissions.push('view:dashboard', 'moderate:services', 'view:users') break case 'salesperson': permissions.push('view:dashboard', 'view:sales', 'manage:leads') break } // Add geographical scope permissions permissions.push(`scope:${role}`) return permissions } // Check if user has permission function hasPermission(permission: string): boolean { if (!user.value) return false if (user.value.permissions.includes('*')) return true return user.value.permissions.includes(permission) } // Check if user has required role rank function hasRank(minRank: number): boolean { return user.value?.rank >= minRank } // Check if user can access scope function canAccessScope(requestedScopeId: number, requestedRegionCode?: string): boolean { if (!user.value) return false // Superadmin can access everything if (user.value.role === 'superadmin') return true // Check scope_id match if (user.value.scope_id && user.value.scope_id === requestedScopeId) return true // Check region_code match if (user.value.region_code && requestedRegionCode) { return user.value.region_code === requestedRegionCode } return false } // Login action async function login(email: string, password: string): Promise { isLoading.value = true error.value = null try { // DEVELOPMENT MODE BYPASS: If email is admin@servicefinder.com or we're in dev mode // Use the mock JWT token to bypass backend authentication const isDevMode = typeof import.meta !== 'undefined' && (import.meta.env.DEV || import.meta.env.MODE === 'development') const isAdminEmail = email === 'admin@servicefinder.com' || email === 'superadmin@servicefinder.com' if (isDevMode && isAdminEmail) { console.log('[DEV MODE] Using mock authentication bypass for:', email) // Use the exact mock JWT string provided in the task const mockJwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdXBlcmFkbWluQHNlcnZpY2VmaW5kZXIuY29tIiwicm9sZSI6InN1cGVyYWRtaW4iLCJyYW5rIjoxMDAsInNjb3BlX2xldmVsIjoiZ2xvYmFsIiwiZXhwIjozMDAwMDAwMDAwLCJpYXQiOjE3MDAwMDAwMDB9.dummy_signature' // Store token safely (SSR-safe) if (typeof window !== 'undefined') { localStorage.setItem('admin_token', mockJwtToken) } token.value = mockJwtToken parseToken() return true } // Otherwise, call real backend login endpoint const response = await fetch('http://localhost:8000/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }) if (!response.ok) { throw new Error('Login failed') } const data = await response.json() token.value = data.access_token if (typeof window !== 'undefined') { localStorage.setItem('admin_token', token.value) } parseToken() return true } catch (err) { error.value = err instanceof Error ? err.message : 'Login failed' return false } finally { isLoading.value = false } } // Logout action function logout(): void { token.value = null user.value = null if (typeof window !== 'undefined') { localStorage.removeItem('admin_token') } } // Initialize store if (token.value) { parseToken() } return { // State token, user, isAuthenticated, isLoading, error, // Getters getUserRole, getUserRank, getScopeLevel, getRegionCode, getScopeId, getPermissions, // Actions login, logout, hasPermission, hasRank, canAccessScope, parseToken } })