logs
This commit is contained in:
parent
9576ef412a
commit
6c7402ea4b
@ -839,3 +839,13 @@ $routes->group('bds_upload', function($routes) {
|
||||
$routes->get('getBdsDumpFileErrorData',"PolicyTransactionController::getBdsDumpFileErrorData");
|
||||
$routes->get('getBdsDumpExcelFileErrors/(:any)',"PolicyTransactionController::getBdsDumpExcelFileErrors/$1");
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
$routes->group('logs', function($routes) {
|
||||
$routes->get('/', 'LogController::index');
|
||||
$routes->get('view/(:segment)', 'LogController::view/$1');
|
||||
$routes->get('download/(:segment)', 'LogController::download/$1');
|
||||
$routes->get('delete/(:segment)', 'LogController::delete/$1');
|
||||
$routes->get('clearAll', 'LogController::clearAll');
|
||||
});
|
||||
|
||||
@ -45,9 +45,14 @@ class ApiServiceController extends BaseController
|
||||
|
||||
$tpaID = $data['tpa_id'];
|
||||
|
||||
if ($tpaID == $this->medi_assist_primary_key) { // MediAssist
|
||||
if ($tpaID == $this->medi_assist_primary_key)
|
||||
{ // MediAssist
|
||||
$mediAssistController = new MediAssistApiController();
|
||||
return $mediAssistController->SubmitClaim($claimId);
|
||||
}else if ($tpaID == $this->vidal_primary_key)
|
||||
{ // Vidal
|
||||
$vidalApiController = new VidalApiController;
|
||||
return $vidalApiController->SubmitClaim($claimId);
|
||||
}else{
|
||||
log_message('error', "This ticket id ( {$claimId} ) TPA has no API service enabled. TPA ID : {$tpaID}");
|
||||
}
|
||||
|
||||
259
app/Controllers/LogController.php
Normal file
259
app/Controllers/LogController.php
Normal file
@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
use CodeIgniter\Controller;
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
class LogController extends BaseController
|
||||
{
|
||||
private $logPath;
|
||||
public $dModel;
|
||||
public $session;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Path to log files
|
||||
$this->logPath = WRITEPATH . 'logs/';
|
||||
$this->session = session();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display list of all log files
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
$logFiles = $this->getLogFiles();
|
||||
|
||||
$data = [
|
||||
'title' => 'Log Files',
|
||||
'logFiles' => $logFiles
|
||||
];
|
||||
|
||||
return $this->loadLayout('logs/index', $data);
|
||||
|
||||
// return view('logs/index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all log files sorted by date (latest first)
|
||||
*/
|
||||
private function getLogFiles()
|
||||
{
|
||||
$files = [];
|
||||
|
||||
if (!is_dir($this->logPath)) {
|
||||
return $files;
|
||||
}
|
||||
|
||||
$iterator = new \DirectoryIterator($this->logPath);
|
||||
|
||||
foreach ($iterator as $fileInfo) {
|
||||
if ($fileInfo->isFile() && $fileInfo->getExtension() === 'log') {
|
||||
$files[] = [
|
||||
'name' => $fileInfo->getFilename(),
|
||||
'path' => $fileInfo->getPathname(),
|
||||
'size' => $this->formatBytes($fileInfo->getSize()),
|
||||
'modified' => $fileInfo->getMTime(),
|
||||
'modified_date' => date('Y-m-d H:i:s', $fileInfo->getMTime())
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by modified time (latest first)
|
||||
usort($files, function($a, $b) {
|
||||
return $b['modified'] - $a['modified'];
|
||||
});
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* View specific log file content
|
||||
*/
|
||||
public function view($filename = null)
|
||||
{
|
||||
|
||||
|
||||
|
||||
if (!$filename) {
|
||||
return redirect()->to('/logs')->with('error', 'No log file specified');
|
||||
}
|
||||
|
||||
// Security: prevent directory traversal
|
||||
$filename = basename($filename);
|
||||
$filePath = $this->logPath . $filename;
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
return redirect()->to('/logs')->with('error', 'Log file not found');
|
||||
}
|
||||
|
||||
// ✅ extract date from filename: log-YYYY-MM-DD.log
|
||||
if (preg_match('/log-(\d{4}-\d{2}-\d{2})\.log/', $filename, $match)) {
|
||||
$currentDate = $match[1];
|
||||
|
||||
$prevDate = date('Y-m-d', strtotime('-1 day', strtotime($currentDate)));
|
||||
$nextDate = date('Y-m-d', strtotime('+1 day', strtotime($currentDate)));
|
||||
|
||||
$prevFile = "log-$prevDate.log";
|
||||
$nextFile = "log-$nextDate.log";
|
||||
|
||||
$prevExists = file_exists($this->logPath . $prevFile);
|
||||
$nextExists = file_exists($this->logPath . $nextFile);
|
||||
}
|
||||
|
||||
// Read log file content
|
||||
$content = file_get_contents($filePath);
|
||||
$logEntries = $this->parseLogFile($content);
|
||||
|
||||
$data = [
|
||||
'title' => 'View Log: ' . $filename,
|
||||
'filename' => $filename,
|
||||
'logEntries' => $logEntries,
|
||||
'prevFile' => $prevExists ? $prevFile : null,
|
||||
'nextFile' => $nextExists ? $nextFile : null,
|
||||
'fileSize' => $this->formatBytes(filesize($filePath)),
|
||||
'lastModified' => date('Y-m-d H:i:s', filemtime($filePath))
|
||||
];
|
||||
|
||||
// print_r( $data); die;
|
||||
|
||||
return $this->loadLayout('logs/view', $data);
|
||||
// return view('logs/view', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse log file into structured array
|
||||
*/
|
||||
|
||||
private function parseLogFile($content)
|
||||
{
|
||||
$entries = [];
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
$currentEntry = null;
|
||||
|
||||
// Messages to filter out
|
||||
$skipPatterns = [
|
||||
'/Session: Class initialized using/',
|
||||
'/Session class already loaded/',
|
||||
];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Match CI4 log format: LEVEL - date --> message
|
||||
if (preg_match('/^(\w+)\s*-\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s*-->\s*(.*)$/', $line, $matches)) {
|
||||
|
||||
// Save previous entry if exists (before checking skip)
|
||||
if ($currentEntry !== null) {
|
||||
$entries[] = $currentEntry;
|
||||
$currentEntry = null;
|
||||
}
|
||||
|
||||
// Check if this message should be skipped
|
||||
$shouldSkip = false;
|
||||
foreach ($skipPatterns as $pattern) {
|
||||
if (preg_match($pattern, $matches[3])) {
|
||||
$shouldSkip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($shouldSkip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start new entry
|
||||
$currentEntry = [
|
||||
'level' => $matches[1],
|
||||
'date' => $matches[2],
|
||||
'message' => $matches[3]
|
||||
];
|
||||
} elseif ($currentEntry !== null && trim($line) !== '') {
|
||||
// Continuation of previous message
|
||||
$currentEntry['message'] .= "\n" . $line;
|
||||
}
|
||||
}
|
||||
|
||||
// Add last entry
|
||||
if ($currentEntry !== null) {
|
||||
$entries[] = $currentEntry;
|
||||
}
|
||||
|
||||
return array_reverse($entries); // Latest first
|
||||
}
|
||||
|
||||
/**
|
||||
* Download log file
|
||||
*/
|
||||
public function download($filename = null)
|
||||
{
|
||||
if (!$filename) {
|
||||
return redirect()->to('/logs')->with('error', 'No log file specified');
|
||||
}
|
||||
|
||||
$filename = basename($filename);
|
||||
$filePath = $this->logPath . $filename;
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
return redirect()->to('/logs')->with('error', 'Log file not found');
|
||||
}
|
||||
|
||||
return $this->response->download($filePath, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete log file
|
||||
*/
|
||||
public function delete($filename = null)
|
||||
{
|
||||
if (!$filename) {
|
||||
return redirect()->to('/logs')->with('error', 'No log file specified');
|
||||
}
|
||||
|
||||
$filename = basename($filename);
|
||||
$filePath = $this->logPath . $filename;
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
return redirect()->to('/logs')->with('error', 'Log file not found');
|
||||
}
|
||||
|
||||
if (unlink($filePath)) {
|
||||
return redirect()->to('/logs')->with('success', 'Log file deleted successfully');
|
||||
} else {
|
||||
return redirect()->to('/logs')->with('error', 'Failed to delete log file');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format bytes to human readable format
|
||||
*/
|
||||
private function formatBytes($bytes, $precision = 2)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= pow(1024, $pow);
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all log files
|
||||
*/
|
||||
public function clearAll()
|
||||
{
|
||||
$logFiles = $this->getLogFiles();
|
||||
$deleted = 0;
|
||||
|
||||
foreach ($logFiles as $file) {
|
||||
if (unlink($file['path'])) {
|
||||
$deleted++;
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->to('/logs')->with('success', $deleted . ' log file(s) deleted successfully');
|
||||
}
|
||||
}
|
||||
@ -150,7 +150,7 @@ class MediAssistApiController extends BaseController
|
||||
return;
|
||||
|
||||
} else {
|
||||
log_message('error', 'TPA CLAIM PUSH SUCCESS BUT claimReferenceNo EMPTY | claimId: '.$claimId.' | response: '.json_encode($response));
|
||||
log_message('error', 'TPA CLAIM PUSH API SUCCESS BUT claimReferenceNo EMPTY | claimId: '.$claimId.' | response: '.json_encode($response));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ class MediAssistApiController extends BaseController
|
||||
$client_policy_id = $requestData['client_policy_id'] ?? null;
|
||||
|
||||
if (empty($policyNo)) {
|
||||
log_message('error', 'GetBenefDetails: policy_no missing in request');
|
||||
log_message('error', 'TPA ID PULL | policy_no missing in request');
|
||||
if($function_calling_type == "job"){
|
||||
return ['status' => false, 'message' => 'policy_no required'];
|
||||
}else{
|
||||
@ -232,7 +232,7 @@ class MediAssistApiController extends BaseController
|
||||
}
|
||||
|
||||
if (empty($client_policy_id)) {
|
||||
log_message('error', 'GetBenefDetails: client_policy_id missing in request');
|
||||
log_message('error', 'TPA ID PULL | client_policy_id missing in request');
|
||||
if($function_calling_type == "job"){
|
||||
return ['status' => false, 'message' => 'client_policy_id required'];
|
||||
}else{
|
||||
@ -240,8 +240,7 @@ class MediAssistApiController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
log_message('error', "GetBenefDetails called for policy_no: {$policyNo}");
|
||||
log_message('error', "GetBenefDetails called for client_policy_id: {$client_policy_id}");
|
||||
log_message('error', "TPA ID PULL | called for policy_no: {$policyNo} , client_policy_id: {$client_policy_id}");
|
||||
|
||||
$employeePolicyModel = new EmployeePolicyModel();
|
||||
$employeePolicyData = $employeePolicyModel
|
||||
@ -260,7 +259,7 @@ class MediAssistApiController extends BaseController
|
||||
->findAll();
|
||||
|
||||
if (empty($employeePolicyData)) {
|
||||
log_message('error', 'GetBenefDetails: employeePolicyData is empty');
|
||||
log_message('error', 'TPA ID PULL FAILED | employeePolicyData is empty (tpa_id IS NULL from nhance) for this tpa id pull request');
|
||||
if($function_calling_type == "job"){
|
||||
return ['status' => false, 'message' => 'employeePolicyData not found'];
|
||||
}else{
|
||||
@ -284,8 +283,8 @@ class MediAssistApiController extends BaseController
|
||||
"employeeId" => ""
|
||||
];
|
||||
|
||||
log_message('error', "GetBenefDetails API Request (startIndex={$startIndex}): " . json_encode($body));
|
||||
log_message('error', "GetBenefDetails API parems " . json_encode([$url, $method, $headers, $body]));
|
||||
log_message('error', "TPA ID PULL | API Request (startIndex={$startIndex}): " . json_encode($body));
|
||||
log_message('error', "TPA ID PULL | API parems " . json_encode([$url, $method, $headers, $body]));
|
||||
|
||||
$response = call_third_party_api($url, $method, $headers, $body);
|
||||
|
||||
@ -300,7 +299,7 @@ class MediAssistApiController extends BaseController
|
||||
log_message('error', "Failed to update file table status.");
|
||||
}
|
||||
|
||||
log_message('error', 'GetBenefDetails API failed: ' . json_encode($response));
|
||||
log_message('error', 'TPA ID PULL API FAILED | API failed: ' . json_encode($response));
|
||||
|
||||
if($function_calling_type == "job"){
|
||||
return ['status' => false, 'message' => 'API call failed', 'data' => $response];
|
||||
@ -312,7 +311,7 @@ class MediAssistApiController extends BaseController
|
||||
$data = $response['data'] ?? [];
|
||||
|
||||
if (!isset($data['benefDetails'])) {
|
||||
log_message('error', "GetBenefDetails: 'benefDetails' missing in API response: " . json_encode($data));
|
||||
log_message('error', "TPA ID PULL FAILED |: 'benefDetails' missing in API response: " . json_encode($data));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -406,7 +405,7 @@ class MediAssistApiController extends BaseController
|
||||
}
|
||||
|
||||
|
||||
log_message('error', "GetBenefDetails completed. Total fetched={$totalCount}, updated={$updated}");
|
||||
log_message('error', "TPA ID PULL SUCCESS | completed. Total fetched={$totalCount}, updated={$updated}");
|
||||
|
||||
if($function_calling_type == "job"){
|
||||
return [
|
||||
@ -532,7 +531,7 @@ class MediAssistApiController extends BaseController
|
||||
$response = call_third_party_api($url, $method, $headers, $body);
|
||||
|
||||
if ($response['status'] != true || empty($response['data']['claimsData'][0])) {
|
||||
log_message('error', 'Claim status API failed for ticket ID: ' . $claimId);
|
||||
log_message('error', 'CLAIM STATUS FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response));
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => false,
|
||||
@ -598,7 +597,7 @@ class MediAssistApiController extends BaseController
|
||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||
|
||||
// LOG UPDATE
|
||||
log_message('info', "Updated ticket ID $claimId with claim status: $currentStatus");
|
||||
log_message('info', "CLAIM STATUS SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => true,
|
||||
|
||||
@ -7,15 +7,18 @@ use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
class VidalApiController extends BaseController
|
||||
{
|
||||
public function index()
|
||||
protected $db;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
$this->db = \Config\Database::connect();
|
||||
}
|
||||
|
||||
|
||||
function uploadFileToVidal($filePath,$filename)
|
||||
{
|
||||
$apiUrl = "https://devapigw.vidalhealthtpa.com/partner-integration/api/files/upload-url";
|
||||
// $apiUrl = "https://devapigw.vidalhealthtpa.com/partner-integration/api/files/upload-url";
|
||||
$apiUrl = getenv('VIDAL_API_BASE_URL').'/files/upload-url';
|
||||
$subscriptionKey = getenv('VIDAL_API_SUBSCRIPTION_KEY');
|
||||
|
||||
log_message('error', "Starting file upload process for filename: $filename | Path: $filePath");
|
||||
@ -105,10 +108,8 @@ class VidalApiController extends BaseController
|
||||
{
|
||||
helper('api');
|
||||
|
||||
//Prepare body data
|
||||
$db = \Config\Database::connect();
|
||||
// Fetch the data from DB
|
||||
$data = $db->table('ticket_master tm')
|
||||
$data = $this->db->table('ticket_master tm')
|
||||
->select('
|
||||
tm.id,
|
||||
tm.emp_mobile as mobileNo,
|
||||
@ -116,9 +117,14 @@ class VidalApiController extends BaseController
|
||||
tm.doa as admissionDate,
|
||||
tm.dod as dischargeDate,
|
||||
tm.hospital_name as hospitalName,
|
||||
tm.hospital_address as hospitalAddress,
|
||||
tm.hospital_state as hospitalState,
|
||||
tm.hospital_city as hospitalCity,
|
||||
tm.hospital_pin_code as hospitalPinCode,
|
||||
tm.hospital_phone_no as hospitalPhoneNo,
|
||||
tm.claim_amount as requestedAmount,
|
||||
tm.tpa_no as dependentUniqueId,
|
||||
cp.policy_no as policyNo,
|
||||
e.id as dependentUniqueIdOld,
|
||||
e.emp_code as memberId,
|
||||
tn.note as disease,
|
||||
tn.note as reasonForHospitalization,
|
||||
@ -126,7 +132,7 @@ class VidalApiController extends BaseController
|
||||
cf.url as filePath,
|
||||
pt.policy_type as typeOfClaim,
|
||||
ep.tpa_id as empanelmentNo,
|
||||
ep.tpa_id as dependentUniqueId,
|
||||
|
||||
')
|
||||
->join('employees e', 'e.id = tm.emp_id', 'left')
|
||||
->join('client_policy cp', 'tm.client_policy_id = cp.id', 'left')
|
||||
@ -196,7 +202,9 @@ class VidalApiController extends BaseController
|
||||
],
|
||||
];
|
||||
// dd($body);
|
||||
log_message('error', "Submit claim failed - payload ". json_encode($body ));
|
||||
|
||||
log_message('error', 'TPA CLAIM PUSH | claimId: '.$claimId.' | payload: '.json_encode($body));
|
||||
|
||||
// $body = [
|
||||
// 'policyNo' => "351500/D0534/PP/20-20/PC",
|
||||
// 'dependentUniqueId' => "EN000000182-C-41",
|
||||
@ -227,16 +235,41 @@ class VidalApiController extends BaseController
|
||||
$response = call_third_party_api($url, $method, $headers, $body);
|
||||
|
||||
if($response['status'] != true){
|
||||
return $this->response->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'failed.',
|
||||
'data' => $response
|
||||
]);
|
||||
log_message('error', 'TPA CLAIM PUSH FAILED | claimId: '.$claimId.' | response: '.json_encode($response));
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->response->setJSON($response);
|
||||
// return $this->response->setJSON($response);
|
||||
|
||||
if($response['data']['status'] == 'SUCCESS')
|
||||
{
|
||||
$claimNO = $response['data']['data']['claimNO'] ?? null;
|
||||
$claimInwardNO = $response['data']['data']['claimInwardNO'] ?? null;
|
||||
|
||||
if(!empty($claimNO) && !empty($claimInwardNO)){
|
||||
|
||||
log_message('error', 'TPA CLAIM PUSH SUCCESS | claimId: '.$claimId.' | claimNO: '.$claimNO.' | claimInwardNO: '.$claimInwardNO);
|
||||
|
||||
$this->db->table('ticket_master')
|
||||
->where('id',$claimId)
|
||||
->update([ 'tpa_claim_push_reference_no' => $claimInwardNO , 'tpa_claim_id' => $claimNO ]);
|
||||
|
||||
return;
|
||||
|
||||
} else {
|
||||
log_message('error', 'TPA CLAIM PUSH API SUCCESS BUT claimNO,claimInwardNO EMPTY | claimId: '.$claimId.' | response: '.json_encode($response));
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
log_message('error', 'TPA CLAIM PUSH API FAILED | claimId: '.$claimId.' | response: '.json_encode($response));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------yet to start only submit claim given
|
||||
|
||||
public function fileUpload()
|
||||
{
|
||||
|
||||
|
||||
303
app/Views/logs/index.php
Normal file
303
app/Views/logs/index.php
Normal file
@ -0,0 +1,303 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= esc($title) ?></title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
} */
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header p {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #e9ecef;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #c82333;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(220, 53, 69, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #5568d3;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.no-logs {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.no-logs svg {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 600;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-card h3 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-card p {
|
||||
opacity: 0.9;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- <div class="header">
|
||||
<h1>📋 Log File Manager</h1>
|
||||
<p>View and manage your CodeIgniter 4 log files</p>
|
||||
</div> -->
|
||||
|
||||
<div class="content">
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="alert alert-success">
|
||||
✓ <?= session()->getFlashdata('success') ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="alert alert-error">
|
||||
✗ <?= session()->getFlashdata('error') ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- <div class="stats">
|
||||
<div class="stat-card">
|
||||
<h3><?= count($logFiles) ?></h3>
|
||||
<p>Total Log Files</p>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="actions">
|
||||
<h2 style="color: #495057;">Log Files</h2>
|
||||
<?php if (!empty($logFiles)): ?>
|
||||
<!-- <a href="<?= base_url('logs/clearAll') ?>"
|
||||
class="btn btn-danger"
|
||||
onclick="return confirm('Are you sure you want to delete all log files?')">
|
||||
🗑️ Clear All Logs
|
||||
</a> -->
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (empty($logFiles)): ?>
|
||||
<div class="no-logs">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<h3>No Log Files Found</h3>
|
||||
<p>There are no log files to display at the moment.</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File Name</th>
|
||||
<th>Size</th>
|
||||
<th>Last Modified</th>
|
||||
<th style="text-align: center;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($logFiles as $file): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="file-name">
|
||||
📄 <?= esc($file['name']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= esc($file['size']) ?></td>
|
||||
<td><?= esc($file['modified_date']) ?></td>
|
||||
<td>
|
||||
<div class="action-buttons" style="justify-content: center;">
|
||||
<a href="<?= base_url('logs/view/' . urlencode($file['name'])) ?>"
|
||||
class="btn btn-primary btn-sm">
|
||||
View
|
||||
</a>
|
||||
<a href="<?= base_url('logs/download/' . urlencode($file['name'])) ?>"
|
||||
class="btn btn-success btn-sm">
|
||||
Download
|
||||
</a>
|
||||
<!-- <a href="<?= base_url('logs/delete/' . urlencode($file['name'])) ?>"
|
||||
class="btn btn-danger btn-sm"
|
||||
onclick="return confirm('Are you sure you want to delete this log file?')">
|
||||
Delete
|
||||
</a> -->
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
483
app/Views/logs/view.php
Normal file
483
app/Views/logs/view.php
Normal file
@ -0,0 +1,483 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= esc($title) ?></title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
} */
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
/* background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); */
|
||||
color: black;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.file-info {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
margin-top: 15px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.file-info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #dee2e6;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
border-color: #667eea;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 10px 15px;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
width: 300px;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.search-box:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.log-entries {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
background: white;
|
||||
border-left: 4px solid #6c757d;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.log-entry:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.log-entry.critical {
|
||||
border-left-color: #dc3545;
|
||||
background: #fff5f5;
|
||||
}
|
||||
|
||||
.log-entry.error {
|
||||
border-left-color: #fd7e14;
|
||||
background: #fff8f5;
|
||||
}
|
||||
|
||||
.log-entry.warning {
|
||||
border-left-color: #ffc107;
|
||||
background: #fffef5;
|
||||
}
|
||||
|
||||
.log-entry.info {
|
||||
border-left-color: #17a2b8;
|
||||
background: #f5fcfd;
|
||||
}
|
||||
|
||||
.log-entry.debug {
|
||||
border-left-color: #6c757d;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.log-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.log-level {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.level-critical {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.level-error {
|
||||
background: #fd7e14;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.level-warning {
|
||||
background: #ffc107;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.level-info {
|
||||
background: #17a2b8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.level-debug {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.log-date {
|
||||
color: #6c757d;
|
||||
font-size: 13px;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.log-message {
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.no-logs {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
padding: 0px;
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
border-width: 1px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
|
||||
border-color: #0056b3;
|
||||
}
|
||||
|
||||
.stat-card h3 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-card p {
|
||||
opacity: 0.9;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.search-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<h1>
|
||||
📄 <?= esc($filename) ?>
|
||||
</h1>
|
||||
|
||||
<div style="display:flex; gap:10px; margin-bottom:15px;">
|
||||
<?php if ($prevFile): ?>
|
||||
<a href="<?= base_url('logs/view/' . $prevFile) ?>" class="btn btn-primary">Previous</a>
|
||||
<?php else: ?>
|
||||
<button class="btn btn-secondary" disabled>⬅ Previous Day</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($nextFile): ?>
|
||||
<a href="<?= base_url('logs/view/' . $nextFile) ?>" class="btn btn-primary">Next</a>
|
||||
<?php else: ?>
|
||||
<button class="btn btn-secondary" disabled>Next Day ➡</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <div class="file-info">
|
||||
<div class="file-info-item">
|
||||
<strong>Size:</strong> <?= esc($fileSize) ?>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<strong>Last Modified:</strong> <?= esc($lastModified) ?>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<strong>Total Entries:</strong> <?= count($logEntries) ?>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="content">
|
||||
<div class="stats">
|
||||
|
||||
|
||||
|
||||
<!--<div class="stat-card">
|
||||
<h3 id="total-count"><?= count($logEntries) ?></h3>
|
||||
<p>Total Entries</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="critical-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => strtoupper($e['level']) === 'CRITICAL')) ?>
|
||||
</h3>
|
||||
<p>Critical</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="error-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => strtoupper($e['level']) === 'ERROR')) ?>
|
||||
</h3>
|
||||
<p>Errors</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="warning-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => strtoupper($e['level']) === 'WARNING')) ?>
|
||||
</h3>
|
||||
<p>Warnings</p>
|
||||
</div> -->
|
||||
|
||||
|
||||
|
||||
<div class="stat-card">
|
||||
<h3 id="newtoken-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => stripos($e['message'], 'TPA CLAIM PUSH SUCCESS') !== false)) ?>
|
||||
</h3>
|
||||
<p>Claim success</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="newtoken-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => stripos($e['message'], 'TPA CLAIM PUSH FAILED') !== false)) ?>
|
||||
</h3>
|
||||
<p>Claim failed</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="newtoken-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => stripos($e['message'], 'TPA ID PULL SUCCESS') !== false)) ?>
|
||||
</h3>
|
||||
<p>Tpa no pull success</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="newtoken-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => stripos($e['message'], 'TPA ID PULL FAILED') !== false)) ?>
|
||||
</h3>
|
||||
<p>Tpa no pull Failed</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="newtoken-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => stripos($e['message'], 'CLAIM STATUS SUCCESS') !== false)) ?>
|
||||
</h3>
|
||||
<p>Claim status fetch success</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="newtoken-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => stripos($e['message'], 'CLAIM STATUS FAILED') !== false)) ?>
|
||||
</h3>
|
||||
<p>Claim status fetch failed</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="newtoken-count">
|
||||
<?= count(array_filter($logEntries, fn($e) => stripos($e['message'], 'Ecard Request PUSH SUCCESS') !== false)) ?>
|
||||
</h3>
|
||||
<p>Ecard Request</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="filter-group">
|
||||
<strong>Filter:</strong>
|
||||
<button class="filter-btn active" data-level="all">All</button>
|
||||
<button class="filter-btn" data-level="critical">Critical</button>
|
||||
<button class="filter-btn" data-level="error">Error</button>
|
||||
<button class="filter-btn" data-level="warning">Warning</button>
|
||||
<button class="filter-btn" data-level="info">Info</button>
|
||||
<button class="filter-btn" data-level="debug">Debug</button>
|
||||
</div>
|
||||
<input type="text" class="search-box" id="searchBox" placeholder="🔍 Search log messages...">
|
||||
</div>
|
||||
|
||||
<?php if (empty($logEntries)): ?>
|
||||
<div class="no-logs">
|
||||
<h3>No Log Entries Found</h3>
|
||||
<p>This log file is empty or couldn't be parsed.</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="log-entries" id="logEntries">
|
||||
<?php foreach ($logEntries as $entry): ?>
|
||||
<?php
|
||||
$level = strtolower($entry['level']);
|
||||
$levelClass = 'level-' . $level;
|
||||
?>
|
||||
<div class="log-entry <?= $level ?>" data-level="<?= $level ?>">
|
||||
<div class="log-header">
|
||||
<span class="log-level <?= $levelClass ?>">
|
||||
<?= esc(strtoupper($entry['level'])) ?>
|
||||
</span>
|
||||
<span class="log-date"><?= esc($entry['date']) ?></span>
|
||||
</div>
|
||||
<div class="log-message"><?= esc($entry['message']) ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Filter functionality
|
||||
const filterBtns = document.querySelectorAll('.filter-btn');
|
||||
const logEntries = document.querySelectorAll('.log-entry');
|
||||
const searchBox = document.getElementById('searchBox');
|
||||
|
||||
let currentFilter = 'all';
|
||||
|
||||
filterBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
filterBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
currentFilter = btn.dataset.level;
|
||||
applyFilters();
|
||||
});
|
||||
});
|
||||
|
||||
searchBox.addEventListener('input', applyFilters);
|
||||
|
||||
function applyFilters() {
|
||||
const searchTerm = searchBox.value.toLowerCase();
|
||||
|
||||
logEntries.forEach(entry => {
|
||||
const level = entry.dataset.level;
|
||||
const message = entry.querySelector('.log-message').textContent.toLowerCase();
|
||||
|
||||
const matchesFilter = currentFilter === 'all' || level === currentFilter;
|
||||
const matchesSearch = message.includes(searchTerm);
|
||||
|
||||
if (matchesFilter && matchesSearch) {
|
||||
entry.classList.remove('hidden');
|
||||
} else {
|
||||
entry.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user