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;
}