release #111
@ -1,5 +1,3 @@
|
|||||||
import { UnsignedEvent } from 'nostr-tools'
|
|
||||||
|
|
||||||
export interface SignedEvent {
|
export interface SignedEvent {
|
||||||
kind: number
|
kind: number
|
||||||
tags: string[][]
|
tags: string[][]
|
||||||
@ -14,5 +12,3 @@ export interface NostrJoiningBlock {
|
|||||||
block: number
|
block: number
|
||||||
encodedEventPointer: string
|
encodedEventPointer: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Rumor = UnsignedEvent & { id: string }
|
|
||||||
|
@ -8,13 +8,12 @@ import {
|
|||||||
verifyEvent
|
verifyEvent
|
||||||
} from 'nostr-tools'
|
} from 'nostr-tools'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { MetadataController, NostrController } from '../controllers'
|
import { NostrController } from '../controllers'
|
||||||
import { appPrivateRoutes } from '../routes'
|
import { AuthState } from '../store/auth/types'
|
||||||
|
import store from '../store/store'
|
||||||
|
import { CreateSignatureEventContent, Meta } from '../types'
|
||||||
import { hexToNpub, now } from './nostr'
|
import { hexToNpub, now } from './nostr'
|
||||||
import { parseJson } from './string'
|
import { parseJson } from './string'
|
||||||
import { CreateSignatureEventContent, Meta } from '../types'
|
|
||||||
import store from '../store/store'
|
|
||||||
import { AuthState } from '../store/auth/types'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a file to a file storage service.
|
* Uploads a file to a file storage service.
|
||||||
@ -57,128 +56,6 @@ export const uploadToFileStorage = async (
|
|||||||
return response.data.url as string
|
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
|
|
||||||
? 'Your signature is requested on the document below!'
|
|
||||||
: 'You have received a signed document.'
|
|
||||||
|
|
||||||
const decryptionUrl = `${window.location.origin}/#${
|
|
||||||
appPrivateRoutes.sign
|
|
||||||
}?file=${encodeURIComponent(fileUrl)}&key=${encodeURIComponent(
|
|
||||||
encryptionKey
|
|
||||||
)}`
|
|
||||||
|
|
||||||
const content = `${initialLine}\n\n${decryptionUrl}`
|
|
||||||
|
|
||||||
// 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'))
|
|
||||||
}, 60000) // Timeout duration = 60 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((errResults) => {
|
|
||||||
console.log('err :>> ', errResults)
|
|
||||||
toast.error('An error occurred while publishing DM')
|
|
||||||
|
|
||||||
errResults.forEach((errResult: any) => {
|
|
||||||
toast.error(
|
|
||||||
`Publishing to ${errResult.relay} caused the following error: ${errResult.error}`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs an event for a meta.json file.
|
* Signs an event for a meta.json file.
|
||||||
* @param content contains content for event.
|
* @param content contains content for event.
|
||||||
|
@ -209,10 +209,7 @@ export const getRoboHashPicture = (
|
|||||||
return `https://robohash.org/${npub}.png?set=set${set}`
|
return `https://robohash.org/${npub}.png?set=set${set}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TWO_DAYS = 2 * 24 * 60 * 60
|
|
||||||
|
|
||||||
export const now = () => Math.round(Date.now() / 1000)
|
export const now = () => Math.round(Date.now() / 1000)
|
||||||
export const randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate nip44 conversation key
|
* Generate nip44 conversation key
|
||||||
|
Loading…
Reference in New Issue
Block a user