From 173ddfd05fad2c0b8749e79f35b4aa7a83e47781 Mon Sep 17 00:00:00 2001 From: Mono Mono Date: Sat, 14 Feb 2026 17:49:49 -0500 Subject: [PATCH] feat(Login): add example jwt token --- src/components/Login.vue | 42 +++++++++++++++++++++ src/pages/autenticarse.vue | 7 ++++ src/pages/login.vue | 7 ++++ src/services/auth.js | 49 ++++++++++++++++++++++++ src/services/django-api.js | 77 ++++++++++++++++++++++---------------- vite.config.mjs | 12 +++++- 6 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 src/components/Login.vue create mode 100644 src/pages/autenticarse.vue create mode 100644 src/pages/login.vue create mode 100644 src/services/auth.js diff --git a/src/components/Login.vue b/src/components/Login.vue new file mode 100644 index 0000000..b499f04 --- /dev/null +++ b/src/components/Login.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/pages/autenticarse.vue b/src/pages/autenticarse.vue new file mode 100644 index 0000000..d6a52a2 --- /dev/null +++ b/src/pages/autenticarse.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/pages/login.vue b/src/pages/login.vue new file mode 100644 index 0000000..d6a52a2 --- /dev/null +++ b/src/pages/login.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/services/auth.js b/src/services/auth.js new file mode 100644 index 0000000..8700ff3 --- /dev/null +++ b/src/services/auth.js @@ -0,0 +1,49 @@ +class AuthService { + static TOKEN_KEY = 'access_token'; + static REFRESH_KEY = 'refresh_token'; + + static login(credentials) { + const url = `${import.meta.env.VITE_DJANGO_BASE_URL}/api/token/`; + return fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(credentials), + }) + .then(r => r.json()) + .then(data => { + localStorage.setItem(this.TOKEN_KEY, data.access); + localStorage.setItem(this.REFRESH_KEY, data.refresh); + return data; + }); + } + + static getAccessToken() { + return localStorage.getItem(this.TOKEN_KEY); + } + + static getRefreshToken() { + return localStorage.getItem(this.REFRESH_KEY); + } + + static async refresh() { + const refresh = this.getRefreshToken(); + if (!refresh) throw new Error('No refresh token'); + + const url = `${import.meta.env.VITE_DJANGO_BASE_URL}/api/token/refresh/`; + const resp = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ refresh }), + }); + const data = await resp.json(); + localStorage.setItem(this.TOKEN_KEY, data.access); + return data.access; + } + + static logout() { + localStorage.removeItem(this.TOKEN_KEY); + localStorage.removeItem(this.REFRESH_KEY); + } +} + +export default AuthService; diff --git a/src/services/django-api.js b/src/services/django-api.js index 653ad3f..a52cd3b 100644 --- a/src/services/django-api.js +++ b/src/services/django-api.js @@ -1,8 +1,38 @@ +import AuthService from '@/services/auth'; + class DjangoApi { constructor() { this.base = import.meta.env.VITE_DJANGO_BASE_URL; } + _authHeaders() { + const token = AuthService.getAccessToken(); + return token ? { Authorization: `Bearer ${token}` } : {}; + } + + _handleResponse(response) { + if (!response.ok) { + // Si el token ha expirado (401) intentamos refrescar y re‑intentar + if (response.status === 401) { + return AuthService.refresh().then(newToken => { + // volver a ejecutar la petición original con el nuevo token + const retryHeaders = { + ...response.headers, + Authorization: `Bearer ${newToken}`, + }; + // aquí usamos fetch de nuevo con los mismos parámetros + // (para simplificar, delegamos a getRequest/postRequest) + // En la práctica, extrae la lógica a una función reutilizable. + throw new Error('Retry logic should be implemented here'); + }); + } + return response.json().then(err => { + throw new Error(`Error ${response.status}: ${err.detail || response.statusText}`); + }); + } + return response.json(); + } + getCustomers() { const url = this.base + '/don_confiao/api/customers/'; return this.getRequest(url); @@ -79,42 +109,23 @@ class DjangoApi { } getRequest(url) { - return new Promise ((resolve, reject) => { - fetch(url) - .then(response => response.json()) - .then(data => { - resolve(data); - }) - .catch(error => { - reject(error); - }); - }); + return fetch(url, { + headers: { + 'Content-Type': 'application/json', + ...this._authHeaders(), + }, + }).then(this._handleResponse); } postRequest(url, content) { - return new Promise((resolve, reject) => { - fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(content) - }) - .then(response => { - if (!response.ok) { - reject(new Error(`Error ${response.status}: ${response.statusText}`)); - } else { - response.json().then(data => { - if (!data) { - reject(new Error('La respuesta no es un JSON válido')); - } else { - resolve(data); - } - }); - } - }) - .catch(error => reject(error)); - }); + return fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...this._authHeaders(), + }, + body: JSON.stringify(content), + }).then(this._handleResponse); } } diff --git a/vite.config.mjs b/vite.config.mjs index a1a9dec..3d70eca 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -11,6 +11,16 @@ import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' import { defineConfig } from 'vite' import { fileURLToPath, URL } from 'node:url' +const aliasMap = { + '@': fileURLToPath(new URL('./src', import.meta.url)), +}; + +console.log('🔧 Alias configurado:'); +console.log(aliasMap); // <-- se muestra en la terminal al iniciar Vite +console.log('Resolución real de "@":', aliasMap['@']); + + + // https://vitejs.dev/config/ export default defineConfig({ plugins: [ @@ -50,7 +60,7 @@ export default defineConfig({ } }, resolve: { alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)) + '@': fileURLToPath(new URL('./src', import.meta.url)), }, extensions: [ '.js',