Ctrl K
ComunidadGuías…

web/src/stores/auth.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import axios from '@/utils/axios'
import { getProfileImageUrl } from '@/utils/url'

export const useAuthStore = defineStore('auth', () => {
  const user = ref(null)
  const token = ref(localStorage.getItem('token') || null)
  const refreshToken = ref(localStorage.getItem('refreshToken') || null)
  const loading = ref(false)
  const error = ref(null)
  const isCoach = ref(false)
  const schedulingEnabled = ref(false)
  const logoVariant = ref('logo')

  const logoPath = computed(() => logoVariant.value === 'betalogo' ? '/beta/logo.svg' : '/logo.svg')
  const isAuthenticated = computed(() => !!token.value && !!user.value)
  const isCoachRole = computed(() => user.value?.role === 'coach')
  const isAdmin = computed(() => user.value?.role === 'admin')
  const isCoachOrAdmin = computed(() => user.value?.role === 'coach' || user.value?.role === 'admin')

  // Initialize auth state from localStorage
  async function init() {
    const savedUser = localStorage.getItem('user')
    if (savedUser && token.value) {
      try {
        const parsedUser = JSON.parse(savedUser)
        // Ensure profile_image has full URL for display
        if (parsedUser.profile_image) {
          parsedUser.profile_image = getProfileImageUrl(parsedUser.profile_image)
        }
        user.value = parsedUser
        // Set default authorization header
        axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}`
        // Fetch full profile to get is_coach status
        await fetchProfile()
      } catch {
        logout()
      }
    }
  }

  async function login(email, password, rememberMe = false) {
    loading.value = true
    error.value = null
    try {
      const response = await axios.post('/api/auth/login', {
        email,
        password,
        remember_me: rememberMe
      })
      token.value = response.data.token
      user.value = response.data.user

      // Save to localStorage
      localStorage.setItem('token', token.value)
      localStorage.setItem('user', JSON.stringify(user.value))

      // Save refresh token if provided (when remember_me is true)
      if (response.data.refresh_token) {
        refreshToken.value = response.data.refresh_token
        localStorage.setItem('refreshToken', refreshToken.value)
      }

      // Set default authorization header
      axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}`

      // Fetch full profile including is_coach status
      await fetchProfile()

      return true
    } catch (e) {
      console.error('Login error:', e)
      if (e.response) {
        // Server responded with error status
        error.value = e.response.data?.message || `Server error: ${e.response.status}`
      } else if (e.request) {
        // Request was made but no response received
        error.value = 'Cannot connect to server. Please check if the server is running.'
      } else {
        // Something else happened
        error.value = e.message || 'Login failed'
      }
      return false
    } finally {
      loading.value = false
    }
  }

  async function register(userData) {
    loading.value = true
    error.value = null
    try {
      const response = await axios.post('/api/auth/register', userData)
      token.value = response.data.token
      user.value = response.data.user

      localStorage.setItem('token', token.value)
      localStorage.setItem('user', JSON.stringify(user.value))

      axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}`

      return true
    } catch (e) {
      console.error('Registration error:', e)
      if (e.response) {
        // Server responded with error status
        error.value = e.response.data?.message || `Server error: ${e.response.status}`
      } else if (e.request) {
        // Request was made but no response received
        error.value = 'Cannot connect to server. Please check if the server is running.'
      } else {
        // Something else happened
        error.value = e.message || 'Registration failed'
      }
      return false
    } finally {
      loading.value = false
    }
  }

  async function logout() {
    // Revoke refresh token if it exists
    if (refreshToken.value) {
      try {
        await axios.post('/api/auth/revoke', {
          refresh_token: refreshToken.value
        })
      } catch (e) {
        // Ignore errors during revocation
        console.error('Failed to revoke refresh token:', e)
      }
    }

    user.value = null
    token.value = null
    refreshToken.value = null
    isCoach.value = false
    localStorage.removeItem('token')
    localStorage.removeItem('user')
    localStorage.removeItem('refreshToken')
    delete axios.defaults.headers.common['Authorization']
  }

  async function refreshAccessToken() {
    if (!refreshToken.value) {
      return false
    }

    try {
      const response = await axios.post('/api/auth/refresh', {
        refresh_token: refreshToken.value
      })

      token.value = response.data.token
      user.value = response.data.user

      // Update localStorage
      localStorage.setItem('token', token.value)
      localStorage.setItem('user', JSON.stringify(user.value))

      // Set default authorization header
      axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}`

      return true
    } catch {
      // Refresh token is invalid or expired, logout
      logout()
      return false
    }
  }

  async function updateProfile(updates) {
    loading.value = true
    error.value = null
    try {
      const response = await axios.put('/api/users/profile', updates)
      user.value = response.data.user
      localStorage.setItem('user', JSON.stringify(user.value))
      return true
    } catch (e) {
      error.value = e.response?.data?.message || 'Profile update failed'
      return false
    } finally {
      loading.value = false
    }
  }

  // Fetch user profile and is_coach status
  async function fetchProfile() {
    if (!token.value) return false
    try {
      const response = await axios.get('/api/users/profile')
      user.value = response.data.user
      isCoach.value = response.data.is_coach || false
      localStorage.setItem('user', JSON.stringify(user.value))
      return true
    } catch (e) {
      console.error('Failed to fetch profile:', e)
      return false
    }
  }

  // Fetch scheduling_enabled and logo_variant from version endpoint
  async function fetchSchedulingEnabled() {
    try {
      const response = await axios.get('/api/version')
      schedulingEnabled.value = response.data.scheduling_enabled || false
      logoVariant.value = response.data.logo_variant || 'logo'

      // Dynamically update favicon and apple-touch-icon if using beta variant
      if (logoVariant.value !== 'logo') {
        const prefix = '/beta'
        const favicon = document.querySelector('link[rel="icon"]')
        if (favicon) favicon.href = `${prefix}/favicon.ico`
        const appleTouchIcon = document.querySelector('link[rel="apple-touch-icon"]')
        if (appleTouchIcon) appleTouchIcon.href = `${prefix}/apple-touch-icon.png`
      }
    } catch (e) {
      console.error('Failed to fetch version info:', e)
      schedulingEnabled.value = false
    }
  }

  // Initialize on store creation
  init()
  // Fetch scheduling status (public endpoint, no auth required)
  fetchSchedulingEnabled()

  return {
    user,
    token,
    refreshToken,
    loading,
    error,
    isCoach,
    schedulingEnabled,
    logoVariant,
    logoPath,
    isAuthenticated,
    isCoachRole,
    isAdmin,
    isCoachOrAdmin,
    login,
    register,
    logout,
    refreshAccessToken,
    updateProfile,
    fetchProfile,
    fetchSchedulingEnabled
  }
})