Update v9.3.0: Enhanced Security with Telegram Integration
- Incremented version to 9.3.0 (versionCode: 93000) - Added Telegram integration for device notifications - Implemented token-based verification system - Enhanced device registry with IP/country detection - Added split token verification for admin/user validation - Improved dashboard with real-time notifications - Enhanced blocking system with token verification - Added geo-location tracking for devices - Improved device management interface - Enhanced security controls and monitoring 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ async function fetchDevices() {
|
||||
function renderTable(devices) {
|
||||
tableBody.innerHTML = '';
|
||||
if (!devices.length) {
|
||||
tableBody.innerHTML = '<tr><td colspan="7" class="empty">Sin registros</td></tr>';
|
||||
tableBody.innerHTML = '<tr><td colspan="10" class="empty">Sin registros</td></tr>';
|
||||
return;
|
||||
}
|
||||
devices.sort((a, b) => (a.lastSeen || '').localeCompare(b.lastSeen || '') * -1);
|
||||
@@ -31,6 +31,21 @@ function renderTable(devices) {
|
||||
tr.classList.add('blocked');
|
||||
}
|
||||
const alias = device.alias && device.alias.trim().length ? device.alias : 'Sin alias';
|
||||
const verificationStatus = device.verification && device.verification.status ? device.verification.status : 'pending';
|
||||
const needsVerification = verificationStatus !== 'verified';
|
||||
const verificationText = needsVerification
|
||||
? `Pendiente - Token cliente: ${device.verification && device.verification.clientPart ? device.verification.clientPart : 'N/A'}`
|
||||
: `Verificado ${device.verification.verifiedAt ? `(${formatDate(device.verification.verifiedAt)})` : ''}`;
|
||||
const statusLabel = device.blocked
|
||||
? 'Bloqueado'
|
||||
: needsVerification ? 'Pendiente token' : 'Activo';
|
||||
|
||||
const actions = [`<button data-action="alias">Alias</button>`];
|
||||
if (needsVerification) {
|
||||
actions.push('<button data-action="verify" class="primary">Verificar token</button>');
|
||||
}
|
||||
actions.push(device.blocked ? '<button data-action="unblock" class="primary">Desbloquear</button>' : '<button data-action="block" class="danger">Bloquear</button>');
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>
|
||||
<div class="alias">${alias}</div>
|
||||
@@ -39,11 +54,13 @@ function renderTable(devices) {
|
||||
<td>${device.deviceId}</td>
|
||||
<td>${[device.manufacturer, device.model].filter(Boolean).join(' ')}</td>
|
||||
<td>${device.appVersionName || ''} (${device.appVersionCode || ''})</td>
|
||||
<td>${device.ip || '-'}</td>
|
||||
<td>${formatCountry(device.country)}</td>
|
||||
<td>${verificationText}</td>
|
||||
<td>${formatDate(device.lastSeen)}</td>
|
||||
<td>${device.blocked ? 'Bloqueado' : 'Activo'}</td>
|
||||
<td>${statusLabel}</td>
|
||||
<td class="actions-cell">
|
||||
<button data-action="alias">Alias</button>
|
||||
${device.blocked ? '<button data-action="unblock" class="primary">Desbloquear</button>' : '<button data-action="block" class="danger">Bloquear</button>'}
|
||||
${actions.join(' ')}
|
||||
</td>
|
||||
`;
|
||||
tr.dataset.deviceId = device.deviceId;
|
||||
@@ -58,6 +75,13 @@ function formatDate(value) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
function formatCountry(value) {
|
||||
if (!value || value === 'N/A') {
|
||||
return '-';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async function blockDevice(deviceId) {
|
||||
const reason = prompt('Motivo del bloqueo (opcional):');
|
||||
await fetch(`/api/devices/${encodeURIComponent(deviceId)}/block`, {
|
||||
@@ -73,6 +97,28 @@ async function unblockDevice(deviceId) {
|
||||
await fetchDevices();
|
||||
}
|
||||
|
||||
async function verifyDevice(deviceId) {
|
||||
const clientTokenPart = prompt('Introduce el token que aparece en el dispositivo:');
|
||||
if (clientTokenPart === null) {
|
||||
return;
|
||||
}
|
||||
const adminTokenPart = prompt('Introduce el token recibido en Telegram:');
|
||||
if (adminTokenPart === null) {
|
||||
return;
|
||||
}
|
||||
const response = await fetch(`/api/devices/${encodeURIComponent(deviceId)}/verify`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ clientTokenPart, adminTokenPart })
|
||||
});
|
||||
if (!response.ok) {
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
alert(payload.error || 'No se pudo verificar el token');
|
||||
return;
|
||||
}
|
||||
await fetchDevices();
|
||||
}
|
||||
|
||||
async function updateAlias(deviceId) {
|
||||
const alias = prompt('Nuevo alias para el dispositivo:');
|
||||
if (alias === null) {
|
||||
@@ -106,6 +152,8 @@ tableBody.addEventListener('click', async (event) => {
|
||||
await unblockDevice(deviceId);
|
||||
} else if (action === 'alias') {
|
||||
await updateAlias(deviceId);
|
||||
} else if (action === 'verify') {
|
||||
await verifyDevice(deviceId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
Reference in New Issue
Block a user