mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 19:47:08 +08:00
feat: 重构数据库登录与路径发现
This commit is contained in:
+4
-3
@@ -655,9 +655,10 @@ app.whenReady().then(async () => {
|
||||
return error ? { success: false, error } : { success: true }
|
||||
})
|
||||
|
||||
ipcMain.handle('db:disconnect', () => {
|
||||
if (!chat.isReady()) return { success: false, error: '数据库当前未连接' }
|
||||
chat.setChatDb(null)
|
||||
ipcMain.handle('db:disconnect', (_, options?: { closeNative?: boolean }) => {
|
||||
// 断开操作保持幂等:渲染进程可能已标记断开,或主进程连接已先行失效。
|
||||
// 即使当前未就绪,也应让用户正常返回登录页。
|
||||
if (options?.closeNative !== false && chat.isReady()) chat.setChatDb(null)
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import { discoverWindowsDbRoots } from '../windows-db-root-discovery'
|
||||
|
||||
export interface AppSettings {
|
||||
dbRoot: string
|
||||
@@ -37,14 +38,7 @@ function getDefaultDbRootCandidates(home: string): string[] {
|
||||
path.join(os.homedir(), 'AppData', 'Roaming', 'Tencent', 'xwechat_files')
|
||||
]
|
||||
|
||||
for (const drive of getWindowsDrives()) {
|
||||
candidates.push(path.join(`${drive}:\\`, 'xwechat_files'))
|
||||
candidates.push(path.join(`${drive}:\\`, 'WeChat Files'))
|
||||
for (const child of listDirectories(`${drive}:\\`)) {
|
||||
candidates.push(path.join(child, 'xwechat_files'))
|
||||
candidates.push(path.join(child, 'WeChat Files'))
|
||||
}
|
||||
}
|
||||
candidates.push(...discoverWindowsDbRoots())
|
||||
|
||||
return unique(candidates)
|
||||
}
|
||||
@@ -68,32 +62,6 @@ function getWeflowDbPathCandidates(home: string): string[] {
|
||||
return candidates
|
||||
}
|
||||
|
||||
function getWindowsDrives(): string[] {
|
||||
const drives: string[] = []
|
||||
for (let code = 67; code <= 90; code += 1) {
|
||||
const drive = String.fromCharCode(code)
|
||||
if (fs.existsSync(`${drive}:\\`)) drives.push(drive)
|
||||
}
|
||||
return drives
|
||||
}
|
||||
|
||||
function listDirectories(root: string): string[] {
|
||||
try {
|
||||
return fs
|
||||
.readdirSync(root)
|
||||
.map((name) => path.join(root, name))
|
||||
.filter((candidate) => {
|
||||
try {
|
||||
return fs.statSync(candidate).isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function unique(values: string[]): string[] {
|
||||
return Array.from(new Set(values))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import { discoverWindowsDbRoots } from './windows-db-root-discovery'
|
||||
import crypto from 'crypto'
|
||||
import { createRequire } from 'module'
|
||||
import { createConnection, Socket } from 'net'
|
||||
@@ -303,14 +304,7 @@ export class Wcdb4Client {
|
||||
path.join(home, 'WeChat Files'),
|
||||
path.join(home, 'AppData', 'Roaming', 'Tencent', 'xwechat_files')
|
||||
]
|
||||
for (const drive of Wcdb4Client.getWindowsDrives()) {
|
||||
candidates.push(path.join(`${drive}:\\`, 'xwechat_files'))
|
||||
candidates.push(path.join(`${drive}:\\`, 'WeChat Files'))
|
||||
for (const child of Wcdb4Client.listDirectories(`${drive}:\\`)) {
|
||||
candidates.push(path.join(child, 'xwechat_files'))
|
||||
candidates.push(path.join(child, 'WeChat Files'))
|
||||
}
|
||||
}
|
||||
candidates.push(...discoverWindowsDbRoots())
|
||||
return Array.from(new Set(candidates))
|
||||
}
|
||||
return [path.join(home, 'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files')]
|
||||
@@ -335,32 +329,6 @@ export class Wcdb4Client {
|
||||
return candidates
|
||||
}
|
||||
|
||||
private static getWindowsDrives(): string[] {
|
||||
const drives: string[] = []
|
||||
for (let code = 67; code <= 90; code += 1) {
|
||||
const drive = String.fromCharCode(code)
|
||||
if (fs.existsSync(`${drive}:\\`)) drives.push(drive)
|
||||
}
|
||||
return drives
|
||||
}
|
||||
|
||||
private static listDirectories(root: string): string[] {
|
||||
try {
|
||||
return fs
|
||||
.readdirSync(root)
|
||||
.map((name) => path.join(root, name))
|
||||
.filter((candidate) => {
|
||||
try {
|
||||
return fs.statSync(candidate).isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private static hasDbStorage(candidate: string): boolean {
|
||||
try {
|
||||
return fs.statSync(candidate).isDirectory() && fs.existsSync(path.join(candidate, 'db_storage'))
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
const DB_ROOT_NAMES = new Set(['xwechat_files', 'wechat files'])
|
||||
const SKIPPED_DIRECTORY_NAMES = new Set([
|
||||
'$recycle.bin',
|
||||
'system volume information',
|
||||
'windows',
|
||||
'program files',
|
||||
'program files (x86)',
|
||||
'programdata',
|
||||
'recovery'
|
||||
])
|
||||
const MAX_VISITED_DIRECTORIES_PER_DRIVE = 20_000
|
||||
let cachedDiscoveredRoots: string[] | null = null
|
||||
|
||||
function unique(values: string[]): string[] {
|
||||
return Array.from(new Set(values.map((value) => path.normalize(value))))
|
||||
}
|
||||
|
||||
export function getWindowsDrives(): string[] {
|
||||
if (process.platform !== 'win32') return []
|
||||
const drives: string[] = []
|
||||
for (let code = 67; code <= 90; code += 1) {
|
||||
const root = `${String.fromCharCode(code)}:\\`
|
||||
if (fs.existsSync(root)) drives.push(root)
|
||||
}
|
||||
return drives
|
||||
}
|
||||
|
||||
export function scanWindowsDbRoots(driveRoots: string[], maxDepth = 3): string[] {
|
||||
const results: string[] = []
|
||||
|
||||
for (const driveRoot of driveRoots) {
|
||||
const queue: Array<{ directory: string; depth: number }> = [{ directory: driveRoot, depth: 0 }]
|
||||
let visited = 0
|
||||
|
||||
while (queue.length > 0 && visited < MAX_VISITED_DIRECTORIES_PER_DRIVE) {
|
||||
const current = queue.shift()
|
||||
if (!current || current.depth >= maxDepth) continue
|
||||
|
||||
let entries: fs.Dirent[]
|
||||
try {
|
||||
entries = fs.readdirSync(current.directory, { withFileTypes: true })
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || entry.isSymbolicLink()) continue
|
||||
const lowered = entry.name.toLowerCase()
|
||||
const fullPath = path.join(current.directory, entry.name)
|
||||
const depth = current.depth + 1
|
||||
visited += 1
|
||||
|
||||
if (DB_ROOT_NAMES.has(lowered)) {
|
||||
results.push(fullPath)
|
||||
continue
|
||||
}
|
||||
if (depth < maxDepth && !SKIPPED_DIRECTORY_NAMES.has(lowered)) {
|
||||
queue.push({ directory: fullPath, depth })
|
||||
}
|
||||
if (visited >= MAX_VISITED_DIRECTORIES_PER_DRIVE) break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unique(results)
|
||||
}
|
||||
|
||||
export function discoverWindowsDbRoots(): string[] {
|
||||
if (!cachedDiscoveredRoots) {
|
||||
cachedDiscoveredRoots = scanWindowsDbRoots(getWindowsDrives(), 3)
|
||||
}
|
||||
return [...cachedDiscoveredRoots]
|
||||
}
|
||||
Reference in New Issue
Block a user