This commit is contained in:
Junior
2026-04-21 18:05:15 -03:00
parent 8c3c56de09
commit d29137be9d
41 changed files with 3945 additions and 318 deletions

View File

@@ -1,16 +1,45 @@
// Wrapper sobre $fetch que injeta o Authorization header automaticamente.
// Em caso de 401, tenta renovar o token via refresh e repete a requisição.
export function useApi() {
const { public: { apiBase } } = useRuntimeConfig()
const token = useCookie<string | null>('auth_token')
const refreshToken = useCookie<string | null>('refresh_token')
function apiFetch<T>(path: string, options: Parameters<typeof $fetch>[1] = {}): Promise<T> {
return $fetch<T>(`${apiBase}${path}`, {
async function tryRefresh(): Promise<boolean> {
if (!refreshToken.value) return false
try {
const res = await $fetch<{ access_token: string; refresh_token: string }>(
`${apiBase}/auth/refresh`,
{ method: 'POST', body: { refresh_token: refreshToken.value } }
)
token.value = res.access_token
refreshToken.value = res.refresh_token
return true
} catch {
token.value = null
refreshToken.value = null
navigateTo('/login')
return false
}
}
async function apiFetch<T>(path: string, options: Parameters<typeof $fetch>[1] = {}): Promise<T> {
const doFetch = () => $fetch<T>(`${apiBase}${path}`, {
...options,
headers: {
...(options.headers as Record<string, string> || {}),
...(token.value ? { Authorization: `Bearer ${token.value}` } : {}),
},
})
try {
return await doFetch()
} catch (err: any) {
if (err?.status === 401 && await tryRefresh()) {
return await doFetch()
}
throw err
}
}
return { apiFetch }

View File

@@ -4,6 +4,17 @@ interface AuthUser {
papel: string
}
export interface TenantOption {
id: string
name: string
slug: string
}
export type LoginResult =
| { success: true }
| { success: false; error: string }
| { needsTenantSelect: true; tenants: TenantOption[]; email: string; password: string }
export function useAuth() {
const { public: { apiBase } } = useRuntimeConfig()
@@ -23,23 +34,48 @@ export function useAuth() {
}
}
async function login(email: string, password: string, slug: string): Promise<{ success: boolean; error?: string }> {
function _applyTokens(access: string, refresh: string) {
token.value = access
refreshToken.value = refresh
const payload = JSON.parse(atob(access.split('.')[1]))
user.value = { nome: payload.email.split('@')[0], email: payload.email, papel: payload.role }
}
async function login(email: string, password: string): Promise<LoginResult> {
try {
const res = await $fetch<{ access_token: string; refresh_token: string }>(`${apiBase}/auth/login`, {
const res = await $fetch<{
tokens?: { access_token: string; refresh_token: string }
tenants?: TenantOption[]
}>(`${apiBase}/auth/login`, {
method: 'POST',
body: { email, password, slug },
body: { email, password },
})
token.value = res.access_token
refreshToken.value = res.refresh_token
if (res.tokens) {
_applyTokens(res.tokens.access_token, res.tokens.refresh_token)
return { success: true }
}
const payload = JSON.parse(atob(res.access_token.split('.')[1]))
user.value = { nome: payload.email.split('@')[0], email: payload.email, papel: payload.role }
if (res.tenants && res.tenants.length > 0) {
return { needsTenantSelect: true, tenants: res.tenants, email, password }
}
return { success: false, error: 'Resposta inesperada do servidor.' }
} catch (err: any) {
return { success: false, error: err?.data?.error || 'E-mail ou senha incorretos.' }
}
}
async function selectTenant(email: string, password: string, tenantId: string): Promise<{ success: boolean; error?: string }> {
try {
const res = await $fetch<{ access_token: string; refresh_token: string }>(`${apiBase}/auth/login/select-tenant`, {
method: 'POST',
body: { email, password, tenant_id: tenantId },
})
_applyTokens(res.access_token, res.refresh_token)
return { success: true }
} catch (err: any) {
const msg = err?.data?.error || 'E-mail, senha ou organização incorretos.'
return { success: false, error: msg }
return { success: false, error: err?.data?.error || 'Erro ao selecionar empresa.' }
}
}
@@ -57,5 +93,5 @@ export function useAuth() {
navigateTo('/login')
}
return { user, isAuthenticated, login, logout }
return { user, isAuthenticated, login, selectTenant, logout }
}