97 lines
3.3 KiB
TypeScript
97 lines
3.3 KiB
TypeScript
import { nostrService } from './nostr'
|
|
import type { ArticleDraft, PublishedArticle } from './articlePublisherTypes'
|
|
import type { AlbyInvoice } from '@/types/alby'
|
|
import { createArticleInvoice, createPreviewEvent } from './articleInvoice'
|
|
import { encryptArticleContent, encryptDecryptionKey } from './articleEncryption'
|
|
import { storePrivateContent } from './articleStorage'
|
|
import type { PublishResult } from './publishResult'
|
|
|
|
export function buildFailure(error?: string): PublishedArticle {
|
|
const base: PublishedArticle = {
|
|
articleId: '',
|
|
previewEventId: '',
|
|
success: false,
|
|
}
|
|
return error ? { ...base, error } : base
|
|
}
|
|
|
|
export async function publishPreview(
|
|
draft: ArticleDraft,
|
|
invoice: AlbyInvoice,
|
|
authorPubkey: string,
|
|
presentationId: string,
|
|
extraTags?: string[][],
|
|
encryptedContent?: string,
|
|
encryptedKey?: string,
|
|
returnStatus?: boolean
|
|
): Promise<import('nostr-tools').Event | null | PublishResult> {
|
|
const previewEvent = await createPreviewEvent(draft, invoice, authorPubkey, presentationId, extraTags, encryptedContent, encryptedKey)
|
|
if (returnStatus) {
|
|
return await nostrService.publishEvent(previewEvent, true)
|
|
}
|
|
const publishedEvent = await nostrService.publishEvent(previewEvent, false)
|
|
return publishedEvent ?? null
|
|
}
|
|
|
|
export function buildArticleExtraTags(draft: ArticleDraft, _category: NonNullable<ArticleDraft['category']>): string[][] {
|
|
// Media tags are still supported in the new system
|
|
const extraTags: string[][] = []
|
|
if (draft.media && draft.media.length > 0) {
|
|
draft.media.forEach((m) => {
|
|
extraTags.push(['media', m.url, m.type])
|
|
})
|
|
}
|
|
return extraTags
|
|
}
|
|
|
|
export async function encryptAndPublish(
|
|
draft: ArticleDraft,
|
|
authorPubkey: string,
|
|
authorPrivateKeyForEncryption: string,
|
|
category: NonNullable<ArticleDraft['category']>,
|
|
presentationId: string
|
|
): Promise<PublishedArticle> {
|
|
const { encryptedContent, key, iv } = await encryptArticleContent(draft.content)
|
|
const encryptedKey = await encryptDecryptionKey(key, iv, authorPrivateKeyForEncryption, authorPubkey)
|
|
const invoice = await createArticleInvoice(draft)
|
|
const extraTags = buildArticleExtraTags(draft, category)
|
|
const publishResult = await publishPreview(draft, invoice, authorPubkey, presentationId, extraTags, encryptedContent, encryptedKey, true)
|
|
|
|
if (!publishResult) {
|
|
return buildFailure('Failed to publish article')
|
|
}
|
|
|
|
// Handle both old format (Event | null) and new format (PublishResult)
|
|
let event: import('nostr-tools').Event | null = null
|
|
let relayStatuses: import('./publishResult').RelayPublishStatus[] | undefined
|
|
|
|
if (publishResult && 'event' in publishResult && 'relayStatuses' in publishResult) {
|
|
// New format with statuses
|
|
event = publishResult.event
|
|
relayStatuses = publishResult.relayStatuses
|
|
} else if (publishResult && 'id' in publishResult) {
|
|
// Old format (Event)
|
|
event = publishResult
|
|
}
|
|
|
|
if (!event) {
|
|
return buildFailure('Failed to publish article')
|
|
}
|
|
|
|
await storePrivateContent(event.id, draft.content, authorPubkey, invoice, key, iv)
|
|
console.warn('Article published with encrypted content', {
|
|
articleId: event.id,
|
|
authorPubkey,
|
|
timestamp: new Date().toISOString(),
|
|
relayStatuses,
|
|
})
|
|
|
|
return {
|
|
articleId: event.id,
|
|
previewEventId: event.id,
|
|
invoice,
|
|
success: true,
|
|
relayStatuses,
|
|
}
|
|
}
|