474 lines
20 KiB
PHP
474 lines
20 KiB
PHP
<?php
|
||
/**
|
||
* Gestión Unificada de Administradores - WhatsApp Bot Manager
|
||
* Fecha: 4 de enero de 2026
|
||
*/
|
||
|
||
require_once 'config/config.php';
|
||
|
||
// Solo permitir acceso desde localhost por seguridad
|
||
$allowedIPs = ['127.0.0.1', '::1', 'localhost'];
|
||
$clientIP = $_SERVER['REMOTE_ADDR'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? 'unknown';
|
||
|
||
if (!in_array($clientIP, $allowedIPs) && $clientIP !== 'unknown') {
|
||
die("❌ Acceso denegado. Esta utilidad solo puede ejecutarse desde localhost por seguridad.");
|
||
}
|
||
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="es">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>🛠️ Gestión de Administradores - WhatsApp Bot Manager</title>
|
||
<style>
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
background: linear-gradient(135deg, #25D366 0%, #128C7E 100%);
|
||
color: #333;
|
||
margin: 0;
|
||
padding: 20px;
|
||
min-height: 100vh;
|
||
}
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
padding: 40px;
|
||
border-radius: 15px;
|
||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||
}
|
||
h1 {
|
||
color: #25D366;
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
.info-box {
|
||
background: #e8f5e8;
|
||
padding: 20px;
|
||
border-left: 5px solid #25D366;
|
||
margin: 20px 0;
|
||
border-radius: 5px;
|
||
}
|
||
.warning-box {
|
||
background: #fff3cd;
|
||
padding: 20px;
|
||
border-left: 5px solid #ffc107;
|
||
margin: 20px 0;
|
||
border-radius: 5px;
|
||
}
|
||
.error-box {
|
||
background: #f8d7da;
|
||
padding: 20px;
|
||
border-left: 5px solid #dc3545;
|
||
margin: 20px 0;
|
||
border-radius: 5px;
|
||
}
|
||
.form-group {
|
||
margin: 20px 0;
|
||
}
|
||
label {
|
||
display: block;
|
||
font-weight: bold;
|
||
margin-bottom: 8px;
|
||
color: #333;
|
||
}
|
||
input[type="password"], input[type="text"], input[type="email"] {
|
||
width: 100%;
|
||
padding: 12px;
|
||
border: 2px solid #ddd;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
box-sizing: border-box;
|
||
}
|
||
input:focus {
|
||
outline: none;
|
||
border-color: #25D366;
|
||
}
|
||
.btn {
|
||
background: #25D366;
|
||
color: white;
|
||
padding: 12px 30px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
margin: 10px 5px;
|
||
}
|
||
.btn:hover {
|
||
background: #1aa347;
|
||
}
|
||
.btn-danger {
|
||
background: #dc3545;
|
||
}
|
||
.btn-danger:hover {
|
||
background: #c82333;
|
||
}
|
||
.btn-warning {
|
||
background: #ffc107;
|
||
color: #000;
|
||
}
|
||
.btn-warning:hover {
|
||
background: #e0a800;
|
||
}
|
||
.admin-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 20px 0;
|
||
}
|
||
.admin-table th, .admin-table td {
|
||
border: 1px solid #ddd;
|
||
padding: 12px;
|
||
text-align: left;
|
||
}
|
||
.admin-table th {
|
||
background: #25D366;
|
||
color: white;
|
||
}
|
||
.admin-table tr:nth-child(even) {
|
||
background: #f8f9fa;
|
||
}
|
||
.status-active {
|
||
color: #28a745;
|
||
font-weight: bold;
|
||
}
|
||
.status-inactive {
|
||
color: #dc3545;
|
||
font-weight: bold;
|
||
}
|
||
.tab-container {
|
||
display: flex;
|
||
border-bottom: 2px solid #25D366;
|
||
margin-bottom: 20px;
|
||
}
|
||
.tab {
|
||
padding: 15px 30px;
|
||
cursor: pointer;
|
||
border: none;
|
||
background: #f8f9fa;
|
||
margin-right: 5px;
|
||
border-radius: 8px 8px 0 0;
|
||
}
|
||
.tab.active {
|
||
background: #25D366;
|
||
color: white;
|
||
}
|
||
.tab-content {
|
||
display: none;
|
||
}
|
||
.tab-content.active {
|
||
display: block;
|
||
}
|
||
code {
|
||
background: #f8f9fa;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
font-family: monospace;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>🛠️ Gestión Unificada de Administradores</h1>
|
||
|
||
<div class="tab-container">
|
||
<button class="tab active" onclick="showTab('list')">📋 Lista de Administradores</button>
|
||
<button class="tab" onclick="showTab('add')">➕ Agregar Admin</button>
|
||
<button class="tab" onclick="showTab('password')">🔑 Cambiar Contraseña</button>
|
||
<button class="tab" onclick="showTab('test')">🔍 Probar Login</button>
|
||
<button class="tab" onclick="showTab('config')">⚙️ Configuración</button>
|
||
</div>
|
||
|
||
<?php
|
||
try {
|
||
$pdo = new PDO(
|
||
"mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET,
|
||
DB_USER,
|
||
DB_PASS,
|
||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
|
||
);
|
||
|
||
// Verificar si el sistema está migrado
|
||
$checkTable = $pdo->query("SHOW TABLES LIKE 'admin_users'");
|
||
$tableExists = $checkTable->rowCount() > 0;
|
||
|
||
if (!$tableExists) {
|
||
echo '<div class="error-box">';
|
||
echo '<h4>❌ Sistema No Migrado</h4>';
|
||
echo '<p>El sistema de contraseñas aún no ha sido migrado a la base de datos.</p>';
|
||
echo '<p><a href="migrar_passwords_bd.php" class="btn">🔄 Migrar Sistema Ahora</a></p>';
|
||
echo '</div>';
|
||
exit;
|
||
}
|
||
|
||
// Procesar acciones
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||
|
||
if ($_POST['action'] === 'add_admin') {
|
||
$username = trim($_POST['username']);
|
||
$password = $_POST['password'];
|
||
$fullName = trim($_POST['full_name']);
|
||
$email = trim($_POST['email']);
|
||
|
||
if (strlen($username) < 3) {
|
||
echo '<div class="error-box">❌ El usuario debe tener al menos 3 caracteres</div>';
|
||
} elseif (strlen($password) < 6) {
|
||
echo '<div class="error-box">❌ La contraseña debe tener al menos 6 caracteres</div>';
|
||
} else {
|
||
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
|
||
|
||
try {
|
||
$stmt = $pdo->prepare("INSERT INTO admin_users (username, password_hash, full_name, email) VALUES (?, ?, ?, ?)");
|
||
$stmt->execute([$username, $passwordHash, $fullName, $email]);
|
||
echo '<div class="info-box">✅ Administrador creado exitosamente: <strong>' . htmlspecialchars($username) . '</strong></div>';
|
||
} catch (PDOException $e) {
|
||
echo '<div class="error-box">❌ Error: ' . (strpos($e->getMessage(), 'Duplicate') !== false ? 'El usuario ya existe' : $e->getMessage()) . '</div>';
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($_POST['action'] === 'update_password') {
|
||
$username = $_POST['username'];
|
||
$newPassword = $_POST['new_password'];
|
||
$confirmPassword = $_POST['confirm_password'];
|
||
|
||
if ($newPassword !== $confirmPassword) {
|
||
echo '<div class="error-box">❌ Las contraseñas no coinciden</div>';
|
||
} elseif (strlen($newPassword) < 6) {
|
||
echo '<div class="error-box">❌ La contraseña debe tener al menos 6 caracteres</div>';
|
||
} else {
|
||
$passwordHash = password_hash($newPassword, PASSWORD_DEFAULT);
|
||
$stmt = $pdo->prepare("UPDATE admin_users SET password_hash = ?, updated_at = NOW() WHERE username = ?");
|
||
|
||
if ($stmt->execute([$passwordHash, $username])) {
|
||
echo '<div class="info-box">✅ Contraseña actualizada para: <strong>' . htmlspecialchars($username) . '</strong></div>';
|
||
} else {
|
||
echo '<div class="error-box">❌ Error actualizando contraseña</div>';
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($_POST['action'] === 'toggle_status') {
|
||
$userId = $_POST['user_id'];
|
||
$stmt = $pdo->prepare("UPDATE admin_users SET is_active = NOT is_active, updated_at = NOW() WHERE id = ?");
|
||
|
||
if ($stmt->execute([$userId])) {
|
||
echo '<div class="info-box">✅ Estado del usuario actualizado</div>';
|
||
} else {
|
||
echo '<div class="error-box">❌ Error actualizando estado</div>';
|
||
}
|
||
}
|
||
|
||
if ($_POST['action'] === 'test_login') {
|
||
$testUsername = $_POST['test_username'];
|
||
$testPassword = $_POST['test_password'];
|
||
|
||
$stmt = $pdo->prepare("SELECT * FROM admin_users WHERE username = ? AND is_active = 1");
|
||
$stmt->execute([$testUsername]);
|
||
$admin = $stmt->fetch();
|
||
|
||
if ($admin && password_verify($testPassword, $admin['password_hash'])) {
|
||
echo '<div class="info-box">✅ <strong>LOGIN EXITOSO</strong><br>';
|
||
echo 'Usuario: ' . htmlspecialchars($admin['username']) . '<br>';
|
||
echo 'Nombre: ' . htmlspecialchars($admin['full_name']) . '<br>';
|
||
echo 'Último login: ' . ($admin['last_login'] ?? 'Nunca') . '</div>';
|
||
|
||
// Actualizar último login
|
||
$updateLogin = $pdo->prepare("UPDATE admin_users SET last_login = NOW() WHERE id = ?");
|
||
$updateLogin->execute([$admin['id']]);
|
||
} else {
|
||
echo '<div class="error-box">❌ <strong>LOGIN FALLIDO</strong><br>';
|
||
if (!$admin) {
|
||
echo 'Usuario no encontrado o inactivo';
|
||
} else {
|
||
echo 'Contraseña incorrecta';
|
||
}
|
||
echo '</div>';
|
||
}
|
||
}
|
||
|
||
if ($_POST['action'] === 'clear_login_attempts') {
|
||
$attemptsFile = '.login_attempts.json';
|
||
if (file_exists($attemptsFile)) {
|
||
unlink($attemptsFile);
|
||
echo '<div class="info-box">✅ Intentos de login bloqueados eliminados</div>';
|
||
} else {
|
||
echo '<div class="warning-box">⚠️ No hay bloqueos para eliminar</div>';
|
||
}
|
||
}
|
||
}
|
||
} catch (Exception $e) {
|
||
echo '<div class="error-box">❌ Error de base de datos: ' . $e->getMessage() . '</div>';
|
||
exit;
|
||
}
|
||
?>
|
||
|
||
<!-- Tab: Lista de Administradores -->
|
||
<div id="list" class="tab-content active">
|
||
<h3>📋 Administradores del Sistema</h3>
|
||
|
||
<?php
|
||
$stmt = $pdo->query("SELECT * FROM admin_users ORDER BY created_at DESC");
|
||
$admins = $stmt->fetchAll();
|
||
|
||
if (empty($admins)) {
|
||
echo '<div class="warning-box">⚠️ No hay administradores en la base de datos</div>';
|
||
} else {
|
||
echo '<table class="admin-table">';
|
||
echo '<tr><th>ID</th><th>Usuario</th><th>Nombre</th><th>Email</th><th>Estado</th><th>Último Login</th><th>Acciones</th></tr>';
|
||
|
||
foreach ($admins as $admin) {
|
||
echo '<tr>';
|
||
echo '<td>' . $admin['id'] . '</td>';
|
||
echo '<td><strong>' . htmlspecialchars($admin['username']) . '</strong></td>';
|
||
echo '<td>' . htmlspecialchars($admin['full_name'] ?? 'Sin nombre') . '</td>';
|
||
echo '<td>' . htmlspecialchars($admin['email'] ?? 'Sin email') . '</td>';
|
||
echo '<td><span class="status-' . ($admin['is_active'] ? 'active">✅ Activo' : 'inactive">❌ Inactivo') . '</span></td>';
|
||
echo '<td>' . ($admin['last_login'] ?? 'Nunca') . '</td>';
|
||
echo '<td>';
|
||
echo '<form method="post" style="display: inline-block;">';
|
||
echo '<input type="hidden" name="action" value="toggle_status">';
|
||
echo '<input type="hidden" name="user_id" value="' . $admin['id'] . '">';
|
||
echo '<button type="submit" class="btn btn-warning" style="padding: 5px 10px; font-size: 12px;">';
|
||
echo $admin['is_active'] ? 'Desactivar' : 'Activar';
|
||
echo '</button>';
|
||
echo '</form>';
|
||
echo '</td>';
|
||
echo '</tr>';
|
||
}
|
||
echo '</table>';
|
||
}
|
||
?>
|
||
</div>
|
||
|
||
<!-- Tab: Agregar Admin -->
|
||
<div id="add" class="tab-content">
|
||
<h3>➕ Agregar Nuevo Administrador</h3>
|
||
|
||
<form method="post">
|
||
<div class="form-group">
|
||
<label for="username">Usuario:</label>
|
||
<input type="text" id="username" name="username" required minlength="3">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="password">Contraseña:</label>
|
||
<input type="password" id="password" name="password" required minlength="6">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="full_name">Nombre completo:</label>
|
||
<input type="text" id="full_name" name="full_name">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="email">Email:</label>
|
||
<input type="email" id="email" name="email">
|
||
</div>
|
||
<input type="hidden" name="action" value="add_admin">
|
||
<button type="submit" class="btn">➕ Crear Administrador</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Tab: Cambiar Contraseña -->
|
||
<div id="password" class="tab-content">
|
||
<h3>🔑 Cambiar Contraseña de Administrador</h3>
|
||
|
||
<form method="post">
|
||
<div class="form-group">
|
||
<label for="username_pass">Seleccionar usuario:</label>
|
||
<select id="username_pass" name="username" required style="width: 100%; padding: 12px; border: 2px solid #ddd; border-radius: 8px;">
|
||
<option value="">Seleccionar...</option>
|
||
<?php
|
||
foreach ($admins as $admin) {
|
||
echo '<option value="' . htmlspecialchars($admin['username']) . '">' .
|
||
htmlspecialchars($admin['username']) . ' - ' . htmlspecialchars($admin['full_name'] ?? 'Sin nombre') .
|
||
'</option>';
|
||
}
|
||
?>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="new_password">Nueva contraseña:</label>
|
||
<input type="password" id="new_password" name="new_password" required minlength="6">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="confirm_password">Confirmar contraseña:</label>
|
||
<input type="password" id="confirm_password" name="confirm_password" required minlength="6">
|
||
</div>
|
||
<input type="hidden" name="action" value="update_password">
|
||
<button type="submit" class="btn">🔑 Actualizar Contraseña</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Tab: Probar Login -->
|
||
<div id="test" class="tab-content">
|
||
<h3>🔍 Probar Credenciales de Login</h3>
|
||
|
||
<form method="post">
|
||
<div class="form-group">
|
||
<label for="test_username">Usuario:</label>
|
||
<input type="text" id="test_username" name="test_username" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="test_password">Contraseña:</label>
|
||
<input type="password" id="test_password" name="test_password" required>
|
||
</div>
|
||
<input type="hidden" name="action" value="test_login">
|
||
<button type="submit" class="btn">🔍 Probar Login</button>
|
||
</form>
|
||
|
||
<div class="info-box" style="margin-top: 20px;">
|
||
<h4>🌐 Enlaces de Acceso:</h4>
|
||
<p><strong>Login del sistema:</strong> <a href="login.php" target="_blank">login.php</a></p>
|
||
<p><strong>Panel principal:</strong> <a href="index.php" target="_blank">index.php</a></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab: Configuración -->
|
||
<div id="config" class="tab-content">
|
||
<h3>⚙️ Configuración del Sistema</h3>
|
||
|
||
<div class="info-box">
|
||
<h4>📊 Estado del Sistema</h4>
|
||
<p><strong>Total de administradores:</strong> <?php echo count($admins); ?></p>
|
||
<p><strong>Administradores activos:</strong> <?php echo count(array_filter($admins, fn($a) => $a['is_active'])); ?></p>
|
||
<p><strong>Base de datos:</strong> <?php echo DB_NAME; ?></p>
|
||
<p><strong>Sistema migrado:</strong> ✅ Sí</p>
|
||
</div>
|
||
|
||
<form method="post" style="margin-top: 20px;">
|
||
<input type="hidden" name="action" value="clear_login_attempts">
|
||
<button type="submit" class="btn btn-danger">🗑️ Limpiar Intentos de Login Bloqueados</button>
|
||
</form>
|
||
|
||
<div class="warning-box" style="margin-top: 20px;">
|
||
<h4>⚠️ Información Importante:</h4>
|
||
<ul>
|
||
<li>Las contraseñas se almacenan de forma segura usando hash</li>
|
||
<li>Los usuarios inactivos no pueden iniciar sesión</li>
|
||
<li>Se registra la fecha del último login de cada usuario</li>
|
||
<li>Esta herramienta debe eliminarse en producción</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function showTab(tabName) {
|
||
// Ocultar todos los tabs
|
||
var tabs = document.querySelectorAll('.tab-content');
|
||
tabs.forEach(function(tab) {
|
||
tab.classList.remove('active');
|
||
});
|
||
|
||
// Remover clase active de todos los botones
|
||
var buttons = document.querySelectorAll('.tab');
|
||
buttons.forEach(function(button) {
|
||
button.classList.remove('active');
|
||
});
|
||
|
||
// Mostrar el tab seleccionado
|
||
document.getElementById(tabName).classList.add('active');
|
||
event.target.classList.add('active');
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|