Compare commits

..

53 Commits
v4.1.8 ... main

Author SHA1 Message Date
hicccc77
d537d81f1c fix(deps): 修复安全漏洞 2026-03-28 21:15:14 +08:00
hicccc77
26c6700152 fix: 修复 CodeQL code scanning warning 问题 2026-03-28 21:12:29 +08:00
hicccc77
49fb96d7a3 Revert "Revert "fix(deps): 修复安全漏洞""
This reverts commit d256ee5696.
2026-03-28 19:29:17 +08:00
hicccc77
d256ee5696 Revert "fix(deps): 修复安全漏洞"
This reverts commit 06079659af.
2026-03-28 19:28:45 +08:00
hicccc77
bd70a7bfa8 Revert "fix: 修复 Linux 下内存扫描找不到微信进程的问题\n\n增加 pidof/pgrep/ps aux 三重兜底逻辑,兼容不同发行版\n(flatpak、AppImage、wechat-bin 等安装方式),解决 #575"
This reverts commit 3fb09bad0d.
2026-03-28 19:28:45 +08:00
hicccc77
3fb09bad0d fix: 修复 Linux 下内存扫描找不到微信进程的问题\n\n增加 pidof/pgrep/ps aux 三重兜底逻辑,兼容不同发行版\n(flatpak、AppImage、wechat-bin 等安装方式),解决 #575 2026-03-28 19:05:21 +08:00
hicccc77
06079659af fix(deps): 修复安全漏洞 2026-03-28 17:36:28 +08:00
hicccc77
22d8049c2c Revert "fix: 兼容微信新目录结构多一层嵌套导致账号目录识别失败的问题"
This reverts commit 5f6b0e8960.
2026-03-28 17:30:56 +08:00
hicccc77
5f6b0e8960 fix: 兼容微信新目录结构多一层嵌套导致账号目录识别失败的问题
修复 scanWxids 和 scanWxidCandidates 在 2.0b4.0.9/xwechat_files/wxid_xxx
结构下扫描不到账号目录的问题,增加往下多扫一层的兜底逻辑

Fixes #541
2026-03-28 17:28:52 +08:00
hicccc77
9b8da7774d fix: 替换失效的 downloads badge 为 shields.io 2026-03-28 17:05:33 +08:00
hicccc77
eabed55a7a fix: 修复 README Downloads badge 嵌套在 Issues 链接内的问题 2026-03-28 17:03:28 +08:00
hicccc77
32cc74f99c merge: 同步 main 最新代码到 dev(依赖更新、版本 4.3.0、资源文件) 2026-03-28 16:54:47 +08:00
cc
ffc4cc3d96 Merge pull request #574 from hicccc77/dependabot/npm_and_yarn/npm_and_yarn-8abc9b7730
chore(deps): bump the npm_and_yarn group across 1 directory with 3 updates
2026-03-28 16:50:28 +08:00
dependabot[bot]
007cf57efd chore(deps): bump the npm_and_yarn group across 1 directory with 3 updates
Bumps the npm_and_yarn group with 3 updates in the / directory: [minimatch](https://github.com/isaacs/minimatch), [brace-expansion](https://github.com/juliangruber/brace-expansion) and [rollup](https://github.com/rollup/rollup).


Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5)

Updates `brace-expansion` from 1.1.12 to 1.1.13
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.13)

Updates `rollup` from 4.55.1 to 4.60.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.55.1...v4.60.0)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.13
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: rollup
  dependency-version: 4.60.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-28 07:56:41 +00:00
