login realizado
This commit is contained in:
parent
c819bb5aab
commit
be8b0d0c38
585
public.php
585
public.php
@ -6,6 +6,7 @@ chdir(__DIR__);
|
||||
|
||||
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmSecurity;
|
||||
use SmsNotifier\Service\PaymentIntentService;
|
||||
|
||||
// Carga manual del servicio
|
||||
@ -72,6 +73,119 @@ try {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 0. LOGIN SCREEN SUPPORT & AUTHENTICATION
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// Determinar si el usuario necesita login (si no tiene sesión UCRM activa)
|
||||
$security = UcrmSecurity::create();
|
||||
$currentUser = $security->getUser();
|
||||
$needsLogin = !$currentUser;
|
||||
|
||||
$nmsBaseUrl = "https://{$ipServer}/nms/api/v2.1";
|
||||
|
||||
// 0a. NMS Login (POST username + password)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['action']) && $_GET['action'] === 'nms_login') {
|
||||
header('Content-Type: application/json');
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$username = $input['username'] ?? '';
|
||||
$password = $input['password'] ?? '';
|
||||
|
||||
if (!$username || !$password) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Se requieren usuario y contraseña.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$nmsClient = new \GuzzleHttp\Client(['verify' => false, 'http_errors' => false]);
|
||||
$resp = $nmsClient->post("{$nmsBaseUrl}/user/login", [
|
||||
'json' => ['username' => $username, 'password' => $password],
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
]);
|
||||
|
||||
$statusCode = $resp->getStatusCode();
|
||||
$body = json_decode($resp->getBody()->getContents(), true);
|
||||
$authToken = $resp->getHeaderLine('x-auth-token');
|
||||
|
||||
if ($statusCode === 200 && $authToken) {
|
||||
$logger->info("NMS Login OK for user: {$username}");
|
||||
echo json_encode(['success' => true, 'token' => $authToken, 'user' => $body]);
|
||||
} elseif ($statusCode === 201) {
|
||||
$logger->info("NMS Login requires 2FA for user: {$username}");
|
||||
http_response_code(201);
|
||||
echo json_encode(['requires2FA' => true, 'twoFactorToken' => $body]);
|
||||
} else {
|
||||
$logger->warning("NMS Login failed for user: {$username} (HTTP {$statusCode})");
|
||||
http_response_code($statusCode ?: 401);
|
||||
echo json_encode(['error' => $body['message'] ?? 'Credenciales inválidas.', 'statusCode' => $statusCode]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$logger->error("NMS Login exception: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error de conexión con el servidor UISP.']);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// 0b. NMS TOTP Login
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['action']) && $_GET['action'] === 'nms_login_totp') {
|
||||
header('Content-Type: application/json');
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
try {
|
||||
$nmsClient = new \GuzzleHttp\Client(['verify' => false, 'http_errors' => false]);
|
||||
$resp = $nmsClient->post("{$nmsBaseUrl}/user/login/totpauth", [
|
||||
'json' => $input,
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
]);
|
||||
|
||||
$statusCode = $resp->getStatusCode();
|
||||
$body = json_decode($resp->getBody()->getContents(), true);
|
||||
$authToken = $resp->getHeaderLine('x-auth-token');
|
||||
|
||||
if ($statusCode === 200 && $authToken) {
|
||||
$logger->info("NMS 2FA Login OK");
|
||||
echo json_encode(['success' => true, 'token' => $authToken, 'user' => $body]);
|
||||
} else {
|
||||
http_response_code($statusCode ?: 401);
|
||||
echo json_encode(['error' => $body['message'] ?? 'Código TOTP inválido.']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error de conexión con el servidor UISP.']);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// 0c. NMS Verify Session
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'nms_verify_session') {
|
||||
header('Content-Type: application/json');
|
||||
$token = $_SERVER['HTTP_X_AUTH_TOKEN'] ?? '';
|
||||
if (!$token) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'No token provided.']);
|
||||
exit;
|
||||
}
|
||||
try {
|
||||
$nmsClient = new \GuzzleHttp\Client(['verify' => false, 'http_errors' => false]);
|
||||
$resp = $nmsClient->get("{$nmsBaseUrl}/user", [
|
||||
'headers' => ['x-auth-token' => $token],
|
||||
]);
|
||||
if ($resp->getStatusCode() === 200) {
|
||||
echo json_encode(['success' => true, 'user' => json_decode($resp->getBody()->getContents(), true)]);
|
||||
} else {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Sesión inválida o expirada.']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error verificando sesión.']);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// API HANDLERS
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
if ($_POST['action'] === 'save_installers') {
|
||||
@ -988,10 +1102,272 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* ── Login Overlay ─────────────────────────────────────── */
|
||||
#loginOverlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 99999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
|
||||
font-family: 'Outfit', sans-serif;
|
||||
transition: opacity 0.5s ease, visibility 0.5s ease;
|
||||
}
|
||||
|
||||
#loginOverlay.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 400px;
|
||||
max-width: 92vw;
|
||||
background: rgba(30, 41, 59, 0.85);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 24px;
|
||||
padding: 48px 36px 36px;
|
||||
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||||
text-align: center;
|
||||
animation: loginSlideIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||
}
|
||||
|
||||
@keyframes loginSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px) scale(0.96);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 12px;
|
||||
filter: drop-shadow(0 4px 20px rgba(99, 102, 241, 0.3));
|
||||
}
|
||||
|
||||
.login-title {
|
||||
color: #f8fafc;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
color: #94a3b8;
|
||||
font-size: 0.85rem;
|
||||
margin: 0 0 28px;
|
||||
}
|
||||
|
||||
.login-field {
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.login-field label {
|
||||
display: block;
|
||||
color: #94a3b8;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.login-field input {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
background: rgba(15, 23, 42, 0.7);
|
||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||
border-radius: 12px;
|
||||
color: #f1f5f9;
|
||||
font-size: 0.95rem;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
outline: none;
|
||||
transition: border-color 0.25s, box-shadow 0.25s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-field input:focus {
|
||||
border-color: #6366f1;
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
|
||||
}
|
||||
|
||||
.login-field input::placeholder {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
padding: 13px;
|
||||
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s, box-shadow 0.25s;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.login-btn:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 30px rgba(99, 102, 241, 0.35);
|
||||
}
|
||||
|
||||
.login-btn:active:not(:disabled) {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.login-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.login-error {
|
||||
background: rgba(239, 68, 68, 0.12);
|
||||
border: 1px solid rgba(239, 68, 68, 0.25);
|
||||
color: #fca5a5;
|
||||
padding: 10px 14px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 16px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-error.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.login-spinner {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-top-color: white;
|
||||
border-radius: 50%;
|
||||
animation: loginSpin 0.6s linear infinite;
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
@keyframes loginSpin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#loginTotpSection {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#loginTotpSection.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
margin-top: 24px;
|
||||
color: #475569;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.login-footer a {
|
||||
color: #6366f1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Particles background */
|
||||
.login-particles {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-particles span {
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: rgba(99, 102, 241, 0.3);
|
||||
border-radius: 50%;
|
||||
animation: loginFloat 12s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes loginFloat {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0) translateX(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-400px) translateX(100px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- ━━━ Login Overlay ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -->
|
||||
<div id="loginOverlay">
|
||||
<div class="login-particles" id="loginParticles"></div>
|
||||
<div class="login-card">
|
||||
<img src="?action=image&file=whatsapp-logo.png" class="login-logo" alt="SIIP">
|
||||
<h2 class="login-title">SIIP Notifications</h2>
|
||||
<p class="login-subtitle">Inicia sesión con tu cuenta de UISP</p>
|
||||
|
||||
<div class="login-error" id="loginError"></div>
|
||||
|
||||
<!-- Formulario principal -->
|
||||
<div id="loginFormSection">
|
||||
<div class="login-field">
|
||||
<label for="loginUsername">Usuario</label>
|
||||
<input type="text" id="loginUsername" placeholder="Ingresa tu usuario" autocomplete="username" autofocus>
|
||||
</div>
|
||||
<div class="login-field">
|
||||
<label for="loginPassword">Contraseña</label>
|
||||
<input type="password" id="loginPassword" placeholder="Ingresa tu contraseña" autocomplete="current-password">
|
||||
</div>
|
||||
<button class="login-btn" id="loginBtn" onclick="handleLogin()">Iniciar Sesión</button>
|
||||
</div>
|
||||
|
||||
<!-- Sección 2FA (oculta por defecto) -->
|
||||
<div id="loginTotpSection">
|
||||
<div class="login-field">
|
||||
<label for="loginTotpCode">Código de autenticación (2FA)</label>
|
||||
<input type="text" id="loginTotpCode" placeholder="Código de 6 dígitos" maxlength="6" inputmode="numeric" autocomplete="one-time-code">
|
||||
</div>
|
||||
<button class="login-btn" id="loginTotpBtn" onclick="handleTotpLogin()">Verificar Código</button>
|
||||
</div>
|
||||
|
||||
<div class="login-footer">
|
||||
Acceso restringido a administradores UISP · SIIP © <?php echo date('Y'); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
@ -1482,6 +1858,9 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
<div id="toast"></div>
|
||||
|
||||
<script>
|
||||
const NEEDS_LOGIN = <?php echo $needsLogin ? 'true' : 'false'; ?>;
|
||||
let SYSTEM_USER_ID = <?php echo $currentUser ? $currentUser->userId : 'null'; ?>;
|
||||
|
||||
const store = {
|
||||
installers: <?php echo json_encode($installersData['instaladores']); ?>,
|
||||
theme: localStorage.getItem('theme') || 'light',
|
||||
@ -2213,6 +2592,212 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
return `<span style="background-color: ${bg}; color: ${color}; padding: 4px 10px; border-radius: 9999px; font-size: 0.75rem; font-weight: 600; text-transform: capitalize;">${label}</span>`;
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
// ━━━ Login Logic ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
(function() {
|
||||
const ALLOWED_ROLES = ['admin', 'superadmin'];
|
||||
let _twoFactorData = null;
|
||||
|
||||
// Generar partículas decorativas
|
||||
const particlesEl = document.getElementById('loginParticles');
|
||||
if (particlesEl) {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const s = document.createElement('span');
|
||||
s.style.left = Math.random() * 100 + '%';
|
||||
s.style.top = (60 + Math.random() * 40) + '%';
|
||||
s.style.animationDelay = (Math.random() * 12) + 's';
|
||||
s.style.animationDuration = (8 + Math.random() * 8) + 's';
|
||||
particlesEl.appendChild(s);
|
||||
}
|
||||
}
|
||||
|
||||
function showLoginError(msg) {
|
||||
const el = document.getElementById('loginError');
|
||||
el.textContent = msg;
|
||||
el.classList.add('visible');
|
||||
}
|
||||
|
||||
function hideLoginError() {
|
||||
document.getElementById('loginError').classList.remove('visible');
|
||||
}
|
||||
|
||||
function setLoginLoading(loading) {
|
||||
const btn = document.getElementById('loginBtn');
|
||||
const totpBtn = document.getElementById('loginTotpBtn');
|
||||
if (loading) {
|
||||
btn.disabled = true;
|
||||
totpBtn.disabled = true;
|
||||
btn.innerHTML = '<span class="login-spinner"></span>Autenticando...';
|
||||
totpBtn.innerHTML = '<span class="login-spinner"></span>Verificando...';
|
||||
} else {
|
||||
btn.disabled = false;
|
||||
totpBtn.disabled = false;
|
||||
btn.textContent = 'Iniciar Sesión';
|
||||
totpBtn.textContent = 'Verificar Código';
|
||||
}
|
||||
}
|
||||
|
||||
function onLoginSuccess(token, user) {
|
||||
sessionStorage.setItem('nms_auth_token', token);
|
||||
sessionStorage.setItem('nms_user', JSON.stringify(user));
|
||||
// Actualizar SYSTEM_USER_ID global
|
||||
SYSTEM_USER_ID = user.id || user.userId || null;
|
||||
console.log('Login OK — User:', user.username, 'Role:', user.role, 'ID:', SYSTEM_USER_ID);
|
||||
|
||||
// Ocultar overlay con animación
|
||||
const overlay = document.getElementById('loginOverlay');
|
||||
overlay.classList.add('hidden');
|
||||
setTimeout(() => {
|
||||
overlay.style.display = 'none';
|
||||
}, 500);
|
||||
}
|
||||
|
||||
window.handleLogin = async function() {
|
||||
hideLoginError();
|
||||
const username = document.getElementById('loginUsername').value.trim();
|
||||
const password = document.getElementById('loginPassword').value;
|
||||
|
||||
if (!username || !password) {
|
||||
showLoginError('Ingresa usuario y contraseña.');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoginLoading(true);
|
||||
try {
|
||||
const resp = await fetch('?action=nms_login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await resp.json();
|
||||
|
||||
if (data.success && data.token) {
|
||||
// Verificar rol
|
||||
const role = data.user?.role || '';
|
||||
if (!ALLOWED_ROLES.includes(role)) {
|
||||
showLoginError(`Acceso denegado. Tu rol (${role}) no tiene permisos para este portal.`);
|
||||
setLoginLoading(false);
|
||||
return;
|
||||
}
|
||||
onLoginSuccess(data.token, data.user);
|
||||
} else if (data.requires2FA) {
|
||||
_twoFactorData = data.twoFactorToken;
|
||||
document.getElementById('loginFormSection').style.display = 'none';
|
||||
document.getElementById('loginTotpSection').classList.add('visible');
|
||||
document.getElementById('loginTotpCode').focus();
|
||||
setLoginLoading(false);
|
||||
} else {
|
||||
showLoginError(data.error || 'Credenciales inválidas.');
|
||||
setLoginLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
showLoginError('Error de conexión. Intenta de nuevo.');
|
||||
setLoginLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.handleTotpLogin = async function() {
|
||||
hideLoginError();
|
||||
const code = document.getElementById('loginTotpCode').value.trim();
|
||||
if (!code || code.length < 6) {
|
||||
showLoginError('Ingresa el código de 6 dígitos.');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoginLoading(true);
|
||||
try {
|
||||
const payload = {
|
||||
..._twoFactorData,
|
||||
totpCode: code
|
||||
};
|
||||
const resp = await fetch('?action=nms_login_totp', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const data = await resp.json();
|
||||
if (data.success && data.token) {
|
||||
const role = data.user?.role || '';
|
||||
if (!ALLOWED_ROLES.includes(role)) {
|
||||
showLoginError(`Acceso denegado. Tu rol (${role}) no tiene permisos.`);
|
||||
setLoginLoading(false);
|
||||
return;
|
||||
}
|
||||
onLoginSuccess(data.token, data.user);
|
||||
} else {
|
||||
showLoginError(data.error || 'Código TOTP inválido.');
|
||||
setLoginLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
showLoginError('Error de conexión. Intenta de nuevo.');
|
||||
setLoginLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Verificar sesión almacenada al cargar
|
||||
async function verifyStoredSession() {
|
||||
const token = sessionStorage.getItem('nms_auth_token');
|
||||
if (!token) return false;
|
||||
|
||||
try {
|
||||
const resp = await fetch('?action=nms_verify_session', {
|
||||
headers: {
|
||||
'x-auth-token': token
|
||||
},
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success && data.user) {
|
||||
const role = data.user?.role || '';
|
||||
if (ALLOWED_ROLES.includes(role)) {
|
||||
onLoginSuccess(token, data.user);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
/* token inválido, mostrar login */
|
||||
}
|
||||
|
||||
sessionStorage.removeItem('nms_auth_token');
|
||||
sessionStorage.removeItem('nms_user');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enter key handlers
|
||||
document.getElementById('loginPassword')?.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') handleLogin();
|
||||
});
|
||||
document.getElementById('loginUsername')?.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') document.getElementById('loginPassword').focus();
|
||||
});
|
||||
document.getElementById('loginTotpCode')?.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') handleTotpLogin();
|
||||
});
|
||||
|
||||
// Inicialización: decidir si mostrar login o portal
|
||||
if (NEEDS_LOGIN) {
|
||||
// No hay sesión UCRM, intentar con token almacenado
|
||||
verifyStoredSession().then(valid => {
|
||||
if (!valid) {
|
||||
document.getElementById('loginOverlay').style.display = 'flex';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Sesión UCRM activa, ocultar login overlay
|
||||
const overlay = document.getElementById('loginOverlay');
|
||||
overlay.classList.add('hidden');
|
||||
overlay.style.display = 'none';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user