refactor: restructure project layout

- Move main process to electron/main/
- Move preload to electron/preload/
- Add shared types in electron/shared/
- Split redis-service into modular redis/ directory
- Flatten renderer to src/ (remove src/renderer/ nesting)
- Update electron.vite.config.ts paths
- Update tsconfig paths
- Output to electron-dist/
This commit is contained in:
2026-07-04 21:12:50 +08:00
parent bb885c6d6d
commit bdcf4fe299
59 changed files with 654 additions and 624 deletions
+85
View File
@@ -0,0 +1,85 @@
// src/main/redis/connection.ts
// Redis 连接管理
import Redis, { RedisOptions } from 'ioredis'
import type { ConnectionOptions } from '../../shared/types'
export type RedisClient = Redis
const clients = new Map<string, RedisClient>()
function buildRedisOptions(opts: ConnectionOptions): RedisOptions {
const options: RedisOptions = {
host: opts.host,
port: opts.port,
username: opts.username || undefined,
password: opts.password || undefined,
stringNumbers: true,
enableReadyCheck: true,
family: 0,
connectTimeout: 10000,
retryStrategy(times: number) {
if (times > 3) return null
return Math.min(times * 200, 1000)
},
maxRetriesPerRequest: 3,
}
if (opts.tls) {
options.tls = {
rejectUnauthorized: false,
checkServerIdentity: () => undefined,
}
}
return options
}
export function getClient(id: string): RedisClient | undefined {
return clients.get(id)
}
export function isClientConnected(id: string): boolean {
const client = clients.get(id)
return client?.status === 'ready'
}
export async function connect(id: string, opts: ConnectionOptions): Promise<void> {
if (clients.has(id)) {
await disconnect(id)
}
return new Promise((resolve, reject) => {
const redis = new Redis(buildRedisOptions(opts))
redis.once('ready', () => {
clients.set(id, redis)
resolve()
})
redis.once('error', (err: Error) => {
redis.disconnect()
reject(err)
})
setTimeout(() => {
if (!clients.has(id)) {
redis.disconnect()
reject(new Error('连接超时,请检查主机和端口'))
}
}, 12000)
})
}
export async function disconnect(id: string): Promise<void> {
const client = clients.get(id)
if (client) {
client.disconnect()
clients.delete(id)
}
}
export function disconnectAll(): void {
for (const [id] of clients) {
disconnect(id)
}
}
+30
View File
@@ -0,0 +1,30 @@
// src/main/redis/hash.ts
// Hash 操作
import { getClient } from './connection'
export async function getHashFields(
id: string, key: string, cursor: string, count: number
): Promise<{ cursor: string; fields: [string, string][] }> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
const [nextCursor, raw] = await client.hscan(key, cursor, 'COUNT', count)
const fields: [string, string][] = []
for (let i = 0; i < raw.length; i += 2) {
fields.push([raw[i], raw[i + 1]])
}
return { cursor: nextCursor, fields }
}
export async function setHashField(
id: string, key: string, field: string, value: string
): Promise<void> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
await client.hset(key, field, value)
}
export async function deleteHashField(id: string, key: string, field: string): Promise<void> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
await client.hdel(key, field)
}
+11
View File
@@ -0,0 +1,11 @@
// src/main/redis/index.ts
// Redis 服务统一导出
export * from './connection'
export * from './keys'
export * from './string'
export * from './hash'
export * from './list'
export * from './set'
export * from './zset'
export * from './stream'
export * from './server'
+73
View File
@@ -0,0 +1,73 @@
// src/main/redis/keys.ts
// Key 操作
import { getClient } from './connection'
export async function getKeyType(id: string, key: string): Promise<string> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.type(key)
}
export async function getKeyTTL(id: string, key: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.ttl(key)
}
export async function deleteKey(id: string, key: string): Promise<void> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
await client.del(key)
}
export async function setKeyTTL(id: string, key: string, ttl: number): Promise<void> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
if (ttl < 0) {
await client.persist(key)
} else {
await client.expire(key, ttl)
}
}
export async function renameKey(id: string, oldKey: string, newKey: string): Promise<void> {
const client = getClient(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 = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return (await client.exists(key)) === 1
}
export async function batchDelete(id: string, keys: string[]): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
if (keys.length === 0) return 0
const pipeline = client.pipeline()
for (const key of keys) {
pipeline.del(key)
}
const results = await pipeline.exec()
return results?.filter(([err]) => !err).length ?? 0
}
export async function memoryUsage(id: string, key: string): Promise<number | null> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.memory('USAGE', key) as Promise<number | null>
}
export async function scanKeys(
id: string,
cursor: string,
pattern: string,
count: number
): Promise<{ cursor: string; keys: string[] }> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
const [nextCursor, keys] = await client.scan(cursor, 'MATCH', pattern, 'COUNT', count)
return { cursor: nextCursor, keys }
}
+33
View File
@@ -0,0 +1,33 @@
// src/main/redis/list.ts
// List 操作
import { getClient } from './connection'
export async function listRange(id: string, key: string, start: number, stop: number): Promise<string[]> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.lrange(key, start, stop)
}
export async function listLength(id: string, key: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.llen(key)
}
export async function listPush(id: string, key: string, ...values: string[]): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.rpush(key, ...values)
}
export async function listSet(id: string, key: string, index: number, value: string): Promise<void> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
await client.lset(key, index, value)
}
export async function listRemove(id: string, key: string, count: number, value: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.lrem(key, count, value)
}
+45
View File
@@ -0,0 +1,45 @@
// src/main/redis/server.ts
// 服务器操作
import { getClient } from './connection'
export async function execute(id: string, command: string, args: string[]): Promise<unknown> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.call(command, ...args)
}
export async function getServerInfo(id: string): Promise<string> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.info() as Promise<string>
}
export async function selectDb(id: string, db: number): Promise<void> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
await client.select(db)
}
export async function dbSize(id: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.dbsize() as Promise<number>
}
export async function ping(id: string): Promise<string> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.ping()
}
export async function slowLogGet(id: string, count: number): Promise<any[]> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.slowlog('GET', count) as Promise<any[]>
}
export async function slowLogLen(id: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.slowlog('LEN') as Promise<number>
}
+27
View File
@@ -0,0 +1,27 @@
// src/main/redis/set.ts
// Set 操作
import { getClient } from './connection'
export async function setMembers(id: string, key: string): Promise<string[]> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.smembers(key)
}
export async function setSize(id: string, key: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.scard(key)
}
export async function setAdd(id: string, key: string, ...members: string[]): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.sadd(key, ...members)
}
export async function setRemove(id: string, key: string, ...members: string[]): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.srem(key, ...members)
}
+37
View File
@@ -0,0 +1,37 @@
// src/main/redis/stream.ts
// Stream 操作
import { getClient } from './connection'
export async function streamInfo(id: string, key: string): Promise<any> {
const client = getClient(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 = getClient(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 = getClient(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 = getClient(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 = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.xdel(key, ...ids)
}
+15
View File
@@ -0,0 +1,15 @@
// src/main/redis/string.ts
// String 操作
import { getClient } from './connection'
export async function getStringValue(id: string, key: string): Promise<string | null> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.get(key)
}
export async function setStringValue(id: string, key: string, value: string): Promise<void> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
await client.set(key, value)
}
+28
View File
@@ -0,0 +1,28 @@
// src/main/redis/zset.ts
// Sorted Set 操作
import { getClient } from './connection'
export async function zsetRange(id: string, key: string, start: number, stop: number, withScores: boolean): Promise<string[]> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
if (withScores) return client.zrange(key, start, stop, 'WITHSCORES')
return client.zrange(key, start, stop)
}
export async function zsetSize(id: string, key: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.zcard(key)
}
export async function zsetAdd(id: string, key: string, score: number, member: string): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.zadd(key, score, member)
}
export async function zsetRemove(id: string, key: string, ...members: string[]): Promise<number> {
const client = getClient(id)
if (!client) throw new Error(`Client ${id} not found`)
return client.zrem(key, ...members)
}