diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php new file mode 100644 index 0000000..298ef5e --- /dev/null +++ b/app/Exceptions/Handler.php @@ -0,0 +1,49 @@ +shouldReport($exception)) { + try { + $this->notifyDiscord($exception); + } catch (Throwable $e) { + // Evitar que fallos en la notificación rompan el flujo + logger()->error("Error al enviar notificación a Discord: " . $e->getMessage()); + } + } + + parent::report($exception); + } + + protected function notifyDiscord(Throwable $exception) +{ + $details = [ + 'message' => $exception->getMessage(), + 'file' => "{$exception->getFile()}:{$exception->getLine()}", + 'trace' => substr($exception->getTraceAsString(), 0, 1800), + ]; + + // Logea el error antes de enviarlo a Discord + logger()->error('Excepción capturada', $details); + + // Envía la notificación a Discord + DiscordNotifier::notifyEvent( + 'Excepción en el sistema 🚨', + $details, + asset('images/logo.png') + ); +} +} diff --git a/app/Filament/Resources/DrawsResource.php b/app/Filament/Resources/DrawsResource.php index 513f40e..3f19ee3 100644 --- a/app/Filament/Resources/DrawsResource.php +++ b/app/Filament/Resources/DrawsResource.php @@ -12,7 +12,7 @@ use Modules\Draws\Models\Draws as ModelsDraws; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\DateTimePicker; -use Filament\Forms\Components\TextArea; +use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Select; use Filament\Tables\Columns\TextColumn; use Modules\Lottery\Models\Lottery; diff --git a/app/Filament/Resources/LoteryResource.php b/app/Filament/Resources/LoteryResource.php index 1d7f3db..e3c216c 100644 --- a/app/Filament/Resources/LoteryResource.php +++ b/app/Filament/Resources/LoteryResource.php @@ -10,7 +10,7 @@ use Filament\Tables; use Filament\Tables\Table; use Filament\Forms\Components\TextInput; -use Filament\Forms\Components\TextArea; +use Filament\Forms\Components\Textarea; use Filament\Tables\Columns\TextColumn; use Modules\Lottery\Models\Lottery; diff --git a/app/Filament/Resources/Pages/CustomDashboard.php b/app/Filament/Resources/Pages/CustomDashboard.php index e3f2f7e..bb4289c 100644 --- a/app/Filament/Resources/Pages/CustomDashboard.php +++ b/app/Filament/Resources/Pages/CustomDashboard.php @@ -1,6 +1,6 @@ $message, + ]; + + if (!empty($embed)) { + $payload['embeds'] = [$embed]; + } + + Http::post($webhookUrl, $payload); + } + } + + public static function notifyException(\Throwable $exception) + { + $trace = substr($exception->getTraceAsString(), 0, 1800); // Discord tiene límites. + $message = "**🚨 Exception Alert**\n" + . "**Mensaje:** {$exception->getMessage()}\n" + . "**Archivo:** {$exception->getFile()}:{$exception->getLine()}\n" + . "**Trace:** ```{$trace}```"; + + self::send($message); + } + + public static function notifyEvent($eventType, $details = [], $imageUrl = null) + { + $webhookUrl = env('DISCORD_WEBHOOK_URL'); + + if ($webhookUrl) { + $logoUrl = $imageUrl ?? asset('logo.png'); // Imagen por defecto + $embed = [ + 'title' => '🔔 Notificación del Sistema', + 'description' => "Se ha detectado un evento: **{$eventType}**.", + 'color' => 7506394, // Color (hex: #72A0C1) + 'fields' => [], + 'footer' => [ + 'text' => 'Notificaciones del Sistema', + 'icon_url' => $logoUrl, + ], + 'timestamp' => now()->toIso8601String(), + ]; + + // Agregar detalles al mensaje. + foreach ($details as $key => $value) { + $embed['fields'][] = [ + 'name' => ucfirst($key), + 'value' => $value, + 'inline' => true, + ]; + } + + self::send('', $embed); + } + } +} diff --git a/app/Http/Middleware/HandleExceptions.php b/app/Http/Middleware/HandleExceptions.php new file mode 100644 index 0000000..70bae6e --- /dev/null +++ b/app/Http/Middleware/HandleExceptions.php @@ -0,0 +1,30 @@ +reportToDiscord($e); // Enviar notificación a Discord + throw $e; // Rethrow para permitir que el sistema maneje la excepción + } + } + + protected function reportToDiscord(Throwable $exception) + { + $message = "**🚨 Excepción Crítica**\n" + . "**Mensaje:** {$exception->getMessage()}\n" + . "**Archivo:** {$exception->getFile()}:{$exception->getLine()}\n" + . "**Trace:** ```" . substr($exception->getTraceAsString(), 0, 1800) . "```"; + + DiscordNotifier::send($message); + } +} diff --git a/app/Http/Request/UserRequest.php b/app/Http/Request/UserRequest.php index 1c3576e..e57fa3a 100644 --- a/app/Http/Request/UserRequest.php +++ b/app/Http/Request/UserRequest.php @@ -1,6 +1,6 @@ $event->user->id, + 'email' => $event->user->email, + ]); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 7b14728..46033e2 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Helpers\DiscordNotifier; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -51,5 +52,50 @@ public function routeNotificationForWebhook() return env('DISCORD_WEBHOOK_URL'); } + protected static function boot() + { + parent::boot(); + + // Evento cuando el modelo es creado + static::created(function ($model) { + $createdBy = auth()->user(); + DiscordNotifier::notifyEvent('Usuario Creado', [ + 'ID Usuario Creado' => $model->id, + 'Nombre Usuario Creado' => $model->name, + 'ID Quien Creó' => $createdBy->id ?? 'Desconocido', + 'Nombre Quien Creó' => $createdBy->name ?? 'Desconocido', + ], + asset('images/logo.png') + ); + }); + + // Evento cuando el modelo es actualizado + static::updated(function ($model) { + DiscordNotifier::notifyEvent('Usuario actualizado', [ + 'ID Usuario Actualizado' => $model->id, + 'Nombre Usuario Actualizado' => $model->name, + 'Cambios Realizados' => json_encode($model->getChanges()), // Cambios realizados en formato JSON + 'ID Quien Actualizó' => $updatedBy->id ?? 'Desconocido', + 'Nombre Quien Actualizó' => $updatedBy->name ?? 'Desconocido', + ], + asset('images/logo.png') + ); + }); + + // Evento cuando el modelo es eliminado + static::deleted(function ($model) { + $deletedBy = auth()->user(); // Usuario autenticado + DiscordNotifier::notifyEvent( + 'Usuario Borrado', + [ + 'ID Usuario Eliminado' => $model->id, + 'Nombre Usuario Eliminado' => $model->name, + 'ID Quien Eliminó' => $deletedBy->id ?? 'Desconocido', + 'Nombre Quien Eliminó' => $deletedBy->name ?? 'Desconocido', + ], + asset('images/logo.png') // Ruta de imagen pública + ); + }); + } } diff --git a/app/Notifications/Channels/DiscordChannel.php b/app/Notifications/Channels/DiscordChannel.php deleted file mode 100644 index 9dc0807..0000000 --- a/app/Notifications/Channels/DiscordChannel.php +++ /dev/null @@ -1,22 +0,0 @@ -toDiscord($notifiable); - $client = new Client(); - $response = $client->post(config('services.discord.webhook_url'), [ - 'headers' => ['Content-Type' => 'application/json'], - 'json' => $message - ]); - return $response; - } - } -} diff --git a/app/Notifications/DiscordNotification.php b/app/Notifications/DiscordNotification.php deleted file mode 100644 index d659676..0000000 --- a/app/Notifications/DiscordNotification.php +++ /dev/null @@ -1,62 +0,0 @@ -name = $name; - $this->email = $email; - } - public function via($notifiable) - { - return [\App\Notifications\Channels\DiscordChannel::class]; - } - public function toDiscord($notifiable) - { - // Obtener la URL base de la aplicación - // Construir la URL completa al logo - $logoUrl = 'http://127.0.0.1:8000/assets/media/auth/Logo-Gananza1.svg'; - return [ - 'content' => 'Nuevo registro de usuario', - 'embeds' => [ - [ - 'title' => 'Nuevo Registro de Usuario', - 'type' => 'rich', - 'color' => 7506394, - 'description' => "Un nuevo usuario se ha registrado a través de Google.", - 'thumbnail' => [ - 'url' => $logoUrl - ], - 'fields' => [ - [ - 'name' => 'Nombre', - 'value' => $this->name, - 'inline' => true - ], - [ - 'name' => 'Correo', - 'value' => $this->email, - 'inline' => true - ], - [ - 'name' => 'Fecha', - 'value' => now()->format('Y-m-d H:i:s'), - 'inline' => true - ] - ], - 'footer' => [ - 'text' => 'Gananza', - 'icon_url' => $logoUrl - ], - 'timestamp' => now()->toIso8601String() - ] - ] - ]; - } -} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..5751a31 --- /dev/null +++ b/app/Providers/EventServiceProvider.php @@ -0,0 +1,31 @@ + [ + SendLoginNotification::class, + ], + ]; + + /** + * Register any events for your application. + * + * @return void + */ + public function boot() + { + parent::boot(); + } +} diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index be7a3e3..3b59efe 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -2,7 +2,7 @@ namespace App\Providers\Filament; -use App\Filament\Pages\CustomDashboard; +use App\Filament\Resources\Pages\CustomDashboard; use App\Http\Middleware\AdminPasswordVerification; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\DisableBladeIconComponents; diff --git a/bootstrap/app.php b/bootstrap/app.php index 56b7636..c10fa31 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -16,6 +16,7 @@ $middleware->web(append: [ \App\Http\Middleware\HandleInertiaRequests::class, \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class, + \App\Http\Middleware\HandleExceptions::class, ]); // Registra los alias de los middlewares de Spatie diff --git a/bootstrap/providers.php b/bootstrap/providers.php index e9586ad..1fbacc0 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -2,6 +2,7 @@ return [ App\Providers\AppServiceProvider::class, + App\Providers\EventServiceProvider::class, App\Providers\Filament\AdminPanelProvider::class, App\Providers\FortifyServiceProvider::class, App\Providers\JetstreamServiceProvider::class, diff --git a/composer.json b/composer.json index 5610e4d..b67fae1 100644 --- a/composer.json +++ b/composer.json @@ -31,12 +31,16 @@ "pestphp/pest": "^3.5", "pestphp/pest-plugin-laravel": "^3.0" }, + "autoload": { "psr-4": { "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" - } + }, + "files": [ + "app/Helpers/DiscordNotifier.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/config/telescope.php b/config/telescope.php index aec6769..69f9e6d 100644 --- a/config/telescope.php +++ b/config/telescope.php @@ -120,7 +120,7 @@ ], 'ignore_commands' => [ - // + 'http:post https://discord.com/api/webhooks/*', ], /* @@ -204,4 +204,11 @@ Watchers\ScheduleWatcher::class => env('TELESCOPE_SCHEDULE_WATCHER', true), Watchers\ViewWatcher::class => env('TELESCOPE_VIEW_WATCHER', true), ], + + 'filter' => [ + 'type' => [ + Illuminate\Http\Client\RequestException::class, + Symfony\Component\HttpKernel\Exception\HttpException::class, + ], +], ]; diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000..947a35e Binary files /dev/null and b/public/images/logo.png differ diff --git a/routes/web.php b/routes/web.php index fdf6214..51af6c4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -6,10 +6,13 @@ use App\Http\Controllers\PaymentController; use App\Http\Controllers\PaymentVerificationController; use App\Http\Middleware\AdminPasswordVerification; +use App\Models\User; use Illuminate\Auth\Middleware\Authorize; use Illuminate\Foundation\Application; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Route; use Inertia\Inertia; +use Laravel\Telescope\Telescope; use Modules\Raffle\Http\Controllers\RaffleController; use Modules\Ticket\Http\Controllers\TicketController; @@ -22,7 +25,10 @@ 'phpVersion' => PHP_VERSION, ]); }); - +Route::get('/test-discord', function () { + \App\Helpers\DiscordNotifier::send("Prueba directa de notificación a Discord."); + return "Mensaje enviado."; +}); // Grupo de rutas autenticadas Route::middleware(['auth:sanctum', config('jetstream.auth_session')])->group(function () { Route::get('/dashboard', function () {