SHA256
Checkpoint: первая рабочая версия звонков, сигналинг будет переделан
This commit is contained in:
@@ -3,8 +3,10 @@ import {
|
||||
buildArweaveDataUrl,
|
||||
getArweaveUploadPrice,
|
||||
prepareAvatarImageFile,
|
||||
sha256HexFromArrayBuffer,
|
||||
uploadArweaveFile,
|
||||
validateArweaveTxId,
|
||||
validateSha256Hex,
|
||||
validateAvatarSourceFile,
|
||||
} from '../services/arweave-file-service.js';
|
||||
import { saveProfileAvatarArweave } from '../services/user-profile-params.js';
|
||||
@@ -69,6 +71,7 @@ export function openAvatarWizard({
|
||||
let optimized = null;
|
||||
let priceInfo = null;
|
||||
let uploadedTxId = '';
|
||||
let uploadedSha256Hex = '';
|
||||
|
||||
function revokePreviewUrl() {
|
||||
if (!lastPreviewUrl) return;
|
||||
@@ -158,6 +161,7 @@ export function openAvatarWizard({
|
||||
const showStepExistingPreview = async (txId) => {
|
||||
if (closed) return;
|
||||
const previewUrl = buildArweaveDataUrl({ gateway: cleanGateway, txId });
|
||||
let existingSha256Hex = '';
|
||||
root.innerHTML = `
|
||||
<div class="modal" data-avatar-wizard-modal="true">
|
||||
<div class="modal-card stack avatar-wizard-card">
|
||||
@@ -165,7 +169,7 @@ export function openAvatarWizard({
|
||||
<div class="avatar-preview-circle avatar-wizard-preview">
|
||||
<img alt="Предпросмотр аватара" data-preview-image="true" />
|
||||
</div>
|
||||
<p class="meta-muted">После сохранения в профиль будет записан только Transaction ID. Сам файл хранится в Arweave.</p>
|
||||
<p class="meta-muted">После сохранения в профиль будут записаны SHA-256 и Transaction ID. Сам файл хранится в Arweave.</p>
|
||||
<p class="avatar-wizard-error" data-error="true"></p>
|
||||
<div class="avatar-wizard-actions">
|
||||
<button class="secondary-btn" type="button" data-action="back">Назад</button>
|
||||
@@ -186,15 +190,19 @@ export function openAvatarWizard({
|
||||
|
||||
try {
|
||||
await ensurePreviewImage(previewUrl, imageEl);
|
||||
const response = await fetch(previewUrl, { method: 'GET', cache: 'no-store' });
|
||||
if (!response.ok) throw new Error('BAD_AVATAR_FETCH');
|
||||
existingSha256Hex = await sha256HexFromArrayBuffer(await response.arrayBuffer());
|
||||
if (!validateSha256Hex(existingSha256Hex)) throw new Error('BAD_AVATAR_HASH');
|
||||
if (saveBtn instanceof HTMLButtonElement) saveBtn.disabled = false;
|
||||
} catch (error) {
|
||||
setNodeText(errorEl, 'Не удалось загрузить изображение по этому Transaction ID');
|
||||
setNodeText(errorEl, 'Не удалось проверить файл по этому Transaction ID.');
|
||||
if (saveBtn instanceof HTMLButtonElement) saveBtn.disabled = true;
|
||||
}
|
||||
|
||||
saveBtn?.addEventListener('click', async () => {
|
||||
try {
|
||||
await saveProfileAvatarArweave(cleanLogin, txId);
|
||||
await saveProfileAvatarArweave(cleanLogin, txId, existingSha256Hex);
|
||||
if (typeof onAvatarSaved === 'function') await onAvatarSaved();
|
||||
close(true, resolve);
|
||||
} catch {
|
||||
@@ -238,7 +246,7 @@ export function openAvatarWizard({
|
||||
<img alt="Предпросмотр аватара" data-preview-image="true" />
|
||||
</div>
|
||||
<div class="avatar-wizard-meta" data-meta="true"></div>
|
||||
<p class="meta-muted">После сохранения в профиль будет записан только Transaction ID. Сам файл хранится в Arweave.</p>
|
||||
<p class="meta-muted">После сохранения в профиль будут записаны SHA-256 и Transaction ID. Сам файл хранится в Arweave.</p>
|
||||
<p class="avatar-wizard-error" data-error="true"></p>
|
||||
<div class="avatar-wizard-actions">
|
||||
<button class="secondary-btn" type="button" data-action="back">Назад</button>
|
||||
@@ -259,6 +267,7 @@ export function openAvatarWizard({
|
||||
let selectedFile = null;
|
||||
optimized = null;
|
||||
priceInfo = null;
|
||||
uploadedSha256Hex = '';
|
||||
|
||||
modal?.addEventListener('click', (event) => {
|
||||
if (event.target === modal) close(false, resolve);
|
||||
@@ -295,6 +304,7 @@ export function openAvatarWizard({
|
||||
<div>Итоговый размер: ${escapeHtml(formatBytes(optimized.optimizedSizeBytes))}</div>
|
||||
<div>Итоговое разрешение: ${escapeHtml(`${optimized.width} × ${optimized.height}`)}</div>
|
||||
<div>Тип файла: ${escapeHtml(optimized.contentType)}</div>
|
||||
<div>SHA-256: ${escapeHtml(String(optimized.sha256Hex || '').toLowerCase())}</div>
|
||||
<div>Примерная цена загрузки: ${escapeHtml(formatAr(priceInfo.ar))} AR</div>
|
||||
`;
|
||||
|
||||
@@ -338,9 +348,13 @@ export function openAvatarWizard({
|
||||
],
|
||||
});
|
||||
uploadedTxId = String(uploaded.id || '').trim();
|
||||
uploadedSha256Hex = String(optimized?.sha256Hex || '').trim().toLowerCase();
|
||||
if (!uploadedTxId) {
|
||||
throw new Error('Пустой Transaction ID');
|
||||
}
|
||||
if (!validateSha256Hex(uploadedSha256Hex)) {
|
||||
throw new Error('Некорректный SHA256');
|
||||
}
|
||||
showStepUploaded();
|
||||
} catch {
|
||||
setNodeText(errorEl, 'Не удалось загрузить файл в Arweave.');
|
||||
@@ -357,6 +371,8 @@ export function openAvatarWizard({
|
||||
<h3 class="modal-title">Файл загружен в Arweave</h3>
|
||||
<p class="meta-muted">Transaction ID:</p>
|
||||
<p class="avatar-wizard-meta">${escapeHtml(uploadedTxId)}</p>
|
||||
<p class="meta-muted">SHA-256:</p>
|
||||
<p class="avatar-wizard-meta">${escapeHtml(uploadedSha256Hex)}</p>
|
||||
<p class="avatar-wizard-error" data-error="true"></p>
|
||||
<div class="avatar-wizard-actions">
|
||||
<button class="ghost-btn" type="button" data-action="copy-id">Скопировать ID</button>
|
||||
@@ -378,7 +394,7 @@ export function openAvatarWizard({
|
||||
});
|
||||
root.querySelector('[data-action="set-avatar"]')?.addEventListener('click', async () => {
|
||||
try {
|
||||
await saveProfileAvatarArweave(cleanLogin, uploadedTxId);
|
||||
await saveProfileAvatarArweave(cleanLogin, uploadedTxId, uploadedSha256Hex);
|
||||
if (typeof onAvatarSaved === 'function') await onAvatarSaved();
|
||||
close(true, resolve);
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user