refactor: simplify bunker implementation following applesauce patterns

- Remove bunkerFixVersion migration logic
- Simplify account loading to match applesauce examples
- Simplify reconnectBunkerSigner (no waiting, no complex logging)
- Direct nip04/nip44 exposure from signer (like ExtensionAccount)
- Clean up bookmark service account checking
- Keep debug logs for now until verified working
This commit is contained in:
Gigi
2025-10-16 22:48:46 +02:00
parent 39c8b3dfe4
commit 77cbb9394f
3 changed files with 21 additions and 150 deletions

View File

@@ -199,34 +199,15 @@ function App() {
pool.group(RELAYS)
// Load persisted accounts from localStorage
try {
const accountsJson = localStorage.getItem('accounts')
if (accountsJson) {
const parsed = JSON.parse(accountsJson)
// Clear old bunker accounts (they were created with wrong setup)
const bunkerFixVersion = localStorage.getItem('bunkerFixVersion')
if (bunkerFixVersion !== '1') {
console.log('[bunker] Clearing old bunker accounts (need to reconnect with fixed setup)')
const nonBunkerAccounts = parsed.filter((acc: any) => acc.type !== 'nostr-connect')
if (nonBunkerAccounts.length > 0) {
await accounts.fromJSON(nonBunkerAccounts)
}
localStorage.setItem('bunkerFixVersion', '1')
localStorage.removeItem('active')
} else {
await accounts.fromJSON(parsed)
// Restore active account
const activeId = localStorage.getItem('active')
if (activeId && accounts.getAccount(activeId)) {
accounts.setActive(activeId)
}
}
}
} catch (err) {
console.error('[bunker] Failed to restore accounts:', err)
// Load persisted accounts from localStorage (per applesauce examples)
const savedAccounts = JSON.parse(localStorage.getItem('accounts') || '[]')
await accounts.fromJSON(savedAccounts)
// Restore active account
const activeAccountId = localStorage.getItem('active')
if (activeAccountId) {
const account = accounts.getAccount(activeAccountId)
if (account) accounts.setActive(account)
}
// Persist accounts to localStorage
@@ -242,18 +223,12 @@ function App() {
}
})
// Reconnect bunker signers on page load
// Reconnect bunker signers on page load (per applesauce pattern)
const reconnectedAccounts = new Set<string>()
const bunkerReconnectSub = accounts.active$.subscribe(async (account) => {
if (account?.type === 'nostr-connect' && !reconnectedAccounts.has(account.id)) {
reconnectedAccounts.add(account.id)
try {
await reconnectBunkerSigner(account as Accounts.NostrConnectAccount<unknown>, pool)
console.log('[bunker] Reconnected to bunker signer')
} catch (error) {
console.error('[bunker] Failed to reconnect signer:', error)
}
await reconnectBunkerSigner(account as Accounts.NostrConnectAccount<unknown>, pool)
}
})

View File

