mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-21 21:47:00 +08:00
chore: 补充
This commit is contained in:
@@ -1,10 +1,6 @@
|
||||
import { app } from 'electron'
|
||||
import path from 'path'
|
||||
import {
|
||||
chooseUserDataRoot,
|
||||
getUserDataRoots,
|
||||
LEGACY_USER_DATA_NAME
|
||||
} from './app-data-paths'
|
||||
import { getUserDataRoots, LEGACY_USER_DATA_NAME, selectUserDataRoot } from './app-data-paths'
|
||||
|
||||
// This module must remain the first main-process import. Static imports in
|
||||
// settings/cache services can otherwise resolve Electron paths before the
|
||||
@@ -13,10 +9,11 @@ app.setName(process.platform === 'win32' ? 'WeFlow' : LEGACY_USER_DATA_NAME)
|
||||
|
||||
const isolatedUserData = process.env['WXE_USER_DATA']
|
||||
const roots = getUserDataRoots(app.getPath('appData'))
|
||||
const selectedUserData = chooseUserDataRoot({
|
||||
const userDataSelection = selectUserDataRoot({
|
||||
...roots,
|
||||
isolated: isolatedUserData
|
||||
})
|
||||
const selectedUserData = userDataSelection.selected
|
||||
|
||||
app.setPath('userData', selectedUserData)
|
||||
app.setPath('sessionData', selectedUserData)
|
||||
@@ -28,4 +25,4 @@ if (process.platform === 'darwin') {
|
||||
app.setPath('logs', path.join(app.getPath('home'), 'Library', 'Logs', 'TraceMemo'))
|
||||
}
|
||||
|
||||
export { roots, selectedUserData }
|
||||
export { roots, selectedUserData, userDataSelection }
|
||||
|
||||
+155
-8
@@ -2,10 +2,12 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export const LEGACY_USER_DATA_NAME = 'WechatExplorer'
|
||||
export const LEGACY_PACKAGE_USER_DATA_NAME = 'wechatexplorer'
|
||||
export const CURRENT_USER_DATA_NAME = 'tracememo'
|
||||
|
||||
export interface UserDataRoots {
|
||||
legacy: string
|
||||
legacyPackage: string
|
||||
current: string
|
||||
}
|
||||
|
||||
@@ -13,6 +15,41 @@ export interface UserDataSelectionInput extends UserDataRoots {
|
||||
isolated?: string
|
||||
}
|
||||
|
||||
export type UserDataRootKind = 'isolated' | 'legacy-display' | 'legacy-package' | 'current'
|
||||
|
||||
export type UserDataSelectionReason =
|
||||
| 'isolated-override'
|
||||
| 'legacy-display-assets'
|
||||
| 'legacy-package-assets'
|
||||
| 'legacy-shared-assets'
|
||||
| 'legacy-conflict-display-preferred'
|
||||
| 'current-assets'
|
||||
| 'clean-install'
|
||||
|
||||
export interface UserDataSelection {
|
||||
selected: string
|
||||
selectedKind: UserDataRootKind
|
||||
reason: UserDataSelectionReason
|
||||
directories: {
|
||||
legacy: boolean
|
||||
legacyPackage: boolean
|
||||
current: boolean
|
||||
}
|
||||
assets: {
|
||||
legacy: boolean
|
||||
legacyPackage: boolean
|
||||
current: boolean
|
||||
}
|
||||
legacyRootsEquivalent: boolean
|
||||
legacyConflict: boolean
|
||||
}
|
||||
|
||||
export interface UserDataSelectionDependencies {
|
||||
directoryExists: (root: string) => boolean
|
||||
hasAssets: (root: string) => boolean
|
||||
areSameDirectory: (first: string, second: string) => boolean
|
||||
}
|
||||
|
||||
function isNonEmptyFile(filePath: string): boolean {
|
||||
try {
|
||||
const stat = fs.statSync(filePath)
|
||||
@@ -37,7 +74,11 @@ function hasPersistentEntries(directoryPath: string): boolean {
|
||||
function hasDatabaseKey(directoryPath: string): boolean {
|
||||
try {
|
||||
return fs.readdirSync(directoryPath, { withFileTypes: true }).some((entry) => {
|
||||
return entry.isFile() && entry.name.endsWith('.bin') && isNonEmptyFile(path.join(directoryPath, entry.name))
|
||||
return (
|
||||
entry.isFile() &&
|
||||
entry.name.endsWith('.bin') &&
|
||||
isNonEmptyFile(path.join(directoryPath, entry.name))
|
||||
)
|
||||
})
|
||||
} catch {
|
||||
return false
|
||||
@@ -56,6 +97,26 @@ function hasKnowledgeDatabase(root: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function isExistingDirectory(directoryPath: string): boolean {
|
||||
try {
|
||||
return fs.statSync(directoryPath).isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function areSameExistingDirectory(firstPath: string, secondPath: string): boolean {
|
||||
try {
|
||||
const first = fs.statSync(firstPath)
|
||||
const second = fs.statSync(secondPath)
|
||||
if (!first.isDirectory() || !second.isDirectory()) return false
|
||||
if (first.ino && first.dev === second.dev && first.ino === second.ino) return true
|
||||
return fs.realpathSync.native(firstPath) === fs.realpathSync.native(secondPath)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime-only Chromium files are deliberately excluded. A directory is a
|
||||
* valid data root only when it contains at least one user-owned marker.
|
||||
@@ -85,20 +146,106 @@ export function hasValidUserAssets(root: string): boolean {
|
||||
export function getUserDataRoots(appDataPath: string): UserDataRoots {
|
||||
return {
|
||||
legacy: path.join(appDataPath, LEGACY_USER_DATA_NAME),
|
||||
legacyPackage: path.join(appDataPath, LEGACY_PACKAGE_USER_DATA_NAME),
|
||||
current: path.join(appDataPath, CURRENT_USER_DATA_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select exactly one root. This intentionally does not copy, merge, delete or
|
||||
* modify either directory. Legacy wins when both roots contain user assets so
|
||||
* a v2.1.9 upgrade remains deterministic and lossless.
|
||||
* modify any directory. The visible-name legacy root remains first priority;
|
||||
* the lowercase v2.1.9 package-name root is the fallback on case-sensitive
|
||||
* filesystems. If both distinct legacy roots contain assets, the visible-name
|
||||
* root wins deterministically and the caller receives conflict diagnostics.
|
||||
*/
|
||||
export function chooseUserDataRoot(input: UserDataSelectionInput): string {
|
||||
export function selectUserDataRoot(
|
||||
input: UserDataSelectionInput,
|
||||
dependencies: UserDataSelectionDependencies = {
|
||||
directoryExists: isExistingDirectory,
|
||||
hasAssets: hasValidUserAssets,
|
||||
areSameDirectory: areSameExistingDirectory
|
||||
}
|
||||
): UserDataSelection {
|
||||
const isolated = input.isolated?.trim()
|
||||
if (isolated) return path.resolve(isolated)
|
||||
if (isolated) {
|
||||
return {
|
||||
selected: path.resolve(isolated),
|
||||
selectedKind: 'isolated',
|
||||
reason: 'isolated-override',
|
||||
directories: { legacy: false, legacyPackage: false, current: false },
|
||||
assets: { legacy: false, legacyPackage: false, current: false },
|
||||
legacyRootsEquivalent: false,
|
||||
legacyConflict: false
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidUserAssets(input.legacy)) return input.legacy
|
||||
if (hasValidUserAssets(input.current)) return input.current
|
||||
return input.current
|
||||
const directories = {
|
||||
legacy: dependencies.directoryExists(input.legacy),
|
||||
legacyPackage: dependencies.directoryExists(input.legacyPackage),
|
||||
current: dependencies.directoryExists(input.current)
|
||||
}
|
||||
const assets = {
|
||||
legacy: dependencies.hasAssets(input.legacy),
|
||||
legacyPackage: dependencies.hasAssets(input.legacyPackage),
|
||||
current: dependencies.hasAssets(input.current)
|
||||
}
|
||||
const legacyRootsEquivalent =
|
||||
directories.legacy &&
|
||||
directories.legacyPackage &&
|
||||
dependencies.areSameDirectory(input.legacy, input.legacyPackage)
|
||||
|
||||
if (assets.legacy) {
|
||||
const legacyConflict = assets.legacyPackage && !legacyRootsEquivalent
|
||||
return {
|
||||
selected: input.legacy,
|
||||
selectedKind: 'legacy-display',
|
||||
reason: legacyConflict
|
||||
? 'legacy-conflict-display-preferred'
|
||||
: legacyRootsEquivalent
|
||||
? 'legacy-shared-assets'
|
||||
: 'legacy-display-assets',
|
||||
directories,
|
||||
assets,
|
||||
legacyRootsEquivalent,
|
||||
legacyConflict
|
||||
}
|
||||
}
|
||||
|
||||
if (assets.legacyPackage) {
|
||||
return {
|
||||
selected: input.legacyPackage,
|
||||
selectedKind: 'legacy-package',
|
||||
reason: 'legacy-package-assets',
|
||||
directories,
|
||||
assets,
|
||||
legacyRootsEquivalent: false,
|
||||
legacyConflict: false
|
||||
}
|
||||
}
|
||||
|
||||
if (assets.current) {
|
||||
return {
|
||||
selected: input.current,
|
||||
selectedKind: 'current',
|
||||
reason: 'current-assets',
|
||||
directories,
|
||||
assets,
|
||||
legacyRootsEquivalent: false,
|
||||
legacyConflict: false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
selected: input.current,
|
||||
selectedKind: 'current',
|
||||
reason: 'clean-install',
|
||||
directories,
|
||||
assets,
|
||||
legacyRootsEquivalent: false,
|
||||
legacyConflict: false
|
||||
}
|
||||
}
|
||||
|
||||
export function chooseUserDataRoot(input: UserDataSelectionInput): string {
|
||||
return selectUserDataRoot(input).selected
|
||||
}
|
||||
|
||||
+21
-1
@@ -1,4 +1,4 @@
|
||||
import './app-data-bootstrap'
|
||||
import { userDataSelection } from './app-data-bootstrap'
|
||||
import './preload-env'
|
||||
import {
|
||||
app,
|
||||
@@ -513,6 +513,26 @@ app.whenReady().then(async () => {
|
||||
message: 'TraceMemo 启动',
|
||||
details: { build: BUILD_MARK, platform: process.platform, version: app.getVersion() }
|
||||
})
|
||||
appLogger.write({
|
||||
level: userDataSelection.legacyConflict ? 'warn' : 'info',
|
||||
scope: 'app-data-bootstrap',
|
||||
message: userDataSelection.legacyConflict
|
||||
? '检测到两个独立的 legacy userData,已按兼容优先级选择 WechatExplorer'
|
||||
: 'userData 路径选择完成',
|
||||
details: {
|
||||
selectedPath: userDataSelection.selected,
|
||||
selectedKind: userDataSelection.selectedKind,
|
||||
reason: userDataSelection.reason,
|
||||
legacyExists: userDataSelection.directories.legacy,
|
||||
legacyPackageExists: userDataSelection.directories.legacyPackage,
|
||||
currentExists: userDataSelection.directories.current,
|
||||
legacyAssets: userDataSelection.assets.legacy,
|
||||
legacyPackageAssets: userDataSelection.assets.legacyPackage,
|
||||
currentAssets: userDataSelection.assets.current,
|
||||
legacyRootsEquivalent: userDataSelection.legacyRootsEquivalent,
|
||||
legacyConflict: userDataSelection.legacyConflict
|
||||
}
|
||||
})
|
||||
process.on('uncaughtException', (error) => {
|
||||
appLogger.write({
|
||||
level: 'error',
|
||||
|
||||
Reference in New Issue
Block a user