diff --git a/README.md b/README.md index a365908..ac09c41 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,6 @@ Szkielet komponentu Joomla 6 do gromadzenia pytań testowych. ## Instalacja -W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.43.6). +W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.43.7). Po instalacji komponent jest dostępny w panelu administratora jako **Pytania testowe**. diff --git a/administrator/components/com_testquestions/language/en-GB/com_testquestions.ini b/administrator/components/com_testquestions/language/en-GB/com_testquestions.ini index 18c7a6f..d7c5bc0 100644 --- a/administrator/components/com_testquestions/language/en-GB/com_testquestions.ini +++ b/administrator/components/com_testquestions/language/en-GB/com_testquestions.ini @@ -70,6 +70,18 @@ COM_TESTQUESTIONS_AI_GROK_KEY="Klucz API Grok" COM_TESTQUESTIONS_AI_OPENAI_MODEL="Model OpenAI" COM_TESTQUESTIONS_AI_GROK_MODEL="Model Grok" COM_TESTQUESTIONS_AI_MODEL_INVALID="Wybierz prawidłowy model dla każdego dostawcy AI." +COM_TESTQUESTIONS_AI_BALANCE="Saldo kont API" +COM_TESTQUESTIONS_AI_OPENAI_BALANCE_INFO="OpenAI nie udostępnia salda przedpłaconych środków przez zwykłe API. Saldo sprawdzisz w panelu rozliczeń: " +COM_TESTQUESTIONS_AI_OPENAI_BILLING_LINK="Otwórz rozliczenia OpenAI" +COM_TESTQUESTIONS_AI_GROK_TEAM_ID="ID zespołu xAI" +COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY="Klucz zarządzający xAI" +COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_SAVED="Klucz zarządzający jest zapisany. Pozostaw pole bez zmian, aby go zachować." +COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_INFO="Wymagany jest osobny Management API key z xAI Console → Settings → Management Keys." +COM_TESTQUESTIONS_AI_CHECK_GROK_BALANCE="Sprawdź saldo xAI" +COM_TESTQUESTIONS_AI_GROK_BALANCE_INFO="Odczyt dotyczy dostępnych kredytów przedpłaconych zespołu xAI. Nie obejmuje limitu rozliczeń postpaid." +COM_TESTQUESTIONS_AI_GROK_BALANCE_CONFIG_REQUIRED="Aby sprawdzić saldo xAI, wpisz ID zespołu i klucz zarządzający, a następnie ponów próbę." +COM_TESTQUESTIONS_AI_GROK_BALANCE_FAILED="Nie udało się odczytać salda xAI. Sprawdź ID zespołu, klucz zarządzający i jego uprawnienia." +COM_TESTQUESTIONS_AI_GROK_BALANCE_RESULT="Dostępne kredyty przedpłacone xAI: $%s USD" COM_TESTQUESTIONS_AI_SETTINGS_MENU="Konfiguracja AI" COM_TESTQUESTIONS_AI_SETTINGS_SAVED="Ustawienia AI zapisano." COM_TESTQUESTIONS_AI_SAVE_FAILED="Nie udało się zapisać ustawień AI." diff --git a/administrator/components/com_testquestions/sql/updates/mysql/1.43.7.sql b/administrator/components/com_testquestions/sql/updates/mysql/1.43.7.sql new file mode 100644 index 0000000..2d1060c --- /dev/null +++ b/administrator/components/com_testquestions/sql/updates/mysql/1.43.7.sql @@ -0,0 +1 @@ +-- No database changes required for version 1.43.7. diff --git a/administrator/components/com_testquestions/src/Controller/AiController.php b/administrator/components/com_testquestions/src/Controller/AiController.php index d750c84..dfc5dd5 100644 --- a/administrator/components/com_testquestions/src/Controller/AiController.php +++ b/administrator/components/com_testquestions/src/Controller/AiController.php @@ -5,6 +5,7 @@ namespace Komponent\Component\Testquestions\Administrator\Controller; defined('_JEXEC') or die; use Joomla\CMS\Factory; +use Joomla\CMS\Http\HttpFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Controller\BaseController; use Joomla\CMS\Router\Route; @@ -14,6 +15,65 @@ use Komponent\Component\Testquestions\Administrator\Service\AiService; class AiController extends BaseController { public function save(): void + { + $this->saveSettings(); + + $this->setRedirect( + Route::_('index.php?option=com_testquestions&view=ai', false), + Text::_('COM_TESTQUESTIONS_AI_SETTINGS_SAVED') + ); + } + + public function checkbalance(): void + { + $settings = $this->saveSettings(); + $teamId = trim((string) ($settings['ai_grok_team_id'] ?? '')); + $managementKey = trim((string) ($settings['ai_grok_management_key'] ?? '')); + + if ($teamId === '' || $managementKey === '') { + $this->setRedirect( + Route::_('index.php?option=com_testquestions&view=ai', false), + Text::_('COM_TESTQUESTIONS_AI_GROK_BALANCE_CONFIG_REQUIRED'), + 'warning' + ); + return; + } + + try { + $response = HttpFactory::getHttp()->get( + 'https://management-api.x.ai/v1/billing/teams/' . rawurlencode($teamId) . '/prepaid/balance', + ['Authorization' => 'Bearer ' . $managementKey, 'Accept' => 'application/json'], + 30 + ); + } catch (\Throwable $error) { + $this->setRedirect( + Route::_('index.php?option=com_testquestions&view=ai', false), + Text::_('COM_TESTQUESTIONS_AI_GROK_BALANCE_FAILED'), + 'error' + ); + return; + } + + $body = json_decode((string) $response->body, true); + if ((int) $response->code < 200 || (int) $response->code >= 300 || !isset($body['total']['val']) || !is_numeric($body['total']['val'])) { + $this->setRedirect( + Route::_('index.php?option=com_testquestions&view=ai', false), + Text::_('COM_TESTQUESTIONS_AI_GROK_BALANCE_FAILED'), + 'error' + ); + return; + } + + $balance = abs((float) $body['total']['val']) / 100; + $message = Text::sprintf('COM_TESTQUESTIONS_AI_GROK_BALANCE_RESULT', number_format($balance, 2, ',', ' ')); + $this->setRedirect( + Route::_('index.php?option=com_testquestions&view=ai', false), + $message, + 'message' + ); + } + + private function saveSettings(): array { if (!Session::checkToken()) { throw new \RuntimeException(Text::_('JINVALID_TOKEN'), 403); @@ -56,38 +116,41 @@ class AiController extends BaseController $settings = json_decode((string) $row->params, true); $settings = is_array($settings) ? $settings : []; $legacyKey = trim((string) ($settings['ai_api_key'] ?? '')); - $settings['ai_provider'] = $provider; foreach ($selectedModels as $modelProvider => $model) { $settings['ai_' . $modelProvider . '_model'] = $model; } + foreach (['openai', 'grok'] as $keyProvider) { $fieldName = 'ai_' . $keyProvider . '_api_key'; $apiKey = trim((string) $input->post->get($fieldName, '', 'RAW')); - if ($apiKey !== '') { $settings[$fieldName] = $apiKey; } - if ($keyProvider === $provider && empty($settings[$fieldName]) && $legacyKey !== '') { $settings[$fieldName] = $legacyKey; } } + + $teamId = trim((string) $input->post->get('ai_grok_team_id', '', 'STRING')); + if ($teamId !== '') { + $settings['ai_grok_team_id'] = $teamId; + } + $managementKey = trim((string) $input->post->get('ai_grok_management_key', '', 'RAW')); + if ($managementKey !== '') { + $settings['ai_grok_management_key'] = $managementKey; + } unset($settings['ai_api_key']); $extension = (object) [ 'extension_id' => (int) $row->extension_id, 'params' => json_encode($settings, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ]; - if (!$db->updateObject('#__extensions', $extension, 'extension_id')) { throw new \RuntimeException(Text::_('COM_TESTQUESTIONS_AI_SAVE_FAILED')); } - $this->setRedirect( - Route::_('index.php?option=com_testquestions&view=ai', false), - Text::_('COM_TESTQUESTIONS_AI_SETTINGS_SAVED') - ); + return $settings; } public function cancel(): void diff --git a/administrator/components/com_testquestions/tmpl/ai/default.php b/administrator/components/com_testquestions/tmpl/ai/default.php index d27db32..d61d1e4 100644 --- a/administrator/components/com_testquestions/tmpl/ai/default.php +++ b/administrator/components/com_testquestions/tmpl/ai/default.php @@ -54,6 +54,31 @@ +
+

+
+ + +
+
+ + +
+
+ +
+ + +
+ + settings['ai_grok_management_key']) ? \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_SAVED') : \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_AI_GROK_MANAGEMENT_KEY_INFO'); ?> + +
+ +
+
diff --git a/com_testquestions.xml b/com_testquestions.xml index 6fecc7e..9b04bce 100644 --- a/com_testquestions.xml +++ b/com_testquestions.xml @@ -4,7 +4,7 @@ 2026-10-14 Komponent GNU General Public License version 2 or later - 1.43.6 + 1.43.7 COM_TESTQUESTIONS_DESCRIPTION com_testquestions Komponent\Component\Testquestions