Files
whatsapp/gestion_admin_unificada.php
T
2026-01-12 11:06:43 -05:00

474 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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>