fix(a54): reject empty credentials before slot allocation

This commit is contained in:
ochenstarik-ui 2026-08-31 20:55:42 +07:00
parent ddeba2db0e
commit b58bfc6c77
3 changed files with 12 additions and 2 deletions

View file

@ -739,6 +739,9 @@ class ActionExecutor:
valid, reason = AutoAssigner.validate_slot(prov_norm, slot)
if not valid:
return {'ok': False, 'message': reason}
existing_status = ProfileAuthManager.get_profile_status(prov_norm, slot) if slot else {}
if not token and prov_norm not in ('local', 'vllm', 'ollama') and not existing_status.get('authenticated'):
return {'ok': False, 'message': 'Не указан API-ключ или не завершена авторизация'}
validation = None
if token or prov_norm in ('local', 'vllm', 'ollama'):
from .connection_preflight import validate_connection

View file

@ -864,9 +864,9 @@ function renderAccountCheck(profile) {
${checking ? `<p>${escapeHtml(profile.display_name || profile.profile_id)}: идёт опрос провайдера, это может занять до минуты на этап.</p>` : ''}
<p>${escapeHtml(check.message || "Подключение ещё не проверялось")}</p>
<p>${escapeHtml(modelStatus)}</p>
<div class="account-models">${models.map(modelBrandLabel).join('')}</div>
<details ${models.length <= 16 ? 'open' : ''}><summary>Каталог моделей (${models.length})</summary><div class="account-models">${models.map(modelBrandLabel).join('')}</div></details>
${profile.provider === 'ollama' ? `<p>Выше — модели указанного сервера Ollama.</p><p>Облачный каталог Ollama: ${meta.cloud?.error ? 'Н/Д — ' + escapeHtml(meta.cloud.error) : meta.cloud?.models ? escapeHtml(meta.cloud.models.join(', ')) : 'Н/Д — ещё не получен'}</p><p>Доступ аккаунта к облачным моделям: Н/Д до успешного вызова. Для прямого вызова нужен API-ключ Ollama; для локального клиента — вход через ollama signin.</p>` : ''}
<button class="btn btn-ghost btn-sm" ${checking ? 'disabled' : ''} onclick="event.stopPropagation(); handleAccountProbe('${escapeHtml(profile.profile_id)}')">${checking ? 'Проверяется…' : 'Проверить подключение и модели'}</button>
<button class="btn btn-ghost btn-sm" ${checking ? 'disabled' : ''} onclick="event.stopPropagation(); handleAccountProbe('${escapeHtml(profile.profile_id)}')">${checking ? 'Проверяется…' : 'Проверить подключение'}</button>
</div>`;
}

View file

@ -240,3 +240,10 @@ def test_bulk_protects_symlink_into_ag(services, tmp_path):
assert preview['data']['targets'] == []
assert ActionExecutor.execute('clear_accounts', {'confirmed': True, 'targets': []})['ok']
assert target.read_text() == 'protected fixture'
@pytest.mark.parametrize('provider', ['openrouter', 'nvidia', 'claude'])
def test_empty_key_never_allocates_slot(services, provider):
result = ActionExecutor.execute('add_account', {'provider': provider, 'token': ''})
assert not result['ok'] and result['message']
assert not load_router_config().profiles