|
3 | 3 | const WebSocket = require('ws') |
4 | 4 | const { getPublicKey, getSignature, getEventHash, nip04 } = require('nostr-tools') |
5 | 5 | const { hexToBytes } = require('@noble/hashes/utils') |
6 | | - |
| 6 | +const keychain = require('./keychain') |
7 | 7 | let activeWs = null |
8 | 8 | let activeConn = null |
9 | 9 | let pendingCalls = new Map() |
| 10 | +async function decryptStoredSecret(row) { |
| 11 | + if (row.encrypted_secret) { |
| 12 | + const key = await keychain.getOrCreateKey() |
| 13 | + return keychain.decrypt(row.encrypted_secret, key) |
| 14 | + } |
10 | 15 |
|
| 16 | + // Backward compatibility with older databases |
| 17 | + return row.secret |
| 18 | +} |
11 | 19 | function parseNwcUri(uri) { |
12 | 20 | const normalised = uri.trim() |
13 | 21 | .replace('nostr+walletconnect://', 'nwc://') |
@@ -91,30 +99,97 @@ async function nwcRequest(method, params = {}) { |
91 | 99 | }) |
92 | 100 | } |
93 | 101 |
|
| 102 | + |
94 | 103 | async function connect(DB, { nwcUri, name }) { |
95 | 104 | const parsed = parseNwcUri(nwcUri) |
| 105 | + |
96 | 106 | await openWs(parsed.relay, parsed.pubkey, parsed.secret) |
| 107 | + |
| 108 | + const key = await keychain.getOrCreateKey() |
| 109 | + const encryptedSecret = keychain.encrypt(parsed.secret, key) |
| 110 | + |
97 | 111 | const db = DB._db() |
| 112 | + |
98 | 113 | db.prepare('UPDATE nwc_connections SET active=0').run() |
| 114 | + |
99 | 115 | const r = db |
100 | | - .prepare('INSERT INTO nwc_connections(name,relay_url,wallet_pubkey,secret,active,created_at) VALUES(?,?,?,?,1,?)') |
101 | | - .run(name || 'My node', parsed.relay, parsed.pubkey, parsed.secret, Math.floor(Date.now() / 1000)) |
102 | | - activeConn = { relay_url: parsed.relay, wallet_pubkey: parsed.pubkey, secret: parsed.secret } |
103 | | - return { id: r.lastInsertRowid, name, relay: parsed.relay, pubkey: parsed.pubkey, active: true } |
| 116 | + .prepare(` |
| 117 | + INSERT INTO nwc_connections |
| 118 | + (name, relay_url, wallet_pubkey, secret, encrypted_secret, active, created_at) |
| 119 | + VALUES (?, ?, ?, ?, ?, 1, ?) |
| 120 | + `) |
| 121 | + .run( |
| 122 | + name || 'My node', |
| 123 | + parsed.relay, |
| 124 | + parsed.pubkey, |
| 125 | + '', // legacy field intentionally blank |
| 126 | + encryptedSecret, |
| 127 | + Math.floor(Date.now() / 1000) |
| 128 | + ) |
| 129 | + |
| 130 | + activeConn = { |
| 131 | + relay_url: parsed.relay, |
| 132 | + wallet_pubkey: parsed.pubkey, |
| 133 | + secret: parsed.secret, |
| 134 | + } |
| 135 | + |
| 136 | + return { |
| 137 | + id: r.lastInsertRowid, |
| 138 | + name, |
| 139 | + relay: parsed.relay, |
| 140 | + pubkey: parsed.pubkey, |
| 141 | + active: true, |
| 142 | + } |
104 | 143 | } |
105 | 144 |
|
106 | 145 | async function reconnectFromDB(DB) { |
107 | | - const row = DB._db().prepare('SELECT * FROM nwc_connections WHERE active=1').get() |
| 146 | + const row = DB._db() |
| 147 | + .prepare('SELECT * FROM nwc_connections WHERE active=1') |
| 148 | + .get() |
| 149 | + |
108 | 150 | if (!row) return false |
| 151 | + |
109 | 152 | try { |
110 | | - await openWs(row.relay_url, row.wallet_pubkey, row.secret) |
111 | | - activeConn = row |
| 153 | + const secret = await decryptStoredSecret(row) |
| 154 | + |
| 155 | + await openWs( |
| 156 | + row.relay_url, |
| 157 | + row.wallet_pubkey, |
| 158 | + secret |
| 159 | + ) |
| 160 | + |
| 161 | + // Auto-migrate legacy plaintext entries |
| 162 | + if (!row.encrypted_secret && row.secret) { |
| 163 | + const key = await keychain.getOrCreateKey() |
| 164 | + const encryptedSecret = keychain.encrypt(row.secret, key) |
| 165 | + |
| 166 | + DB._db() |
| 167 | + .prepare(` |
| 168 | + UPDATE nwc_connections |
| 169 | + SET encrypted_secret = ?, secret = '' |
| 170 | + WHERE id = ? |
| 171 | + `) |
| 172 | + .run(encryptedSecret, row.id) |
| 173 | + } |
| 174 | + |
| 175 | + activeConn = { |
| 176 | + ...row, |
| 177 | + secret, |
| 178 | + } |
| 179 | + |
112 | 180 | return true |
113 | | - } catch (_) { |
| 181 | + } catch (err) { |
| 182 | + console.error('Failed to reconnect NWC:', err.message) |
114 | 183 | return false |
115 | 184 | } |
116 | 185 | } |
117 | 186 |
|
| 187 | + |
| 188 | + |
| 189 | + |
| 190 | + |
| 191 | + |
| 192 | + |
118 | 193 | function disconnect(DB) { |
119 | 194 | if (activeWs) { try { activeWs.close() } catch (_) {} activeWs = null } |
120 | 195 | activeConn = null |
|
0 commit comments