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
@@ -0,0 +1,5 @@
|
||||
-- Token único por navegador/dispositivo para restricción sin depender de IP pública.
|
||||
-- NULL = dispositivo registrado solo por IP (compatibilidad hacia atrás).
|
||||
ALTER TABLE turnero_dispositivos
|
||||
ADD COLUMN IF NOT EXISTS token VARCHAR(64) DEFAULT NULL,
|
||||
ADD UNIQUE KEY IF NOT EXISTS uq_dispositivo_token (token);
|
||||
@@ -23,22 +23,23 @@ $ip = trim($input['ip'] ?? '');
|
||||
$nombre = trim($input['nombre'] ?? '');
|
||||
$lugarId = (int)($input['lugar_id'] ?? 0);
|
||||
$activo = (int)($input['activo'] ?? 1);
|
||||
$token = trim($input['token'] ?? '') ?: null;
|
||||
|
||||
if ($ip === '') jsonError('La IP es requerida');
|
||||
if ($nombre === '') jsonError('El nombre es requerido');
|
||||
if ($lugarId <= 0) jsonError('Debes asignar un lugar');
|
||||
if ($ip === '' && $token === null) jsonError('Se requiere IP o token de dispositivo');
|
||||
|
||||
if ($id) {
|
||||
$stmt = db()->prepare(
|
||||
'UPDATE turnero_dispositivos SET ip=?, nombre=?, lugar_id=?, activo=? WHERE id=?'
|
||||
'UPDATE turnero_dispositivos SET ip=?, nombre=?, lugar_id=?, activo=?, token=? WHERE id=?'
|
||||
);
|
||||
$stmt->execute([$ip, $nombre, $lugarId, $activo, $id]);
|
||||
$stmt->execute([$ip, $nombre, $lugarId, $activo, $token, $id]);
|
||||
jsonOk(['id' => $id], 'Dispositivo actualizado');
|
||||
} else {
|
||||
$stmt = db()->prepare(
|
||||
'INSERT INTO turnero_dispositivos (ip, nombre, lugar_id, activo) VALUES (?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE nombre=VALUES(nombre), lugar_id=VALUES(lugar_id), activo=VALUES(activo)'
|
||||
'INSERT INTO turnero_dispositivos (ip, nombre, lugar_id, activo, token) VALUES (?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE nombre=VALUES(nombre), lugar_id=VALUES(lugar_id), activo=VALUES(activo), token=VALUES(token)'
|
||||
);
|
||||
$stmt->execute([$ip, $nombre, $lugarId, $activo]);
|
||||
$stmt->execute([$ip, $nombre, $lugarId, $activo, $token]);
|
||||
jsonOk(['id' => (int)db()->lastInsertId()], 'Dispositivo creado');
|
||||
}
|
||||
|
||||
@@ -482,15 +482,15 @@ $tab = $_GET['tab'] ?? 'lugares';
|
||||
<span class="badge bg-secondary-subtle text-secondary"><?= count($dispositivos) ?></span>
|
||||
</div>
|
||||
<p class="text-muted small mb-3">
|
||||
Cada tablet queda bloqueada a su lugar asignado según la IP de red.
|
||||
Si la IP no aparece aquí, el dispositivo verá todos los lugares disponibles.
|
||||
Cada equipo queda bloqueado a su lugar asignado por token de navegador.
|
||||
Si el equipo no aparece aquí, verá todos los lugares disponibles.
|
||||
</p>
|
||||
|
||||
<div class="table-responsive mb-3">
|
||||
<table class="table table-sm table-hover align-middle" style="font-size:.82rem">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>IP</th>
|
||||
<th>Token / ID equipo</th>
|
||||
<th>Nombre / descripción</th>
|
||||
<th>Lugar asignado</th>
|
||||
<th class="text-center">Estado</th>
|
||||
@@ -500,7 +500,13 @@ $tab = $_GET['tab'] ?? 'lugares';
|
||||
<tbody id="tabla-dispositivos">
|
||||
<?php foreach ($dispositivos as $dv): ?>
|
||||
<tr data-dv-id="<?= $dv['id'] ?>">
|
||||
<td><code><?= htmlspecialchars($dv['ip']) ?></code></td>
|
||||
<td>
|
||||
<?php if (!empty($dv['token'])): ?>
|
||||
<code title="<?= htmlspecialchars($dv['token']) ?>"><?= htmlspecialchars(substr($dv['token'], 0, 8)) ?>…</code>
|
||||
<?php else: ?>
|
||||
<code class="text-muted"><?= htmlspecialchars($dv['ip'] ?: '—') ?></code>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($dv['nombre']) ?></td>
|
||||
<td><?= htmlspecialchars($dv['lugar_nombre'] ?? '—') ?></td>
|
||||
<td class="text-center">
|
||||
@@ -510,7 +516,7 @@ $tab = $_GET['tab'] ?? 'lugares';
|
||||
</td>
|
||||
<td class="text-end" style="white-space:nowrap">
|
||||
<button class="btn-icon text-primary"
|
||||
onclick="editarDispositivo(<?= $dv['id'] ?>, '<?= addslashes($dv['ip']) ?>', '<?= addslashes($dv['nombre']) ?>', <?= (int)$dv['lugar_id'] ?>, <?= (int)$dv['activo'] ?>)"
|
||||
onclick="editarDispositivo(<?= $dv['id'] ?>, '<?= addslashes($dv['ip'] ?? '') ?>', '<?= addslashes($dv['nombre']) ?>', <?= (int)$dv['lugar_id'] ?>, <?= (int)$dv['activo'] ?>, '<?= addslashes($dv['token'] ?? '') ?>')"
|
||||
title="Editar">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
@@ -529,26 +535,23 @@ $tab = $_GET['tab'] ?? 'lugares';
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info py-2 px-3 mb-3 d-flex align-items-center gap-2" style="font-size:.85rem">
|
||||
<i class="fas fa-wifi"></i>
|
||||
Tu IP actual: <code class="ms-1"><?= htmlspecialchars($_myIp) ?></code>
|
||||
<button class="btn btn-sm btn-outline-primary ms-2 py-0" onclick="document.getElementById('dv-ip').value='<?= htmlspecialchars($_myIp) ?>';document.getElementById('dv-ip').focus()">
|
||||
Usar esta IP
|
||||
<div class="alert alert-primary py-2 px-3 mb-3 d-flex align-items-center gap-2 flex-wrap" style="font-size:.85rem">
|
||||
<i class="fas fa-fingerprint"></i>
|
||||
Token de <strong>este equipo</strong>: <code id="cfg-my-token" style="font-size:.8rem">cargando…</code>
|
||||
<button class="btn btn-sm btn-primary ms-auto" onclick="registrarEsteEquipo()">
|
||||
<i class="fas fa-plus me-1"></i>Registrar este equipo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-add p-3" style="background:#f8fafc;border-radius:10px;border:1px solid #e2e8f0">
|
||||
<p class="section-title mb-3"><i class="fas fa-plus me-1"></i>Agregar dispositivo</p>
|
||||
<input type="hidden" id="dv-token">
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small fw-semibold">IP <span class="text-danger">*</span></label>
|
||||
<input type="text" id="dv-ip" class="form-control form-control-sm" placeholder="192.168.1.10">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small fw-semibold">Nombre / descripción <span class="text-danger">*</span></label>
|
||||
<input type="text" id="dv-nombre" class="form-control form-control-sm" placeholder="Recepción 1 — María López">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small fw-semibold">Lugar asignado <span class="text-danger">*</span></label>
|
||||
<select id="dv-lugar" class="form-select form-select-sm">
|
||||
<option value="">— Seleccionar —</option>
|
||||
@@ -575,10 +578,7 @@ $tab = $_GET['tab'] ?? 'lugares';
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="edit-dv-id">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold">IP</label>
|
||||
<input type="text" id="edit-dv-ip" class="form-control form-control-sm">
|
||||
</div>
|
||||
<input type="hidden" id="edit-dv-token">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold">Nombre / descripción</label>
|
||||
<input type="text" id="edit-dv-nombre" class="form-control form-control-sm">
|
||||
@@ -1309,19 +1309,41 @@ async function eliminarLugar(id, nombre) {
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// DISPOSITIVOS / TABLETS
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Mostrar token de este equipo en el banner
|
||||
(function() {
|
||||
function setTokenDisplay() {
|
||||
var t = window._turneroDeviceToken || localStorage.getItem('turnero_device_token');
|
||||
if (!t) return;
|
||||
var el = document.getElementById('cfg-my-token');
|
||||
if (el) el.textContent = t.substring(0, 8) + '… (' + t + ')';
|
||||
}
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', setTokenDisplay);
|
||||
else setTokenDisplay();
|
||||
// Reintentar si el sidebar aún no corrió
|
||||
setTimeout(setTokenDisplay, 500);
|
||||
})();
|
||||
|
||||
function registrarEsteEquipo() {
|
||||
var t = window._turneroDeviceToken || localStorage.getItem('turnero_device_token');
|
||||
if (!t) { toast('Token no disponible aún, espera un momento', 'error'); return; }
|
||||
document.getElementById('dv-token').value = t;
|
||||
document.getElementById('dv-nombre').focus();
|
||||
document.getElementById('dv-nombre').scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
async function guardarDispositivo(id = null) {
|
||||
const pfx = id ? 'edit-dv-' : 'dv-';
|
||||
const ip = document.getElementById(pfx + 'ip')?.value.trim();
|
||||
const pfx = id ? 'edit-dv-' : 'dv-';
|
||||
const token = document.getElementById(pfx + 'token')?.value.trim() || null;
|
||||
const nombre = document.getElementById(pfx + 'nombre')?.value.trim();
|
||||
const lugarId = parseInt(document.getElementById(pfx + 'lugar')?.value);
|
||||
const activo = id ? (document.getElementById('edit-dv-activo')?.checked ? 1 : 0) : 1;
|
||||
|
||||
if (!ip) { toast('La IP es requerida', 'error'); return; }
|
||||
if (!nombre) { toast('El nombre es requerido', 'error'); return; }
|
||||
if (!lugarId) { toast('Selecciona un lugar', 'error'); return; }
|
||||
if (!token) { toast('Haz clic en "Registrar este equipo" primero', 'error'); return; }
|
||||
|
||||
try {
|
||||
const body = { ip, nombre, lugar_id: lugarId, activo };
|
||||
const body = { token, nombre, lugar_id: lugarId, activo, ip: '' };
|
||||
if (id) body.id = id;
|
||||
const res = await fetch(API + 'save_dispositivo.php', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
@@ -1329,17 +1351,17 @@ async function guardarDispositivo(id = null) {
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!json.ok) { toast(json.error || 'Error', 'error'); return; }
|
||||
toast(id ? 'Dispositivo actualizado' : 'Dispositivo creado');
|
||||
toast(id ? 'Dispositivo actualizado' : 'Dispositivo registrado');
|
||||
bootstrap.Modal.getInstance(document.getElementById('modalEditarDispositivo'))?.hide();
|
||||
setTimeout(() => location.reload(), 600);
|
||||
} catch (e) { toast('Error de conexión', 'error'); }
|
||||
}
|
||||
|
||||
function editarDispositivo(id, ip, nombre, lugarId, activo) {
|
||||
document.getElementById('edit-dv-id').value = id;
|
||||
document.getElementById('edit-dv-ip').value = ip;
|
||||
document.getElementById('edit-dv-nombre').value = nombre;
|
||||
document.getElementById('edit-dv-lugar').value = lugarId;
|
||||
function editarDispositivo(id, ip, nombre, lugarId, activo, token) {
|
||||
document.getElementById('edit-dv-id').value = id;
|
||||
document.getElementById('edit-dv-token').value = token || '';
|
||||
document.getElementById('edit-dv-nombre').value = nombre;
|
||||
document.getElementById('edit-dv-lugar').value = lugarId;
|
||||
document.getElementById('edit-dv-activo').checked = !!activo;
|
||||
new bootstrap.Modal(document.getElementById('modalEditarDispositivo')).show();
|
||||
}
|
||||
|
||||
@@ -9,20 +9,35 @@ if (!isUserLoggedIn()) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Tablet asignada → forzar su lugar, bloquear cualquier otro
|
||||
// Chequeo en vivo por IP (no solo sesión, para sesiones preexistentes)
|
||||
// Tablet asignada → forzar su lugar por token de navegador (o IP como fallback)
|
||||
$lugarForzado = 0;
|
||||
try {
|
||||
$_clientIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);
|
||||
$_dispPdo = Database::getInstance()->getConnection();
|
||||
$_dispStmt = $_dispPdo->prepare(
|
||||
"SELECT td.lugar_id, td.nombre, tl.tipo
|
||||
FROM turnero_dispositivos td
|
||||
JOIN turnero_lugares tl ON tl.id = td.lugar_id
|
||||
WHERE td.ip = ? AND td.activo = 1 LIMIT 1"
|
||||
);
|
||||
$_dispStmt->execute([$_clientIp]);
|
||||
$_dispRow = $_dispStmt->fetch(PDO::FETCH_ASSOC);
|
||||
$_dispPdo = Database::getInstance()->getConnection();
|
||||
$_dispRow = null;
|
||||
// 1. Chequeo por token de navegador (cookie)
|
||||
$_devToken = trim($_COOKIE['turnero_token'] ?? '');
|
||||
if ($_devToken) {
|
||||
$_s = $_dispPdo->prepare(
|
||||
"SELECT td.lugar_id, td.nombre, tl.tipo
|
||||
FROM turnero_dispositivos td
|
||||
JOIN turnero_lugares tl ON tl.id = td.lugar_id
|
||||
WHERE td.token = ? AND td.activo = 1 LIMIT 1"
|
||||
);
|
||||
$_s->execute([$_devToken]);
|
||||
$_dispRow = $_s->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||
}
|
||||
// 2. Fallback por IP
|
||||
if (!$_dispRow) {
|
||||
$_clientIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);
|
||||
$_s = $_dispPdo->prepare(
|
||||
"SELECT td.lugar_id, td.nombre, tl.tipo
|
||||
FROM turnero_dispositivos td
|
||||
JOIN turnero_lugares tl ON tl.id = td.lugar_id
|
||||
WHERE td.ip = ? AND td.token IS NULL AND td.activo = 1 LIMIT 1"
|
||||
);
|
||||
$_s->execute([$_clientIp]);
|
||||
$_dispRow = $_s->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||
}
|
||||
if ($_dispRow) {
|
||||
$_SESSION['turnero_dispositivo'] = $_dispRow;
|
||||
$_forzado = (int)$_dispRow['lugar_id'];
|
||||
|
||||
@@ -9,22 +9,37 @@ if (!isUserLoggedIn()) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Tablet asignada → forzar su escritorio, bloquear cualquier otro
|
||||
// Chequeo en vivo por IP (no solo sesión, para sesiones preexistentes)
|
||||
// Tablet asignada → forzar su escritorio por token de navegador (o IP como fallback)
|
||||
$_recepForzado = 0;
|
||||
try {
|
||||
$_clientIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);
|
||||
$_dispPdo = Database::getInstance()->getConnection();
|
||||
$_dispStmt = $_dispPdo->prepare(
|
||||
"SELECT td.lugar_id, td.nombre, tl.tipo
|
||||
FROM turnero_dispositivos td
|
||||
JOIN turnero_lugares tl ON tl.id = td.lugar_id
|
||||
WHERE td.ip = ? AND td.activo = 1 LIMIT 1"
|
||||
);
|
||||
$_dispStmt->execute([$_clientIp]);
|
||||
$_dispRow = $_dispStmt->fetch(PDO::FETCH_ASSOC);
|
||||
$_dispPdo = Database::getInstance()->getConnection();
|
||||
$_dispRow = null;
|
||||
// 1. Chequeo por token de navegador (cookie)
|
||||
$_devToken = trim($_COOKIE['turnero_token'] ?? '');
|
||||
if ($_devToken) {
|
||||
$_s = $_dispPdo->prepare(
|
||||
"SELECT td.lugar_id, td.nombre, tl.tipo
|
||||
FROM turnero_dispositivos td
|
||||
JOIN turnero_lugares tl ON tl.id = td.lugar_id
|
||||
WHERE td.token = ? AND td.activo = 1 LIMIT 1"
|
||||
);
|
||||
$_s->execute([$_devToken]);
|
||||
$_dispRow = $_s->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||
}
|
||||
// 2. Fallback por IP
|
||||
if (!$_dispRow) {
|
||||
$_clientIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);
|
||||
$_s = $_dispPdo->prepare(
|
||||
"SELECT td.lugar_id, td.nombre, tl.tipo
|
||||
FROM turnero_dispositivos td
|
||||
JOIN turnero_lugares tl ON tl.id = td.lugar_id
|
||||
WHERE td.ip = ? AND td.token IS NULL AND td.activo = 1 LIMIT 1"
|
||||
);
|
||||
$_s->execute([$_clientIp]);
|
||||
$_dispRow = $_s->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||
}
|
||||
if ($_dispRow) {
|
||||
$_SESSION['turnero_dispositivo'] = $_dispRow; // mantener sesión actualizada
|
||||
$_SESSION['turnero_dispositivo'] = $_dispRow;
|
||||
$_forzado = (int)$_dispRow['lugar_id'];
|
||||
if ($_dispRow['tipo'] !== 'recepcion') {
|
||||
header('Location: ' . BASE_URL . 'erp.php?m=turnero&v=lugar&lugar_id=' . $_forzado); exit;
|
||||
|
||||
@@ -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