hicccc77
c6dba71197 fix: 修复 macOS release notes 中 xattr 命令被 bash 吞掉的问题 2026-03-28 15:55:11 +08:00
cc
8aa162e294 Merge pull request #568 from hicccc77/dependabot/npm_and_yarn/npm_and_yarn-1ca40131d0
chore(deps): bump @tootallnate/once from 2.0.0 to removed in the npm_and_yarn group across 1 directory
2026-03-28 14:51:11 +08:00
dependabot[bot]
51d6dec7ff chore(deps): bump @tootallnate/once
Bumps the npm_and_yarn group with 1 update in the / directory: [@tootallnate/once](https://github.com/TooTallNate/once).


Removes `@tootallnate/once`

---
updated-dependencies:
- dependency-name: "@tootallnate/once"
  dependency-version: 
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 22:39:12 +00:00
hicccc77
f1b2762769 fix(deps): 修复安全漏洞 2026-03-28 06:37:44 +08:00
hicccc77
d126be2aa5 chore: 更新资源文件 2026-03-27 22:03:27 +08:00
hicccc77
ea034ee76a ci: fix pnpm audit exit code causing job failure 2026-03-27 21:43:16 +08:00
hicccc77
39634a690c fix(deps): remove ajv override to fix electron-builder compatibility 2026-03-27 21:09:28 +08:00
hicccc77
a7001eb6da fix(deps): upgrade react-router-dom to 7.13.2 and add pnpm overrides for security vulnerabilities
- Upgrade react-router-dom ^7.1.1 -> ^7.13.2
- Add pnpm.overrides to force safe versions of: tar, minimatch, rollup,
  immutable, lodash, ajv, brace-expansion, picomatch
2026-03-27 21:04:44 +08:00
hicccc77
71e3540f18 ci: add gitleaks config to suppress false positives 2026-03-27 20:58:14 +08:00
hicccc77
78cadfd352 ci: add security-events write permission for CodeQL 2026-03-27 19:12:31 +08:00
hicccc77
da15f829d3 ci: add security-events write permission for CodeQL 2026-03-27 19:12:20 +08:00
hicccc77
bb60694013 ci: fix pnpm install frozen-lockfile issue 2026-03-27 18:17:26 +08:00
hicccc77
b3758d2baf ci: fix pnpm install frozen-lockfile issue 2026-03-27 18:17:14 +08:00
hicccc77
bc794e9a44 ci: add daily security scan workflow for all branches 2026-03-27 17:59:09 +08:00
hicccc77
c80115d0f7 ci: add daily security scan workflow for all branches 2026-03-27 17:56:35 +08:00
xuncha
6277576249 Merge pull request #560 from JiQingzhe2004/main
feat: 强制更新支持 minimumVersion,阻止低版本用户继续使用
2026-03-27 15:23:02 +08:00
JiQingzhe2004
2201d369fa chore: bump version to 4.3.0 2026-03-27 14:43:33 +08:00
JiQingzhe2004
9f4e4790f5 feat: 强制更新支持 minimumVersion,阻止低版本用户继续使用 2026-03-27 14:43:08 +08:00
xuncha
501e373e38 Merge pull request #559 from xunchahaha/main
更新打包
2026-03-27 13:10:50 +08:00
xuncha
b2cf7c92d5 更新打包 2026-03-27 13:10:27 +08:00
xuncha
e92e13c045 Merge pull request #558 from hicccc77/xunchahaha-patch-1
Delete preinstall.js
2026-03-27 12:59:45 +08:00
xuncha
f3dec958b0 Delete preinstall.js 2026-03-27 12:48:42 +08:00
hicccc77
0cf8ea8166 chore: bump version to 4.2.0 2026-03-26 23:20:42 +08:00
hicccc77
74b830dd79 chore: update service files and xkey_helper
- xkey_helper: use mach exception port to intercept EXC_BREAKPOINT,
  fixes key capture failure on macOS 26.2
2026-03-26 23:19:08 +08:00
cc
8668c168a7 333 2026-03-26 22:43:59 +08:00
cc
8b8c5f33ce 333 2026-03-26 22:34:50 +08:00
cc
2fcbb026df 222 2026-03-26 22:32:33 +08:00
cc
66ee72380d 222 2026-03-26 22:30:21 +08:00
cc
4f16345351 111 2026-03-26 22:26:33 +08:00
cc
5110618996 再次修复 2026-03-26 22:19:30 +08:00
cc
bf51368cf4 修复密钥问题 2026-03-26 22:16:30 +08:00
cc
d6054745d6 修复macos打包错误 2026-03-26 22:00:42 +08:00
hicccc77
a4731f25f8 chore: update xkey_helper (macOS) with pure semantic scan mode
Always use pure semantic scan mode (KNOWN_RVA=0) regardless of
WeChat version, improving compatibility for versions < 4.1.8.
2026-03-26 21:16:14 +08:00
hicccc77
6c4507e495 fix(ci): remove invalid --no-fail-on-no-release flag from gh release edit 2026-03-26 20:33:18 +08:00
cc
cfa335564a Merge pull request #549 from hicccc77/dev
Dev
2026-03-25 20:02:58 +08:00
Forrest
61ef10de9b Merge pull request #545 from JiQingzhe2004/main
更新图标
2026-03-25 02:09:50 +08:00
Forrest
73f36d6b29 更新图标 2026-03-25 01:36:04 +08:00
Forrest
666a1a3296 Merge branch 'hicccc77:main' into main 2026-03-25 00:18:12 +08:00
xuncha
b5a371da87 Merge pull request #349 from hicccc77/dev
Dev
2026-03-13 08:55:32 +03:00
28 changed files with 887 additions and 1431 deletions

View File

@@ -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
@@ -68,9 +49,24 @@ jobs:
run: | run: |
npx electron-builder --mac dmg --arm64 --publish always npx electron-builder --mac dmg --arm64 --publish always
- name: Inject minimumVersion into latest yml
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
TAG=${GITHUB_REF_NAME}
REPO=${{ github.repository }}
MINIMUM_VERSION="4.1.7"
for YML_FILE in latest-mac.yml latest-arm64-mac.yml; do
gh release download "$TAG" --repo "$REPO" --pattern "$YML_FILE" --output "/tmp/$YML_FILE" 2>/dev/null || continue
if ! grep -q 'minimumVersion' "/tmp/$YML_FILE"; then
echo "minimumVersion: $MINIMUM_VERSION" >> "/tmp/$YML_FILE"
fi
gh release upload "$TAG" --repo "$REPO" "/tmp/$YML_FILE" --clobber
done
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
@@ -105,9 +101,22 @@ jobs:
run: | run: |
npx electron-builder --linux --publish always npx electron-builder --linux --publish always
- name: Inject minimumVersion into latest yml
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
TAG=${GITHUB_REF_NAME}
REPO=${{ github.repository }}
MINIMUM_VERSION="4.1.7"
gh release download "$TAG" --repo "$REPO" --pattern "latest-linux.yml" --output "/tmp/latest-linux.yml" 2>/dev/null
if [ -f /tmp/latest-linux.yml ] && ! grep -q 'minimumVersion' /tmp/latest-linux.yml; then
echo "minimumVersion: $MINIMUM_VERSION" >> /tmp/latest-linux.yml
gh release upload "$TAG" --repo "$REPO" /tmp/latest-linux.yml --clobber
fi
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
@@ -137,15 +146,27 @@ jobs:
npx vite build npx vite build
- name: Package and Publish - name: Package and Publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npx electron-builder --win nsis --x64 --publish always '--config.artifactName=${productName}-${version}-x64-Setup.${ext}'
- name: Inject minimumVersion into latest yml
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash shell: bash
run: | run: |
npx electron-builder --win nsis --x64 --publish always "-c.artifactName=\${productName}-\${version}-x64-Setup.\${ext}" TAG=${GITHUB_REF_NAME}
REPO=${{ github.repository }}
MINIMUM_VERSION="4.1.7"
gh release download "$TAG" --repo "$REPO" --pattern "latest.yml" --output "/tmp/latest.yml" 2>/dev/null
if [ -f /tmp/latest.yml ] && ! grep -q 'minimumVersion' /tmp/latest.yml; then
echo "minimumVersion: $MINIMUM_VERSION" >> /tmp/latest.yml
gh release upload "$TAG" --repo "$REPO" /tmp/latest.yml --clobber
fi
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
@@ -175,11 +196,24 @@ jobs:
npx vite build npx vite build
- name: Package and Publish Windows arm64 - name: Package and Publish Windows arm64
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npx electron-builder --win nsis --arm64 --publish always '--config.publish.channel=latest-arm64' '--config.artifactName=${productName}-${version}-arm64-Setup.${ext}'
- name: Inject minimumVersion into latest yml
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash shell: bash
run: | run: |
npx electron-builder --win nsis --arm64 --publish always -c.publish.channel=latest-arm64 "-c.artifactName=\${productName}-\${version}-arm64-Setup.\${ext}" TAG=${GITHUB_REF_NAME}
REPO=${{ github.repository }}
MINIMUM_VERSION="4.1.7"
gh release download "$TAG" --repo "$REPO" --pattern "latest-arm64.yml" --output "/tmp/latest-arm64.yml" 2>/dev/null
if [ -f /tmp/latest-arm64.yml ] && ! grep -q 'minimumVersion' /tmp/latest-arm64.yml; then
echo "minimumVersion: $MINIMUM_VERSION" >> /tmp/latest-arm64.yml
gh release upload "$TAG" --repo "$REPO" /tmp/latest-arm64.yml --clobber
fi
update-release-notes: update-release-notes:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -190,53 +224,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 +281,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

95
.github/workflows/security-scan.yml vendored Normal file
View File

@@ -0,0 +1,95 @@
name: Security Scan
on:
schedule:
- cron: '0 2 * * *' # 每天 UTC 02:00北京时间 10:00
workflow_dispatch: # 支持手动触发
permissions:
contents: read
security-events: write
actions: read
jobs:
security-scan:
name: Security Scan (${{ matrix.branch }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
branch:
- main
steps:
- name: Checkout ${{ matrix.branch }}
uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
# 1. npm audit - 检查依赖漏洞
- name: Dependency vulnerability audit
run: pnpm audit --audit-level=moderate
continue-on-error: true
# 2. CodeQL 静态分析
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript, typescript
queries: security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: '/language:javascript-typescript/branch:${{ matrix.branch }}'
# 3. 密钥/敏感信息扫描
- name: Secret scanning with Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
# 动态获取所有分支并扫描(排除已在 matrix 中的)
scan-all-branches:
name: Scan additional branches
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: List all branches
id: branches
run: |
git branch -r | grep -v HEAD | sed 's|origin/||' | tr -d ' ' | while read branch; do
echo "Branch: $branch"
done
- name: Run pnpm audit on all branches
run: |
git branch -r | grep -v HEAD | sed 's|origin/||' | tr -d ' ' | while read branch; do
echo "===== Auditing branch: $branch ====="
git checkout "$branch" 2>/dev/null || continue
pnpm install --frozen-lockfile --silent 2>/dev/null || npm install --silent 2>/dev/null || true
pnpm audit --audit-level=moderate 2>/dev/null || true
done
continue-on-error: true

23
.gitleaks.toml Normal file
View File

@@ -0,0 +1,23 @@
title = "Gitleaks Config"
[extend]
# 继承默认规则
useDefault = true
# 排除误报路径
[[rules]]
id = "curl-auth-header"
[rules.allowlist]
paths = [
'''docs/HTTP-API\.md'''
]
regexes = [
'''YOUR_TOKEN'''
]
[[rules]]
id = "generic-api-key"
[rules.allowlist]
paths = [
'''src/pages/ChatPage\.tsx'''
]

View File

@@ -19,7 +19,9 @@ WeFlow 是一个**完全本地**的微信**实时**聊天记录查看、分析
</a> </a>
<a href="https://github.com/hicccc77/WeFlow/issues"> <a href="https://github.com/hicccc77/WeFlow/issues">
<img src="https://img.shields.io/github/issues/hicccc77/WeFlow?style=flat-square" alt="Issues"> <img src="https://img.shields.io/github/issues/hicccc77/WeFlow?style=flat-square" alt="Issues">
<img src="https://gh-down-badges.linkof.link/hicccc77/WeFlow/" alt="Downloads" /> </a>
<a href="https://github.com/hicccc77/WeFlow/releases">
<img src="https://img.shields.io/github/downloads/hicccc77/WeFlow/total?style=flat-square" alt="Downloads" />
</a> </a>
<a href="https://t.me/weflow_cc"> <a href="https://t.me/weflow_cc">
<img src="https://img.shields.io/badge/Telegram%20频道-0088cc?style=flat-square&logo=telegram&logoColor=0088cc&labelColor=white" alt="Telegram"> <img src="https://img.shields.io/badge/Telegram%20频道-0088cc?style=flat-square&logo=telegram&logoColor=0088cc&labelColor=white" alt="Telegram">

View File

@@ -1242,7 +1242,8 @@ function registerIpcHandlers() {
return { return {
hasUpdate: true, hasUpdate: true,
version: latestVersion, version: latestVersion,
releaseNotes: normalizeReleaseNotes(result.updateInfo.releaseNotes) releaseNotes: normalizeReleaseNotes(result.updateInfo.releaseNotes),
minimumVersion: (result.updateInfo as any).minimumVersion
} }
} }
} }
@@ -1300,7 +1301,7 @@ function registerIpcHandlers() {
try { try {
console.log('[Update] 开始下载更新...') console.log('[Update] 开始下载更新...')
await autoUpdater.downloadUpdate() await autoUpdater.downloadUpdate()
} catch (error) { } catch (error: any) {
console.error('[Update] 下载更新失败:', error) console.error('[Update] 下载更新失败:', error)
// 失败时清理状态和监听器 // 失败时清理状态和监听器
isDownloadInProgress = false isDownloadInProgress = false
@@ -1312,7 +1313,10 @@ function registerIpcHandlers() {
autoUpdater.removeListener('update-downloaded', downloadedHandler) autoUpdater.removeListener('update-downloaded', downloadedHandler)
downloadedHandler = null downloadedHandler = null
} }
throw error
// 统一错误提示格式,避免出现 [object Object] 的 JSON 字符串
const errorMessage = error.message || (typeof error === 'string' ? error : JSON.stringify(error))
throw new Error(errorMessage)
} }
}) })
@@ -2636,19 +2640,19 @@ function registerIpcHandlers() {
// 密钥获取 // 密钥获取
ipcMain.handle('key:autoGetDbKey', async (event) => { ipcMain.handle('key:autoGetDbKey', async (event) => {
return keyService.autoGetDbKey(180_000, (message, level) => { return keyService.autoGetDbKey(180_000, (message: string, level: number) => {
event.sender.send('key:dbKeyStatus', { message, level }) event.sender.send('key:dbKeyStatus', { message, level })
}) })
}) })
ipcMain.handle('key:autoGetImageKey', async (event, manualDir?: string, wxid?: string) => { ipcMain.handle('key:autoGetImageKey', async (event, manualDir?: string, wxid?: string) => {
return keyService.autoGetImageKey(manualDir, (message) => { return keyService.autoGetImageKey(manualDir, (message: string) => {
event.sender.send('key:imageKeyStatus', { message }) event.sender.send('key:imageKeyStatus', { message })
}, wxid) }, wxid)
}) })
ipcMain.handle('key:scanImageKeyFromMemory', async (event, userDir: string) => { ipcMain.handle('key:scanImageKeyFromMemory', async (event, userDir: string) => {
return keyService.autoGetImageKeyByMemoryScan(userDir, (message) => { return keyService.autoGetImageKeyByMemoryScan(userDir, (message: string) => {
event.sender.send('key:imageKeyStatus', { message }) event.sender.send('key:imageKeyStatus', { message })
}) })
}) })
@@ -2703,7 +2707,8 @@ function checkForUpdatesOnStartup() {
// 通知渲染进程有新版本 // 通知渲染进程有新版本
mainWindow.webContents.send('app:updateAvailable', { mainWindow.webContents.send('app:updateAvailable', {
version: latestVersion, version: latestVersion,
releaseNotes: normalizeReleaseNotes(result.updateInfo.releaseNotes) releaseNotes: normalizeReleaseNotes(result.updateInfo.releaseNotes),
minimumVersion: (result.updateInfo as any).minimumVersion
}) })
} }
} }

