Files
SHiNE-server/shine-UI/js/services/qr-key-transfer-service.js
T

85 lines
3.0 KiB
JavaScript

import qrcode from '../vendor-qrcode-generator.js';
const TRANSFER_PREFIX = 'shine-key-transfer-v1:';
const encoder = new TextEncoder();
const decoder = new TextDecoder();
function bytesToBase64Url(bytes) {
let binary = '';
bytes.forEach((b) => {
binary += String.fromCharCode(b);
});
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
}
function base64UrlToBytes(value) {
const normalized = String(value || '').trim().replace(/-/g, '+').replace(/_/g, '/');
const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
const binary = atob(padded);
const out = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
return out;
}
export function keyLabel(id) {
if (id === 'blockchain') return 'ключ блокчейна';
if (id === 'device' || id === 'client') return 'ключ доступа';
return id;
}
export function describeTransferKeys(keys = {}) {
const out = [];
if (keys.clientKey) out.push('ключ доступа');
if (keys.blockchainKey) out.push('ключ блокчейна');
return out;
}
export function makeKeyTransferText({ login, keys }) {
const clientKey = String(keys?.clientKey || '').trim();
const blockchainKey = String(keys?.blockchainKey || '').trim();
if (!clientKey || !blockchainKey) {
throw new Error('Для подключения нужны ключ доступа и ключ блокчейна');
}
const payload = {
v: 1,
type: 'shine-key-transfer',
login: String(login || '').trim(),
keys: { clientKey, blockchainKey },
createdAtMs: Date.now(),
};
const json = JSON.stringify(payload);
return `${TRANSFER_PREFIX}${bytesToBase64Url(encoder.encode(json))}`;
}
export function parseKeyTransferText(text) {
const raw = String(text || '').trim();
if (!raw.startsWith(TRANSFER_PREFIX)) {
throw new Error('Это не QR-код переноса ключей Сияния');
}
const json = decoder.decode(base64UrlToBytes(raw.slice(TRANSFER_PREFIX.length)));
const payload = JSON.parse(json);
if (payload?.v !== 1 || payload?.type !== 'shine-key-transfer') {
throw new Error('Неподдерживаемый формат QR-кода');
}
const login = String(payload.login || '').trim();
if (!login) throw new Error('В QR-коде нет логина');
const keys = payload.keys && typeof payload.keys === 'object' ? payload.keys : {};
const clientKey = String(keys.clientKey || '').trim();
const blockchainKey = String(keys.blockchainKey || '').trim();
if (!clientKey || !blockchainKey) {
throw new Error('В QR-коде нет полного набора рабочих ключей');
}
return {
login,
keys: { clientKey, blockchainKey },
keyTypes: describeTransferKeys({ clientKey, blockchainKey }),
};
}
export function renderQrSvg(text, { cellSize = 4, margin = 4 } = {}) {
const qr = qrcode(0, 'L');
qr.addData(String(text || ''), 'Byte');
qr.make();
return qr.createSvgTag(cellSize, margin);
}