feat: Command Log dialog

- New commandLogger.ts: in-memory log (max 5000 entries)
- withLog() wrapper in ipc-handlers.ts for all write commands
- CommandLog.vue: table with time/command/duration, write-only filter
- IPC: redis:getCommandLog, redis:clearCommandLog
- Ctrl+G shortcut from Sidebar
- i18n keys (zh-CN + en)
This commit is contained in:
2026-07-05 21:48:33 +08:00
parent 15869943e8
commit d131a2732d
10 changed files with 270 additions and 38 deletions
+60 -37
View File
@@ -5,6 +5,21 @@ import store, { ConnectionConfig } from './store'
import { encrypt, decrypt } from './credential'
import * as redis from './redis'
// Helper: wrap handler with command logging
function withLog(cmd: string, fn: (...args: any[]) => any) {
return async (...args: any[]) => {
const start = Date.now()
try {
const result = await fn(...args)
redis.log('current', cmd, Date.now() - start)
return result
} catch (err) {
redis.log('current', cmd, Date.now() - start)
throw err
}
}
}
export function registerIpcHandlers(): void {
// Window
ipcMain.on('window:minimize', () => BrowserWindow.getFocusedWindow()?.minimize())
@@ -88,15 +103,15 @@ export function registerIpcHandlers(): void {
})
// Redis - 服务器
ipcMain.handle('redis:execute', async (_e, id: string, command: string, args: string[]) => {
ipcMain.handle('redis:execute', withLog('EXEC', async (_e, id: string, command: string, args: string[]) => {
return redis.execute(id, command, args)
})
}))
ipcMain.handle('redis:getInfo', async (_e, id: string) => {
return redis.getServerInfo(id)
})
ipcMain.handle('redis:selectDb', async (_e, id: string, db: number) => {
ipcMain.handle('redis:selectDb', withLog('SELECT', async (_e, id: string, db: number) => {
await redis.selectDb(id, db)
})
}))
ipcMain.handle('redis:dbSize', async (_e, id: string) => {
return redis.dbSize(id)
})
@@ -117,21 +132,21 @@ export function registerIpcHandlers(): void {
ipcMain.handle('redis:getKeyTTL', async (_e, id: string, key: string) => {
return redis.getKeyTTL(id, key)
})
ipcMain.handle('redis:setTTL', async (_e, id: string, key: string, ttl: number) => {
ipcMain.handle('redis:setTTL', withLog('EXPIRE', async (_e, id: string, key: string, ttl: number) => {
await redis.setKeyTTL(id, key, ttl)
})
ipcMain.handle('redis:deleteKey', async (_e, id: string, key: string) => {
}))
ipcMain.handle('redis:deleteKey', withLog('DEL', async (_e, id: string, key: string) => {
await redis.deleteKey(id, key)
})
ipcMain.handle('redis:renameKey', async (_e, id: string, oldKey: string, newKey: string) => {
}))
ipcMain.handle('redis:renameKey', withLog('RENAME', async (_e, id: string, oldKey: string, newKey: string) => {
await redis.renameKey(id, oldKey, newKey)
})
}))
ipcMain.handle('redis:existsKey', async (_e, id: string, key: string) => {
return redis.existsKey(id, key)
})
ipcMain.handle('redis:batchDelete', async (_e, id: string, keys: string[]) => {
ipcMain.handle('redis:batchDelete', withLog('DEL batch', async (_e, id: string, keys: string[]) => {
return redis.batchDelete(id, keys)
})
}))
ipcMain.handle('redis:memoryUsage', async (_e, id: string, key: string) => {
return redis.memoryUsage(id, key)
})
@@ -140,20 +155,20 @@ export function registerIpcHandlers(): void {
ipcMain.handle('redis:getString', async (_e, id: string, key: string) => {
return redis.getStringValue(id, key)
})
ipcMain.handle('redis:setString', async (_e, id: string, key: string, value: string) => {
ipcMain.handle('redis:setString', withLog('SET', async (_e, id: string, key: string, value: string) => {
await redis.setStringValue(id, key, value)
})
}))
// Redis - Hash
ipcMain.handle('redis:hashScan', async (_e, id: string, key: string, cursor: string, count: number) => {
return redis.getHashFields(id, key, cursor, count)
})
ipcMain.handle('redis:hashSet', async (_e, id: string, key: string, field: string, value: string) => {
ipcMain.handle('redis:hashSet', withLog('HSET', async (_e, id: string, key: string, field: string, value: string) => {
await redis.setHashField(id, key, field, value)
})
ipcMain.handle('redis:hashDel', async (_e, id: string, key: string, field: string) => {
}))
ipcMain.handle('redis:hashDel', withLog('HDEL', async (_e, id: string, key: string, field: string) => {
await redis.deleteHashField(id, key, field)
})
}))
// Redis - List
ipcMain.handle('redis:listRange', async (_e, id: string, key: string, start: number, stop: number) => {
@@ -162,15 +177,15 @@ export function registerIpcHandlers(): void {
ipcMain.handle('redis:listLength', async (_e, id: string, key: string) => {
return redis.listLength(id, key)
})
ipcMain.handle('redis:listPush', async (_e, id: string, key: string, values: string[]) => {
ipcMain.handle('redis:listPush', withLog('RPUSH', async (_e, id: string, key: string, values: string[]) => {
return redis.listPush(id, key, ...values)
})
ipcMain.handle('redis:listSet', async (_e, id: string, key: string, index: number, value: string) => {
}))
ipcMain.handle('redis:listSet', withLog('LSET', async (_e, id: string, key: string, index: number, value: string) => {
await redis.listSet(id, key, index, value)
})
ipcMain.handle('redis:listRemove', async (_e, id: string, key: string, count: number, value: string) => {
}))
ipcMain.handle('redis:listRemove', withLog('LREM', async (_e, id: string, key: string, count: number, value: string) => {
return redis.listRemove(id, key, count, value)
})
}))
// Redis - Set
ipcMain.handle('redis:setMembers', async (_e, id: string, key: string) => {
@@ -179,12 +194,12 @@ export function registerIpcHandlers(): void {
ipcMain.handle('redis:setSize', async (_e, id: string, key: string) => {
return redis.setSize(id, key)
})
ipcMain.handle('redis:setAdd', async (_e, id: string, key: string, members: string[]) => {
ipcMain.handle('redis:setAdd', withLog('SADD', async (_e, id: string, key: string, members: string[]) => {
return redis.setAdd(id, key, ...members)
})
ipcMain.handle('redis:setRemove', async (_e, id: string, key: string, members: string[]) => {
}))
ipcMain.handle('redis:setRemove', withLog('SREM', async (_e, id: string, key: string, members: string[]) => {
return redis.setRemove(id, key, ...members)
})
}))
// Redis - Zset
ipcMain.handle('redis:zsetRange', async (_e, id: string, key: string, start: number, stop: number, withScores: boolean) => {
@@ -193,12 +208,12 @@ export function registerIpcHandlers(): void {
ipcMain.handle('redis:zsetSize', async (_e, id: string, key: string) => {
return redis.zsetSize(id, key)
})
ipcMain.handle('redis:zsetAdd', async (_e, id: string, key: string, score: number, member: string) => {
ipcMain.handle('redis:zsetAdd', withLog('ZADD', async (_e, id: string, key: string, score: number, member: string) => {
return redis.zsetAdd(id, key, score, member)
})
ipcMain.handle('redis:zsetRemove', async (_e, id: string, key: string, members: string[]) => {
}))
ipcMain.handle('redis:zsetRemove', withLog('ZREM', async (_e, id: string, key: string, members: string[]) => {
return redis.zsetRemove(id, key, ...members)
})
}))
// Redis - Stream
ipcMain.handle('redis:streamInfo', async (_e, id: string, key: string) => {
@@ -207,13 +222,21 @@ export function registerIpcHandlers(): void {
ipcMain.handle('redis:streamRange', async (_e, id: string, key: string, start: string, end: string, count: number) => {
return redis.streamRange(id, key, start, end, count)
})
ipcMain.handle('redis:streamAdd', async (_e, id: string, key: string, streamId: string, fieldValues: string[]) => {
ipcMain.handle('redis:streamAdd', withLog('XADD', async (_e, id: string, key: string, streamId: string, fieldValues: string[]) => {
return redis.streamAdd(id, key, streamId, fieldValues)
})
ipcMain.handle('redis:streamTrim', async (_e, id: string, key: string, maxLen: number) => {
}))
ipcMain.handle('redis:streamTrim', withLog('XTRIM', async (_e, id: string, key: string, maxLen: number) => {
return redis.streamTrim(id, key, maxLen)
})
ipcMain.handle('redis:streamDel', async (_e, id: string, key: string, ids: string[]) => {
}))
ipcMain.handle('redis:streamDel', withLog('XDEL', async (_e, id: string, key: string, ids: string[]) => {
return redis.streamDel(id, key, ...ids)
}))
// Command Log
ipcMain.handle('redis:getCommandLog', () => {
return redis.getLog()
})
ipcMain.handle('redis:clearCommandLog', () => {
redis.clearLog()
})
}