38471-vm/pages/logs_view.php
2026-05-03 07:32:13 +00:00

157 lines
12 KiB
PHP

<?php
$wablasTableReady = function_exists('wablasDispatchLogTableReady') && wablasDispatchLogTableReady();
$wablasLogs = $wablasTableReady && function_exists('wablasFetchRecentDispatchLogs')
? wablasFetchRecentDispatchLogs(25)
: [];
$wablasPreview = static function (string $value, int $limit = 120): string {
$value = trim($value);
if ($value === '') {
return '';
}
if (function_exists('mb_strlen') && function_exists('mb_substr')) {
return mb_strlen($value) > $limit ? mb_substr($value, 0, $limit - 3) . '...' : $value;
}
return strlen($value) > $limit ? substr($value, 0, $limit - 3) . '...' : $value;
};
$logFiles = ['runtime_debug.log', 'error_log', 'login_debug.log', 'post_debug.log', 'search_debug.log', 'debug.log'];
?>
<div class="d-flex flex-column gap-4">
<div class="card border-0 shadow-sm rounded-4 overflow-hidden">
<div class="card-header bg-white py-3 d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-3 border-0">
<div>
<h1 class="h5 m-0 fw-bold text-primary" data-en="System Logs & Wablas Activity" data-ar="سجلات النظام ونشاط Wablas">System Logs & Wablas Activity</h1>
<p class="text-muted small mb-0" data-en="Monitor file logs plus recent WhatsApp dispatch results" data-ar="راقب سجلات الملفات بالإضافة إلى نتائج إرسال واتساب الأخيرة">Monitor file logs plus recent WhatsApp dispatch results</p>
</div>
<div class="d-flex gap-2 flex-wrap">
<a href="<?= htmlspecialchars(page_url('settings', ['tab' => 'integrations'])) ?>" class="btn btn-outline-secondary btn-sm rounded-pill px-3">
<i class="bi bi-whatsapp me-1"></i>
<span data-en="Wablas Settings" data-ar="إعدادات Wablas">Wablas Settings</span>
</a>
<button onclick="window.location.reload()" class="btn btn-outline-primary btn-sm rounded-pill px-3">
<i class="bi bi-arrow-clockwise me-1"></i>
<span data-en="Refresh" data-ar="تحديث">Refresh</span>
</button>
</div>
</div>
<div class="card-body p-0">
<?php if (!$wablasTableReady): ?>
<div class="p-4">
<div class="alert alert-warning border-0 shadow-sm mb-0" data-en="The Wablas dispatch log table is not available yet. Run the migration before using WhatsApp automation." data-ar="جدول سجل إرسال Wablas غير متاح بعد. شغّل الترحيل قبل استخدام أتمتة واتساب.">The Wablas dispatch log table is not available yet. Run the migration before using WhatsApp automation.</div>
</div>
<?php elseif ($wablasLogs === []): ?>
<div class="text-center py-5 px-4">
<i class="bi bi-chat-square-text fs-1 text-muted opacity-25"></i>
<p class="text-muted mt-3 mb-1" data-en="No Wablas activity has been logged yet." data-ar="لم يتم تسجيل أي نشاط Wablas حتى الآن.">No Wablas activity has been logged yet.</p>
<p class="text-muted small mb-0" data-en="Create a sales invoice, send a test WhatsApp from Settings, or run the queue to see entries here." data-ar="أنشئ فاتورة بيع، أو أرسل رسالة واتساب تجريبية من الإعدادات، أو شغّل قائمة الانتظار لرؤية السجلات هنا.">Create a sales invoice, send a test WhatsApp from Settings, or run the queue to see entries here.</p>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th class="px-4 py-3" data-en="Event" data-ar="الحدث">Event</th>
<th class="px-4 py-3" data-en="Status" data-ar="الحالة">Status</th>
<th class="px-4 py-3" data-en="Schedule / Attempt" data-ar="الجدولة / المحاولة">Schedule / Attempt</th>
<th class="px-4 py-3" data-en="Recipients / Message" data-ar="المستلمون / الرسالة">Recipients / Message</th>
<th class="px-4 py-3" data-en="Response" data-ar="الاستجابة">Response</th>
</tr>
</thead>
<tbody>
<?php foreach ($wablasLogs as $row): ?>
<?php
$eventType = (string) ($row['event_type'] ?? '');
$eventLabel = function_exists('wablasDispatchEventLabel') ? wablasDispatchEventLabel($eventType) : ucfirst(str_replace('_', ' ', $eventType));
$statusMeta = function_exists('wablasDispatchStatusMeta')
? wablasDispatchStatusMeta((string) ($row['status'] ?? 'pending'))
: ['label' => ucfirst((string) ($row['status'] ?? 'Pending')), 'class' => 'bg-warning bg-opacity-10 text-warning'];
$requestPayload = function_exists('wablasJsonDecode') ? wablasJsonDecode((string) ($row['request_payload'] ?? '')) : [];
$responsePayload = function_exists('wablasJsonDecode') ? wablasJsonDecode((string) ($row['response_payload'] ?? '')) : [];
$numbers = isset($requestPayload['numbers']) && is_array($requestPayload['numbers']) ? array_values($requestPayload['numbers']) : [];
$numbersPreview = implode(', ', array_slice($numbers, 0, 3));
if (count($numbers) > 3) {
$numbersPreview .= ' +' . (count($numbers) - 3) . ' more';
}
$messagePreview = $wablasPreview((string) ($requestPayload['message'] ?? ''), 140);
$httpCode = (string) ($responsePayload['http_code'] ?? '');
$errorMessage = trim((string) ($row['error_message'] ?? ''));
?>
<tr>
<td class="px-4 py-3">
<div class="fw-semibold text-dark"><?= htmlspecialchars($eventLabel !== '' ? $eventLabel : 'Dispatch') ?></div>
<div class="small text-muted"><code><?= htmlspecialchars((string) ($row['event_key'] ?? '')) ?></code></div>
<div class="small text-muted mt-1">
<span data-en="Created" data-ar="أُنشئ">Created</span>:
<?= htmlspecialchars((string) ($row['created_at'] ?? '—')) ?>
</div>
</td>
<td class="px-4 py-3">
<span class="badge rounded-pill <?= htmlspecialchars($statusMeta['class']) ?> px-3 py-2"><?= htmlspecialchars($statusMeta['label']) ?></span>
<div class="small text-muted mt-2">
<span data-en="Attempts" data-ar="المحاولات">Attempts</span>:
<?= (int) ($row['attempt_count'] ?? 0) ?>
</div>
</td>
<td class="px-4 py-3 small text-muted">
<div><span class="fw-semibold text-dark" data-en="Scheduled" data-ar="مجدول">Scheduled</span>: <?= htmlspecialchars((string) ($row['scheduled_for'] ?? '—')) ?></div>
<div class="mt-1"><span class="fw-semibold text-dark" data-en="Last attempt" data-ar="آخر محاولة">Last attempt</span>: <?= htmlspecialchars((string) ($row['last_attempt_at'] ?? '—')) ?></div>
</td>
<td class="px-4 py-3">
<div class="small text-muted mb-1">
<span class="fw-semibold text-dark" data-en="Recipients" data-ar="المستلمون">Recipients</span>:
<?= htmlspecialchars($numbersPreview !== '' ? $numbersPreview : '—') ?>
</div>
<div class="small text-muted">
<span class="fw-semibold text-dark" data-en="Message" data-ar="الرسالة">Message</span>:
<?= htmlspecialchars($messagePreview !== '' ? $messagePreview : '—') ?>
</div>
</td>
<td class="px-4 py-3">
<?php if ($httpCode !== ''): ?>
<div class="small text-muted mb-1"><span class="fw-semibold text-dark">HTTP</span>: <?= htmlspecialchars($httpCode) ?></div>
<?php endif; ?>
<?php if ($errorMessage !== ''): ?>
<div class="small text-danger"><?= htmlspecialchars($wablasPreview($errorMessage, 160)) ?></div>
<?php else: ?>
<div class="small text-muted" data-en="No error recorded" data-ar="لا يوجد خطأ مسجل">No error recorded</div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
<div class="card border-0 shadow-sm rounded-4 overflow-hidden">
<div class="card-header bg-white py-3 border-0">
<div>
<h2 class="h6 m-0 fw-bold text-primary" data-en="File-based Debug Logs" data-ar="سجلات التصحيح النصية">File-based Debug Logs</h2>
<p class="text-muted small mb-0" data-en="Latest lines from the app log files available in this workspace" data-ar="أحدث السطور من ملفات سجلات التطبيق المتاحة في مساحة العمل هذه">Latest lines from the app log files available in this workspace</p>
</div>
</div>
<div class="card-body p-0">
<div class="bg-dark text-light p-4 font-monospace small" style="max-height: 600px; overflow-y: auto;">
<?php
$foundLogs = false;
foreach ($logFiles as $file) {
$path = __DIR__ . '/../' . $file;
if (file_exists($path) && is_readable($path)) {
$foundLogs = true;
echo "<div class='mb-4'><h6 class='text-warning border-bottom border-secondary pb-2'>--- " . htmlspecialchars(basename($file)) . " ---</h6>";
$lines = shell_exec("tail -n 50 " . escapeshellarg($path));
echo "<pre class='mb-0 text-info'>" . htmlspecialchars((string) $lines) . "</pre></div>";
}
}
if (!$foundLogs) {
echo "<div class='text-center py-5'><i class='bi bi-journal-x fs-1 opacity-25'></i><p class='mt-2 opacity-50'>No accessible log files found.</p></div>";
}
?>
</div>
</div>
</div>
</div>