@@ -83,49 +83,16 @@ export const fetchBookmarks = async (
// Keep existing bookmarks visible; do not clear list if nothing new found
return
}
// Aggregate across events
// Get account with signer for decryption
const maybeAccount = activeAccount as AccountWithExtension
console.log('[bunker] 🔐 Account object:', {
hasSignEvent: typeof maybeAccount?.signEvent === 'function',
hasSigner: !!maybeAccount?.signer,
accountType: maybeAccount?.type || typeof maybeAccount,
accountKeys: maybeAccount ? Object.keys(maybeAccount) : []
})
// For ExtensionAccount, we need a signer with nip04/nip44 for decrypting hidden content
// The ExtensionAccount itself has nip04/nip44 getters that proxy to the signer
let signerCandidate: unknown = maybeAccount
// Fallback to raw signer if account doesn't expose nip04/nip44
const hasNip04Prop = (signerCandidate as { nip04?: unknown })?.nip04 !== undefined
const hasNip44Prop = (signerCandidate as { nip44?: unknown })?.nip44 !== undefined
console.log('[bunker] 🔍 Account nip04/nip44 check:', {
hasNip04Prop,
hasNip44Prop,
nip04Type: typeof (signerCandidate as { nip04?: unknown })?.nip04,
nip44Type: typeof (signerCandidate as { nip44?: unknown })?.nip44
})
if (signerCandidate && !hasNip04Prop && !hasNip44Prop && maybeAccount?.signer) {
// Fallback to the raw signer if account doesn't have nip04/nip44
console.log('[bunker] ⚠️ Account missing nip04/nip44, falling back to signer')
signerCandidate = maybeAccount.signer
const signerHasNip04 = (signerCandidate as { nip04?: unknown })?.nip04 !== undefined
const signerHasNip44 = (signerCandidate as { nip44?: unknown })?.nip44 !== undefined
console.log('[bunker] 🔍 Signer nip04/nip44 check:', {
signerHasNip04,
signerHasNip44,
nip04Type: typeof (signerCandidate as { nip04?: unknown })?.nip04,
nip44Type: typeof (signerCandidate as { nip44?: unknown })?.nip44
})
}
console.log('[bunker] 🔑 Final signer candidate:', {
exists: !!signerCandidate,
type: typeof signerCandidate,
hasNip04: hasNip04Decrypt(signerCandidate),
hasNip44: hasNip44Decrypt(signerCandidate)
})
const { publicItemsAll, privateItemsAll, newestCreatedAt, latestContent, allTags } = await collectBookmarksFromEvents(
bookmarkListEvents,
activeAccount,

View File

@@ -28,102 +28,31 @@ export function getDefaultBunkerPermissions(): string[] {
/**
* Reconnect a bunker signer after page load
* Ensures the signer is listening and connected to the correct relays
* Ensures the signer is listening and ready for signing/decryption
*/
export async function reconnectBunkerSigner(
account: Accounts.NostrConnectAccount<unknown>,
pool: RelayPool
): Promise<void> {
// Add bunker relays to pool for signing communication
// Add bunker relays to pool
if (account.signer.relays) {
const bunkerRelays = account.signer.relays
pool.group(bunkerRelays)
// Wait for at least one bunker relay to be connected
// This ensures signing/decryption requests can be sent
console.log('[bunker] Waiting for relay connections...', bunkerRelays)
await new Promise<void>((resolve) => {
const checkInterval = setInterval(() => {
const connectedRelays = bunkerRelays.filter(url => {
const relay = pool.relays.get(url)
return relay?.connected
})
if (connectedRelays.length > 0) {
console.log('[bunker] ✅ Connected to', connectedRelays.length, 'bunker relay(s)')
clearInterval(checkInterval)
resolve()
}
}, 100)
// Timeout after 5 seconds
setTimeout(() => {
clearInterval(checkInterval)
console.warn('[bunker] ⚠️ Timeout waiting for relay connections, proceeding anyway')
resolve()
}, 5000)
})
pool.group(account.signer.relays)
}
// Open signer subscription if not already listening
// Open signer subscription for NIP-46 responses
if (!account.signer.listening) {
console.log('[bunker] Opening signer subscription for NIP-46 responses...')
await account.signer.open()
console.log('[bunker] ✅ Signer subscription active, listening for bunker responses')
} else {
console.log('[bunker] Signer already listening')
}
// Mark as connected (bunker remembers permissions from initial connection)
account.signer.isConnected = true
console.log('[bunker] Signer marked as connected, ready for signing/decryption')
// Expose nip04/nip44 at account level for compatibility with logging
// Cache wrapped methods to ensure they're used consistently
// Expose nip04/nip44 at account level (like ExtensionAccount does)
if (!('nip04' in account)) {
const nip04Wrapped = {
encrypt: async (pubkey: string, plaintext: string) => {
console.log('[bunker] 🔐 nip04.encrypt called', { pubkey: pubkey.slice(0, 8) })
const result = await account.signer.nip04!.encrypt(pubkey, plaintext)
console.log('[bunker] ✅ nip04.encrypt completed')
return result
},
decrypt: async (pubkey: string, ciphertext: string) => {
console.log('[bunker] 🔓 nip04.decrypt called', { pubkey: pubkey.slice(0, 8), ciphertextLength: ciphertext.length })
try {
const result = await account.signer.nip04!.decrypt(pubkey, ciphertext)
console.log('[bunker] ✅ nip04.decrypt completed')
return result
} catch (err) {
console.error('[bunker] ❌ nip04.decrypt failed:', err)
throw err
}
}
};
(account as any).nip04 = nip04Wrapped
(account as any).nip04 = account.signer.nip04
}
if (!('nip44' in account)) {
const nip44Wrapped = {
encrypt: async (pubkey: string, plaintext: string) => {
console.log('[bunker] 🔐 nip44.encrypt called', { pubkey: pubkey.slice(0, 8) })
const result = await account.signer.nip44!.encrypt(pubkey, plaintext)
console.log('[bunker] ✅ nip44.encrypt completed')
return result
},
decrypt: async (pubkey: string, ciphertext: string) => {
console.log('[bunker] 🔓 nip44.decrypt called', { pubkey: pubkey.slice(0, 8), ciphertextLength: ciphertext.length })
try {
const result = await account.signer.nip44!.decrypt(pubkey, ciphertext)
console.log('[bunker] ✅ nip44.decrypt completed', { plaintextLength: result.length })
return result
} catch (err) {
console.error('[bunker] ❌ nip44.decrypt failed:', err)
throw err
}
}
};
(account as any).nip44 = nip44Wrapped
(account as any).nip44 = account.signer.nip44
}
}