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:
@@ -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')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
Reference in New Issue
Block a user