/** * useAuth Composable * * Manages authentication state including login, logout, token storage, and user info */ import { ref, computed, watch } from 'vue'; import { apiClient } from '../utilities/api-client.js'; // Shared state const token = ref(localStorage.getItem('auth_token') || null); const user = ref(null); const isLoading = ref(false); const error = ref(null); // Compute derived states const isAuthenticated = computed(() => !!token.value && !!user.value); const isAdmin = computed(() => user.value?.isAdmin || false); /** * Load and verify stored token on app startup */ async function initializeAuth() { const storedToken = localStorage.getItem('auth_token'); if (!storedToken) { token.value = null; user.value = null; return; } try { const response = await apiClient.post('/api/auth/verify', { token: storedToken }); token.value = storedToken; user.value = response.user; } catch (err) { // Token is invalid, clear it localStorage.removeItem('auth_token'); token.value = null; user.value = null; } } /** * Login with password * @param {string} password - Admin password * @returns {Promise} Response with token and user info * @throws {Error} If login fails */ async function login(password) { isLoading.value = true; error.value = null; try { if (!password) { throw new Error('Password is required'); } const response = await apiClient.post('/api/auth/login', { password }); // Store token token.value = response.token; user.value = response.user; localStorage.setItem('auth_token', response.token); return response; } catch (err) { error.value = err.message; token.value = null; user.value = null; throw err; } finally { isLoading.value = false; } } /** * Logout and clear authentication */ async function logout() { isLoading.value = true; try { await apiClient.post('/api/auth/logout', {}); } catch (err) { console.error('Logout error:', err); } finally { // Clear token regardless of API response token.value = null; user.value = null; localStorage.removeItem('auth_token'); isLoading.value = false; } } /** * Refresh the current token * @returns {Promise} Response with new token * @throws {Error} If refresh fails */ async function refreshToken() { if (!token.value) { throw new Error('No token to refresh'); } try { const response = await apiClient.post('/api/auth/refresh', { token: token.value }); token.value = response.token; localStorage.setItem('auth_token', response.token); return response; } catch (err) { // If refresh fails, logout await logout(); throw err; } } /** * Get current user info * @returns {Promise} User info */ async function getUserInfo() { try { const response = await apiClient.get('/api/auth/user', { headers: { Authorization: `Bearer ${token.value}` } }); user.value = response.user; return response.user; } catch (err) { // If getting user info fails, logout await logout(); throw err; } } /** * Check if user has a specific permission * @param {string|string[]} permissions - Permission or array of permissions * @returns {boolean} True if user has permission */ function hasPermission(permissions) { if (!user.value) return false; const perms = Array.isArray(permissions) ? permissions : [permissions]; return perms.some(perm => user.value.permissions?.includes(perm)); } /** * Add auth token to API client headers */ export function setupAuthInterceptor() { if (token.value) { apiClient.setDefaultHeader('Authorization', `Bearer ${token.value}`); } // Watch for token changes and update header watch(token, newToken => { if (newToken) { apiClient.setDefaultHeader('Authorization', `Bearer ${newToken}`); } else { apiClient.removeDefaultHeader('Authorization'); } }); } export function useAuth() { return { // State token: computed(() => token.value), user: computed(() => user.value), isLoading: computed(() => isLoading.value), error: computed(() => error.value), // Computed isAuthenticated, isAdmin, // Methods initializeAuth, login, logout, refreshToken, getUserInfo, hasPermission, setupAuthInterceptor }; }