import { renderHeader } from '../components/header.js'; import { state } from '../state.js'; import { base64ToBytes, bytesToBase58 } from '../services/crypto-utils.js'; import { extractSeed32FromPkcs8B64 } from '../services/client-key-utils.js'; export const pageMeta = { id: 'registration-draft-keys-view', title: 'Сгенерированные ключи', showAppChrome: false }; function makeSecretField({ label, value }) { const wrap = document.createElement('div'); wrap.className = 'stack'; const labelEl = document.createElement('span'); labelEl.className = 'field-label'; labelEl.textContent = label; const row = document.createElement('div'); row.className = 'inline-input-row'; const eyeIcon = ` `; const eyeOffIcon = ` `; const input = document.createElement('input'); input.className = 'input key-input'; input.type = 'password'; input.readOnly = true; input.value = value; const toggleBtn = document.createElement('button'); toggleBtn.className = 'icon-btn key-toggle-btn'; toggleBtn.type = 'button'; toggleBtn.innerHTML = eyeOffIcon; toggleBtn.setAttribute('aria-label', 'Показать ключ'); toggleBtn.title = 'Показать ключ'; toggleBtn.addEventListener('click', () => { if (input.type === 'password') { input.type = 'text'; toggleBtn.innerHTML = eyeIcon; toggleBtn.setAttribute('aria-label', 'Скрыть ключ'); toggleBtn.title = 'Скрыть ключ'; } else { input.type = 'password'; toggleBtn.innerHTML = eyeOffIcon; toggleBtn.setAttribute('aria-label', 'Показать ключ'); toggleBtn.title = 'Показать ключ'; } }); row.append(input, toggleBtn); wrap.append(labelEl, row); return wrap; } function makePublicField({ label, value }) { const wrap = document.createElement('div'); wrap.className = 'stack'; const labelEl = document.createElement('span'); labelEl.className = 'field-label'; labelEl.textContent = label; const input = document.createElement('input'); input.className = 'input key-input'; input.type = 'text'; input.readOnly = true; input.value = value; wrap.append(labelEl, input); return wrap; } export function render({ navigate }) { const screen = document.createElement('section'); screen.className = 'stack'; const keyBundle = state.registrationDraft.preGeneratedKeyBundle; const card = document.createElement('div'); card.className = 'card stack'; if (!keyBundle) { const msg = document.createElement('p'); msg.className = 'status-line is-unavailable'; msg.textContent = 'Ключи ещё не сгенерированы. Вернитесь на экран регистрации и введите логин с паролем.'; card.append(msg); } else { const warning = document.createElement('p'); warning.className = 'meta-muted'; warning.textContent = 'Никому не сообщайте приватные ключи и секрет. Они не хранятся на сервере и существуют только на вашем устройстве.'; card.append(warning); // Master secret let secretB58 = ''; try { secretB58 = bytesToBase58(base64ToBytes(keyBundle.masterSecretB64)); } catch { secretB58 = '(не удалось извлечь)'; } card.append(makeSecretField({ label: 'Главный секрет (master secret, base58, 32 байта)', value: secretB58 })); // Recovery key const recoverySep = document.createElement('p'); recoverySep.className = 'field-label'; recoverySep.textContent = 'Recovery key'; card.append(recoverySep); card.append(makePublicField({ label: 'Recovery — публичный (base58)', value: bytesToBase58(base64ToBytes(keyBundle.recoveryPair.publicKeyB64)), })); card.append(makeSecretField({ label: 'Recovery — приватный (seed base58, 32 байта)', value: bytesToBase58(extractSeed32FromPkcs8B64(keyBundle.recoveryPair.privatePkcs8B64)), })); // Root key const rootSep = document.createElement('p'); rootSep.className = 'field-label'; rootSep.textContent = 'Root key'; card.append(rootSep); card.append(makePublicField({ label: 'Root — публичный (base58)', value: bytesToBase58(base64ToBytes(keyBundle.rootPair.publicKeyB64)), })); card.append(makeSecretField({ label: 'Root — приватный (seed base58, 32 байта)', value: bytesToBase58(extractSeed32FromPkcs8B64(keyBundle.rootPair.privatePkcs8B64)), })); // Blockchain key const bchSep = document.createElement('p'); bchSep.className = 'field-label'; bchSep.textContent = 'Blockchain key'; card.append(bchSep); card.append(makePublicField({ label: 'Blockchain — публичный (base58)', value: bytesToBase58(base64ToBytes(keyBundle.blockchainPair.publicKeyB64)), })); card.append(makeSecretField({ label: 'Blockchain — приватный (seed base58, 32 байта)', value: bytesToBase58(extractSeed32FromPkcs8B64(keyBundle.blockchainPair.privatePkcs8B64)), })); // Client key const devSep = document.createElement('p'); devSep.className = 'field-label'; devSep.textContent = 'Client key (= Solana wallet)'; card.append(devSep); card.append(makePublicField({ label: 'Client — публичный (base58)', value: bytesToBase58(base64ToBytes(keyBundle.clientPair.publicKeyB64)), })); card.append(makeSecretField({ label: 'Client — приватный (seed base58, 32 байта)', value: bytesToBase58(extractSeed32FromPkcs8B64(keyBundle.clientPair.privatePkcs8B64)), })); } const actions = document.createElement('div'); actions.className = 'auth-footer-actions'; const backButton = document.createElement('button'); backButton.className = 'ghost-btn'; backButton.type = 'button'; backButton.textContent = 'Назад'; backButton.addEventListener('click', () => navigate('registration-payment-view')); actions.append(backButton); screen.append( renderHeader({ title: 'Сгенерированные ключи', leftAction: { label: '←', onClick: () => navigate('registration-payment-view') }, }), card, actions, ); return screen; }