fix: patch all P2 quality/performance issues from project review

- ipc-handlers: withLog extracts actual connection ID from args[0] (P2#20)
- updater: add initialized guard to prevent duplicate IPC registration (P2#25)
- win-state: log writeFile errors instead of empty callback (P2#26)
- format: detectFormat samples up to 4096 chars instead of 100 (P2#28)
- commandLogger: add 30+ missing write commands to WRITE_COMMANDS (P2#32)
- ReJsonEditor: bind Monaco theme to monacoTheme computed (P2#19)
- keys: normalize empty SCAN pattern to '*' (P2#21)
- SlowLogView: remove duplicate <style scoped> block (P2#29)
- CliView: deduplicate XGROUP in writeCommands Set (P2#33)
- key store: use connectionStore instead of IPC for separator (P2#22)
- NewConnectionDialog: add visible guard after async watch (P2#30)
- KeyList: remove dangerouslyUseHTMLString, use plain text + i18n (P2#31)
- KeyList: batch DUMP export with Promise.allSettled (batch=20) (P2#23)
- HashEditor: lazy load field TTL on click instead of bulk HTTL (P2#24)
- P2#27 already fixed in P0#2; P2#34 is feature (N4); P2#35 not a bug
- docs: update PROJECT_REVIEW.md with P2 fix status
This commit is contained in:
2026-07-12 14:30:18 +08:00
parent 70db25b337
commit 1a5e4dd706
19 changed files with 94 additions and 110 deletions
+5 -4
View File
@@ -6,14 +6,15 @@ import * as redis from './redis'
// Helper: wrap handler with command logging
function withLog(cmd: string, fn: (...args: any[]) => any) {
return async (...args: any[]) => {
return async (_e: any, ...args: any[]) => {
const start = Date.now()
const connId = args[0] || 'unknown'
try {
const result = await fn(...args)
redis.log('current', cmd, Date.now() - start)
const result = await fn(_e, ...args)
redis.log(connId, cmd, Date.now() - start)
return result
} catch (err) {
redis.log('current', cmd, Date.now() - start)
redis.log(connId, cmd, Date.now() - start)
throw err
}
}
+9
View File
@@ -22,6 +22,15 @@ const WRITE_COMMANDS = new Set([
'LPOP', 'RPOP', 'BLPOP', 'BRPOP',
'ZINCRBY', 'ZREMRANGEBYRANK', 'ZREMRANGEBYSCORE',
'UNLINK', 'MOVE', 'RENAMENX',
'JSON.SET', 'JSON.DEL', 'JSON.ARRAPPEND', 'JSON.ARRINDEX', 'JSON.ARRINSERT',
'JSON.ARRTRIM', 'JSON.ARRPOP', 'JSON.NUMINCRBY', 'JSON.STRAPPEND', 'JSON.STRLEN',
'JSON.OBJSET', 'JSON.OBJDEL', 'COPY', 'RESTORE', 'MIGRATE',
'PEXPIRE', 'PEXPIREAT', 'PSETEX', 'SETNX',
'GEOADD', 'BITFIELD', 'SETBIT', 'HSETNX',
'LINSERT', 'RPOPLPUSH', 'LMOVE',
'SPOP', 'SMOVE', 'SDIFFSTORE', 'SINTERSTORE', 'SUNIONSTORE',
'ZPOPMAX', 'ZPOPMIN', 'ZREMRANGEBYLEX',
'XGROUP', 'XCLAIM', 'XSETID', 'SWAPDB',
])
export function log(connection: string, command: string, duration: number): void {
+2 -1
View File
@@ -43,7 +43,8 @@ export function detectFormat(value: string): FormatType {
// Deflate (zlib) magic: 78 01/9c/da
if (value.charCodeAt(0) === 0x78 && [0x01, 0x9c, 0xda, 0x5e].includes(value.charCodeAt(1))) return 'deflate'
// Check for non-printable characters → binary/hex
const hasNonPrintable = /[\x00-\x08\x0e-\x1f]/.test(value.substring(0, 100))
const sample = value.length > 4096 ? value.substring(0, 4096) : value
const hasNonPrintable = /[\x00-\x08\x0e-\x1f]/.test(sample)
if (hasNonPrintable) return 'hex'
return 'text'
}
+1
View File
@@ -93,6 +93,7 @@ export async function scanKeys(
count: number
): Promise<{ cursor: string; keys: string[] }> {
const client = requireClient(id)
if (!pattern || pattern === '') pattern = '*'
const [nextCursor, keys] = await client.scan(cursor, 'MATCH', pattern, 'COUNT', count)
return { cursor: nextCursor, keys }
}
+3
View File
@@ -6,8 +6,11 @@ import electronUpdater from 'electron-updater'
import { BrowserWindow, ipcMain } from 'electron'
let mainWindow: BrowserWindow | null = null
let initialized = false
export function initUpdater(win: BrowserWindow): void {
if (initialized) return
initialized = true
mainWindow = win
const autoUpdater = electronUpdater.autoUpdater
+3 -1
View File
@@ -44,7 +44,9 @@ function getLastState(): WinState {
function saveState(win: BrowserWindow): void {
const bounds = win.getBounds()
const state = { ...bounds, maximized: win.isMaximized() }
writeFile(STATE_FILE, JSON.stringify(state), 'utf-8', () => {})
writeFile(STATE_FILE, JSON.stringify(state), 'utf-8', (err) => {
if (err) console.error('Failed to save window state:', err)
})
}
export function restoreAndTrack(win: BrowserWindow): void {