2026-02-06 19:51:04 +00:00
|
|
|
/**
|
|
|
|
|
* Servicio de autenticación para Dolibarr
|
|
|
|
|
*
|
|
|
|
|
* Maneja login, logout y gestión de tokens/sesiones
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Tipos para la autenticación
|
|
|
|
|
export interface AuthUser {
|
|
|
|
|
id: number;
|
|
|
|
|
login: string;
|
|
|
|
|
firstname: string;
|
|
|
|
|
lastname: string;
|
|
|
|
|
email: string;
|
|
|
|
|
admin: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LoginCredentials {
|
|
|
|
|
login: string;
|
|
|
|
|
password: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LoginResponse {
|
|
|
|
|
success: boolean;
|
|
|
|
|
token?: string;
|
|
|
|
|
user?: AuthUser;
|
|
|
|
|
error?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AuthState {
|
|
|
|
|
isAuthenticated: boolean;
|
|
|
|
|
user: AuthUser | null;
|
|
|
|
|
token: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Constantes
|
|
|
|
|
const AUTH_TOKEN_KEY = "dolibarr_auth_token";
|
|
|
|
|
const AUTH_USER_KEY = "dolibarr_auth_user";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Obtiene la URL base de la API de Dolibarr
|
|
|
|
|
*/
|
|
|
|
|
function getApiUrl(): string {
|
2026-05-07 15:52:12 +00:00
|
|
|
// En cliente, usar proxy para evitar problemas de red/CORS
|
|
|
|
|
if (typeof window !== "undefined") {
|
|
|
|
|
return "/api/dolibarr";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const url = process.env.DOLIBARR_API_URL || process.env.NEXT_PUBLIC_API_URL;
|
|
|
|
|
|
2026-02-06 19:51:04 +00:00
|
|
|
if (!url) {
|
|
|
|
|
throw new Error("API URL not configured");
|
|
|
|
|
}
|
2026-05-07 15:52:12 +00:00
|
|
|
|
2026-02-06 19:51:04 +00:00
|
|
|
return url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Realiza login contra la API de Dolibarr
|
|
|
|
|
* Dolibarr usa el endpoint /login para autenticar y devuelve un token
|
|
|
|
|
*/
|
|
|
|
|
export async function login(credentials: LoginCredentials): Promise<LoginResponse> {
|
|
|
|
|
try {
|
|
|
|
|
const apiUrl = getApiUrl();
|
|
|
|
|
|
|
|
|
|
// Dolibarr API login endpoint
|
|
|
|
|
const response = await fetch(`${apiUrl}/login`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Accept": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
login: credentials.login,
|
|
|
|
|
password: credentials.password,
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const errorData = await response.json().catch(() => ({}));
|
|
|
|
|
|
|
|
|
|
if (response.status === 401 || response.status === 403) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: "Credenciales incorrectas. Verifica tu usuario y contraseña.",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: errorData.error || `Error del servidor: ${response.status}`,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
// Dolibarr devuelve el token directamente en success.token
|
|
|
|
|
const token = data.success?.token || data.token;
|
|
|
|
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: "No se recibió token de autenticación",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Obtener información del usuario con el token
|
|
|
|
|
const userResponse = await fetch(`${apiUrl}/users/info?DOLAPIKEY=${token}`, {
|
|
|
|
|
headers: {
|
|
|
|
|
"Accept": "application/json",
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let user: AuthUser | undefined;
|
|
|
|
|
|
|
|
|
|
if (userResponse.ok) {
|
|
|
|
|
const userData = await userResponse.json();
|
|
|
|
|
user = {
|
|
|
|
|
id: parseInt(userData.id),
|
|
|
|
|
login: userData.login,
|
|
|
|
|
firstname: userData.firstname || "",
|
|
|
|
|
lastname: userData.lastname || "",
|
|
|
|
|
email: userData.email || "",
|
|
|
|
|
admin: userData.admin === "1" || userData.admin === 1,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Guardar en localStorage
|
|
|
|
|
saveAuthData(token, user);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: true,
|
|
|
|
|
token,
|
|
|
|
|
user,
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Login error:", error);
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: "Error de conexión. Verifica tu conexión a internet.",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Realiza logout - limpia datos locales
|
|
|
|
|
* Nota: Dolibarr no tiene endpoint de logout, los tokens expiran solos
|
|
|
|
|
*/
|
|
|
|
|
export async function logout(): Promise<void> {
|
|
|
|
|
// Limpiar datos de autenticación
|
|
|
|
|
clearAuthData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Guarda los datos de autenticación en localStorage y cookie
|
|
|
|
|
*/
|
|
|
|
|
function saveAuthData(token: string, user?: AuthUser): void {
|
|
|
|
|
if (typeof window === "undefined") return;
|
|
|
|
|
|
|
|
|
|
localStorage.setItem(AUTH_TOKEN_KEY, token);
|
|
|
|
|
|
|
|
|
|
if (user) {
|
|
|
|
|
localStorage.setItem(AUTH_USER_KEY, JSON.stringify(user));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Limpia los datos de autenticación
|
|
|
|
|
*/
|
|
|
|
|
function clearAuthData(): void {
|
|
|
|
|
if (typeof window === "undefined") return;
|
|
|
|
|
|
|
|
|
|
localStorage.removeItem(AUTH_TOKEN_KEY);
|
|
|
|
|
localStorage.removeItem(AUTH_USER_KEY);
|
|
|
|
|
|
|
|
|
|
// Limpiar cookie
|
|
|
|
|
document.cookie = `${AUTH_TOKEN_KEY}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Obtiene el token guardado
|
|
|
|
|
*/
|
|
|
|
|
export function getStoredToken(): string | null {
|
|
|
|
|
if (typeof window === "undefined") return null;
|
|
|
|
|
return localStorage.getItem(AUTH_TOKEN_KEY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Obtiene el usuario guardado
|
|
|
|
|
*/
|
|
|
|
|
export function getStoredUser(): AuthUser | null {
|
|
|
|
|
if (typeof window === "undefined") return null;
|
|
|
|
|
|
|
|
|
|
const userJson = localStorage.getItem(AUTH_USER_KEY);
|
|
|
|
|
if (!userJson) return null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(userJson);
|
|
|
|
|
} catch {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Verifica si el token actual es válido
|
|
|
|
|
*/
|
|
|
|
|
export async function verifyToken(): Promise<boolean> {
|
|
|
|
|
const token = getStoredToken();
|
|
|
|
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const apiUrl = getApiUrl();
|
|
|
|
|
|
|
|
|
|
// Verificar token haciendo una llamada simple a la API
|
|
|
|
|
const response = await fetch(`${apiUrl}/users/info?DOLAPIKEY=${token}`, {
|
|
|
|
|
headers: {
|
|
|
|
|
"Accept": "application/json",
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
// Token inválido o expirado
|
|
|
|
|
clearAuthData();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Obtiene el estado de autenticación actual
|
|
|
|
|
*/
|
|
|
|
|
export function getAuthState(): AuthState {
|
|
|
|
|
const token = getStoredToken();
|
|
|
|
|
const user = getStoredUser();
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
isAuthenticated: !!token,
|
|
|
|
|
user,
|
|
|
|
|
token,
|
|
|
|
|
};
|
|
|
|
|
}
|