Laravel 11, Vuexy Admin 10.3, by admin@koneko.mx

This commit is contained in:
2025-01-25 04:23:40 -06:00
parent c3045b43b1
commit 64d505910f
1283 changed files with 140198 additions and 0 deletions

View File

@ -0,0 +1,215 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use Modules\Admin\App\Models\Setting;
class AdminSettingsService
{
private $driver;
private $imageDisk = 'public';
private $favicon_basePath = 'favicon/';
private $image_logo_basePath = 'images/logo/';
private $faviconsSizes = [
'180x180' => [180, 180],
'192x192' => [192, 192],
'152x152' => [152, 152],
'120x120' => [120, 120],
'76x76' => [76, 76],
'16x16' => [16, 16],
];
private $imageLogoMaxPixels1 = 22500; // Primera versión (px^2)
private $imageLogoMaxPixels2 = 75625; // Segunda versión (px^2)
private $imageLogoMaxPixels3 = 262144; // Tercera versión (px^2)
private $imageLogoMaxPixels4 = 230400; // Tercera versión (px^2) en Base64
protected $cacheTTL = 60 * 24 * 30; // 30 días en minutos
public function __construct()
{
$this->driver = config('image.driver', 'gd');
}
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
public function processAndSaveFavicon($image): void
{
Storage::makeDirectory($this->imageDisk . '/' . $this->favicon_basePath);
// Eliminar favicons antiguos
$this->deleteOldFavicons();
// Guardar imagen original
$imageManager = new ImageManager($this->driver);
$imageName = uniqid('admin_favicon_');
$image = $imageManager->read($image->getRealPath());
foreach ($this->faviconsSizes as $size => [$width, $height]) {
$resizedPath = $this->favicon_basePath . $imageName . "_{$size}.png";
$image->cover($width, $height);
Storage::disk($this->imageDisk)->put($resizedPath, $image->toPng(indexed: true));
}
$this->updateSetting('admin_favicon_ns', $this->favicon_basePath . $imageName);
}
protected function deleteOldFavicons(): void
{
// Obtener el favicon actual desde la base de datos
$currentFavicon = Setting::where('key', 'admin_favicon_ns')->value('value');
if ($currentFavicon) {
$filePaths = [
$this->imageDisk . '/' . $currentFavicon,
$this->imageDisk . '/' . $currentFavicon . '_16x16.png',
$this->imageDisk . '/' . $currentFavicon . '_76x76.png',
$this->imageDisk . '/' . $currentFavicon . '_120x120.png',
$this->imageDisk . '/' . $currentFavicon . '_152x152.png',
$this->imageDisk . '/' . $currentFavicon . '_180x180.png',
$this->imageDisk . '/' . $currentFavicon . '_192x192.png',
];
foreach ($filePaths as $filePath) {
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
}
}
}
public function processAndSaveImageLogo($image, string $type = ''): void
{
// Crear directorio si no existe
Storage::makeDirectory($this->imageDisk . '/' . $this->image_logo_basePath);
// Eliminar imágenes antiguas
$this->deleteOldImageWebapp($type);
// Leer imagen original
$imageManager = new ImageManager($this->driver);
$image = $imageManager->read($image->getRealPath());
// Generar tres versiones con diferentes áreas máximas
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels1, 'small'); // Versión 1
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels2, 'medium'); // Versión 2
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels3); // Versión 3
$this->generateAndSaveImageAsBase64($image, $type, $this->imageLogoMaxPixels4); // Versión 3
}
private function generateAndSaveImage($image, string $type, int $maxPixels, string $suffix = ''): void
{
$imageClone = clone $image;
// Escalar imagen conservando aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
$imageName = 'admin_image_logo' . ($suffix ? '_' . $suffix : '') . ($type == 'dark' ? '_dark' : '');
// Generar nombre y ruta
$imageNameUid = uniqid($imageName . '_', ".png");
$resizedPath = $this->image_logo_basePath . $imageNameUid;
// Guardar imagen en PNG
Storage::disk($this->imageDisk)->put($resizedPath, $imageClone->toPng(indexed: true));
// Actualizar configuración
$this->updateSetting($imageName, $resizedPath);
}
private function resizeImageToMaxPixels($image, int $maxPixels)
{
// Obtener dimensiones originales de la imagen
$originalWidth = $image->width(); // Método para obtener el ancho
$originalHeight = $image->height(); // Método para obtener el alto
// Calcular el aspecto
$aspectRatio = $originalWidth / $originalHeight;
// Calcular dimensiones redimensionadas conservando aspecto
if ($aspectRatio > 1) { // Ancho es dominante
$newWidth = sqrt($maxPixels * $aspectRatio);
$newHeight = $newWidth / $aspectRatio;
} else { // Alto es dominante
$newHeight = sqrt($maxPixels / $aspectRatio);
$newWidth = $newHeight * $aspectRatio;
}
// Redimensionar la imagen
$image->resize(
round($newWidth), // Redondear para evitar problemas con números decimales
round($newHeight),
function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
}
);
return $image;
}
private function generateAndSaveImageAsBase64($image, string $type, int $maxPixels): void
{
$imageClone = clone $image;
// Redimensionar imagen conservando el aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
// Convertir a Base64
$base64Image = (string) $imageClone->toJpg(40)->toDataUri();
// Guardar como configuración
$this->updateSetting(
"admin_image_logo_base64" . ($type === 'dark' ? '_dark' : ''),
$base64Image // Ya incluye "data:image/png;base64,"
);
}
protected function deleteOldImageWebapp(string $type = ''): void
{
// Determinar prefijo según el tipo (normal o dark)
$suffix = $type === 'dark' ? '_dark' : '';
// Claves relacionadas con las imágenes que queremos limpiar
$imageKeys = [
"admin_image_logo{$suffix}",
"admin_image_logo_small{$suffix}",
"admin_image_logo_medium{$suffix}",
];
// Recuperar las imágenes actuales en una sola consulta
$settings = Setting::whereIn('key', $imageKeys)->pluck('value', 'key');
foreach ($imageKeys as $key) {
// Obtener la imagen correspondiente
$currentImage = $settings[$key] ?? null;
if ($currentImage) {
// Construir la ruta del archivo y eliminarlo si existe
$filePath = $this->imageDisk . '/' . $currentImage;
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
// Eliminar la configuración de la base de datos
Setting::where('key', $key)->delete();
}
}
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Modules\Admin\App\Models\Setting;
class AdminTemplateService
{
protected $cacheTTL = 60 * 24 * 30; // 30 días en minutos
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
public function getAdminVars($adminSetting = false)
{
try {
return Cache::remember('admin_settings', $this->cacheTTL, function () use ($adminSetting) {
$settings = Setting::global()
->where('key', 'LIKE', 'admin_%')
->pluck('value', 'key')
->toArray();
$adminSettings = [
'title' => $settings['admin_title'] ?? config('_var.appTitle'),
'author' => config('_var.author'),
'description' => config('_var.description'),
'favicon' => $this->getFaviconPaths($settings),
'app_name' => $settings['admin_app_name'] ?? config('_var.appName'),
'image_logo' => $this->getImageLogoPaths($settings),
];
return $adminSetting
? $adminSettings[$adminSetting]
: $adminSettings;
});
} catch (\Exception $e) {
echo __METHOD__;
echo "<br>" . $e->getMessage() . "<br><br>";
die('You must configure the database.');
}
}
public function getVuexyCustomizerVars()
{
// Obtener valores de la base de datos
$settings = Setting::global()
->where('key', 'LIKE', 'vuexy_%')
->pluck('value', 'key')
->toArray();
// Obtener configuraciones predeterminadas
$defaultConfig = Config::get('custom.custom', []);
// Mezclar las configuraciones predeterminadas con las de la base de datos
return collect($defaultConfig)
->mapWithKeys(function ($defaultValue, $key) use ($settings) {
$vuexyKey = 'vuexy_' . $key; // Convertir clave al formato de la base de datos
// Obtener valor desde la base de datos o usar el predeterminado
$value = $settings[$vuexyKey] ?? $defaultValue;
// Forzar booleanos para claves específicas
if (in_array($key, ['displayCustomizer', 'footerFixed', 'menuFixed', 'menuCollapsed', 'showDropdownOnHover'])) {
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
return [$key => $value];
})
->toArray();
}
/**
* Obtiene los paths de favicon en distintos tamaños.
*/
private function getFaviconPaths(array $settings): array
{
$defaultFavicon = config('_var.appFavicon');
$namespace = $settings['admin_favicon_ns'] ?? null;
return [
'namespace' => $namespace,
'16x16' => $namespace ? "{$namespace}_16x16.png" : $defaultFavicon,
'76x76' => $namespace ? "{$namespace}_76x76.png" : $defaultFavicon,
'120x120' => $namespace ? "{$namespace}_120x120.png" : $defaultFavicon,
'152x152' => $namespace ? "{$namespace}_152x152.png" : $defaultFavicon,
'180x180' => $namespace ? "{$namespace}_180x180.png" : $defaultFavicon,
'192x192' => $namespace ? "{$namespace}_192x192.png" : $defaultFavicon,
];
}
/**
* Obtiene los paths de los logos en distintos tamaños.
*/
private function getImageLogoPaths(array $settings): array
{
$defaultLogo = config('_var.appLogo');
return [
'small' => $this->getImagePath($settings, 'admin_image_logo_small', $defaultLogo),
'medium' => $this->getImagePath($settings, 'admin_image_logo_medium', $defaultLogo),
'large' => $this->getImagePath($settings, 'admin_image_logo', $defaultLogo),
'small_dark' => $this->getImagePath($settings, 'admin_image_logo_small_dark', $defaultLogo),
'medium_dark' => $this->getImagePath($settings, 'admin_image_logo_medium_dark', $defaultLogo),
'large_dark' => $this->getImagePath($settings, 'admin_image_logo_dark', $defaultLogo),
];
}
/**
* Obtiene un path de imagen o retorna un valor predeterminado.
*/
private function getImagePath(array $settings, string $key, string $default): string
{
return $settings[$key] ?? $default;
}
public static function clearAdminVarsCache()
{
Cache::forget("admin_settings");
}
}

View File

@ -0,0 +1,235 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class CacheConfigService
{
public function getConfig(): array
{
return [
'cache' => $this->getCacheConfig(),
'session' => $this->getSessionConfig(),
'database' => $this->getDatabaseConfig(),
'driver' => $this->getDriverVersion(),
'memcachedInUse' => $this->isDriverInUse('memcached'),
'redisInUse' => $this->isDriverInUse('redis'),
];
}
private function getCacheConfig(): array
{
$cacheConfig = Config::get('cache');
$driver = $cacheConfig['default'];
switch ($driver) {
case 'redis':
$connection = config('database.redis.cache');
$cacheConfig['host'] = $connection['host'] ?? 'localhost';
$cacheConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'database':
$connection = config('database.connections.' . config('cache.stores.database.connection'));
$cacheConfig['host'] = $connection['host'] ?? 'localhost';
$cacheConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'memcached':
$servers = config('cache.stores.memcached.servers');
$cacheConfig['host'] = $servers[0]['host'] ?? 'localhost';
$cacheConfig['database'] = 'N/A';
break;
case 'file':
$cacheConfig['host'] = storage_path('framework/cache/data');
$cacheConfig['database'] = 'N/A';
break;
default:
$cacheConfig['host'] = 'N/A';
$cacheConfig['database'] = 'N/A';
break;
}
return $cacheConfig;
}
private function getSessionConfig(): array
{
$sessionConfig = Config::get('session');
$driver = $sessionConfig['driver'];
switch ($driver) {
case 'redis':
$connection = config('database.redis.sessions');
$sessionConfig['host'] = $connection['host'] ?? 'localhost';
$sessionConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'database':
$connection = config('database.connections.' . $sessionConfig['connection']);
$sessionConfig['host'] = $connection['host'] ?? 'localhost';
$sessionConfig['database'] = $connection['database'] ?? 'N/A';
break;
case 'memcached':
$servers = config('cache.stores.memcached.servers');
$sessionConfig['host'] = $servers[0]['host'] ?? 'localhost';
$sessionConfig['database'] = 'N/A';
break;
case 'file':
$sessionConfig['host'] = storage_path('framework/sessions');
$sessionConfig['database'] = 'N/A';
break;
default:
$sessionConfig['host'] = 'N/A';
$sessionConfig['database'] = 'N/A';
break;
}
return $sessionConfig;
}
private function getDatabaseConfig(): array
{
$databaseConfig = Config::get('database');
$connection = $databaseConfig['default'];
$connectionConfig = config('database.connections.' . $connection);
$databaseConfig['host'] = $connectionConfig['host'] ?? 'localhost';
$databaseConfig['database'] = $connectionConfig['database'] ?? 'N/A';
return $databaseConfig;
}
private function getDriverVersion(): array
{
$drivers = [];
$defaultDatabaseDriver = config('database.default'); // Obtén el driver predeterminado
switch ($defaultDatabaseDriver) {
case 'mysql':
case 'mariadb':
$drivers['mysql'] = [
'version' => $this->getMySqlVersion(),
'details' => config("database.connections.$defaultDatabaseDriver"),
];
$drivers['mariadb'] = $drivers['mysql'];
case 'pgsql':
$drivers['pgsql'] = [
'version' => $this->getPgSqlVersion(),
'details' => config("database.connections.pgsql"),
];
break;
case 'sqlsrv':
$drivers['sqlsrv'] = [
'version' => $this->getSqlSrvVersion(),
'details' => config("database.connections.sqlsrv"),
];
break;
default:
$drivers['unknown'] = [
'version' => 'No disponible',
'details' => 'Driver no identificado',
];
break;
}
// Opcional: Agrega detalles de Redis y Memcached si están en uso
if ($this->isDriverInUse('redis')) {
$drivers['redis'] = [
'version' => $this->getRedisVersion(),
];
}
if ($this->isDriverInUse('memcached')) {
$drivers['memcached'] = [
'version' => $this->getMemcachedVersion(),
];
}
return $drivers;
}
private function getMySqlVersion(): string
{
try {
$version = DB::selectOne('SELECT VERSION() as version');
return $version->version ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getPgSqlVersion(): string
{
try {
$version = DB::selectOne("SHOW server_version");
return $version->server_version ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getSqlSrvVersion(): string
{
try {
$version = DB::selectOne("SELECT @@VERSION as version");
return $version->version ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getMemcachedVersion(): string
{
try {
$memcached = new \Memcached();
$memcached->addServer(
Config::get('cache.stores.memcached.servers.0.host'),
Config::get('cache.stores.memcached.servers.0.port')
);
$stats = $memcached->getStats();
foreach ($stats as $serverStats) {
return $serverStats['version'] ?? 'No disponible';
}
return 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function getRedisVersion(): string
{
try {
$info = Redis::info();
return $info['redis_version'] ?? 'No disponible';
} catch (\Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
protected function isDriverInUse(string $driver): bool
{
return in_array($driver, [
Config::get('cache.default'),
Config::get('session.driver'),
Config::get('queue.default'),
]);
}
}

View File

@ -0,0 +1,389 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\File;
class CacheManagerService
{
private string $driver;
public function __construct(string $driver = null)
{
$this->driver = $driver ?? config('cache.default');
}
/**
* Obtiene estadísticas de caché para el driver especificado.
*/
public function getCacheStats(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver)) {
return $this->response('warning', 'Driver no soportado o no configurado.');
}
try {
return match ($driver) {
'database' => $this->_getDatabaseStats(),
'file' => $this->_getFilecacheStats(),
'redis' => $this->_getRedisStats(),
'memcached' => $this->_getMemcachedStats(),
default => $this->response('info', 'No hay estadísticas disponibles para este driver.'),
};
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas: ' . $e->getMessage());
}
}
public function clearCache(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver)) {
return $this->response('warning', 'Driver no soportado o no configurado.');
}
try {
switch ($driver) {
case 'redis':
$keysCleared = $this->clearRedisCache();
return $keysCleared
? $this->response('warning', 'Se ha purgado toda la caché de Redis.')
: $this->response('info', 'No se encontraron claves en Redis para eliminar.');
case 'memcached':
$keysCleared = $this->clearMemcachedCache();
return $keysCleared
? $this->response('warning', 'Se ha purgado toda la caché de Memcached.')
: $this->response('info', 'No se encontraron claves en Memcached para eliminar.');
case 'database':
$rowsDeleted = $this->clearDatabaseCache();
return $rowsDeleted
? $this->response('warning', 'Se ha purgado toda la caché almacenada en la base de datos.')
: $this->response('info', 'No se encontraron registros en la caché de la base de datos.');
case 'file':
$filesDeleted = $this->clearFilecache();
return $filesDeleted
? $this->response('warning', 'Se ha purgado toda la caché de archivos.')
: $this->response('info', 'No se encontraron archivos en la caché para eliminar.');
default:
Cache::flush();
return $this->response('warning', 'Caché purgada.');
}
} catch (\Exception $e) {
return $this->response('danger', 'Error al limpiar la caché: ' . $e->getMessage());
}
}
public function getRedisStats()
{
try {
if (!Redis::ping()) {
return $this->response('warning', 'No se puede conectar con el servidor Redis.');
}
$info = Redis::info();
$databases = $this->getRedisDatabases();
$redisInfo = [
'server' => config('database.redis.default.host'),
'redis_version' => $info['redis_version'] ?? 'N/A',
'os' => $info['os'] ?? 'N/A',
'tcp_port' => $info['tcp_port'] ?? 'N/A',
'connected_clients' => $info['connected_clients'] ?? 'N/A',
'blocked_clients' => $info['blocked_clients'] ?? 'N/A',
'maxmemory' => $info['maxmemory'] ?? 0,
'used_memory_human' => $info['used_memory_human'] ?? 'N/A',
'used_memory_peak' => $info['used_memory_peak'] ?? 'N/A',
'used_memory_peak_human' => $info['used_memory_peak_human'] ?? 'N/A',
'total_system_memory' => $info['total_system_memory'] ?? 0,
'total_system_memory_human' => $info['total_system_memory_human'] ?? 'N/A',
'maxmemory_human' => $info['maxmemory_human'] !== '0B' ? $info['maxmemory_human'] : 'Sin Límite',
'total_connections_received' => number_format($info['total_connections_received']) ?? 'N/A',
'total_commands_processed' => number_format($info['total_commands_processed']) ?? 'N/A',
'maxmemory_policy' => $info['maxmemory_policy'] ?? 'N/A',
'role' => $info['role'] ?? 'N/A',
'cache_database' => '',
'sessions_database' => '',
'general_database' => ',',
'keys' => $databases['total_keys'],
'used_memory' => $info['used_memory'] ?? 0,
'uptime' => gmdate('H\h i\m s\s', $info['uptime_in_seconds'] ?? 0),
'databases' => $databases,
];
return $this->response('success', 'Se a recargado las estadísticas de Redis.', ['info' => $redisInfo]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al conectar con el servidor Redis: ' . Redis::getLastError());
}
}
public function getMemcachedStats()
{
try {
$memcachedStats = [];
// Crear instancia del cliente Memcached
$memcached = new \Memcached();
$memcached->addServer(config('memcached.host'), config('memcached.port'));
// Obtener estadísticas del servidor
$stats = $memcached->getStats();
foreach ($stats as $server => $data) {
$server = explode(':', $server);
$memcachedStats[] = [
'server' => $server[0],
'tcp_port' => $server[1],
'uptime' => $data['uptime'] ?? 'N/A',
'version' => $data['version'] ?? 'N/A',
'libevent' => $data['libevent'] ?? 'N/A',
'max_connections' => $data['max_connections'] ?? 0,
'total_connections' => $data['total_connections'] ?? 0,
'rejected_connections' => $data['rejected_connections'] ?? 0,
'curr_items' => $data['curr_items'] ?? 0, // Claves almacenadas
'bytes' => $data['bytes'] ?? 0, // Memoria usada
'limit_maxbytes' => $data['limit_maxbytes'] ?? 0, // Memoria máxima
'cmd_get' => $data['cmd_get'] ?? 0, // Comandos GET ejecutados
'cmd_set' => $data['cmd_set'] ?? 0, // Comandos SET ejecutados
'get_hits' => $data['get_hits'] ?? 0, // GET exitosos
'get_misses' => $data['get_misses'] ?? 0, // GET fallidos
'evictions' => $data['evictions'] ?? 0, // Claves expulsadas
'bytes_read' => $data['bytes_read'] ?? 0, // Bytes leídos
'bytes_written' => $data['bytes_written'] ?? 0, // Bytes escritos
'total_items' => $data['total_items'] ?? 0,
];
}
return $this->response('success', 'Se a recargado las estadísticas de Memcached.', ['info' => $memcachedStats]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al conectar con el servidor Memcached: ' . $e->getMessage());
}
}
/**
* Obtiene estadísticas para caché en base de datos.
*/
private function _getDatabaseStats(): array
{
try {
$recordCount = DB::table('cache')->count();
$tableInfo = DB::select("SHOW TABLE STATUS WHERE Name = 'cache'");
$memory_usage = isset($tableInfo[0]) ? $this->formatBytes($tableInfo[0]->Data_length + $tableInfo[0]->Index_length) : 'N/A';
return $this->response('success', 'Se ha recargado la información de la caché de base de datos.', ['item_count' => $recordCount, 'memory_usage' => $memory_usage]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de la base de datos: ' . $e->getMessage());
}
}
/**
* Obtiene estadísticas para caché en archivos.
*/
private function _getFilecacheStats(): array
{
try {
$cachePath = config('cache.stores.file.path');
$files = glob($cachePath . '/*');
$memory_usage = $this->formatBytes(array_sum(array_map('filesize', $files)));
return $this->response('success', 'Se ha recargado la información de la caché de archivos.', ['item_count' => count($files), 'memory_usage' => $memory_usage]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de archivos: ' . $e->getMessage());
}
}
private function _getRedisStats()
{
try {
$prefix = config('cache.prefix'); // Asegúrate de agregar el sufijo correcto si es necesario
$info = Redis::info();
$keys = Redis::connection('cache')->keys($prefix . '*');
$memory_usage = $this->formatBytes($info['used_memory'] ?? 0);
return $this->response('success', 'Se ha recargado la información de la caché de Redis.', ['item_count' => count($keys), 'memory_usage' => $memory_usage]);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de Redis: ' . $e->getMessage());
}
}
public function _getMemcachedStats(): array
{
try {
// Obtener estadísticas generales del servidor
$stats = Cache::getStore()->getMemcached()->getStats();
if (empty($stats)) {
return $this->response('error', 'No se pudieron obtener las estadísticas del servidor Memcached.', ['item_count' => 0, 'memory_usage' => 0]);
}
// Usar el primer servidor configurado (en la mayoría de los casos hay uno)
$serverStats = array_shift($stats);
return $this->response(
'success',
'Estadísticas del servidor Memcached obtenidas correctamente.',
[
'item_count' => $serverStats['curr_items'] ?? 0, // Número total de claves
'memory_usage' => $this->formatBytes($serverStats['bytes'] ?? 0), // Memoria usada
'max_memory' => $this->formatBytes($serverStats['limit_maxbytes'] ?? 0), // Memoria máxima asignada
]
);
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas de Memcached: ' . $e->getMessage());
}
}
private function getRedisDatabases(): array
{
// Verificar si Redis está en uso
$isRedisUsed = collect([
config('cache.default'),
config('session.driver'),
config('queue.default'),
])->contains('redis');
if (!$isRedisUsed) {
return []; // Si Redis no está en uso, devolver un arreglo vacío
}
// Configuraciones de bases de datos de Redis según su uso
$databases = [
'default' => config('database.redis.default.database', 0), // REDIS_DB
'cache' => config('database.redis.cache.database', 0), // REDIS_CACHE_DB
'sessions' => config('database.redis.sessions.database', 0), // REDIS_SESSION_DB
];
$result = [];
$totalKeys = 0;
// Recorrer solo las bases configuradas y activas
foreach ($databases as $type => $db) {
Redis::select($db); // Seleccionar la base de datos
$keys = Redis::dbsize(); // Contar las claves en la base
if ($keys > 0) {
$result[$type] = [
'database' => $db,
'keys' => $keys,
];
$totalKeys += $keys;
}
}
if (!empty($result)) {
$result['total_keys'] = $totalKeys;
}
return $result;
}
private function clearDatabaseCache(): bool
{
$count = DB::table(config('cache.stores.database.table'))->count();
if ($count > 0) {
DB::table(config('cache.stores.database.table'))->truncate();
return true;
}
return false;
}
private function clearFilecache(): bool
{
$cachePath = config('cache.stores.file.path');
$files = glob($cachePath . '/*');
if (!empty($files)) {
File::deleteDirectory($cachePath);
return true;
}
return false;
}
private function clearRedisCache(): bool
{
$prefix = config('cache.prefix', '');
$keys = Redis::connection('cache')->keys($prefix . '*');
if (!empty($keys)) {
Redis::connection('cache')->flushdb();
// Simulate cache clearing delay
sleep(1);
return true;
}
return false;
}
private function clearMemcachedCache(): bool
{
// Obtener el cliente Memcached directamente
$memcached = Cache::store('memcached')->getStore()->getMemcached();
// Ejecutar flush para eliminar todo
if ($memcached->flush()) {
// Simulate cache clearing delay
sleep(1);
return true;
}
return false;
}
/**
* Verifica si un driver es soportado.
*/
private function isSupportedDriver(string $driver): bool
{
return in_array($driver, ['redis', 'memcached', 'database', 'file']);
}
/**
* Convierte bytes en un formato legible.
*/
private function formatBytes($bytes)
{
$sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf('%.2f', $bytes / pow(1024, $factor)) . ' ' . $sizes[$factor];
}
/**
* Genera una respuesta estandarizada.
*/
private function response(string $status, string $message, array $data = []): array
{
return array_merge(compact('status', 'message'), $data);
}
}

View File

@ -0,0 +1,244 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Crypt;
use Modules\Admin\App\Models\Setting;
class GlobalSettingsService
{
/**
* Tiempo de vida del caché en minutos (30 días).
*/
private $cacheTTL = 60 * 24 * 30;
/**
* Actualiza o crea una configuración.
*/
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
/**
* Carga y sobrescribe las configuraciones del sistema.
*/
public function loadSystemConfig(): void
{
try {
$config = Cache::remember('global_system_config', $this->cacheTTL, function () {
$settings = Setting::global()
->where('key', 'LIKE', 'config.%')
->pluck('value', 'key')
->toArray();
return [
'servicesFacebook' => $this->buildServiceConfig($settings, 'config.services.facebook.', 'services.facebook'),
'servicesGoogle' => $this->buildServiceConfig($settings, 'config.services.google.', 'services.google'),
'custom' => $this->buildVuexyCustomConfig($settings),
];
});
Config::set('services.facebook', $config['servicesFacebook']);
Config::set('services.google', $config['servicesGoogle']);
Config::set('custom', $config['custom']);
} catch (\Exception $e) {
echo __METHOD__;
echo "<br>" . $e->getMessage() . "<br><br>";
die('You must configure the database.');
}
}
/**
* Verifica si un bloque de configuraciones está presente.
*/
protected function hasBlockConfig(array $settings, string $blockPrefix): bool
{
return array_key_exists($blockPrefix, array_filter($settings, fn($key) => str_starts_with($key, $blockPrefix), ARRAY_FILTER_USE_KEY));
}
/**
* Construye la configuración de un servicio (Facebook, Google, etc.).
*/
protected function buildServiceConfig(array $settings, string $blockPrefix, string $defaultConfigKey): array
{
if (!$this->hasBlockConfig($settings, $blockPrefix)) {
return config($defaultConfigKey);
}
return [
'client_id' => $settings["{$blockPrefix}client_id"] ?? '',
'client_secret' => $settings["{$blockPrefix}client_secret"] ?? '',
'redirect' => $settings["{$blockPrefix}redirect"] ?? '',
];
}
/**
* Construye la configuración personalizada de Vuexy.
*/
protected function buildVuexyCustomConfig(array $settings): array
{
// Configuración predeterminada del sistema
$defaultCustomConfig = config('custom', []);
// Convertimos las claves planas a un array multidimensional
$settingsNested = Arr::undot($settings);
// Navegamos hasta la parte relevante del array desanidado
$customSettings = $settingsNested['config']['custom'] ?? [];
// Fusionamos la configuración predeterminada con los valores del sistema
$mergedConfig = array_replace_recursive($defaultCustomConfig, $customSettings);
// Normalizamos los valores booleanos
return $this->normalizeBooleanFields($mergedConfig);
}
/**
* Normaliza los campos booleanos.
*/
protected function normalizeBooleanFields(array $config): array
{
$booleanFields = [
'myRTLSupport',
'myRTLMode',
'hasCustomizer',
'displayCustomizer',
'footerFixed',
'menuFixed',
'menuCollapsed',
'showDropdownOnHover',
];
foreach ($booleanFields as $field) {
if (isset($config['custom'][$field])) {
$config['custom'][$field] = (bool) $config['custom'][$field];
}
}
return $config;
}
/**
* Limpia el caché de la configuración del sistema.
*/
public static function clearSystemConfigCache(): void
{
Cache::forget('global_system_config');
}
/**
* Elimina las claves config.custom.* y limpia global_system_config
*/
public static function clearVuexyCustomConfig(): void
{
Setting::where('key', 'LIKE', 'config.custom.%')->delete();
Cache::forget('global_system_config');
}
/**
* Obtiene y sobrescribe la configuración de correo electrónico.
*/
public function getMailSystemConfig(): array
{
return Cache::remember('mail_system_config', $this->cacheTTL, function () {
$settings = Setting::global()
->where('key', 'LIKE', 'mail.%')
->pluck('value', 'key')
->toArray();
$defaultMailersSmtpVars = config('mail.mailers.smtp');
return [
'mailers' => [
'smtp' => array_merge($defaultMailersSmtpVars, [
'url' => $settings['mail.mailers.smtp.url'] ?? $defaultMailersSmtpVars['url'],
'host' => $settings['mail.mailers.smtp.host'] ?? $defaultMailersSmtpVars['host'],
'port' => $settings['mail.mailers.smtp.port'] ?? $defaultMailersSmtpVars['port'],
'encryption' => $settings['mail.mailers.smtp.encryption'] ?? $defaultMailersSmtpVars['encryption'],
'username' => $settings['mail.mailers.smtp.username'] ?? $defaultMailersSmtpVars['username'],
'password' => isset($settings['mail.mailers.smtp.password']) && !empty($settings['mail.mailers.smtp.password'])
? Crypt::decryptString($settings['mail.mailers.smtp.password'])
: $defaultMailersSmtpVars['password'],
'timeout' => $settings['mail.mailers.smtp.timeout'] ?? $defaultMailersSmtpVars['timeout'],
]),
],
'from' => [
'address' => $settings['mail.from.address'] ?? config('mail.from.address'),
'name' => $settings['mail.from.name'] ?? config('mail.from.name'),
],
'reply_to' => [
'method' => $settings['mail.reply_to.method'] ?? config('mail.reply_to.method'),
'email' => $settings['mail.reply_to.email'] ?? config('mail.reply_to.email'),
'name' => $settings['mail.reply_to.name'] ?? config('mail.reply_to.name'),
],
];
});
}
/**
* Limpia el caché de la configuración de correo electrónico.
*/
public static function clearMailSystemConfigCache(): void
{
Cache::forget('mail_system_config');
}
/*
protected function buildFortifyFeatures(array $settings): array
{
return array_filter([
!empty($settings['config.fortify.features.registration']) && $settings['config.fortify.features.registration'] == 1
? \Laravel\Fortify\Features::registration()
: null,
!empty($settings['config.fortify.features.resetPasswords']) && $settings['config.fortify.features.resetPasswords'] == 1
? \Laravel\Fortify\Features::resetPasswords()
: null,
!empty($settings['config.fortify.features.emailVerification']) && $settings['config.fortify.features.emailVerification'] == 1
? \Laravel\Fortify\Features::emailVerification()
: null,
!empty($settings['config.fortify.features.updateProfileInformation']) && $settings['config.fortify.features.updateProfileInformation'] == 1
? \Laravel\Fortify\Features::updateProfileInformation()
: null,
!empty($settings['config.fortify.features.updatePasswords']) && $settings['config.fortify.features.updatePasswords'] == 1
? \Laravel\Fortify\Features::updatePasswords()
: null,
!empty($settings['config.fortify.features.twoFactorAuthentication.confirm']) && $settings['config.fortify.features.twoFactorAuthentication.confirm'] == 1
? \Laravel\Fortify\Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => !empty($settings['config.fortify.features.twoFactorAuthentication.confirmPassword'])
&& $settings['config.fortify.features.twoFactorAuthentication.confirmPassword'] == 1,
'window' => $settings['config.fortify.features.twoFactorAuthentication.window'] ?? 1,
])
: null,
]);
}
protected function loadUserSettings()
{
if (Auth::check()) {
$userId = Auth::id();
// Cargar configuraciones del usuario desde la caché
return Cache::remember("user_settings_{$userId}", $this->cacheTTL, function () use ($userId) {
return \App\Models\Setting::forUser($userId)->pluck('value', 'key')->toArray();
});
}
return [];
}
*/
}

View File

@ -0,0 +1,153 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class SessionManagerService
{
private string $driver;
public function __construct(string $driver = null)
{
$this->driver = $driver ?? config('session.driver');
}
public function getSessionStats(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver))
return $this->response('warning', 'Driver no soportado o no configurado.', ['session_count' => 0]);
try {
switch ($driver) {
case 'redis':
return $this->getRedisStats();
case 'database':
return $this->getDatabaseStats();
case 'file':
return $this->getFileStats();
default:
return $this->response('warning', 'Driver no reconocido.', ['session_count' => 0]);
}
} catch (\Exception $e) {
return $this->response('danger', 'Error al obtener estadísticas: ' . $e->getMessage(), ['session_count' => 0]);
}
}
public function clearSessions(string $driver = null): array
{
$driver = $driver ?? $this->driver;
if (!$this->isSupportedDriver($driver)) {
return $this->response('warning', 'Driver no soportado o no configurado.');
}
try {
switch ($driver) {
case 'redis':
return $this->clearRedisSessions();
case 'memcached':
Cache::getStore()->flush();
return $this->response('success', 'Se eliminó la memoria caché de sesiones en Memcached.');
case 'database':
DB::table('sessions')->truncate();
return $this->response('success', 'Se eliminó la memoria caché de sesiones en la base de datos.');
case 'file':
return $this->clearFileSessions();
default:
return $this->response('warning', 'Driver no reconocido.');
}
} catch (\Exception $e) {
return $this->response('danger', 'Error al limpiar las sesiones: ' . $e->getMessage());
}
}
private function getRedisStats()
{
$prefix = config('cache.prefix'); // Asegúrate de agregar el sufijo correcto si es necesario
$keys = Redis::connection('sessions')->keys($prefix . '*');
return $this->response('success', 'Se ha recargado la información de la caché de Redis.', ['session_count' => count($keys)]);
}
private function getDatabaseStats(): array
{
$sessionCount = DB::table('sessions')->count();
return $this->response('success', 'Se ha recargado la información de la base de datos.', ['session_count' => $sessionCount]);
}
private function getFileStats(): array
{
$cachePath = config('session.files');
$files = glob($cachePath . '/*');
return $this->response('success', 'Se ha recargado la información de sesiones de archivos.', ['session_count' => count($files)]);
}
/**
* Limpia sesiones en Redis.
*/
private function clearRedisSessions(): array
{
$prefix = config('cache.prefix', '');
$keys = Redis::connection('sessions')->keys($prefix . '*');
if (!empty($keys)) {
Redis::connection('sessions')->flushdb();
// Simulate cache clearing delay
sleep(1);
return $this->response('success', 'Se eliminó la memoria caché de sesiones en Redis.');
}
return $this->response('info', 'No se encontraron claves para eliminar en Redis.');
}
/**
* Limpia sesiones en archivos.
*/
private function clearFileSessions(): array
{
$cachePath = config('session.files');
$files = glob($cachePath . '/*');
if (!empty($files)) {
foreach ($files as $file) {
unlink($file);
}
return $this->response('success', 'Se eliminó la memoria caché de sesiones en archivos.');
}
return $this->response('info', 'No se encontraron sesiones en archivos para eliminar.');
}
private function isSupportedDriver(string $driver): bool
{
return in_array($driver, ['redis', 'memcached', 'database', 'file']);
}
/**
* Genera una respuesta estandarizada.
*/
private function response(string $status, string $message, array $data = []): array
{
return array_merge(compact('status', 'message'), $data);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,213 @@
<?php
namespace Modules\Admin\App\Services;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use Modules\Admin\App\Models\Setting;
class WebsiteSettingsService
{
private $driver;
private $imageDisk = 'public';
private $favicon_basePath = 'favicon/';
private $image_logo_basePath = 'images/logo/';
private $faviconsSizes = [
'180x180' => [180, 180],
'192x192' => [192, 192],
'152x152' => [152, 152],
'120x120' => [120, 120],
'76x76' => [76, 76],
'16x16' => [16, 16],
];
private $imageLogoMaxPixels1 = 22500; // Primera versión (px^2)
private $imageLogoMaxPixels2 = 75625; // Segunda versión (px^2) en Base64
private $imageLogoMaxPixels3 = 230400; // Tercera versión (px^2)
protected $cacheTTL = 60 * 24 * 30; // 30 días en minutos
public function __construct()
{
$this->driver = config('image.driver', 'gd');
}
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
public function processAndSaveFavicon($image): void
{
Storage::makeDirectory($this->imageDisk . '/' . $this->favicon_basePath);
// Eliminar favicons antiguos
$this->deleteOldFavicons();
// Guardar imagen original
$imageManager = new ImageManager($this->driver);
$imageName = uniqid('website_favicon_');
$image = $imageManager->read($image->getRealPath());
foreach ($this->faviconsSizes as $size => [$width, $height]) {
$resizedPath = $this->favicon_basePath . $imageName . "_{$size}.png";
$image->cover($width, $height);
Storage::disk($this->imageDisk)->put($resizedPath, $image->toPng(indexed: true));
}
$this->updateSetting('website_favicon_ns', $this->favicon_basePath . $imageName);
}
protected function deleteOldFavicons(): void
{
// Obtener el favicon actual desde la base de datos
$currentFavicon = Setting::where('key', 'website_favicon')->value('value');
if ($currentFavicon) {
$filePaths = [
$this->imageDisk . '/' . $currentFavicon,
$this->imageDisk . '/' . $currentFavicon . '_16x16.png',
$this->imageDisk . '/' . $currentFavicon . '_192x192.png',
$this->imageDisk . '/' . $currentFavicon . '_76x76.png',
$this->imageDisk . '/' . $currentFavicon . '_120x120.png',
$this->imageDisk . '/' . $currentFavicon . '_152x152.png',
$this->imageDisk . '/' . $currentFavicon . '_180x180.png',
];
foreach ($filePaths as $filePath) {
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
}
}
}
public function processAndSaveImageLogo($image, string $type = ''): void
{
// Crear directorio si no existe
Storage::makeDirectory($this->imageDisk . '/' . $this->image_logo_basePath);
// Eliminar imágenes antiguas
$this->deleteOldImageWebapp($type);
// Leer imagen original
$imageManager = new ImageManager($this->driver);
$image = $imageManager->read($image->getRealPath());
// Generar tres versiones con diferentes áreas máximas
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels1, 'small'); // Versión 1
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels2, 'medium'); // Versión 2
$this->generateAndSaveImage($image, $type, $this->imageLogoMaxPixels3); // Versión 3
$this->generateAndSaveImageAsBase64($image, $type, $this->imageLogoMaxPixels3); // Versión 3
}
private function generateAndSaveImage($image, string $type, int $maxPixels, string $suffix = ''): void
{
$imageClone = clone $image;
// Escalar imagen conservando aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
$imageName = 'website_image_logo' . ($suffix ? '_' . $suffix : '') . ($type == 'dark' ? '_dark' : '');
// Generar nombre y ruta
$imageNameUid = uniqid($imageName . '_', ".png");
$resizedPath = $this->image_logo_basePath . $imageNameUid;
// Guardar imagen en PNG
Storage::disk($this->imageDisk)->put($resizedPath, $imageClone->toPng(indexed: true));
// Actualizar configuración
$this->updateSetting($imageName, $resizedPath);
}
private function resizeImageToMaxPixels($image, int $maxPixels)
{
// Obtener dimensiones originales de la imagen
$originalWidth = $image->width(); // Método para obtener el ancho
$originalHeight = $image->height(); // Método para obtener el alto
// Calcular el aspecto
$aspectRatio = $originalWidth / $originalHeight;
// Calcular dimensiones redimensionadas conservando aspecto
if ($aspectRatio > 1) { // Ancho es dominante
$newWidth = sqrt($maxPixels * $aspectRatio);
$newHeight = $newWidth / $aspectRatio;
} else { // Alto es dominante
$newHeight = sqrt($maxPixels / $aspectRatio);
$newWidth = $newHeight * $aspectRatio;
}
// Redimensionar la imagen
$image->resize(
round($newWidth), // Redondear para evitar problemas con números decimales
round($newHeight),
function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
}
);
return $image;
}
private function generateAndSaveImageAsBase64($image, string $type, int $maxPixels): void
{
$imageClone = clone $image;
// Redimensionar imagen conservando el aspecto
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
// Convertir a Base64
$base64Image = (string) $imageClone->toJpg(40)->toDataUri();
// Guardar como configuración
$this->updateSetting(
"website_image_logo_base64" . ($type === 'dark' ? '_dark' : ''),
$base64Image // Ya incluye "data:image/png;base64,"
);
}
protected function deleteOldImageWebapp(string $type = ''): void
{
// Determinar prefijo según el tipo (normal o dark)
$suffix = $type === 'dark' ? '_dark' : '';
// Claves relacionadas con las imágenes que queremos limpiar
$imageKeys = [
"website_image_logo{$suffix}",
"website_image_logo_small{$suffix}",
"website_image_logo_medium{$suffix}",
"website_image_logo_base64{$suffix}",
];
foreach ($imageKeys as $key) {
// Obtener la ruta de la imagen actual desde la base de datos
$currentImage = Setting::where('key', $key)->value('value');
// Si es una imagen en disco, eliminarla
if ($currentImage && !str_starts_with($currentImage, 'data:image')) {
$filePath = $this->imageDisk . '/' . $currentImage;
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
}
// Opcional: Eliminar la configuración de la base de datos
Setting::where('key', $key)->delete();
}
}
}