fix(web): подключённый аккаунт не появлялся в списке
Владелец подключил первый аккаунт Antigravity на сервере, получил
«Авторизация успешно завершена» — и не увидел его в разделе «Аккаунты».
Учётные данные сохранялись правильно: save_profile_auth и load_profile_auth
симметричны, get_profile_status после записи отдаёт authenticated=True —
проверено исполнением. Не совпадало другое: состояние профилей берётся из
кэша UnifiedHealthService, а фоновый цикл веб-сервера обновляет снапшот с
force_scan=False и кэш не трогает.
Измерено на изолированном каталоге:
до входа ag-w1 -> not_configured
сразу после входа ag-w1 -> not_configured
после refresh(force=False) ag-w1 -> not_configured <- цикл делает это
после refresh(force=True) ag-w1 -> not_tested
То есть аккаунт не появился бы никогда, пока хаб не перезапустят.
Теперь после успешного входа выполняется пересбор с force_scan=True — во всех
четырёх точках завершения: device-flow, ручная вставка адреса, опрос
redirect-потока и код Claude. Ошибка пересбора логируется и сам вход не
роняет.
Функция объявлена на уровне модуля: вложенной она была видна не всем точкам,
и device-flow получал NameError внутри обработки успеха. Мой тест этого не
поймал, потому что проверял только redirect-путь — нашла проверка ruff.
Проверено: not_configured -> not_tested сразу после входа, без ручного
обновления; путь device-flow исполняется без NameError. 432 passed, ruff
чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0800ca94e0
commit
681c899cfd
1 changed files with 27 additions and 0 deletions
|
|
@ -236,6 +236,27 @@ def do_set_model(profile_id: str, model: str, role_id: Optional[str] = None) ->
|
||||||
return False, "Не удалось сохранить файл конфигурации"
|
return False, "Не удалось сохранить файл конфигурации"
|
||||||
|
|
||||||
|
|
||||||
|
# Подключённый аккаунт обязан появиться в списке сразу.
|
||||||
|
#
|
||||||
|
# Учётные данные сохраняются на диск, но состояние профилей берётся из кэша
|
||||||
|
# UnifiedHealthService, а фоновый цикл веб-сервера обновляет снапшот с
|
||||||
|
# force_scan=False, то есть кэш не трогает. Проверено исполнением: после входа
|
||||||
|
# профиль оставался not_configured и при обычном refresh, и переходил в
|
||||||
|
# not_tested только при force_scan=True. Владелец видел «аккаунт подключён» и
|
||||||
|
# пустой список аккаунтов.
|
||||||
|
#
|
||||||
|
# Функция объявлена на уровне модуля намеренно: как вложенная она была видна
|
||||||
|
# не всем точкам завершения входа, и device-flow получал NameError внутри
|
||||||
|
# обработки успеха.
|
||||||
|
def _rescan_after_auth() -> None:
|
||||||
|
try:
|
||||||
|
from antigravity_provider.router.state_store import HubStateStore
|
||||||
|
|
||||||
|
HubStateStore.get().refresh(force_scan=True)
|
||||||
|
except Exception as exc: # пересбор не должен ронять сам вход
|
||||||
|
logger.warning("Не удалось пересобрать снапшот после входа: %s", exc)
|
||||||
|
|
||||||
|
|
||||||
class ActionExecutor:
|
class ActionExecutor:
|
||||||
"""Shared execution layer for Desktop and Web actions."""
|
"""Shared execution layer for Desktop and Web actions."""
|
||||||
|
|
||||||
|
|
@ -310,6 +331,7 @@ class ActionExecutor:
|
||||||
|
|
||||||
status = getattr(session, 'status', 'unknown')
|
status = getattr(session, 'status', 'unknown')
|
||||||
if status == 'completed':
|
if status == 'completed':
|
||||||
|
_rescan_after_auth()
|
||||||
return {'ok': True, 'message': 'Аккаунт подключён', 'data': {'status': status}}
|
return {'ok': True, 'message': 'Аккаунт подключён', 'data': {'status': status}}
|
||||||
if status in ('failed', 'timeout'):
|
if status in ('failed', 'timeout'):
|
||||||
reason = getattr(session, 'error_msg', None) or 'Авторизация не завершена'
|
reason = getattr(session, 'error_msg', None) or 'Авторизация не завершена'
|
||||||
|
|
@ -402,6 +424,8 @@ class ActionExecutor:
|
||||||
if not session:
|
if not session:
|
||||||
return {'ok': False, 'message': 'Сессия авторизации не найдена или уже завершена'}
|
return {'ok': False, 'message': 'Сессия авторизации не найдена или уже завершена'}
|
||||||
ok, msg = session.handle_auth_code(raw_value)
|
ok, msg = session.handle_auth_code(raw_value)
|
||||||
|
if ok:
|
||||||
|
_rescan_after_auth()
|
||||||
else:
|
else:
|
||||||
from antigravity_provider.router.profile_oauth import get_oauth_session
|
from antigravity_provider.router.profile_oauth import get_oauth_session
|
||||||
|
|
||||||
|
|
@ -409,6 +433,8 @@ class ActionExecutor:
|
||||||
if not session:
|
if not session:
|
||||||
return {'ok': False, 'message': 'Сессия авторизации не найдена или уже завершена'}
|
return {'ok': False, 'message': 'Сессия авторизации не найдена или уже завершена'}
|
||||||
ok, msg = session.handle_manual_callback_url(raw_value)
|
ok, msg = session.handle_manual_callback_url(raw_value)
|
||||||
|
if ok:
|
||||||
|
_rescan_after_auth()
|
||||||
return {'ok': ok, 'message': msg}
|
return {'ok': ok, 'message': msg}
|
||||||
|
|
||||||
if action == 'poll_redirect_auth':
|
if action == 'poll_redirect_auth':
|
||||||
|
|
@ -420,6 +446,7 @@ class ActionExecutor:
|
||||||
return {'ok': False, 'message': 'Сессия авторизации не найдена'}
|
return {'ok': False, 'message': 'Сессия авторизации не найдена'}
|
||||||
status = getattr(session, 'status', 'unknown')
|
status = getattr(session, 'status', 'unknown')
|
||||||
if status == 'completed':
|
if status == 'completed':
|
||||||
|
_rescan_after_auth()
|
||||||
return {'ok': True, 'message': 'Аккаунт подключён', 'data': {'status': 'completed'}}
|
return {'ok': True, 'message': 'Аккаунт подключён', 'data': {'status': 'completed'}}
|
||||||
if status in ('failed', 'cancelled'):
|
if status in ('failed', 'cancelled'):
|
||||||
return {'ok': False, 'message': session.error_msg or 'Авторизация не удалась'}
|
return {'ok': False, 'message': session.error_msg or 'Авторизация не удалась'}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue