#28 feat(api): add logout.
This commit is contained in:
@@ -1,3 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<h1>Login</h1>
|
||||||
|
<v-form @submit.prevent="login">
|
||||||
|
<v-text-field v-model="username" label="Usuario" required />
|
||||||
|
<v-text-field v-model="password" label="Contraseña" type="password" required />
|
||||||
|
<v-btn type="submit">Entrar</v-btn>
|
||||||
|
<v-alert v-if="error" type="error">{{ error }}</v-alert>
|
||||||
|
</v-form>
|
||||||
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import AuthService from '@/services/auth';
|
import AuthService from '@/services/auth';
|
||||||
@@ -10,33 +19,8 @@
|
|||||||
async function login() {
|
async function login() {
|
||||||
try {
|
try {
|
||||||
await AuthService.login({ username: username.value, password: password.value });
|
await AuthService.login({ username: username.value, password: password.value });
|
||||||
// opcional: redirigir al dashboard
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e.message;
|
error.value = e.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ejemplo de llamada a clientes (requiere token)
|
|
||||||
const api = inject('api');
|
|
||||||
|
|
||||||
async function loadCustomers() {
|
|
||||||
try {
|
|
||||||
const data = await api.getCustomers();
|
|
||||||
console.log(data);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
<h1>Login</h1>
|
|
||||||
<v-form @submit.prevent="login">
|
|
||||||
<v-text-field v-model="username" label="Usuario" required />
|
|
||||||
<v-text-field v-model="password" label="Contraseña" type="password" required />
|
|
||||||
<v-btn type="submit">Entrar</v-btn>
|
|
||||||
<v-alert v-if="error" type="error">{{ error }}</v-alert>
|
|
||||||
</v-form>
|
|
||||||
|
|
||||||
<v-btn @click="loadCustomers">Cargar clientes</v-btn>
|
|
||||||
</template>
|
|
||||||
|
|||||||
29
src/components/Logout.vue
Normal file
29
src/components/Logout.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<style scoped>
|
||||||
|
p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<v-container class="d-flex flex-column align-center justify-center" style="height: 100vh;">
|
||||||
|
<v-progress-circular indeterminate color="primary" />
|
||||||
|
<p class="mt-4">Cerrando sesión…</p>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import AuthService from '@/services/auth';
|
||||||
|
export default {
|
||||||
|
name: 'DonConfiao',
|
||||||
|
mounted() {
|
||||||
|
this.logout();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logout() {
|
||||||
|
AuthService.logout();
|
||||||
|
this.$router.push({
|
||||||
|
path: "/autenticarse"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
menuItems: [
|
menuItems: [
|
||||||
{ title: 'Inicio', route: '/', icon: 'mdi-home'},
|
{ title: 'Inicio', route: '/', icon: 'mdi-home'},
|
||||||
{ title: 'Comprar', route:'/comprar', icon: 'mdi-cart'},
|
{ title: 'Comprar', route:'/comprar', icon: 'mdi-cart'},
|
||||||
|
{ title: 'Salir', route:'/salir', icon: 'mdi-cart'},
|
||||||
],
|
],
|
||||||
menuAdminItems: [
|
menuAdminItems: [
|
||||||
{ title: 'Cuadrar tarro', route: '/cuadrar_tarro', icon: 'mdi-calculator'},
|
{ title: 'Cuadrar tarro', route: '/cuadrar_tarro', icon: 'mdi-calculator'},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Login />
|
<Logout />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -2,7 +2,7 @@ import axios from 'axios';
|
|||||||
import AuthService from '@/services/auth';
|
import AuthService from '@/services/auth';
|
||||||
|
|
||||||
const http = axios.create({
|
const http = axios.create({
|
||||||
baseURL: import.meta.env.VITE_DJANGO_BASE_URL, // ← raíz del API
|
baseURL: import.meta.env.VITE_DJANGO_BASE_URL,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
@@ -20,23 +20,19 @@ http.interceptors.request.use(
|
|||||||
);
|
);
|
||||||
|
|
||||||
http.interceptors.response.use(
|
http.interceptors.response.use(
|
||||||
response => response, // paso directo si todo está OK
|
response => response,
|
||||||
async error => {
|
async error => {
|
||||||
const originalRequest = error.config;
|
const originalRequest = error.config;
|
||||||
|
|
||||||
// Sólo intentamos refrescar una vez
|
|
||||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||||
originalRequest._retry = true;
|
originalRequest._retry = true;
|
||||||
try {
|
try {
|
||||||
const newAccess = await AuthService.refresh(); // guarda el nuevo token
|
const newAccess = await AuthService.refresh();
|
||||||
// vuelve a colocar el header actualizado
|
|
||||||
originalRequest.headers.Authorization = `Bearer ${newAccess}`;
|
originalRequest.headers.Authorization = `Bearer ${newAccess}`;
|
||||||
return http.request(originalRequest); // re‑intenta la petición
|
return http.request(originalRequest);
|
||||||
} catch (refreshError) {
|
} catch (refreshError) {
|
||||||
// Si el refresh falla, forzamos logout
|
|
||||||
AuthService.logout();
|
AuthService.logout();
|
||||||
// opcional: redirigir al login
|
window.location.href = '/autenticarse';
|
||||||
window.location.href = '/login';
|
|
||||||
return Promise.reject(refreshError);
|
return Promise.reject(refreshError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user