Salud_UT/saludut-inpec/src/app/services/auth.ts
2026-01-05 12:04:41 -05:00

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 }
);
}
}