feat: add Akeeba Backup and Admin Tools checks to health endpoint
New health checks: - backup: last backup date/status/size, days since, total count, 7d count - security: Admin Tools WAF status, blocked requests 24h/7d Degraded reasons: - No backups found - Last backup older than 7 days - Last backup failed/incomplete Dashboard updated with Backup and Security rows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+222
-1
@@ -1126,6 +1126,29 @@ class MokoWaaS extends CMSPlugin
|
||||
$reasons[] = 'Low disk space: '
|
||||
. $check['free_disk_mb'] . ' MB free';
|
||||
}
|
||||
elseif ($name === 'backup')
|
||||
{
|
||||
if (!empty($check['message']))
|
||||
{
|
||||
$reasons[] = $check['message'];
|
||||
}
|
||||
elseif (isset($check['days_since'])
|
||||
&& $check['days_since'] > 7)
|
||||
{
|
||||
$reasons[] = 'Last backup '
|
||||
. $check['days_since'] . ' days ago';
|
||||
}
|
||||
elseif (isset($check['last_status'])
|
||||
&& $check['last_status'] !== 'complete')
|
||||
{
|
||||
$reasons[] = 'Last backup status: '
|
||||
. $check['last_status'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$reasons[] = 'Backup: degraded';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$reasons[] = $name . ': degraded';
|
||||
@@ -1156,12 +1179,16 @@ class MokoWaaS extends CMSPlugin
|
||||
*/
|
||||
protected function collectHealthChecks()
|
||||
{
|
||||
return [
|
||||
$checks = [
|
||||
'database' => $this->checkDatabase(),
|
||||
'filesystem' => $this->checkFilesystem(),
|
||||
'cache' => $this->checkCache(),
|
||||
'extensions' => $this->checkExtensions(),
|
||||
'backup' => $this->checkAkeebaBackup(),
|
||||
'security' => $this->checkAdminTools(),
|
||||
];
|
||||
|
||||
return $checks;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1347,6 +1374,200 @@ class MokoWaaS extends CMSPlugin
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Akeeba Backup status — last backup date, status, and profile.
|
||||
*
|
||||
* Queries the #__ak_stats table (Akeeba Backup) for the most recent
|
||||
* backup record. Returns 'not_installed' if the table doesn't exist.
|
||||
*
|
||||
* @return array Check result with backup info
|
||||
*
|
||||
* @since 02.01.39
|
||||
*/
|
||||
protected function checkAkeebaBackup()
|
||||
{
|
||||
try
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Check if Akeeba Backup is installed
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
$akTable = $prefix . 'ak_stats';
|
||||
|
||||
if (!in_array($akTable, $tables))
|
||||
{
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'installed' => false,
|
||||
];
|
||||
}
|
||||
|
||||
// Get the most recent backup
|
||||
$query = $db->getQuery(true)
|
||||
->select([
|
||||
$db->quoteName('id'),
|
||||
$db->quoteName('description'),
|
||||
$db->quoteName('status'),
|
||||
$db->quoteName('backupstart'),
|
||||
$db->quoteName('backupend'),
|
||||
$db->quoteName('profile_id'),
|
||||
$db->quoteName('total_size'),
|
||||
])
|
||||
->from($db->quoteName('#__ak_stats'))
|
||||
->order($db->quoteName('id') . ' DESC');
|
||||
|
||||
$db->setQuery($query, 0, 1);
|
||||
$latest = $db->loadObject();
|
||||
|
||||
if (!$latest)
|
||||
{
|
||||
return [
|
||||
'status' => 'degraded',
|
||||
'installed' => true,
|
||||
'message' => 'No backups found',
|
||||
];
|
||||
}
|
||||
|
||||
// Count total backups and recent (last 7 days)
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from($db->quoteName('#__ak_stats'))
|
||||
);
|
||||
$totalBackups = (int) $db->loadResult();
|
||||
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from($db->quoteName('#__ak_stats'))
|
||||
->where($db->quoteName('backupstart')
|
||||
. ' >= DATE_SUB(NOW(), INTERVAL 7 DAY)')
|
||||
);
|
||||
$recentBackups = (int) $db->loadResult();
|
||||
|
||||
// Check if last backup is older than 7 days
|
||||
$lastDate = $latest->backupstart;
|
||||
$daysSince = (int) ((time() - strtotime($lastDate)) / 86400);
|
||||
$backupSize = $latest->total_size
|
||||
? round($latest->total_size / 1048576)
|
||||
: null;
|
||||
|
||||
$status = 'ok';
|
||||
|
||||
if ($latest->status !== 'complete')
|
||||
{
|
||||
$status = 'degraded';
|
||||
}
|
||||
elseif ($daysSince > 7)
|
||||
{
|
||||
$status = 'degraded';
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => $status,
|
||||
'installed' => true,
|
||||
'last_backup' => $lastDate,
|
||||
'last_status' => $latest->status,
|
||||
'last_size_mb' => $backupSize,
|
||||
'days_since' => $daysSince,
|
||||
'profile_id' => (int) $latest->profile_id,
|
||||
'total_backups' => $totalBackups,
|
||||
'recent_7d' => $recentBackups,
|
||||
'description' => $latest->description,
|
||||
];
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'installed' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Admin Tools status — WAF status, security exceptions.
|
||||
*
|
||||
* Queries Admin Tools tables for firewall status and recent blocks.
|
||||
* Returns 'not_installed' if tables don't exist.
|
||||
*
|
||||
* @return array Check result with security info
|
||||
*
|
||||
* @since 02.01.39
|
||||
*/
|
||||
protected function checkAdminTools()
|
||||
{
|
||||
try
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
|
||||
// Check if Admin Tools is installed
|
||||
$atTable = $prefix . 'admintools_log';
|
||||
|
||||
if (!in_array($atTable, $tables))
|
||||
{
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'installed' => false,
|
||||
];
|
||||
}
|
||||
|
||||
// Count blocked requests in last 24h
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from($db->quoteName('#__admintools_log'))
|
||||
->where($db->quoteName('logdate')
|
||||
. ' >= DATE_SUB(NOW(), INTERVAL 1 DAY)')
|
||||
);
|
||||
$blocked24h = (int) $db->loadResult();
|
||||
|
||||
// Count blocked in last 7 days
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from($db->quoteName('#__admintools_log'))
|
||||
->where($db->quoteName('logdate')
|
||||
. ' >= DATE_SUB(NOW(), INTERVAL 7 DAY)')
|
||||
);
|
||||
$blocked7d = (int) $db->loadResult();
|
||||
|
||||
// Check WAF config if available
|
||||
$wafEnabled = null;
|
||||
$wafTable = $prefix . 'admintools_wafconfig';
|
||||
|
||||
if (in_array($wafTable, $tables))
|
||||
{
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->select($db->quoteName('value'))
|
||||
->from($db->quoteName('#__admintools_wafconfig'))
|
||||
->where($db->quoteName('key') . ' = '
|
||||
. $db->quote('ipworkarounds'))
|
||||
);
|
||||
$wafEnabled = $db->loadResult() !== null;
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'installed' => true,
|
||||
'blocked_24h' => $blocked24h,
|
||||
'blocked_7d' => $blocked7d,
|
||||
'waf_active' => $wafEnabled,
|
||||
];
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'installed' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a JSON health response and terminate execution.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user