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:
Jonathan Miller
2026-05-22 22:43:41 -05:00
parent 21020027d0
commit 17eaaf2347
+222 -1
View File
@@ -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.
*