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 { interface AuthUser {
nome: string nome: string
email: string email: string
@@ -8,26 +5,54 @@ interface AuthUser {
} }
export function useAuth() { export function useAuth() {
const { public: { apiBase } } = useRuntimeConfig()
const user = useState<AuthUser | null>('auth_user', () => null) 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) const isAuthenticated = computed(() => !!token.value)
// Restaura usuário do token ao recarregar
if (token.value && !user.value) { 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 }> { async function login(email: string, password: string, slug: string): Promise<{ success: boolean; error?: string }> {
if (email === MOCK_EMAIL && password === MOCK_PASSWORD) { try {
token.value = 'mock_token_' + Date.now() const res = await $fetch<{ access_token: string; refresh_token: string }>(`${apiBase}/auth/login`, {
user.value = { nome: 'Admin', email, papel: 'Administrador' } 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 } 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() { 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 token.value = null
refreshToken.value = null
user.value = null user.value = null
navigateTo('/login') navigateTo('/login')
} }

View File

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

View File

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