This commit is contained in:
PhilReact 2025-05-17 05:35:32 +03:00
parent dccc7d311f
commit 13a77762b6
11 changed files with 413 additions and 296 deletions

View File

@ -166,11 +166,10 @@ export const encryptAndPublishSymmetricKeyGroupChat = async ({
const registeredName = await getNameInfo(); const registeredName = await getNameInfo();
const data = await publishData({ const data = await publishData({
registeredName, registeredName,
file: encryptedData, data: encryptedData,
service: 'DOCUMENT_PRIVATE', service: 'DOCUMENT_PRIVATE',
identifier: `symmetric-qchat-group-${groupId}`, identifier: `symmetric-qchat-group-${groupId}`,
uploadType: 'file', uploadType: 'base64',
isBase64: true,
withFee: true, withFee: true,
}); });
return { return {
@ -230,11 +229,10 @@ export const encryptAndPublishSymmetricKeyGroupChatForAdmins = async ({
const registeredName = await getNameInfo(); const registeredName = await getNameInfo();
const data = await publishData({ const data = await publishData({
registeredName, registeredName,
file: encryptedData, data: encryptedData,
service: 'DOCUMENT_PRIVATE', service: 'DOCUMENT_PRIVATE',
identifier: `admins-symmetric-qchat-group-${groupId}`, identifier: `admins-symmetric-qchat-group-${groupId}`,
uploadType: 'file', uploadType: 'base64',
isBase64: true,
withFee: true, withFee: true,
}); });
return { return {
@ -259,11 +257,10 @@ export const publishGroupEncryptedResource = async ({
if (!registeredName) throw new Error('You need a name to publish'); if (!registeredName) throw new Error('You need a name to publish');
const data = await publishData({ const data = await publishData({
registeredName, registeredName,
file: encryptedData, data: encryptedData,
service: 'DOCUMENT', service: 'DOCUMENT',
identifier, identifier,
uploadType: 'file', uploadType: 'base64',
isBase64: true,
withFee: true, withFee: true,
}); });
return data; return data;
@ -295,11 +292,10 @@ export const publishOnQDN = async ({
const res = await publishData({ const res = await publishData({
registeredName, registeredName,
file: data, data,
service, service,
identifier, identifier,
uploadType, uploadType,
isBase64: true,
withFee: true, withFee: true,
title, title,
description, description,

View File

@ -189,11 +189,10 @@ export const AppPublish = ({ names, categories }) => {
publishFee: fee.fee + ' QORT', publishFee: fee.fee + ' QORT',
}); });
setIsLoading('Publishing... Please wait.'); setIsLoading('Publishing... Please wait.');
const fileBase64 = await fileToBase64(file);
await new Promise((res, rej) => { await new Promise((res, rej) => {
window window
.sendMessage('publishOnQDN', { .sendMessage('publishOnQDN', {
data: fileBase64, data: file,
service: appType, service: appType,
title, title,
description, description,

View File

@ -177,6 +177,7 @@ export const AppsPrivate = ({ myName }) => {
data: decryptedData, data: decryptedData,
identifier: newPrivateAppValues?.identifier, identifier: newPrivateAppValues?.identifier,
service: newPrivateAppValues?.service, service: newPrivateAppValues?.service,
uploadType: 'base64',
}) })
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {

View File

@ -615,13 +615,22 @@ export const useQortalMessageListener = (
); );
} else if (event?.data?.action === 'SAVE_FILE') { } else if (event?.data?.action === 'SAVE_FILE') {
try { try {
const res = await saveFile(event.data, null, true, { await saveFile(event.data, null, true, {
openSnackGlobal, openSnackGlobal,
setOpenSnackGlobal, setOpenSnackGlobal,
infoSnackCustom, infoSnackCustom,
setInfoSnackCustom, setInfoSnackCustom,
}); });
} catch (error) {} event.ports[0].postMessage({
result: true,
error: null,
});
} catch (error) {
event.ports[0].postMessage({
result: null,
error: error?.message || 'Failed to save file',
});
}
} else if ( } else if (
event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' || event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' ||
event?.data?.action === 'PUBLISH_QDN_RESOURCE' || event?.data?.action === 'PUBLISH_QDN_RESOURCE' ||

View File

@ -803,6 +803,7 @@ export const ChatGroup = ({
data: 'RA==', data: 'RA==',
identifier: onEditMessage?.images[0]?.identifier, identifier: onEditMessage?.images[0]?.identifier,
service: onEditMessage?.images[0]?.service, service: onEditMessage?.images[0]?.service,
uploadType: 'base64',
}); });
} }
if (chatImagesToSave?.length > 0) { if (chatImagesToSave?.length > 0) {

View File

@ -233,6 +233,7 @@ export const ListOfGroupPromotions = () => {
data: data, data: data,
identifier: identifier, identifier: identifier,
service: 'DOCUMENT', service: 'DOCUMENT',
uploadType: 'base64',
}) })
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {

View File

@ -87,6 +87,7 @@ export const GroupAvatar = ({
data: avatarBase64, data: avatarBase64,
identifier: `qortal_group_avatar_${groupId}`, identifier: `qortal_group_avatar_${groupId}`,
service: 'THUMBNAIL', service: 'THUMBNAIL',
uploadType: 'base64',
}) })
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {

View File

@ -80,6 +80,7 @@ export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => {
data: avatarBase64, data: avatarBase64,
identifier: 'qortal_avatar', identifier: 'qortal_avatar',
service: 'THUMBNAIL', service: 'THUMBNAIL',
uploadType: 'base64',
}) })
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {

View File

@ -164,6 +164,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
data: encryptData, data: encryptData,
identifier: 'ext_saved_settings', identifier: 'ext_saved_settings',
service: 'DOCUMENT_PRIVATE', service: 'DOCUMENT_PRIVATE',
uploadType: 'base64',
}) })
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {

View File

@ -1,55 +1,92 @@
// @ts-nocheck // @ts-nocheck
import { Buffer } from "buffer" import { Buffer } from 'buffer';
import Base58 from "../../deps/Base58" import Base58 from '../../deps/Base58';
import nacl from "../../deps/nacl-fast" import nacl from '../../deps/nacl-fast';
import utils from "../../utils/utils" import utils from '../../utils/utils';
import { createEndpoint, getBaseApi } from "../../background"; import { createEndpoint, getBaseApi } from '../../background';
import { getData } from "../../utils/chromeStorage"; import { getData } from '../../utils/chromeStorage';
export async function reusableGet(endpoint) { export async function reusableGet(endpoint) {
const validApi = await getBaseApi(); const validApi = await getBaseApi();
const response = await fetch(validApi + endpoint); const response = await fetch(validApi + endpoint);
const data = await response.json(); const data = await response.json();
return data return data;
} }
async function reusablePost(endpoint, _body) { async function reusablePost(endpoint, _body) {
// const validApi = await findUsableApi(); // const validApi = await findUsableApi();
const url = await createEndpoint(endpoint) const url = await createEndpoint(endpoint);
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}, },
body: _body body: _body,
}); });
let data let data;
try { try {
data = await response.clone().json() data = await response.clone().json();
} catch (e) { } catch (e) {
data = await response.text() data = await response.text();
}
return data;
}
async function reusablePostStream(endpoint, _body) {
const url = await createEndpoint(endpoint);
const headers = {};
const response = await fetch(url, {
method: 'POST',
headers,
body: _body,
});
return response; // return the actual response so calling code can use response.ok
}
async function uploadChunkWithRetry(endpoint, formData, index, maxRetries = 3) {
let attempt = 0;
while (attempt < maxRetries) {
try {
const response = await reusablePostStream(endpoint, formData);
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText);
}
return; // Success
} catch (err) {
attempt++;
console.warn(
`Chunk ${index} failed (attempt ${attempt}): ${err.message}`
);
if (attempt >= maxRetries) {
throw new Error(`Chunk ${index} failed after ${maxRetries} attempts`);
}
// Wait 10 seconds before next retry
await new Promise((res) => setTimeout(res, 10_000));
}
} }
return data
} }
async function getKeyPair() { async function getKeyPair() {
const res = await getData<any>("keyPair").catch(() => null); const res = await getData<any>('keyPair').catch(() => null);
if (res) { if (res) {
return res return res;
} else { } else {
throw new Error("Wallet not authenticated"); throw new Error('Wallet not authenticated');
} }
} }
export const publishData = async ({ export const publishData = async ({
registeredName, registeredName,
file, data,
service, service,
identifier, identifier,
uploadType, uploadType,
isBase64,
filename, filename,
withFee, withFee,
title, title,
@ -60,207 +97,273 @@ export const publishData = async ({
tag3, tag3,
tag4, tag4,
tag5, tag5,
feeAmount feeAmount,
}: any) => { }: any) => {
console.log('data', data);
const validateName = async (receiverName: string) => { const validateName = async (receiverName: string) => {
return await reusableGet(`/names/${receiverName}`) return await reusableGet(`/names/${receiverName}`);
} };
const convertBytesForSigning = async (transactionBytesBase58: string) => { const convertBytesForSigning = async (transactionBytesBase58: string) => {
return await reusablePost('/transactions/convert', transactionBytesBase58) return await reusablePost('/transactions/convert', transactionBytesBase58);
} };
const getArbitraryFee = async () => { const getArbitraryFee = async () => {
const timestamp = Date.now() const timestamp = Date.now();
let fee = await reusableGet(`/transactions/unitfee?txType=ARBITRARY&timestamp=${timestamp}`) let fee = await reusableGet(
`/transactions/unitfee?txType=ARBITRARY&timestamp=${timestamp}`
);
return { return {
timestamp, timestamp,
fee: Number(fee), fee: Number(fee),
feeToShow: (Number(fee) / 1e8).toFixed(8) feeToShow: (Number(fee) / 1e8).toFixed(8),
} };
} };
const signArbitraryWithFee = (arbitraryBytesBase58, arbitraryBytesForSigningBase58, keyPair) => { const signArbitraryWithFee = (
arbitraryBytesBase58,
arbitraryBytesForSigningBase58,
keyPair
) => {
if (!arbitraryBytesBase58) { if (!arbitraryBytesBase58) {
throw new Error('ArbitraryBytesBase58 not defined') throw new Error('ArbitraryBytesBase58 not defined');
} }
if (!keyPair) { if (!keyPair) {
throw new Error('keyPair not defined') throw new Error('keyPair not defined');
} }
const arbitraryBytes = Base58.decode(arbitraryBytesBase58) const arbitraryBytes = Base58.decode(arbitraryBytesBase58);
const _arbitraryBytesBuffer = Object.keys(arbitraryBytes).map(function (key) { return arbitraryBytes[key]; }) const _arbitraryBytesBuffer = Object.keys(arbitraryBytes).map(
const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer) function (key) {
const arbitraryBytesForSigning = Base58.decode(arbitraryBytesForSigningBase58) return arbitraryBytes[key];
const _arbitraryBytesForSigningBuffer = Object.keys(arbitraryBytesForSigning).map(function (key) { return arbitraryBytesForSigning[key]; })
const arbitraryBytesForSigningBuffer = new Uint8Array(_arbitraryBytesForSigningBuffer)
const signature = nacl.sign.detached(arbitraryBytesForSigningBuffer, keyPair.privateKey)
return utils.appendBuffer(arbitraryBytesBuffer, signature)
} }
);
const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer);
const arbitraryBytesForSigning = Base58.decode(
arbitraryBytesForSigningBase58
);
const _arbitraryBytesForSigningBuffer = Object.keys(
arbitraryBytesForSigning
).map(function (key) {
return arbitraryBytesForSigning[key];
});
const arbitraryBytesForSigningBuffer = new Uint8Array(
_arbitraryBytesForSigningBuffer
);
const signature = nacl.sign.detached(
arbitraryBytesForSigningBuffer,
keyPair.privateKey
);
return utils.appendBuffer(arbitraryBytesBuffer, signature);
};
const processTransactionVersion2 = async (bytes) => { const processTransactionVersion2 = async (bytes) => {
return await reusablePost(
return await reusablePost('/transactions/process?apiVersion=2', Base58.encode(bytes)) '/transactions/process?apiVersion=2',
} Base58.encode(bytes)
);
};
const signAndProcessWithFee = async (transactionBytesBase58: string) => { const signAndProcessWithFee = async (transactionBytesBase58: string) => {
let convertedBytesBase58 = await convertBytesForSigning( let convertedBytesBase58 = await convertBytesForSigning(
transactionBytesBase58 transactionBytesBase58
) );
if (convertedBytesBase58.error) { if (convertedBytesBase58.error) {
throw new Error('Error when signing') throw new Error('Error when signing');
} }
const resKeyPair = await getKeyPair();
const resKeyPair = await getKeyPair() const parsedData = resKeyPair;
const parsedData = resKeyPair
const uint8PrivateKey = Base58.decode(parsedData.privateKey); const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey); const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = { const keyPair = {
privateKey: uint8PrivateKey, privateKey: uint8PrivateKey,
publicKey: uint8PublicKey publicKey: uint8PublicKey,
}; };
let signedArbitraryBytes = signArbitraryWithFee(transactionBytesBase58, convertedBytesBase58, keyPair) let signedArbitraryBytes = signArbitraryWithFee(
const response = await processTransactionVersion2(signedArbitraryBytes) transactionBytesBase58,
convertedBytesBase58,
keyPair
);
const response = await processTransactionVersion2(signedArbitraryBytes);
let myResponse = { error: '' } let myResponse = { error: '' };
if (response === false) { if (response === false) {
throw new Error('Error when signing') throw new Error('Error when signing');
} else { } else {
myResponse = response myResponse = response;
} }
return myResponse return myResponse;
} };
const validate = async () => { const validate = async () => {
let validNameRes = await validateName(registeredName) let validNameRes = await validateName(registeredName);
if (validNameRes.error) { if (validNameRes.error) {
throw new Error('Name not found') throw new Error('Name not found');
} }
let fee = null let fee = null;
if (withFee && feeAmount) { if (withFee && feeAmount) {
fee = feeAmount fee = feeAmount;
} else if (withFee) { } else if (withFee) {
const res = await getArbitraryFee() const res = await getArbitraryFee();
if (res.fee) { if (res.fee) {
fee = res.fee fee = res.fee;
} else { } else {
throw new Error('unable to get fee') throw new Error('unable to get fee');
} }
} }
let transactionBytes = await uploadData(registeredName, file, fee) let transactionBytes = await uploadData(registeredName, data, fee);
console.log('transactionBytes length', transactionBytes?.length);
if (!transactionBytes || transactionBytes.error) { if (!transactionBytes || transactionBytes.error) {
throw new Error(transactionBytes?.message || 'Error when uploading') throw new Error(transactionBytes?.message || 'Error when uploading');
} else if (transactionBytes.includes('Error 500 Internal Server Error')) { } else if (transactionBytes.includes('Error 500 Internal Server Error')) {
throw new Error('Error when uploading') throw new Error('Error when uploading');
} }
let signAndProcessRes let signAndProcessRes;
if (withFee) { if (withFee) {
signAndProcessRes = await signAndProcessWithFee(transactionBytes) signAndProcessRes = await signAndProcessWithFee(transactionBytes);
} }
if (signAndProcessRes?.error) { if (signAndProcessRes?.error) {
throw new Error('Error when signing') throw new Error('Error when signing');
} }
return signAndProcessRes return signAndProcessRes;
};
const uploadData = async (registeredName: string, data: any, fee: number) => {
console.log('data', uploadType, data);
let postBody = '';
let urlSuffix = '';
if (data != null) {
if (uploadType === 'base64') {
urlSuffix = '/base64';
} }
const uploadData = async (registeredName: string, file:any, fee: number) => { if (uploadType === 'base64') {
postBody = data;
let postBody = '' }
let urlSuffix = '' } else {
throw new Error('No data provided');
if (file != null) {
// If we're sending zipped data, make sure to use the /zip version of the POST /arbitrary/* API
if (uploadType === 'zip') {
urlSuffix = '/zip'
} }
// If we're sending file data, use the /base64 version of the POST /arbitrary/* API let uploadDataUrl = `/arbitrary/${service}/${registeredName}`;
else if (uploadType === 'file') { let paramQueries = '';
urlSuffix = '/base64'
}
// Base64 encode the file to work around compatibility issues between javascript and java byte arrays
if (isBase64) {
postBody = file
}
if (!isBase64) {
let fileBuffer = new Uint8Array(await file.arrayBuffer())
postBody = Buffer.from(fileBuffer).toString("base64")
}
}
let uploadDataUrl = `/arbitrary/${service}/${registeredName}${urlSuffix}`
if (identifier?.trim().length > 0) { if (identifier?.trim().length > 0) {
uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}${urlSuffix}` uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}`;
} }
uploadDataUrl = uploadDataUrl + `?fee=${fee}` paramQueries = paramQueries + `?fee=${fee}`;
if (filename != null && filename != 'undefined') { if (filename != null && filename != 'undefined') {
uploadDataUrl = uploadDataUrl + '&filename=' + encodeURIComponent(filename) paramQueries = paramQueries + '&filename=' + encodeURIComponent(filename);
} }
if (title != null && title != 'undefined') { if (title != null && title != 'undefined') {
uploadDataUrl = uploadDataUrl + '&title=' + encodeURIComponent(title) paramQueries = paramQueries + '&title=' + encodeURIComponent(title);
} }
if (description != null && description != 'undefined') { if (description != null && description != 'undefined') {
uploadDataUrl = uploadDataUrl + '&description=' + encodeURIComponent(description) paramQueries =
paramQueries + '&description=' + encodeURIComponent(description);
} }
if (category != null && category != 'undefined') { if (category != null && category != 'undefined') {
uploadDataUrl = uploadDataUrl + '&category=' + encodeURIComponent(category) paramQueries = paramQueries + '&category=' + encodeURIComponent(category);
} }
if (tag1 != null && tag1 != 'undefined') { if (tag1 != null && tag1 != 'undefined') {
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag1) paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag1);
} }
if (tag2 != null && tag2 != 'undefined') { if (tag2 != null && tag2 != 'undefined') {
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag2) paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag2);
} }
if (tag3 != null && tag3 != 'undefined') { if (tag3 != null && tag3 != 'undefined') {
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag3) paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag3);
} }
if (tag4 != null && tag4 != 'undefined') { if (tag4 != null && tag4 != 'undefined') {
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag4) paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag4);
} }
if (tag5 != null && tag5 != 'undefined') { if (tag5 != null && tag5 != 'undefined') {
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag5) paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag5);
}
if (uploadType === 'zip') {
paramQueries = paramQueries + '&isZip=' + true;
} }
return await reusablePost(uploadDataUrl, postBody) if (uploadType === 'base64') {
if (urlSuffix) {
uploadDataUrl = uploadDataUrl + urlSuffix;
} }
uploadDataUrl = uploadDataUrl + paramQueries;
return await reusablePost(uploadDataUrl, postBody);
}
const file = data;
const urlCheck = `/arbitrary/check-tmp-space?totalSize=${file.size}`;
const checkEndpoint = await createEndpoint(urlCheck);
const checkRes = await fetch(checkEndpoint);
if (!checkRes.ok) {
throw new Error('Not enough space on your hard drive');
}
const chunkUrl = uploadDataUrl + `/chunk`;
const chunkSize = 5 * 1024 * 1024; // 5MB
const totalChunks = Math.ceil(file.size / chunkSize);
for (let index = 0; index < totalChunks; index++) {
const start = index * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('chunk', chunk, file.name); // Optional: include filename
formData.append('index', index);
await uploadChunkWithRetry(chunkUrl, formData, index);
}
const finalizeUrl = uploadDataUrl + `/finalize` + paramQueries;
const finalizeEndpoint = await createEndpoint(finalizeUrl);
const response = await fetch(finalizeEndpoint, {
method: 'POST',
headers: {},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Finalize failed: ${errorText}`);
}
const result = await response.text(); // Base58-encoded unsigned transaction
return result;
};
try { try {
return await validate() return await validate();
} catch (error: any) { } catch (error: any) {
throw new Error(error?.message) throw new Error(error?.message);
}
} }
};

View File

@ -1076,7 +1076,7 @@ export const publishQDNResource = async (
const title = data.title; const title = data.title;
const description = data.description; const description = data.description;
const category = data.category; const category = data.category;
const file = data?.file || data?.blob;
const tags = data?.tags || []; const tags = data?.tags || [];
const result = {}; const result = {};
@ -1091,9 +1091,7 @@ export const publishQDNResource = async (
if (data.identifier == null) { if (data.identifier == null) {
identifier = 'default'; identifier = 'default';
} }
if (data?.file || data?.blob) {
data64 = await fileToBase64(data?.file || data?.blob);
}
if ( if (
data.encrypt && data.encrypt &&
(!data.publicKeys || (!data.publicKeys ||
@ -1108,6 +1106,9 @@ export const publishQDNResource = async (
const parsedData = resKeyPair; const parsedData = resKeyPair;
const privateKey = parsedData.privateKey; const privateKey = parsedData.privateKey;
const userPublicKey = parsedData.publicKey; const userPublicKey = parsedData.publicKey;
if (data?.file || data?.blob) {
data64 = await fileToBase64(data?.file || data?.blob);
}
const encryptDataResponse = encryptDataGroup({ const encryptDataResponse = encryptDataGroup({
data64, data64,
publicKeys: data.publicKeys, publicKeys: data.publicKeys,
@ -1154,11 +1155,10 @@ export const publishQDNResource = async (
try { try {
const resPublish = await publishData({ const resPublish = await publishData({
registeredName: encodeURIComponent(name), registeredName: encodeURIComponent(name),
file: data64, data: data64 ? data64 : file,
service: service, service: service,
identifier: encodeURIComponent(identifier), identifier: encodeURIComponent(identifier),
uploadType: 'file', uploadType: data64 ? 'base64' : 'file',
isBase64: true,
filename: filename, filename: filename,
title, title,
description, description,
@ -1263,13 +1263,6 @@ export const publishMultipleQDNResources = async (
} }
} }
// if (
// data.encrypt &&
// (!data.publicKeys ||
// (Array.isArray(data.publicKeys) && data.publicKeys.length === 0))
// ) {
// throw new Error("Encrypting data requires public keys");
// }
const fee = await getFee('ARBITRARY'); const fee = await getFee('ARBITRARY');
const registeredName = await getNameInfo(); const registeredName = await getNameInfo();
const name = registeredName; const name = registeredName;
@ -1398,14 +1391,13 @@ export const publishMultipleQDNResources = async (
} }
const service = resource.service; const service = resource.service;
let identifier = resource.identifier; let identifier = resource.identifier;
let data64 = resource?.data64 || resource?.base64; let rawData = resource?.data64 || resource?.base64;
const filename = resource.filename; const filename = resource.filename;
const title = resource.title; const title = resource.title;
const description = resource.description; const description = resource.description;
const category = resource.category; const category = resource.category;
const tags = resource?.tags || []; const tags = resource?.tags || [];
const result = {}; const result = {};
// Fill tags dynamically while maintaining backward compatibility // Fill tags dynamically while maintaining backward compatibility
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
result[`tag${i + 1}`] = tags[i] || resource[`tag${i + 1}`] || undefined; result[`tag${i + 1}`] = tags[i] || resource[`tag${i + 1}`] || undefined;
@ -1427,22 +1419,27 @@ export const publishMultipleQDNResources = async (
continue; continue;
} }
if (resource.file) { if (resource.file) {
data64 = await fileToBase64(resource.file); rawData = resource.file;
} }
if (resourceEncrypt) { if (resourceEncrypt) {
try { try {
if (resource?.file) {
rawData = await fileToBase64(resource.file);
}
console.log('encrypteddata', rawData);
const resKeyPair = await getKeyPair(); const resKeyPair = await getKeyPair();
const parsedData = resKeyPair; const parsedData = resKeyPair;
const privateKey = parsedData.privateKey; const privateKey = parsedData.privateKey;
const userPublicKey = parsedData.publicKey; const userPublicKey = parsedData.publicKey;
const encryptDataResponse = encryptDataGroup({ const encryptDataResponse = encryptDataGroup({
data64, data64: rawData,
publicKeys: data.publicKeys, publicKeys: data.publicKeys,
privateKey, privateKey,
userPublicKey, userPublicKey,
}); });
if (encryptDataResponse) { if (encryptDataResponse) {
data64 = encryptDataResponse; rawData = encryptDataResponse;
} }
} catch (error) { } catch (error) {
const errorMsg = const errorMsg =
@ -1457,16 +1454,21 @@ export const publishMultipleQDNResources = async (
} }
try { try {
const dataType =
resource?.base64 || resource?.data64 || resourceEncrypt
? 'base64'
: 'file';
console.log('dataType', dataType);
await retryTransaction( await retryTransaction(
publishData, publishData,
[ [
{ {
registeredName: encodeURIComponent(name), registeredName: encodeURIComponent(name),
file: data64, data: rawData,
service: service, service: service,
identifier: encodeURIComponent(identifier), identifier: encodeURIComponent(identifier),
uploadType: 'file', uploadType: dataType,
isBase64: true, // isBase64: true,
filename: filename, filename: filename,
title, title,
description, description,
@ -1902,6 +1904,41 @@ export const joinGroup = async (data, isFromExtension) => {
export const saveFile = async (data, sender, isFromExtension, snackMethods) => { export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
try { try {
if (data?.location) {
const requiredFieldsLocation = ['service', 'name', 'filename'];
const missingFieldsLocation: string[] = [];
requiredFieldsLocation.forEach((field) => {
if (!data?.location[field]) {
missingFieldsLocation.push(field);
}
});
if (missingFieldsLocation.length > 0) {
const missingFieldsString = missingFieldsLocation.join(', ');
const errorMsg = `Missing fields: ${missingFieldsString}`;
throw new Error(errorMsg);
}
const resPermission = await getUserPermission(
{
text1: 'Would you like to download:',
highlightedText: `${data?.location?.filename}`,
},
isFromExtension
);
const { accepted } = resPermission;
if (!accepted) throw new Error('User declined to save file');
const a = document.createElement('a');
let locationUrl = `/arbitrary/${data.location.service}/${data.location.name}`;
if (data.location.identifier) {
locationUrl = locationUrl + `/${data.location.identifier}`;
}
const endpoint = await createEndpoint(locationUrl);
a.href = endpoint;
a.download = data.location.filename;
document.body.appendChild(a);
a.click();
a.remove();
return true;
}
const requiredFields = ['filename', 'blob']; const requiredFields = ['filename', 'blob'];
const missingFields: string[] = []; const missingFields: string[] = [];
requiredFields.forEach((field) => { requiredFields.forEach((field) => {
@ -1916,6 +1953,8 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
} }
const filename = data.filename; const filename = data.filename;
const blob = data.blob; const blob = data.blob;
const mimeType = blob.type || data.mimeType;
const resPermission = await getUserPermission( const resPermission = await getUserPermission(
{ {
text1: 'Would you like to download:', text1: 'Would you like to download:',
@ -1924,30 +1963,7 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
isFromExtension isFromExtension
); );
const { accepted } = resPermission; const { accepted } = resPermission;
if (!accepted) throw new Error('User declined to save file');
if (accepted) {
const mimeType = blob.type || data.mimeType;
let backupExention = filename.split('.').pop();
if (backupExention) {
backupExention = '.' + backupExention;
}
const fileExtension = mimeToExtensionMap[mimeType] || backupExention;
let fileHandleOptions = {};
if (!mimeType) {
throw new Error('A mimeType could not be derived');
}
if (!fileExtension) {
const obj = {};
throw new Error('A file extension could not be derived');
}
if (fileExtension && mimeType) {
fileHandleOptions = {
accept: {
[mimeType]: [fileExtension],
},
};
}
showSaveFilePicker( showSaveFilePicker(
{ {
filename, filename,
@ -1956,18 +1972,8 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
}, },
snackMethods snackMethods
); );
// sendToSaveFilePicker(
// {
// filename,
// mimeType,
// blob,
// fileId
// }
// );
return true; return true;
} else {
throw new Error('User declined to save file');
}
} catch (error) { } catch (error) {
throw new Error(error?.message || 'Failed to initiate download'); throw new Error(error?.message || 'Failed to initiate download');
} }
@ -5391,12 +5397,11 @@ export const multiPaymentWithPrivateData = async (data, isFromExtension) => {
[ [
{ {
registeredName: encodeURIComponent(name), registeredName: encodeURIComponent(name),
file: encryptDataResponse, data: encryptDataResponse,
service: transaction.service, service: transaction.service,
identifier: encodeURIComponent(transaction.identifier), identifier: encodeURIComponent(transaction.identifier),
uploadType: 'file', uploadType: 'base64',
description: transaction?.description, description: transaction?.description,
isBase64: true,
apiVersion: 2, apiVersion: 2,
withFee: true, withFee: true,
}, },
@ -5443,12 +5448,11 @@ export const multiPaymentWithPrivateData = async (data, isFromExtension) => {
[ [
{ {
registeredName: encodeURIComponent(name), registeredName: encodeURIComponent(name),
file: encryptDataResponse, data: encryptDataResponse,
service: transaction.service, service: transaction.service,
identifier: encodeURIComponent(transaction.identifier), identifier: encodeURIComponent(transaction.identifier),
uploadType: 'file', uploadType: 'base64',
description: transaction?.description, description: transaction?.description,
isBase64: true,
apiVersion: 2, apiVersion: 2,
withFee: true, withFee: true,
}, },