SHA256
Улучшен вход пользователя в аккаунт
This commit is contained in:
@@ -73,7 +73,7 @@ export function render({ navigate }) {
|
||||
screen.append(
|
||||
renderHeader({
|
||||
title: 'Войти через другое устройство',
|
||||
leftAction: { label: '←', onClick: () => navigate('login-view') },
|
||||
leftAction: { label: '←', onClick: () => { void cancelActivePairingAndBack(); } },
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -82,22 +82,17 @@ export function render({ navigate }) {
|
||||
panel.innerHTML = '<h1 class="login-panel-title">Войти через другое устройство</h1>';
|
||||
|
||||
const formCard = document.createElement('div');
|
||||
formCard.className = 'card stack';
|
||||
formCard.className = 'card stack login-device-preparation';
|
||||
formCard.innerHTML = `
|
||||
<label class="stack">
|
||||
<span class="field-label">Введите логин</span>
|
||||
<input class="input" id="pair-login" type="text" autocomplete="username" placeholder="" value="" />
|
||||
</label>
|
||||
<label class="checkbox-row">
|
||||
<input type="checkbox" id="pair-use-password" />
|
||||
использовать доп. пароль
|
||||
</label>
|
||||
<label class="stack">
|
||||
<span class="field-label">Пароль подключения</span>
|
||||
<p class="auth-copy" id="pair-login-label"></p>
|
||||
<input id="pair-login" type="hidden" value="" />
|
||||
<input id="pair-use-password" type="checkbox" hidden />
|
||||
<label class="stack" id="pair-password-wrap" style="display:none;">
|
||||
<span class="field-label">Дополнительный пароль подключения</span>
|
||||
<input class="input" id="pair-password" type="password" autocomplete="current-password" placeholder="Пароль, заданный на другом устройстве" />
|
||||
</label>
|
||||
<button class="primary-btn" type="button" id="pair-start-btn">Получить код</button>
|
||||
<p class="meta-muted" id="pair-mode-hint">Получить код для входа можно только если сейчас есть хотя бы одно другое активное устройство этого пользователя в сети.</p>
|
||||
<button class="primary-btn" type="button" id="pair-start-btn" style="display:none;">Получить код</button>
|
||||
<p class="meta-muted" id="pair-mode-hint">Создаём код для входа…</p>
|
||||
`;
|
||||
|
||||
const status = document.createElement('p');
|
||||
@@ -110,10 +105,14 @@ export function render({ navigate }) {
|
||||
resultWrap.innerHTML = codeCardHtml();
|
||||
|
||||
const loginInput = formCard.querySelector('#pair-login');
|
||||
const loginLabelEl = formCard.querySelector('#pair-login-label');
|
||||
const usePasswordInput = formCard.querySelector('#pair-use-password');
|
||||
const passwordInput = formCard.querySelector('#pair-password');
|
||||
const startBtn = formCard.querySelector('#pair-start-btn');
|
||||
const modeHintEl = formCard.querySelector('#pair-mode-hint');
|
||||
|
||||
loginInput.value = String(state.loginDraft.login || '').trim();
|
||||
loginLabelEl.textContent = loginInput.value ? `Вход для @${loginInput.value}` : '';
|
||||
const shortCodeEl = resultWrap.querySelector('#pairing-short-code');
|
||||
const statusHintEl = resultWrap.querySelector('#pairing-status-hint');
|
||||
const onlineHintEl = resultWrap.querySelector('#pairing-online-hint');
|
||||
@@ -127,12 +126,11 @@ export function render({ navigate }) {
|
||||
const syncPasswordUi = () => {
|
||||
const usePassword = !!usePasswordInput.checked;
|
||||
passwordInput.parentElement.style.display = usePassword ? '' : 'none';
|
||||
startBtn.style.display = usePassword ? '' : 'none';
|
||||
modeHintEl.textContent = usePassword
|
||||
? 'Получить код для входа можно только если сейчас есть хотя бы одно другое активное устройство этого пользователя в сети. Если на доверённом устройстве включён доп. пароль, введите его.'
|
||||
: 'Получить код для входа можно только если сейчас есть хотя бы одно другое активное устройство этого пользователя в сети.';
|
||||
if (!usePassword) {
|
||||
passwordInput.value = '';
|
||||
}
|
||||
? 'На доверённом устройстве для входа по коду включён дополнительный пароль.'
|
||||
: 'Код можно подтвердить на уже подключённом устройстве. Если оно сейчас не в сети, заявка будет ждать до истечения срока.';
|
||||
if (!usePassword) passwordInput.value = '';
|
||||
};
|
||||
|
||||
const stopPolling = () => {
|
||||
@@ -157,6 +155,8 @@ export function render({ navigate }) {
|
||||
activePairingId = '';
|
||||
activeExpiresAtMs = 0;
|
||||
startBtn.disabled = false;
|
||||
startBtn.style.display = '';
|
||||
startBtn.textContent = 'Получить новый код';
|
||||
cancelBtn.style.display = 'none';
|
||||
resetCodeCard(resultWrap, shortCodeEl, statusHintEl, onlineHintEl, expireHintEl);
|
||||
setStatus(status, 'Время ожидания истекло. Получите новый код.', 'error');
|
||||
@@ -310,14 +310,16 @@ export function render({ navigate }) {
|
||||
setAuthBusy(true);
|
||||
setAuthError('');
|
||||
setAuthInfo('');
|
||||
setStatus(status, 'Проверяем пользователя и создаём pairing-заявку...', 'info');
|
||||
setStatus(status, 'Создаём код для входа…', 'info');
|
||||
clearActivePairing();
|
||||
|
||||
try {
|
||||
await authService.reconnect(state.entrySettings.shineServer);
|
||||
const user = await authService.getUser(login);
|
||||
if (!user?.exists) {
|
||||
throw new Error('Пользователь не найден.');
|
||||
const resolved = await authService.resolveLoginForAuth(login);
|
||||
if (String(resolved?.resolution || '').toUpperCase() !== 'LOCAL') {
|
||||
state.loginDraft.login = String(resolved?.login || login).trim();
|
||||
navigate('login-view');
|
||||
return;
|
||||
}
|
||||
|
||||
requesterMaterial = await createRequesterPairingMaterial();
|
||||
@@ -339,59 +341,66 @@ export function render({ navigate }) {
|
||||
shortCodeEl.textContent = formatPairingShortCode(payload?.shortCode || '');
|
||||
statusHintEl.textContent = 'Откройте на доверенном устройстве: Настройки -> Устройства -> Подключить устройство -> Подключить по коду.';
|
||||
onlineHintEl.textContent = payload?.trustedSessionOnline
|
||||
? 'Сейчас есть хотя бы одна онлайн доверенная сессия, которая может принять заявку.'
|
||||
: 'Сейчас нет онлайн доверенной сессии. Заявка будет ждать, пока пользователь откроет уже подключённое устройство.';
|
||||
? 'Доверенное устройство сейчас в сети и может сразу принять заявку.'
|
||||
: 'Доверенное устройство сейчас не в сети. Заявка будет ждать его подключения.';
|
||||
resultWrap.style.display = '';
|
||||
cancelBtn.style.display = '';
|
||||
startCountdown(payload?.expiresAtMs);
|
||||
state.loginDraft.login = login;
|
||||
setStatus(status, 'Код создан. Ожидаем подтверждение на другом устройстве...', 'info');
|
||||
schedulePoll();
|
||||
} catch (error) {
|
||||
startBtn.disabled = false;
|
||||
const message = toUserMessage(error, 'Не удалось начать вход через другое устройство.');
|
||||
setAuthError(message);
|
||||
setStatus(status, message, 'error');
|
||||
if (String(error?.code || '').toUpperCase() === 'PAIRING_PASSWORD_INVALID' && !usePassword) {
|
||||
usePasswordInput.checked = true;
|
||||
syncPasswordUi();
|
||||
modeHintEl.textContent = 'Для этого аккаунта включён дополнительный пароль подключения. Введите его, чтобы получить код.';
|
||||
setStatus(status, 'Введите дополнительный пароль подключения.', 'info');
|
||||
window.setTimeout(() => passwordInput.focus(), 0);
|
||||
} else {
|
||||
startBtn.style.display = '';
|
||||
startBtn.textContent = 'Повторить';
|
||||
const message = toUserMessage(error, 'Не удалось начать вход через другое устройство.');
|
||||
setAuthError(message);
|
||||
setStatus(status, message, 'error');
|
||||
}
|
||||
} finally {
|
||||
setAuthBusy(false);
|
||||
}
|
||||
});
|
||||
|
||||
cancelBtn.addEventListener('click', async () => {
|
||||
if (!activePairingId || !requesterMaterial?.sessionKey) {
|
||||
clearActivePairing();
|
||||
startBtn.disabled = false;
|
||||
cancelBtn.style.display = 'none';
|
||||
return;
|
||||
const cancelActivePairingAndBack = async () => {
|
||||
const pairingId = activePairingId;
|
||||
const requesterSessionKey = requesterMaterial?.sessionKey;
|
||||
isDisposed = true;
|
||||
stopPolling();
|
||||
stopCountdown();
|
||||
if (pairingId && requesterSessionKey) {
|
||||
try {
|
||||
await authService.cancelTrustedDeviceLogin(pairingId, requesterSessionKey);
|
||||
} catch {
|
||||
// Навигацию назад не блокируем из-за ошибки отмены уже созданной заявки.
|
||||
}
|
||||
}
|
||||
cancelBtn.disabled = true;
|
||||
try {
|
||||
await authService.cancelTrustedDeviceLogin(activePairingId, requesterMaterial.sessionKey);
|
||||
clearActivePairing();
|
||||
startBtn.disabled = false;
|
||||
setStatus(status, 'Ожидание подключения отменено.', 'info');
|
||||
} catch (error) {
|
||||
const message = toUserMessage(error, 'Не удалось отменить ожидание подключения.');
|
||||
setAuthError(message);
|
||||
setStatus(status, message, 'error');
|
||||
} finally {
|
||||
cancelBtn.disabled = false;
|
||||
cancelBtn.style.display = activePairingId ? '' : 'none';
|
||||
}
|
||||
});
|
||||
navigate('login-view');
|
||||
};
|
||||
|
||||
screen.cleanup = () => {
|
||||
isDisposed = true;
|
||||
stopPolling();
|
||||
stopCountdown();
|
||||
if (activePairingId && requesterMaterial?.sessionKey) {
|
||||
void authService.cancelTrustedDeviceLogin(activePairingId, requesterMaterial.sessionKey).catch(() => {});
|
||||
}
|
||||
};
|
||||
|
||||
const resultActions = document.createElement('div');
|
||||
resultActions.className = 'row';
|
||||
resultActions.append(cancelBtn);
|
||||
resultWrap.append(resultActions);
|
||||
|
||||
panel.append(formCard, status, resultWrap);
|
||||
screen.append(panel);
|
||||
|
||||
if (!String(loginInput.value || '').trim()) {
|
||||
window.setTimeout(() => navigate('login-view'), 0);
|
||||
} else {
|
||||
// После проверки логина на предыдущем экране код создаётся сразу.
|
||||
window.setTimeout(() => startBtn.click(), 0);
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user