mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-02-19 14:20:51 +08:00
feat: add warning card for db key
This commit is contained in:
@@ -358,7 +358,13 @@ export const useApi = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getWxStatus = () => {
|
||||||
|
return useFetch('/api/wechat/status', {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
detectWechat,
|
detectWechat,
|
||||||
detectCurrentAccount,
|
detectCurrentAccount,
|
||||||
@@ -392,5 +398,6 @@ export const useApi = () => {
|
|||||||
getWrappedAnnualCard,
|
getWrappedAnnualCard,
|
||||||
getDbKey,
|
getDbKey,
|
||||||
getImageKey,
|
getImageKey,
|
||||||
|
getWxStatus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,6 +361,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 警告渲染 -->
|
||||||
|
<transition name="fade">
|
||||||
|
<div v-if="warning" class="bg-amber-50 border border-amber-200 rounded-lg p-4 mt-6 flex items-start">
|
||||||
|
<svg class="h-5 w-5 mr-2 flex-shrink-0 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||||
|
</svg>
|
||||||
|
<div>
|
||||||
|
<p class="font-semibold text-amber-800">温馨提示</p>
|
||||||
|
<p class="text-sm mt-1 text-amber-700">{{ warning }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
<!-- 错误提示 -->
|
<!-- 错误提示 -->
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
@@ -403,10 +416,11 @@
|
|||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import { useApi } from '~/composables/useApi'
|
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 loading = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
|
const warning = ref('') // 警告,用于密钥提示
|
||||||
const currentStep = ref(0)
|
const currentStep = ref(0)
|
||||||
const mediaAccount = ref('')
|
const mediaAccount = ref('')
|
||||||
const isGettingDbKey = ref(false)
|
const isGettingDbKey = ref(false)
|
||||||
@@ -496,10 +510,29 @@ const handleGetDbKey = async () => {
|
|||||||
isGettingDbKey.value = true
|
isGettingDbKey.value = true
|
||||||
|
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
warning.value = ''
|
||||||
formErrors.key = ''
|
formErrors.key = ''
|
||||||
|
|
||||||
try {
|
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()
|
const { data, error: fetchError } = await getDbKey()
|
||||||
|
|
||||||
if (fetchError.value) {
|
if (fetchError.value) {
|
||||||
@@ -509,14 +542,14 @@ const handleGetDbKey = async () => {
|
|||||||
|
|
||||||
const res = data.value
|
const res = data.value
|
||||||
|
|
||||||
if (res && (res.status === 0)) {
|
if (res && res.status === 0) {
|
||||||
if (res.data?.db_key) {
|
if (res.data?.db_key) {
|
||||||
formData.key = res.data.db_key
|
formData.key = res.data.db_key
|
||||||
|
warning.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 有错误信息且不是ok时弹出提示
|
|
||||||
if (res.errmsg && res.errmsg !== 'ok') {
|
if (res.errmsg && res.errmsg !== 'ok') {
|
||||||
error.value = res.errmsg
|
warning.value = res.errmsg
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error.value = '获取失败: ' + (res?.errmsg || '未知错误')
|
error.value = '获取失败: ' + (res?.errmsg || '未知错误')
|
||||||
@@ -536,6 +569,7 @@ const handleGetImageKey = async () => {
|
|||||||
manualKeyErrors.aes_key = ''
|
manualKeyErrors.aes_key = ''
|
||||||
|
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
warning.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data, fetchError } = await getImageKey()
|
const { data, fetchError } = await getImageKey()
|
||||||
@@ -573,6 +607,7 @@ const applyManualKeys = () => {
|
|||||||
manualKeyErrors.xor_key = ''
|
manualKeyErrors.xor_key = ''
|
||||||
manualKeyErrors.aes_key = ''
|
manualKeyErrors.aes_key = ''
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
warning.value = ''
|
||||||
|
|
||||||
const aes = normalizeAesKey(manualKeys.aes_key)
|
const aes = normalizeAesKey(manualKeys.aes_key)
|
||||||
if (!aes.ok) {
|
if (!aes.ok) {
|
||||||
@@ -666,6 +701,7 @@ const handleDecrypt = async () => {
|
|||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
warning.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await decryptDatabase({
|
const result = await decryptDatabase({
|
||||||
@@ -712,6 +748,7 @@ const decryptAllImages = async () => {
|
|||||||
mediaDecrypting.value = true
|
mediaDecrypting.value = true
|
||||||
mediaDecryptResult.value = null
|
mediaDecryptResult.value = null
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
warning.value = ''
|
||||||
|
|
||||||
// 重置进度
|
// 重置进度
|
||||||
decryptProgress.current = 0
|
decryptProgress.current = 0
|
||||||
@@ -787,6 +824,7 @@ const decryptAllImages = async () => {
|
|||||||
// 从密钥步骤进入图片解密步骤
|
// 从密钥步骤进入图片解密步骤
|
||||||
const goToMediaDecryptStep = async () => {
|
const goToMediaDecryptStep = async () => {
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
warning.value = ''
|
||||||
// 校验并应用(未填写则允许直接进入,后端会使用已保存密钥或报错提示)
|
// 校验并应用(未填写则允许直接进入,后端会使用已保存密钥或报错提示)
|
||||||
const ok = applyManualKeys()
|
const ok = applyManualKeys()
|
||||||
if (!ok || manualKeyErrors.xor_key || manualKeyErrors.aes_key) return
|
if (!ok || manualKeyErrors.xor_key || manualKeyErrors.aes_key) return
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ dependencies = [
|
|||||||
"pilk>=0.2.4",
|
"pilk>=0.2.4",
|
||||||
"pypinyin>=0.53.0",
|
"pypinyin>=0.53.0",
|
||||||
"wx_key",
|
"wx_key",
|
||||||
|
"packaging",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from typing import Optional
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from ..key_store import get_account_keys_from_store
|
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 ..media_helpers import _load_media_keys, _resolve_account_dir
|
||||||
from ..path_fix import PathFixRoute
|
from ..path_fix import PathFixRoute
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
import psutil
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from ..logging_config import get_logger
|
from ..logging_config import get_logger
|
||||||
@@ -71,3 +71,49 @@ async def detect_current_account(data_root_path: Optional[str] = None):
|
|||||||
'error': str(e),
|
'error': str(e),
|
||||||
'data': None,
|
'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
|
||||||
|
}
|
||||||
|
|||||||
3
uv.lock
generated
3
uv.lock
generated
@@ -845,6 +845,7 @@ dependencies = [
|
|||||||
{ name = "cryptography" },
|
{ name = "cryptography" },
|
||||||
{ name = "fastapi" },
|
{ name = "fastapi" },
|
||||||
{ name = "loguru" },
|
{ name = "loguru" },
|
||||||
|
{ name = "packaging" },
|
||||||
{ name = "pilk" },
|
{ name = "pilk" },
|
||||||
{ name = "psutil" },
|
{ name = "psutil" },
|
||||||
{ name = "pycryptodome" },
|
{ name = "pycryptodome" },
|
||||||
@@ -869,6 +870,7 @@ requires-dist = [
|
|||||||
{ name = "cryptography", specifier = ">=41.0.0" },
|
{ name = "cryptography", specifier = ">=41.0.0" },
|
||||||
{ name = "fastapi", specifier = ">=0.104.0" },
|
{ name = "fastapi", specifier = ">=0.104.0" },
|
||||||
{ name = "loguru", specifier = ">=0.7.0" },
|
{ name = "loguru", specifier = ">=0.7.0" },
|
||||||
|
{ name = "packaging" },
|
||||||
{ name = "pilk", specifier = ">=0.2.4" },
|
{ name = "pilk", specifier = ">=0.2.4" },
|
||||||
{ name = "psutil", specifier = ">=7.0.0" },
|
{ name = "psutil", specifier = ">=7.0.0" },
|
||||||
{ name = "pycryptodome", specifier = ">=3.23.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-cp311-cp311-win_amd64.whl" },
|
||||||
{ path = "wx_key-1.0.0-cp312-cp312-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-cp313-cp313-win_amd64.whl" },
|
||||||
|
{ path = "wx_key-1.0.0-cp314-cp314-win_amd64.whl" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
Reference in New Issue
Block a user