4b9a675d0f
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Project CI / Lint & Validate (push) Successful in 36s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 40s
Generic: Project CI / Tests (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
All Joomla element names, PHP classes, language files, folder structure, and manifest references renamed from mokosuite to mokosuiteclient. This repo is now the client-facing tracker for the MokoSuite platform.
252 lines
5.6 KiB
PHP
252 lines
5.6 KiB
PHP
<?php
|
|
namespace Moko\Component\MokoSuiteClient\Administrator\Model;
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Factory;
|
|
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
|
|
|
|
class MaintenanceModel extends BaseDatabaseModel
|
|
{
|
|
/**
|
|
* Get database table status (size, rows, engine, overhead).
|
|
*/
|
|
public function getTableStatus(): array
|
|
{
|
|
$db = $this->getDatabase();
|
|
$prefix = $db->getPrefix();
|
|
|
|
$db->setQuery('SHOW TABLE STATUS');
|
|
$tables = $db->loadObjectList() ?: [];
|
|
|
|
$results = [];
|
|
$totalSize = 0;
|
|
$totalOverhead = 0;
|
|
|
|
foreach ($tables as $t)
|
|
{
|
|
$sizeMb = round(($t->Data_length + $t->Index_length) / 1048576, 2);
|
|
$overheadKb = round(($t->Data_free ?? 0) / 1024, 1);
|
|
$totalSize += $sizeMb;
|
|
$totalOverhead += $overheadKb;
|
|
|
|
$results[] = (object) [
|
|
'name' => $t->Name,
|
|
'rows' => (int) $t->Rows,
|
|
'engine' => $t->Engine,
|
|
'size_mb' => $sizeMb,
|
|
'overhead_kb' => $overheadKb,
|
|
'is_moko' => str_contains($t->Name, 'mokosuiteclient'),
|
|
];
|
|
}
|
|
|
|
usort($results, fn($a, $b) => $b->size_mb <=> $a->size_mb);
|
|
|
|
return ['tables' => $results, 'total_size_mb' => round($totalSize, 2), 'total_overhead_kb' => round($totalOverhead, 1), 'count' => \count($results)];
|
|
}
|
|
|
|
/**
|
|
* Optimize all tables or specific ones.
|
|
*/
|
|
public function optimizeTables(array $tableNames = []): array
|
|
{
|
|
$db = $this->getDatabase();
|
|
$count = 0;
|
|
|
|
try
|
|
{
|
|
if (empty($tableNames))
|
|
{
|
|
$db->setQuery('SHOW TABLE STATUS WHERE Data_free > 0');
|
|
$tables = $db->loadObjectList() ?: [];
|
|
$tableNames = array_column($tables, 'Name');
|
|
}
|
|
|
|
foreach ($tableNames as $name)
|
|
{
|
|
$db->setQuery('OPTIMIZE TABLE ' . $db->quoteName($name));
|
|
$db->execute();
|
|
$count++;
|
|
}
|
|
|
|
return ['success' => true, 'message' => "Optimized {$count} tables."];
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
return ['success' => false, 'message' => 'Optimize failed: ' . $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Repair all tables.
|
|
*/
|
|
public function repairTables(): array
|
|
{
|
|
$db = $this->getDatabase();
|
|
|
|
try
|
|
{
|
|
$db->setQuery('SHOW TABLE STATUS');
|
|
$tables = $db->loadObjectList() ?: [];
|
|
$count = 0;
|
|
|
|
foreach ($tables as $t)
|
|
{
|
|
if ($t->Engine === 'InnoDB' || $t->Engine === 'MyISAM')
|
|
{
|
|
$db->setQuery('REPAIR TABLE ' . $db->quoteName($t->Name));
|
|
$db->execute();
|
|
$count++;
|
|
}
|
|
}
|
|
|
|
return ['success' => true, 'message' => "Repaired {$count} tables."];
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
return ['success' => false, 'message' => 'Repair failed: ' . $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Purge expired sessions.
|
|
*/
|
|
public function purgeSessions(): array
|
|
{
|
|
try
|
|
{
|
|
$db = $this->getDatabase();
|
|
$db->setQuery(
|
|
$db->getQuery(true)
|
|
->delete($db->quoteName('#__session'))
|
|
->where($db->quoteName('time') . ' < ' . (time() - 86400))
|
|
)->execute();
|
|
|
|
return ['success' => true, 'message' => 'Expired sessions purged. ' . $db->getAffectedRows() . ' removed.'];
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
return ['success' => false, 'message' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
// ==================================================================
|
|
// Temp/Cache Cleanup (#128)
|
|
// ==================================================================
|
|
|
|
/**
|
|
* Get directory sizes for cleanup.
|
|
*/
|
|
public function getCleanupInfo(): array
|
|
{
|
|
$dirs = [
|
|
['path' => JPATH_ROOT . '/cache', 'label' => 'Site Cache'],
|
|
['path' => JPATH_ADMINISTRATOR . '/cache', 'label' => 'Admin Cache'],
|
|
['path' => JPATH_ROOT . '/tmp', 'label' => 'Temp Directory'],
|
|
['path' => JPATH_ADMINISTRATOR . '/logs', 'label' => 'Log Files'],
|
|
];
|
|
|
|
$results = [];
|
|
|
|
foreach ($dirs as $dir)
|
|
{
|
|
$size = 0;
|
|
$files = 0;
|
|
|
|
if (is_dir($dir['path']))
|
|
{
|
|
$iterator = new \RecursiveIteratorIterator(
|
|
new \RecursiveDirectoryIterator($dir['path'], \RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $file)
|
|
{
|
|
if ($file->isFile())
|
|
{
|
|
$size += $file->getSize();
|
|
$files++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$results[] = (object) [
|
|
'label' => $dir['label'],
|
|
'path' => $dir['path'],
|
|
'size_mb' => round($size / 1048576, 2),
|
|
'files' => $files,
|
|
'writable' => is_writable($dir['path']),
|
|
];
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Clean a specific directory.
|
|
*/
|
|
public function cleanDirectory(string $dirKey): array
|
|
{
|
|
$allowed = [
|
|
'site_cache' => JPATH_ROOT . '/cache',
|
|
'admin_cache' => JPATH_ADMINISTRATOR . '/cache',
|
|
'tmp' => JPATH_ROOT . '/tmp',
|
|
'logs' => JPATH_ADMINISTRATOR . '/logs',
|
|
];
|
|
|
|
if (!isset($allowed[$dirKey]))
|
|
{
|
|
return ['success' => false, 'message' => 'Invalid directory.'];
|
|
}
|
|
|
|
$dir = $allowed[$dirKey];
|
|
|
|
if (!is_dir($dir))
|
|
{
|
|
return ['success' => false, 'message' => 'Directory not found.'];
|
|
}
|
|
|
|
$count = 0;
|
|
|
|
try
|
|
{
|
|
$iterator = new \RecursiveIteratorIterator(
|
|
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
|
\RecursiveIteratorIterator::CHILD_FIRST
|
|
);
|
|
|
|
foreach ($iterator as $item)
|
|
{
|
|
// Keep index.html and .htaccess files
|
|
$name = $item->getFilename();
|
|
|
|
if ($name === 'index.html' || $name === '.htaccess')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ($item->isDir())
|
|
{
|
|
@rmdir($item->getPathname());
|
|
}
|
|
else
|
|
{
|
|
@unlink($item->getPathname());
|
|
$count++;
|
|
}
|
|
}
|
|
|
|
// Also clear opcache
|
|
if (\function_exists('opcache_reset'))
|
|
{
|
|
\opcache_reset();
|
|
}
|
|
|
|
return ['success' => true, 'message' => "Cleaned {$count} files from {$dirKey}."];
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
return ['success' => false, 'message' => 'Cleanup failed: ' . $e->getMessage()];
|
|
}
|
|
}
|
|
}
|