fix: patch all P1 bugs from project review

- pubsub: add sender.isDestroyed() checks + destroyed event auto-cleanup (P1#10)
- pubsub: add .catch() to monitor() promise + isDestroyed check (P1#11)
- connection: move server.on('error') before server.listen() (P1#12)
- preload: onSubscribeMessage/onMonitorMessage return cleanup functions (P1#9)
- CliView: save and call IPC listener cleanup in onUnmounted (P1#9)
- KeyDetail: move setInterval into onMounted, cleanup in onUnmounted (P1#8)
- key store: add loadKeyVersion guard to prevent race condition (P1#13)
- ListEditor: LSET sentinel + LREM for index-based deletion (P1#14)
- ipc-handlers: remove non-null assertion, add null guard in reorderConnections (P1#16)
- StreamEditor: add Confirm/Cancel buttons to Trim dialog (P1#17)
- KeyBrowser: remove { immediate: true } to prevent double scan (P1#18)
- P1#7 MULTI/EXEC: investigated, not a bug (Redis server handles queuing)
- docs: update PROJECT_REVIEW.md with P1 fix status
This commit is contained in:
2026-07-12 14:18:48 +08:00
parent 9d73ff019c
commit 70db25b337
12 changed files with 112 additions and 61 deletions
+3 -2
View File
@@ -95,10 +95,11 @@ export function registerIpcHandlers(): void {
ipcMain.handle('storage:reorderConnections', (_e, ids: string[]) => {
const connections = store.get('connections', [])
const reordered = ids.map((id, i) => {
const conn = connections.find((c: ConnectionConfig) => c.id === id)!
const conn = connections.find((c: ConnectionConfig) => c.id === id)
if (!conn) return null
conn.order = i
return conn
})
}).filter((c): c is ConnectionConfig => c !== null)
store.set('connections', reordered)
return reordered
})
+5 -5
View File
@@ -93,15 +93,15 @@ async function createSshTunnel(opts: ConnectionOptions): Promise<{ localPort: nu
)
})
server.listen(0, '127.0.0.1', () => {
const localPort = (server.address() as AddressInfo).port
resolve({ localPort, sshClient, server })
})
server.on('error', (err) => {
sshClient.end()
reject(new Error(`SSH tunnel server error: ${err.message}`))
})
server.listen(0, '127.0.0.1', () => {
const localPort = (server.address() as AddressInfo).port
resolve({ localPort, sshClient, server })
})
})
sshClient.on('error', (err: Error) => {
+11 -5
View File
@@ -19,20 +19,22 @@ export function startSubscribe(
const sub = new Redis(existing.options)
subClients.set(id, sub)
sender.once('destroyed', () => stopSubscribe(id))
if (isPattern) {
sub.psubscribe(...channels)
sub.on('pmessage', (_pattern, channel, message) => {
sender.send('redis:subscribeMessage', { channel, message, pattern: _pattern })
if (!sender.isDestroyed()) sender.send('redis:subscribeMessage', { channel, message, pattern: _pattern })
})
} else {
sub.subscribe(...channels)
sub.on('message', (channel, message) => {
sender.send('redis:subscribeMessage', { channel, message })
if (!sender.isDestroyed()) sender.send('redis:subscribeMessage', { channel, message })
})
}
sub.on('error', (err) => {
sender.send('redis:subscribeMessage', { error: err.message })
if (!sender.isDestroyed()) sender.send('redis:subscribeMessage', { error: err.message })
})
}
@@ -54,13 +56,17 @@ export function startMonitor(id: string, sender: WebContents): void {
const monitor = new Redis(existing.options)
monitorClients.set(id, monitor)
sender.once('destroyed', () => stopMonitor(id))
monitor.monitor().then((mon) => {
mon.on('monitor', (time: string, args: string[], source: string, database: number) => {
sender.send('redis:monitorMessage', { time, args, source, database })
if (!sender.isDestroyed()) sender.send('redis:monitorMessage', { time, args, source, database })
})
mon.on('error', (err: Error) => {
sender.send('redis:monitorMessage', { error: err.message })
if (!sender.isDestroyed()) sender.send('redis:monitorMessage', { error: err.message })
})
}).catch(err => {
if (!sender.isDestroyed()) sender.send('redis:monitorMessage', { error: err.message })
})
}