feat: HQ config sync, menu language fix, catalog cleanup
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 25s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 54s
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
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 25s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 54s
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
Client stores HQ-configured support_pin_hours from heartbeat response. PIN TTL now configurable from HQ instead of hardcoded 72h. Admin sidebar menu loads component-local language files fixing untranslated keys for MokoSuiteCross. Removed MokoSuiteHQ from extension catalog.
This commit is contained in:
@@ -28,6 +28,12 @@
|
||||
- **Frontend link in status bar** — cache/temp module now has 4 buttons: Site (frontend link), PIN, Cache, Temp
|
||||
- **Help buttons** — all admin views link to Gitea wiki pages via toolbar help button
|
||||
- **Support PIN in heartbeat** — core system plugin includes current PIN in heartbeat payload to HQ
|
||||
- **HQ config sync** — client stores HQ-configured `support_pin_hours` from heartbeat response, PIN TTL now configurable from HQ
|
||||
|
||||
### Changed
|
||||
- Admin sidebar menu module now loads component-local language files (fixes untranslated keys for MokoSuiteCross and other components)
|
||||
- Support PIN TTL is now configurable via HQ global options instead of hardcoded 72 hours
|
||||
- Removed MokoSuiteHQ from extension catalog (internal app, not for client sites)
|
||||
- **SupportPinHelper** — shared helper centralises PIN generation across dashboard, cpanel module, cache module, and AJAX controller
|
||||
- **Current IP display** — firewall plugin settings show admin's IP with copy button
|
||||
- **Heartbeat monitor** — consolidated into core plugin from retired monitor plugin, with diagnostic logging on all bail-out points
|
||||
|
||||
@@ -20,15 +20,6 @@
|
||||
<protected>true</protected>
|
||||
<updateserver>https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient/raw/branch/main/updates.xml</updateserver>
|
||||
</extension>
|
||||
<extension>
|
||||
<name>MokoSuiteHQ</name>
|
||||
<element>pkg_mokosuitehq</element>
|
||||
<type>package</type>
|
||||
<description>Centralized control panel for managing all MokoSuite client installations.</description>
|
||||
<icon>icon-tachometer-alt</icon>
|
||||
<category>Platform</category>
|
||||
<updateserver>https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteHQ/raw/branch/main/updates.xml</updateserver>
|
||||
</extension>
|
||||
<extension>
|
||||
<name>MokoSuiteBackup</name>
|
||||
<element>pkg_mokosuitebackup</element>
|
||||
|
||||
@@ -23,8 +23,8 @@ use Joomla\Database\DatabaseInterface;
|
||||
*/
|
||||
class SupportPinHelper
|
||||
{
|
||||
/** @var int PIN validity window in seconds (72 hours) */
|
||||
public const PIN_TTL = 72 * 3600;
|
||||
/** @var int Default PIN validity window in seconds (72 hours) */
|
||||
public const PIN_TTL_DEFAULT = 72 * 3600;
|
||||
|
||||
/**
|
||||
* Load core plugin params and return PIN state.
|
||||
@@ -74,11 +74,12 @@ class SupportPinHelper
|
||||
|
||||
$result['available'] = true;
|
||||
|
||||
$pinTtl = (int) ($params['support_pin_hours'] ?? 0) * 3600 ?: self::PIN_TTL_DEFAULT;
|
||||
$requestedAt = (int) ($params['support_pin_requested_at'] ?? 0);
|
||||
|
||||
if ($requestedAt && (time() - $requestedAt) < self::PIN_TTL)
|
||||
if ($requestedAt && (time() - $requestedAt) < $pinTtl)
|
||||
{
|
||||
$result['pin'] = self::generate($token, $requestedAt);
|
||||
$result['pin'] = self::generate($token, $requestedAt, $pinTtl);
|
||||
}
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
@@ -94,12 +95,14 @@ class SupportPinHelper
|
||||
*
|
||||
* @param string $token Health API token (HMAC key).
|
||||
* @param int $timestamp The request timestamp.
|
||||
* @param int $ttl PIN validity window in seconds.
|
||||
*
|
||||
* @return string e.g. "MOKO-A1B2-C3D4"
|
||||
*/
|
||||
public static function generate(string $token, int $timestamp): string
|
||||
public static function generate(string $token, int $timestamp, int $ttl = 0): string
|
||||
{
|
||||
$window = floor($timestamp / self::PIN_TTL);
|
||||
$ttl = $ttl ?: self::PIN_TTL_DEFAULT;
|
||||
$window = floor($timestamp / $ttl);
|
||||
$hash = hash_hmac('sha256', (string) $window, $token);
|
||||
|
||||
return 'MOKO-' . strtoupper(substr($hash, 0, 4)) . '-' . strtoupper(substr($hash, 4, 4));
|
||||
@@ -121,7 +124,7 @@ class SupportPinHelper
|
||||
return ['success' => false, 'message' => 'Health token not configured.'];
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$now = time();
|
||||
$params = $state['params'];
|
||||
$params['support_pin_requested_at'] = $now;
|
||||
|
||||
@@ -132,8 +135,10 @@ class SupportPinHelper
|
||||
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
$pin = self::generate($state['token'], $now);
|
||||
$pinHours = (int) ($params['support_pin_hours'] ?? 0) ?: (int) (self::PIN_TTL_DEFAULT / 3600);
|
||||
$pinTtl = $pinHours * 3600;
|
||||
$pin = self::generate($state['token'], $now, $pinTtl);
|
||||
|
||||
return ['success' => true, 'pin' => $pin, 'message' => 'PIN generated — valid for 72 hours.'];
|
||||
return ['success' => true, 'pin' => $pin, 'message' => 'PIN generated — valid for ' . $pinHours . ' hours.'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,15 @@ try
|
||||
{
|
||||
$lang->load($m->element . '.sys', JPATH_ADMINISTRATOR);
|
||||
$lang->load($m->element, JPATH_ADMINISTRATOR);
|
||||
|
||||
// Also try component-local language path (Joomla 5/6 pattern)
|
||||
$compLangPath = JPATH_ADMINISTRATOR . '/components/' . $m->element;
|
||||
if (is_dir($compLangPath . '/language'))
|
||||
{
|
||||
$lang->load($m->element . '.sys', $compLangPath);
|
||||
$lang->load($m->element, $compLangPath);
|
||||
}
|
||||
|
||||
$loadedLangs[$m->element] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2743,6 +2743,13 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
|
||||
|
||||
if ($response->code >= 200 && $response->code < 300)
|
||||
{
|
||||
$body = json_decode($response->body, true);
|
||||
|
||||
if (!empty($body['config']['support_pin_hours']))
|
||||
{
|
||||
$this->syncHqConfig('support_pin_hours', (int) $body['config']['support_pin_hours']);
|
||||
}
|
||||
|
||||
$this->app->enqueueMessage('MokoSuiteHQ heartbeat: site registered successfully.', 'message');
|
||||
}
|
||||
else
|
||||
@@ -2763,6 +2770,47 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function syncHqConfig(string $key, $value): void
|
||||
{
|
||||
try
|
||||
{
|
||||
$db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class);
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName(['extension_id', 'params']))
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokosuiteclient'))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'));
|
||||
|
||||
$ext = $db->setQuery($query)->loadObject();
|
||||
|
||||
if (!$ext)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$params = json_decode($ext->params, true) ?: [];
|
||||
|
||||
if (($params[$key] ?? null) === $value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$params[$key] = $value;
|
||||
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('params') . ' = ' . $db->quote(json_encode($params)))
|
||||
->where($db->quoteName('extension_id') . ' = ' . (int) $ext->extension_id)
|
||||
)->execute();
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
// Config sync is non-critical
|
||||
}
|
||||
}
|
||||
|
||||
private function signHeartbeatRequest(string $domain, int $timestamp, string $token): ?string
|
||||
{
|
||||
$signingKeyB64 = $this->params->get('monitor_signing_key', '');
|
||||
|
||||
Reference in New Issue
Block a user