mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-27 15:07:55 +00:00
Compare commits
6 Commits
v4.1.8
...
xunchahaha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3dec958b0 | ||
|
|
cfa335564a | ||
|
|
61ef10de9b | ||
|
|
73f36d6b29 | ||
|
|
666a1a3296 | ||
|
|
b5a371da87 |
85
.github/workflows/release.yml
vendored
85
.github/workflows/release.yml
vendored
@@ -12,27 +12,8 @@ env:
|
|||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prepare-release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Mark release as pre-release (building)
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
TAG="$GITHUB_REF_NAME"
|
|
||||||
REPO="$GITHUB_REPOSITORY"
|
|
||||||
# Create or update the release as a pre-release with a placeholder note
|
|
||||||
if gh release view "$TAG" --repo "$REPO" > /dev/null 2>&1; then
|
|
||||||
gh release edit "$TAG" --repo "$REPO" --prerelease --notes $'## ⚠️ 正在自动构建中,请勿下载\n\n各平台安装包正在构建,完成后将自动更新本页面并正式发布。\n\n**请勿在此期间下载任何文件。**'
|
|
||||||
else
|
|
||||||
gh release create "$TAG" --repo "$REPO" --prerelease --title "$TAG" --notes $'## ⚠️ 正在自动构建中,请勿下载\n\n各平台安装包正在构建,完成后将自动更新本页面并正式发布。\n\n**请勿在此期间下载任何文件。**'
|
|
||||||
fi
|
|
||||||
|
|
||||||
release-mac-arm64:
|
release-mac-arm64:
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
needs: prepare-release
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
@@ -70,7 +51,6 @@ jobs:
|
|||||||
|
|
||||||
release-linux:
|
release-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: prepare-release
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
@@ -107,7 +87,6 @@ jobs:
|
|||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: prepare-release
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
@@ -139,13 +118,11 @@ jobs:
|
|||||||
- name: Package and Publish
|
- name: Package and Publish
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
npx electron-builder --win nsis --x64 --publish always "-c.artifactName=\${productName}-\${version}-x64-Setup.\${ext}"
|
npx electron-builder --win nsis --x64 --publish always '--config.artifactName=${productName}-${version}-x64-Setup.${ext}'
|
||||||
|
|
||||||
release-windows-arm64:
|
release-windows-arm64:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: prepare-release
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
@@ -177,9 +154,8 @@ jobs:
|
|||||||
- name: Package and Publish Windows arm64
|
- name: Package and Publish Windows arm64
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
npx electron-builder --win nsis --arm64 --publish always -c.publish.channel=latest-arm64 "-c.artifactName=\${productName}-\${version}-arm64-Setup.\${ext}"
|
npx electron-builder --win nsis --arm64 --publish always '--config.publish.channel=latest-arm64' '--config.artifactName=${productName}-${version}-arm64-Setup.${ext}'
|
||||||
|
|
||||||
update-release-notes:
|
update-release-notes:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -190,53 +166,6 @@ jobs:
|
|||||||
- release-windows-arm64
|
- release-windows-arm64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Fix latest.yml to point to x64 installer
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
TAG="$GITHUB_REF_NAME"
|
|
||||||
VERSION="${TAG#v}"
|
|
||||||
REPO="$GITHUB_REPOSITORY"
|
|
||||||
|
|
||||||
# Find the x64 exe asset name
|
|
||||||
ASSETS_JSON="$(gh release view "$TAG" --repo "$REPO" --json assets)"
|
|
||||||
X64_ASSET="$(echo "$ASSETS_JSON" | jq -r '[.assets[].name | select(test("x64.*\\.exe$"))][0] // ""')"
|
|
||||||
if [ -z "$X64_ASSET" ]; then
|
|
||||||
X64_ASSET="$(echo "$ASSETS_JSON" | jq -r '[.assets[].name | select(test("\\.exe$")) | select(test("arm64") | not)][0] // ""')"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$X64_ASSET" ]; then
|
|
||||||
echo "ERROR: Could not find x64 exe asset"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Downloading x64 installer: $X64_ASSET"
|
|
||||||
gh release download "$TAG" --repo "$REPO" --pattern "$X64_ASSET" --dir /tmp/weflow-x64
|
|
||||||
|
|
||||||
SHA512_B64="$(sha512sum "/tmp/weflow-x64/$X64_ASSET" | awk '{print $1}' | xxd -r -p | base64 -w 0)"
|
|
||||||
SIZE="$(stat -c%s "/tmp/weflow-x64/$X64_ASSET")"
|
|
||||||
RELEASE_DATE="$(gh release view "$TAG" --repo "$REPO" --json publishedAt -q .publishedAt)"
|
|
||||||
|
|
||||||
cat > /tmp/latest.yml <<YMLEOF
|
|
||||||
version: $VERSION
|
|
||||||
files:
|
|
||||||
- url: $X64_ASSET
|
|
||||||
sha512: $SHA512_B64
|
|
||||||
size: $SIZE
|
|
||||||
path: $X64_ASSET
|
|
||||||
sha512: $SHA512_B64
|
|
||||||
releaseDate: '$RELEASE_DATE'
|
|
||||||
YMLEOF
|
|
||||||
|
|
||||||
# Strip leading spaces (heredoc indentation)
|
|
||||||
sed -i 's/^ //' /tmp/latest.yml
|
|
||||||
cat /tmp/latest.yml
|
|
||||||
|
|
||||||
gh release upload "$TAG" --repo "$REPO" /tmp/latest.yml --clobber
|
|
||||||
echo "latest.yml updated successfully to point to $X64_ASSET"
|
|
||||||
|
|
||||||
- name: Generate release notes with platform download links
|
- name: Generate release notes with platform download links
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -294,18 +223,10 @@ jobs:
|
|||||||
## macOS 安装提示(未知来源)
|
## macOS 安装提示(未知来源)
|
||||||
- 若打开时提示“来自未知开发者”或“无法验证开发者”,请到「系统设置 -> 隐私与安全性」中允许打开该应用。
|
- 若打开时提示“来自未知开发者”或“无法验证开发者”,请到「系统设置 -> 隐私与安全性」中允许打开该应用。
|
||||||
- 如果仍被系统拦截,请在终端执行以下命令去除隔离标记:
|
- 如果仍被系统拦截,请在终端执行以下命令去除隔离标记:
|
||||||
- xattr -rd com.apple.quarantine /Applications/WeFlow.app
|
- `xattr -dr com.apple.quarantine "/Applications/WeFlow.app"`
|
||||||
- 执行后重新打开 WeFlow。
|
- 执行后重新打开 WeFlow。
|
||||||
|
|
||||||
> 如果某个平台链接暂时未生成,可进入完整发布页查看全部资源:$RELEASE_PAGE
|
> 如果某个平台链接暂时未生成,可进入完整发布页查看全部资源:$RELEASE_PAGE
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
gh release edit "$TAG" --repo "$REPO" --notes-file release_notes.md
|
gh release edit "$TAG" --repo "$REPO" --notes-file release_notes.md
|
||||||
|
|
||||||
- name: Mark release as published (no longer pre-release)
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
gh release edit "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --latest --no-fail-on-no-release --draft=false --prerelease=false
|
|
||||||
|
|||||||
@@ -146,67 +146,27 @@ const normalizeReleaseNotes = (rawReleaseNotes: unknown): string => {
|
|||||||
|
|
||||||
if (!merged.trim()) return ''
|
if (!merged.trim()) return ''
|
||||||
|
|
||||||
const normalizeHeadingText = (raw: string): string => {
|
|
||||||
return raw
|
|
||||||
.replace(/<[^>]*>/g, ' ')
|
|
||||||
.replace(/ /gi, ' ')
|
|
||||||
.replace(/&/gi, '&')
|
|
||||||
.replace(/</gi, '<')
|
|
||||||
.replace(/>/gi, '>')
|
|
||||||
.replace(/"/gi, '"')
|
|
||||||
.replace(/'/gi, '\'')
|
|
||||||
.replace(/'/gi, '\'')
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/[::]/g, '')
|
|
||||||
.replace(/\s+/g, '')
|
|
||||||
.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
const shouldStripReleaseSection = (headingRaw: string): boolean => {
|
const shouldStripReleaseSection = (headingRaw: string): boolean => {
|
||||||
const heading = normalizeHeadingText(headingRaw)
|
const heading = headingRaw.trim().toLowerCase()
|
||||||
if (!heading) return false
|
if (heading === '下载' || heading === 'download') return true
|
||||||
if (heading.startsWith('下载') || heading.startsWith('download')) return true
|
|
||||||
|
|
||||||
if ((heading.includes('macos') || heading.startsWith('mac')) && heading.includes('安装提示')) return true
|
const compactHeading = heading.replace(/\s+/g, '')
|
||||||
|
if (compactHeading.startsWith('macos安装提示')) return true
|
||||||
|
if (compactHeading.startsWith('mac安装提示')) return true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兼容 electron-updater 直接返回 HTML 的场景(含 dir/anchor 等标签嵌套)
|
// 兼容 electron-updater 直接返回 HTML 的场景
|
||||||
const removeDownloadSectionFromHtml = (input: string): string => {
|
const removeDownloadSectionFromHtml = (input: string): string => {
|
||||||
const headingPattern = /<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi
|
return input
|
||||||
const headings: Array<{ start: number; end: number; headingText: string }> = []
|
.replace(
|
||||||
let match: RegExpExecArray | null
|
/<h[1-6][^>]*>\s*(?:下载|download)\s*<\/h[1-6]>\s*[\s\S]*?(?=<h[1-6]\b|$)/gi,
|
||||||
|
''
|
||||||
while ((match = headingPattern.exec(input)) !== null) {
|
)
|
||||||
const full = match[0]
|
.replace(
|
||||||
headings.push({
|
/<h[1-6][^>]*>\s*(?:mac\s*os|mac)\s*安装提示(?:\s*[((]\s*未知来源\s*[))])?\s*<\/h[1-6]>\s*[\s\S]*?(?=<h[1-6]\b|$)/gi,
|
||||||
start: match.index,
|
''
|
||||||
end: match.index + full.length,
|
)
|
||||||
headingText: match[2] || ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headings.length === 0) return input
|
|
||||||
|
|
||||||
const rangesToRemove: Array<{ start: number; end: number }> = []
|
|
||||||
for (let i = 0; i < headings.length; i += 1) {
|
|
||||||
const current = headings[i]
|
|
||||||
if (!shouldStripReleaseSection(current.headingText)) continue
|
|
||||||
|
|
||||||
const nextStart = i + 1 < headings.length ? headings[i + 1].start : input.length
|
|
||||||
rangesToRemove.push({ start: current.start, end: nextStart })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rangesToRemove.length === 0) return input
|
|
||||||
|
|
||||||
let output = ''
|
|
||||||
let cursor = 0
|
|
||||||
for (const range of rangesToRemove) {
|
|
||||||
output += input.slice(cursor, range.start)
|
|
||||||
cursor = range.end
|
|
||||||
}
|
|
||||||
output += input.slice(cursor)
|
|
||||||
return output
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兼容 Markdown 场景(Action 最终 release note 模板)
|
// 兼容 Markdown 场景(Action 最终 release note 模板)
|
||||||
@@ -235,8 +195,6 @@ const normalizeReleaseNotes = (rawReleaseNotes: unknown): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cleaned = removeDownloadSectionFromMarkdown(removeDownloadSectionFromHtml(merged))
|
const cleaned = removeDownloadSectionFromMarkdown(removeDownloadSectionFromHtml(merged))
|
||||||
// 兜底:即使没有匹配到标题,也不在弹窗展示 macOS 隔离标记清理命令
|
|
||||||
.replace(/^[ \t>*-]*`?\s*xattr\s+-[a-z]*d[a-z]*\s+com\.apple\.quarantine[^\n]*`?\s*$/gim, '')
|
|
||||||
.replace(/\n{3,}/g, '\n\n')
|
.replace(/\n{3,}/g, '\n\n')
|
||||||
.trim()
|
.trim()
|
||||||
|
|
||||||
|
|||||||
@@ -93,39 +93,27 @@ export class DbPathService {
|
|||||||
const possiblePaths: string[] = []
|
const possiblePaths: string[] = []
|
||||||
const home = homedir()
|
const home = homedir()
|
||||||
|
|
||||||
|
// macOS 微信路径(固定)
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
// macOS 微信 4.0.5+ 新路径(优先检测)
|
|
||||||
const appSupportBase = join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Library', 'Application Support', 'com.tencent.xinWeChat')
|
|
||||||
if (existsSync(appSupportBase)) {
|
|
||||||
try {
|
|
||||||
const entries = readdirSync(appSupportBase)
|
|
||||||
for (const entry of entries) {
|
|
||||||
// 匹配形如 2.0b4.0.9 的版本目录
|
|
||||||
if (/^\d+\.\d+b\d+\.\d+/.test(entry) || /^\d+\.\d+\.\d+/.test(entry)) {
|
|
||||||
possiblePaths.push(join(appSupportBase, entry))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch { }
|
|
||||||
}
|
|
||||||
// macOS 旧路径兜底
|
|
||||||
possiblePaths.push(join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files'))
|
possiblePaths.push(join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files'))
|
||||||
} else {
|
} else {
|
||||||
// Windows 微信4.x 数据目录
|
// Windows 微信4.x 数据目录
|
||||||
possiblePaths.push(join(home, 'Documents', 'xwechat_files'))
|
possiblePaths.push(join(home, 'Documents', 'xwechat_files'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (const path of possiblePaths) {
|
for (const path of possiblePaths) {
|
||||||
if (!existsSync(path)) continue
|
if (existsSync(path)) {
|
||||||
|
const rootName = path.split(/[/\\]/).pop()?.toLowerCase()
|
||||||
|
if (rootName !== 'xwechat_files' && rootName !== 'wechat files') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否有有效的账号目录,或本身就是账号目录
|
// 检查是否有有效的账号目录
|
||||||
const accounts = this.findAccountDirs(path)
|
const accounts = this.findAccountDirs(path)
|
||||||
if (accounts.length > 0) {
|
if (accounts.length > 0) {
|
||||||
return { success: true, path }
|
return { success: true, path }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果该目录本身就是账号目录(直接包含 db_storage 等)
|
|
||||||
if (this.isAccountDir(path)) {
|
|
||||||
return { success: true, path }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,20 +295,6 @@ export class DbPathService {
|
|||||||
getDefaultPath(): string {
|
getDefaultPath(): string {
|
||||||
const home = homedir()
|
const home = homedir()
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
// 优先返回 4.0.5+ 新路径
|
|
||||||
const appSupportBase = join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Library', 'Application Support', 'com.tencent.xinWeChat')
|
|
||||||
if (existsSync(appSupportBase)) {
|
|
||||||
try {
|
|
||||||
const entries = readdirSync(appSupportBase)
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (/^\d+\.\d+b\d+\.\d+/.test(entry) || /^\d+\.\d+\.\d+/.test(entry)) {
|
|
||||||
const candidate = join(appSupportBase, entry)
|
|
||||||
if (existsSync(candidate)) return candidate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch { }
|
|
||||||
}
|
|
||||||
// 旧版本路径兜底
|
|
||||||
return join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files')
|
return join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files')
|
||||||
}
|
}
|
||||||
return join(home, 'Documents', 'xwechat_files')
|
return join(home, 'Documents', 'xwechat_files')
|
||||||
|
|||||||
@@ -935,17 +935,10 @@ export class KeyServiceMac {
|
|||||||
private resolveXwechatRootFromPath(accountPath?: string): string | null {
|
private resolveXwechatRootFromPath(accountPath?: string): string | null {
|
||||||
const normalized = String(accountPath || '').replace(/\\/g, '/').replace(/\/+$/, '')
|
const normalized = String(accountPath || '').replace(/\\/g, '/').replace(/\/+$/, '')
|
||||||
if (!normalized) return null
|
if (!normalized) return null
|
||||||
|
|
||||||
// 旧路径:xwechat_files
|
|
||||||
const marker = '/xwechat_files'
|
const marker = '/xwechat_files'
|
||||||
const markerIdx = normalized.indexOf(marker)
|
const markerIdx = normalized.indexOf(marker)
|
||||||
if (markerIdx >= 0) return normalized.slice(0, markerIdx + marker.length)
|
if (markerIdx < 0) return null
|
||||||
|
return normalized.slice(0, markerIdx + marker.length)
|
||||||
// 新路径(微信 4.0.5+):Application Support/com.tencent.xinWeChat/2.0b4.0.9
|
|
||||||
const newMarkerMatch = normalized.match(/^(.*\/com\.tencent\.xinWeChat\/(?:\d+\.\d+b\d+\.\d+|\d+\.\d+\.\d+))(\/|$)/)
|
|
||||||
if (newMarkerMatch) return newMarkerMatch[1]
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private pushAccountIdCandidates(candidates: string[], value?: string): void {
|
private pushAccountIdCandidates(candidates: string[], value?: string): void {
|
||||||
@@ -1103,16 +1096,6 @@ export class KeyServiceMac {
|
|||||||
candidates.add(`${base}/app_data/net/kvcomm`)
|
candidates.add(`${base}/app_data/net/kvcomm`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 微信 4.0.5+ 新路径推导:版本目录同级的 net/kvcomm
|
|
||||||
const newMarkerMatch = normalized.match(/^(.*\/com\.tencent\.xinWeChat\/(?:\d+\.\d+b\d+\.\d+|\d+\.\d+\.\d+))/)
|
|
||||||
if (newMarkerMatch) {
|
|
||||||
const versionBase = newMarkerMatch[1]
|
|
||||||
candidates.add(`${versionBase}/net/kvcomm`)
|
|
||||||
// 上级目录也尝试
|
|
||||||
const parentBase = versionBase.replace(/\/[^\/]+$/, '')
|
|
||||||
candidates.add(`${parentBase}/net/kvcomm`)
|
|
||||||
}
|
|
||||||
|
|
||||||
let cursor = accountPath
|
let cursor = accountPath
|
||||||
for (let i = 0; i < 6; i++) {
|
for (let i = 0; i < 6; i++) {
|
||||||
candidates.add(join(cursor, 'net', 'kvcomm'))
|
candidates.add(join(cursor, 'net', 'kvcomm'))
|
||||||
|
|||||||
@@ -304,17 +304,7 @@ export class WcdbCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private formatInitProtectionError(code: number): string {
|
private formatInitProtectionError(code: number): string {
|
||||||
const messages: Record<number, string> = {
|
return `错误码: ${code}`
|
||||||
'-3001': '未找到数据库目录 (db_storage),请确认已选择正确的微信数据目录(应包含以 wxid_ 开头的子文件夹)',
|
|
||||||
'-3002': '未找到 session.db 文件,请确认微信已登录并且数据目录完整',
|
|
||||||
'-3003': '数据库句柄无效,请重试',
|
|
||||||
'-3004': '恢复数据库连接失败,请重试',
|
|
||||||
'-2301': '动态库加载失败,请检查安装是否完整',
|
|
||||||
'-2302': 'WCDB 初始化异常,请重试',
|
|
||||||
'-2303': 'WCDB 未能成功初始化',
|
|
||||||
}
|
|
||||||
const msg = messages[String(code) as keyof typeof messages]
|
|
||||||
return msg ? `${msg} (错误码: ${code})` : `操作失败,错误码: ${code}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isLogEnabled(): boolean {
|
private isLogEnabled(): boolean {
|
||||||
@@ -490,49 +480,6 @@ export class WcdbCore {
|
|||||||
}
|
}
|
||||||
} catch { }
|
} catch { }
|
||||||
}
|
}
|
||||||
// 兜底:向上查找 db_storage(最多 2 级),处理用户选择了子目录的情况
|
|
||||||
try {
|
|
||||||
let parent = normalized
|
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
const up = join(parent, '..')
|
|
||||||
if (up === parent) break
|
|
||||||
parent = up
|
|
||||||
const candidateUp = join(parent, 'db_storage')
|
|
||||||
if (existsSync(candidateUp)) return candidateUp
|
|
||||||
if (wxid) {
|
|
||||||
const viaWxidUp = join(parent, wxid, 'db_storage')
|
|
||||||
if (existsSync(viaWxidUp)) return viaWxidUp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch { }
|
|
||||||
// 兜底:递归搜索 basePath 下的 db_storage 目录(最多 3 层深)
|
|
||||||
try {
|
|
||||||
const found = this.findDbStorageRecursive(normalized, 3)
|
|
||||||
if (found) return found
|
|
||||||
} catch { }
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private findDbStorageRecursive(dir: string, maxDepth: number): string | null {
|
|
||||||
if (maxDepth <= 0) return null
|
|
||||||
try {
|
|
||||||
const entries = readdirSync(dir)
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (entry.toLowerCase() === 'db_storage') {
|
|
||||||
const candidate = join(dir, entry)
|
|
||||||
try { if (statSync(candidate).isDirectory()) return candidate } catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const entry of entries) {
|
|
||||||
const entryPath = join(dir, entry)
|
|
||||||
try {
|
|
||||||
if (statSync(entryPath).isDirectory()) {
|
|
||||||
const found = this.findDbStorageRecursive(entryPath, maxDepth - 1)
|
|
||||||
if (found) return found
|
|
||||||
}
|
|
||||||
} catch { }
|
|
||||||
}
|
|
||||||
} catch { }
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
"build": "tsc && vite build && electron-builder",
|
"build": "tsc && vite build && electron-builder",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"electron:dev": "vite --mode electron",
|
"electron:dev": "vite --mode electron",
|
||||||
"electron:build": "npm run build"
|
"electron:build": "npm run build",
|
||||||
|
"preinstall": "node preinstall.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
@@ -178,4 +179,4 @@
|
|||||||
],
|
],
|
||||||
"icon": "resources/icon.icns"
|
"icon": "resources/icon.icns"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
public/icon.ico
BIN
public/icon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 364 KiB |
BIN
public/icon.png
BIN
public/icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 570 KiB |
BIN
public/logo.png
BIN
public/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 570 KiB |
Reference in New Issue
Block a user