fix(turnero): IP real con X-Forwarded-For; chat back solo para recepcionista

- App.php: clientIp() público resuelve X-Forwarded-For/X-Real-IP
- module.php: usa el mismo IP resolution en sidebar de recepcionista
- chat.php: botón Menú regresa al desk solo si rol=recepcionista con IP
  registrada; bacteriólogo no aplica lógica de IP en este flujo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-08 08:53:42 -05:00
co-authored by Claude Sonnet 4.6
parent 6f56734b53
commit 1f2198225c
3 changed files with 29 additions and 18 deletions
+9 -2
View File
@@ -125,6 +125,13 @@ class App
// ─── Helpers de rol ───────────────────────────────────────────────────── // ─── Helpers de rol ─────────────────────────────────────────────────────
/** IP real del cliente, soporta proxy con X-Forwarded-For. */
public static function clientIp(): string
{
$raw = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
return trim(explode(',', $raw)[0]);
}
/** /**
* Devuelve el lugar_id de recepción asignado a la IP del cliente, o null si no está registrada. * Devuelve el lugar_id de recepción asignado a la IP del cliente, o null si no está registrada.
*/ */
@@ -137,7 +144,7 @@ class App
JOIN turnero_lugares l ON l.id = d.lugar_id JOIN turnero_lugares l ON l.id = d.lugar_id
WHERE d.ip = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1" WHERE d.ip = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1"
); );
$st->execute([$_SERVER['REMOTE_ADDR'] ?? '']); $st->execute([self::clientIp()]);
$id = $st->fetchColumn(); $id = $st->fetchColumn();
return $id ? (int)$id : null; return $id ? (int)$id : null;
} catch (\Throwable $_) { } catch (\Throwable $_) {
@@ -155,7 +162,7 @@ class App
$base = '/erp.php?m=turnero&v=lugar&lugar_id='; $base = '/erp.php?m=turnero&v=lugar&lugar_id=';
try { try {
$pdo = Database::getInstance()->getConnection(); $pdo = Database::getInstance()->getConnection();
$ip = $_SERVER['REMOTE_ADDR'] ?? ''; $ip = self::clientIp();
// ¿IP registrada? // ¿IP registrada?
$dev = $pdo->prepare( $dev = $pdo->prepare(
"SELECT lugar_id FROM turnero_dispositivos WHERE ip = ? AND activo = 1 LIMIT 1" "SELECT lugar_id FROM turnero_dispositivos WHERE ip = ? AND activo = 1 LIMIT 1"
+3 -2
View File
@@ -9,6 +9,7 @@ $_trAdminRoles = ['superadmin', 'admin', 'supervisor'];
$_trIsAdmin = in_array($_trRole, $_trAdminRoles, true); $_trIsAdmin = in_array($_trRole, $_trAdminRoles, true);
$_trIsRecep = $_trRole === 'recepcionista'; $_trIsRecep = $_trRole === 'recepcionista';
$_trIsBacte = $_trRole === 'bacteriologo'; $_trIsBacte = $_trRole === 'bacteriologo';
$_trClientIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);
try { try {
$_trPdo = Database::getInstance()->getConnection(); $_trPdo = Database::getInstance()->getConnection();
@@ -41,7 +42,7 @@ if ($_trIsRecep) {
JOIN turnero_lugares l ON l.id = d.lugar_id JOIN turnero_lugares l ON l.id = d.lugar_id
WHERE d.ip = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1" WHERE d.ip = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1"
); );
$_trRecepIpRow->execute([$_SERVER['REMOTE_ADDR'] ?? '']); $_trRecepIpRow->execute([$_trClientIp]);
$_trRecepIpDesk = $_trRecepIpRow->fetchColumn() ?: null; $_trRecepIpDesk = $_trRecepIpRow->fetchColumn() ?: null;
} catch (\Throwable $_) {} } catch (\Throwable $_) {}
@@ -77,7 +78,7 @@ if ($_trIsRecep) {
$_trIpRow = $_trPdo->prepare( $_trIpRow = $_trPdo->prepare(
"SELECT lugar_id FROM turnero_dispositivos WHERE ip = ? AND activo = 1 LIMIT 1" "SELECT lugar_id FROM turnero_dispositivos WHERE ip = ? AND activo = 1 LIMIT 1"
); );
$_trIpRow->execute([$_SERVER['REMOTE_ADDR'] ?? '']); $_trIpRow->execute([$_trClientIp]);
$_trIpLugar = $_trIpRow->fetchColumn() ?: null; $_trIpLugar = $_trIpRow->fetchColumn() ?: null;
} catch (\Throwable $_) {} } catch (\Throwable $_) {}
+17 -14
View File
@@ -10,21 +10,24 @@ if (!isUserLoggedIn()) { header('Location: ' . BASE_URL . 'login.php'); exit; }
$turneroPhoneId = getConfigFromDB('whatsapp_phone_number_id_turnero', ''); $turneroPhoneId = getConfigFromDB('whatsapp_phone_number_id_turnero', '');
// Determinar URL de regreso según IP del cliente // URL de regreso: recepcionista con IP registrada → su desk; resto → dashboard
$_chatBackUrl = BASE_URL . 'erp.php?m=turnero&v=dashboard'; $_chatBackUrl = BASE_URL . 'erp.php?m=turnero&v=dashboard';
try { if (($_SESSION['admin_user']['role'] ?? '') === 'recepcionista') {
$pdo = Database::getInstance()->getConnection(); try {
$st = $pdo->prepare( $pdo = Database::getInstance()->getConnection();
"SELECT d.lugar_id FROM turnero_dispositivos d $chatIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);
JOIN turnero_lugares l ON l.id = d.lugar_id $st = $pdo->prepare(
WHERE d.ip = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1" "SELECT d.lugar_id FROM turnero_dispositivos d
); JOIN turnero_lugares l ON l.id = d.lugar_id
$st->execute([$_SERVER['REMOTE_ADDR'] ?? '']); WHERE d.ip = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1"
$deskId = $st->fetchColumn(); );
if ($deskId) { $st->execute([$chatIp]);
$_chatBackUrl = BASE_URL . 'erp.php?m=turnero&v=recepcion&desk_id=' . (int)$deskId; $lugId = $st->fetchColumn();
} if ($lugId) {
} catch (\Throwable $_) {} $_chatBackUrl = BASE_URL . 'erp.php?m=turnero&v=recepcion&desk_id=' . (int)$lugId;
}
} catch (\Throwable $_) {}
}
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="es"> <html lang="es">