Update security measures and improve caching strategy in API calls
This commit is contained in:
parent
8ae76d86c7
commit
33d4f1df70
|
|
@ -169,6 +169,8 @@ La aplicación implementa las siguientes medidas de seguridad:
|
|||
- ✅ API Route proxy para todas las llamadas a Dolibarr
|
||||
- ✅ Variables de entorno seguras
|
||||
- ✅ Validación de datos de entrada
|
||||
- ✅ Validación de token contra Dolibarr en cada request
|
||||
- ✅ Sin caché de datos en servidor (evita datos antiguos)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -195,6 +197,10 @@ La aplicación implementa las siguientes medidas de seguridad:
|
|||
- Revisa la URL en `.env.local`
|
||||
- Comprueba que la API esté habilitada en Dolibarr
|
||||
|
||||
### Me pide login constantemente / No veo datos
|
||||
- Si Dolibarr está apagado, el middleware fuerza login y no hay datos
|
||||
- Asegúrate de tener Docker levantado y Dolibarr accesible en `http://localhost:8200`
|
||||
|
||||
### No se muestran proyectos
|
||||
- Ejecuta `npm run seed` para crear datos de prueba
|
||||
- Verifica las credenciales de la API
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ export async function GET(
|
|||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
// Cache durante 60 segundos
|
||||
next: { revalidate: 60 }
|
||||
// Sin cache en servidor para evitar datos antiguos
|
||||
cache: 'no-store'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@ function saveAuthData(token: string, user?: AuthUser): void {
|
|||
}
|
||||
|
||||
// También guardar en cookie para que el middleware pueda leerlo
|
||||
document.cookie = `${AUTH_TOKEN_KEY}=${token}; path=/; max-age=${60 * 60 * 24 * 7}; SameSite=Lax`;
|
||||
// Cookie de sesión: se elimina al cerrar el navegador
|
||||
document.cookie = `${AUTH_TOKEN_KEY}=${token}; path=/; SameSite=Lax`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ async function dolibarrDirectFetch(endpoint: string, options: RequestInit = {})
|
|||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
// Cache durante 60 segundos en servidor
|
||||
next: { revalidate: 60 }
|
||||
// Sin cache en servidor para evitar datos antiguos
|
||||
cache: 'no-store'
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,28 @@ const PUBLIC_ROUTES = ["/login"];
|
|||
// Rutas que siempre deben ser accesibles (assets, API, etc.)
|
||||
const ALWAYS_ALLOWED = ["/_next", "/api", "/favicon.ico", "/avatar.jpg"];
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
async function verifyDolibarrToken(token: string): Promise<boolean> {
|
||||
const apiUrl = process.env.DOLIBARR_API_URL || process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
if (!apiUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/users/info?DOLAPIKEY=${token}`, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
// Permitir siempre assets y APIs
|
||||
|
|
@ -15,21 +36,38 @@ export function middleware(request: NextRequest) {
|
|||
return NextResponse.next();
|
||||
}
|
||||
|
||||
// Obtener token de las cookies (si existe)
|
||||
const token = request.cookies.get("dolibarr_auth_token")?.value;
|
||||
|
||||
// Verificar si es ruta pública
|
||||
const isPublicRoute = PUBLIC_ROUTES.some((route) => pathname === route);
|
||||
|
||||
if (!token && !isPublicRoute) {
|
||||
// No hay token y la ruta es privada -> redirigir a login
|
||||
const loginUrl = new URL("/login", request.url);
|
||||
loginUrl.searchParams.set("redirect", pathname);
|
||||
return NextResponse.redirect(loginUrl);
|
||||
// Obtener token de las cookies (si existe)
|
||||
const token = request.cookies.get("dolibarr_auth_token")?.value;
|
||||
|
||||
if (!token) {
|
||||
if (!isPublicRoute) {
|
||||
const loginUrl = new URL("/login", request.url);
|
||||
loginUrl.searchParams.set("redirect", pathname);
|
||||
return NextResponse.redirect(loginUrl);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
if (token && pathname === "/login") {
|
||||
// Hay token y está en login -> redirigir a home
|
||||
const isValid = await verifyDolibarrToken(token);
|
||||
|
||||
if (!isValid) {
|
||||
const loginUrl = new URL("/login", request.url);
|
||||
loginUrl.searchParams.set("redirect", pathname);
|
||||
|
||||
const response = NextResponse.redirect(loginUrl);
|
||||
response.cookies.set("dolibarr_auth_token", "", {
|
||||
path: "/",
|
||||
expires: new Date(0),
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
if (pathname === "/login") {
|
||||
return NextResponse.redirect(new URL("/", request.url));
|
||||
}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 292 KiB |
|
|
@ -116,7 +116,7 @@
|
|||
**Explicación:**
|
||||
- Si es servidor usa llamada directa.
|
||||
- Si es cliente usa el proxy interno.
|
||||
- Se aplica caché corta (`revalidate: 60`).
|
||||
- No se aplica caché en servidor (`cache: 'no-store'`).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue