2024-04-18 11:12:11 +00:00
import axios from 'axios'
import { EventTemplate } from 'nostr-tools'
import { MetadataController , NostrController } from '../controllers'
import { toast } from 'react-toastify'
2024-04-19 10:57:44 +00:00
import { appPrivateRoutes } from '../routes'
2024-04-18 11:12:11 +00:00
/ * *
* Uploads a file to a file storage service .
* @param blob The Blob object representing the file to upload .
* @param nostrController The NostrController instance for handling authentication .
* @returns The URL of the uploaded file .
* /
export const uploadToFileStorage = async (
blob : Blob ,
nostrController : NostrController
) = > {
// Get the current timestamp in seconds
const unixNow = Math . floor ( Date . now ( ) / 1000 )
// Create a File object with the Blob data
const file = new File ( [ blob ] , ` zipped- ${ unixNow } .zip ` , {
type : 'application/zip'
} )
// Define event metadata for authorization
const event : EventTemplate = {
kind : 24242 ,
content : 'Authorize Upload' ,
created_at : Math.floor ( Date . now ( ) / 1000 ) ,
tags : [
[ 't' , 'upload' ] ,
[ 'expiration' , String ( unixNow + 60 * 5 ) ] , // Set expiration time to 5 minutes from now
[ 'name' , file . name ] ,
[ 'size' , String ( file . size ) ]
]
}
// Sign the authorization event using the NostrController
const authEvent = await nostrController . signEvent ( event )
// URL of the file storage service
const FILE_STORAGE_URL = 'https://blossom.sigit.io'
// Upload the file to the file storage service using Axios
const response = await axios . put ( ` ${ FILE_STORAGE_URL } /upload ` , file , {
headers : {
Authorization : 'Nostr ' + btoa ( JSON . stringify ( authEvent ) ) , // Set authorization header
'Content-Type' : 'application/zip' // Set content type header
}
} )
// Return the URL of the uploaded file
return response . data . url as string
}
/ * *
* Sends a Direct Message ( DM ) to a recipient , encrypting the content and handling authentication .
* @param fileUrl The URL of the encrypted zip file to be included in the DM .
* @param encryptionKey The encryption key used to decrypt the zip file to be included in the DM .
* @param pubkey The public key of the recipient .
* @param nostrController The NostrController instance for handling authentication and encryption .
* @param isSigner Boolean indicating whether the recipient is a signer or viewer .
* @param setAuthUrl Function to set the authentication URL in the component state .
* /
export const sendDM = async (
fileUrl : string ,
encryptionKey : string ,
pubkey : string ,
nostrController : NostrController ,
isSigner : boolean ,
setAuthUrl : ( value : React.SetStateAction < string | undefined > ) = > void
) = > {
// Construct the content of the DM
const initialLine = isSigner
? 'You have been requested for a signature.'
: 'You have received a signed document.'
2024-04-19 10:57:44 +00:00
const decryptionUrl = ` http://app.sigit.io ${ appPrivateRoutes . decryptZip } ?file= ${ fileUrl } &key= ${ encryptionKey } `
const content = ` ${ initialLine } \ nHere is the URL for the zip file that you can download. \ n ${ fileUrl } \ nHowever, this zip file is encrypted and you need to decrypt it using ${ decryptionUrl } `
2024-04-18 11:12:11 +00:00
// Set up event listener for authentication event
nostrController . on ( 'nsecbunker-auth' , ( url ) = > {
setAuthUrl ( url )
} )
// Set up timeout promise to handle encryption timeout
const timeoutPromise = new Promise < never > ( ( _ , reject ) = > {
setTimeout ( ( ) = > {
reject ( new Error ( 'Timeout occurred' ) )
} , 15000 ) // Timeout duration = 15 seconds
} )
// Encrypt the DM content, with timeout
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 ) // Clear authentication URL
} )
// Return if encryption failed
if ( ! encrypted ) return
// Construct event metadata for the DM
const event : EventTemplate = {
kind : 4 , // DM event type
content : encrypted , // Encrypted DM content
created_at : Math.floor ( Date . now ( ) / 1000 ) , // Current timestamp
tags : [ [ 'p' , pubkey ] ] // Tag with recipient's public key
}
// Sign the DM event
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
} )
// Return if event signing failed
if ( ! signedEvent ) return
// Get relay list metadata
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
} )
// Return if metadata retrieval failed
if ( ! relaySet ) return
// Ensure relay list is not empty
if ( relaySet . read . length === 0 ) {
toast . error ( 'No relay found for publishing encrypted DM' )
return
}
// Publish the signed DM event to the recipient's read relays
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
} )
}
/ * *
* Signs an event for a meta . json file .
* @param receiver The recipient ' s public key .
* @param fileHashes Object containing file hashes .
* @param nostrController The NostrController instance for signing the event .
* @param setIsLoading Function to set loading state in the component .
* @returns A Promise resolving to the signed event , or null if signing fails .
* /
export const signEventForMetaFile = async (
receiver : string ,
fileHashes : {
[ key : string ] : string
} ,
nostrController : NostrController ,
setIsLoading : ( value : React.SetStateAction < boolean > ) = > void
) = > {
// Construct the event metadata for the meta file
const event : EventTemplate = {
kind : 1 , // Event type for meta file
tags : [ [ 'r' , receiver ] ] , // Tag with recipient's public key
content : JSON.stringify ( fileHashes ) , // Convert file hashes to JSON string
created_at : Math.floor ( Date . now ( ) / 1000 ) // Current timestamp
}
// Sign the event
const signedEvent = await nostrController . signEvent ( event ) . catch ( ( err ) = > {
console . error ( err )
toast . error ( err . message || 'Error occurred in signing nostr event' )
setIsLoading ( false ) // Set loading state to false
return null
} )
return signedEvent // Return the signed event
}