forked from Qortal/qortal-ui
Merge pull request #157 from Philreact/feature/encrypt-decrypt-data-qapp
added two events for encryption
This commit is contained in:
commit
e6b770ff0e
@ -645,7 +645,9 @@
|
|||||||
"bchange41": "Do you give this application permission to access this list?",
|
"bchange41": "Do you give this application permission to access this list?",
|
||||||
"bchange42": "Items",
|
"bchange42": "Items",
|
||||||
"bchange43": "Do you give this application permission to add to this list?",
|
"bchange43": "Do you give this application permission to add to this list?",
|
||||||
"bchange44": "Do you give this application permission to delete from this list?"
|
"bchange44": "Do you give this application permission to delete from this list?",
|
||||||
|
"bchange45": "Encrypt",
|
||||||
|
"bchange46": "Do you give this application permission to save the following file"
|
||||||
},
|
},
|
||||||
"datapage": {
|
"datapage": {
|
||||||
"dchange1": "Data Management",
|
"dchange1": "Data Management",
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
export const mimeToExtensionMap = {
|
||||||
|
// Documents
|
||||||
|
"application/pdf": ".pdf",
|
||||||
|
"application/msword": ".doc",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
|
||||||
|
"application/vnd.ms-excel": ".xls",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
||||||
|
"application/vnd.ms-powerpoint": ".ppt",
|
||||||
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
|
||||||
|
"application/vnd.oasis.opendocument.text": ".odt",
|
||||||
|
"application/vnd.oasis.opendocument.spreadsheet": ".ods",
|
||||||
|
"application/vnd.oasis.opendocument.presentation": ".odp",
|
||||||
|
"text/plain": ".txt",
|
||||||
|
"text/csv": ".csv",
|
||||||
|
"text/html": ".html",
|
||||||
|
"application/xhtml+xml": ".xhtml",
|
||||||
|
"application/xml": ".xml",
|
||||||
|
"application/json": ".json",
|
||||||
|
|
||||||
|
// Images
|
||||||
|
"image/jpeg": ".jpg",
|
||||||
|
"image/png": ".png",
|
||||||
|
"image/gif": ".gif",
|
||||||
|
"image/webp": ".webp",
|
||||||
|
"image/svg+xml": ".svg",
|
||||||
|
"image/tiff": ".tif",
|
||||||
|
"image/bmp": ".bmp",
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
"audio/mpeg": ".mp3",
|
||||||
|
"audio/ogg": ".ogg",
|
||||||
|
"audio/wav": ".wav",
|
||||||
|
"audio/webm": ".weba",
|
||||||
|
"audio/aac": ".aac",
|
||||||
|
|
||||||
|
// Video
|
||||||
|
"video/mp4": ".mp4",
|
||||||
|
"video/webm": ".webm",
|
||||||
|
"video/ogg": ".ogv",
|
||||||
|
"video/x-msvideo": ".avi",
|
||||||
|
"video/quicktime": ".mov",
|
||||||
|
"video/x-ms-wmv": ".wmv",
|
||||||
|
"video/mpeg": ".mpeg",
|
||||||
|
"video/3gpp": ".3gp",
|
||||||
|
"video/3gpp2": ".3g2",
|
||||||
|
"video/x-matroska": ".mkv",
|
||||||
|
"video/x-flv": ".flv",
|
||||||
|
|
||||||
|
// Archives
|
||||||
|
"application/zip": ".zip",
|
||||||
|
"application/x-rar-compressed": ".rar",
|
||||||
|
"application/x-tar": ".tar",
|
||||||
|
"application/x-7z-compressed": ".7z",
|
||||||
|
"application/x-gzip": ".gz",
|
||||||
|
"application/x-bzip2": ".bz2",
|
||||||
|
};
|
@ -0,0 +1,84 @@
|
|||||||
|
import nacl from '../../../../qortal-ui-crypto/api/deps/nacl-fast.js'
|
||||||
|
import ed2curve from '../../../../qortal-ui-crypto/api/deps/ed2curve.js'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function uint8ArrayToBase64(uint8Array) {
|
||||||
|
const length = uint8Array.length;
|
||||||
|
let base64String = '';
|
||||||
|
const chunkSize = 1024 * 1024; // Process 1MB at a time
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i += chunkSize) {
|
||||||
|
const chunkEnd = Math.min(i + chunkSize, length);
|
||||||
|
const chunk = uint8Array.subarray(i, chunkEnd);
|
||||||
|
const binaryString = chunk.reduce((acc, byte) => acc + String.fromCharCode(byte), '');
|
||||||
|
base64String += btoa(binaryString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64String;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function base64ToUint8Array(base64) {
|
||||||
|
const binaryString = atob(base64)
|
||||||
|
const len = binaryString.length
|
||||||
|
const bytes = new Uint8Array(len)
|
||||||
|
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
bytes[i] = binaryString.charCodeAt(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const encryptData = ({ data64, recipientPublicKey }) => {
|
||||||
|
|
||||||
|
|
||||||
|
const Uint8ArrayData = base64ToUint8Array(data64)
|
||||||
|
const uint8Array = Uint8ArrayData
|
||||||
|
|
||||||
|
if (!(uint8Array instanceof Uint8Array)) {
|
||||||
|
|
||||||
|
throw new Error("The Uint8ArrayData you've submitted is invalid")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const privateKey = window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey
|
||||||
|
if (!privateKey) {
|
||||||
|
|
||||||
|
throw new Error("Unable to retrieve keys")
|
||||||
|
}
|
||||||
|
const publicKeyUnit8Array = window.parent.Base58.decode(recipientPublicKey)
|
||||||
|
|
||||||
|
const convertedPrivateKey = ed2curve.convertSecretKey(privateKey)
|
||||||
|
const convertedPublicKey = ed2curve.convertPublicKey(publicKeyUnit8Array)
|
||||||
|
const sharedSecret = new Uint8Array(32)
|
||||||
|
nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey)
|
||||||
|
|
||||||
|
const chatEncryptionSeed = new window.parent.Sha256().process(sharedSecret).finish().result
|
||||||
|
|
||||||
|
const nonce = new Uint8Array(24);
|
||||||
|
window.crypto.getRandomValues(nonce);
|
||||||
|
const encryptedData = nacl.secretbox(uint8Array, nonce, chatEncryptionSeed)
|
||||||
|
|
||||||
|
const str = "qortalEncryptedData";
|
||||||
|
const strEncoder = new TextEncoder();
|
||||||
|
const strUint8Array = strEncoder.encode(str);
|
||||||
|
|
||||||
|
const combinedData = new Uint8Array(strUint8Array.length + nonce.length + encryptedData.length);
|
||||||
|
|
||||||
|
combinedData.set(strUint8Array);
|
||||||
|
|
||||||
|
combinedData.set(nonce, strUint8Array.length);
|
||||||
|
combinedData.set(encryptedData, strUint8Array.length + nonce.length);
|
||||||
|
|
||||||
|
const uint8arrayToData64 = uint8ArrayToBase64(combinedData)
|
||||||
|
|
||||||
|
return {
|
||||||
|
encryptedData: uint8arrayToData64,
|
||||||
|
recipientPublicKey
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log({ error })
|
||||||
|
throw new Error("Error in encrypting data")
|
||||||
|
}
|
||||||
|
}
|
@ -35,4 +35,13 @@ export const GET_LIST_ITEMS = 'GET_LIST_ITEMS'
|
|||||||
export const ADD_LIST_ITEMS = 'ADD_LIST_ITEMS'
|
export const ADD_LIST_ITEMS = 'ADD_LIST_ITEMS'
|
||||||
|
|
||||||
// DELETE_LIST_ITEM
|
// DELETE_LIST_ITEM
|
||||||
export const DELETE_LIST_ITEM = 'DELETE_LIST_ITEM'
|
export const DELETE_LIST_ITEM = 'DELETE_LIST_ITEM'
|
||||||
|
|
||||||
|
// ENCRYPT_DATA
|
||||||
|
export const ENCRYPT_DATA = 'ENCRYPT_DATA'
|
||||||
|
|
||||||
|
// DECRYPT_DATA
|
||||||
|
export const DECRYPT_DATA = 'DECRYPT_DATA'
|
||||||
|
|
||||||
|
// SAVE_FILE
|
||||||
|
export const SAVE_FILE = 'SAVE_FILE'
|
@ -8,11 +8,11 @@ import {
|
|||||||
translateUnsafeHTML,
|
translateUnsafeHTML,
|
||||||
registerTranslateConfig,
|
registerTranslateConfig,
|
||||||
} from 'lit-translate';
|
} from 'lit-translate';
|
||||||
import * as actions from '../../components/qdn-action-types';
|
|
||||||
registerTranslateConfig({
|
registerTranslateConfig({
|
||||||
loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json()),
|
loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json()),
|
||||||
});
|
});
|
||||||
|
import FileSaver from 'file-saver'
|
||||||
|
import * as actions from '../../components/qdn-action-types';
|
||||||
import '@material/mwc-button';
|
import '@material/mwc-button';
|
||||||
import '@material/mwc-icon';
|
import '@material/mwc-icon';
|
||||||
import '@material/mwc-checkbox'
|
import '@material/mwc-checkbox'
|
||||||
@ -21,6 +21,10 @@ import WebWorkerChat from 'web-worker:./computePowWorker.src.js';
|
|||||||
import { publishData } from '../../../utils/publish-image.js';
|
import { publishData } from '../../../utils/publish-image.js';
|
||||||
import { Loader } from '../../../utils/loader.js';
|
import { Loader } from '../../../utils/loader.js';
|
||||||
import { QORT_DECIMALS } from 'qortal-ui-crypto/api/constants';
|
import { QORT_DECIMALS } from 'qortal-ui-crypto/api/constants';
|
||||||
|
import nacl from '../../../../../qortal-ui-crypto/api/deps/nacl-fast.js'
|
||||||
|
import ed2curve from '../../../../../qortal-ui-crypto/api/deps/ed2curve.js'
|
||||||
|
import { mimeToExtensionMap } from '../../components/qdn-action-constants';
|
||||||
|
import { base64ToUint8Array, encryptData, uint8ArrayToBase64 } from '../../components/qdn-action-encryption';
|
||||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent });
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent });
|
||||||
|
|
||||||
class WebBrowser extends LitElement {
|
class WebBrowser extends LitElement {
|
||||||
@ -148,8 +152,7 @@ class WebBrowser extends LitElement {
|
|||||||
if (
|
if (
|
||||||
this.identifier && this.identifier != 'null' &&
|
this.identifier && this.identifier != 'null' &&
|
||||||
this.identifier != 'default'
|
this.identifier != 'default'
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
displayUrl = displayUrl.concat('/' + this.identifier);
|
displayUrl = displayUrl.concat('/' + this.identifier);
|
||||||
}
|
}
|
||||||
if (this.path != null && this.path != '/')
|
if (this.path != null && this.path != '/')
|
||||||
@ -200,7 +203,7 @@ class WebBrowser extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedAddress = {}
|
this.selectedAddress = {}
|
||||||
this.btcFeePerByte = 100
|
this.btcFeePerByte = 100
|
||||||
this.ltcFeePerByte = 30
|
this.ltcFeePerByte = 30
|
||||||
this.dogeFeePerByte = 1000
|
this.dogeFeePerByte = 1000
|
||||||
@ -268,7 +271,7 @@ class WebBrowser extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFullScreen() {
|
renderFullScreen() {
|
||||||
if (window.innerHeight == screen.height) {
|
if (window.innerHeight == screen.height) {
|
||||||
return html`
|
return html`
|
||||||
<mwc-button
|
<mwc-button
|
||||||
@ -290,7 +293,7 @@ class WebBrowser extends LitElement {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goFullScreen() {
|
goFullScreen() {
|
||||||
var elem = this.shadowRoot.getElementById('websitesWrapper')
|
var elem = this.shadowRoot.getElementById('websitesWrapper')
|
||||||
@ -309,7 +312,7 @@ class WebBrowser extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exitFullScreen() {
|
exitFullScreen() {
|
||||||
if(document.exitFullscreen) {
|
if (document.exitFullscreen) {
|
||||||
document.exitFullscreen()
|
document.exitFullscreen()
|
||||||
} else if (document.mozCancelFullScreen) {
|
} else if (document.mozCancelFullScreen) {
|
||||||
document.mozCancelFullScreen()
|
document.mozCancelFullScreen()
|
||||||
@ -535,7 +538,7 @@ class WebBrowser extends LitElement {
|
|||||||
}
|
}
|
||||||
let res1;
|
let res1;
|
||||||
if (!skip) {
|
if (!skip) {
|
||||||
res1 = await showModalAndWait(
|
res1 = await showModalAndWait(
|
||||||
actions.GET_USER_ACCOUNT,
|
actions.GET_USER_ACCOUNT,
|
||||||
{
|
{
|
||||||
service: this.service,
|
service: this.service,
|
||||||
@ -558,6 +561,68 @@ class WebBrowser extends LitElement {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case actions.DECRYPT_DATA: {
|
||||||
|
const requiredFields = ['encryptedData', 'publicKey'];
|
||||||
|
const missingFields = [];
|
||||||
|
|
||||||
|
requiredFields.forEach((field) => {
|
||||||
|
if (!data[field]) {
|
||||||
|
missingFields.push(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const missingFieldsString = missingFields.join(', ');
|
||||||
|
const errorMsg = `Missing fields: ${missingFieldsString}`
|
||||||
|
let data = {};
|
||||||
|
data['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(data);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
const { encryptedData, publicKey } = data
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
const uint8Array = base64ToUint8Array(encryptedData)
|
||||||
|
const combinedData = uint8Array
|
||||||
|
const str = "qortalEncryptedData";
|
||||||
|
const strEncoder = new TextEncoder();
|
||||||
|
const strUint8Array = strEncoder.encode(str);
|
||||||
|
|
||||||
|
const strData = combinedData.slice(0, strUint8Array.length);
|
||||||
|
const nonce = combinedData.slice(strUint8Array.length, strUint8Array.length + 24);
|
||||||
|
const _encryptedData = combinedData.slice(strUint8Array.length + 24);
|
||||||
|
|
||||||
|
const privateKey = window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey
|
||||||
|
const _publicKey = window.parent.Base58.decode(publicKey)
|
||||||
|
|
||||||
|
if (!privateKey || !_publicKey) {
|
||||||
|
data['error'] = "Unable to retrieve keys"
|
||||||
|
response = JSON.stringify(data);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertedPrivateKey = ed2curve.convertSecretKey(privateKey)
|
||||||
|
const convertedPublicKey = ed2curve.convertPublicKey(_publicKey)
|
||||||
|
const sharedSecret = new Uint8Array(32);
|
||||||
|
nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey)
|
||||||
|
|
||||||
|
const _chatEncryptionSeed = new window.parent.Sha256().process(sharedSecret).finish().result
|
||||||
|
const _decryptedData = nacl.secretbox.open(_encryptedData, nonce, _chatEncryptionSeed)
|
||||||
|
const decryptedDataToBase64 = uint8ArrayToBase64(_decryptedData)
|
||||||
|
response = JSON.stringify(decryptedDataToBase64);
|
||||||
|
|
||||||
|
break;
|
||||||
|
} catch (error) {
|
||||||
|
console.log({ error })
|
||||||
|
const data = {};
|
||||||
|
const errorMsg = error.message || "Error in decrypting data"
|
||||||
|
data['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(data);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
case actions.GET_LIST_ITEMS: {
|
case actions.GET_LIST_ITEMS: {
|
||||||
const requiredFields = ['list_name'];
|
const requiredFields = ['list_name'];
|
||||||
const missingFields = [];
|
const missingFields = [];
|
||||||
@ -775,6 +840,7 @@ class WebBrowser extends LitElement {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case actions.PUBLISH_QDN_RESOURCE: {
|
case actions.PUBLISH_QDN_RESOURCE: {
|
||||||
|
// optional fields: encrypt:boolean recipientPublicKey:string
|
||||||
const requiredFields = ['service', 'name', 'data64'];
|
const requiredFields = ['service', 'name', 'data64'];
|
||||||
const missingFields = [];
|
const missingFields = [];
|
||||||
|
|
||||||
@ -796,7 +862,7 @@ class WebBrowser extends LitElement {
|
|||||||
const service = data.service;
|
const service = data.service;
|
||||||
const name = data.name;
|
const name = data.name;
|
||||||
let identifier = data.identifier;
|
let identifier = data.identifier;
|
||||||
const data64 = data.data64;
|
let data64 = data.data64;
|
||||||
const filename = data.filename;
|
const filename = data.filename;
|
||||||
const title = data.title;
|
const title = data.title;
|
||||||
const description = data.description;
|
const description = data.description;
|
||||||
@ -809,12 +875,45 @@ class WebBrowser extends LitElement {
|
|||||||
if (data.identifier == null) {
|
if (data.identifier == null) {
|
||||||
identifier = 'default';
|
identifier = 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.encrypt && !data.recipientPublicKey) {
|
||||||
|
let data = {};
|
||||||
|
data['error'] = "Encrypting data requires the recipient's public key";
|
||||||
|
response = JSON.stringify(data);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (!data.encrypt && data.service.endsWith("_PRIVATE")) {
|
||||||
|
let data = {};
|
||||||
|
data['error'] = "Only encrypted data can go into private services";
|
||||||
|
response = JSON.stringify(data);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.encrypt) {
|
||||||
|
try {
|
||||||
|
const encryptDataResponse = encryptData({
|
||||||
|
data64, recipientPublicKey: data.recipientPublicKey
|
||||||
|
})
|
||||||
|
if (encryptDataResponse.encryptedData) {
|
||||||
|
data64 = encryptDataResponse.encryptedData
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const obj = {};
|
||||||
|
const errorMsg = error.message || 'Upload failed due to failed encryption';
|
||||||
|
obj['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(obj);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
const res2 = await showModalAndWait(
|
const res2 = await showModalAndWait(
|
||||||
actions.PUBLISH_QDN_RESOURCE,
|
actions.PUBLISH_QDN_RESOURCE,
|
||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
identifier,
|
identifier,
|
||||||
service
|
service,
|
||||||
|
encrypt: data.encrypt
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (res2.action === 'accept') {
|
if (res2.action === 'accept') {
|
||||||
@ -833,15 +932,15 @@ class WebBrowser extends LitElement {
|
|||||||
isBase64: true,
|
isBase64: true,
|
||||||
filename: filename,
|
filename: filename,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
category,
|
category,
|
||||||
tag1,
|
tag1,
|
||||||
tag2,
|
tag2,
|
||||||
tag3,
|
tag3,
|
||||||
tag4,
|
tag4,
|
||||||
tag5,
|
tag5,
|
||||||
apiVersion: 2,
|
apiVersion: 2,
|
||||||
withFee: res2.userData.isWithFee === true ? true: false
|
withFee: res2.userData.isWithFee === true ? true : false
|
||||||
});
|
});
|
||||||
|
|
||||||
response = JSON.stringify(resPublish);
|
response = JSON.stringify(resPublish);
|
||||||
@ -897,10 +996,17 @@ class WebBrowser extends LitElement {
|
|||||||
response = JSON.stringify(data);
|
response = JSON.stringify(data);
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if (data.encrypt && !data.recipientPublicKey) {
|
||||||
|
let data = {};
|
||||||
|
data['error'] = "Encrypting data requires the recipient's public key";
|
||||||
|
response = JSON.stringify(data);
|
||||||
|
break
|
||||||
|
}
|
||||||
const res2 = await showModalAndWait(
|
const res2 = await showModalAndWait(
|
||||||
actions.PUBLISH_MULTIPLE_QDN_RESOURCES,
|
actions.PUBLISH_MULTIPLE_QDN_RESOURCES,
|
||||||
{
|
{
|
||||||
resources,
|
resources,
|
||||||
|
encrypt: data.encrypt
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -928,7 +1034,7 @@ class WebBrowser extends LitElement {
|
|||||||
const service = resource.service;
|
const service = resource.service;
|
||||||
const name = resource.name;
|
const name = resource.name;
|
||||||
let identifier = resource.identifier;
|
let identifier = resource.identifier;
|
||||||
const data64 = resource.data64;
|
let data64 = resource.data64;
|
||||||
const filename = resource.filename;
|
const filename = resource.filename;
|
||||||
const title = resource.title;
|
const title = resource.title;
|
||||||
const description = resource.description;
|
const description = resource.description;
|
||||||
@ -942,6 +1048,28 @@ class WebBrowser extends LitElement {
|
|||||||
identifier = 'default';
|
identifier = 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data.encrypt && service.endsWith("_PRIVATE")) {
|
||||||
|
throw new Error("Only encrypted data can go into private services")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.encrypt) {
|
||||||
|
try {
|
||||||
|
const encryptDataResponse = encryptData({
|
||||||
|
data64, recipientPublicKey: data.recipientPublicKey
|
||||||
|
})
|
||||||
|
if (encryptDataResponse.encryptedData) {
|
||||||
|
data64 = encryptDataResponse.encryptedData
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error.message || 'Upload failed due to failed encryption'
|
||||||
|
throw new Error(errorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const worker = new WebWorker();
|
const worker = new WebWorker();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -1009,14 +1137,14 @@ class WebBrowser extends LitElement {
|
|||||||
const groupId = data.groupId;
|
const groupId = data.groupId;
|
||||||
const isRecipient = groupId ? false : true
|
const isRecipient = groupId ? false : true
|
||||||
const sendMessage = async (messageText, chatReference) => {
|
const sendMessage = async (messageText, chatReference) => {
|
||||||
|
|
||||||
let _reference = new Uint8Array(64);
|
let _reference = new Uint8Array(64);
|
||||||
window.crypto.getRandomValues(_reference);
|
window.crypto.getRandomValues(_reference);
|
||||||
let reference = window.parent.Base58.encode(_reference);
|
let reference = window.parent.Base58.encode(_reference);
|
||||||
const sendMessageRequest = async () => {
|
const sendMessageRequest = async () => {
|
||||||
let chatResponse
|
let chatResponse
|
||||||
|
|
||||||
if(isRecipient){
|
if (isRecipient) {
|
||||||
chatResponse = await parentEpml.request('chat', {
|
chatResponse = await parentEpml.request('chat', {
|
||||||
type: 18,
|
type: 18,
|
||||||
nonce: this.selectedAddress.nonce,
|
nonce: this.selectedAddress.nonce,
|
||||||
@ -1037,13 +1165,13 @@ class WebBrowser extends LitElement {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isRecipient){
|
if (!isRecipient) {
|
||||||
chatResponse = await parentEpml.request('chat', {
|
chatResponse = await parentEpml.request('chat', {
|
||||||
type: 181,
|
type: 181,
|
||||||
nonce: this.selectedAddress.nonce,
|
nonce: this.selectedAddress.nonce,
|
||||||
params: {
|
params: {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
groupID: Number(groupId),
|
groupID: Number(groupId),
|
||||||
hasReceipient: 0,
|
hasReceipient: 0,
|
||||||
hasChatReference: 0,
|
hasChatReference: 0,
|
||||||
chatReference: chatReference,
|
chatReference: chatReference,
|
||||||
@ -1057,7 +1185,7 @@ class WebBrowser extends LitElement {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const msgResponse = await _computePow(chatResponse)
|
const msgResponse = await _computePow(chatResponse)
|
||||||
return msgResponse;
|
return msgResponse;
|
||||||
};
|
};
|
||||||
@ -1109,12 +1237,12 @@ class WebBrowser extends LitElement {
|
|||||||
if (result.action === "accept") {
|
if (result.action === "accept") {
|
||||||
let hasPublicKey = true;
|
let hasPublicKey = true;
|
||||||
|
|
||||||
if(isRecipient){
|
if (isRecipient) {
|
||||||
const res = await parentEpml.request('apiCall', {
|
const res = await parentEpml.request('apiCall', {
|
||||||
type: 'api',
|
type: 'api',
|
||||||
url: `/addresses/publickey/${recipient}`
|
url: `/addresses/publickey/${recipient}`
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.error === 102) {
|
if (res.error === 102) {
|
||||||
this._publicKey.key = ''
|
this._publicKey.key = ''
|
||||||
this._publicKey.hasPubKey = false
|
this._publicKey.hasPubKey = false
|
||||||
@ -1128,14 +1256,14 @@ class WebBrowser extends LitElement {
|
|||||||
hasPublicKey = false;
|
hasPublicKey = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!hasPublicKey && isRecipient) {
|
if (!hasPublicKey && isRecipient) {
|
||||||
response = '{"error": "Cannot send an encrypted message to this user since they do not have their publickey on chain."}';
|
response = '{"error": "Cannot send an encrypted message to this user since they do not have their publickey on chain."}';
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const tiptapJson = {
|
const tiptapJson = {
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
@ -1174,16 +1302,16 @@ class WebBrowser extends LitElement {
|
|||||||
response = msgResponse;
|
response = msgResponse;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
if(error.message){
|
if (error.message) {
|
||||||
let data = {};
|
let data = {};
|
||||||
data['error'] = error.message;
|
data['error'] = error.message;
|
||||||
response = JSON.stringify(data);
|
response = JSON.stringify(data);
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
response = '{"error": "Request could not be fulfilled"}';
|
response = '{"error": "Request could not be fulfilled"}';
|
||||||
} finally {
|
} finally {
|
||||||
this.loader.hide();
|
this.loader.hide();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -1259,6 +1387,112 @@ class WebBrowser extends LitElement {
|
|||||||
// If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}`
|
// If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}`
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case actions.SAVE_FILE: {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const requiredFields = ['filename', 'blob'];
|
||||||
|
const missingFields = [];
|
||||||
|
|
||||||
|
requiredFields.forEach((field) => {
|
||||||
|
if (!data[field]) {
|
||||||
|
missingFields.push(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const missingFieldsString = missingFields.join(', ');
|
||||||
|
const errorMsg = `Missing fields: ${missingFieldsString}`
|
||||||
|
let data = {};
|
||||||
|
data['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(data);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const filename = data.filename
|
||||||
|
const blob = data.blob
|
||||||
|
|
||||||
|
const res = await showModalAndWait(
|
||||||
|
actions.SAVE_FILE,
|
||||||
|
{
|
||||||
|
filename
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.action === 'reject') {
|
||||||
|
response = '{"error": "User declined request"}';
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const mimeType = blob.type || data.mimeType
|
||||||
|
let backupExention = filename.split('.').pop()
|
||||||
|
if (backupExention) {
|
||||||
|
backupExention = '.' + backupExention
|
||||||
|
}
|
||||||
|
const fileExtension = mimeToExtensionMap[mimeType] || backupExention
|
||||||
|
let fileHandleOptions = {}
|
||||||
|
if (!mimeType) {
|
||||||
|
const obj = {};
|
||||||
|
const errorMsg = 'A mimeType could not be derived';
|
||||||
|
obj['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(obj);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (!fileExtension) {
|
||||||
|
const obj = {};
|
||||||
|
const errorMsg = 'A file extension could not be derived';
|
||||||
|
obj['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(obj);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (fileExtension && mimeType) {
|
||||||
|
fileHandleOptions = {
|
||||||
|
accept: {
|
||||||
|
[mimeType]: [fileExtension]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileHandle = await self.showSaveFilePicker({
|
||||||
|
suggestedName: filename,
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: mimeType,
|
||||||
|
...fileHandleOptions
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
const writeFile = async (fileHandle, contents) => {
|
||||||
|
const writable = await fileHandle.createWritable()
|
||||||
|
await writable.write(contents)
|
||||||
|
await writable.close()
|
||||||
|
}
|
||||||
|
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
const obj = {};
|
||||||
|
const errorMsg = 'User declined the download';
|
||||||
|
obj['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(obj);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
FileSaver.saveAs(blob, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
response = JSON.stringify(true);
|
||||||
|
} catch (error) {
|
||||||
|
const obj = {};
|
||||||
|
const errorMsg = error.message || 'Failed to initiate download';
|
||||||
|
obj['error'] = errorMsg;
|
||||||
|
response = JSON.stringify(obj);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// case 'DEPLOY_AT': {
|
// case 'DEPLOY_AT': {
|
||||||
// const requiredFields = ['name', 'description', 'tags', 'creationBytes', 'amount', 'assetId', 'type'];
|
// const requiredFields = ['name', 'description', 'tags', 'creationBytes', 'amount', 'assetId', 'type'];
|
||||||
@ -1333,19 +1567,19 @@ class WebBrowser extends LitElement {
|
|||||||
url: `/addresses/balance/${qortAddress}?apiKey=${this.getApiKey()}`,
|
url: `/addresses/balance/${qortAddress}?apiKey=${this.getApiKey()}`,
|
||||||
})
|
})
|
||||||
response = QORTBalance
|
response = QORTBalance
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const data = {};
|
const data = {};
|
||||||
const errorMsg = error.message || get("browserpage.bchange21");
|
const errorMsg = error.message || get("browserpage.bchange21");
|
||||||
data['error'] = errorMsg;
|
data['error'] = errorMsg;
|
||||||
response = JSON.stringify(data);
|
response = JSON.stringify(data);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
this.loader.hide();
|
this.loader.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else {
|
// else {
|
||||||
// let _url = ``
|
// let _url = ``
|
||||||
// let _body = null
|
// let _body = null
|
||||||
@ -1407,7 +1641,7 @@ class WebBrowser extends LitElement {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
case actions.SEND_COIN: {
|
case actions.SEND_COIN: {
|
||||||
const requiredFields = ['coin', 'destinationAddress', 'amount']
|
const requiredFields = ['coin', 'destinationAddress', 'amount']
|
||||||
@ -1466,7 +1700,7 @@ class WebBrowser extends LitElement {
|
|||||||
const balance = (Number(transformDecimals) / 1e8).toFixed(8)
|
const balance = (Number(transformDecimals) / 1e8).toFixed(8)
|
||||||
const fee = await this.sendQortFee()
|
const fee = await this.sendQortFee()
|
||||||
|
|
||||||
if (amountDecimals + (fee * QORT_DECIMALS) > walletBalanceDecimals) {
|
if (amountDecimals + (fee * QORT_DECIMALS) > walletBalanceDecimals) {
|
||||||
let errorMsg = "Insufficient Funds!"
|
let errorMsg = "Insufficient Funds!"
|
||||||
let failedMsg = get("walletpage.wchange26")
|
let failedMsg = get("walletpage.wchange26")
|
||||||
let pleaseMsg = get("walletpage.wchange44")
|
let pleaseMsg = get("walletpage.wchange44")
|
||||||
@ -1621,7 +1855,7 @@ class WebBrowser extends LitElement {
|
|||||||
this.loader.hide()
|
this.loader.hide()
|
||||||
throw new Error('Error: could not send coin')
|
throw new Error('Error: could not send coin')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -2642,6 +2876,7 @@ async function showModalAndWait(type, data) {
|
|||||||
${type === actions.PUBLISH_MULTIPLE_QDN_RESOURCES ? `
|
${type === actions.PUBLISH_MULTIPLE_QDN_RESOURCES ? `
|
||||||
<div class="modal-subcontainer">
|
<div class="modal-subcontainer">
|
||||||
<p class="modal-paragraph">${get("browserpage.bchange19")}</p>
|
<p class="modal-paragraph">${get("browserpage.bchange19")}</p>
|
||||||
|
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange45")}:</span> ${data.encrypt ? true : false}</p>
|
||||||
<table>
|
<table>
|
||||||
${data.resources.map((resource) => `
|
${data.resources.map((resource) => `
|
||||||
<tr>
|
<tr>
|
||||||
@ -2667,6 +2902,7 @@ async function showModalAndWait(type, data) {
|
|||||||
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange30")}:</span> ${data.service}</p>
|
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange30")}:</span> ${data.service}</p>
|
||||||
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange31")}:</span> ${data.name}</p>
|
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange31")}:</span> ${data.name}</p>
|
||||||
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange32")}:</span> ${data.identifier}</p>
|
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange32")}:</span> ${data.identifier}</p>
|
||||||
|
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph"><span style="font-weight: bold">${get("browserpage.bchange45")}:</span> ${data.encrypt ? true : false}</p>
|
||||||
<div class="checkbox-row">
|
<div class="checkbox-row">
|
||||||
<label for="isWithFee" id="isWithFeeLabel" style="color: var(--black);">
|
<label for="isWithFee" id="isWithFeeLabel" style="color: var(--black);">
|
||||||
${get('browserpage.bchange29')}
|
${get('browserpage.bchange29')}
|
||||||
@ -2710,6 +2946,12 @@ async function showModalAndWait(type, data) {
|
|||||||
<p class="modal-paragraph">${get("browserpage.bchange42")}: <span> ${data.items.join(', ')}</span></p>
|
<p class="modal-paragraph">${get("browserpage.bchange42")}: <span> ${data.items.join(', ')}</span></p>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
|
${type === actions.SAVE_FILE ? `
|
||||||
|
<div class="modal-subcontainer">
|
||||||
|
<p class="modal-paragraph">${get("browserpage.bchange46")}: <span> ${data.filename}</span></p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
${type === actions.DELETE_LIST_ITEM ? `
|
${type === actions.DELETE_LIST_ITEM ? `
|
||||||
<div class="modal-subcontainer">
|
<div class="modal-subcontainer">
|
||||||
<p class="modal-paragraph">${get("browserpage.bchange44")}</p>
|
<p class="modal-paragraph">${get("browserpage.bchange44")}</p>
|
||||||
@ -2772,7 +3014,7 @@ async function showModalAndWait(type, data) {
|
|||||||
if (checkbox) {
|
if (checkbox) {
|
||||||
checkbox.addEventListener('click', (e) => {
|
checkbox.addEventListener('click', (e) => {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
window.parent.reduxStore.dispatch( window.parent.reduxAction.removeQAPPAutoAuth(false))
|
window.parent.reduxStore.dispatch(window.parent.reduxAction.removeQAPPAutoAuth(false))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
window.parent.reduxStore.dispatch(window.parent.reduxAction.allowQAPPAutoAuth(true))
|
window.parent.reduxStore.dispatch(window.parent.reduxAction.allowQAPPAutoAuth(true))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user