diff --git a/frontend/composables/useApi.js b/frontend/composables/useApi.js index 3858c35..a78b3c9 100644 --- a/frontend/composables/useApi.js +++ b/frontend/composables/useApi.js @@ -358,7 +358,13 @@ export const useApi = () => { method: 'GET', }) } - + + const getWxStatus = () => { + return useFetch('/api/wechat/status', { + method: 'GET', + }) + } + return { detectWechat, detectCurrentAccount, @@ -392,5 +398,6 @@ export const useApi = () => { getWrappedAnnualCard, getDbKey, getImageKey, + getWxStatus, } } diff --git a/frontend/pages/decrypt.vue b/frontend/pages/decrypt.vue index 2942d26..bef19e1 100644 --- a/frontend/pages/decrypt.vue +++ b/frontend/pages/decrypt.vue @@ -361,6 +361,19 @@ + + + +
+ + + +
+

温馨提示

+

{{ warning }}

+
+
+
@@ -403,10 +416,11 @@ import { ref, reactive, computed, onMounted } from 'vue' import { useApi } from '~/composables/useApi' -const { decryptDatabase, saveMediaKeys, getSavedKeys, getDbKey, getImageKey } = useApi() +const { decryptDatabase, saveMediaKeys, getSavedKeys, getDbKey, getImageKey, getWxStatus } = useApi() const loading = ref(false) const error = ref('') +const warning = ref('') // 警告,用于密钥提示 const currentStep = ref(0) const mediaAccount = ref('') const isGettingDbKey = ref(false) @@ -496,10 +510,29 @@ const handleGetDbKey = async () => { isGettingDbKey.value = true error.value = '' - + warning.value = '' formErrors.key = '' try { + const { data: statusData, error: statusError } = await getWxStatus() + + if (statusError.value) { + error.value = '无法获取微信状态: ' + statusError.value.message + isGettingDbKey.value = false + return + } + + const wxStatus = statusData.value?.wx_status + + if (wxStatus?.is_running) { + warning.value = '检测到微信正在运行,5秒后将终止进程并重启以获取密钥!!' + await new Promise(resolve => setTimeout(resolve, 5000)) + } else { + // 没有逻辑 + } + + warning.value = '正在启动微信以获取密钥,请确保微信未开启“自动登录”,并在启动后 1 分钟内完成登录操作。' + const { data, error: fetchError } = await getDbKey() if (fetchError.value) { @@ -509,14 +542,14 @@ const handleGetDbKey = async () => { const res = data.value - if (res && (res.status === 0)) { + if (res && res.status === 0) { if (res.data?.db_key) { formData.key = res.data.db_key + warning.value = '' } - // 有错误信息且不是ok时弹出提示 if (res.errmsg && res.errmsg !== 'ok') { - error.value = res.errmsg + warning.value = res.errmsg } } else { error.value = '获取失败: ' + (res?.errmsg || '未知错误') @@ -536,6 +569,7 @@ const handleGetImageKey = async () => { manualKeyErrors.aes_key = '' error.value = '' + warning.value = '' try { const { data, fetchError } = await getImageKey() @@ -573,6 +607,7 @@ const applyManualKeys = () => { manualKeyErrors.xor_key = '' manualKeyErrors.aes_key = '' error.value = '' + warning.value = '' const aes = normalizeAesKey(manualKeys.aes_key) if (!aes.ok) { @@ -666,6 +701,7 @@ const handleDecrypt = async () => { loading.value = true error.value = '' + warning.value = '' try { const result = await decryptDatabase({ @@ -712,6 +748,7 @@ const decryptAllImages = async () => { mediaDecrypting.value = true mediaDecryptResult.value = null error.value = '' + warning.value = '' // 重置进度 decryptProgress.current = 0 @@ -787,6 +824,7 @@ const decryptAllImages = async () => { // 从密钥步骤进入图片解密步骤 const goToMediaDecryptStep = async () => { error.value = '' + warning.value = '' // 校验并应用(未填写则允许直接进入,后端会使用已保存密钥或报错提示) const ok = applyManualKeys() if (!ok || manualKeyErrors.xor_key || manualKeyErrors.aes_key) return diff --git a/pyproject.toml b/pyproject.toml index a3651f7..3773d41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ "pilk>=0.2.4", "pypinyin>=0.53.0", "wx_key", + "packaging", ] [project.optional-dependencies] diff --git a/src/wechat_decrypt_tool/routers/keys.py b/src/wechat_decrypt_tool/routers/keys.py index f4cf35d..5abd00a 100644 --- a/src/wechat_decrypt_tool/routers/keys.py +++ b/src/wechat_decrypt_tool/routers/keys.py @@ -3,6 +3,7 @@ from typing import Optional from fastapi import APIRouter from ..key_store import get_account_keys_from_store +from ..key_service import get_db_key_workflow from ..media_helpers import _load_media_keys, _resolve_account_dir from ..path_fix import PathFixRoute diff --git a/src/wechat_decrypt_tool/routers/wechat_detection.py b/src/wechat_decrypt_tool/routers/wechat_detection.py index ae0077d..b2fe1c5 100644 --- a/src/wechat_decrypt_tool/routers/wechat_detection.py +++ b/src/wechat_decrypt_tool/routers/wechat_detection.py @@ -1,5 +1,5 @@ from typing import Optional - +import psutil from fastapi import APIRouter from ..logging_config import get_logger @@ -71,3 +71,49 @@ async def detect_current_account(data_root_path: Optional[str] = None): 'error': str(e), 'data': None, } + + +@router.get("/api/wechat/status", summary="检查微信运行状态") +async def check_wechat_status(): + """ + 检查系统中是否有 Weixin.exe 或 WeChat.exe 进程在运行 + 返回: status=0 成功, wx_status={is_running: bool, pid: int, ...} + """ + process_name_targets = ["Weixin.exe", "WeChat.exe"] + + wx_status = { + "is_running": False, + "pid": None, + "exe_path": None, + "memory_usage_mb": 0.0 + } + + try: + for proc in psutil.process_iter(['pid', 'name', 'exe', 'memory_info']): + try: + if proc.info['name'] and proc.info['name'] in process_name_targets: + wx_status["is_running"] = True + wx_status["pid"] = proc.info['pid'] + wx_status["exe_path"] = proc.info['exe'] + + mem = proc.info['memory_info'] + if mem: + wx_status["memory_usage_mb"] = round(mem.rss / (1024 * 1024), 2) + + break + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + continue + + return { + "status": 0, + "errmsg": "ok", + "wx_status": wx_status + } + + except Exception as e: + # 即使出错也返回 JSON,但 status 非 0 + return { + "status": -1, + "errmsg": f"检查进程失败: {str(e)}", + "wx_status": wx_status + } diff --git a/uv.lock b/uv.lock index b8d73d9..fc6360c 100644 --- a/uv.lock +++ b/uv.lock @@ -845,6 +845,7 @@ dependencies = [ { name = "cryptography" }, { name = "fastapi" }, { name = "loguru" }, + { name = "packaging" }, { name = "pilk" }, { name = "psutil" }, { name = "pycryptodome" }, @@ -869,6 +870,7 @@ requires-dist = [ { name = "cryptography", specifier = ">=41.0.0" }, { name = "fastapi", specifier = ">=0.104.0" }, { name = "loguru", specifier = ">=0.7.0" }, + { name = "packaging" }, { name = "pilk", specifier = ">=0.2.4" }, { name = "psutil", specifier = ">=7.0.0" }, { name = "pycryptodome", specifier = ">=3.23.0" }, @@ -901,6 +903,7 @@ wheels = [ { path = "wx_key-1.0.0-cp311-cp311-win_amd64.whl" }, { path = "wx_key-1.0.0-cp312-cp312-win_amd64.whl" }, { path = "wx_key-1.0.0-cp313-cp313-win_amd64.whl" }, + { path = "wx_key-1.0.0-cp314-cp314-win_amd64.whl" }, ] [[package]]