Respaldo automático 2026-06-30 06:20

This commit is contained in:
Servidor Neptuno 2026-06-30 06:20:53 +00:00
parent bde75f61f3
commit 3bf93fa635
3 changed files with 347 additions and 185 deletions

View File

@ -183,3 +183,5 @@ Ideas pendientes, sin compromiso de implementación:
[PolyForm Noncommercial 1.0.0](./LICENSE) — código fuente disponible para cualquier uso **no comercial**: estudio personal, proyectos propios sin fines de lucro, instituciones educativas, organizaciones de salud pública, ONGs y organismos de gobierno. Para uso comercial (ofrecerlo como servicio, instalarlo a terceros a cambio de un pago, integrarlo en un producto comercial) se necesita una licencia aparte — abrí un issue o contactá a quien mantiene este repositorio. [PolyForm Noncommercial 1.0.0](./LICENSE) — código fuente disponible para cualquier uso **no comercial**: estudio personal, proyectos propios sin fines de lucro, instituciones educativas, organizaciones de salud pública, ONGs y organismos de gobierno. Para uso comercial (ofrecerlo como servicio, instalarlo a terceros a cambio de un pago, integrarlo en un producto comercial) se necesita una licencia aparte — abrí un issue o contactá a quien mantiene este repositorio.
--- ---
Construido de forma iterativa junto a [Claude](https://claude.ai) (Anthropic).

158
config/config.example.php Normal file
View File

@ -0,0 +1,158 @@
<?php
/**
* ============================================================
* CONFIGURACIÓN DE BASE DE DATOS
* ============================================================
* Completá estos 4 datos con los que te dio cPanel al crear
* la base de datos MySQL (sección "Bases de datos MySQL").
*
* DB_HOST -> casi siempre es "localhost"
* DB_NAME -> el nombre completo, ej: "usuario_legajos"
* DB_USER -> el usuario completo, ej: "usuario_admin"
* DB_PASS -> la contraseña que elegiste para ese usuario
* ============================================================
*/
define('DB_HOST', 'localhost');
define('DB_NAME', 'TU_USUARIO_legajos');
define('DB_USER', 'TU_USUARIO_admin');
define('DB_PASS', 'TU_CONTRASEÑA_AQUI');
/**
* Clave secreta usada para firmar la sesión de acceso.
* Cambiala por cualquier texto largo y random, una sola vez,
* antes de subir el sitio. No la compartas.
*/
define('APP_SECRET', 'cambiar-esto-por-un-texto-largo-y-aleatorio-unico');
// ------------------------------------------------------------
// No es necesario tocar nada debajo de esta línea
// ------------------------------------------------------------
function obtenerConexion() {
static $pdo = null;
if ($pdo === null) {
try {
$dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4';
$pdo = new PDO($dsn, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
} catch (PDOException $e) {
http_response_code(500);
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'ok' => false,
'error' => 'No se pudo conectar a la base de datos. Revisá config.php con los datos reales de tu cPanel.',
'detalle' => $e->getMessage()
]);
exit;
}
}
return $pdo;
}
session_start();
function requiereSesion() {
if (empty($_SESSION['autenticado']) || $_SESSION['autenticado'] !== true) {
http_response_code(401);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'error' => 'Sesión no válida. Iniciá sesión nuevamente.']);
exit;
}
}
/**
* Corta la ejecución con 403 si el usuario logueado no es "profesional".
* Usar en cualquier endpoint que exponga datos clínicos sensibles.
*/
function requiereRolProfesional() {
requiereSesion();
if (($_SESSION['rol'] ?? '') !== 'profesional') {
http_response_code(403);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'error' => 'Tu usuario no tiene permiso para acceder a esta información clínica.']);
exit;
}
}
function rolActual() {
return $_SESSION['rol'] ?? null;
}
function esProfesional() {
return rolActual() === 'profesional';
}
function esDesarrollador() {
return rolActual() === 'desarrollador';
}
/**
* Corta la ejecución con 403 si quien está logueado no es el
* desarrollador. Se usa para altas/bajas de usuarios y sedes.
*/
function requiereDesarrollador() {
requiereSesion();
if (!esDesarrollador()) {
http_response_code(403);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'error' => 'Esta acción solo puede hacerla el desarrollador del sistema.']);
exit;
}
}
/**
* El "profesional activo" es el dueño de los pacientes que se
* deben ver en esta sesión. Si quien entró es un profesional,
* es su propio ID. Si es una administrativa, es el profesional
* que eligió representar al momento de loguearse (guardado en
* sesión como profesional_activo_id).
*/
function idProfesionalActivo() {
if (esProfesional()) {
return $_SESSION['usuario_id'] ?? null;
}
return $_SESSION['profesional_activo_id'] ?? null;
}
/**
* Corta la ejecución con 400 si no hay un profesional activo
* resuelto en la sesión (no debería pasar en uso normal, pero
* protege contra estados de sesión inconsistentes).
*/
function requiereProfesionalActivo() {
requiereSesion();
if (!idProfesionalActivo()) {
http_response_code(400);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'error' => 'No se pudo determinar a qué profesional pertenecen estos datos. Volvé a iniciar sesión.']);
exit;
}
}
/**
* Registra una acción en el historial de cambios (auditoría).
* $accion: 'crear' | 'editar' | 'eliminar' | etc.
* $entidad: 'paciente' | 'sesion' | 'cita' | etc.
*/
function registrarAuditoria($pdo, $accion, $entidad, $entidadId, $descripcion = null) {
try {
$stmt = $pdo->prepare('
INSERT INTO historial_cambios (usuario_id, usuario_nombre, accion, entidad, entidad_id, descripcion)
VALUES (?, ?, ?, ?, ?, ?)
');
$stmt->execute([
$_SESSION['usuario_id'] ?? null,
$_SESSION['nombre_usuario'] ?? null,
$accion,
$entidad,
$entidadId,
$descripcion,
]);
} catch (Exception $e) {
// La auditoría nunca debe romper la operación principal.
}
}

View File

@ -65,6 +65,7 @@ CREATE TABLE IF NOT EXISTS usuarios_sedes (
CREATE TABLE IF NOT EXISTS profesionales_legajos ( CREATE TABLE IF NOT EXISTS profesionales_legajos (
id INT PRIMARY KEY AUTO_INCREMENT, id INT PRIMARY KEY AUTO_INCREMENT,
usuario_id INT NOT NULL UNIQUE, usuario_id INT NOT NULL UNIQUE,
numero_legajo VARCHAR(20) NULL UNIQUE,
titulo ENUM('Dr.','Dra.','Lic.','Tec.','Mg.','Prof.','Otro') NOT NULL DEFAULT 'Dr.', titulo ENUM('Dr.','Dra.','Lic.','Tec.','Mg.','Prof.','Otro') NOT NULL DEFAULT 'Dr.',
nombre VARCHAR(80) NOT NULL, nombre VARCHAR(80) NOT NULL,
apellido VARCHAR(80) NOT NULL, apellido VARCHAR(80) NOT NULL,
@ -74,6 +75,7 @@ CREATE TABLE IF NOT EXISTS profesionales_legajos (
especialidad VARCHAR(150) NULL, especialidad VARCHAR(150) NULL,
email VARCHAR(150) NULL, email VARCHAR(150) NULL,
telefono VARCHAR(40) NULL, telefono VARCHAR(40) NULL,
firma_digital MEDIUMTEXT NULL,
creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP, creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
actualizado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, actualizado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (usuario_id) REFERENCES usuarios(id) ON DELETE CASCADE FOREIGN KEY (usuario_id) REFERENCES usuarios(id) ON DELETE CASCADE