turnero: restricción por token de navegador en lugar de IP pública
- Token UUID generado en localStorage del navegador, persistido como cookie - turnero_dispositivos.token: nueva columna para identificar equipos - recepcion.php y lugar.php: chequean token (cookie) primero, IP como fallback - configuracion.php: banner con token del equipo actual + botón "Registrar este equipo" - save_dispositivo.php: acepta campo token al crear/editar dispositivo - Migración: 20260709_dispositivos_token.sql Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
9651f14302
commit
d5b6de1f46
@@ -244,68 +244,40 @@ if (class_exists('ModuleRegistry', false)) {
|
||||
<?= htmlspecialchars($_user_name, ENT_QUOTES, 'UTF-8') ?>
|
||||
</div>
|
||||
<div class="sidebar-user-role"><?= htmlspecialchars($_role_label, ENT_QUOTES, 'UTF-8') ?></div>
|
||||
<div style="font-size:.68rem;color:#94a3b8;margin-top:3px;letter-spacing:.02em">
|
||||
<i class="fas fa-server me-1"></i>
|
||||
Servidor ve: <code style="font-size:.68rem;color:#64748b"><?= htmlspecialchars($_SERVER['REMOTE_ADDR'] ?? '') ?></code>
|
||||
</div>
|
||||
<?php if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])): ?>
|
||||
<div style="font-size:.65rem;color:#b0bec5;letter-spacing:.02em">
|
||||
Forwarded: <code style="font-size:.65rem;color:#b0bec5"><?= htmlspecialchars(trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0])) ?></code>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div id="sb-local-ip" style="font-size:.68rem;color:#94a3b8;margin-top:1px;letter-spacing:.02em;cursor:pointer" title="Clic para ver cómo encontrar tu IP local" onclick="sbMostrarAyudaIP()">
|
||||
<i class="fas fa-laptop me-1"></i>LAN: <span id="sb-ip-val">detectando…</span>
|
||||
<div style="font-size:.68rem;color:#94a3b8;margin-top:3px;letter-spacing:.02em" title="ID único de este navegador">
|
||||
<i class="fas fa-fingerprint me-1"></i><span id="sb-device-token" style="font-family:monospace">—</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<!-- Modal ayuda IP -->
|
||||
<div id="sb-modal-ip" style="display:none;position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.5);align-items:center;justify-content:center">
|
||||
<div style="background:#fff;border-radius:12px;padding:24px;max-width:340px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,.2)">
|
||||
<h6 style="margin:0 0 12px;font-size:.95rem"><i class="fas fa-network-wired me-2 text-primary"></i>Tu IP en la red local</h6>
|
||||
<p style="font-size:.82rem;color:#555;margin:0 0 10px">El navegador no puede leerla directamente. Para encontrarla:</p>
|
||||
<div style="font-size:.8rem;background:#f1f5f9;border-radius:8px;padding:10px 12px;line-height:1.8">
|
||||
<strong>Windows:</strong> <code>Win + R</code> → <code>cmd</code> → <code>ipconfig</code><br>
|
||||
<strong>Mac:</strong> Preferencias → Red → IP<br>
|
||||
<strong>Android:</strong> Ajustes → WiFi → detalles de la red
|
||||
</div>
|
||||
<button onclick="document.getElementById('sb-modal-ip').style.display='none'" style="margin-top:14px;width:100%;padding:7px;border:none;border-radius:7px;background:#1565c0;color:#fff;font-size:.85rem;cursor:pointer">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function sbMostrarAyudaIP() {
|
||||
document.getElementById('sb-modal-ip').style.display = 'flex';
|
||||
function getOrCreateToken() {
|
||||
var t = localStorage.getItem('turnero_device_token');
|
||||
if (!t) {
|
||||
t = typeof crypto !== 'undefined' && crypto.randomUUID
|
||||
? crypto.randomUUID()
|
||||
: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16 | 0;
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
localStorage.setItem('turnero_device_token', t);
|
||||
}
|
||||
// Setear como cookie para que PHP lo lea en cada request
|
||||
document.cookie = 'turnero_token=' + t + '; path=/; SameSite=Strict; max-age=31536000';
|
||||
return t;
|
||||
}
|
||||
window.sbMostrarAyudaIP = sbMostrarAyudaIP;
|
||||
|
||||
function detectLocalIP() {
|
||||
var el = document.getElementById('sb-ip-val');
|
||||
var RTC = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
|
||||
if (!RTC) { el.textContent = '(ver ayuda)'; return; }
|
||||
var pc = new RTC({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }), found = false;
|
||||
pc.createDataChannel('');
|
||||
pc.createOffer().then(function(o) { return pc.setLocalDescription(o); }).catch(function(){});
|
||||
pc.onicecandidate = function(e) {
|
||||
if (found || !e || !e.candidate) return;
|
||||
var ips = e.candidate.candidate.match(/\b(\d{1,3}\.){3}\d{1,3}\b/g) || [];
|
||||
ips.forEach(function(ip) {
|
||||
if (found) return;
|
||||
if (/^(192\.168\.|10\.|172\.(1[6-9]|2\d|3[01])\.)/.test(ip)) {
|
||||
found = true;
|
||||
el.textContent = ip;
|
||||
pc.close();
|
||||
}
|
||||
});
|
||||
};
|
||||
setTimeout(function() {
|
||||
if (!found) el.textContent = '(ver ayuda)';
|
||||
}, 4000);
|
||||
function init() {
|
||||
var token = getOrCreateToken();
|
||||
var el = document.getElementById('sb-device-token');
|
||||
if (el) el.textContent = token.substring(0, 8) + '…';
|
||||
// Exponer globalmente para que configuracion.php lo use
|
||||
window._turneroDeviceToken = token;
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', detectLocalIP);
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
detectLocalIP();
|
||||
init();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user