View File

@@ -1135,7 +1135,7 @@ class AnnualReportService {
const now = Date.now() const now = Date.now()
if (now - lastProgressAt > 200) { if (now - lastProgressAt > 200) {
let progress = 30 let progress: number
if (totalMessagesForProgress > 0) { if (totalMessagesForProgress > 0) {
const ratio = Math.min(1, processedMessages / totalMessagesForProgress) const ratio = Math.min(1, processedMessages / totalMessagesForProgress)
progress = 30 + Math.floor(ratio * 50) progress = 30 + Math.floor(ratio * 50)

View File

@@ -2009,7 +2009,7 @@ class ChatService {
selectableColumns = resolvedColumns selectableColumns = resolvedColumns
} }
if (!selectableColumns || selectableColumns.length === 0) return rows if (selectableColumns.length === 0) return rows
const selectColumns = ['username', ...selectableColumns] const selectColumns = ['username', ...selectableColumns]
const sql = `SELECT ${selectColumns.map((column) => this.quoteSqlIdentifier(column)).join(', ')} FROM contact WHERE username IS NOT NULL AND username != ''` const sql = `SELECT ${selectColumns.map((column) => this.quoteSqlIdentifier(column)).join(', ')} FROM contact WHERE username IS NOT NULL AND username != ''`

View File

@@ -562,14 +562,14 @@ export class ImageDecryptService {
if (allowThumbnail || !isThumb) { if (allowThumbnail || !isThumb) {
this.logInfo('[ImageDecrypt] hardlink hit (datName)', { imageMd5: imageDatName, path: preferredPath }) this.logInfo('[ImageDecrypt] hardlink hit (datName)', { imageMd5: imageDatName, path: preferredPath })
this.cacheDatPath(accountDir, imageDatName, preferredPath) this.cacheDatPath(accountDir, imageDatName, preferredPath)
if (imageMd5) this.cacheDatPath(accountDir, imageMd5, preferredPath) this.cacheDatPath(accountDir, imageMd5, preferredPath)
return preferredPath return preferredPath
} }
// 找到缩略图但要求高清图,尝试同目录查找高清图变体 // 找到缩略图但要求高清图,尝试同目录查找高清图变体
const hdPath = this.findHdVariantInSameDir(preferredPath) const hdPath = this.findHdVariantInSameDir(preferredPath)
if (hdPath) { if (hdPath) {
this.cacheDatPath(accountDir, imageDatName, hdPath) this.cacheDatPath(accountDir, imageDatName, hdPath)
if (imageMd5) this.cacheDatPath(accountDir, imageMd5, hdPath) this.cacheDatPath(accountDir, imageMd5, hdPath)
return hdPath return hdPath
} }
return null return null

View File

@@ -389,7 +389,7 @@ export class KeyServiceMac {
`set timeoutSec to ${timeoutSec}`, `set timeoutSec to ${timeoutSec}`,
'try', 'try',
'with timeout of timeoutSec seconds', 'with timeout of timeoutSec seconds',
'set outText to do shell script cmd with administrator privileges', 'set outText to do shell script (cmd & " 2>&1") with administrator privileges',
'end timeout', 'end timeout',
'return "WF_OK::" & outText', 'return "WF_OK::" & outText',
'on error errMsg number errNum partial result pr', 'on error errMsg number errNum partial result pr',

1965
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "weflow", "name": "weflow",
"version": "2.1.0", "version": "4.3.0",
"description": "WeFlow", "description": "WeFlow",
"main": "dist-electron/main.js", "main": "dist-electron/main.js",
"author": { "author": {
@@ -38,7 +38,7 @@
"react": "^19.2.3", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router-dom": "^7.1.1", "react-router-dom": "^7.13.2",
"react-virtuoso": "^4.18.1", "react-virtuoso": "^4.18.1",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"sherpa-onnx-node": "^1.10.38", "sherpa-onnx-node": "^1.10.38",
@@ -53,7 +53,7 @@
"@types/react-dom": "^19.1.0", "@types/react-dom": "^19.1.0",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"electron": "^39.2.7", "electron": "^39.2.7",
"electron-builder": "^25.1.8", "electron-builder": "^26.8.1",
"sass": "^1.83.0", "sass": "^1.83.0",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"typescript": "^5.6.3", "typescript": "^5.6.3",
@@ -61,6 +61,18 @@
"vite-plugin-electron": "^0.28.8", "vite-plugin-electron": "^0.28.8",
"vite-plugin-electron-renderer": "^0.14.6" "vite-plugin-electron-renderer": "^0.14.6"
}, },
"pnpm": {
"overrides": {
"tar": ">=6.2.1",
"minimatch": ">=3.1.2",
"rollup": ">=4.0.0",
"immutable": ">=4.0.0",
"lodash": ">=4.17.21",
"brace-expansion": ">=1.1.11",
"picomatch": ">=2.3.1",
"ajv": ">=8.18.0"
}
},
"build": { "build": {
"appId": "com.WeFlow.app", "appId": "com.WeFlow.app",
"publish": { "publish": {
@@ -177,5 +189,11 @@
} }
], ],
"icon": "resources/icon.icns" "icon": "resources/icon.icns"
},
"overrides": {
"picomatch": "^4.0.4",
"tar": "^7.5.13",
"immutable": "^5.1.5",
"ajv": ">=6.14.0"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 570 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 570 KiB

Binary file not shown.

Binary file not shown.

BIN
resources/libwcdb_api.so Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
resources/xkey_helper_macos Normal file

Binary file not shown.

View File

@@ -312,10 +312,14 @@ function App() {
const removeUpdateListener = window.electronAPI?.app?.onUpdateAvailable?.((info: any) => { const removeUpdateListener = window.electronAPI?.app?.onUpdateAvailable?.((info: any) => {
// 发现新版本时保存更新信息,锁定状态下不弹窗,解锁后再显示 // 发现新版本时保存更新信息,锁定状态下不弹窗,解锁后再显示
if (info) { if (info) {
setUpdateInfo({ ...info, hasUpdate: true }) window.electronAPI.app.getVersion().then((currentVersion: string) => {
if (!useAppStore.getState().isLocked) { const isMandatory = !!(info.minimumVersion && currentVersion &&
setShowUpdateDialog(true) currentVersion.localeCompare(info.minimumVersion, undefined, { numeric: true, sensitivity: 'base' }) <= 0)
} setUpdateInfo({ ...info, hasUpdate: true, isMandatory })
if (!useAppStore.getState().isLocked) {
setShowUpdateDialog(true)
}
})
} }
}) })
const removeProgressListener = window.electronAPI?.app?.onDownloadProgress?.((progress: any) => { const removeProgressListener = window.electronAPI?.app?.onDownloadProgress?.((progress: any) => {
@@ -685,10 +689,11 @@ function App() {
<UpdateDialog <UpdateDialog
open={showUpdateDialog} open={showUpdateDialog}
updateInfo={updateInfo} updateInfo={updateInfo}
onClose={() => setShowUpdateDialog(false)} onClose={() => { if (!(updateInfo as any)?.isMandatory) setShowUpdateDialog(false) }}
onUpdate={handleUpdateNow} onUpdate={handleUpdateNow}
onIgnore={handleIgnoreUpdate} onIgnore={handleIgnoreUpdate}
isDownloading={isDownloading} isDownloading={isDownloading}
isMandatory={!!(updateInfo as any)?.isMandatory}
progress={downloadProgress} progress={downloadProgress}
/> />

View File

@@ -283,3 +283,12 @@
opacity: 1; opacity: 1;
} }
} }
.mandatory-tip {
color: #e53e3e;
font-size: 13px;
text-align: center;
margin: 0 0 8px;
padding: 6px 12px;
background: rgba(229, 62, 62, 0.08);
border-radius: 6px;
}

View File

@@ -14,6 +14,7 @@ interface UpdateDialogProps {
onUpdate: () => void onUpdate: () => void
onIgnore?: () => void onIgnore?: () => void
isDownloading: boolean isDownloading: boolean
isMandatory?: boolean
progress: number | { progress: number | {
percent: number percent: number
bytesPerSecond?: number bytesPerSecond?: number
@@ -30,6 +31,7 @@ const UpdateDialog: React.FC<UpdateDialogProps> = ({
onUpdate, onUpdate,
onIgnore, onIgnore,
isDownloading, isDownloading,
isMandatory,
progress progress
}) => { }) => {
if (!open || !updateInfo) return null if (!open || !updateInfo) return null
@@ -69,7 +71,7 @@ const UpdateDialog: React.FC<UpdateDialogProps> = ({
return ( return (
<div className="update-dialog-overlay"> <div className="update-dialog-overlay">
<div className="update-dialog"> <div className="update-dialog">
{!isDownloading && ( {!isDownloading && !isMandatory && (
<button className="close-btn" onClick={onClose}> <button className="close-btn" onClick={onClose}>
<X size={20} /> <X size={20} />
</button> </button>
@@ -119,11 +121,14 @@ const UpdateDialog: React.FC<UpdateDialogProps> = ({
</div> </div>
) : ( ) : (
<div className="actions"> <div className="actions">
{onIgnore && ( {onIgnore && !isMandatory && (
<button className="btn-ignore" onClick={onIgnore}> <button className="btn-ignore" onClick={onIgnore}>
</button> </button>
)} )}
{isMandatory && (
<p className="mandatory-tip">使</p>
)}
<button className="btn-update" onClick={onUpdate}> <button className="btn-update" onClick={onUpdate}>
</button> </button>

View File

@@ -3227,7 +3227,7 @@ function ChatPage(props: ChatPageProps) {
const session = sessionMapRef.current.get(sessionId) const session = sessionMapRef.current.get(sessionId)
const unreadCount = session?.unreadCount ?? 0 const unreadCount = session?.unreadCount ?? 0
let messageLimit = currentBatchSizeRef.current let messageLimit: number
if (offset === 0) { if (offset === 0) {
const preferredLimit = Number.isFinite(options.forceInitialLimit) const preferredLimit = Number.isFinite(options.forceInitialLimit)
@@ -7901,7 +7901,7 @@ function MessageBubble({
useEffect(() => { useEffect(() => {
if (emojiLocalPath) return if (emojiLocalPath) return
// 后端已从本地缓存找到文件(转发表情包无 CDN URL 的情况) // 后端已从本地缓存找到文件(转发表情包无 CDN URL 的情况)
if (isEmoji && message.emojiLocalPath && !emojiLocalPath) { if (isEmoji && message.emojiLocalPath) {
captureEmojiResizeBaseline() captureEmojiResizeBaseline()
setEmojiLocalPath(message.emojiLocalPath) setEmojiLocalPath(message.emojiLocalPath)
return return

BIN
temp_assets.json Normal file

Binary file not shown.