// src/main/ipc-handlers.ts // IPC 通道处理器 import { ipcMain, BrowserWindow, nativeTheme, dialog } from 'electron' 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()) ipcMain.on('window:maximize', () => { const win = BrowserWindow.getFocusedWindow() if (win) win.isMaximized() ? win.unmaximize() : win.maximize() }) ipcMain.on('window:close', () => BrowserWindow.getFocusedWindow()?.close()) // Theme ipcMain.handle('theme:get', () => nativeTheme.shouldUseDarkColors) ipcMain.on('theme:set', (_e, theme: 'system' | 'dark' | 'light') => { nativeTheme.themeSource = theme }) // Dialog ipcMain.handle('dialog:openFile', async (_e, options: Electron.OpenDialogOptions) => { const win = BrowserWindow.getFocusedWindow() if (!win) return null const result = await dialog.showOpenDialog(win, options) if (result.canceled || result.filePaths.length === 0) return null const fs = await import('fs') try { return { path: result.filePaths[0], content: fs.readFileSync(result.filePaths[0], 'utf-8') } } catch { return null } }) // Credential ipcMain.handle('credential:encrypt', (_e, text: string) => encrypt(text)) ipcMain.handle('credential:decrypt', (_e, encoded: string) => decrypt(encoded)) // Storage - connections ipcMain.handle('storage:getConnections', () => store.get('connections', [])) ipcMain.handle('storage:saveConnection', (_e, conn: ConnectionConfig) => { const connections = store.get('connections', []) const idx = connections.findIndex((c: ConnectionConfig) => c.id === conn.id) if (idx >= 0) connections[idx] = conn else connections.push(conn) store.set('connections', connections) return connections }) ipcMain.handle('storage:deleteConnection', (_e, id: string) => { const connections = store.get('connections', []).filter((c: ConnectionConfig) => c.id !== id) store.set('connections', connections) return connections }) 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)! conn.order = i return conn }) store.set('connections', reordered) return reordered }) // Storage - settings ipcMain.handle('storage:getSettings', () => store.get('settings')) ipcMain.handle('storage:saveSettings', (_e, settings) => { store.set('settings', settings) }) // Redis - 连接 ipcMain.handle('redis:connect', async (_e, id: string, opts) => { try { await redis.connect(id, opts) return { success: true } } catch (err: any) { return { success: false, error: err.message } } }) ipcMain.handle('redis:disconnect', async (_e, id: string) => { await redis.disconnect(id) }) ipcMain.handle('redis:ping', async (_e, id: string) => { return redis.ping(id) }) ipcMain.handle('redis:isConnected', (_e, id: string) => { return redis.isClientConnected(id) }) // Redis - 服务器 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', 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) }) ipcMain.handle('redis:slowLogGet', async (_e, id: string, count: number) => { return redis.slowLogGet(id, count) }) ipcMain.handle('redis:slowLogLen', async (_e, id: string) => { return redis.slowLogLen(id) }) // Redis - Key 操作 ipcMain.handle('redis:scanKeys', async (_e, id: string, cursor: string, pattern: string, count: number) => { return redis.scanKeys(id, cursor, pattern, count) }) ipcMain.handle('redis:getKeyType', async (_e, id: string, key: string) => { return redis.getKeyType(id, key) }) ipcMain.handle('redis:getKeyTTL', async (_e, id: string, key: string) => { return redis.getKeyTTL(id, key) }) ipcMain.handle('redis:setTTL', withLog('EXPIRE', async (_e, id: string, key: string, ttl: number) => { await redis.setKeyTTL(id, key, ttl) })) ipcMain.handle('redis:deleteKey', withLog('DEL', async (_e, id: string, key: string) => { await redis.deleteKey(id, key) })) 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', 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) }) // Redis - String ipcMain.handle('redis:getString', async (_e, id: string, key: string) => { return redis.getStringValue(id, key) }) ipcMain.handle('redis:setString', withLog('SET', async (_e, id: string, key: string, value: string) => { await redis.setStringValue(id, key, value) })) // ReJson ipcMain.handle('redis:rejsonGet', async (_e, id: string, key: string, path: string) => { return redis.rejsonGet(id, key, path) }) ipcMain.handle('redis:rejsonSet', withLog('JSON.SET', async (_e, id: string, key: string, path: string, value: string) => { await redis.rejsonSet(id, key, path, 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', withLog('HSET', async (_e, id: string, key: string, field: string, value: string) => { await redis.setHashField(id, key, field, value) })) 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) => { return redis.listRange(id, key, start, stop) }) ipcMain.handle('redis:listLength', async (_e, id: string, key: string) => { return redis.listLength(id, key) }) ipcMain.handle('redis:listPush', withLog('RPUSH', async (_e, id: string, key: string, values: string[]) => { return redis.listPush(id, key, ...values) })) 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', 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) => { return redis.setMembers(id, key) }) ipcMain.handle('redis:setSize', async (_e, id: string, key: string) => { return redis.setSize(id, key) }) ipcMain.handle('redis:setAdd', withLog('SADD', async (_e, id: string, key: string, members: string[]) => { return redis.setAdd(id, key, ...members) })) 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) => { return redis.zsetRange(id, key, start, stop, withScores) }) ipcMain.handle('redis:zsetSize', async (_e, id: string, key: string) => { return redis.zsetSize(id, key) }) 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', 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) => { return redis.streamInfo(id, key) }) 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', withLog('XADD', async (_e, id: string, key: string, streamId: string, fieldValues: string[]) => { return redis.streamAdd(id, key, streamId, fieldValues) })) ipcMain.handle('redis:streamTrim', withLog('XTRIM', async (_e, id: string, key: string, maxLen: number) => { return redis.streamTrim(id, key, maxLen) })) ipcMain.handle('redis:streamDel', withLog('XDEL', async (_e, id: string, key: string, ids: string[]) => { return redis.streamDel(id, key, ...ids) })) // Redis - Stream Consumer Groups ipcMain.handle('redis:streamGroups', async (_e, id: string, key: string) => { return redis.streamGroups(id, key) }) ipcMain.handle('redis:streamConsumers', async (_e, id: string, key: string, group: string) => { return redis.streamConsumers(id, key, group) }) ipcMain.handle('redis:streamGroupCreate', withLog('XGROUP CREATE', async (_e, id: string, key: string, group: string, startId: string, mkstream: boolean) => { return redis.streamGroupCreate(id, key, group, startId, mkstream) })) ipcMain.handle('redis:streamGroupDestroy', withLog('XGROUP DESTROY', async (_e, id: string, key: string, group: string) => { return redis.streamGroupDestroy(id, key, group) })) ipcMain.handle('redis:streamAck', withLog('XACK', async (_e, id: string, key: string, group: string, ids: string[]) => { return redis.streamAck(id, key, group, ...ids) })) // Command Log ipcMain.handle('redis:getCommandLog', () => { return redis.getLog() }) ipcMain.handle('redis:clearCommandLog', () => { redis.clearLog() }) // Format decode ipcMain.handle('redis:decodeValue', (_e, value: string, format: string) => { return redis.decodeValue(value, format as any) }) ipcMain.handle('redis:detectFormat', (_e, value: string) => { return redis.detectFormat(value) }) ipcMain.handle('redis:customFormat', (_e, value: string, command: string, key: string) => { return redis.customFormat(value, command, key) }) // Pub/Sub ipcMain.handle('redis:subscribe', async (_e, id: string, channels: string[], isPattern: boolean) => { try { redis.startSubscribe(id, channels, isPattern, _e.sender) return { success: true } } catch (err: any) { return { success: false, error: err.message } } }) ipcMain.handle('redis:unsubscribe', async (_e, id: string) => { redis.stopSubscribe(id) }) // Monitor ipcMain.handle('redis:monitor', async (_e, id: string) => { try { redis.startMonitor(id, _e.sender) return { success: true } } catch (err: any) { return { success: false, error: err.message } } }) ipcMain.handle('redis:monitorStop', async (_e, id: string) => { redis.stopMonitor(id) }) }