feat(phase5): stream editor, i18n, settings, key management

- StreamEditor: view/add/trim/delete entries with field management
- KeyDetail: TTL editor dialog, rename dialog, copy key name button
- i18n: English + Chinese locale system with composable
- SettingsView: theme selector, language switch, scan count, font size
- MainArea: added Settings tab
- RedisService: stream/rename/exists operations
- Preload: stream/rename/exists IPC channels
This commit is contained in:
2026-07-04 18:26:51 +08:00
parent 4c66263477
commit 6b9c6a1ed5
14 changed files with 903 additions and 8 deletions
+25
View File
@@ -182,4 +182,29 @@ export function registerIpcHandlers(): void {
ipcMain.handle('redis:batchDelete', async (_e, id: string, keys: string[]) => {
return redisService.batchDelete(id, keys)
})
// Stream
ipcMain.handle('redis:streamInfo', async (_e, id: string, key: string) => {
return redisService.streamInfo(id, key)
})
ipcMain.handle('redis:streamRange', async (_e, id: string, key: string, start: string, end: string, count: number) => {
return redisService.streamRange(id, key, start, end, count)
})
ipcMain.handle('redis:streamAdd', async (_e, id: string, key: string, streamId: string, fieldValues: string[]) => {
return redisService.streamAdd(id, key, streamId, fieldValues)
})
ipcMain.handle('redis:streamTrim', async (_e, id: string, key: string, maxLen: number) => {
return redisService.streamTrim(id, key, maxLen)
})
ipcMain.handle('redis:streamDel', async (_e, id: string, key: string, ids: string[]) => {
return redisService.streamDel(id, key, ...ids)
})
// Key ops
ipcMain.handle('redis:renameKey', async (_e, id: string, oldKey: string, newKey: string) => {
await redisService.renameKey(id, oldKey, newKey)
})
ipcMain.handle('redis:existsKey', async (_e, id: string, key: string) => {
return redisService.existsKey(id, key)
})
}
+47
View File
@@ -290,3 +290,50 @@ export async function batchDelete(id: string, keys: string[]): Promise<number> {
const results = await pipeline.exec()
return results?.filter(([err]) => !err).length ?? 0
}
// Stream operations
export async function streamInfo(id: string, key: string): Promise<any> {
const client = clients.get(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.xinfo('STREAM', key)
}
export async function streamRange(
id: string, key: string, start: string, end: string, count: number
): Promise<any[]> {
const client = clients.get(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.xrange(key, start, end, 'COUNT', count)
}
export async function streamAdd(
id: string, key: string, streamId: string, fieldValues: string[]
): Promise<string | null> {
const client = clients.get(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.xadd(key, streamId, ...fieldValues)
}
export async function streamTrim(id: string, key: string, maxLen: number): Promise<number> {
const client = clients.get(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.xtrim(key, 'MAXLEN', maxLen)
}
export async function streamDel(id: string, key: string, ...ids: string[]): Promise<number> {
const client = clients.get(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.xdel(key, ...ids)
}
export async function renameKey(id: string, oldKey: string, newKey: string): Promise<void> {
const client = clients.get(id)
if (!client) throw new Error(`Client ${id} not found`)
await client.rename(oldKey, newKey)
}
export async function existsKey(id: string, key: string): Promise<boolean> {
const client = clients.get(id)
if (!client) throw new Error(`Client ${id} not found`)
return (await client.exists(key)) === 1
}