From a32abaf9e703d481b3b8fc45739b312e907979a9 Mon Sep 17 00:00:00 2001 From: SwiftHawk Date: Thu, 18 Apr 2024 16:12:11 +0500 Subject: [PATCH] feat: implemented the UI and logic for signing document --- src/components/AppBar/AppBar.tsx | 18 +++ src/pages/home/index.tsx | 183 +++++----------------- src/pages/sign/index.tsx | 261 +++++++++++++++++++++++++++++++ src/pages/sign/style.module.scss | 27 ++++ src/routes/index.tsx | 8 +- src/types/index.ts | 1 + src/types/zip.ts | 13 ++ src/utils/file.ts | 16 -- src/utils/hash.ts | 19 +++ src/utils/index.ts | 4 +- src/utils/misc.ts | 196 +++++++++++++++++++++++ src/utils/string.ts | 19 +++ src/utils/zip.ts | 32 ++++ 13 files changed, 636 insertions(+), 161 deletions(-) create mode 100644 src/pages/sign/index.tsx create mode 100644 src/pages/sign/style.module.scss create mode 100644 src/types/zip.ts delete mode 100644 src/utils/file.ts create mode 100644 src/utils/hash.ts create mode 100644 src/utils/misc.ts create mode 100644 src/utils/zip.ts diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index 56f5495..b712993 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -135,6 +135,12 @@ export const AppBar = () => { to={appPrivateRoutes.decryptZip} component={Link} /> + )} @@ -201,6 +207,18 @@ export const AppBar = () => { Decrypt Zip + + + + )} diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 2381ec9..85586b5 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -13,10 +13,8 @@ import { TextField, Typography } from '@mui/material' -import axios from 'axios' import JSZip from 'jszip' import { MuiFileInput } from 'mui-file-input' -import { EventTemplate } from 'nostr-tools' import { useEffect, useState } from 'react' import { useSelector } from 'react-redux' import { Link } from 'react-router-dom' @@ -30,10 +28,13 @@ import { ProfileMetadata } from '../../types' import { encryptArrayBuffer, generateEncryptionKey, - getFileHash, + getHash, pubToHex, queryNip05, - shorten + sendDM, + shorten, + signEventForMetaFile, + uploadToFileStorage } from '../../utils' import styles from './style.module.scss' @@ -162,7 +163,25 @@ export const HomePage = () => { const fileHashes: { [key: string]: string } = {} for (const file of selectedFiles) { - const hash = await getFileHash(file) + const arraybuffer = await file.arrayBuffer().catch((err) => { + console.log( + `err while getting arrayBuffer of file ${file.name} :>> `, + err + ) + toast.error( + err.message || `err while getting arrayBuffer of file ${file.name}` + ) + return null + }) + + if (!arraybuffer) return + + const hash = await getHash(arraybuffer) + + if (!hash) { + setIsLoading(false) + return + } fileHashes[file.name] = hash } @@ -173,20 +192,13 @@ export const HomePage = () => { zip.file(`files/${file.name}`, file) }) - const event: EventTemplate = { - kind: 1, - tags: [['r', signers[0]]], - content: JSON.stringify(fileHashes), - created_at: Math.floor(Date.now() / 1000) - } - setLoadingSpinnerDesc('Signing nostr event') - const signedEvent = await nostrController.signEvent(event).catch((err) => { - console.error(err) - toast.error(err.message || 'Error occurred in signing nostr event') - setIsLoading(false) - return null - }) + const signedEvent = await signEventForMetaFile( + signers[0], + fileHashes, + nostrController, + setIsLoading + ) if (!signedEvent) return @@ -238,7 +250,7 @@ export const HomePage = () => { const blob = new Blob([encryptedArrayBuffer]) setLoadingSpinnerDesc('Uploading zip file to file storage.') - const fileUrl = await uploadToFileStorage(blob) + const fileUrl = await uploadToFileStorage(blob, nostrController) .then((url) => { toast.success('zip file uploaded to file storage') return url @@ -251,134 +263,19 @@ export const HomePage = () => { if (!fileUrl) return - await sendDMToFirstSigner(fileUrl, encryptionKey, signers[0]) + setLoadingSpinnerDesc('Sending DM to first signer') + await sendDM( + fileUrl, + encryptionKey, + signers[0], + nostrController, + true, + setAuthUrl + ) setIsLoading(false) } - const uploadToFileStorage = async (blob: Blob) => { - const unixNow = Math.floor(Date.now() / 1000) - - const file = new File([blob], `zipped-${unixNow}.zip`, { - type: 'application/zip' - }) - - const event: EventTemplate = { - kind: 24242, - content: 'Authorize Upload', - created_at: Math.floor(Date.now() / 1000), - tags: [ - ['t', 'upload'], - ['expiration', String(unixNow + 60 * 5)], - ['name', file.name], - ['size', String(file.size)] - ] - } - - setLoadingSpinnerDesc('Signing auth event for uploading zip') - const authEvent = await nostrController.signEvent(event) - - // todo: use env variable - const FILE_STORAGE_URL = 'https://blossom.sigit.io' - - const response = await axios.put(`${FILE_STORAGE_URL}/upload`, file, { - headers: { - Authorization: 'Nostr ' + btoa(JSON.stringify(authEvent)), - 'Content-Type': 'application/zip' - } - }) - - return response.data.url as string - } - - const sendDMToFirstSigner = async ( - fileUrl: string, - encryptionKey: string, - pubkey: string - ) => { - const content = `You have been requested for a signature.\nHere is the url for zip file that you can download.\n - ${fileUrl}\nHowever this zip file is encrypted and you need to decrypt it using https://app.sigit.io\n Encryption key: ${encryptionKey}` - - nostrController.on('nsecbunker-auth', (url) => { - setAuthUrl(url) - }) - - setLoadingSpinnerDesc('encrypting content for DM') - - const timeoutPromise = new Promise((_, reject) => { - setTimeout(() => { - reject(new Error('Timeout occurred')) - }, 15000) // timeout duration = 15 sec - }) - - const encrypted = await Promise.race([ - nostrController.nip04Encrypt(pubkey, content), - timeoutPromise - ]) - .then((res) => { - return res - }) - .catch((err) => { - console.log('err :>> ', err) - toast.error( - err.message || 'An error occurred while encrypting DM content' - ) - return null - }) - .finally(() => { - setAuthUrl(undefined) - }) - - if (!encrypted) return - - const event: EventTemplate = { - kind: 4, - content: encrypted, - created_at: Math.floor(Date.now() / 1000), - tags: [['p', signers[0]]] - } - - setLoadingSpinnerDesc('signing event for DM') - const signedEvent = await nostrController.signEvent(event).catch((err) => { - console.log('err :>> ', err) - toast.error(err.message || 'An error occurred while signing event for DM') - return null - }) - - if (!signedEvent) return - - setLoadingSpinnerDesc('Publishing encrypted DM') - - const metadataController = new MetadataController() - const relaySet = await metadataController - .findRelayListMetadata(pubkey) - .catch((err) => { - toast.error( - err.message || 'An error occurred while finding relay list metadata' - ) - return null - }) - - if (!relaySet) return - - // NOTE: according to Nip65 - // DMs SHOULD only be broadcasted to the author's WRITE relays and to the receiver's READ relays to keep maximum privacy. - if (relaySet.read.length === 0) { - toast.error('No relay found for publishing encrypted DM') - } - - await nostrController - .publishEvent(signedEvent, relaySet.read) - .then((relays) => { - toast.success(`Encrypted DM sent on: ${relays.join('\n')}`) - }) - .catch((err) => { - console.log('err :>> ', err) - toast.error(err.message || 'An error occurred while publishing DM') - return null - }) - } - if (authUrl) { return (