feat: add BackupStatusHelper for bridge integration (#47)
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Auto Version Bump / Version Bump (push) Successful in 15s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 13s
Generic: Project CI / Lint & Validate (pull_request) Successful in 32s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 35s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Auto Version Bump / Version Bump (push) Successful in 15s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 13s
Generic: Project CI / Lint & Validate (pull_request) Successful in 32s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 35s
Static helper class for external consumers (bridge plugins, MCP servers) to query backup status without bootstrapping the full component. Methods: - isInstalled(): check if MokoSuiteBackup is installed and enabled - getLatestRecord(): get the most recent completed/failed backup - getStatusSummary(): full heartbeat payload with latest status, all-time/7-day totals, and consecutive success streak
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package MokoSuiteBackup
|
||||
* @subpackage com_mokosuitebackup
|
||||
* @author Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\MokoSuiteBackup\Administrator\Helper;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
|
||||
/**
|
||||
* Lightweight helper for external consumers (bridge plugins, MCP servers, CLI tools)
|
||||
* to query MokoSuiteBackup status without bootstrapping the full component.
|
||||
*
|
||||
* Usage from any Joomla plugin:
|
||||
* \Joomla\Component\MokoSuiteBackup\Administrator\Helper\BackupStatusHelper::getStatusSummary()
|
||||
*/
|
||||
class BackupStatusHelper
|
||||
{
|
||||
/**
|
||||
* Check whether MokoSuiteBackup is installed and enabled.
|
||||
*/
|
||||
public static function isInstalled(): bool
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('pkg_mokosuitebackup'))
|
||||
->where($db->quoteName('enabled') . ' = 1');
|
||||
|
||||
return (int) $db->setQuery($query)->loadResult() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest backup record for a given profile (or any profile).
|
||||
*
|
||||
* @param int|null $profileId Limit to a specific profile, or null for any.
|
||||
* @return object|null Record object or null if no backups exist.
|
||||
*/
|
||||
public static function getLatestRecord(?int $profileId = null): ?object
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$query = $db->getQuery(true)
|
||||
->select([
|
||||
$db->quoteName('r.id'),
|
||||
$db->quoteName('r.profile_id'),
|
||||
$db->quoteName('r.description'),
|
||||
$db->quoteName('r.status'),
|
||||
$db->quoteName('r.origin'),
|
||||
$db->quoteName('r.backup_type'),
|
||||
$db->quoteName('r.archivename'),
|
||||
$db->quoteName('r.total_size'),
|
||||
$db->quoteName('r.db_size'),
|
||||
$db->quoteName('r.files_count'),
|
||||
$db->quoteName('r.tables_count'),
|
||||
$db->quoteName('r.backupstart'),
|
||||
$db->quoteName('r.backupend'),
|
||||
$db->quoteName('r.filesexist'),
|
||||
$db->quoteName('r.remote_filename'),
|
||||
$db->quoteName('r.checksum'),
|
||||
$db->quoteName('p.title', 'profile_title'),
|
||||
])
|
||||
->from($db->quoteName('#__mokosuitebackup_records', 'r'))
|
||||
->join('LEFT', $db->quoteName('#__mokosuitebackup_profiles', 'p') . ' ON p.id = r.profile_id')
|
||||
->where($db->quoteName('r.status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'fail'])) . ')')
|
||||
->order($db->quoteName('r.backupstart') . ' DESC');
|
||||
|
||||
if ($profileId !== null) {
|
||||
$query->where($db->quoteName('r.profile_id') . ' = ' . (int) $profileId);
|
||||
}
|
||||
|
||||
$query->setLimit(1);
|
||||
$record = $db->setQuery($query)->loadObject();
|
||||
|
||||
return $record ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full status summary for heartbeat payloads.
|
||||
*
|
||||
* Returns an array suitable for JSON encoding in bridge heartbeats:
|
||||
* - latest backup status, time, size, destination
|
||||
* - total and recent (7-day) backup counts
|
||||
* - streak of consecutive successes
|
||||
*
|
||||
* @return array{installed: bool, latest: ?array, totals: array}
|
||||
*/
|
||||
public static function getStatusSummary(): array
|
||||
{
|
||||
if (!self::isInstalled()) {
|
||||
return ['installed' => false, 'latest' => null, 'totals' => []];
|
||||
}
|
||||
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
|
||||
// Latest completed/failed backup
|
||||
$latest = self::getLatestRecord();
|
||||
$latestArray = null;
|
||||
|
||||
if ($latest) {
|
||||
$latestArray = [
|
||||
'status' => $latest->status,
|
||||
'backup_type' => $latest->backup_type,
|
||||
'description' => $latest->description,
|
||||
'backup_start' => $latest->backupstart,
|
||||
'backup_end' => $latest->backupend,
|
||||
'total_size' => (int) $latest->total_size,
|
||||
'destination' => $latest->remote_filename ? 'remote' : 'local',
|
||||
'profile' => $latest->profile_title,
|
||||
'origin' => $latest->origin,
|
||||
'files_count' => (int) $latest->files_count,
|
||||
'tables_count' => (int) $latest->tables_count,
|
||||
];
|
||||
}
|
||||
|
||||
// Totals
|
||||
$query = $db->getQuery(true)
|
||||
->select([
|
||||
'COUNT(*) AS total',
|
||||
'SUM(CASE WHEN ' . $db->quoteName('status') . ' = ' . $db->quote('complete') . ' THEN 1 ELSE 0 END) AS success',
|
||||
'SUM(CASE WHEN ' . $db->quoteName('status') . ' = ' . $db->quote('fail') . ' THEN 1 ELSE 0 END) AS failed',
|
||||
])
|
||||
->from($db->quoteName('#__mokosuitebackup_records'));
|
||||
|
||||
$allTime = $db->setQuery($query)->loadObject();
|
||||
|
||||
// Recent (last 7 days)
|
||||
$query = $db->getQuery(true)
|
||||
->select([
|
||||
'COUNT(*) AS total',
|
||||
'SUM(CASE WHEN ' . $db->quoteName('status') . ' = ' . $db->quote('complete') . ' THEN 1 ELSE 0 END) AS success',
|
||||
'SUM(CASE WHEN ' . $db->quoteName('status') . ' = ' . $db->quote('fail') . ' THEN 1 ELSE 0 END) AS failed',
|
||||
])
|
||||
->from($db->quoteName('#__mokosuitebackup_records'))
|
||||
->where($db->quoteName('backupstart') . ' >= DATE_SUB(NOW(), INTERVAL 7 DAY)');
|
||||
|
||||
$recent = $db->setQuery($query)->loadObject();
|
||||
|
||||
// Success streak — count consecutive successes from latest backward
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('status'))
|
||||
->from($db->quoteName('#__mokosuitebackup_records'))
|
||||
->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'fail'])) . ')')
|
||||
->order($db->quoteName('backupstart') . ' DESC')
|
||||
->setLimit(50);
|
||||
|
||||
$statuses = $db->setQuery($query)->loadColumn();
|
||||
$streak = 0;
|
||||
|
||||
foreach ($statuses as $s) {
|
||||
if ($s === 'complete') {
|
||||
$streak++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'installed' => true,
|
||||
'latest' => $latestArray,
|
||||
'totals' => [
|
||||
'all_time' => (int) ($allTime->total ?? 0),
|
||||
'all_success' => (int) ($allTime->success ?? 0),
|
||||
'all_failed' => (int) ($allTime->failed ?? 0),
|
||||
'recent_total' => (int) ($recent->total ?? 0),
|
||||
'recent_success' => (int) ($recent->success ?? 0),
|
||||
'recent_failed' => (int) ($recent->failed ?? 0),
|
||||
'success_streak' => $streak,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user