SHA256
109 lines
3.2 KiB
JavaScript
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,
|
|
};
|
|
}
|