SHA256
Обновить pairing устройств и доработать ESP32 UI
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
buildSessionAttachPayload,
|
||||
deriveEspPairingPasswordHash,
|
||||
encryptPairingPayloadForRequester,
|
||||
formatPairingShortCode,
|
||||
} from '../services/device-pairing-service.js';
|
||||
import { loadEncryptedUserSecrets } from '../services/key-vault.js';
|
||||
import { toUserMessage } from '../services/ui-error-texts.js';
|
||||
@@ -29,8 +30,10 @@ function setStatus(statusEl, message, kind = 'info') {
|
||||
statusEl.style.display = message ? '' : 'none';
|
||||
}
|
||||
|
||||
function normalizeCode(value) {
|
||||
return String(value || '').replace(/\D+/g, '').slice(0, 7);
|
||||
const SESSION_TYPE_WALLET = 50;
|
||||
|
||||
function pairingSessionKindLabel(sessionType) {
|
||||
return Number(sessionType || 0) === SESSION_TYPE_WALLET ? 'Wallet session' : 'Client session';
|
||||
}
|
||||
|
||||
function buildTransferKeys(savedKeys, { withExtras = false }) {
|
||||
@@ -54,19 +57,19 @@ function buildTransferKeys(savedKeys, { withExtras = false }) {
|
||||
}
|
||||
|
||||
function requestCardHtml(request) {
|
||||
const shortCode = String(request?.shortCode || '').trim() || '0000000';
|
||||
const shortCode = formatPairingShortCode(request?.shortCode || '');
|
||||
const client = String(request?.requesterClientPlatform || 'unknown');
|
||||
const requesterSessionType = Number(request?.requesterSessionType || 0);
|
||||
const expiresText = request?.expiresAtMs ? formatRelativeTime(request.expiresAtMs) : '—';
|
||||
const sessionOnly = requesterSessionType === 50;
|
||||
const sessionOnly = requesterSessionType === SESSION_TYPE_WALLET;
|
||||
const sessionKind = pairingSessionKindLabel(requesterSessionType);
|
||||
return `
|
||||
<div class="card stack" data-pairing-id="${String(request?.pairingId || '')}">
|
||||
<div class="row" style="align-items:flex-start;">
|
||||
<div class="stack" style="gap:4px;">
|
||||
<div style="font-size:28px; font-weight:700; letter-spacing:0.14em;">${shortCode}</div>
|
||||
<span class="meta-muted">Платформа: ${client}</span>
|
||||
<span class="meta-muted">Тип сессии: ${requesterSessionType || '—'}</span>
|
||||
<span class="meta-muted">Тип payload: ${Number(request?.payloadType || 0)}</span>
|
||||
<span class="meta-muted">Session name: ${client}</span>
|
||||
<span class="meta-muted">Session kind: ${sessionKind}</span>
|
||||
<span class="meta-muted">Истекает: ${expiresText}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -198,14 +201,11 @@ export function render({ navigate }) {
|
||||
const requestsCard = document.createElement('div');
|
||||
requestsCard.className = 'card stack';
|
||||
requestsCard.innerHTML = `
|
||||
<div class="row" style="align-items:flex-end; gap:10px; flex-wrap:wrap;">
|
||||
<label class="stack" style="flex:1 1 180px;">
|
||||
<span class="field-label">Код нового устройства</span>
|
||||
<input class="input" id="pairing-code-filter" inputmode="numeric" maxlength="7" placeholder="7 цифр" />
|
||||
</label>
|
||||
<div class="row" style="justify-content:space-between; align-items:center; gap:10px; flex-wrap:wrap;">
|
||||
<span class="field-label">Активные заявки</span>
|
||||
<button class="ghost-btn" type="button" id="refresh-pairing-requests">Обновить заявки</button>
|
||||
</div>
|
||||
<p class="meta-muted">Если код не введён, показываются все активные заявки. Для wallet-заявки можно выпустить отдельную session-only сессию без передачи постоянных ключей.</p>
|
||||
<p class="meta-muted">Показываются все активные заявки. Для wallet-заявки можно выпустить отдельную session-only сессию без передачи постоянных ключей.</p>
|
||||
<div class="stack" id="pairing-requests-list"></div>
|
||||
`;
|
||||
|
||||
@@ -213,7 +213,6 @@ export function render({ navigate }) {
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.style.display = 'none';
|
||||
const keySummaryEl = keySummaryCard.querySelector('#pairing-key-summary');
|
||||
const codeFilterInput = requestsCard.querySelector('#pairing-code-filter');
|
||||
const refreshBtn = requestsCard.querySelector('#refresh-pairing-requests');
|
||||
const requestsListEl = requestsCard.querySelector('#pairing-requests-list');
|
||||
|
||||
@@ -413,23 +412,17 @@ export function render({ navigate }) {
|
||||
};
|
||||
|
||||
const renderRequests = () => {
|
||||
const filterCode = normalizeCode(codeFilterInput.value);
|
||||
const filtered = filterCode
|
||||
? requests.filter((item) => normalizeCode(item?.shortCode) === filterCode)
|
||||
: requests;
|
||||
requestsListEl.innerHTML = '';
|
||||
|
||||
if (!filtered.length) {
|
||||
if (!requests.length) {
|
||||
const empty = document.createElement('p');
|
||||
empty.className = 'meta-muted';
|
||||
empty.textContent = filterCode
|
||||
? 'Заявка с таким кодом пока не найдена.'
|
||||
: 'Активных заявок сейчас нет.';
|
||||
empty.textContent = 'Активных заявок сейчас нет.';
|
||||
requestsListEl.append(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
filtered.forEach((request) => {
|
||||
requests.forEach((request) => {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = requestCardHtml(request);
|
||||
requestsListEl.append(wrapper.firstElementChild);
|
||||
@@ -470,12 +463,12 @@ export function render({ navigate }) {
|
||||
const approveRequest = async (request, mode) => {
|
||||
const withExtras = mode === 'with-extras';
|
||||
let payload;
|
||||
if (!withExtras && Number(request?.requesterSessionType || 0) === 50) {
|
||||
if (!withExtras && Number(request?.requesterSessionType || 0) === SESSION_TYPE_WALLET) {
|
||||
const delegatedSession = await authService.createDelegatedSessionWithDeviceKey({
|
||||
login: state.session.login,
|
||||
devicePrivPkcs8: String(savedKeys?.deviceKey || '').trim(),
|
||||
sessionKey: String(request?.requesterSessionKey || '').trim(),
|
||||
sessionType: Number(request?.requesterSessionType || 50) || 50,
|
||||
sessionType: Number(request?.requesterSessionType || SESSION_TYPE_WALLET) || SESSION_TYPE_WALLET,
|
||||
clientPlatform: String(request?.requesterClientPlatform || '').trim() || 'Wallet plugin',
|
||||
clientInfo: 'Wallet session approved via device pairing',
|
||||
});
|
||||
@@ -493,7 +486,7 @@ export function render({ navigate }) {
|
||||
}
|
||||
const encryptedPayload = await encryptPairingPayloadForRequester(request?.requesterSessionKey, payload);
|
||||
await runPairingOpWithSessionRestore(() => authService.approveTrustedDeviceLogin(request?.pairingId, encryptedPayload));
|
||||
const sessionOnly = !withExtras && Number(request?.requesterSessionType || 0) === 50;
|
||||
const sessionOnly = !withExtras && Number(request?.requesterSessionType || 0) === SESSION_TYPE_WALLET;
|
||||
showToast(
|
||||
withExtras
|
||||
? 'Ключи переданы на новое устройство'
|
||||
@@ -600,10 +593,6 @@ export function render({ navigate }) {
|
||||
refreshBtn.addEventListener('click', () => {
|
||||
void reloadRequests();
|
||||
});
|
||||
codeFilterInput.addEventListener('input', () => {
|
||||
codeFilterInput.value = normalizeCode(codeFilterInput.value);
|
||||
renderRequests();
|
||||
});
|
||||
|
||||
requestsListEl.addEventListener('click', async (event) => {
|
||||
const target = event.target;
|
||||
|
||||
Reference in New Issue
Block a user