UI: добавить вложения Arweave в каналы

This commit is contained in:
AidarKC
2026-07-30 11:04:50 +04:00
parent c7684d6fe1
commit e9e6628b21
10 changed files with 974 additions and 29 deletions
@@ -93,6 +93,53 @@ function pickCachedWallet(secrets) {
};
}
function normalizeExtraWallets(secrets) {
const rows = Array.isArray(secrets?.arweaveWallets) ? secrets.arweaveWallets : [];
return rows
.filter((item) => item && typeof item === 'object')
.map((item) => ({
id: String(item.id || item.address || '').trim(),
label: String(item.label || '').trim(),
address: String(item.address || '').trim(),
owner: String(item.owner || '').trim(),
jwk: item.jwk,
createdAtMs: Number(item.createdAtMs || 0),
}))
.filter((item) => item.id && item.address && item.jwk && typeof item.jwk === 'object');
}
function parseJwkSecretString(secret) {
const raw = String(secret || '').trim();
if (!raw) throw new Error('Введите секрет Arweave-кошелька.');
const tryParse = (value) => {
const parsed = JSON.parse(value);
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('Некорректный JWK Arweave.');
}
return parsed;
};
if (raw.startsWith('{')) return tryParse(raw);
const normalized = raw
.replace(/-/g, '+')
.replace(/_/g, '/')
.padEnd(Math.ceil(raw.length / 4) * 4, '=');
try {
return tryParse(atob(normalized));
} catch {
throw new Error('Секрет должен быть JSON JWK или base64url(JSON JWK).');
}
}
function validateArweaveJwk(jwk) {
if (!jwk || typeof jwk !== 'object') throw new Error('Некорректный JWK Arweave.');
['kty', 'e', 'n', 'd', 'p', 'q', 'dp', 'dq', 'qi'].forEach((key) => {
if (!String(jwk[key] || '').trim()) throw new Error(`В JWK Arweave нет поля ${key}.`);
});
}
function safeStatus(onStatus, text) {
if (typeof onStatus !== 'function') return;
try {
@@ -102,6 +149,63 @@ function safeStatus(onStatus, text) {
}
}
export async function getArweaveWalletChoices({ login, storagePwd, onStatus } = {}) {
const derived = await getArweaveWalletFromStoredClientKey({ login, storagePwd, onStatus });
const secrets = await loadEncryptedUserSecrets(String(login || '').trim(), String(storagePwd || '').trim());
const extras = normalizeExtraWallets(secrets);
return [
{
id: 'derived-client-key',
label: 'Стандартный из client key',
address: derived.address,
owner: derived.owner,
jwk: derived.jwk,
isDefault: true,
},
...extras.map((item) => ({
...item,
label: item.label || `Arweave ${item.address.slice(0, 6)}...${item.address.slice(-6)}`,
isDefault: false,
})),
];
}
export async function addArweaveWalletSecret({ login, storagePwd, secret, label = '' } = {}) {
const cleanLogin = String(login || '').trim();
const cleanPwd = String(storagePwd || '').trim();
if (!cleanLogin || !cleanPwd) throw new Error('Нет активной сессии.');
const jwk = parseJwkSecretString(secret);
validateArweaveJwk(jwk);
const moduleRef = await loadArweaveLib();
const Arweave = moduleRef?.default || moduleRef;
const arweave = Arweave.init({ host: 'arweave.net', port: 443, protocol: 'https' });
const address = await arweave.wallets.jwkToAddress(jwk);
const owner = String(jwk.n || '').trim();
const id = `manual-${address}`;
const createdAtMs = Date.now();
const wallet = {
id,
label: String(label || '').trim() || `Arweave ${address.slice(0, 6)}...${address.slice(-6)}`,
address,
owner,
jwk,
createdAtMs,
};
await updateEncryptedUserSecrets(cleanLogin, cleanPwd, (current) => {
const wallets = normalizeExtraWallets(current).filter((item) => item.address !== address);
wallets.push(wallet);
return {
...current,
arweaveWallets: wallets,
};
});
return wallet;
}
export async function getArweaveWalletFromStoredClientKey({ login, storagePwd, onStatus } = {}) {
const cleanLogin = String(login || '').trim();
const cleanPwd = String(storagePwd || '').trim();