WIP - BFF
This commit is contained in:
@@ -1,40 +1,25 @@
|
||||
export const useApi = () => {
|
||||
const config = useRuntimeConfig()
|
||||
const router = useRouter()
|
||||
const { toast } = useToast()
|
||||
const { isLoggedIn, logout } = useAuth()
|
||||
|
||||
// Use current domain for API calls (same subdomain routing)
|
||||
/**
|
||||
* API calls now go through the Nitro BFF proxy at /api/*
|
||||
* The proxy handles:
|
||||
* - Auth token injection from HTTP-only cookies
|
||||
* - Tenant subdomain extraction and forwarding
|
||||
* - Forwarding requests to the NestJS backend
|
||||
*/
|
||||
const getApiBaseUrl = () => {
|
||||
if (import.meta.client) {
|
||||
// In browser, use current hostname but with port 3000 for API
|
||||
const currentHost = window.location.hostname
|
||||
const protocol = window.location.protocol
|
||||
//return `${protocol}//${currentHost}:3000`
|
||||
return `${protocol}//${currentHost}`
|
||||
}
|
||||
// Fallback for SSR
|
||||
return config.public.apiBaseUrl
|
||||
// All API calls go through Nitro proxy - works for both SSR and client
|
||||
return ''
|
||||
}
|
||||
|
||||
const getHeaders = () => {
|
||||
// Headers are now minimal - auth and tenant are handled by the Nitro proxy
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
// Add tenant ID from localStorage or state
|
||||
if (import.meta.client) {
|
||||
const tenantId = localStorage.getItem('tenantId')
|
||||
if (tenantId) {
|
||||
headers['x-tenant-id'] = tenantId
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
|
||||
@@ -1,61 +1,131 @@
|
||||
/**
|
||||
* Authentication composable using BFF (Backend for Frontend) pattern
|
||||
* Auth tokens are stored in HTTP-only cookies managed by Nitro server
|
||||
* Tenant context is stored in a readable cookie for client-side access
|
||||
*/
|
||||
export const useAuth = () => {
|
||||
const tokenCookie = useCookie('token')
|
||||
const authMessageCookie = useCookie('authMessage')
|
||||
const tenantCookie = useCookie('routebox_tenant')
|
||||
const router = useRouter()
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
// Reactive user state - populated from /api/auth/me
|
||||
const user = useState<any>('auth_user', () => null)
|
||||
const isAuthenticated = useState<boolean>('auth_is_authenticated', () => false)
|
||||
const isLoading = useState<boolean>('auth_is_loading', () => false)
|
||||
|
||||
/**
|
||||
* Check if user is logged in
|
||||
* Uses server-side session validation via /api/auth/me
|
||||
*/
|
||||
const isLoggedIn = () => {
|
||||
if (!import.meta.client) return false
|
||||
const token = localStorage.getItem('token')
|
||||
const tenantId = localStorage.getItem('tenantId')
|
||||
return !!(token && tenantId)
|
||||
return isAuthenticated.value
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
if (import.meta.client) {
|
||||
// Call backend logout endpoint
|
||||
try {
|
||||
const token = localStorage.getItem('token')
|
||||
const tenantId = localStorage.getItem('tenantId')
|
||||
|
||||
if (token) {
|
||||
await fetch(`${config.public.apiBaseUrl}/api/auth/logout`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
...(tenantId && { 'x-tenant-id': tenantId }),
|
||||
},
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error)
|
||||
/**
|
||||
* Login with email and password
|
||||
* Calls the Nitro BFF login endpoint which sets HTTP-only cookies
|
||||
*/
|
||||
const login = async (email: string, password: string) => {
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
const response = await $fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
body: { email, password },
|
||||
})
|
||||
|
||||
if (response.success) {
|
||||
user.value = response.user
|
||||
isAuthenticated.value = true
|
||||
return { success: true, user: response.user }
|
||||
}
|
||||
|
||||
// Clear local storage
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('tenantId')
|
||||
localStorage.removeItem('user')
|
||||
|
||||
// Clear cookie for server-side check
|
||||
tokenCookie.value = null
|
||||
|
||||
// Set flash message for login page
|
||||
authMessageCookie.value = 'Logged out successfully'
|
||||
|
||||
// Redirect to login page
|
||||
router.push('/login')
|
||||
return { success: false, error: 'Login failed' }
|
||||
} catch (error: any) {
|
||||
const message = error.data?.message || error.message || 'Login failed'
|
||||
return { success: false, error: message }
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout user
|
||||
* Calls the Nitro BFF logout endpoint which clears HTTP-only cookies
|
||||
*/
|
||||
const logout = async () => {
|
||||
try {
|
||||
await $fetch('/api/auth/logout', {
|
||||
method: 'POST',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error)
|
||||
}
|
||||
|
||||
// Clear local state
|
||||
user.value = null
|
||||
isAuthenticated.value = false
|
||||
|
||||
// Set flash message for login page
|
||||
authMessageCookie.value = 'Logged out successfully'
|
||||
|
||||
// Redirect to login page
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current authentication status
|
||||
* Validates session with backend via Nitro BFF
|
||||
*/
|
||||
const checkAuth = async () => {
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
const response = await $fetch('/api/auth/me', {
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
if (response.authenticated && response.user) {
|
||||
user.value = response.user
|
||||
isAuthenticated.value = true
|
||||
return true
|
||||
}
|
||||
} catch (error) {
|
||||
// Session invalid or expired
|
||||
user.value = null
|
||||
isAuthenticated.value = false
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current user
|
||||
*/
|
||||
const getUser = () => {
|
||||
if (!import.meta.client) return null
|
||||
const userStr = localStorage.getItem('user')
|
||||
return userStr ? JSON.parse(userStr) : null
|
||||
return user.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current tenant ID from cookie
|
||||
*/
|
||||
const getTenantId = () => {
|
||||
return tenantCookie.value
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
user,
|
||||
isAuthenticated,
|
||||
isLoading,
|
||||
// Methods
|
||||
isLoggedIn,
|
||||
login,
|
||||
logout,
|
||||
checkAuth,
|
||||
getUser,
|
||||
getTenantId,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user