getIdentity(); if (!$user->authorise('core.manage', 'com_mokosuite')) { $this->sendJson(403, ['error' => 'Not authorized']); return; } if ($app->input->getMethod() !== 'POST') { $this->sendJson(405, ['error' => 'POST required']); return; } $db = Factory::getDbo(); $results = []; // 1. Reset article hit counters try { $db->setQuery( $db->getQuery(true) ->update($db->quoteName('#__content')) ->set($db->quoteName('hits') . ' = 0') )->execute(); $results['hits_reset'] = $db->getAffectedRows(); } catch (\Throwable $e) { $results['hits_reset'] = 'error: ' . $e->getMessage(); } // 2. Delete content version history try { $db->setQuery( $db->getQuery(true)->delete($db->quoteName('#__history')) )->execute(); $results['versions_deleted'] = $db->getAffectedRows(); } catch (\Throwable $e) { $results['versions_deleted'] = 'error: ' . $e->getMessage(); } // 3. Regenerate heartbeat token if requested $input = $app->getInput()->json; $resetToken = (bool) ($input->get('reset_token', false, 'BOOLEAN')); if ($resetToken) { try { $newToken = bin2hex(random_bytes(32)); $plugin = \Joomla\CMS\Plugin\PluginHelper::getPlugin('system', 'mokosuite'); if ($plugin) { $pluginParams = new \Joomla\Registry\Registry($plugin->params); $pluginParams->set('health_api_token', $newToken); $db->setQuery( $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('params') . ' = ' . $db->quote($pluginParams->toString())) ->where($db->quoteName('element') . ' = ' . $db->quote('mokosuite')) ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) )->execute(); $results['token_regenerated'] = true; $results['new_token'] = $newToken; } } catch (\Throwable $e) { $results['token_regenerated'] = 'error: ' . $e->getMessage(); } } // 4. Reset all user API tokens if requested $resetApiTokens = (bool) ($input->get('reset_api_tokens', false, 'BOOLEAN')); if ($resetApiTokens) { try { // Get users who have API tokens before deleting $db->setQuery( $db->getQuery(true) ->select('DISTINCT ' . $db->quoteName('user_id')) ->from($db->quoteName('#__user_keys')) ->where($db->quoteName('series') . ' LIKE ' . $db->quote('api-%')) ); $affectedUserIds = $db->loadColumn() ?: []; $db->setQuery( $db->getQuery(true)->delete($db->quoteName('#__user_keys')) ->where($db->quoteName('series') . ' LIKE ' . $db->quote('api-%')) )->execute(); $results['api_tokens_revoked'] = $db->getAffectedRows(); // Notify affected users if (!empty($affectedUserIds)) { $this->notifyTokenReset($db, $affectedUserIds); $results['users_notified'] = \count($affectedUserIds); } } catch (\Throwable $e) { $results['api_tokens_revoked'] = 'error: ' . $e->getMessage(); } } // 5. Flag site for fresh client info setup try { // Write a flag file that the core plugin checks on next admin load $flagFile = JPATH_ADMINISTRATOR . '/cache/mokosuite_setup_required.flag'; file_put_contents($flagFile, json_encode([ 'created' => gmdate('Y-m-d\TH:i:s\Z'), 'reason' => 'provision-reset', 'remote_ip' => $_SERVER['REMOTE_ADDR'] ?? '', ])); $results['setup_flag'] = true; } catch (\Throwable $e) { $results['setup_flag'] = 'error: ' . $e->getMessage(); } $this->sendJson(200, [ 'status' => 'ok', 'message' => 'Site provisioned for new client.', 'results' => $results, ]); } /** * Notify users that their API tokens have been revoked. */ private function notifyTokenReset($db, array $userIds): void { try { $db->setQuery( $db->getQuery(true) ->select([$db->quoteName('name'), $db->quoteName('email')]) ->from($db->quoteName('#__users')) ->whereIn($db->quoteName('id'), $userIds) ->where($db->quoteName('block') . ' = 0') ); $users = $db->loadObjectList() ?: []; $config = Factory::getConfig(); $siteName = $config->get('sitename', 'Joomla'); $siteUrl = rtrim(\Joomla\CMS\Uri\Uri::root(), '/'); $mailer = Factory::getMailer(); foreach ($users as $u) { try { $mailer->clearAllRecipients(); $mailer->addRecipient($u->email, $u->name); $mailer->setSubject($siteName . ' — API tokens have been reset'); $mailer->setBody( "Hello {$u->name},\n\n" . "Your API access tokens on {$siteName} have been revoked by an administrator.\n\n" . "If you use API integrations, please log in and generate a new token:\n" . "{$siteUrl}/administrator/\n\n" . "— {$siteName}" ); $mailer->send(); } catch (\Throwable $e) { // Non-critical } } } catch (\Throwable $e) { // Non-critical } } private function sendJson(int $code, array $data): void { http_response_code($code); header('Content-Type: application/json; charset=utf-8'); echo json_encode($data, JSON_UNESCAPED_SLASHES); Factory::getApplication()->close(); } }