- Implemented UnitsEquivalenceModal for managing unit equivalences. - Added unit equivalence store and service for CRUD operations. - Integrated unit equivalence management into Units.vue component. - Created new HTML page for unit equivalence management layout. - Defined TypeScript interfaces for unit equivalences. - Enhanced user permissions for managing unit equivalences.
246 lines
7.2 KiB
TypeScript
246 lines
7.2 KiB
TypeScript
import api from '../../../services/api';
|
|
import type { User, LoginCredentials, Role } from '../composables/useAuth';
|
|
|
|
export interface LoginResponse {
|
|
status: string;
|
|
data: {
|
|
user: User;
|
|
userRoles: Role[];
|
|
token: string;
|
|
};
|
|
}
|
|
|
|
export interface RegisterData {
|
|
name: string;
|
|
email: string;
|
|
password: string;
|
|
password_confirmation: string;
|
|
}
|
|
|
|
interface SessionRefreshData {
|
|
user: User;
|
|
roles: Role[];
|
|
permissions: string[];
|
|
}
|
|
|
|
class AuthService {
|
|
private extractArrayFromResponse(payload: unknown): unknown[] {
|
|
if (Array.isArray(payload)) {
|
|
return payload;
|
|
}
|
|
|
|
if (!payload || typeof payload !== 'object') {
|
|
return [];
|
|
}
|
|
|
|
const root = payload as Record<string, unknown>;
|
|
|
|
if (Array.isArray(root.data)) {
|
|
return root.data;
|
|
}
|
|
|
|
if (root.data && typeof root.data === 'object') {
|
|
const nested = root.data as Record<string, unknown>;
|
|
|
|
if (Array.isArray(nested.permissions)) {
|
|
return nested.permissions;
|
|
}
|
|
|
|
if (Array.isArray(nested.roles)) {
|
|
return nested.roles;
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
private normalizePermissions(payload: unknown): string[] {
|
|
const items = this.extractArrayFromResponse(payload);
|
|
|
|
return items
|
|
.map((item) => {
|
|
if (typeof item === 'string') {
|
|
return item;
|
|
}
|
|
|
|
if (item && typeof item === 'object' && typeof (item as Record<string, unknown>).name === 'string') {
|
|
return (item as Record<string, string>).name;
|
|
}
|
|
|
|
return null;
|
|
})
|
|
.filter((permission): permission is string => Boolean(permission));
|
|
}
|
|
|
|
private normalizeRoles(payload: unknown): Role[] {
|
|
const items = this.extractArrayFromResponse(payload);
|
|
|
|
return items.filter((item): item is Role => {
|
|
if (!item || typeof item !== 'object') {
|
|
return false;
|
|
}
|
|
|
|
const role = item as Partial<Role>;
|
|
return typeof role.name === 'string';
|
|
});
|
|
}
|
|
|
|
private resolveAuthError(error: any, fallback = 'Error en autenticación'): never {
|
|
if (error.response?.status === 422 && error.response?.data?.errors) {
|
|
const firstError = Object.values(error.response.data.errors)?.[0] as string[] | undefined;
|
|
throw new Error(firstError?.[0] || error.response.data.message || fallback);
|
|
}
|
|
|
|
if (error.response?.status === 429) {
|
|
throw new Error('Demasiados intentos de inicio de sesión. Intenta nuevamente en un minuto.');
|
|
}
|
|
|
|
throw new Error(error.response?.data?.message || fallback);
|
|
}
|
|
|
|
/**
|
|
* Iniciar sesión
|
|
*/
|
|
async login(credentials: LoginCredentials): Promise<{ user: User; token: string }> {
|
|
try {
|
|
const response = await api.post<LoginResponse>('/api/auth/login', {
|
|
email: credentials.email,
|
|
password: credentials.password
|
|
});
|
|
|
|
// Retornar solo user y token para mantener compatibilidad con useAuth
|
|
return {
|
|
user: response.data.data.user,
|
|
token: response.data.data.token
|
|
};
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al iniciar sesión');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registrar nuevo usuario
|
|
*/
|
|
async register(data: RegisterData): Promise<LoginResponse> {
|
|
try {
|
|
const response = await api.post<LoginResponse>('/api/auth/register', data);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al registrar usuario');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cerrar sesión
|
|
*/
|
|
async logout(): Promise<void> {
|
|
try {
|
|
await api.post('/api/auth/logout');
|
|
} catch (error: any) {
|
|
console.error('Error al cerrar sesión:', error);
|
|
// No lanzar error para que el logout local continúe aunque falle el backend
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtener usuario actual
|
|
*/
|
|
async getCurrentUser(): Promise<User> {
|
|
try {
|
|
const response = await api.get<User>('/api/user');
|
|
return response.data;
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al obtener usuario');
|
|
}
|
|
}
|
|
|
|
async getUserRoles(): Promise<Role[]> {
|
|
try {
|
|
const response = await api.get('/api/user/roles');
|
|
return this.normalizeRoles(response.data);
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al obtener roles del usuario');
|
|
}
|
|
}
|
|
|
|
async getUserPermissions(): Promise<string[]> {
|
|
try {
|
|
const response = await api.get('/api/user/permissions');
|
|
console.log('User permissions response:', response);
|
|
|
|
return this.normalizePermissions(response.data);
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al obtener permisos del usuario');
|
|
}
|
|
}
|
|
|
|
async refreshSession(): Promise<SessionRefreshData> {
|
|
try {
|
|
const [currentUser, roles, permissions] = await Promise.all([
|
|
this.getCurrentUser(),
|
|
this.getUserRoles(),
|
|
this.getUserPermissions(),
|
|
]);
|
|
|
|
return {
|
|
user: currentUser,
|
|
roles,
|
|
permissions,
|
|
};
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al sincronizar sesión');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Actualizar perfil de usuario
|
|
*/
|
|
async updateProfile(data: Partial<User>): Promise<User> {
|
|
try {
|
|
const response = await api.put<User>('/api/auth/profile', data);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al actualizar perfil');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cambiar contraseña
|
|
*/
|
|
async changePassword(currentPassword: string, newPassword: string): Promise<void> {
|
|
try {
|
|
await api.post('/api/auth/change-password', {
|
|
current_password: currentPassword,
|
|
new_password: newPassword
|
|
});
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al cambiar contraseña');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Solicitar recuperación de contraseña
|
|
*/
|
|
async forgotPassword(email: string): Promise<void> {
|
|
try {
|
|
await api.post('/api/auth/forgot-password', { email });
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al solicitar recuperación');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resetear contraseña con token
|
|
*/
|
|
async resetPassword(token: string, password: string): Promise<void> {
|
|
try {
|
|
await api.post('/api/auth/reset-password', { token, password });
|
|
} catch (error: any) {
|
|
this.resolveAuthError(error, 'Error al resetear contraseña');
|
|
}
|
|
}
|
|
}
|
|
|
|
export const authService = new AuthService();
|
|
export default authService;
|