cambios en el crear producto y en el refresh token
This commit is contained in:
@@ -52,7 +52,7 @@ const authConfig: NextAuthConfig = {
|
||||
password: credentials?.password as string,
|
||||
};
|
||||
|
||||
// Asigna el tipo `SignInActionResult` que ahora incluye `null`
|
||||
// Asigna el tipo `SignInActionResult` que ahora incluye `null`
|
||||
const response: SignInActionResult = await SignInAction(credential);
|
||||
|
||||
// **NUEVO: Manejar el caso `null` primero**
|
||||
@@ -69,15 +69,15 @@ const authConfig: NextAuthConfig = {
|
||||
response.type === 'UNKNOWN_ERROR') // Incluye todos los tipos de error posibles
|
||||
) {
|
||||
// Si es un error, lánzalo. Este camino termina aquí.
|
||||
throw new CredentialsSignin( "Error en la API:" + response.message);
|
||||
throw new CredentialsSignin("Error en la API:" + response.message);
|
||||
}
|
||||
|
||||
if (!('user' in response)) {
|
||||
// Esto solo ocurriría si SignInAction devolvió un objeto que no es null,
|
||||
// no es un error conocido por 'type', PERO tampoco tiene la propiedad 'user'.
|
||||
// Es un caso de respuesta inesperada del API.
|
||||
console.error("Respuesta de SignInAction con formato inesperado: falta la propiedad 'user'.");
|
||||
throw new CredentialsSignin("Error en el formato de la respuesta del servidor.");
|
||||
if (!('user' in response)) {
|
||||
// Esto solo ocurriría si SignInAction devolvió un objeto que no es null,
|
||||
// no es un error conocido por 'type', PERO tampoco tiene la propiedad 'user'.
|
||||
// Es un caso de respuesta inesperada del API.
|
||||
console.error("Respuesta de SignInAction con formato inesperado: falta la propiedad 'user'.");
|
||||
throw new CredentialsSignin("Error en el formato de la respuesta del servidor.");
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -91,7 +91,7 @@ const authConfig: NextAuthConfig = {
|
||||
refresh_token: response?.tokens.refresh_token ?? '',
|
||||
refresh_expire_in: response?.tokens.refresh_expire_in ?? 0,
|
||||
};
|
||||
|
||||
|
||||
|
||||
},
|
||||
}),
|
||||
@@ -102,46 +102,61 @@ const authConfig: NextAuthConfig = {
|
||||
callbacks: {
|
||||
async jwt({
|
||||
token,
|
||||
user
|
||||
user,
|
||||
}: {
|
||||
token: any;
|
||||
user: User;
|
||||
}) {
|
||||
// Si es un nuevo login, asignamos los datos
|
||||
if (user) {
|
||||
token.id = user.id;
|
||||
token.username = user.username;
|
||||
token.fullname = user.fullname;
|
||||
token.email = user.email;
|
||||
token.role = user.role;
|
||||
token.access_token = user.access_token;
|
||||
token.access_expire_in = user.access_expire_in;
|
||||
token.refresh_token = user.refresh_token;
|
||||
token.refresh_expire_in = user.refresh_expire_in;
|
||||
}
|
||||
try {
|
||||
// Si es un nuevo login, asignamos los datos
|
||||
if (user) {
|
||||
token.id = user.id;
|
||||
token.username = user.username;
|
||||
token.fullname = user.fullname;
|
||||
token.email = user.email;
|
||||
token.role = user.role;
|
||||
token.access_token = user.access_token;
|
||||
token.access_expire_in = user.access_expire_in;
|
||||
token.refresh_token = user.refresh_token;
|
||||
token.refresh_expire_in = user.refresh_expire_in;
|
||||
return token; // IMPORTANTE: retornamos el token aquí para evitar que entre en el siguiente 'if'
|
||||
}
|
||||
|
||||
// Renovar access_token si ha expirado
|
||||
if (Date.now() / 1000 > (token.access_expire_in as number)) {
|
||||
if (Date.now() / 1000 > (token.refresh_expire_in as number)) {
|
||||
// Verificar si el access_token ha expirado
|
||||
const now = Date.now() / 1000;
|
||||
if (now < (token.access_expire_in as number)) {
|
||||
return token; // Si el token no ha expirado, lo retornamos sin cambios
|
||||
}
|
||||
|
||||
// Si el access_token ha expirado, verificar el refresh_token
|
||||
if (now > (token.refresh_expire_in as number)) {
|
||||
console.log("Refresh token ha expirado. Forzando logout.");
|
||||
return null; // Forzar logout
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await resfreshTokenAction({
|
||||
token: token.refresh_token as string,
|
||||
});
|
||||
if (!res) throw new Error('Failed to refresh token');
|
||||
token.access_token = res.tokens.access_token;
|
||||
token.access_expire_in = res.tokens.access_expire_in;
|
||||
token.refresh_token = res.tokens.refresh_token;
|
||||
token.refresh_expire_in = res.tokens.refresh_expire_in;
|
||||
} catch (error) {
|
||||
console.log("error: ",error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Si el access_token ha expirado pero el refresh_token es válido, renovar
|
||||
console.log("Renovando token de acceso...");
|
||||
const res = await resfreshTokenAction({
|
||||
token: token.refresh_token as string,
|
||||
});
|
||||
|
||||
return token;
|
||||
console.log("Pepe");
|
||||
|
||||
|
||||
if (!res) throw new Error('Fallo al renovar el token');
|
||||
|
||||
// Actualizar el token con la nueva información
|
||||
return {
|
||||
...token,
|
||||
access_token: res.tokens.access_token,
|
||||
access_expire_in: res.tokens.access_expire_in,
|
||||
refresh_token: res.tokens.refresh_token,
|
||||
refresh_expire_in: res.tokens.refresh_expire_in,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error al renovar el token: ", error);
|
||||
return null; // Fallo al renovar, forzar logout
|
||||
}
|
||||
},
|
||||
async session({ session, token }: { session: Session; token: DefaultJWT }) {
|
||||
session.access_token = token.access_token as string;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
'use server';
|
||||
import { env } from '@/lib/env'; // Importamos la configuración de entorno validada
|
||||
import { env } from '@/lib/env';
|
||||
import axios from 'axios';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Crear instancia de Axios con la URL base validada
|
||||
const fetchApi = axios.create({
|
||||
baseURL: env.API_URL, // Aquí usamos env.API_URL en vez de process.env.BACKEND_URL
|
||||
// No establecer Content-Type aquí. Axios lo manejará automáticamente con FormData.
|
||||
baseURL: env.API_URL,
|
||||
});
|
||||
|
||||
// Interceptor para incluir el token automáticamente en las peticiones
|
||||
// ESTE INTERCEPTOR ESTÁ BIEN PARA EL RESTO DE LAS PETICIONES AUTENTICADAS
|
||||
fetchApi.interceptors.request.use(async (config: any) => {
|
||||
try {
|
||||
// Importación dinámica para evitar la referencia circular
|
||||
const { auth } = await import('@/lib/auth');
|
||||
const { auth } = await import('@/lib/auth'); // Importación dinámica
|
||||
const session = await auth();
|
||||
const token = session?.access_token;
|
||||
|
||||
@@ -22,44 +21,35 @@ fetchApi.interceptors.request.use(async (config: any) => {
|
||||
}
|
||||
|
||||
// **Importante:** Si el body es FormData, elimina el Content-Type para que Axios lo configure automáticamente.
|
||||
// Esto es necesario porque 'multipart/form-data' requiere un 'boundary' que Axios añade.
|
||||
if (config.data instanceof FormData) {
|
||||
delete config.headers['Content-Type'];
|
||||
} else {
|
||||
// Para otros tipos de datos, asegura que el Content-Type sea 'application/json'
|
||||
config.headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
return config;
|
||||
} catch (error) {
|
||||
console.error('Error getting auth token:', error);
|
||||
console.error('Error al obtener el token de autenticación para el interceptor:', error);
|
||||
// IMPORTANTE: Si ocurre un error aquí, es mejor rechazar la promesa
|
||||
// para que la solicitud no se envíe sin autorización.
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
/**
|
||||
* Función para hacer peticiones con validación de respuesta
|
||||
* @param schema - Esquema de Zod para validar la respuesta
|
||||
* @param url - Endpoint a consultar
|
||||
* @param method - Método HTTP (GET, POST, PUT, PATCH, DELETE)
|
||||
* @param data - Datos a enviar (puede ser un objeto JSON o FormData para archivos)
|
||||
* @returns [error, data] -> Devuelve el error como objeto estructurado si hay fallo, o los datos validados
|
||||
*/
|
||||
// safeFetchApi sigue siendo útil para el resto de las llamadas que requieren autenticación
|
||||
export const safeFetchApi = async <T extends z.ZodSchema<any>>(
|
||||
schema: T,
|
||||
url: string,
|
||||
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' = 'GET',
|
||||
data?: any, // Renombrado a 'data' para mayor claridad y consistencia con Axios
|
||||
data?: any,
|
||||
): Promise<
|
||||
[{ type: string; message: string; details?: any } | null, z.infer<T> | null]
|
||||
> => {
|
||||
try {
|
||||
console.log(url,method,data);
|
||||
|
||||
const response = await fetchApi({
|
||||
method,
|
||||
url,
|
||||
data, // Axios usa 'data' para el body de POST/PUT/PATCH
|
||||
data,
|
||||
});
|
||||
|
||||
const parsed = schema.safeParse(response.data);
|
||||
|
||||
11
apps/web/lib/refreshApi.ts
Normal file
11
apps/web/lib/refreshApi.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import axios from 'axios';
|
||||
import { env } from '@/lib/env'; // Asegúrate de que env está correctamente configurado
|
||||
|
||||
// Crea una instancia de Axios SÓLO para la API de refresh token
|
||||
// Sin el interceptor que obtiene la sesión para evitar la dependencia circular
|
||||
export const refreshApi = axios.create({
|
||||
baseURL: env.API_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json', // El refresh token se envía en el body JSON
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user