From 28d1b5d1c64eb1e2f45fbb3fdaecb63922461af8 Mon Sep 17 00:00:00 2001 From: cheney Date: Thu, 6 Aug 2026 08:58:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B0=83=E8=AF=95=EF=BC=8C?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=E6=AD=A3=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 39 +++++++++++++++++++++ package.json | 7 ++-- scripts/wsl/build-apk.sh | 76 +++++++++++++++++++++++++++++----------- src/debug/pagespy.js | 21 +++++++++++ src/main.jsx | 3 ++ 5 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 src/debug/pagespy.js diff --git a/package-lock.json b/package-lock.json index 8fc8e5b..a187a06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "iboard", "version": "1.0.0", "dependencies": { + "@huolala-tech/page-spy": "^1.5.7", "@prisma/adapter-mariadb": "^7.9.1", "@prisma/client": "^7.7.0", "cors": "^2.8.5", @@ -279,6 +280,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz", @@ -768,6 +778,20 @@ "hono": "^4" } }, + "node_modules/@huolala-tech/page-spy": { + "version": "1.5.7", + "resolved": "https://registry.npmmirror.com/@huolala-tech/page-spy/-/page-spy-1.5.7.tgz", + "integrity": "sha512-M19WkH6K2taPpdi7gZ9Tz6v0vY/yW4ziffPXdU+p9+pdSmu1s0OhfxjcsWgERAYsaRLZNloSWqJZyWT5zsYIwA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.0", + "copy-to-clipboard": "^3.3.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -2997,6 +3021,15 @@ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/cors": { "version": "2.8.6", "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz", @@ -5616,6 +5649,12 @@ "node": ">=18" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index 0539b92..3af08ed 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "type": "module", "scripts": { - "tauri": "tauri", + "tauri": "tauri", "dev:web": "vite", "dev:server": "node --experimental-strip-types --no-warnings server/index.js", "dev": "concurrently -n web,api -c blue,green \"npm run dev:web\" \"npm run dev:server\"", @@ -22,6 +22,7 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { + "@huolala-tech/page-spy": "^1.5.7", "@prisma/adapter-mariadb": "^7.9.1", "@prisma/client": "^7.7.0", "cors": "^2.8.5", @@ -29,8 +30,8 @@ "express": "^4.21.0", "gray-matter": "^4.0.3", "jsonwebtoken": "^9.0.2", - "marked": "^17.0.6", "mariadb": "^3.5.3", + "marked": "^17.0.6", "prisma": "^7.7.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -48,4 +49,4 @@ "sharp": "^0.33.0", "vite": "^5.4.0" } -} \ No newline at end of file +} diff --git a/scripts/wsl/build-apk.sh b/scripts/wsl/build-apk.sh index 648231b..b723dae 100644 --- a/scripts/wsl/build-apk.sh +++ b/scripts/wsl/build-apk.sh @@ -39,15 +39,25 @@ echo "VITE_API_BASE=$VITE_API_BASE" echo "=== 1. 构建前端 ===" npm run build +# 1.5 拷贝 dist/ 到 Android 工程 assets/, 绕过 Tauri CLI 时这步要做 +# Tauri CLI 默认在 android build 前会干这事, 我们走直 gradle 要自己补上 +ANDROID_ASSETS="$WIN_PROJECT/src-tauri/gen/android/app/src/main/assets" +mkdir -p "$ANDROID_ASSETS" +# 清空旧的 (避免上次的残余) +find "$ANDROID_ASSETS" -mindepth 1 -maxdepth 1 ! -name dexopt ! -name tauri.conf.json -exec rm -rf {} + +cp -r dist/. "$ANDROID_ASSETS/" +echo "前端 bundle 已拷贝到 $ANDROID_ASSETS" +ls -la "$ANDROID_ASSETS" | head -10 + # 2. 走不同路径 cd src-tauri # 全部 4 个 Android 架构的 rustBuild 任务 (Tauri 2 默认会编 4 个) RUST_BUILD_TASKS=( ":app:rustBuildArm64Release" - ":app:rustBuildArmv7Release" + ":app:rustBuildArmRelease" ":app:rustBuildX86_64Release" - ":app:rustBuildI686Release" + ":app:rustBuildX86Release" ) SKIP_ARGS=() for t in "${RUST_BUILD_TASKS[@]}"; do SKIP_ARGS+=(-x "${t#:}"); done @@ -77,25 +87,51 @@ if [ "$VARIANT" = "release" ]; then ./gradlew --no-daemon assembleRelease "${SKIP_ARGS[@]}" APK_DIR="$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/universal/release" - echo - echo "=== 产物 (未签名) ===" - ls -lh "$APK_DIR"/*.apk 2>/dev/null || echo "(未找到 APK)" + # Gradle 配了 signingConfig 就输出 app-universal-release.apk, + # 否则输出 app-universal-release-unsigned.apk, 按文件名判断是否已签名 + SIGNED_APK="$APK_DIR/app-universal-release.apk" + UNSIGNED_APK="$APK_DIR/app-universal-release-unsigned.apk" + + if [ -f "$SIGNED_APK" ]; then + APK_PATH="$SIGNED_APK" + APK_STATUS="已签名" + elif [ -f "$UNSIGNED_APK" ]; then + APK_PATH="$UNSIGNED_APK" + APK_STATUS="未签名" + else + APK_PATH="" + APK_STATUS="未找到" + fi echo - echo "=== Release 签名 (按需) ===" - echo "1. 生成 keystore (一次性):" - echo " keytool -genkey -v -keystore ~/iboard-release.keystore -alias iboard -keyalg RSA -keysize 2048 -validity 10000" - echo "2. 在 ~/.gradle/gradle.properties 写:" - echo " IBOARD_UPLOAD_STORE_FILE=/home/$(whoami)/iboard-release.keystore" - echo " IBOARD_UPLOAD_KEY_ALIAS=iboard" - echo " IBOARD_UPLOAD_STORE_PASSWORD=..." - echo " IBOARD_UPLOAD_KEY_PASSWORD=..." - echo "3. 重跑本脚本 (Gradle 读 gradle.properties 里的 IBOARD_UPLOAD_* 自动用 releaseSigning)" - echo - echo "=== 临时用 debug keystore 手动签 (仅测试) ===" - echo " \$ANDROID_HOME/build-tools/*/apksigner sign --ks ~/.android/debug.keystore \\" - echo " --ks-pass pass:android --key-pass pass:android \\" - echo " $APK_DIR/app-universal-release-unsigned.apk" + echo "=== 产物 ($APK_STATUS) ===" + if [ -n "$APK_PATH" ]; then + ls -lh "$APK_PATH" + else + ls -lh "$APK_DIR"/*.apk 2>/dev/null || echo "(未找到 APK)" + fi + + if [ "$APK_STATUS" = "已签名" ]; then + echo + echo "=== 安装到设备 ===" + echo " adb install -r $APK_PATH" + elif [ "$APK_STATUS" = "未签名" ]; then + echo + echo "=== Release 签名 (按需) ===" + echo "1. 生成 keystore (一次性):" + echo " keytool -genkey -v -keystore ~/iboard-release.keystore -alias iboard -keyalg RSA -keysize 2048 -validity 10000" + echo "2. 在 ~/.gradle/gradle.properties 写:" + echo " IBOARD_UPLOAD_STORE_FILE=/home/$(whoami)/iboard-release.keystore" + echo " IBOARD_UPLOAD_KEY_ALIAS=iboard" + echo " IBOARD_UPLOAD_STORE_PASSWORD=..." + echo " IBOARD_UPLOAD_KEY_PASSWORD=..." + echo "3. 重跑本脚本 (Gradle 读 gradle.properties 里的 IBOARD_UPLOAD_* 自动用 releaseSigning)" + echo + echo "=== 临时用 debug keystore 手动签 (仅测试) ===" + echo " \$ANDROID_HOME/build-tools/*/apksigner sign --ks ~/.android/debug.keystore \" + echo " --ks-pass pass:android --key-pass pass:android \" + echo " $APK_PATH" + fi else echo "[debug] Tauri CLI debug 路径 (不传 --release 不会触发 bug), 直接走" npx tauri android build --apk @@ -104,4 +140,4 @@ else echo echo "=== 产物 ===" ls -lh "$APK_DIR"/*.apk 2>/dev/null || echo "(未找到 APK)" -fi \ No newline at end of file +fi diff --git a/src/debug/pagespy.js b/src/debug/pagespy.js new file mode 100644 index 0000000..c30b7ca --- /dev/null +++ b/src/debug/pagespy.js @@ -0,0 +1,21 @@ +// 仅在 Tauri WebView 内启用 PageSpy, 用于生产环境远程调试. +// web 模式 (Vite dev / 浏览器) 通过检测 window.__TAURI_INTERNALS__ 直接跳过, +// 配合 dynamic import 让 page-spy 这个 chunk 在 web 模式下完全不下载. +// VITE_PAGESPY_API 留空可关闭连接 (例如本地调试不想连远端 PageSpy 服务时). +// 加载失败不阻塞应用启动, 仅 console.warn 提示. + +const PAGESPY_API = import.meta.env.VITE_PAGESPY_API || 'http://bh.vps.honor3.com:6752' +const inTauri = typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window + +if (inTauri && PAGESPY_API) { + // 动态 import: 仅在 Tauri 内才下载 page-spy chunk + import('@huolala-tech/page-spy') + .then(({ default: PageSpy }) => { + // PageSpy 默认会 hook fetch / XMLHttpRequest, 自动抓取网络请求和错误 + const instance = new PageSpy({ api: PAGESPY_API }) + window.__IBOARD_PAGESPY__ = instance + }) + .catch((err) => { + console.warn('[PageSpy] init failed:', err) + }) +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 13b27f5..c624370 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,3 +1,6 @@ +// 远程调试: 仅在 Tauri WebView 内启用, web 模式自动跳过 +import './debug/pagespy.js' + import React from 'react' import ReactDOM from 'react-dom/client' import { BrowserRouter } from 'react-router-dom'