feat: await owner-aware Nostr restores

This commit is contained in:
2026-08-22 16:55:12 +02:00
parent 7607e47358
commit f55f208836
2 changed files with 25 additions and 33 deletions
+17 -20
View File
@@ -10,12 +10,9 @@
import type {RestoreResult} from '../storage/backup' import type {RestoreResult} from '../storage/backup'
import {applyBackup} from '../storage/backup' import {applyBackup} from '../storage/backup'
import {linkingPubKeyHex} from '../keys'
import type { import type {BackupPart, BackupPartPayload, NostrEvent} from './events'
BackupPart,
BackupPartPayload,
NostrEvent
} from './events'
import { import {
BACKUP_EVENT_KIND, BACKUP_EVENT_KIND,
BACKUP_PARTS, BACKUP_PARTS,
@@ -23,7 +20,7 @@ import {
buildBackupEvent, buildBackupEvent,
deriveBackupKey, deriveBackupKey,
dTagOf, dTagOf,
parseBackupEvent parseBackupEvent,
} from './events' } from './events'
import type {BackupTransport} from './transport' import type {BackupTransport} from './transport'
import {defaultTransport} from './transport' import {defaultTransport} from './transport'
@@ -42,20 +39,17 @@ export const publishBackup = async (
secretKey: Uint8Array, secretKey: Uint8Array,
parts: Partial<BackupPartPayload>, parts: Partial<BackupPartPayload>,
relays: string[], relays: string[],
options: PublishBackupOptions = {} options: PublishBackupOptions = {},
): Promise<PublishBackupResult> => { ): Promise<PublishBackupResult> => {
const at = options.createdAt ?? Math.floor(Date.now() / 1000) const at = options.createdAt ?? Math.floor(Date.now() / 1000)
const present = BACKUP_PARTS.filter(part => parts[part] !== undefined) const present = BACKUP_PARTS.filter((part) => parts[part] !== undefined)
if (present.length === 0) return {published: []} if (present.length === 0) return {published: []}
const transport = options.transport ?? (await defaultTransport()) const transport = options.transport ?? (await defaultTransport())
const published: BackupPart[] = [] const published: BackupPart[] = []
for (const part of present) { for (const part of present) {
const payload = parts[part] const payload = parts[part]
if (payload === undefined) continue if (payload === undefined) continue
await transport.publish( await transport.publish(relays, buildBackupEvent(secretKey, part, payload, at))
relays,
buildBackupEvent(secretKey, part, payload, at)
)
published.push(part) published.push(part)
} }
return {published} return {published}
@@ -75,12 +69,12 @@ export type FetchBackupOptions = {
export const fetchBackup = async ( export const fetchBackup = async (
pubkey: string, pubkey: string,
relays: string[], relays: string[],
options: FetchBackupOptions options: FetchBackupOptions,
): Promise<Partial<BackupPartPayload>> => { ): Promise<Partial<BackupPartPayload>> => {
const transport = options.transport ?? (await defaultTransport()) const transport = options.transport ?? (await defaultTransport())
const events = await transport.fetch(relays, { const events = await transport.fetch(relays, {
kinds: [BACKUP_EVENT_KIND], kinds: [BACKUP_EVENT_KIND],
authors: [pubkey] authors: [pubkey],
}) })
const byTag = new Map<string, NostrEvent[]>() const byTag = new Map<string, NostrEvent[]>()
for (const event of events) { for (const event of events) {
@@ -130,23 +124,26 @@ export type NostrRestoreResult = RestoreResult & {
export const restoreFromNostr = async ( export const restoreFromNostr = async (
linkingPrivKey: Uint8Array, linkingPrivKey: Uint8Array,
relays: string[], relays: string[],
options: {transport?: BackupTransport} = {} options: {transport?: BackupTransport} = {},
): Promise<NostrRestoreResult> => { ): Promise<NostrRestoreResult> => {
const secretKey = deriveBackupKey(linkingPrivKey) const secretKey = deriveBackupKey(linkingPrivKey)
const parts = await fetchBackup(backupPubkey(secretKey), relays, { const parts = await fetchBackup(backupPubkey(secretKey), relays, {
secretKey, secretKey,
transport: options.transport transport: options.transport,
}) })
const result = applyBackup({ const result = await applyBackup(
{
type: 'sattle-backup', type: 'sattle-backup',
version: 1, version: 1,
createdAt: Date.now(), createdAt: Date.now(),
bearers: parts.notes ?? [], bearers: parts.notes ?? [],
trustedMints: parts.mints, trustedMints: parts.mints,
settings: parts.settings settings: parts.settings,
}) },
linkingPubKeyHex(linkingPrivKey),
)
return { return {
...result, ...result,
found: BACKUP_PARTS.filter(part => parts[part] !== undefined) found: BACKUP_PARTS.filter((part) => parts[part] !== undefined),
} }
} }
+3 -8
View File
@@ -24,14 +24,9 @@ export {
backupPubkey, backupPubkey,
buildBackupEvent, buildBackupEvent,
buildBackupEvents, buildBackupEvents,
parseBackupEvent parseBackupEvent,
} from './nostr/events'
export type {
NostrEvent,
BackupPart,
BackupPartPayload,
ParsedBackupEvent
} from './nostr/events' } from './nostr/events'
export type {NostrEvent, BackupPart, BackupPartPayload, ParsedBackupEvent} from './nostr/events'
export type {BackupTransport, NostrFilter} from './nostr/transport' export type {BackupTransport, NostrFilter} from './nostr/transport'
@@ -40,7 +35,7 @@ export type {
PublishBackupOptions, PublishBackupOptions,
PublishBackupResult, PublishBackupResult,
FetchBackupOptions, FetchBackupOptions,
NostrRestoreResult NostrRestoreResult,
} from './nostr/sync' } from './nostr/sync'
export {createBackupPublisher} from './nostr/publisher' export {createBackupPublisher} from './nostr/publisher'