184 lines
4.9 KiB
PHP
184 lines
4.9 KiB
PHP
<?php
|
|
/**
|
|
* Dashboard Recent Activity Endpoint
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Core\Database;
|
|
use App\Core\Encryption;
|
|
use App\Middleware\AuthMiddleware;
|
|
|
|
$decoded = AuthMiddleware::check();
|
|
$db = Database::getInstance();
|
|
|
|
$tenantId = $decoded['tenant_id'] ?? null;
|
|
$role = $decoded['role'];
|
|
|
|
try {
|
|
if ($role === 'super_admin') {
|
|
$where = "WHERE 1=1";
|
|
$params = [];
|
|
} else {
|
|
$where = "WHERE a.tenant_id = ?";
|
|
$params = [$tenantId];
|
|
}
|
|
|
|
$stmt = $db->prepare("
|
|
SELECT
|
|
a.id,
|
|
a.action,
|
|
a.entity_type,
|
|
a.entity_id,
|
|
a.new_data,
|
|
a.created_at,
|
|
u.name AS user_name
|
|
FROM audit_logs a
|
|
LEFT JOIN users u ON a.user_id = u.id
|
|
$where
|
|
ORDER BY a.created_at DESC
|
|
LIMIT 20
|
|
");
|
|
$stmt->execute($params);
|
|
$activities = $stmt->fetchAll();
|
|
|
|
foreach ($activities as &$activity) {
|
|
$activity['user_name'] = decryptIfEncrypted($activity['user_name'] ?? null) ?: 'مستخدم مجهول';
|
|
$activity['details'] = decodeAuditData($activity['new_data'] ?? null);
|
|
$activity['summary'] = buildActivitySummary($activity);
|
|
unset($activity['new_data']);
|
|
}
|
|
unset($activity);
|
|
|
|
json_success($activities);
|
|
|
|
} catch (\Exception $e) {
|
|
error_log('Dashboard Recent Activity Error: ' . $e->getMessage());
|
|
json_error('Failed to fetch recent activity', 500);
|
|
}
|
|
|
|
function decodeAuditData(?string $json): array
|
|
{
|
|
if (!$json) {
|
|
return [];
|
|
}
|
|
|
|
$decoded = json_decode($json, true);
|
|
if (!is_array($decoded)) {
|
|
return [];
|
|
}
|
|
|
|
return decryptAuditPayload($decoded);
|
|
}
|
|
|
|
function decryptAuditPayload(array $payload): array
|
|
{
|
|
foreach ($payload as $key => $value) {
|
|
if (is_array($value)) {
|
|
$payload[$key] = decryptAuditPayload($value);
|
|
continue;
|
|
}
|
|
|
|
if (is_string($value)) {
|
|
$payload[$key] = decryptIfEncrypted($value);
|
|
}
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
function decryptIfEncrypted(mixed $value): string
|
|
{
|
|
if ($value === null) {
|
|
return '';
|
|
}
|
|
|
|
$text = trim((string)$value);
|
|
if ($text === '' || !looksEncrypted($text)) {
|
|
return $text;
|
|
}
|
|
|
|
try {
|
|
$decrypted = Encryption::decrypt($text);
|
|
return $decrypted !== false ? $decrypted : $text;
|
|
} catch (\Throwable $e) {
|
|
return $text;
|
|
}
|
|
}
|
|
|
|
function looksEncrypted(string $value): bool
|
|
{
|
|
$normalized = str_starts_with($value, '==') ? substr($value, 2) : $value;
|
|
|
|
if (strlen($normalized) < 40 || strlen($normalized) % 4 !== 0) {
|
|
return false;
|
|
}
|
|
|
|
return (bool)preg_match('/^[A-Za-z0-9+\/]+={0,2}$/', $normalized);
|
|
}
|
|
|
|
function buildActivitySummary(array $activity): string
|
|
{
|
|
$data = is_array($activity['details'] ?? null) ? $activity['details'] : [];
|
|
$action = (string)($activity['action'] ?? '');
|
|
|
|
return match ($action) {
|
|
'payment.created' => buildPaymentSummary('تم إنشاء طلب دفع', $data),
|
|
'payment.approved' => buildPaymentSummary('تم اعتماد طلب دفع', $data),
|
|
'payment.rejected' => buildPaymentSummary('تم رفض طلب دفع', $data),
|
|
'subscription.activated' => buildPaymentSummary('تم تفعيل الاشتراك', $data),
|
|
'invoice.approved' => buildEntitySummary('تم اعتماد الفاتورة', $data, ['invoice_number', 'number']),
|
|
'invoice.extracted' => buildEntitySummary('تم استخراج بيانات الفاتورة', $data, ['invoice_number', 'number']),
|
|
'company.created' => buildEntitySummary('تمت إضافة شركة', $data, ['name', 'company_name']),
|
|
'user.created' => buildEntitySummary('تمت إضافة مستخدم', $data, ['name', 'email']),
|
|
default => '',
|
|
};
|
|
}
|
|
|
|
function buildPaymentSummary(string $label, array $data): string
|
|
{
|
|
$parts = [$label];
|
|
|
|
$plan = firstTextValue($data, ['plan_name', 'plan_name_ar', 'plan_id']);
|
|
if ($plan !== '') {
|
|
$parts[] = "الباقة: {$plan}";
|
|
}
|
|
|
|
$amount = firstTextValue($data, ['amount', 'amount_jod', 'price_jod']);
|
|
if ($amount !== '') {
|
|
$parts[] = "القيمة: {$amount} د.أ";
|
|
}
|
|
|
|
$reference = firstTextValue($data, ['ref', 'reference', 'internal_reference']);
|
|
if ($reference !== '') {
|
|
$parts[] = "المرجع: {$reference}";
|
|
}
|
|
|
|
return implode(' - ', $parts);
|
|
}
|
|
|
|
function buildEntitySummary(string $label, array $data, array $keys): string
|
|
{
|
|
$value = firstTextValue($data, $keys);
|
|
return $value === '' ? $label : "{$label}: {$value}";
|
|
}
|
|
|
|
function firstTextValue(array $data, array $keys): string
|
|
{
|
|
foreach ($keys as $key) {
|
|
if (!array_key_exists($key, $data) || $data[$key] === null) {
|
|
continue;
|
|
}
|
|
|
|
$value = $data[$key];
|
|
if (is_scalar($value)) {
|
|
$text = trim((string)$value);
|
|
if ($text !== '') {
|
|
return $text;
|
|
}
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|