From 157af4399f1b6a30c911113ce5bd2bc1f6339fff Mon Sep 17 00:00:00 2001 From: Jokul Date: Fri, 10 Jul 2026 23:22:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=B1=82=E7=BA=A7=E9=87=8D=E6=9E=84=E5=90=8E=E7=9A=84=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 恢复 IPC 类型声明 (src/electron-api.d.ts),修复 31 个 TS2339 错误 - 修复构建断裂:vite.config.ts 改回 electron.vite.config.ts - 修复 2 个 verbatimModuleSyntax type-import 报错 - 修正 .gitignore 构建产物路径 (electron-dist/ -> dist-electron/ + release/) - 清理 55+ 处过时路径注释 - 更新 AGENTS.md 架构文档与 IPC 通道表 - 修正 docs/ROADMAP.md 过时引用 --- .gitignore | 4 +- AGENTS.md | 110 +++++++++++------ docs/ROADMAP.md | 4 +- vite.config.ts => electron.vite.config.ts | 0 electron/credential.ts | 1 - electron/ipc-handlers.ts | 3 +- electron/main.ts | 1 - electron/preload.ts | 1 - electron/redis/commandLogger.ts | 1 - electron/redis/connection.ts | 3 +- electron/redis/format.ts | 1 - electron/redis/hash.ts | 1 - electron/redis/index.ts | 1 - electron/redis/keys.ts | 1 - electron/redis/list.ts | 1 - electron/redis/pubsub.ts | 1 - electron/redis/server.ts | 1 - electron/redis/set.ts | 1 - electron/redis/stream.ts | 1 - electron/redis/string.ts | 1 - electron/redis/zset.ts | 1 - electron/store.ts | 1 - electron/win-state.ts | 1 - src/App.vue | 1 - src/components/CliView.vue | 1 - src/components/CommandLog.vue | 1 - src/components/ConnectionCard.vue | 1 - src/components/ConnectionList.vue | 1 - src/components/DbSelector.vue | 1 - src/components/HashEditor.vue | 1 - src/components/KeyBrowser.vue | 1 - src/components/KeyDetail.vue | 1 - src/components/KeyList.vue | 1 - src/components/ListEditor.vue | 1 - src/components/MainArea.vue | 1 - src/components/MemoryAnalysis.vue | 1 - src/components/NewConnectionDialog.vue | 1 - src/components/ReJsonEditor.vue | 1 - src/components/SetEditor.vue | 1 - src/components/SettingsView.vue | 1 - src/components/Sidebar.vue | 1 - src/components/SlowLogView.vue | 1 - src/components/StatusBar.vue | 1 - src/components/StatusView.vue | 1 - src/components/StreamEditor.vue | 1 - src/components/StringEditor.vue | 1 - src/components/TitleBar.vue | 1 - src/components/ZsetEditor.vue | 1 - src/composables/useKeyboard.ts | 1 - src/electron-api.d.ts | 144 ++++++++++++++++++++++ src/i18n/index.ts | 1 - src/i18n/locales/de.ts | 1 - src/i18n/locales/en.ts | 1 - src/i18n/locales/ja.ts | 1 - src/i18n/locales/ko.ts | 1 - src/i18n/locales/zh-CN.ts | 1 - src/main.ts | 1 - src/stores/app.ts | 1 - src/stores/connection.ts | 1 - src/stores/key.ts | 1 - src/utils/binary.ts | 1 - tsconfig.node.json | 2 +- 62 files changed, 222 insertions(+), 102 deletions(-) rename vite.config.ts => electron.vite.config.ts (100%) create mode 100644 src/electron-api.d.ts diff --git a/.gitignore b/.gitignore index 93210c8..42269c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ -electron-dist/ +dist-electron/ +release/ dist/ *.log npm-debug.log* @@ -19,4 +20,3 @@ legacy/ # Brainstorming artifacts .superpowers/ -electron-dist/ diff --git a/AGENTS.md b/AGENTS.md index 7f98126..cd1816b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# AGENTS.md — JRedisDesktop v2 +# AGENTS.md - JRedisDesktop v2 ## Project identity @@ -6,10 +6,10 @@ Fork of [AnotherRedisDesktopManager](https://github.com/qishibo/AnotherRedisDesk ## Stack -- **Shell:** Electron 43 (`electron/main/index.ts`) -- **Preload:** `electron/preload/index.ts` (contextBridge, typed API) +- **Shell:** Electron 43 (`electron/main.ts`) +- **Preload:** `electron/preload.ts` (contextBridge, typed API) - **Renderer:** Vue 3 + Pinia + Element Plus + Vue Router (`src/`) -- **Shared:** `electron/shared/types.ts` (cross-process types) +- **IPC types:** `src/electron-api.d.ts` (ElectronAPI interface, global Window augmentation) - **Build:** electron-vite + Vite 6 + TypeScript 5 - **Redis client:** ioredis 5 (main process only) - **Persistence:** electron-store + safeStorage (credentials) @@ -18,46 +18,46 @@ Fork of [AnotherRedisDesktopManager](https://github.com/qishibo/AnotherRedisDesk ```bash npm run dev # electron-vite dev (HMR, renderer on :5173) -npm run build # production build → electron-dist/ +npm run build # production build -> dist-electron/ npm start # preview built app npm run build:unpack # build + electron-builder --dir npm run build:win # build + electron-builder --win npm run build:mac # build + electron-builder --mac npm run build:linux # build + electron-builder --linux +npm run lint # eslint --fix ``` ## Architecture ``` -Electron main (electron/main/) - ├── index.ts # Window creation, app lifecycle - ├── redis/ # Redis service modules - │ ├── index.ts # Unified export - │ ├── connection.ts # connect, disconnect, isClientConnected - │ ├── keys.ts # scan, delete, rename, ttl, batchDelete - │ ├── string.ts # get, set - │ ├── hash.ts # hscan, hset, hdel - │ ├── list.ts # range, push, set, remove - │ ├── set.ts # members, add, remove - │ ├── zset.ts # range, add, remove - │ ├── stream.ts # info, range, add, trim - │ ├── server.ts # info, select, ping, slowlog - │ ├── pubsub.ts # SUBSCRIBE/UNSUBSCRIBE - │ ├── commandLogger.ts # command execution log - │ └── format.ts # value decode/format detection +Electron main (electron/) + ├── main.ts # Window creation, app lifecycle + ├── preload.ts # contextBridge.exposeInMainWorld + ├── ipc-handlers.ts # all IPC channel handlers ├── store.ts # electron-store (connections, settings) ├── credential.ts # safeStorage encrypt/decrypt ├── win-state.ts # window position persistence ├── updater.ts # electron-updater (check/download/install) - └── ipc-handlers.ts # all IPC channel handlers - -Preload (electron/preload/) - ├── index.ts # contextBridge.exposeInMainWorld - └── index.d.ts # ElectronAPI type declarations + └── redis/ # Redis service modules + ├── index.ts # Unified export + ├── connection.ts # connect, disconnect, isClientConnected + ├── keys.ts # scan, delete, rename, ttl, batchDelete + ├── string.ts # get, set + ├── hash.ts # hscan, hset, hdel + ├── list.ts # range, push, set, remove + ├── set.ts # members, add, remove + ├── zset.ts # range, add, remove + ├── stream.ts # info, range, add, trim + ├── server.ts # info, select, ping, slowlog + ├── pubsub.ts # SUBSCRIBE/UNSUBSCRIBE + ├── commandLogger.ts # command execution log + └── format.ts # value decode/format detection Renderer (src/) ├── main.ts # Vue 3 bootstrap (Pinia, Element Plus) ├── App.vue # Shell layout (TitleBar + Sidebar + MainArea + StatusBar) + ├── env.d.ts # Vite client + *.vue / *.css module declarations + ├── electron-api.d.ts # ElectronAPI interface + global Window augmentation ├── stores/ │ ├── app.ts # theme, fontSize, scanCount │ ├── connection.ts # connections CRUD, connect/disconnect, health check @@ -91,7 +91,10 @@ Renderer (src/) │ ├── index.ts # useI18n composable │ └── locales/ │ ├── en.ts # English - │ └── zh-CN.ts # 中文 + │ ├── zh-CN.ts # 中文 + │ ├── ja.ts # 日本語 + │ ├── ko.ts # 한국어 + │ └── de.ts # Deutsch ├── composables/ │ └── useKeyboard.ts # keyboard shortcut handler ├── styles/ @@ -110,57 +113,82 @@ Renderer (src/) | `redis:connect` | invoke | Create Redis connection | | `redis:disconnect` | invoke | Close connection | | `redis:execute` | invoke | Execute arbitrary command | +| `redis:getInfo` | invoke | INFO server info | | `redis:ping` | invoke | Health check ping | | `redis:isConnected` | invoke | Check connection status | | `redis:scanKeys` | invoke | SCAN with pattern | +| `redis:getKeyType` | invoke | TYPE of a key | +| `redis:getKeyTTL` | invoke | TTL of a key | +| `redis:deleteKey` | invoke | DEL a single key | +| `redis:existsKey` | invoke | EXISTS check | | `redis:getString` | invoke | GET string value | | `redis:setString` | invoke | SET string value | | `redis:hashScan` | invoke | HSCAN fields | | `redis:hashSet` | invoke | HSET field | | `redis:hashDel` | invoke | HDEL field | | `redis:listRange` | invoke | LRANGE elements | +| `redis:listLength` | invoke | LLEN | | `redis:listPush` | invoke | RPUSH elements | +| `redis:listSet` | invoke | LSET element | +| `redis:listRemove` | invoke | LREM elements | | `redis:setMembers` | invoke | SMEMBERS | +| `redis:setSize` | invoke | SCARD | +| `redis:setAdd` | invoke | SADD members | +| `redis:setRemove` | invoke | SREM members | | `redis:zsetRange` | invoke | ZRANGE with scores | +| `redis:zsetSize` | invoke | ZCARD | +| `redis:zsetAdd` | invoke | ZADD member | +| `redis:zsetRemove` | invoke | ZREM members | +| `redis:streamInfo` | invoke | XINFO STREAM | | `redis:streamRange` | invoke | XRANGE entries | | `redis:streamAdd` | invoke | XADD entry | +| `redis:streamTrim` | invoke | XTRIM | +| `redis:streamDel` | invoke | XDEL entries | +| `redis:streamGroups` | invoke | XINFO GROUPS | +| `redis:streamConsumers` | invoke | XINFO CONSUMERS | +| `redis:streamGroupCreate` | invoke | XGROUP CREATE | +| `redis:streamGroupDestroy` | invoke | XGROUP DESTROY | +| `redis:streamAck` | invoke | XACK messages | | `redis:batchDelete` | invoke | Pipeline DEL multiple keys | | `redis:renameKey` | invoke | RENAME key | | `redis:setTTL` | invoke | EXPIRE/PERSIST | | `redis:selectDb` | invoke | SELECT database | | `redis:dbSize` | invoke | DBSIZE | | `redis:slowLogGet` | invoke | SLOWLOG GET | +| `redis:slowLogLen` | invoke | SLOWLOG LEN | | `redis:memoryUsage` | invoke | MEMORY USAGE | | `redis:subscribe` | invoke | SUBSCRIBE to channels | | `redis:unsubscribe` | invoke | UNSUBSCRIBE from channels | +| `redis:subscribeMessage` | send | Push subscribed message to renderer | | `redis:monitor` | invoke | Start MONITOR stream | | `redis:monitorStop` | invoke | Stop MONITOR stream | +| `redis:monitorMessage` | send | Push monitored command to renderer | | `redis:getCommandLog` | invoke | Get command execution log | | `redis:clearCommandLog` | invoke | Clear command log | | `redis:decodeValue` | invoke | Decode value by format | | `redis:detectFormat` | invoke | Auto-detect value format | | `redis:customFormat` | invoke | Custom external formatter | -| `redis:streamGroups` | invoke | XINFO GROUPS | -| `redis:streamConsumers` | invoke | XINFO CONSUMERS | -| `redis:streamGroupCreate` | invoke | XGROUP CREATE | -| `redis:streamGroupDestroy` | invoke | XGROUP DESTROY | -| `redis:streamAck` | invoke | XACK messages | | `redis:rejsonGet` | invoke | JSON.GET value | | `redis:rejsonSet` | invoke | JSON.SET value | | `storage:getConnections` | invoke | Read connections | | `storage:saveConnection` | invoke | Save connection | | `storage:deleteConnection` | invoke | Delete connection | +| `storage:reorderConnections` | invoke | Reorder connections | +| `storage:getSettings` | invoke | Read settings | +| `storage:saveSettings` | invoke | Save settings | | `credential:encrypt` | invoke | safeStorage encrypt | | `credential:decrypt` | invoke | safeStorage decrypt | | `dialog:openFile` | invoke | Open file picker | | `theme:get` | invoke | Get OS dark mode | | `theme:set` | send | Set theme source | +| `theme:os-updated` | send | Push OS theme change to renderer | | `window:minimize` | send | Minimize window | | `window:maximize` | send | Toggle maximize | | `window:close` | send | Close window | | `updater:check` | invoke | Check for updates | | `updater:download` | invoke | Download update | | `updater:install` | invoke | Quit and install update | +| `updater:status` | send | Push updater status to renderer | ## Keyboard Shortcuts @@ -175,21 +203,25 @@ Renderer (src/) ## Build config - `electron.vite.config.ts` defines three build targets: main, preload, renderer +- Main + preload output to `dist-electron/`; renderer to `dist-electron/renderer/` +- electron-builder packages into `release/` - Renderer dev server runs on port 5173 - Main/preload use `externalizeDepsPlugin()` to keep node_modules external +- `@renderer` alias resolves to `src` ## TypeScript -- Root `tsconfig.json` references `tsconfig.node.json` (main + preload) and `tsconfig.web.json` (renderer) -- Node target includes `src/main/**/*`, `src/preload/**/*`, `electron.vite.config.ts` -- Web target includes `src/**/*` -- `@renderer` alias resolves to `src` +- Root `tsconfig.json` references `tsconfig.node.json` (main + preload) and `tsconfig.app.json` (renderer) +- Node target (`tsconfig.node.json`) includes `electron/**/*` + `electron.vite.config.ts` +- App target (`tsconfig.app.json`) includes `src/**/*` +- Both use `verbatimModuleSyntax` (type-only imports required) and `moduleResolution: "bundler"` +- `src/electron-api.d.ts` declares the `ElectronAPI` interface and augments global `Window` ## Security -- `contextIsolation: true` — renderer has zero Node.js access -- `nodeIntegration: false` — no Node globals in renderer -- `safeStorage` — OS-level encryption for credentials +- `contextIsolation: true` - renderer has zero Node.js access +- `nodeIntegration: false` - no Node globals in renderer +- `safeStorage` - OS-level encryption for credentials - All Redis operations go through typed IPC to main process ## Legacy diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index def9520..7d16501 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -76,7 +76,7 @@ ## 技术债务 - [x] AGENTS.md 路径更新 — 文档写 `src/renderer/src/components/`,实际是 `src/components/` -- [x] `electron/shared/types.ts` 残留字段 — SSH/Sentinel/Cluster/TLS 路径在类型中存在但从未在 UI 中实现 +- [x] `electron/shared/types.ts` 残留字段(文件已在层级重构中移除) — SSH/Sentinel/Cluster/TLS 路径在类型中存在但从未在 UI 中实现 - [ ] 单元测试 — 当前无任何测试 - [ ] E2E 测试 — 无端到端测试 -- [x] 构建产物清理 — `electron-dist/` 应加入 .gitignore(当前已加入) +- [x] 构建产物清理 — `dist-electron/` 与 `release/` 已加入 .gitignore diff --git a/vite.config.ts b/electron.vite.config.ts similarity index 100% rename from vite.config.ts rename to electron.vite.config.ts diff --git a/electron/credential.ts b/electron/credential.ts index 3940c63..36161e0 100644 --- a/electron/credential.ts +++ b/electron/credential.ts @@ -1,4 +1,3 @@ -// src/main/credential.ts import { safeStorage } from 'electron' export function encrypt(text: string): string { diff --git a/electron/ipc-handlers.ts b/electron/ipc-handlers.ts index 27326ee..e6fe8cb 100644 --- a/electron/ipc-handlers.ts +++ b/electron/ipc-handlers.ts @@ -1,7 +1,6 @@ -// src/main/ipc-handlers.ts // IPC 通道处理器 import { ipcMain, BrowserWindow, nativeTheme, dialog } from 'electron' -import store, { ConnectionConfig } from './store' +import store, { type ConnectionConfig } from './store' import { encrypt, decrypt } from './credential' import * as redis from './redis' diff --git a/electron/main.ts b/electron/main.ts index 88fc17e..4ce4f02 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -1,4 +1,3 @@ -// src/main/index.ts import { app, BrowserWindow, nativeTheme, nativeImage } from 'electron' import { join } from 'path' import { restoreAndTrack } from './win-state' diff --git a/electron/preload.ts b/electron/preload.ts index b9539d4..a21612f 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -1,4 +1,3 @@ -// src/preload/index.ts import { contextBridge, ipcRenderer } from 'electron' const electronAPI = { diff --git a/electron/redis/commandLogger.ts b/electron/redis/commandLogger.ts index fe05d2c..b0a9e2f 100644 --- a/electron/redis/commandLogger.ts +++ b/electron/redis/commandLogger.ts @@ -1,4 +1,3 @@ -// electron/main/redis/commandLogger.ts // 命令日志记录器 export interface CommandEntry { diff --git a/electron/redis/connection.ts b/electron/redis/connection.ts index e716826..a909cd4 100644 --- a/electron/redis/connection.ts +++ b/electron/redis/connection.ts @@ -1,6 +1,5 @@ -// electron/main/redis/connection.ts // Redis 连接管理(支持 SSH 隧道) -import Redis, { RedisOptions } from 'ioredis' +import Redis, { type RedisOptions } from 'ioredis' import { Client as SSHClient } from 'ssh2' import { createServer, type AddressInfo } from 'net' import { readFileSync } from 'fs' diff --git a/electron/redis/format.ts b/electron/redis/format.ts index 2a38aab..a7697d4 100644 --- a/electron/redis/format.ts +++ b/electron/redis/format.ts @@ -1,4 +1,3 @@ -// electron/main/redis/format.ts // 值格式转换(解压/编码/自定义) import { gunzipSync, inflateSync, brotliDecompressSync } from 'zlib' import { execFile } from 'child_process' diff --git a/electron/redis/hash.ts b/electron/redis/hash.ts index 9cb9fc3..a8421a5 100644 --- a/electron/redis/hash.ts +++ b/electron/redis/hash.ts @@ -1,4 +1,3 @@ -// src/main/redis/hash.ts // Hash 操作 import { getClient } from './connection' diff --git a/electron/redis/index.ts b/electron/redis/index.ts index 42ab40b..b1e0707 100644 --- a/electron/redis/index.ts +++ b/electron/redis/index.ts @@ -1,4 +1,3 @@ -// src/main/redis/index.ts // Redis 服务统一导出 export * from './connection' export * from './keys' diff --git a/electron/redis/keys.ts b/electron/redis/keys.ts index dc5f3a2..d8defe2 100644 --- a/electron/redis/keys.ts +++ b/electron/redis/keys.ts @@ -1,4 +1,3 @@ -// src/main/redis/keys.ts // Key 操作 import { getClient } from './connection' diff --git a/electron/redis/list.ts b/electron/redis/list.ts index 86ab442..01fef69 100644 --- a/electron/redis/list.ts +++ b/electron/redis/list.ts @@ -1,4 +1,3 @@ -// src/main/redis/list.ts // List 操作 import { getClient } from './connection' diff --git a/electron/redis/pubsub.ts b/electron/redis/pubsub.ts index 400d901..12690d7 100644 --- a/electron/redis/pubsub.ts +++ b/electron/redis/pubsub.ts @@ -1,4 +1,3 @@ -// electron/main/redis/pubsub.ts // 发布订阅 + MONITOR 流式接口 import Redis from 'ioredis' import { getClient } from './connection' diff --git a/electron/redis/server.ts b/electron/redis/server.ts index ea4cbd0..72076b8 100644 --- a/electron/redis/server.ts +++ b/electron/redis/server.ts @@ -1,4 +1,3 @@ -// src/main/redis/server.ts // 服务器操作 import { getClient } from './connection' diff --git a/electron/redis/set.ts b/electron/redis/set.ts index 3a6cb3a..96a931d 100644 --- a/electron/redis/set.ts +++ b/electron/redis/set.ts @@ -1,4 +1,3 @@ -// src/main/redis/set.ts // Set 操作 import { getClient } from './connection' diff --git a/electron/redis/stream.ts b/electron/redis/stream.ts index 6d700cf..ee4cc6d 100644 --- a/electron/redis/stream.ts +++ b/electron/redis/stream.ts @@ -1,4 +1,3 @@ -// src/main/redis/stream.ts // Stream 操作 import { getClient } from './connection' diff --git a/electron/redis/string.ts b/electron/redis/string.ts index bd38eb2..512e979 100644 --- a/electron/redis/string.ts +++ b/electron/redis/string.ts @@ -1,4 +1,3 @@ -// src/main/redis/string.ts // String + ReJson 操作 import { getClient } from './connection' diff --git a/electron/redis/zset.ts b/electron/redis/zset.ts index 2526130..ba2bd2a 100644 --- a/electron/redis/zset.ts +++ b/electron/redis/zset.ts @@ -1,4 +1,3 @@ -// src/main/redis/zset.ts // Sorted Set 操作 import { getClient } from './connection' diff --git a/electron/store.ts b/electron/store.ts index 7899e03..07faa2a 100644 --- a/electron/store.ts +++ b/electron/store.ts @@ -1,4 +1,3 @@ -// src/main/store.ts import Store from 'electron-store' interface ConnectionConfig { diff --git a/electron/win-state.ts b/electron/win-state.ts index 2eb82e2..0c48688 100644 --- a/electron/win-state.ts +++ b/electron/win-state.ts @@ -1,4 +1,3 @@ -// src/main/win-state.ts import { app, screen, BrowserWindow } from 'electron' import { join } from 'path' import { readFileSync, writeFile } from 'fs' diff --git a/src/App.vue b/src/App.vue index 7f8141d..5395536 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,5 +1,4 @@