chore: remove retired monitor plugin source files
This commit is contained in:
@@ -317,7 +317,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
|
||||
<tr>
|
||||
<td class="text-muted"><?php echo $this->escape(mb_substr($item->title, 0, 30)); ?></td>
|
||||
<td class="text-muted"><?php echo $this->escape($item->username ?? ''); ?></td>
|
||||
<td class="text-muted"><?php echo HTMLHelper::_('date', $item->checked_out_time, 'M d H:i'); ?></td>
|
||||
<td class="text-muted"><?php echo HTMLHelper::_('date', $item->checked_out_time, Text::_('DATE_FORMAT_LC4')); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
@@ -348,7 +348,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
|
||||
<tr>
|
||||
<td class="text-muted"><code><?php echo $this->escape($block->ip); ?></code></td>
|
||||
<td class="text-muted"><span class="badge bg-danger"><?php echo $this->escape($block->rule); ?></span></td>
|
||||
<td class="text-muted"><?php echo HTMLHelper::_('date', $block->created, 'M d H:i'); ?></td>
|
||||
<td class="text-muted"><?php echo HTMLHelper::_('date', $block->created, Text::_('DATE_FORMAT_LC4')); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
@@ -375,7 +375,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
|
||||
<tr>
|
||||
<td class="text-muted"><?php echo $this->escape($login->username ?? ''); ?></td>
|
||||
<td class="text-muted"><code><?php echo $this->escape($login->ip_address ?? ''); ?></code></td>
|
||||
<td class="text-muted"><?php echo HTMLHelper::_('date', $login->log_date, 'M d H:i'); ?></td>
|
||||
<td class="text-muted"><?php echo HTMLHelper::_('date', $login->log_date, Text::_('DATE_FORMAT_LC4')); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
|
||||
@@ -3,6 +3,7 @@ defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\CMS\Session\Session;
|
||||
|
||||
@@ -140,8 +141,8 @@ $typeBadge = [
|
||||
<td><?php echo $this->escape($r->user_name ?? ''); ?><br><small class="text-muted"><?php echo $this->escape($r->user_email ?? ''); ?></small></td>
|
||||
<td><span class="badge <?php echo $typeBadge[$r->type] ?? 'bg-secondary'; ?>"><?php echo ucfirst($r->type); ?></span></td>
|
||||
<td><span class="badge <?php echo $statusBadge[$r->status] ?? 'bg-secondary'; ?>"><?php echo ucfirst($r->status); ?></span></td>
|
||||
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $r->created, 'M d, Y H:i'); ?></td>
|
||||
<td class="text-nowrap small"><?php echo $r->processed ? HTMLHelper::_('date', $r->processed, 'M d, Y H:i') : '—'; ?></td>
|
||||
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $r->created, Text::_('DATE_FORMAT_LC2')); ?></td>
|
||||
<td class="text-nowrap small"><?php echo $r->processed ? HTMLHelper::_('date', $r->processed, Text::_('DATE_FORMAT_LC2')) : '—'; ?></td>
|
||||
<td>
|
||||
<?php if ($r->status === 'pending'): ?>
|
||||
<div class="btn-group btn-group-sm">
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\CMS\Session\Session;
|
||||
|
||||
@@ -32,7 +33,7 @@ $priorities = $this->priorities ?? [];
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong><?php echo $this->escape($t->created_by_name); ?></strong>
|
||||
<small class="text-muted ms-2"><?php echo HTMLHelper::_('date', $t->created, 'M d, Y H:i'); ?></small>
|
||||
<small class="text-muted ms-2"><?php echo HTMLHelper::_('date', $t->created, Text::_('DATE_FORMAT_LC2')); ?></small>
|
||||
</div>
|
||||
<span class="badge bg-dark">Original</span>
|
||||
</div>
|
||||
@@ -59,7 +60,7 @@ $priorities = $this->priorities ?? [];
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong><?php echo $this->escape($reply->user_name ?? 'System'); ?></strong>
|
||||
<small class="text-muted ms-2"><?php echo HTMLHelper::_('date', $reply->created, 'M d, Y H:i'); ?></small>
|
||||
<small class="text-muted ms-2"><?php echo HTMLHelper::_('date', $reply->created, Text::_('DATE_FORMAT_LC2')); ?></small>
|
||||
</div>
|
||||
<?php if ($reply->is_internal): ?>
|
||||
<span class="badge bg-warning text-dark">Internal Note</span>
|
||||
@@ -147,9 +148,9 @@ $priorities = $this->priorities ?? [];
|
||||
<?php if (!empty($t->contact_phone)): ?><br><small><?php echo $this->escape($t->contact_phone); ?></small><?php endif; ?>
|
||||
</td></tr>
|
||||
<?php endif; ?>
|
||||
<tr><td class="text-muted">Created</td><td><?php echo HTMLHelper::_('date', $t->created, 'M d, Y H:i'); ?></td></tr>
|
||||
<?php if ($t->resolved): ?><tr><td class="text-muted">Resolved</td><td><?php echo HTMLHelper::_('date', $t->resolved, 'M d, Y H:i'); ?></td></tr><?php endif; ?>
|
||||
<?php if ($t->closed): ?><tr><td class="text-muted">Closed</td><td><?php echo HTMLHelper::_('date', $t->closed, 'M d, Y H:i'); ?></td></tr><?php endif; ?>
|
||||
<tr><td class="text-muted">Created</td><td><?php echo HTMLHelper::_('date', $t->created, Text::_('DATE_FORMAT_LC2')); ?></td></tr>
|
||||
<?php if ($t->resolved): ?><tr><td class="text-muted">Resolved</td><td><?php echo HTMLHelper::_('date', $t->resolved, Text::_('DATE_FORMAT_LC2')); ?></td></tr><?php endif; ?>
|
||||
<?php if ($t->closed): ?><tr><td class="text-muted">Closed</td><td><?php echo HTMLHelper::_('date', $t->closed, Text::_('DATE_FORMAT_LC2')); ?></td></tr><?php endif; ?>
|
||||
<tr><td class="text-muted">Replies</td><td><?php echo $t->reply_count; ?></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -167,7 +168,7 @@ $priorities = $this->priorities ?? [];
|
||||
$responseOverdue = !$t->sla_responded && strtotime($t->sla_response_due) < time();
|
||||
?>
|
||||
<span class="<?php echo $t->sla_responded ? 'text-success' : ($responseOverdue ? 'text-danger fw-bold' : ''); ?>">
|
||||
<?php echo $t->sla_responded ? 'Responded' : HTMLHelper::_('date', $t->sla_response_due, 'M d H:i'); ?>
|
||||
<?php echo $t->sla_responded ? 'Responded' : HTMLHelper::_('date', $t->sla_response_due, Text::_('DATE_FORMAT_LC4')); ?>
|
||||
<?php echo $responseOverdue ? ' OVERDUE' : ''; ?>
|
||||
</span>
|
||||
</div>
|
||||
@@ -179,7 +180,7 @@ $priorities = $this->priorities ?? [];
|
||||
$resolutionOverdue = !!empty($t->status_is_closed) && strtotime($t->sla_resolution_due) < time();
|
||||
?>
|
||||
<span class="<?php echo !empty($t->status_is_closed) ? 'text-success' : ($resolutionOverdue ? 'text-danger fw-bold' : ''); ?>">
|
||||
<?php echo !empty($t->status_is_closed) ? 'Met' : HTMLHelper::_('date', $t->sla_resolution_due, 'M d H:i'); ?>
|
||||
<?php echo !empty($t->status_is_closed) ? 'Met' : HTMLHelper::_('date', $t->sla_resolution_due, Text::_('DATE_FORMAT_LC4')); ?>
|
||||
<?php echo $resolutionOverdue ? ' OVERDUE' : ''; ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -112,12 +112,12 @@ $token = Session::getFormToken();
|
||||
echo '<em>Unassigned</em>';
|
||||
}
|
||||
?></td>
|
||||
<td class="small"><?php echo HTMLHelper::_('date', $t->created, 'M d H:i'); ?></td>
|
||||
<td class="small"><?php echo HTMLHelper::_('date', $t->created, Text::_('DATE_FORMAT_LC4')); ?></td>
|
||||
<td class="small">
|
||||
<?php if ($t->sla_response_due && !$t->sla_responded): ?>
|
||||
<span title="Response due"><?php echo HTMLHelper::_('date', $t->sla_response_due, 'M d H:i'); ?></span>
|
||||
<span title="Response due"><?php echo HTMLHelper::_('date', $t->sla_response_due, Text::_('DATE_FORMAT_LC4')); ?></span>
|
||||
<?php elseif ($t->sla_resolution_due): ?>
|
||||
<span title="Resolution due"><?php echo HTMLHelper::_('date', $t->sla_resolution_due, 'M d H:i'); ?></span>
|
||||
<span title="Resolution due"><?php echo HTMLHelper::_('date', $t->sla_resolution_due, Text::_('DATE_FORMAT_LC4')); ?></span>
|
||||
<?php else: ?>—<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -3,6 +3,7 @@ defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\CMS\Session\Session;
|
||||
|
||||
@@ -98,7 +99,7 @@ $ruleBadge = [
|
||||
<?php else: ?>
|
||||
<?php foreach ($logs as $log): ?>
|
||||
<tr>
|
||||
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $log->created, 'M d H:i:s'); ?></td>
|
||||
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $log->created, Text::_('DATE_FORMAT_LC4')); ?></td>
|
||||
<td><code><?php echo htmlspecialchars($log->ip); ?></code></td>
|
||||
<td><span class="badge <?php echo $ruleBadge[$log->rule] ?? 'bg-secondary'; ?>"><?php echo htmlspecialchars($log->rule); ?></span></td>
|
||||
<td class="small" style="max-width:250px;overflow:hidden;text-overflow:ellipsis" title="<?php echo htmlspecialchars($log->uri); ?>"><?php echo htmlspecialchars(mb_substr($log->uri, 0, 60)); ?></td>
|
||||
@@ -148,7 +149,7 @@ $ruleBadge = [
|
||||
<tr>
|
||||
<td><code class="small"><?php echo htmlspecialchars($tip->ip); ?></code></td>
|
||||
<td class="fw-bold"><?php echo $tip->cnt; ?></td>
|
||||
<td class="small text-nowrap"><?php echo HTMLHelper::_('date', $tip->last_seen, 'M d'); ?></td>
|
||||
<td class="small text-nowrap"><?php echo HTMLHelper::_('date', $tip->last_seen, Text::_('DATE_FORMAT_LC4')); ?></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger btn-ban-ip" data-ip="<?php echo htmlspecialchars($tip->ip); ?>"
|
||||
data-url="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.banIpFromLog&format=json'); ?>"
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
; MokoSuiteClient Health Monitor Plugin
|
||||
; Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
; License: GPL-3.0-or-later
|
||||
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR="System - MokoSuiteClient Monitor"
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_DESC="Sends heartbeat data to a MokoSuiteClientHQ control panel for centralized site monitoring."
|
||||
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC="Monitoring"
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC_DESC="Configure heartbeat reporting to MokoSuiteClientHQ."
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_LABEL="Send Heartbeat"
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_DESC="Send heartbeat data to MokoSuiteClientHQ when plugin settings are saved."
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_LABEL="MokoSuiteClientHQ URL"
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_DESC="URL of the MokoSuiteClientHQ control panel (e.g. https://mokoconsulting.tech). The heartbeat is sent to /api/index.php/v1/mokosuiteclienthq/heartbeat on this host."
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
; MokoSuiteClient Health Monitor Plugin - System strings
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR="System - MokoSuiteClient Monitor"
|
||||
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_DESC="Site health monitoring, MokoSuiteClientHQ heartbeat integration, and diagnostics."
|
||||
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>System - MokoSuiteClient Monitor</name>
|
||||
<element>mokosuiteclient_monitor</element>
|
||||
<author>Moko Consulting</author>
|
||||
<creationDate>2026-06-02</creationDate>
|
||||
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.46.07</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientMonitor</namespace>
|
||||
|
||||
<files>
|
||||
<folder>src</folder>
|
||||
<folder>services</folder>
|
||||
<folder>language</folder>
|
||||
</files>
|
||||
|
||||
<languages folder="language">
|
||||
<language tag="en-GB">en-GB/plg_system_mokosuiteclient_monitor.ini</language>
|
||||
<language tag="en-GB">en-GB/plg_system_mokosuiteclient_monitor.sys.ini</language>
|
||||
</languages>
|
||||
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic"
|
||||
label="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC"
|
||||
description="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC_DESC">
|
||||
|
||||
<field name="heartbeat_enabled" type="radio" default="1"
|
||||
label="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_LABEL"
|
||||
description="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_DESC"
|
||||
class="btn-group btn-group-yesno">
|
||||
<option value="1">JYES</option>
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
|
||||
<field name="base_url" type="url"
|
||||
default="https://waas.dev.mokoconsulting.tech"
|
||||
label="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_LABEL"
|
||||
description="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_DESC"
|
||||
filter="url" />
|
||||
|
||||
<field name="signing_key" type="hidden"
|
||||
default="LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRQ2xZNnNzOTZpeTZOOGMKTHRxbndhbnU4eEozdDcrdDhXT3hoY0Yyclc2QmlmOVhNaEpnYkw0c055N0wwV1dTT2tkMmZxalBNcDFtOFNyNAo1VnNycjE3cFc5b0FNMmtmdFdsaTZ1NkhTVEYyN2pVVUJrT3o4MHZMRklMMGNGNkJCUkpYN2JVWkRpamdUMjc1ClREb3dXZy82Zk9GeWFEelBHUkJuYXFacTljU2lEYWoyNlpSTVZIbktQUERWTG92VzRPTDQzL2gwZ3BtN25nUGIKdWJlLzFFTDRUMHFRbm1Xc2FEOFZ6VStoRXFGSDRTVUtMaDVNeklGbUxFZzRlZ0xCbTBXcWdxbzZRQVBnZDVPYgoybXhmQndta3RLVm5hcWR6eG9KSytzaTVuZkYreGpxbWRMZThUdmEyTHNuTUxlZmsrODVoQ3hxS2x1eWRta1lXCjlvUk5qcDhiQWdNQkFBRUNnZ0VBQkZOUS9NSVZaV2gxdlZUMFh3TFBvUEkyZjI4TTBrM0gzN0t4MXBxK2t5QzYKenRyK1pBczBCaEFEWjAwNHJOUmRYaG45N0QxVXBJYVdLeUJFZkNZQUEzWmxneS9WQmdGR21sR3VuMWNvdGdXUQoyYzg0SWhLdzNzVFFqL2dJWUxOelFWMTBLUTJYd0JZVHZ1MWhjRFpLeUxCUGJTQ1F4cEhQUGdVcUNRNFljR3lFClErVmc1dHJUYk8wQ2xCZ1U5bkVnYU1RakRJZ0F3WVZPV203dUxJTW84UC9nT3FuT2tmaFhzdzl3VTJVYWxFeTEKRmRZbGhMbGJ0ZS9MZ3lkYlJ2RStjNEtqZVp0Z3ptc1RneEh2dzM5YVVmZUZTclFRT0FjcXc0alNzUjdMck9UZAp5bDhpelRrZVBrTVFMamFqR0pabWdPbitkRzhtUlpMa3FKcWdGaVpqRVFLQmdRRFV0L0xlU0h5SmhvY3VFL240CkZreEpaclJoWUVsWnc2WlZJUnQzWDlPQ1Nmaklab3I1ZkZlczhvUzZySFhKdGZYeWx4QUxOSjJjTUhKTTViVnUKbUFSUFU4cThBeVc0OE03cHAyNmtVVTMxNXc2OU1SUkhzbWgyekRabEtDeG5GM1NSQ3U4YW95d3hZc3RUZ3hkTgo2bDhLNHZsS1dsN3FYblBhWjZjb3lQSU9od0tCZ1FESENuRmRRdW5SMVI2dkxGaVFZMTRiT3QwT0tzVGJYMUJyCmpvUGZySkxvRm5mSCs4VDVnNUdxYkV5T2p0WG1tRXhmTFFpcDBQVXRtc1E0YXlJRFBZYWZtU3RpK2dtQXZFd1MKZTlKcVYxYlRuazUrYnVRZ2FlOW16REpJWkxaczRJUlhrd1Q5aDZ4Q2xKeS80TGJSRHdBU3dUVGJlY01hN3A4UgpQN0p0bjdsYnpRS0JnQzNOR2FjUTFuZktGb3N1VS9FOTQ5a2VHeEtvWjhMREpLcEp3WjgzYTlRdTF6bFhFdTlhCi9ZbklnaG1yam9VSy85VG0vOVpaMHVIUmNKcnNEdCtzTGFsaThsRC9JSDBzcEhDYzAyN2Y3cmhXc3M2N3BaRTIKY2RXNmJLL2xNWUpWQTQxRFhHNVEyZkFjUklsTHZaWFNNL3FsR21ZUEJVYlRaWUNPTnVqS000dzdBb0dBU1dBdwpLcEZnWVZxUDFVUWo0aGEvdW9vWXRBQlFVZzd4TnJWektDSVdoampDTDVkQkpqcTZtSGtVUC9tb0lUcEQ3VkpNCnYwMnBGUWJaRDNOdk5vS1gvbjRZNElRTXZNaXR3cUtqRDFEalVXQXF6N0ZScUNGbGdDQUc2V2szVnl2dG5kczEKRzhISVgwTXFCaEp4VXVDVXhsVXpoelY4RjVHZ1VsdUpDNkMyVklFQ2dZQkJWSkxpZlNVOTlHWGZtK3dPd0RWcgo2bHZoUFgxOTBGVktWQXY3aVVWTXBwWXg4Y0QxYkcyUjRLT29JbnkxYTlxdjA2ZGFzeGVQOStkVjJVMWU3MWl5CkFXWDRBVHIrYitvSGk2eUk1MXRHRk54RUxiNXZYMVpYM3VNaDlWM29iYUpuSFNjYllpKzBBNjlyRmNuNEZuLzUKWXJybWxLTzRlRHFVZkswbVFJVCtwUT09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
|
||||
filter="raw" />
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @package MokoSuiteClient
|
||||
* @subpackage plg_system_mokosuiteclient_monitor
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Moko\Plugin\System\MokoSuiteClientMonitor\Extension\Monitor;
|
||||
|
||||
return new class implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$dispatcher = $container->get(DispatcherInterface::class);
|
||||
$plugin = new Monitor($dispatcher, (array) PluginHelper::getPlugin('system', 'mokosuiteclient_monitor'));
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,353 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @package MokoSuiteClient
|
||||
* @subpackage plg_system_mokosuiteclient_monitor
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Moko\Plugin\System\MokoSuiteClientMonitor\Extension;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Log\Log;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\CMS\Version;
|
||||
use Joomla\Event\SubscriberInterface;
|
||||
use Moko\Plugin\System\MokoSuiteClient\Helper\MokoSuiteClientHelper;
|
||||
|
||||
/**
|
||||
* MokoSuiteClient Health Monitor Plugin
|
||||
*
|
||||
* Sends heartbeat data to a MokoSuiteClientHQ control panel instance.
|
||||
* Each request is RSA-signed with a private key distributed via
|
||||
* the package manifest, verified by Base using the matching public key.
|
||||
*
|
||||
* @since 02.32.00
|
||||
*/
|
||||
class Monitor extends CMSPlugin implements SubscriberInterface
|
||||
{
|
||||
protected $autoloadLanguage = true;
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'onExtensionAfterSave' => 'onExtensionAfterSave',
|
||||
'onAfterInitialise' => 'onAfterInitialise',
|
||||
'onExtensionAfterInstall' => 'onExtensionAfterInstall',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send heartbeat on first admin page load after install/update.
|
||||
*/
|
||||
public function onAfterInitialise(): void
|
||||
{
|
||||
$app = $this->getApplication();
|
||||
if (!$app->isClient('administrator')) return;
|
||||
if (!$this->params->get('heartbeat_enabled', 1)) return;
|
||||
|
||||
$session = \Joomla\CMS\Factory::getSession();
|
||||
if ($session->get('mokosuiteclient.heartbeat_sent', false)) return;
|
||||
|
||||
// Check if version changed since last heartbeat
|
||||
$lastVersion = $this->params->get('_last_heartbeat_version', '');
|
||||
$currentVersion = $this->getMokoSuiteClientVersion();
|
||||
|
||||
if ($lastVersion !== $currentVersion)
|
||||
{
|
||||
$session->set('mokosuiteclient.heartbeat_sent', true);
|
||||
$this->sendHeartbeat();
|
||||
|
||||
// Store version so we don't re-send every session
|
||||
try
|
||||
{
|
||||
$this->params->set('_last_heartbeat_version', $currentVersion);
|
||||
|
||||
$extension = new \Joomla\CMS\Table\Extension(Factory::getDbo());
|
||||
$extension->load(['element' => 'mokosuiteclient_monitor', 'folder' => 'system', 'type' => 'plugin']);
|
||||
$extension->params = $this->params->toString();
|
||||
$extension->store();
|
||||
}
|
||||
catch (\Throwable $e) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send heartbeat immediately after package install/update.
|
||||
*/
|
||||
public function onExtensionAfterInstall($installer, $eid): void
|
||||
{
|
||||
if (!$this->params->get('heartbeat_enabled', 1)) return;
|
||||
|
||||
try { $this->sendHeartbeat(); }
|
||||
catch (\Throwable $e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* After saving this plugin or the core plugin, send heartbeat.
|
||||
*/
|
||||
public function onExtensionAfterSave($event): void
|
||||
{
|
||||
// Joomla 6: single event object; Joomla 5: individual args
|
||||
if (is_object($event) && method_exists($event, 'getArgument'))
|
||||
{
|
||||
$context = $event->getArgument('context', $event->getArgument(0, ''));
|
||||
$table = $event->getArgument('subject', $event->getArgument(1, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
$context = $event;
|
||||
$table = func_get_arg(1);
|
||||
}
|
||||
|
||||
if ($context !== 'com_plugins.plugin' || !$table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$element = $table->element ?? '';
|
||||
|
||||
if (!\in_array($element, ['mokosuiteclient', 'mokosuiteclient_monitor'], true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->params->get('heartbeat_enabled', 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sendHeartbeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send heartbeat to the MokoSuiteClientHQ control panel.
|
||||
*
|
||||
* The request is RSA-signed: the client signs domain|timestamp|token
|
||||
* with its private key. Base verifies with the matching public key.
|
||||
*/
|
||||
private function sendHeartbeat(): void
|
||||
{
|
||||
$baseUrl = rtrim($this->params->get('base_url', ''), '/');
|
||||
|
||||
if (empty($baseUrl))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$coreParams = MokoSuiteClientHelper::getCoreParams();
|
||||
$healthToken = $coreParams->get('health_api_token', '');
|
||||
|
||||
if (empty($healthToken))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$siteUrl = rtrim(Uri::root(), '/');
|
||||
$domain = parse_url($siteUrl, PHP_URL_HOST) ?: '';
|
||||
|
||||
if (empty($domain))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$app = $this->getApplication();
|
||||
$config = Factory::getConfig();
|
||||
$timestamp = time();
|
||||
|
||||
$payload = [
|
||||
'token' => $healthToken,
|
||||
'domain' => $domain,
|
||||
'site_name' => $config->get('sitename', 'Joomla'),
|
||||
'site_url' => $siteUrl,
|
||||
'joomla_version' => (new Version())->getShortVersion(),
|
||||
'php_version' => PHP_VERSION,
|
||||
'mokosuiteclient_version' => $this->getMokoSuiteClientVersion(),
|
||||
'timestamp' => $timestamp,
|
||||
'client_info' => [
|
||||
'company' => $config->get('sitename', ''),
|
||||
'email' => $config->get('mailfrom', ''),
|
||||
],
|
||||
];
|
||||
|
||||
// Include live health data
|
||||
$healthData = $this->fetchLocalHealth($siteUrl, $healthToken);
|
||||
|
||||
if ($healthData !== null)
|
||||
{
|
||||
$payload['health'] = $healthData;
|
||||
}
|
||||
|
||||
// RSA sign the request
|
||||
$headers = ['Content-Type: application/json'];
|
||||
$signature = $this->signRequest($domain, $timestamp, $healthToken);
|
||||
|
||||
if ($signature !== null)
|
||||
{
|
||||
$headers[] = 'X-MokoSuite-Signature: ' . $signature;
|
||||
$headers[] = 'X-MokoSuite-Timestamp: ' . $timestamp;
|
||||
}
|
||||
|
||||
$endpoint = $baseUrl . '/api/index.php/v1/mokosuitehq/heartbeat';
|
||||
$json = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
try
|
||||
{
|
||||
$http = \Joomla\CMS\Http\HttpFactory::getHttp(
|
||||
new \Joomla\Registry\Registry(['follow_location' => true, 'transport.curl' => ['certpath' => false]]),
|
||||
['curl', 'stream']
|
||||
);
|
||||
|
||||
$headerMap = [];
|
||||
foreach ($headers as $h)
|
||||
{
|
||||
[$key, $val] = explode(': ', $h, 2);
|
||||
$headerMap[$key] = $val;
|
||||
}
|
||||
|
||||
$response = $http->post($endpoint, $json, $headerMap, 15);
|
||||
$code = $response->code;
|
||||
$body = json_decode($response->body, true);
|
||||
|
||||
if ($code >= 200 && $code < 300)
|
||||
{
|
||||
$app->enqueueMessage(
|
||||
'MokoSuiteClientHQ heartbeat: ' . ($body['status'] ?? 'ok'),
|
||||
'message'
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::add(
|
||||
\sprintf('Monitor heartbeat HTTP %d: %s', $code, $body['error'] ?? 'Unknown'),
|
||||
Log::WARNING,
|
||||
'mokosuiteclient'
|
||||
);
|
||||
$app->enqueueMessage(
|
||||
'MokoSuiteClientHQ heartbeat failed (HTTP ' . $code . ')',
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
Log::add('Monitor heartbeat failed: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA-sign the request message.
|
||||
*
|
||||
* @param string $domain Site domain.
|
||||
* @param int $timestamp Unix timestamp.
|
||||
* @param string $token Health API token.
|
||||
*
|
||||
* @return string|null Base64-encoded signature, or null if signing fails.
|
||||
*/
|
||||
private function signRequest(string $domain, int $timestamp, string $token): ?string
|
||||
{
|
||||
$signingKeyB64 = $this->params->get('signing_key', '');
|
||||
|
||||
// Fall back to manifest XML default if not yet saved in params
|
||||
if (empty($signingKeyB64))
|
||||
{
|
||||
$manifestFile = JPATH_PLUGINS . '/system/mokosuiteclient_monitor/mokosuiteclient_monitor.xml';
|
||||
|
||||
if (is_file($manifestFile))
|
||||
{
|
||||
$xml = simplexml_load_file($manifestFile);
|
||||
|
||||
if ($xml)
|
||||
{
|
||||
foreach ($xml->xpath('//field[@name="signing_key"]') as $field)
|
||||
{
|
||||
$signingKeyB64 = (string) $field['default'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($signingKeyB64))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$privateKeyPem = base64_decode($signingKeyB64);
|
||||
|
||||
if (empty($privateKeyPem))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$message = $domain . '|' . $timestamp . '|' . $token;
|
||||
$privateKey = openssl_pkey_get_private($privateKeyPem);
|
||||
|
||||
if ($privateKey === false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$signature = '';
|
||||
|
||||
if (openssl_sign($message, $signature, $privateKey, OPENSSL_ALGO_SHA256))
|
||||
{
|
||||
return base64_encode($signature);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch health data from the local site's health endpoint.
|
||||
*/
|
||||
private function fetchLocalHealth(string $siteUrl, string $healthToken): ?array
|
||||
{
|
||||
try
|
||||
{
|
||||
$http = \Joomla\CMS\Http\HttpFactory::getHttp(
|
||||
new \Joomla\Registry\Registry(['follow_location' => true, 'transport.curl' => ['certpath' => false]]),
|
||||
['curl', 'stream']
|
||||
);
|
||||
|
||||
$response = $http->get(
|
||||
$siteUrl . '/?mokosuiteclient=health',
|
||||
['Authorization' => 'Bearer ' . $healthToken, 'Accept' => 'application/json'],
|
||||
10
|
||||
);
|
||||
|
||||
if ($response->code !== 200 || empty($response->body))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return json_decode($response->body, true) ?: null;
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the installed MokoSuiteClient package version.
|
||||
*/
|
||||
private function getMokoSuiteClientVersion(): string
|
||||
{
|
||||
try
|
||||
{
|
||||
$extension = new \Joomla\CMS\Table\Extension(Factory::getDbo());
|
||||
$extension->load(['element' => 'pkg_mokosuiteclient', 'type' => 'package']);
|
||||
$manifest = json_decode($extension->manifest_cache ?? '{}');
|
||||
|
||||
return $manifest->version ?? '';
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user