story-research-zapwall/lib/articlePublisherHelpersEncryption.ts

87 lines
3.0 KiB
TypeScript

import { nip04 } from 'nostr-tools'
import { nostrService } from './nostr'
import { publishAndVerifyMessage } from './articlePublisherHelpersVerification'
export interface SendContentResult {
success: boolean
messageEventId?: string
error?: string
verified?: boolean
}
export function prepareKeyData(storedContent: { content: string; decryptionKey?: string; decryptionIV?: string }): string {
if (storedContent.decryptionKey && storedContent.decryptionIV) {
return JSON.stringify({ key: storedContent.decryptionKey, iv: storedContent.decryptionIV })
}
return storedContent.content // Fallback to old behavior if keys are not available
}
export function buildPrivateMessageEvent(recipientPubkey: string, articleId: string, encryptedKey: string) {
return {
kind: 4,
created_at: Math.floor(Date.now() / 1000),
tags: [
['p', recipientPubkey],
['e', articleId],
],
content: encryptedKey,
}
}
async function publishEncryptedMessage(
articleId: string,
recipientPubkey: string,
authorPubkey: string,
authorPrivateKey: string,
keyData: string
): Promise<{ eventId: string } | null> {
const encryptedKey = await Promise.resolve(nip04.encrypt(authorPrivateKey, recipientPubkey, keyData))
const privateMessageEvent = buildPrivateMessageEvent(recipientPubkey, articleId, encryptedKey)
const publishedEvent = await nostrService.publishEvent(privateMessageEvent)
if (!publishedEvent) {
console.error('Failed to publish private message event', { articleId, recipientPubkey, authorPubkey })
return null
}
console.log('Private message published', {
messageEventId: publishedEvent.id,
articleId,
recipientPubkey,
authorPubkey,
timestamp: new Date().toISOString(),
})
return { eventId: publishedEvent.id }
}
export async function sendEncryptedContent(
articleId: string,
recipientPubkey: string,
storedContent: { content: string; authorPubkey: string; decryptionKey?: string; decryptionIV?: string },
authorPrivateKey: string
): Promise<SendContentResult> {
try {
nostrService.setPrivateKey(authorPrivateKey)
nostrService.setPublicKey(storedContent.authorPubkey)
const keyData = prepareKeyData(storedContent)
const publishResult = await publishEncryptedMessage(articleId, recipientPubkey, storedContent.authorPubkey, authorPrivateKey, keyData)
if (!publishResult) {
return { success: false, error: 'Failed to publish private message event' }
}
const verified = await publishAndVerifyMessage(articleId, recipientPubkey, storedContent.authorPubkey, publishResult.eventId)
return { success: true, messageEventId: publishResult.eventId, verified }
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
console.error('Error sending encrypted content', {
articleId,
recipientPubkey,
authorPubkey: storedContent.authorPubkey,
error: errorMessage,
timestamp: new Date().toISOString(),
})
return { success: false, error: errorMessage }
}
}