396 lines
10 KiB
TypeScript
396 lines
10 KiB
TypeScript
import { Injectable } from '@angular/core';
|
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
import { Observable, BehaviorSubject, tap, catchError, throwError } from 'rxjs';
|
|
import { Router } from '@angular/router';
|
|
import { JobResponse } from './job-types';
|
|
import { API_BASE_URL } from '../config';
|
|
|
|
export interface User {
|
|
id_usuario: number;
|
|
username: string;
|
|
nombre_completo: string;
|
|
nombre_rol: string;
|
|
sedes?: Array<{
|
|
codigo_establecimiento: string;
|
|
nombre_establecimiento: string;
|
|
}>;
|
|
}
|
|
|
|
export interface LoginRequest {
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface LoginResponse {
|
|
token: string;
|
|
usuario: User;
|
|
}
|
|
|
|
export interface RegisterRequest {
|
|
username: string;
|
|
email: string;
|
|
password: string;
|
|
nombre_completo: string;
|
|
id_rol: number;
|
|
sedes?: string[];
|
|
}
|
|
|
|
export interface ActualizarUsuarioPayload {
|
|
username?: string;
|
|
password?: string;
|
|
email?: string;
|
|
nombre_completo?: string;
|
|
id_rol?: number;
|
|
}
|
|
|
|
/* Interfaz Rol para la pantalla de usuarios */
|
|
export interface Rol {
|
|
id_rol: number;
|
|
nombre_rol: string;
|
|
descripcion?: string | null;
|
|
}
|
|
|
|
export interface EstadisticasAutorizacionesDia {
|
|
fecha: string;
|
|
total: number;
|
|
autorizadas: number;
|
|
no_autorizadas: number;
|
|
pendientes: number;
|
|
}
|
|
|
|
export interface EstadisticasAutorizaciones {
|
|
rango: { inicio: string; fin: string };
|
|
resumen: {
|
|
autorizadas: number;
|
|
no_autorizadas: number;
|
|
pendientes: number;
|
|
total: number;
|
|
};
|
|
dias: EstadisticasAutorizacionesDia[];
|
|
}
|
|
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class AuthService {
|
|
private readonly API_URL = API_BASE_URL;
|
|
private readonly TOKEN_KEY = 'auth_token';
|
|
private readonly USER_KEY = 'current_user';
|
|
|
|
private currentUserSubject = new BehaviorSubject<User | null>(null);
|
|
public currentUser$ = this.currentUserSubject.asObservable();
|
|
|
|
private isAuthenticatedSubject = new BehaviorSubject<boolean>(false);
|
|
public isAuthenticated$ = this.isAuthenticatedSubject.asObservable();
|
|
private logoutTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
constructor(
|
|
private http: HttpClient,
|
|
private router: Router
|
|
) {
|
|
this.initializeAuth();
|
|
}
|
|
|
|
private initializeAuth(): void {
|
|
const token = this.getToken();
|
|
const user = this.getUserFromStorage();
|
|
|
|
if (token && user) {
|
|
this.currentUserSubject.next(user);
|
|
this.isAuthenticatedSubject.next(true);
|
|
this.scheduleTokenExpiry(token);
|
|
} else {
|
|
this.clearAuth();
|
|
}
|
|
}
|
|
|
|
// =========== LOGIN ===========
|
|
|
|
login(credentials: LoginRequest): Observable<LoginResponse> {
|
|
return this.http.post<LoginResponse>(`${this.API_URL}/auth/login`, credentials).pipe(
|
|
tap(response => {
|
|
this.setAuth(response.token, response.usuario);
|
|
}),
|
|
catchError((error: any) => {
|
|
console.error('Error en login:', error);
|
|
return throwError(() => error);
|
|
})
|
|
);
|
|
}
|
|
|
|
// =========== REGISTER (SOLO ADMIN) ===========
|
|
|
|
register(userData: RegisterRequest): Observable<any> {
|
|
const headers = this.getAuthHeaders();
|
|
return this.http.post(`${this.API_URL}/auth/register`, userData, { headers }).pipe(
|
|
catchError((error: any) => {
|
|
console.error('Error en register:', error);
|
|
return throwError(() => error);
|
|
})
|
|
);
|
|
}
|
|
|
|
// =========== LOGOUT ===========
|
|
|
|
logout(): void {
|
|
this.clearAuth();
|
|
this.router.navigate(['/login']);
|
|
}
|
|
|
|
// =========== VERIFY TOKEN ===========
|
|
|
|
verifyToken(): Observable<{ usuario: User }> {
|
|
const headers = this.getAuthHeaders();
|
|
return this.http.get<{ usuario: User }>(`${this.API_URL}/auth/verify`, { headers }).pipe(
|
|
tap(response => {
|
|
this.updateUser(response.usuario);
|
|
})
|
|
);
|
|
}
|
|
|
|
// =========== HELPERS DE AUTH ===========
|
|
|
|
getToken(): string | null {
|
|
return localStorage.getItem(this.TOKEN_KEY);
|
|
}
|
|
|
|
private setAuth(token: string, user: User): void {
|
|
localStorage.setItem(this.TOKEN_KEY, token);
|
|
localStorage.setItem(this.USER_KEY, JSON.stringify(user));
|
|
this.currentUserSubject.next(user);
|
|
this.isAuthenticatedSubject.next(true);
|
|
this.scheduleTokenExpiry(token);
|
|
}
|
|
|
|
private clearAuth(): void {
|
|
this.clearLogoutTimer();
|
|
localStorage.removeItem(this.TOKEN_KEY);
|
|
localStorage.removeItem(this.USER_KEY);
|
|
this.currentUserSubject.next(null);
|
|
this.isAuthenticatedSubject.next(false);
|
|
}
|
|
|
|
private scheduleTokenExpiry(token: string): void {
|
|
const exp = this.getTokenExpiry(token);
|
|
if (!exp) {
|
|
return;
|
|
}
|
|
const timeoutMs = exp - Date.now();
|
|
if (timeoutMs <= 0) {
|
|
this.logout();
|
|
return;
|
|
}
|
|
this.clearLogoutTimer();
|
|
this.logoutTimeoutId = setTimeout(() => {
|
|
this.logout();
|
|
}, timeoutMs);
|
|
}
|
|
|
|
private clearLogoutTimer(): void {
|
|
if (this.logoutTimeoutId) {
|
|
clearTimeout(this.logoutTimeoutId);
|
|
this.logoutTimeoutId = null;
|
|
}
|
|
}
|
|
|
|
private getTokenExpiry(token: string): number | null {
|
|
try {
|
|
const payload = token.split('.')[1];
|
|
if (!payload) return null;
|
|
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
const padded = base64.padEnd(base64.length + (4 - (base64.length % 4)) % 4, '=');
|
|
const decoded = JSON.parse(atob(padded));
|
|
if (!decoded?.exp) return null;
|
|
return decoded.exp * 1000;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private updateUser(user: User): void {
|
|
localStorage.setItem(this.USER_KEY, JSON.stringify(user));
|
|
this.currentUserSubject.next(user);
|
|
}
|
|
|
|
private getUserFromStorage(): User | null {
|
|
const userStr = localStorage.getItem(this.USER_KEY);
|
|
return userStr ? JSON.parse(userStr) : null;
|
|
}
|
|
|
|
getAuthHeaders(): HttpHeaders {
|
|
const token = this.getToken();
|
|
return new HttpHeaders({
|
|
'Authorization': `Bearer ${token}`,
|
|
'Content-Type': 'application/json'
|
|
});
|
|
}
|
|
|
|
getCurrentUser(): User | null {
|
|
return this.currentUserSubject.value;
|
|
}
|
|
|
|
isLoggedIn(): boolean {
|
|
return this.isAuthenticatedSubject.value;
|
|
}
|
|
|
|
hasRole(role: string): boolean {
|
|
const user = this.getCurrentUser();
|
|
return user ? user.nombre_rol === role : false;
|
|
}
|
|
|
|
isAdministrador(): boolean {
|
|
return this.hasRole('administrador');
|
|
}
|
|
|
|
isAdministrativoSede(): boolean {
|
|
return this.hasRole('administrativo_sede');
|
|
}
|
|
|
|
puedeCargarPacientes(): boolean {
|
|
return this.isAdministrador();
|
|
}
|
|
|
|
puedeDescargarPdfs(): boolean {
|
|
const user = this.getCurrentUser();
|
|
return !!user && (user.nombre_rol === 'administrador' || user.nombre_rol === 'administrativo_sede');
|
|
}
|
|
|
|
puedeVerTodasAutorizaciones(): boolean {
|
|
return this.isAdministrador();
|
|
}
|
|
|
|
puedeGenerarAutorizaciones(): boolean {
|
|
const user = this.getCurrentUser();
|
|
return !!user && (user.nombre_rol === 'administrador' || user.nombre_rol === 'administrativo_sede');
|
|
}
|
|
|
|
tieneAccesoAEstablecimiento(codigoEstablecimiento: string): boolean {
|
|
const user = this.getCurrentUser();
|
|
if (!user) return false;
|
|
|
|
if (user.nombre_rol === 'administrador') return true;
|
|
|
|
if (user.nombre_rol === 'administrativo_sede' && user.sedes) {
|
|
return user.sedes.some(sede => sede.codigo_establecimiento === codigoEstablecimiento);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// =========== ADMIN: USUARIOS Y REPORTES ===========
|
|
|
|
getUsuarios(limit = 200, offset = 0): Observable<any[]> {
|
|
const headers = this.getAuthHeaders();
|
|
const params = { limit, offset };
|
|
return this.http.get<any[]>(`${this.API_URL}/usuarios`, { headers, params });
|
|
}
|
|
|
|
cambiarEstadoUsuario(idUsuario: number, activo: boolean): Observable<any> {
|
|
const headers = this.getAuthHeaders();
|
|
return this.http.patch(
|
|
`${this.API_URL}/usuarios/${idUsuario}/estado`,
|
|
{ activo },
|
|
{ headers }
|
|
);
|
|
}
|
|
|
|
actualizarUsuario(idUsuario: number, payload: ActualizarUsuarioPayload): Observable<any> {
|
|
const headers = this.getAuthHeaders();
|
|
return this.http.patch(
|
|
`${this.API_URL}/usuarios/${idUsuario}`,
|
|
payload,
|
|
{ headers }
|
|
);
|
|
}
|
|
|
|
// Alias usado en usuarios.ts
|
|
actualizarEstadoUsuario(idUsuario: number, activo: boolean): Observable<any> {
|
|
return this.cambiarEstadoUsuario(idUsuario, activo);
|
|
}
|
|
|
|
// Obtener autorizaciones por fecha (solo administradores)
|
|
getAutorizacionesPorFecha(
|
|
fechaInicio: string,
|
|
fechaFin: string,
|
|
limit = 100000,
|
|
offset = 0,
|
|
establecimiento?: string,
|
|
ambito?: string,
|
|
ips?: string
|
|
): Observable<any[]> {
|
|
const headers = this.getAuthHeaders();
|
|
const params: any = { fecha_inicio: fechaInicio, fecha_fin: fechaFin, limit, offset };
|
|
if (establecimiento) {
|
|
params.establecimiento = establecimiento;
|
|
}
|
|
if (ambito) {
|
|
params.ambito = ambito;
|
|
}
|
|
if (ips) {
|
|
params.ips = ips;
|
|
}
|
|
return this.http.get<any[]>(`${this.API_URL}/autorizaciones-por-fecha`, { headers, params });
|
|
}
|
|
|
|
// Obtener roles desde el backend
|
|
getRoles(): Observable<Rol[]> {
|
|
const headers = this.getAuthHeaders();
|
|
return this.http.get<Rol[]>(`${this.API_URL}/roles`, { headers });
|
|
}
|
|
|
|
getEstadisticasAutorizaciones(
|
|
fechaInicio?: string,
|
|
fechaFin?: string
|
|
): Observable<EstadisticasAutorizaciones> {
|
|
const headers = this.getAuthHeaders();
|
|
const params: any = {};
|
|
if (fechaInicio) params.fecha_inicio = fechaInicio;
|
|
if (fechaFin) params.fecha_fin = fechaFin;
|
|
return this.http.get<EstadisticasAutorizaciones>(
|
|
`${this.API_URL}/autorizaciones-estadisticas`,
|
|
{ headers, params }
|
|
);
|
|
}
|
|
|
|
crearJobPdfAutorizacion(
|
|
numeroAutorizacion: string,
|
|
version?: number
|
|
): Observable<JobResponse> {
|
|
const headers = this.getAuthHeaders();
|
|
const payload: any = { numero_autorizacion: numeroAutorizacion };
|
|
if (typeof version === 'number') {
|
|
payload.version = version;
|
|
}
|
|
return this.http.post<JobResponse>(
|
|
`${this.API_URL}/jobs/autorizacion-pdf`,
|
|
payload,
|
|
{ headers }
|
|
);
|
|
}
|
|
|
|
crearJobZipAutorizaciones(
|
|
fechaInicio: string,
|
|
fechaFin: string,
|
|
establecimiento?: string,
|
|
ambito?: string,
|
|
ips?: string
|
|
): Observable<JobResponse> {
|
|
const headers = this.getAuthHeaders();
|
|
const payload: any = { fecha_inicio: fechaInicio, fecha_fin: fechaFin };
|
|
if (establecimiento) {
|
|
payload.establecimiento = establecimiento;
|
|
}
|
|
if (ambito) {
|
|
payload.ambito = ambito;
|
|
}
|
|
if (ips) {
|
|
payload.ips = ips;
|
|
}
|
|
return this.http.post<JobResponse>(
|
|
`${this.API_URL}/jobs/autorizaciones-zip`,
|
|
payload,
|
|
{ headers }
|
|
);
|
|
}
|
|
}
|