feat(phase2): connection management
- RedisService in main process (ioredis wrapper, execute, scan, hash ops) - electron-store for connection/settings persistence - safeStorage credential encrypt/decrypt - IPC handlers for redis, storage, credential channels - Preload API typed with all new channels - useConnectionStore (Pinia) with connect/disconnect/save/delete - ConnectionCard with 3D hover effect + status indicator - ConnectionList with search filter - NewConnectionDialog (create/edit connection form) - Sidebar component - StatusBar with live connection status + pulse animation - App.vue updated with Sidebar layout
This commit is contained in:
+96
-13
@@ -1,35 +1,118 @@
|
||||
// src/main/ipc-handlers.ts
|
||||
import { ipcMain, BrowserWindow, nativeTheme, dialog } from 'electron'
|
||||
import store, { ConnectionConfig } from './store'
|
||||
import { encrypt, decrypt } from './credential'
|
||||
import * as redisService from './redis-service'
|
||||
|
||||
export function registerIpcHandlers(): void {
|
||||
ipcMain.on('window:minimize', () => {
|
||||
BrowserWindow.getFocusedWindow()?.minimize()
|
||||
})
|
||||
|
||||
// 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())
|
||||
|
||||
ipcMain.on('window:close', () => {
|
||||
BrowserWindow.getFocusedWindow()?.close()
|
||||
})
|
||||
|
||||
// Theme
|
||||
ipcMain.handle('theme:get', () => nativeTheme.shouldUseDarkColors)
|
||||
|
||||
ipcMain.on('theme:set', (_event, theme: 'system' | 'dark' | 'light') => {
|
||||
ipcMain.on('theme:set', (_e, theme: 'system' | 'dark' | 'light') => {
|
||||
nativeTheme.themeSource = theme
|
||||
})
|
||||
|
||||
ipcMain.handle('dialog:openFile', async (_event, options: Electron.OpenDialogOptions) => {
|
||||
// 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 {
|
||||
const content = fs.readFileSync(result.filePaths[0], 'utf-8')
|
||||
return { path: result.filePaths[0], content }
|
||||
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 redisService.connect(id, opts)
|
||||
return { success: true }
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message }
|
||||
}
|
||||
})
|
||||
ipcMain.handle('redis:disconnect', async (_e, id: string) => {
|
||||
await redisService.disconnect(id)
|
||||
})
|
||||
ipcMain.handle('redis:execute', async (_e, id: string, command: string, args: string[]) => {
|
||||
return redisService.execute(id, command, args)
|
||||
})
|
||||
ipcMain.handle('redis:getInfo', async (_e, id: string) => {
|
||||
return redisService.getServerInfo(id)
|
||||
})
|
||||
ipcMain.handle('redis:scanKeys', async (_e, id: string, cursor: string, pattern: string, count: number) => {
|
||||
return redisService.scanKeys(id, cursor, pattern, count)
|
||||
})
|
||||
ipcMain.handle('redis:getKeyType', async (_e, id: string, key: string) => {
|
||||
return redisService.getKeyType(id, key)
|
||||
})
|
||||
ipcMain.handle('redis:getKeyTTL', async (_e, id: string, key: string) => {
|
||||
return redisService.getKeyTTL(id, key)
|
||||
})
|
||||
ipcMain.handle('redis:getString', async (_e, id: string, key: string) => {
|
||||
return redisService.getStringValue(id, key)
|
||||
})
|
||||
ipcMain.handle('redis:setString', async (_e, id: string, key: string, value: string) => {
|
||||
await redisService.setStringValue(id, key, value)
|
||||
})
|
||||
ipcMain.handle('redis:deleteKey', async (_e, id: string, key: string) => {
|
||||
await redisService.deleteKey(id, key)
|
||||
})
|
||||
ipcMain.handle('redis:hashScan', async (_e, id: string, key: string, cursor: string, count: number) => {
|
||||
return redisService.getHashFields(id, key, cursor, count)
|
||||
})
|
||||
ipcMain.handle('redis:hashSet', async (_e, id: string, key: string, field: string, value: string) => {
|
||||
await redisService.setHashField(id, key, field, value)
|
||||
})
|
||||
ipcMain.handle('redis:hashDel', async (_e, id: string, key: string, field: string) => {
|
||||
await redisService.deleteHashField(id, key, field)
|
||||
})
|
||||
ipcMain.handle('redis:setTTL', async (_e, id: string, key: string, ttl: number) => {
|
||||
await redisService.setKeyTTL(id, key, ttl)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user