mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
feat(sidebar): add account data clear action and detail feedback
This commit is contained in:
189
electron/main.ts
189
electron/main.ts
@@ -3,7 +3,7 @@ import { app, BrowserWindow, ipcMain, nativeTheme, session } from 'electron'
|
||||
import { Worker } from 'worker_threads'
|
||||
import { join, dirname } from 'path'
|
||||
import { autoUpdater } from 'electron-updater'
|
||||
import { readFile, writeFile, mkdir } from 'fs/promises'
|
||||
import { readFile, writeFile, mkdir, rm, readdir } from 'fs/promises'
|
||||
import { existsSync } from 'fs'
|
||||
import { ConfigService } from './services/config'
|
||||
import { dbPathService } from './services/dbPathService'
|
||||
@@ -772,6 +772,65 @@ function showMainWindow() {
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeAccountId = (value: string): string => {
|
||||
const trimmed = String(value || '').trim()
|
||||
if (!trimmed) return ''
|
||||
if (trimmed.toLowerCase().startsWith('wxid_')) {
|
||||
const match = trimmed.match(/^(wxid_[^_]+)/i)
|
||||
return match?.[1] || trimmed
|
||||
}
|
||||
const suffixMatch = trimmed.match(/^(.+)_([a-zA-Z0-9]{4})$/)
|
||||
return suffixMatch ? suffixMatch[1] : trimmed
|
||||
}
|
||||
|
||||
const buildAccountNameMatcher = (wxidCandidates: string[]) => {
|
||||
const loweredCandidates = wxidCandidates
|
||||
.map((item) => String(item || '').trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
return (name: string): boolean => {
|
||||
const loweredName = String(name || '').trim().toLowerCase()
|
||||
if (!loweredName) return false
|
||||
return loweredCandidates.some((candidate) => (
|
||||
loweredName === candidate ||
|
||||
loweredName.startsWith(`${candidate}_`) ||
|
||||
loweredName.includes(candidate)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
const removePathIfExists = async (
|
||||
targetPath: string,
|
||||
removedPaths: string[],
|
||||
warnings: string[]
|
||||
): Promise<void> => {
|
||||
if (!targetPath || !existsSync(targetPath)) return
|
||||
try {
|
||||
await rm(targetPath, { recursive: true, force: true })
|
||||
removedPaths.push(targetPath)
|
||||
} catch (error) {
|
||||
warnings.push(`${targetPath}: ${String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
const removeMatchedEntriesInDir = async (
|
||||
rootDir: string,
|
||||
shouldRemove: (name: string) => boolean,
|
||||
removedPaths: string[],
|
||||
warnings: string[]
|
||||
): Promise<void> => {
|
||||
if (!rootDir || !existsSync(rootDir)) return
|
||||
try {
|
||||
const entries = await readdir(rootDir, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
if (!shouldRemove(entry.name)) continue
|
||||
const targetPath = join(rootDir, entry.name)
|
||||
await removePathIfExists(targetPath, removedPaths, warnings)
|
||||
}
|
||||
} catch (error) {
|
||||
warnings.push(`${rootDir}: ${String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 注册 IPC 处理器
|
||||
function registerIpcHandlers() {
|
||||
registerNotificationHandlers()
|
||||
@@ -1190,6 +1249,134 @@ function registerIpcHandlers() {
|
||||
return true
|
||||
})
|
||||
|
||||
ipcMain.handle('chat:clearCurrentAccountData', async (_, options?: { clearCache?: boolean; clearExports?: boolean }) => {
|
||||
const cfg = configService
|
||||
if (!cfg) return { success: false, error: '配置服务未初始化' }
|
||||
|
||||
const clearCache = options?.clearCache === true
|
||||
const clearExports = options?.clearExports === true
|
||||
if (!clearCache && !clearExports) {
|
||||
return { success: false, error: '请至少选择一项清理范围' }
|
||||
}
|
||||
|
||||
const rawWxid = String(cfg.get('myWxid') || '').trim()
|
||||
if (!rawWxid) {
|
||||
return { success: false, error: '当前账号未登录或未识别,无法清理' }
|
||||
}
|
||||
const normalizedWxid = normalizeAccountId(rawWxid)
|
||||
const wxidCandidates = Array.from(new Set([rawWxid, normalizedWxid].filter(Boolean)))
|
||||
const isMatchedAccountName = buildAccountNameMatcher(wxidCandidates)
|
||||
const removedPaths: string[] = []
|
||||
const warnings: string[] = []
|
||||
|
||||
try {
|
||||
wcdbService.close()
|
||||
chatService.close()
|
||||
} catch (error) {
|
||||
warnings.push(`关闭数据库连接失败: ${String(error)}`)
|
||||
}
|
||||
|
||||
if (clearCache) {
|
||||
const [analyticsResult, imageResult] = await Promise.all([
|
||||
analyticsService.clearCache(),
|
||||
imageDecryptService.clearCache()
|
||||
])
|
||||
const chatResult = chatService.clearCaches()
|
||||
const cleanupResults = [analyticsResult, imageResult, chatResult]
|
||||
for (const result of cleanupResults) {
|
||||
if (!result.success && result.error) warnings.push(result.error)
|
||||
}
|
||||
|
||||
const configuredCachePath = String(cfg.get('cachePath') || '').trim()
|
||||
const documentsWeFlowDir = join(app.getPath('documents'), 'WeFlow')
|
||||
const userDataCacheDir = join(app.getPath('userData'), 'cache')
|
||||
const cacheRootCandidates = [
|
||||
configuredCachePath,
|
||||
join(documentsWeFlowDir, 'Images'),
|
||||
join(documentsWeFlowDir, 'Voices'),
|
||||
join(documentsWeFlowDir, 'Emojis'),
|
||||
userDataCacheDir
|
||||
].filter(Boolean)
|
||||
|
||||
for (const wxid of wxidCandidates) {
|
||||
if (configuredCachePath) {
|
||||
await removePathIfExists(join(configuredCachePath, wxid), removedPaths, warnings)
|
||||
await removePathIfExists(join(configuredCachePath, 'Images', wxid), removedPaths, warnings)
|
||||
await removePathIfExists(join(configuredCachePath, 'Voices', wxid), removedPaths, warnings)
|
||||
await removePathIfExists(join(configuredCachePath, 'Emojis', wxid), removedPaths, warnings)
|
||||
}
|
||||
await removePathIfExists(join(documentsWeFlowDir, 'Images', wxid), removedPaths, warnings)
|
||||
await removePathIfExists(join(documentsWeFlowDir, 'Voices', wxid), removedPaths, warnings)
|
||||
await removePathIfExists(join(documentsWeFlowDir, 'Emojis', wxid), removedPaths, warnings)
|
||||
await removePathIfExists(join(userDataCacheDir, wxid), removedPaths, warnings)
|
||||
}
|
||||
|
||||
for (const cacheRoot of cacheRootCandidates) {
|
||||
await removeMatchedEntriesInDir(cacheRoot, isMatchedAccountName, removedPaths, warnings)
|
||||
}
|
||||
}
|
||||
|
||||
if (clearExports) {
|
||||
const configuredExportPath = String(cfg.get('exportPath') || '').trim()
|
||||
const documentsWeFlowDir = join(app.getPath('documents'), 'WeFlow')
|
||||
const exportRootCandidates = [
|
||||
configuredExportPath,
|
||||
join(documentsWeFlowDir, 'exports'),
|
||||
join(documentsWeFlowDir, 'Exports')
|
||||
].filter(Boolean)
|
||||
|
||||
for (const exportRoot of exportRootCandidates) {
|
||||
await removeMatchedEntriesInDir(exportRoot, isMatchedAccountName, removedPaths, warnings)
|
||||
}
|
||||
|
||||
const resetConfigKeys = [
|
||||
'exportSessionRecordMap',
|
||||
'exportLastSessionRunMap',
|
||||
'exportLastContentRunMap',
|
||||
'exportSessionMessageCountCacheMap',
|
||||
'exportSessionContentMetricCacheMap',
|
||||
'exportSnsStatsCacheMap',
|
||||
'snsPageCacheMap',
|
||||
'contactsListCacheMap',
|
||||
'contactsAvatarCacheMap',
|
||||
'lastSession'
|
||||
]
|
||||
for (const key of resetConfigKeys) {
|
||||
const defaultValue = key === 'lastSession' ? '' : {}
|
||||
cfg.set(key as any, defaultValue as any)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const wxidConfigsRaw = cfg.get('wxidConfigs') as Record<string, any> | undefined
|
||||
if (wxidConfigsRaw && typeof wxidConfigsRaw === 'object') {
|
||||
const nextConfigs: Record<string, any> = { ...wxidConfigsRaw }
|
||||
for (const key of Object.keys(nextConfigs)) {
|
||||
if (isMatchedAccountName(key) || normalizeAccountId(key) === normalizedWxid) {
|
||||
delete nextConfigs[key]
|
||||
}
|
||||
}
|
||||
cfg.set('wxidConfigs' as any, nextConfigs as any)
|
||||
}
|
||||
cfg.set('myWxid' as any, '')
|
||||
cfg.set('decryptKey' as any, '')
|
||||
cfg.set('imageXorKey' as any, 0)
|
||||
cfg.set('imageAesKey' as any, '')
|
||||
cfg.set('dbPath' as any, '')
|
||||
cfg.set('lastOpenedDb' as any, '')
|
||||
cfg.set('onboardingDone' as any, false)
|
||||
cfg.set('lastSession' as any, '')
|
||||
} catch (error) {
|
||||
warnings.push(`清理账号配置失败: ${String(error)}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
removedPaths,
|
||||
warning: warnings.length > 0 ? warnings.join('; ') : undefined
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('chat:getSessionDetail', async (_, sessionId: string) => {
|
||||
return chatService.getSessionDetail(sessionId)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user