121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
import { redirect } from '@sveltejs/kit';
|
|
import type { LayoutServerLoad } from './$types';
|
|
|
|
export const load: LayoutServerLoad = async ({ cookies, url, fetch }) => {
|
|
// Verificar si existe el token en las cookies
|
|
let token = cookies.get('access_token');
|
|
const refreshToken = cookies.get('refresh_token');
|
|
|
|
// Si no hay token, redirigir al login
|
|
if (!token) {
|
|
// Guardar la URL a la que intentaba acceder para redirigir después del login
|
|
throw redirect(303, `/login?redirect=${encodeURIComponent(url.pathname)}`);
|
|
}
|
|
|
|
// Configurar la URL de la API
|
|
let apiUrl = process.env.INTERNAL_API_URL;
|
|
if (!apiUrl) {
|
|
apiUrl = import.meta.env.VITE_API_URL;
|
|
// Reemplazar 'localhost' con 'backend' para llamadas desde el servidor (SSR)
|
|
apiUrl = apiUrl?.replace('localhost', 'backend').replace('127.0.0.1', 'backend');
|
|
}
|
|
|
|
// Normalizar la URL: asegurar que termine con '/'
|
|
const baseUrl = apiUrl?.endsWith('/') ? apiUrl : `${apiUrl}/`;
|
|
|
|
// Validar el token con el backend para asegurar que sea válido
|
|
try {
|
|
const response = await fetch(`${baseUrl}v1/auth/me`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
});
|
|
|
|
// Si el token está expirado (401) y tenemos refresh token, intentar refrescar
|
|
if (response.status === 401 && refreshToken) {
|
|
try {
|
|
const refreshResponse = await fetch(`${baseUrl}v1/auth/refresh`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ refresh_token: refreshToken })
|
|
});
|
|
|
|
if (refreshResponse.ok) {
|
|
const refreshData = await refreshResponse.json();
|
|
|
|
// Actualizar las cookies con los nuevos tokens
|
|
cookies.set('access_token', refreshData.access_token, {
|
|
path: '/',
|
|
httpOnly: false,
|
|
sameSite: 'lax',
|
|
secure: process.env.NODE_ENV === 'production',
|
|
maxAge: 60 * 60 * 24 * 7 // 7 días
|
|
});
|
|
|
|
if (refreshData.refresh_token) {
|
|
cookies.set('refresh_token', refreshData.refresh_token, {
|
|
path: '/',
|
|
httpOnly: false,
|
|
sameSite: 'lax',
|
|
secure: process.env.NODE_ENV === 'production',
|
|
maxAge: 60 * 60 * 24 * 30 // 30 días
|
|
});
|
|
}
|
|
|
|
// Usar el nuevo token para obtener la info del usuario
|
|
token = refreshData.access_token;
|
|
|
|
// Reintentar la validación con el nuevo token
|
|
const retryResponse = await fetch(`${baseUrl}v1/auth/me`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
});
|
|
|
|
if (retryResponse.ok) {
|
|
const userData = await retryResponse.json();
|
|
return {
|
|
authenticated: true,
|
|
user: userData
|
|
};
|
|
}
|
|
}
|
|
} catch (refreshError) {
|
|
console.error('🔐 [Dashboard] Error al refrescar token:', refreshError);
|
|
}
|
|
|
|
// Si llegamos aquí, el refresh falló
|
|
cookies.delete('access_token', { path: '/' });
|
|
cookies.delete('refresh_token', { path: '/' });
|
|
throw redirect(303, `/login?redirect=${encodeURIComponent(url.pathname)}`);
|
|
}
|
|
|
|
if (!response.ok) {
|
|
// Token inválido y no se pudo refrescar, limpiar y redirigir
|
|
cookies.delete('access_token', { path: '/' });
|
|
cookies.delete('refresh_token', { path: '/' });
|
|
throw redirect(303, `/login?redirect=${encodeURIComponent(url.pathname)}`);
|
|
}
|
|
|
|
const userData = await response.json();
|
|
|
|
return {
|
|
authenticated: true,
|
|
user: userData
|
|
};
|
|
} catch (error) {
|
|
// Si es un redirect, re-lanzarlo sin tocar las cookies
|
|
if (error && typeof error === 'object' && 'status' in error && 'location' in error) {
|
|
throw error;
|
|
}
|
|
|
|
// Para cualquier otro error (conexión, etc), limpiar token y redirigir
|
|
console.error('🔐 [Dashboard] Error validando token:', error);
|
|
cookies.delete('access_token', { path: '/' });
|
|
cookies.delete('refresh_token', { path: '/' });
|
|
throw redirect(303, `/login?redirect=${encodeURIComponent(url.pathname)}`);
|
|
}
|
|
};
|