366 lines
13 KiB
PHP
366 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* Configuración dinámica del sistema WhatsApp Bot Manager
|
|
* Carga variables de entorno desde .env si existe
|
|
* Desarrollado por U-Site.app
|
|
* Fecha: 13 de noviembre de 2025
|
|
*/
|
|
|
|
// Función para cargar archivo .env
|
|
if (!function_exists('loadEnvFile')) {
|
|
function loadEnvFile($path) {
|
|
if (!file_exists($path)) {
|
|
return false;
|
|
}
|
|
|
|
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($lines as $line) {
|
|
if (strpos($line, '#') === 0 || strpos($line, '=') === false) {
|
|
continue;
|
|
}
|
|
|
|
[$name, $value] = explode('=', $line, 2);
|
|
$name = trim($name);
|
|
$value = trim($value, " \t\n\r\0\x0B\"'");
|
|
|
|
if (!array_key_exists($name, $_ENV)) {
|
|
$_ENV[$name] = $value;
|
|
putenv("$name=$value");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Cargar .env si existe
|
|
$envLoaded = function_exists('loadEnvFile') ? loadEnvFile(__DIR__ . '/../.env') : false;
|
|
|
|
// Función helper para obtener variables de entorno con fallback
|
|
if (!function_exists('env')) {
|
|
function env($key, $default = null) {
|
|
return $_ENV[$key] ?? getenv($key) ?: $default;
|
|
}
|
|
}
|
|
|
|
// Configuración de la base de datos
|
|
if (!defined('DB_HOST')) define('DB_HOST', env('DB_HOST', 'localhost'));
|
|
if (!defined('DB_PORT')) define('DB_PORT', env('DB_PORT', '3306'));
|
|
if (!defined('DB_NAME')) define('DB_NAME', env('DB_NAME', 'whatsapp_bot'));
|
|
if (!defined('DB_USER')) define('DB_USER', env('DB_USER', 'whatsapp_user'));
|
|
if (!defined('DB_PASS')) define('DB_PASS', env('DB_PASS', '')); // Se generará automáticamente en la instalación
|
|
if (!defined('DB_CHARSET')) define('DB_CHARSET', env('DB_CHARSET', 'utf8mb4'));
|
|
|
|
// WhatsApp Business API
|
|
if (!defined('WHATSAPP_TOKEN')) define('WHATSAPP_TOKEN', env('WHATSAPP_TOKEN', 'TU_TOKEN_DE_WHATSAPP_AQUI'));
|
|
if (!defined('WHATSAPP_PHONE_NUMBER_ID')) define('WHATSAPP_PHONE_NUMBER_ID', env('WHATSAPP_PHONE_NUMBER_ID', 'TU_PHONE_ID_AQUI'));
|
|
if (!defined('WHATSAPP_API_URL')) define('WHATSAPP_API_URL', env('WHATSAPP_API_URL', 'https://graph.facebook.com/v22.0/'));
|
|
if (!defined('WEBHOOK_VERIFY_TOKEN')) define('WEBHOOK_VERIFY_TOKEN', env('WEBHOOK_VERIFY_TOKEN', 'mi_token_secreto_123'));
|
|
|
|
// Auto-approve templates: Si está activado, las plantillas creadas localmente se marcarán como 'approved' automáticamente
|
|
if (!defined('AUTO_APPROVE_TEMPLATES')) define('AUTO_APPROVE_TEMPLATES', filter_var(env('AUTO_APPROVE_TEMPLATES', 'false'), FILTER_VALIDATE_BOOLEAN));
|
|
|
|
// Configuración general
|
|
if (!defined('APP_NAME')) define('APP_NAME', env('APP_NAME', 'WhatsApp Bot System'));
|
|
if (!defined('APP_VERSION')) define('APP_VERSION', env('APP_VERSION', '1.0.0'));
|
|
if (!defined('APP_URL')) define('APP_URL', env('APP_URL', 'https://tudominio.com'));
|
|
if (!defined('TIMEZONE')) define('TIMEZONE', env('TIMEZONE', 'America/Bogota'));
|
|
|
|
// Información del desarrollador
|
|
if (!defined('DEVELOPER_NAME')) define('DEVELOPER_NAME', 'U-Site.app');
|
|
if (!defined('DEVELOPER_URL')) define('DEVELOPER_URL', 'https://u-site.app');
|
|
if (!defined('DEVELOPER_EMAIL')) define('DEVELOPER_EMAIL', 'support@u-site.app');
|
|
if (!defined('DEVELOPER_SUPPORT')) define('DEVELOPER_SUPPORT', 'https://u-site.app/support');
|
|
|
|
// Configuración de seguridad
|
|
if (!defined('INSTALLATION_LOCK')) define('INSTALLATION_LOCK', '.installation_completed');
|
|
if (!defined('ADMIN_USERNAME')) define('ADMIN_USERNAME', env('ADMIN_USERNAME', 'admin'));
|
|
if (!defined('ADMIN_PASSWORD')) define('ADMIN_PASSWORD', env('ADMIN_PASSWORD', ''));
|
|
if (!defined('SESSION_TIMEOUT')) define('SESSION_TIMEOUT', (int)env('SESSION_TIMEOUT', 1800));
|
|
if (!defined('MAX_LOGIN_ATTEMPTS')) define('MAX_LOGIN_ATTEMPTS', (int)env('MAX_LOGIN_ATTEMPTS', 3));
|
|
if (!defined('LOGIN_LOCKOUT_TIME')) define('LOGIN_LOCKOUT_TIME', (int)env('LOGIN_LOCKOUT_TIME', 900));
|
|
|
|
// Configuración de logs
|
|
if (!defined('ENABLE_LOGGING')) define('ENABLE_LOGGING', filter_var(env('ENABLE_LOGGING', 'true'), FILTER_VALIDATE_BOOLEAN));
|
|
if (!defined('LOG_LEVEL')) define('LOG_LEVEL', env('LOG_LEVEL', 'INFO')); // DEBUG, INFO, WARNING, ERROR
|
|
if (!defined('LOG_FILE')) define('LOG_FILE', env('LOG_FILE', 'logs/system.log'));
|
|
|
|
// Configuración de archivos
|
|
if (!defined('UPLOAD_MAX_SIZE')) define('UPLOAD_MAX_SIZE', (int)env('UPLOAD_MAX_SIZE', 10485760)); // 10MB
|
|
if (!defined('ALLOWED_FILE_TYPES')) define('ALLOWED_FILE_TYPES', ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'mp4', 'mp3', 'wav']);
|
|
|
|
// Configurar zona horaria
|
|
date_default_timezone_set(TIMEZONE);
|
|
|
|
// Configurar errores para desarrollo (se desactivará en producción)
|
|
$isProduction = env('APP_ENV', 'development') === 'production';
|
|
$installationCompleted = file_exists(dirname(__DIR__) . '/.installation_completed');
|
|
|
|
if (!$isProduction && !$installationCompleted) {
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 1);
|
|
} else {
|
|
error_reporting(0);
|
|
ini_set('display_errors', 0);
|
|
}
|
|
|
|
ini_set('log_errors', 1);
|
|
if (ENABLE_LOGGING) {
|
|
ini_set('error_log', dirname(__DIR__) . '/' . LOG_FILE);
|
|
}
|
|
|
|
// Headers de seguridad
|
|
if (!headers_sent()) {
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('X-Frame-Options: SAMEORIGIN');
|
|
header('X-XSS-Protection: 1; mode=block');
|
|
header('Referrer-Policy: strict-origin-when-cross-origin');
|
|
}
|
|
|
|
// Auto-detectar URL base si no está configurada
|
|
if (!defined('AUTO_DETECTED_URL')) {
|
|
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
|
|
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
|
$path = dirname($_SERVER['SCRIPT_NAME'] ?? '');
|
|
define('AUTO_DETECTED_URL', $protocol . $host . $path);
|
|
}
|
|
|
|
// Función para logging centralizado
|
|
if (!function_exists('writeLog')) {
|
|
function writeLog($level, $message, $context = []) {
|
|
if (!ENABLE_LOGGING) return;
|
|
|
|
$logLevels = ['DEBUG' => 0, 'INFO' => 1, 'WARNING' => 2, 'ERROR' => 3];
|
|
$currentLevel = $logLevels[LOG_LEVEL] ?? 1;
|
|
|
|
if (($logLevels[$level] ?? 1) < $currentLevel) {
|
|
return;
|
|
}
|
|
|
|
$timestamp = date('Y-m-d H:i:s');
|
|
$contextStr = $context ? ' ' . json_encode($context) : '';
|
|
$logMessage = "[$timestamp] [$level] $message$contextStr" . PHP_EOL;
|
|
|
|
$logDir = dirname(dirname(__FILE__)) . '/logs';
|
|
if (!is_dir($logDir)) {
|
|
mkdir($logDir, 0755, true);
|
|
}
|
|
|
|
file_put_contents($logDir . '/system.log', $logMessage, FILE_APPEND | LOCK_EX);
|
|
}
|
|
}
|
|
|
|
// Función para verificar si la instalación está completada
|
|
if (!function_exists('isInstallationCompleted')) {
|
|
function isInstallationCompleted() {
|
|
return file_exists(dirname(__DIR__) . '/.installation_completed');
|
|
}
|
|
}
|
|
|
|
// Función para verificar login
|
|
if (!function_exists('isUserLoggedIn')) {
|
|
function isUserLoggedIn() {
|
|
return isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true;
|
|
}
|
|
}
|
|
|
|
// Función para verificar intentos de login
|
|
if (!function_exists('checkLoginAttempts')) {
|
|
function checkLoginAttempts($ip) {
|
|
$attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
|
|
|
|
if (!file_exists($attemptsFile)) {
|
|
return true;
|
|
}
|
|
|
|
$attempts = json_decode(file_get_contents($attemptsFile), true);
|
|
|
|
if (!isset($attempts[$ip])) {
|
|
return true;
|
|
}
|
|
|
|
$ipData = $attempts[$ip];
|
|
|
|
if (time() > $ipData['locked_until']) {
|
|
unset($attempts[$ip]);
|
|
file_put_contents($attemptsFile, json_encode($attempts));
|
|
return true;
|
|
}
|
|
|
|
return $ipData['attempts'] < MAX_LOGIN_ATTEMPTS;
|
|
}
|
|
}
|
|
|
|
// Función para registrar intento de login fallido
|
|
if (!function_exists('recordFailedLogin')) {
|
|
function recordFailedLogin($ip) {
|
|
$attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
|
|
|
|
$attempts = [];
|
|
if (file_exists($attemptsFile)) {
|
|
$attempts = json_decode(file_get_contents($attemptsFile), true) ?: [];
|
|
}
|
|
|
|
if (!isset($attempts[$ip])) {
|
|
$attempts[$ip] = ['attempts' => 0, 'locked_until' => 0];
|
|
}
|
|
|
|
$attempts[$ip]['attempts']++;
|
|
|
|
if ($attempts[$ip]['attempts'] >= MAX_LOGIN_ATTEMPTS) {
|
|
$attempts[$ip]['locked_until'] = time() + LOGIN_LOCKOUT_TIME;
|
|
}
|
|
|
|
file_put_contents($attemptsFile, json_encode($attempts));
|
|
writeLog('WARNING', 'Failed login attempt', ['ip' => $ip, 'attempts' => $attempts[$ip]['attempts']]);
|
|
}
|
|
}
|
|
|
|
// Función para limpiar intentos de login exitoso
|
|
if (!function_exists('clearLoginAttempts')) {
|
|
function clearLoginAttempts($ip) {
|
|
$attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
|
|
|
|
if (file_exists($attemptsFile)) {
|
|
$attempts = json_decode(file_get_contents($attemptsFile), true) ?: [];
|
|
unset($attempts[$ip]);
|
|
file_put_contents($attemptsFile, json_encode($attempts));
|
|
}
|
|
|
|
writeLog('INFO', 'Successful login', ['ip' => $ip]);
|
|
}
|
|
}
|
|
|
|
// Autoloader mejorado
|
|
spl_autoload_register(function ($class) {
|
|
$directories = [
|
|
dirname(__DIR__) . '/classes/',
|
|
dirname(__DIR__) . '/services/',
|
|
dirname(__DIR__) . '/controllers/'
|
|
];
|
|
|
|
foreach ($directories as $directory) {
|
|
$file = $directory . $class . '.php';
|
|
if (file_exists($file)) {
|
|
require_once $file;
|
|
writeLog('DEBUG', 'Class loaded', ['class' => $class, 'file' => $file]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
writeLog('ERROR', 'Class not found', ['class' => $class]);
|
|
});
|
|
|
|
// Función para obtener configuración desde base de datos
|
|
if (!function_exists('getConfigFromDB')) {
|
|
function getConfigFromDB($key, $default = null) {
|
|
static $configs = null;
|
|
|
|
if ($configs === null && isInstallationCompleted()) {
|
|
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,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
]
|
|
);
|
|
|
|
$stmt = $pdo->query("SELECT config_key, config_value FROM system_config");
|
|
$configs = [];
|
|
while ($row = $stmt->fetch()) {
|
|
$configs[$row['config_key']] = $row['config_value'];
|
|
}
|
|
|
|
writeLog('DEBUG', 'Database configuration loaded');
|
|
} catch (PDOException $e) {
|
|
writeLog('ERROR', 'Error loading config from database', ['error' => $e->getMessage()]);
|
|
$configs = [];
|
|
}
|
|
}
|
|
|
|
return $configs[$key] ?? $default;
|
|
}
|
|
}
|
|
|
|
// Función para validar configuración de WhatsApp
|
|
if (!function_exists('validateWhatsAppConfig')) {
|
|
function validateWhatsAppConfig() {
|
|
$errors = [];
|
|
|
|
if (WHATSAPP_TOKEN === 'TU_TOKEN_DE_WHATSAPP_AQUI' || empty(WHATSAPP_TOKEN)) {
|
|
$errors[] = 'Token de WhatsApp no configurado';
|
|
}
|
|
|
|
if (WHATSAPP_PHONE_NUMBER_ID === 'TU_PHONE_ID_AQUI' || empty(WHATSAPP_PHONE_NUMBER_ID)) {
|
|
$errors[] = 'Phone Number ID de WhatsApp no configurado';
|
|
}
|
|
|
|
if (empty(WEBHOOK_VERIFY_TOKEN)) {
|
|
$errors[] = 'Token de verificación del webhook no configurado';
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
}
|
|
|
|
// Función para crear directorio si no existe
|
|
if (!function_exists('ensureDirectoryExists')) {
|
|
function ensureDirectoryExists($path) {
|
|
if (!is_dir($path)) {
|
|
mkdir($path, 0755, true);
|
|
writeLog('INFO', 'Directory created', ['path' => $path]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Crear directorios necesarios
|
|
ensureDirectoryExists(dirname(__DIR__) . '/logs');
|
|
ensureDirectoryExists(dirname(__DIR__) . '/uploads');
|
|
|
|
// Inicializar sesión si no está activa
|
|
if (session_status() == PHP_SESSION_NONE) {
|
|
ini_set('session.cookie_httponly', 1);
|
|
ini_set('session.cookie_secure', isset($_SERVER['HTTPS']));
|
|
ini_set('session.use_strict_mode', 1);
|
|
// Asegurar que la cookie de sesión y GC respeten el timeout configurado (SESSION_TIMEOUT)
|
|
// Si SESSION_TIMEOUT <= 0 lo interpretamos como "persistente" (no expira): usamos un valor grande para cookie y GC
|
|
$cookieLifetime = (int)SESSION_TIMEOUT;
|
|
if ($cookieLifetime <= 0) {
|
|
// 10 años en segundos
|
|
$cookieLifetime = 10 * 365 * 24 * 60 * 60;
|
|
}
|
|
ini_set('session.cookie_lifetime', $cookieLifetime);
|
|
ini_set('session.gc_maxlifetime', $cookieLifetime);
|
|
session_start();
|
|
}
|
|
|
|
// Verificar timeout de sesión (solo si SESSION_TIMEOUT > 0)
|
|
if (isUserLoggedIn() && isset($_SESSION['last_activity'])) {
|
|
if (defined('SESSION_TIMEOUT') && (int)SESSION_TIMEOUT > 0 && (time() - $_SESSION['last_activity'] > (int)SESSION_TIMEOUT)) {
|
|
writeLog('INFO', 'Session timeout', ['user' => $_SESSION['username'] ?? 'unknown']);
|
|
session_destroy();
|
|
session_start();
|
|
}
|
|
}
|
|
|
|
// Actualizar última actividad
|
|
if (isUserLoggedIn()) {
|
|
$_SESSION['last_activity'] = time();
|
|
}
|
|
|
|
// Registrar acceso si está habilitado el logging
|
|
if (ENABLE_LOGGING && isset($_SERVER['REQUEST_URI'])) {
|
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? 'Unknown';
|
|
|
|
writeLog('DEBUG', 'Page access', [
|
|
'uri' => $_SERVER['REQUEST_URI'],
|
|
'ip' => $ip,
|
|
'user_agent' => substr($userAgent, 0, 100)
|
|
]);
|
|
}
|
|
?>
|