feat: integra autenticação do front com API real

- useAuth.ts: substitui mock por $fetch no /auth/login e /auth/logout
- login.vue: adiciona campo "Organização" (slug) no formulário
- nuxt.config.ts: adiciona runtimeConfig.public.apiBase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Junior
2026-03-14 12:35:19 -03:00
parent 587a0d4f62
commit e7016b6b10
3 changed files with 53 additions and 11 deletions

View File

@@ -1,6 +1,3 @@
const MOCK_EMAIL = 'admin@licitatche.gov.br'
const MOCK_PASSWORD = 'admin123'
interface AuthUser {
nome: string
email: string
@@ -8,26 +5,54 @@ interface AuthUser {
}
export function useAuth() {
const { public: { apiBase } } = useRuntimeConfig()
const user = useState<AuthUser | null>('auth_user', () => null)
const token = useCookie('auth_token', { maxAge: 60 * 60 * 8 })
const token = useCookie<string | null>('auth_token', { maxAge: 60 * 60 * 8 })
const refreshToken = useCookie<string | null>('refresh_token', { maxAge: 60 * 60 * 24 * 7 })
const isAuthenticated = computed(() => !!token.value)
// Restaura usuário do token ao recarregar
if (token.value && !user.value) {
user.value = { nome: 'Admin', email: MOCK_EMAIL, papel: 'Administrador' }
try {
const payload = JSON.parse(atob(token.value.split('.')[1]))
user.value = { nome: payload.email.split('@')[0], email: payload.email, papel: payload.role }
} catch {
token.value = null
}
}
async function login(email: string, password: string): Promise<{ success: boolean; error?: string }> {
if (email === MOCK_EMAIL && password === MOCK_PASSWORD) {
token.value = 'mock_token_' + Date.now()
user.value = { nome: 'Admin', email, papel: 'Administrador' }
async function login(email: string, password: string, slug: string): Promise<{ success: boolean; error?: string }> {
try {
const res = await $fetch<{ access_token: string; refresh_token: string }>(`${apiBase}/auth/login`, {
method: 'POST',
body: { email, password, slug },
})
token.value = res.access_token
refreshToken.value = res.refresh_token
const payload = JSON.parse(atob(res.access_token.split('.')[1]))
user.value = { nome: payload.email.split('@')[0], email: payload.email, papel: payload.role }
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: 'E-mail ou senha incorretos.' }
}
function logout() {
// Fire-and-forget logout no back (invalida refresh token)
if (refreshToken.value) {
$fetch(`${apiBase}/auth/logout`, {
method: 'POST',
body: { refresh_token: refreshToken.value },
}).catch(() => {})
}
token.value = null
refreshToken.value = null
user.value = null
navigateTo('/login')
}

View File

@@ -5,13 +5,14 @@ definePageMeta({ layout: 'auth' })
const { login } = useAuth()
const email = ref('')
const password = ref('')
const slug = ref('')
const error = ref('')
const loading = ref(false)
async function handleSubmit() {
error.value = ''
loading.value = true
const result = await login(email.value, password.value)
const result = await login(email.value, password.value, slug.value)
loading.value = false
if (result.success) {
navigateTo('/')
@@ -49,6 +50,16 @@ async function handleSubmit() {
/>
</UFormField>
<UFormField label="Organização" class="field">
<UInput
v-model="slug"
type="text"
placeholder="minha-empresa"
size="md"
required
/>
</UFormField>
<div class="forgot">
<a href="#">Esqueceu a senha?</a>
</div>

View File

@@ -15,6 +15,12 @@ export default defineNuxtConfig({
'/': { prerender: true }
},
runtimeConfig: {
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE || 'http://localhost:8080/api/v1',
},
},
compatibilityDate: '2025-01-15',
eslint: {