mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-05-17 15:16:59 +00:00
initial
This commit is contained in:
parent
dccc7d311f
commit
13a77762b6
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
@ -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' ||
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -1,266 +1,369 @@
|
|||||||
// @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
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
description,
|
||||||
description,
|
category,
|
||||||
category,
|
tag1,
|
||||||
tag1,
|
tag2,
|
||||||
tag2,
|
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×tamp=${timestamp}`)
|
let fee = await reusableGet(
|
||||||
|
`/transactions/unitfee?txType=ARBITRARY×tamp=${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 = (
|
||||||
if (!arbitraryBytesBase58) {
|
arbitraryBytesBase58,
|
||||||
throw new Error('ArbitraryBytesBase58 not defined')
|
arbitraryBytesForSigningBase58,
|
||||||
}
|
keyPair
|
||||||
|
) => {
|
||||||
if (!keyPair) {
|
if (!arbitraryBytesBase58) {
|
||||||
throw new Error('keyPair not defined')
|
throw new Error('ArbitraryBytesBase58 not defined');
|
||||||
}
|
|
||||||
|
|
||||||
const arbitraryBytes = Base58.decode(arbitraryBytesBase58)
|
|
||||||
const _arbitraryBytesBuffer = Object.keys(arbitraryBytes).map(function (key) { return arbitraryBytes[key]; })
|
|
||||||
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) => {
|
if (!keyPair) {
|
||||||
|
throw new Error('keyPair not defined');
|
||||||
|
}
|
||||||
|
|
||||||
return await reusablePost('/transactions/process?apiVersion=2', Base58.encode(bytes))
|
const arbitraryBytes = Base58.decode(arbitraryBytesBase58);
|
||||||
}
|
const _arbitraryBytesBuffer = Object.keys(arbitraryBytes).map(
|
||||||
|
function (key) {
|
||||||
|
return arbitraryBytes[key];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
const signAndProcessWithFee = async (transactionBytesBase58: string) => {
|
return utils.appendBuffer(arbitraryBytesBuffer, signature);
|
||||||
let convertedBytesBase58 = await convertBytesForSigning(
|
};
|
||||||
transactionBytesBase58
|
|
||||||
)
|
|
||||||
|
|
||||||
|
const processTransactionVersion2 = async (bytes) => {
|
||||||
if (convertedBytesBase58.error) {
|
return await reusablePost(
|
||||||
throw new Error('Error when signing')
|
'/transactions/process?apiVersion=2',
|
||||||
}
|
Base58.encode(bytes)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const signAndProcessWithFee = async (transactionBytesBase58: string) => {
|
||||||
|
let convertedBytesBase58 = await convertBytesForSigning(
|
||||||
|
transactionBytesBase58
|
||||||
|
);
|
||||||
|
|
||||||
const resKeyPair = await getKeyPair()
|
if (convertedBytesBase58.error) {
|
||||||
const parsedData = resKeyPair
|
throw new Error('Error when signing');
|
||||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
}
|
||||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
|
||||||
const keyPair = {
|
|
||||||
privateKey: uint8PrivateKey,
|
|
||||||
publicKey: uint8PublicKey
|
|
||||||
};
|
|
||||||
|
|
||||||
let signedArbitraryBytes = signArbitraryWithFee(transactionBytesBase58, convertedBytesBase58, keyPair)
|
const resKeyPair = await getKeyPair();
|
||||||
const response = await processTransactionVersion2(signedArbitraryBytes)
|
const parsedData = resKeyPair;
|
||||||
|
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
||||||
|
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
||||||
|
const keyPair = {
|
||||||
|
privateKey: uint8PrivateKey,
|
||||||
|
publicKey: uint8PublicKey,
|
||||||
|
};
|
||||||
|
|
||||||
let myResponse = { error: '' }
|
let signedArbitraryBytes = signArbitraryWithFee(
|
||||||
|
transactionBytesBase58,
|
||||||
|
convertedBytesBase58,
|
||||||
|
keyPair
|
||||||
|
);
|
||||||
|
const response = await processTransactionVersion2(signedArbitraryBytes);
|
||||||
|
|
||||||
if (response === false) {
|
let myResponse = { error: '' };
|
||||||
throw new Error('Error when signing')
|
|
||||||
} else {
|
|
||||||
myResponse = response
|
|
||||||
}
|
|
||||||
|
|
||||||
return myResponse
|
if (response === false) {
|
||||||
}
|
throw new Error('Error when signing');
|
||||||
|
} else {
|
||||||
|
myResponse = response;
|
||||||
|
}
|
||||||
|
|
||||||
const validate = async () => {
|
return myResponse;
|
||||||
let validNameRes = await validateName(registeredName)
|
};
|
||||||
|
|
||||||
if (validNameRes.error) {
|
const validate = async () => {
|
||||||
throw new Error('Name not found')
|
let validNameRes = await validateName(registeredName);
|
||||||
}
|
|
||||||
|
|
||||||
let fee = null
|
if (validNameRes.error) {
|
||||||
|
throw new Error('Name not found');
|
||||||
|
}
|
||||||
|
|
||||||
if (withFee && feeAmount) {
|
let fee = null;
|
||||||
fee = feeAmount
|
|
||||||
} else if (withFee) {
|
|
||||||
const res = await getArbitraryFee()
|
|
||||||
if (res.fee) {
|
|
||||||
fee = res.fee
|
|
||||||
} else {
|
|
||||||
throw new Error('unable to get fee')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let transactionBytes = await uploadData(registeredName, file, fee)
|
|
||||||
if (!transactionBytes || transactionBytes.error) {
|
|
||||||
throw new Error(transactionBytes?.message || 'Error when uploading')
|
|
||||||
} else if (transactionBytes.includes('Error 500 Internal Server Error')) {
|
|
||||||
throw new Error('Error when uploading')
|
|
||||||
}
|
|
||||||
|
|
||||||
let signAndProcessRes
|
if (withFee && feeAmount) {
|
||||||
|
fee = feeAmount;
|
||||||
|
} else if (withFee) {
|
||||||
|
const res = await getArbitraryFee();
|
||||||
|
if (res.fee) {
|
||||||
|
fee = res.fee;
|
||||||
|
} else {
|
||||||
|
throw new Error('unable to get fee');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (withFee) {
|
let transactionBytes = await uploadData(registeredName, data, fee);
|
||||||
signAndProcessRes = await signAndProcessWithFee(transactionBytes)
|
console.log('transactionBytes length', transactionBytes?.length);
|
||||||
}
|
if (!transactionBytes || transactionBytes.error) {
|
||||||
|
throw new Error(transactionBytes?.message || 'Error when uploading');
|
||||||
|
} else if (transactionBytes.includes('Error 500 Internal Server Error')) {
|
||||||
|
throw new Error('Error when uploading');
|
||||||
|
}
|
||||||
|
|
||||||
if (signAndProcessRes?.error) {
|
let signAndProcessRes;
|
||||||
throw new Error('Error when signing')
|
|
||||||
}
|
|
||||||
|
|
||||||
return signAndProcessRes
|
if (withFee) {
|
||||||
}
|
signAndProcessRes = await signAndProcessWithFee(transactionBytes);
|
||||||
|
}
|
||||||
|
|
||||||
const uploadData = async (registeredName: string, file:any, fee: number) => {
|
if (signAndProcessRes?.error) {
|
||||||
|
throw new Error('Error when signing');
|
||||||
|
}
|
||||||
|
|
||||||
let postBody = ''
|
return signAndProcessRes;
|
||||||
let urlSuffix = ''
|
};
|
||||||
|
|
||||||
if (file != null) {
|
const uploadData = async (registeredName: string, data: any, fee: number) => {
|
||||||
// If we're sending zipped data, make sure to use the /zip version of the POST /arbitrary/* API
|
console.log('data', uploadType, data);
|
||||||
if (uploadType === 'zip') {
|
let postBody = '';
|
||||||
urlSuffix = '/zip'
|
let urlSuffix = '';
|
||||||
}
|
|
||||||
|
|
||||||
// If we're sending file data, use the /base64 version of the POST /arbitrary/* API
|
if (data != null) {
|
||||||
else if (uploadType === 'file') {
|
if (uploadType === 'base64') {
|
||||||
urlSuffix = '/base64'
|
urlSuffix = '/base64';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base64 encode the file to work around compatibility issues between javascript and java byte arrays
|
if (uploadType === 'base64') {
|
||||||
if (isBase64) {
|
postBody = data;
|
||||||
postBody = file
|
}
|
||||||
}
|
} else {
|
||||||
|
throw new Error('No data provided');
|
||||||
|
}
|
||||||
|
|
||||||
if (!isBase64) {
|
let uploadDataUrl = `/arbitrary/${service}/${registeredName}`;
|
||||||
let fileBuffer = new Uint8Array(await file.arrayBuffer())
|
let paramQueries = '';
|
||||||
postBody = Buffer.from(fileBuffer).toString("base64")
|
if (identifier?.trim().length > 0) {
|
||||||
}
|
uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}`;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
paramQueries = paramQueries + `?fee=${fee}`;
|
||||||
|
|
||||||
let uploadDataUrl = `/arbitrary/${service}/${registeredName}${urlSuffix}`
|
|
||||||
if (identifier?.trim().length > 0) {
|
|
||||||
uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}${urlSuffix}`
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadDataUrl = uploadDataUrl + `?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);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
const file = data;
|
||||||
return await validate()
|
const urlCheck = `/arbitrary/check-tmp-space?totalSize=${file.size}`;
|
||||||
} catch (error: any) {
|
|
||||||
throw new Error(error?.message)
|
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 {
|
||||||
|
return await validate();
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(error?.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -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,50 +1963,17 @@ 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');
|
||||||
|
showSaveFilePicker(
|
||||||
|
{
|
||||||
|
filename,
|
||||||
|
mimeType,
|
||||||
|
blob,
|
||||||
|
},
|
||||||
|
snackMethods
|
||||||
|
);
|
||||||
|
|
||||||
if (accepted) {
|
return true;
|
||||||
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(
|
|
||||||
{
|
|
||||||
filename,
|
|
||||||
mimeType,
|
|
||||||
blob,
|
|
||||||
},
|
|
||||||
snackMethods
|
|
||||||
);
|
|
||||||
// sendToSaveFilePicker(
|
|
||||||
// {
|
|
||||||
// filename,
|
|
||||||
// mimeType,
|
|
||||||
// blob,
|
|
||||||
// fileId
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
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,
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user