diff --git a/app/Http/Controllers/Repuve/LogsController.php b/app/Http/Controllers/Repuve/LogsController.php new file mode 100644 index 0000000..adf97ec --- /dev/null +++ b/app/Http/Controllers/Repuve/LogsController.php @@ -0,0 +1,54 @@ +filters($request); + $logs = $this->logsService->readRepuve($filters); + + return ApiResponse::OK->response([ + 'source' => 'repuve', + 'filters' => $filters, + 'logs' => $logs, + ]); + } + + public function padronEstatalLogs(Request $request) + { + $filters = $this->filters($request); + $logs = $this->logsService->readPadronEstatal($filters); + + return ApiResponse::OK->response([ + 'source' => 'padron-estatal', + 'filters' => $filters, + 'logs' => $logs, + ]); + } + + private function filters(Request $request): array + { + return $request->validate([ + 'start_date' => ['nullable', 'date'], + 'end_date' => ['nullable', 'date', 'after_or_equal:start_date'], + ]); + } +} diff --git a/app/Http/Requests/Repuve/VehicleUpdateRequest.php b/app/Http/Requests/Repuve/VehicleUpdateRequest.php index 8c1bf99..0ae94de 100644 --- a/app/Http/Requests/Repuve/VehicleUpdateRequest.php +++ b/app/Http/Requests/Repuve/VehicleUpdateRequest.php @@ -29,7 +29,7 @@ public function rules(): array 'vehicle.rfv' => 'nullable|string|max:50', 'vehicle.rfc' => 'nullable|string|max:13', 'vehicle.ofcexpedicion' => 'nullable|string|max:100', - 'vehicle.fechaexpedicion' => 'nullable|date', + 'vehicle.fechaexpedicion' => ['nullable', 'date_format:d/m/Y'], 'vehicle.tipo_veh' => 'nullable|string|max:50', 'vehicle.numptas' => 'nullable|string', 'vehicle.observac' => 'nullable|string|max:500', diff --git a/app/Models/Vehicle.php b/app/Models/Vehicle.php index e526c9f..153d0c2 100644 --- a/app/Models/Vehicle.php +++ b/app/Models/Vehicle.php @@ -36,7 +36,6 @@ class Vehicle extends Model ]; protected $casts = [ - 'fechaexpedicion' => 'date', 'reporte_robo' => 'boolean', ]; diff --git a/app/Services/LogsService.php b/app/Services/LogsService.php new file mode 100644 index 0000000..af0a430 --- /dev/null +++ b/app/Services/LogsService.php @@ -0,0 +1,139 @@ + 'logs/repuve-nacional.log', + 'padron-estatal' => 'logs/padron-estatal.log', + ]; + + public function readRepuve(array $filters): array + { + return $this->read('repuve', $filters); + } + + public function readPadronEstatal(array $filters): array + { + return $this->read('padron-estatal', $filters); + } + + private function read(string $source, array $filters): array + { + $path = storage_path($this->sources[$source]); + + if (!is_file($path) || !is_readable($path)) { + return []; + } + + $entries = $this->parseFile($path); + + return $this->applyFilters($entries, $filters); + } + + private function parseFile(string $path): array + { + $file = new SplFileObject($path, 'r'); + $entries = []; + $current = null; + + while (!$file->eof()) { + $line = rtrim((string) $file->fgets(), "\r\n"); + if ($line === '') { + continue; + } + + $header = $this->parseHeader($line); + + if ($header) { + if ($current) { + $entries[] = $this->sanitize($current); + } + $current = $header; + continue; + } + + if ($current) { + $current['message'] .= "\n" . $line; + } + } + + if ($current) { + $entries[] = $this->sanitize($current); + } + + usort($entries, fn($a, $b) => strcmp($b['timestamp'], $a['timestamp'])); + + return $entries; + } + + private function parseHeader(string $line): ?array + { + $pattern = '/^\[(?
[^\]]+)\]\s[^\.\s]+\.(?[A-Z]+):\s(?.*)$/'; + + if (!preg_match($pattern, $line, $m)) { + return null; + } + + try { + $date = Carbon::parse($m['dt']); + } catch (\Throwable $e) { + return null; + } + + return [ + 'timestamp' => $date->toDateTimeString(), + 'level' => strtolower($m['level']), + 'message' => $m['message'], + ]; + } + + private function applyFilters(array $entries, array $filters): array + { + $start = !empty($filters['start_date']) ? Carbon::parse($filters['start_date'])->startOfDay() : null; + $end = !empty($filters['end_date']) ? Carbon::parse($filters['end_date'])->endOfDay() : null; + $level = $filters['level'] ?? null; + + return array_values(array_filter($entries, function (array $entry) use ($start, $end, $level) { + $dt = Carbon::parse($entry['timestamp']); + + if ($start && $dt->lt($start)) { + return false; + } + + if ($end && $dt->gt($end)) { + return false; + } + + if ($level && $entry['level'] !== strtolower($level)) { + return false; + } + + return true; + })); + } + + private function sanitize(array $entry): array + { + $text = $entry['message']; + + $patterns = [ + '/("password"\s*:\s*")[^"]*(")/i' => '$1***$2', + '/("token"\s*:\s*")[^"]*(")/i' => '$1***$2', + '/(authorization)\s*[:=]\s*([^,\s]+)/i' => '$1=***', + '/()(.*?)(<\/arg0>)/i' => '$1***$3', + '/()(.*?)(<\/arg1>)/i' => '$1***$3', + ]; + + foreach ($patterns as $pattern => $replacement) { + $text = preg_replace($pattern, $replacement, $text); + } + + $entry['message'] = $text; + + return $entry; + } +} \ No newline at end of file diff --git a/app/Services/PadronEstatalService.php b/app/Services/PadronEstatalService.php index 2a1d1b7..6771016 100644 --- a/app/Services/PadronEstatalService.php +++ b/app/Services/PadronEstatalService.php @@ -172,11 +172,7 @@ private function parsearRespuesta(string $soapResponse): array */ public function extraerDatosVehiculo(array $datos): array { - // Convertir fecha de DD/MM/YYYY a YYYY-MM-DD - $fechaexpedicion = null; - if (isset($datos['fechaexp']) && $datos['fechaexp']) { - $fechaexpedicion = $this->convertirFecha($datos['fechaexp']); - } + $fechaexpedicion = $datos['fechaexp'] ?? null; return [ 'placa' => $datos['placa'] ?? null, @@ -201,29 +197,6 @@ public function extraerDatosVehiculo(array $datos): array ]; } - /** - * Convierte fecha de DD/MM/YYYY a YYYY-MM-DD - */ - private function convertirFecha(?string $fecha): ?string - { - if (!$fecha) { - return null; - } - - // Si ya está en formato YYYY-MM-DD, retornar tal cual - if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha)) { - return $fecha; - } - - // Convertir de DD/MM/YYYY a YYYY-MM-DD - if (preg_match('/^(\d{2})\/(\d{2})\/(\d{4})$/', $fecha, $matches)) { - return "{$matches[3]}-{$matches[2]}-{$matches[1]}"; - } - - // Si no coincide con ningún formato esperado, retornar null - return null; - } - /** * Extrae los datos del propietario del resultado */ diff --git a/app/Services/RepuveService.php b/app/Services/RepuveService.php index 7b77ef8..b7b65ba 100644 --- a/app/Services/RepuveService.php +++ b/app/Services/RepuveService.php @@ -236,7 +236,9 @@ private function parseVehicleResponse(string $soapResponse, string $niv) 'pedimento', 'fecha_pedimento', 'clave_importador', - 'observaciones' + 'folio_CI', + 'identificador_CI', + 'observaciones', ]; $jsonResponse = []; diff --git a/database/migrations/2026_03_30_085409_change_fechaexpedicion_to_string_in_vehicle_table.php b/database/migrations/2026_03_30_085409_change_fechaexpedicion_to_string_in_vehicle_table.php new file mode 100644 index 0000000..8f4fb83 --- /dev/null +++ b/database/migrations/2026_03_30_085409_change_fechaexpedicion_to_string_in_vehicle_table.php @@ -0,0 +1,28 @@ +string('fechaexpedicion', 10)->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('vehicle', function (Blueprint $table) { + $table->date('fechaexpedicion')->nullable()->change(); + }); + } +}; diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index 64b2825..56971dd 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -153,6 +153,11 @@ public function run(): void $tagAssignToModule = $this->onPermission('tags.assign_to_module', 'Asignar etiquetas a módulo', $tags, 'api'); + // === LOGS DE SERVICIOS === + $logsServices = PermissionType::updateOrCreate(['name' => 'Logs de Servicios']); + + $logsServicesIndex = $this->onPermission('logs.services.index', 'Ver logs de servicios externos', $logsServices, 'api'); + // ========================================================= // ROLES // ========================================================= @@ -194,7 +199,9 @@ public function run(): void // Constancias $tagIndex, $tagCreate, $tagEdit, $tagDestroy, //app - $apkIndex + $apkIndex, + // Logs + $logsServicesIndex ); // Encargado @@ -219,6 +226,8 @@ public function run(): void $packageIndex, $packageCreate, $packageEdit, $packageDestroy, $packageBoxTags, // Constancias $tagIndex, $tagCreate, $tagEdit, $tagDestroy, $tagAssignToModule, + // Logs + $logsServicesIndex ); // Perito diff --git a/routes/api.php b/routes/api.php index 893aadc..b1adb18 100644 --- a/routes/api.php +++ b/routes/api.php @@ -15,6 +15,7 @@ use App\Http\Controllers\Repuve\PackageController; use App\Http\Controllers\Repuve\TagsController; use App\Http\Controllers\Repuve\AppController; +use App\Http\Controllers\Repuve\LogsController; use App\Http\Controllers\System\SettingsController; /** @@ -100,6 +101,10 @@ Route::delete('app/{app}', [AppController::class, 'destroy']); Route::get('app/download', [AppController::class, 'download']); + // Rutas de logs + Route::get('logs/repuve', [LogsController::class, 'repuveLogs']); + Route::get('logs/padron-estatal', [LogsController::class, 'padronEstatalLogs']); + }); /** Rutas públicas */