Prepare modules

This commit is contained in:
2025-03-22 12:44:30 -06:00
parent 099267ee07
commit 7d8566350d
137 changed files with 3723 additions and 4325 deletions

View File

@ -6,13 +6,29 @@ use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use Koneko\VuexyAdmin\Models\Setting;
/**
* Servicio para gestionar la configuración administrativa de VuexyAdmin
*
* Este servicio maneja el procesamiento y almacenamiento de imágenes del favicon
* y logos del panel administrativo, incluyendo diferentes versiones y tamaños.
*
* @package Koneko\VuexyAdmin\Services
*/
class AdminSettingsService
{
/** @var string Driver de procesamiento de imágenes */
private $driver;
private $imageDisk = 'public';
private $favicon_basePath = 'favicon/';
/** @var string Disco de almacenamiento para imágenes */
private $imageDisk = 'public';
/** @var string Ruta base para favicons */
private $favicon_basePath = 'favicon/';
/** @var string Ruta base para logos */
private $image_logo_basePath = 'images/logo/';
/** @var array<string,array<int>> Tamaños predefinidos para favicons */
private $faviconsSizes = [
'180x180' => [180, 180],
'192x192' => [192, 192],
@ -22,28 +38,40 @@ class AdminSettingsService
'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
/** @var int Área máxima en píxeles para la primera versión del logo */
private $imageLogoMaxPixels1 = 22500;
protected $cacheTTL = 60 * 24 * 30; // 30 días en minutos
/** @var int Área máxima en píxeles para la segunda versión del logo */
private $imageLogoMaxPixels2 = 75625;
/** @var int Área máxima en píxeles para la tercera versión del logo */
private $imageLogoMaxPixels3 = 262144;
/** @var int Área máxima en píxeles para la versión base64 del logo */
private $imageLogoMaxPixels4 = 230400;
/** @var int Tiempo de vida en caché en minutos */
protected $cacheTTL = 60 * 24 * 30;
/**
* Constructor del servicio
*
* Inicializa el driver de procesamiento de imágenes desde la configuración
*/
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();
}
/**
* Procesa y guarda un nuevo favicon
*
* Genera múltiples versiones del favicon en diferentes tamaños predefinidos,
* elimina las versiones anteriores y actualiza la configuración.
*
* @param \Illuminate\Http\UploadedFile $image Archivo de imagen subido
* @return void
*/
public function processAndSaveFavicon($image): void
{
Storage::makeDirectory($this->imageDisk . '/' . $this->favicon_basePath);
@ -66,13 +94,20 @@ class AdminSettingsService
Storage::disk($this->imageDisk)->put($resizedPath, $image->toPng(indexed: true));
}
$this->updateSetting('admin_favicon_ns', $this->favicon_basePath . $imageName);
// Actualizar configuración utilizando SettingService
$SettingsService = app(SettingsService::class);
$SettingsService->set('admin.favicon_ns', $this->favicon_basePath . $imageName, null, 'vuexy-admin');
}
/**
* Elimina los favicons antiguos del almacenamiento
*
* @return void
*/
protected function deleteOldFavicons(): void
{
// Obtener el favicon actual desde la base de datos
$currentFavicon = Setting::where('key', 'admin_favicon_ns')->value('value');
$currentFavicon = Setting::where('key', 'admin.favicon_ns')->value('value');
if ($currentFavicon) {
$filePaths = [
@ -93,6 +128,16 @@ class AdminSettingsService
}
}
/**
* Procesa y guarda un nuevo logo
*
* Genera múltiples versiones del logo con diferentes tamaños máximos,
* incluyendo una versión en base64, y actualiza la configuración.
*
* @param \Illuminate\Http\UploadedFile $image Archivo de imagen subido
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
* @return void
*/
public function processAndSaveImageLogo($image, string $type = ''): void
{
// Crear directorio si no existe
@ -112,6 +157,15 @@ class AdminSettingsService
$this->generateAndSaveImageAsBase64($image, $type, $this->imageLogoMaxPixels4); // Versión 3
}
/**
* Genera y guarda una versión del logo
*
* @param \Intervention\Image\Interfaces\ImageInterface $image Imagen a procesar
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
* @param int $maxPixels Área máxima en píxeles
* @param string $suffix Sufijo para el nombre del archivo
* @return void
*/
private function generateAndSaveImage($image, string $type, int $maxPixels, string $suffix = ''): void
{
$imageClone = clone $image;
@ -120,6 +174,7 @@ class AdminSettingsService
$this->resizeImageToMaxPixels($imageClone, $maxPixels);
$imageName = 'admin_image_logo' . ($suffix ? '_' . $suffix : '') . ($type == 'dark' ? '_dark' : '');
$keyValue = 'admin.image.logo' . ($suffix ? '_' . $suffix : '') . ($type == 'dark' ? '_dark' : '');
// Generar nombre y ruta
$imageNameUid = uniqid($imageName . '_', ".png");
@ -129,9 +184,17 @@ class AdminSettingsService
Storage::disk($this->imageDisk)->put($resizedPath, $imageClone->toPng(indexed: true));
// Actualizar configuración
$this->updateSetting($imageName, $resizedPath);
$SettingsService = app(SettingsService::class);
$SettingsService->set($keyValue, $resizedPath, null, 'vuexy-admin');
}
/**
* Redimensiona una imagen manteniendo su proporción
*
* @param \Intervention\Image\Interfaces\ImageInterface $image Imagen a redimensionar
* @param int $maxPixels Área máxima en píxeles
* @return \Intervention\Image\Interfaces\ImageInterface
*/
private function resizeImageToMaxPixels($image, int $maxPixels)
{
// Obtener dimensiones originales de la imagen
@ -163,7 +226,14 @@ class AdminSettingsService
return $image;
}
/**
* Genera y guarda una versión del logo en formato base64
*
* @param \Intervention\Image\Interfaces\ImageInterface $image Imagen a procesar
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
* @param int $maxPixels Área máxima en píxeles
* @return void
*/
private function generateAndSaveImageAsBase64($image, string $type, int $maxPixels): void
{
$imageClone = clone $image;
@ -175,12 +245,16 @@ class AdminSettingsService
$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,"
);
$SettingsService = app(SettingsService::class);
$SettingsService->set("admin.image.logo_base64" . ($type === 'dark' ? '_dark' : ''), $base64Image, null, 'vuexy-admin');
}
/**
* Elimina las imágenes antiguas del logo
*
* @param string $type Tipo de logo ('dark' para modo oscuro, '' para normal)
* @return void
*/
protected function deleteOldImageWebapp(string $type = ''): void
{
// Determinar prefijo según el tipo (normal o dark)
@ -188,9 +262,9 @@ class AdminSettingsService
// Claves relacionadas con las imágenes que queremos limpiar
$imageKeys = [
"admin_image_logo{$suffix}",
"admin_image_logo_small{$suffix}",
"admin_image_logo_medium{$suffix}",
"admin.image_logo{$suffix}",
"admin.image_logo_small{$suffix}",
"admin.image_logo_medium{$suffix}",
];
// Recuperar las imágenes actuales en una sola consulta

View File

@ -7,48 +7,57 @@ use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Schema;
use Koneko\VuexyAdmin\Models\Setting;
/**
* Servicio para gestionar la configuración y personalización del template administrativo.
*
* Esta clase maneja las configuraciones del template VuexyAdmin, incluyendo variables
* de personalización, logos, favicons y otras configuraciones de la interfaz.
* Implementa un sistema de caché para optimizar el rendimiento.
*/
class AdminTemplateService
{
protected $cacheTTL = 60 * 24 * 30; // 30 días en minutos
/** @var int Tiempo de vida del caché en minutos (60 * 24 * 30 = 30 días) */
protected $cacheTTL = 60 * 24 * 30;
public function updateSetting(string $key, string $value): bool
{
$setting = Setting::updateOrCreate(
['key' => $key],
['value' => trim($value)]
);
return $setting->save();
}
public function getAdminVars($adminSetting = false): array
/**
* Obtiene las variables de configuración del admin.
*
* @param string $setting Clave específica de configuración a obtener
* @return array Configuraciones del admin o valor específico si se proporciona $setting
*/
public function getAdminVars(string $setting = ''): array
{
try {
// Verificar si el sistema está inicializado (la tabla `migrations` existe)
if (!Schema::hasTable('migrations')) {
return $this->getDefaultAdminVars($adminSetting);
return $this->getDefaultAdminVars($setting);
}
// Cargar desde el caché o la base de datos si está disponible
return Cache::remember('admin_settings', $this->cacheTTL, function () use ($adminSetting) {
$settings = Setting::global()
->where('key', 'LIKE', 'admin_%')
$adminVars = Cache::remember('admin_settings', $this->cacheTTL, function () {
$settings = Setting::withVirtualValue()
->where('key', 'LIKE', 'admin.%')
->pluck('value', 'key')
->toArray();
$adminSettings = $this->buildAdminVarsArray($settings);
return $adminSetting
? $adminSettings[$adminSetting]
: $adminSettings;
return $this->buildAdminVarsArray($settings);
});
return $setting ? ($adminVars[$setting] ?? []) : $adminVars;
} catch (\Exception $e) {
// En caso de error, devolver valores predeterminados
return $this->getDefaultAdminVars($adminSetting);
return $this->getDefaultAdminVars($setting);
}
}
private function getDefaultAdminVars($adminSetting = false): array
/**
* Obtiene las variables predeterminadas del admin.
*
* @param string $setting Clave específica de configuración a obtener
* @return array Configuraciones predeterminadas o valor específico si se proporciona $setting
*/
private function getDefaultAdminVars(string $setting = ''): array
{
$defaultSettings = [
'title' => config('koneko.appTitle', 'Default Title'),
@ -59,27 +68,41 @@ class AdminTemplateService
'image_logo' => $this->getImageLogoPaths([]),
];
return $adminSetting
? $defaultSettings[$adminSetting] ?? null
return $setting
? $defaultSettings[$setting] ?? null
: $defaultSettings;
}
/**
* Construye el array de variables del admin a partir de las configuraciones.
*
* @param array $settings Array asociativo de configuraciones
* @return array Array estructurado con las variables del admin
*/
private function buildAdminVarsArray(array $settings): array
{
return [
'title' => $settings['admin_title'] ?? config('koneko.appTitle'),
'title' => $settings['admin.title'] ?? config('koneko.appTitle'),
'author' => config('koneko.author'),
'description' => config('koneko.description'),
'description' => $settings['admin.description'] ?? config('koneko.description'),
'favicon' => $this->getFaviconPaths($settings),
'app_name' => $settings['admin_app_name'] ?? config('koneko.appName'),
'app_name' => $settings['admin.app_name'] ?? config('koneko.appName'),
'image_logo' => $this->getImageLogoPaths($settings),
];
}
/**
* Obtiene las variables de personalización de Vuexy.
*
* Combina las configuraciones predeterminadas con las almacenadas en la base de datos,
* aplicando las transformaciones necesarias para tipos específicos como booleanos.
*
* @return array Array asociativo con las variables de personalización
*/
public function getVuexyCustomizerVars()
{
// Obtener valores de la base de datos
$settings = Setting::global()
$settings = Setting::withVirtualValue()
->where('key', 'LIKE', 'vuexy_%')
->pluck('value', 'key')
->toArray();
@ -96,7 +119,7 @@ class AdminTemplateService
$value = $settings[$vuexyKey] ?? $defaultValue;
// Forzar booleanos para claves específicas
if (in_array($key, ['displayCustomizer', 'footerFixed', 'menuFixed', 'menuCollapsed', 'showDropdownOnHover'])) {
if (in_array($key, ['hasCustomizer', 'displayCustomizer', 'footerFixed', 'menuFixed', 'menuCollapsed', 'showDropdownOnHover'])) {
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
@ -106,12 +129,15 @@ class AdminTemplateService
}
/**
* Obtiene los paths de favicon en distintos tamaños.
* Genera las rutas para los diferentes tamaños de favicon.
*
* @param array $settings Array asociativo de configuraciones
* @return array Array con las rutas de los favicons en diferentes tamaños
*/
private function getFaviconPaths(array $settings): array
{
$defaultFavicon = config('koneko.appFavicon');
$namespace = $settings['admin_favicon_ns'] ?? null;
$namespace = $settings['admin.favicon_ns'] ?? null;
return [
'namespace' => $namespace,
@ -125,30 +151,43 @@ class AdminTemplateService
}
/**
* Obtiene los paths de los logos en distintos tamaños.
* Genera las rutas para los diferentes tamaños y versiones del logo.
*
* @param array $settings Array asociativo de configuraciones
* @return array Array con las rutas de los logos en diferentes tamaños y modos
*/
private function getImageLogoPaths(array $settings): array
{
$defaultLogo = config('koneko.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),
'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.
* Obtiene la ruta de una imagen específica desde las configuraciones.
*
* @param array $settings Array asociativo de configuraciones
* @param string $key Clave de la configuración
* @param string $default Valor predeterminado si no se encuentra la configuración
* @return string Ruta de la imagen
*/
private function getImagePath(array $settings, string $key, string $default): string
{
return $settings[$key] ?? $default;
}
/**
* Limpia el caché de las variables del admin.
*
* @return void
*/
public static function clearAdminVarsCache()
{
Cache::forget("admin_settings");

View File

@ -15,7 +15,7 @@ class AvatarInitialsService
protected const INITIAL_MAX_LENGTH = 3;
protected const AVATAR_BACKGROUND = '#EBF4FF';
protected const AVATAR_COLORS = [
'#7367f0',
'#3b82f6',
'#808390',
'#28c76f',
'#ff4c51',

View File

@ -6,8 +6,20 @@ use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
/**
* Servicio para gestionar y obtener información de configuración del sistema de caché.
*
* Esta clase proporciona métodos para obtener información detallada sobre las configuraciones
* de caché, sesión, base de datos y drivers del sistema. Permite consultar versiones,
* estados y configuraciones de diferentes servicios como Redis, Memcached y bases de datos.
*/
class CacheConfigService
{
/**
* Obtiene la configuración completa del sistema de caché y servicios relacionados.
*
* @return array Configuración completa que incluye caché, sesión, base de datos y drivers
*/
public function getConfig(): array
{
return [
@ -20,7 +32,11 @@ class CacheConfigService
];
}
/**
* Obtiene la configuración específica del sistema de caché.
*
* @return array Configuración del caché incluyendo driver, host y base de datos
*/
private function getCacheConfig(): array
{
$cacheConfig = Config::get('cache');
@ -59,6 +75,11 @@ class CacheConfigService
return $cacheConfig;
}
/**
* Obtiene la configuración del sistema de sesiones.
*
* @return array Configuración de sesiones incluyendo driver, host y base de datos
*/
private function getSessionConfig(): array
{
$sessionConfig = Config::get('session');
@ -97,6 +118,11 @@ class CacheConfigService
return $sessionConfig;
}
/**
* Obtiene la configuración de la base de datos principal.
*
* @return array Configuración de la base de datos incluyendo host y nombre de la base de datos
*/
private function getDatabaseConfig(): array
{
$databaseConfig = Config::get('database');
@ -109,7 +135,14 @@ class CacheConfigService
return $databaseConfig;
}
/**
* Obtiene información sobre las versiones de los drivers en uso.
*
* Recopila información detallada sobre las versiones de los drivers de base de datos,
* Redis y Memcached si están en uso en el sistema.
*
* @return array Información de versiones de los drivers activos
*/
private function getDriverVersion(): array
{
$drivers = [];
@ -163,6 +196,11 @@ class CacheConfigService
return $drivers;
}
/**
* Obtiene la versión del servidor MySQL.
*
* @return string Versión del servidor MySQL o mensaje de error
*/
private function getMySqlVersion(): string
{
try {
@ -173,6 +211,11 @@ class CacheConfigService
}
}
/**
* Obtiene la versión del servidor PostgreSQL.
*
* @return string Versión del servidor PostgreSQL o mensaje de error
*/
private function getPgSqlVersion(): string
{
try {
@ -183,6 +226,11 @@ class CacheConfigService
}
}
/**
* Obtiene la versión del servidor SQL Server.
*
* @return string Versión del servidor SQL Server o mensaje de error
*/
private function getSqlSrvVersion(): string
{
try {
@ -193,6 +241,11 @@ class CacheConfigService
}
}
/**
* Obtiene la versión del servidor Memcached.
*
* @return string Versión del servidor Memcached o mensaje de error
*/
private function getMemcachedVersion(): string
{
try {
@ -213,6 +266,11 @@ class CacheConfigService
}
}
/**
* Obtiene la versión del servidor Redis.
*
* @return string Versión del servidor Redis o mensaje de error
*/
private function getRedisVersion(): string
{
try {
@ -223,7 +281,14 @@ class CacheConfigService
}
}
/**
* Verifica si un driver específico está en uso en el sistema.
*
* Comprueba si el driver está siendo utilizado en caché, sesiones o colas.
*
* @param string $driver Nombre del driver a verificar
* @return bool True si el driver está en uso, false en caso contrario
*/
protected function isDriverInUse(string $driver): bool
{
return in_array($driver, [

View File

@ -7,19 +7,38 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\File;
/**
* Servicio para gestionar y administrar el sistema de caché.
*
* Esta clase proporciona funcionalidades para administrar diferentes drivers de caché
* (Redis, Memcached, Database, File), incluyendo operaciones como obtener estadísticas,
* limpiar la caché y monitorear el uso de recursos.
*/
class CacheManagerService
{
/** @var string Driver de caché actualmente seleccionado */
private string $driver;
public function __construct(string $driver = null)
/**
* Constructor del servicio de gestión de caché.
*
* @param mixed $driver Driver de caché a utilizar. Si es null, se usa el driver predeterminado
*/
public function __construct(mixed $driver = null)
{
$this->driver = $driver ?? config('cache.default');
}
/**
* Obtiene estadísticas de caché para el driver especificado.
*
* Recopila información detallada sobre el uso y rendimiento del sistema de caché,
* incluyendo uso de memoria, número de elementos y estadísticas específicas del driver.
*
* @param mixed $driver Driver de caché del cual obtener estadísticas
* @return array Estadísticas del sistema de caché
*/
public function getCacheStats(string $driver = null): array
public function getCacheStats(mixed $driver = null): array
{
$driver = $driver ?? $this->driver;
@ -40,7 +59,13 @@ class CacheManagerService
}
}
public function clearCache(string $driver = null): array
/**
* Limpia la caché del driver especificado.
*
* @param mixed $driver Driver de caché a limpiar
* @return array Resultado de la operación de limpieza
*/
public function clearCache(mixed $driver = null): array
{
$driver = $driver ?? $this->driver;
@ -88,6 +113,11 @@ class CacheManagerService
}
}
/**
* Obtiene estadísticas detalladas del servidor Redis.
*
* @return array Información detallada del servidor Redis incluyendo versión, memoria, clientes y más
*/
public function getRedisStats()
{
try {
@ -132,6 +162,11 @@ class CacheManagerService
}
}
/**
* Obtiene estadísticas detalladas del servidor Memcached.
*
* @return array Información detallada del servidor Memcached incluyendo versión, memoria y estadísticas de uso
*/
public function getMemcachedStats()
{
try {
@ -176,9 +211,10 @@ class CacheManagerService
}
}
/**
* Obtiene estadísticas para caché en base de datos.
*
* @return array Estadísticas de la caché en base de datos incluyendo cantidad de registros y uso de memoria
*/
private function _getDatabaseStats(): array
{
@ -196,6 +232,8 @@ class CacheManagerService
/**
* Obtiene estadísticas para caché en archivos.
*
* @return array Estadísticas de la caché en archivos incluyendo cantidad de archivos y uso de memoria
*/
private function _getFilecacheStats(): array
{
@ -211,6 +249,11 @@ class CacheManagerService
}
}
/**
* Obtiene estadísticas específicas de Redis para la caché.
*
* @return array Estadísticas de Redis incluyendo cantidad de claves y uso de memoria
*/
private function _getRedisStats()
{
try {
@ -227,6 +270,11 @@ class CacheManagerService
}
}
/**
* Obtiene estadísticas específicas de Memcached para la caché.
*
* @return array Estadísticas de Memcached incluyendo cantidad de elementos y uso de memoria
*/
public function _getMemcachedStats(): array
{
try {
@ -254,6 +302,14 @@ class CacheManagerService
}
}
/**
* Obtiene información sobre las bases de datos Redis en uso.
*
* Analiza y recopila información sobre las diferentes bases de datos Redis
* configuradas en el sistema (default, cache, sessions).
*
* @return array Información detallada de las bases de datos Redis
*/
private function getRedisDatabases(): array
{
// Verificar si Redis está en uso
@ -300,7 +356,11 @@ class CacheManagerService
return $result;
}
/**
* Limpia la caché almacenada en base de datos.
*
* @return bool True si se eliminaron registros, False si no había registros para eliminar
*/
private function clearDatabaseCache(): bool
{
$count = DB::table(config('cache.stores.database.table'))->count();
@ -313,6 +373,11 @@ class CacheManagerService
return false;
}
/**
* Limpia la caché almacenada en archivos.
*
* @return bool True si se eliminaron archivos, False si no había archivos para eliminar
*/
private function clearFilecache(): bool
{
$cachePath = config('cache.stores.file.path');
@ -326,6 +391,11 @@ class CacheManagerService
return false;
}
/**
* Limpia la caché almacenada en Redis.
*
* @return bool True si se eliminaron claves, False si no había claves para eliminar
*/
private function clearRedisCache(): bool
{
$prefix = config('cache.prefix', '');
@ -343,6 +413,11 @@ class CacheManagerService
return false;
}
/**
* Limpia la caché almacenada en Memcached.
*
* @return bool True si se limpió la caché, False en caso contrario
*/
private function clearMemcachedCache(): bool
{
// Obtener el cliente Memcached directamente
@ -359,9 +434,11 @@ class CacheManagerService
return false;
}
/**
* Verifica si un driver es soportado.
* Verifica si un driver es soportado por el sistema.
*
* @param string $driver Nombre del driver a verificar
* @return bool True si el driver es soportado, False en caso contrario
*/
private function isSupportedDriver(string $driver): bool
{
@ -369,7 +446,10 @@ class CacheManagerService
}
/**
* Convierte bytes en un formato legible.
* Convierte bytes en un formato legible por humanos.
*
* @param int|float $bytes Cantidad de bytes a formatear
* @return string Cantidad formateada con unidad (B, KB, MB, GB, TB)
*/
private function formatBytes($bytes)
{
@ -380,7 +460,12 @@ class CacheManagerService
}
/**
* Genera una respuesta estandarizada.
* Genera una respuesta estandarizada para las operaciones del servicio.
*
* @param string $status Estado de la operación ('success', 'warning', 'danger', 'info')
* @param string $message Mensaje descriptivo de la operación
* @param array $data Datos adicionales de la operación
* @return array Respuesta estructurada con estado, mensaje y datos
*/
private function response(string $status, string $message, array $data = []): array
{

View File

@ -9,28 +9,26 @@ use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Schema;
use Koneko\VuexyAdmin\Models\Setting;
/**
* Servicio para gestionar la configuración global del sistema.
*
* Esta clase maneja las configuraciones globales del sistema, incluyendo servicios
* externos (Facebook, Google), configuración de Vuexy y sistema de correo.
* Implementa un sistema de caché para optimizar el rendimiento y proporciona
* valores predeterminados cuando es necesario.
*/
class GlobalSettingsService
{
/**
* Tiempo de vida del caché en minutos (30 días).
*/
/** @var int Tiempo de vida del caché en minutos (60 * 24 * 30 = 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.
* Carga la configuración del sistema desde la base de datos o caché.
*
* Gestiona la carga de configuraciones para servicios externos y Vuexy.
* Si la base de datos no está inicializada, utiliza valores predeterminados.
*
* @return void
*/
public function loadSystemConfig(): void
{
@ -41,7 +39,7 @@ class GlobalSettingsService
} else {
// Cargar configuración desde la caché o base de datos
$config = Cache::remember('global_system_config', $this->cacheTTL, function () {
$settings = Setting::global()
$settings = Setting::withVirtualValue()
->where('key', 'LIKE', 'config.%')
->pluck('value', 'key')
->toArray();
@ -58,6 +56,7 @@ class GlobalSettingsService
Config::set('services.facebook', $config['servicesFacebook']);
Config::set('services.google', $config['servicesGoogle']);
Config::set('vuexy', $config['vuexy']);
} catch (\Exception $e) {
// Manejo silencioso de errores para evitar interrupciones
Config::set('services.facebook', config('services.facebook', []));
@ -67,7 +66,9 @@ class GlobalSettingsService
}
/**
* Devuelve una configuración predeterminada si la base de datos no está inicializada.
* Obtiene la configuración predeterminada del sistema.
*
* @return array Configuración predeterminada para servicios y Vuexy
*/
private function getDefaultSystemConfig(): array
{
@ -87,7 +88,11 @@ class GlobalSettingsService
}
/**
* Verifica si un bloque de configuraciones está presente.
* Verifica si existe configuración para un bloque específico.
*
* @param array $settings Array de configuraciones
* @param string $blockPrefix Prefijo del bloque a verificar
* @return bool True si existe configuración para el bloque
*/
protected function hasBlockConfig(array $settings, string $blockPrefix): bool
{
@ -95,13 +100,17 @@ class GlobalSettingsService
}
/**
* Construye la configuración de un servicio (Facebook, Google, etc.).
* Construye la configuración para un servicio específico.
*
* @param array $settings Array de configuraciones
* @param string $blockPrefix Prefijo del bloque de configuración
* @param string $defaultConfigKey Clave de configuración predeterminada
* @return array Configuración del servicio
*/
protected function buildServiceConfig(array $settings, string $blockPrefix, string $defaultConfigKey): array
{
if (!$this->hasBlockConfig($settings, $blockPrefix)) {
return [];
return config($defaultConfigKey);
return config($defaultConfigKey)?? [];
}
return [
@ -112,8 +121,14 @@ class GlobalSettingsService
}
/**
* Construye la configuración personalizada de Vuexy.
*/
* Construye la configuración de Vuexy.
*
* Combina la configuración predeterminada con los valores almacenados
* en la base de datos y normaliza los campos booleanos.
*
* @param array $settings Array de configuraciones
* @return array Configuración de Vuexy normalizada
*/
protected function buildVuexyConfig(array $settings): array
{
// Configuración predeterminada del sistema
@ -133,7 +148,10 @@ class GlobalSettingsService
}
/**
* Normaliza los campos booleanos.
* Normaliza los campos booleanos en la configuración.
*
* @param array $config Configuración a normalizar
* @return array Configuración con campos booleanos normalizados
*/
protected function normalizeBooleanFields(array $config): array
{
@ -158,7 +176,9 @@ class GlobalSettingsService
}
/**
* Limpia el caché de la configuración del sistema.
* Limpia la caché de configuración del sistema.
*
* @return void
*/
public static function clearSystemConfigCache(): void
{
@ -166,21 +186,29 @@ class GlobalSettingsService
}
/**
* Elimina las claves config.vuexy.* y limpia global_system_config
* Limpia la configuración de Vuexy de la base de datos y caché.
*
* @return void
*/
public static function clearVuexyConfig(): void
{
Setting::where('key', 'LIKE', 'config.vuexy.%')->delete();
Cache::forget('global_system_config');
}
/**
* Obtiene y sobrescribe la configuración de correo electrónico.
* Obtiene la configuración del sistema de correo.
*
* Recupera y estructura la configuración de correo incluyendo
* configuración SMTP, direcciones de envío y respuesta.
*
* @return array Configuración completa del sistema de correo
*/
public function getMailSystemConfig(): array
{
return Cache::remember('mail_system_config', $this->cacheTTL, function () {
$settings = Setting::global()
$settings = Setting::withVirtualValue()
->where('key', 'LIKE', 'mail.%')
->pluck('value', 'key')
->toArray();
@ -215,7 +243,9 @@ class GlobalSettingsService
}
/**
* Limpia el caché de la configuración de correo electrónico.
* Limpia la caché de configuración del sistema de correo.
*
* @return void
*/
public static function clearMailSystemConfigCache(): void
{

View File

@ -10,12 +10,12 @@ class SessionManagerService
{
private string $driver;
public function __construct(string $driver = null)
public function __construct(mixed $driver = null)
{
$this->driver = $driver ?? config('session.driver');
}
public function getSessionStats(string $driver = null): array
public function getSessionStats(mixed $driver = null): array
{
$driver = $driver ?? $this->driver;
@ -41,7 +41,7 @@ class SessionManagerService
}
}
public function clearSessions(string $driver = null): array
public function clearSessions(mixed $driver = null): array
{
$driver = $driver ?? $this->driver;

View File

@ -0,0 +1,190 @@
<?php
namespace Koneko\VuexyAdmin\Services;
use Illuminate\Support\Facades\Auth;
use Koneko\VuexyAdmin\Models\Setting;
use Illuminate\Support\Facades\Cache;
class SettingsService
{
/**
* Obtiene una configuración con opciones avanzadas de caché.
*
* @param string $key
* @param int|null $userId
* @param string|null $category
* @param bool $useCache
* @param bool $storeInCache
* @param int|null $cacheTtl
* @return mixed|null
*/
public function get(
string $key,
?int $userId = null,
?string $category = null,
bool $useCache = false,
bool $storeInCache = true,
?int $cacheTtl = 120
) {
$cacheKey = $this->generateCacheKey($key, $userId, $category);
if ($useCache && Cache::has($cacheKey)) {
return Cache::get($cacheKey);
}
$value = $this->retrieveSetting($key, $userId, $category);
if ($storeInCache && $value !== null) {
Cache::put($cacheKey, $value, now()->addMinutes($cacheTtl));
}
return $value;
}
/**
* Guarda o actualiza una configuración con control de caché.
*
* @param string $key
* @param mixed $value
* @param int|null $userId
* @param string|null $category
* @param string|null $mimeType
* @param string|null $fileName
* @param bool $updateCache
* @param int|null $cacheTtl
* @return Setting|null
*/
public function set(
string $key,
mixed $value,
?int $userId = null,
?string $category = null,
?string $mimeType = null,
?string $fileName = null,
bool $updateCache = false,
?int $cacheTtl = 120
): ?Setting {
$data = [
'user_id' => $userId,
'category' => $category,
'mime_type' => $mimeType,
'file_name' => $fileName,
// Inicializar todos los campos de valor como null
'value_string' => null,
'value_integer' => null,
'value_boolean' => null,
'value_float' => null,
'value_text' => null,
'value_binary' => null,
];
// Detectar tipo de valor
if (is_string($value)) {
// Evaluamos la longitud de la cadena
$threshold = 250;
if (strlen($value) > $threshold) {
$data['value_text'] = $value;
} else {
$data['value_string'] = $value;
}
} elseif (is_int($value)) {
$data['value_integer'] = $value;
} elseif (is_bool($value)) {
$data['value_boolean'] = $value;
} elseif (is_float($value)) {
$data['value_float'] = $value;
} elseif (is_resource($value) || $value instanceof \SplFileInfo) {
$data['value_binary'] = is_resource($value)
? stream_get_contents($value)
: file_get_contents($value->getRealPath());
} elseif (is_array($value) || is_object($value)) {
$data['value_text'] = json_encode($value);
}
// Se registra usuario que realiza la acción
if (Auth::check()) {
$data['updated_by'] = Auth::id();
}
$setting = Setting::updateOrCreate(
['key' => $key, 'user_id' => $userId, 'category' => $category],
$data
);
if ($updateCache) {
$cacheKey = $this->generateCacheKey($key, $userId, $category);
Cache::put($cacheKey, $setting->value, now()->addMinutes($cacheTtl));
}
return $setting;
}
/**
* Elimina una configuración.
*
* @param string $key
* @param int|null $userId
* @param string|null $category
* @return Setting|null La configuración eliminada o null si no existía
*/
public function delete(string $key, ?int $userId = null, ?string $category = null): ?Setting
{
$setting = Setting::where('key', $key);
if ($userId !== null) {
$setting->where('user_id', $userId);
}
if ($category !== null) {
$setting->where('category', $category);
}
$setting = $setting->first();
if ($setting) {
$cacheKey = $this->generateCacheKey($key, $userId, $category);
Cache::forget($cacheKey);
$setting->delete();
}
return $setting;
}
/**
* Recupera una configuración de la base de datos.
*
* @param string $key
* @param integer|null $userId
* @param string|null $category
* @return void
*/
protected function retrieveSetting(string $key, ?int $userId, ?string $category)
{
$query = Setting::where('key', $key);
if ($userId !== null) {
$query->where('user_id', $userId);
}
if ($category !== null) {
$query->where('category', $category);
}
return $query->first()?->value;
}
/**
* Genera una clave de caché para una configuración.
*
* @param string $key
* @param integer|null $userId
* @param string|null $category
* @return string
*/
protected function generateCacheKey(string $key, ?int $userId, ?string $category): string
{
return 'settings:' . md5($key . '|' . $userId . '|' . $category);
}
}

View File

@ -2,10 +2,7 @@
namespace Koneko\VuexyAdmin\Services;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\{Auth,Cache,Route,Gate};
use Koneko\VuexyAdmin\Models\Setting;
class VuexyAdminService
@ -26,12 +23,8 @@ class VuexyAdminService
{
$this->user = Auth::user();
$this->vuexySearch = Auth::user() !== null;
$this->orientation = config('vuexy.custom.myLayout');
}
/**
* Obtiene el menú según el estado del usuario (autenticado o no).
*/
public function getMenu()
{
// Obtener el menú desde la caché
@ -45,9 +38,6 @@ class VuexyAdminService
return $this->markActive($menu, $currentRoute);
}
/**
* Menú para usuarios no autenticados.dump
*/
private function getGuestMenu()
{
return Cache::remember('vuexy_menu_guest', now()->addDays(7), function () {
@ -55,12 +45,9 @@ class VuexyAdminService
});
}
/**
* Menú para usuarios autenticados.
*/
private function getUserMenu()
{
Cache::forget("vuexy_menu_user_{$this->user->id}"); // Borrar la caché anterior para actualizarla
//Cache::forget("vuexy_menu_user_{$this->user->id}"); // Borrar la caché anterior para actualizarla
return Cache::remember("vuexy_menu_user_{$this->user->id}", now()->addHours(24), function () {
return $this->getMenuArray();
@ -89,9 +76,6 @@ class VuexyAdminService
return $menu;
}
/**
* Invalida el cache del menú de un usuario.
*/
public static function clearUserMenuCache()
{
$user = Auth::user();
@ -100,9 +84,6 @@ class VuexyAdminService
Cache::forget("vuexy_menu_user_{$user->id}");
}
/**
* Invalida el cache del menú de invitados.
*/
public static function clearGuestMenuCache()
{
Cache::forget('vuexy_menu_guest');
@ -224,7 +205,7 @@ class VuexyAdminService
return $quickLinksData;
}
private function collectQuickLinksFromMenu(array $menu, array &$quickLinks, string $parentTitle = null)
private function collectQuickLinksFromMenu(array $menu, array &$quickLinks, mixed $parentTitle = null)
{
foreach ($menu as $title => $item) {
// Verificar si el elemento está en la lista de quicklinksRouteNames
@ -249,9 +230,6 @@ class VuexyAdminService
}
}
/**
* Verifica si la ruta actual existe en la lista de enlaces.
*/
private function isCurrentPageInList(array $quickLinks, string $currentRoute): bool
{
foreach ($quickLinks['rows'] as $row) {
@ -291,193 +269,8 @@ class VuexyAdminService
<a class='nav-link btn btn-text-secondary btn-icon rounded-pill dropdown-toggle hide-arrow' href='javascript:void(0);' data-bs-toggle='dropdown' data-bs-auto-close='outside' aria-expanded='false'>
<span class='position-relative'>
<i class='ti ti-bell ti-md'></i>
<span class='badge rounded-pill bg-danger badge-dot badge-notifications border'></span>
</span>
</a>
<ul class='dropdown-menu dropdown-menu-end p-0'>
<li class='dropdown-menu-header border-bottom'>
<div class='dropdown-header d-flex align-items-center py-3'>
<h6 class='mb-0 me-auto'>Notification</h6>
<div class='d-flex align-items-center h6 mb-0'>
<span class='badge bg-label-primary me-2'>8 New</span>
<a href='javascript:void(0)' class='btn btn-text-secondary rounded-pill btn-icon dropdown-notifications-all' data-bs-toggle='tooltip' data-bs-placement='top' title='Mark all as read'><i class='ti ti-mail-opened text-heading'></i></a>
</div>
</div>
</li>
<li class='dropdown-notifications-list scrollable-container'>
<ul class='list-group list-group-flush'>
<li class='list-group-item list-group-item-action dropdown-notifications-item'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<img src='' . asset('assets/admin/img/avatars/1.png') . '' alt class='rounded-circle'>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='small mb-1'>Congratulation Lettie 🎉</h6>
<small class='mb-1 d-block text-body'>Won the monthly best seller gold badge</small>
<small class='text-muted'>1h ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<span class='avatar-initial rounded-circle bg-label-danger'>CF</span>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>Charles Franklin</h6>
<small class='mb-1 d-block text-body'>Accepted your connection</small>
<small class='text-muted'>12hr ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item marked-as-read'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<img src='' . asset('assets/admin/img/avatars/2.png') . '' alt class='rounded-circle'>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>New Message ✉️</h6>
<small class='mb-1 d-block text-body'>You have new message from Natalie</small>
<small class='text-muted'>1h ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<span class='avatar-initial rounded-circle bg-label-success'><i class='ti ti-shopping-cart'></i></span>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>Whoo! You have new order 🛒 </h6>
<small class='mb-1 d-block text-body'>ACME Inc. made new order $1,154</small>
<small class='text-muted'>1 day ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item marked-as-read'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<img src='' . asset('assets/admin/img/avatars/9.png') . '' alt class='rounded-circle'>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>Application has been approved 🚀 </h6>
<small class='mb-1 d-block text-body'>Your ABC project application has been approved.</small>
<small class='text-muted'>2 days ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item marked-as-read'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<span class='avatar-initial rounded-circle bg-label-success'><i class='ti ti-chart-pie'></i></span>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>Monthly report is generated</h6>
<small class='mb-1 d-block text-body'>July monthly financial report is generated </small>
<small class='text-muted'>3 days ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item marked-as-read'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<img src='' . asset('assets/admin/img/avatars/5.png') . '' alt class='rounded-circle'>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>Send connection request</h6>
<small class='mb-1 d-block text-body'>Peter sent you connection request</small>
<small class='text-muted'>4 days ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<img src='' . asset('assets/admin/img/avatars/6.png') . '' alt class='rounded-circle'>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>New message from Jane</h6>
<small class='mb-1 d-block text-body'>Your have new message from Jane</small>
<small class='text-muted'>5 days ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
<li class='list-group-item list-group-item-action dropdown-notifications-item marked-as-read'>
<div class='d-flex'>
<div class='flex-shrink-0 me-3'>
<div class='avatar'>
<span class='avatar-initial rounded-circle bg-label-warning'><i class='ti ti-alert-triangle'></i></span>
</div>
</div>
<div class='flex-grow-1'>
<h6 class='mb-1 small'>CPU is running high</h6>
<small class='mb-1 d-block text-body'>CPU Utilization Percent is currently at 88.63%,</small>
<small class='text-muted'>5 days ago</small>
</div>
<div class='flex-shrink-0 dropdown-notifications-actions'>
<a href='javascript:void(0)' class='dropdown-notifications-read'><span class='badge badge-dot'></span></a>
<a href='javascript:void(0)' class='dropdown-notifications-archive'><span class='ti ti-x'></span></a>
</div>
</div>
</li>
</ul>
</li>
<li class='border-top'>
<div class='d-grid p-4'>
<a class='btn btn-primary btn-sm d-flex' href='javascript:void(0);'>
<small class='align-middle'>View all notifications</small>
</a>
</div>
</li>
</ul>
</li>";
}