SHA256
Checkpoint: первая рабочая версия звонков, сигналинг будет переделан
This commit is contained in:
@@ -4,6 +4,7 @@ const MAX_AVATAR_SOURCE_BYTES = 10 * 1024 * 1024;
|
||||
const MAX_AVATAR_SIDE_PX = 768;
|
||||
const AVATAR_QUALITY = 0.86;
|
||||
const TX_ID_RE = /^[A-Za-z0-9_-]{43}$/;
|
||||
const SHA256_HEX_RE = /^[A-Fa-f0-9]{64}$/;
|
||||
|
||||
let arweaveLibPromise = null;
|
||||
|
||||
@@ -92,29 +93,64 @@ function canvasToBlob(canvas, type, quality) {
|
||||
});
|
||||
}
|
||||
|
||||
function bytesToHex(bytes) {
|
||||
return Array.from(bytes, (item) => item.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
export function validateArweaveTxId(txId) {
|
||||
const value = String(txId || '').trim();
|
||||
return TX_ID_RE.test(value);
|
||||
}
|
||||
|
||||
export function buildArweaveAvatarValue(txId) {
|
||||
export function validateSha256Hex(sha256Hex) {
|
||||
const value = String(sha256Hex || '').trim();
|
||||
return SHA256_HEX_RE.test(value);
|
||||
}
|
||||
|
||||
export async function sha256HexFromArrayBuffer(buffer) {
|
||||
if (!(buffer instanceof ArrayBuffer)) throw new Error('Некорректные данные для SHA256');
|
||||
const digest = await crypto.subtle.digest('SHA-256', buffer);
|
||||
return bytesToHex(new Uint8Array(digest));
|
||||
}
|
||||
|
||||
export function buildArweaveAvatarValue(txId, sha256Hex = '') {
|
||||
const cleanTxId = String(txId || '').trim();
|
||||
if (!validateArweaveTxId(cleanTxId)) {
|
||||
throw new Error('Некорректный Transaction ID Arweave');
|
||||
}
|
||||
return `AR:${cleanTxId}`;
|
||||
const cleanSha = String(sha256Hex || '').trim().toLowerCase();
|
||||
if (!cleanSha) return `AR:${cleanTxId}`;
|
||||
if (!validateSha256Hex(cleanSha)) {
|
||||
throw new Error('Некорректный SHA256 хэш аватара');
|
||||
}
|
||||
return `SHA256:${cleanSha},AR:${cleanTxId}`;
|
||||
}
|
||||
|
||||
export function parseArweaveAvatarValue(value) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw.startsWith('AR:')) {
|
||||
return { ok: false, network: '', txId: '' };
|
||||
if (!raw) {
|
||||
return { ok: false, network: '', txId: '', sha256Hex: '' };
|
||||
}
|
||||
|
||||
const arMatch = raw.match(/(?:^|,)\s*AR:([A-Za-z0-9_-]{43})\s*(?:,|$)/);
|
||||
let txId = String(arMatch?.[1] || '').trim();
|
||||
if (!txId) {
|
||||
// fallback для старых/кривых значений без запятых: "...AR:<txid>..."
|
||||
const fallbackAr = raw.match(/AR:([A-Za-z0-9_-]{43})/);
|
||||
txId = String(fallbackAr?.[1] || '').trim();
|
||||
}
|
||||
const txId = raw.slice(3).trim();
|
||||
if (!validateArweaveTxId(txId)) {
|
||||
return { ok: false, network: '', txId: '' };
|
||||
return { ok: false, network: '', txId: '', sha256Hex: '' };
|
||||
}
|
||||
return { ok: true, network: 'AR', txId };
|
||||
|
||||
const shaMatch = raw.match(/(?:^|,)\s*SHA256:([A-Fa-f0-9]{64})\s*(?:,|$)/);
|
||||
let sha256Hex = String(shaMatch?.[1] || '').trim().toLowerCase();
|
||||
if (!sha256Hex) {
|
||||
const fallbackSha = raw.match(/SHA256:([A-Fa-f0-9]{64})/);
|
||||
sha256Hex = String(fallbackSha?.[1] || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
return { ok: true, network: 'AR', txId, sha256Hex };
|
||||
}
|
||||
|
||||
export function buildArweaveDataUrl({ gateway, txId }) {
|
||||
@@ -209,6 +245,8 @@ export async function prepareAvatarImageFile(file) {
|
||||
}
|
||||
|
||||
const optimizedFile = blobToFile(blob, fileName, contentType);
|
||||
const optimizedArrayBuffer = await optimizedFile.arrayBuffer();
|
||||
const sha256Hex = await sha256HexFromArrayBuffer(optimizedArrayBuffer);
|
||||
return {
|
||||
file: optimizedFile,
|
||||
originalSizeBytes: Number(file.size || 0),
|
||||
@@ -218,6 +256,7 @@ export async function prepareAvatarImageFile(file) {
|
||||
width,
|
||||
height,
|
||||
contentType,
|
||||
sha256Hex,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof Error) throw error;
|
||||
|
||||
Reference in New Issue
Block a user