Files
SHiNE-server/shine-UI/js/services/shine-blockchain-snapshot-service.js
T

109 lines
3.2 KiB
JavaScript

const SNAPSHOT_STORAGE_KEY = 'shine-ui-blockchain-snapshot-v1';
function normalizeLogin(login) {
return String(login || '').trim().toLowerCase();
}
function storageBucketKey(login) {
return normalizeLogin(login) || 'anonymous';
}
function decodeBase64ToBytes(base64) {
const raw = String(base64 || '').trim();
if (!raw) return new Uint8Array();
const bin = atob(raw);
const out = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i += 1) out[i] = bin.charCodeAt(i);
return out;
}
function readStorageMap() {
try {
const raw = localStorage.getItem(SNAPSHOT_STORAGE_KEY);
const parsed = raw ? JSON.parse(raw) : {};
return parsed && typeof parsed === 'object' ? parsed : {};
} catch {
return {};
}
}
function writeStorageMap(value) {
try {
localStorage.setItem(SNAPSHOT_STORAGE_KEY, JSON.stringify(value || {}));
} catch {
// ignore localStorage errors
}
}
export function readStoredBlockchainSnapshot(login) {
const key = storageBucketKey(login);
const map = readStorageMap();
const value = map?.[key];
return value && typeof value === 'object' ? value : null;
}
export function saveStoredBlockchainSnapshot(login, snapshot) {
const key = storageBucketKey(login);
const map = readStorageMap();
map[key] = {
...snapshot,
savedAtMs: Date.now(),
};
writeStorageMap(map);
return map[key];
}
export function clearStoredBlockchainSnapshot(login) {
const key = storageBucketKey(login);
const map = readStorageMap();
delete map[key];
writeStorageMap(map);
}
export async function buildBlockchainSnapshotFile({
authService,
login,
blockchainName,
lastBlockNumber,
} = {}) {
const cleanLogin = normalizeLogin(login);
const cleanBlockchainName = String(blockchainName || '').trim();
const maxBlockNumber = Number(lastBlockNumber);
if (!authService?.ws?.request) throw new Error('Сервис сервера недоступен.');
if (!cleanLogin) throw new Error('Не указан логин.');
if (!cleanBlockchainName) throw new Error('Не указано имя блокчейна.');
if (!Number.isFinite(maxBlockNumber) || maxBlockNumber < 0) {
throw new Error('На сервере нет блоков для слепка.');
}
const parts = [];
let totalBytes = 0;
for (let blockNumber = 0; blockNumber <= maxBlockNumber; blockNumber += 1) {
const response = await authService.ws.request('GetBlockchainBlock', {
blockchainName: cleanBlockchainName,
blockNumber,
});
if (response?.status !== 200) {
const message = String(response?.payload?.message || response?.message || 'Не удалось получить блок.');
throw new Error(`Не удалось скачать блок ${blockNumber}: ${message}`);
}
const blockBytes = decodeBase64ToBytes(response?.payload?.blockBytesB64 || '');
parts.push(blockBytes);
totalBytes += blockBytes.length;
}
const file = new File(parts, `${cleanBlockchainName}.shine-blockchain`, {
type: 'application/octet-stream',
lastModified: Date.now(),
});
return {
file,
blockCount: maxBlockNumber + 1,
totalBytes,
lastBlockNumber: maxBlockNumber,
login: cleanLogin,
blockchainName: cleanBlockchainName,
};
}