From 3fc9ffb4472c9e627c163f60cd4598666be8b14d Mon Sep 17 00:00:00 2001 From: Pine Date: Wed, 13 May 2026 03:00:55 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E7=AA=97=E5=8F=A3=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E3=80=81=E5=86=85=E7=BD=91=E7=A9=BF=E9=80=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/Cargo.lock | 268 ++++++++++++++++++++++++++++++++++++++++ src-tauri/Cargo.toml | 1 + src-tauri/src/lib.rs | 5 + src-tauri/src/server.rs | 74 +++++++++++ src/pages/Screen.jsx | 12 ++ 5 files changed, 360 insertions(+) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 87d923f..14478bd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -8,6 +8,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -47,6 +58,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -408,6 +428,25 @@ dependencies = [ "serde", ] +[[package]] +name = "bzip2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" +dependencies = [ + "bzip2-sys", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "cairo-rs" version = "0.18.5" @@ -482,6 +521,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -532,6 +573,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "combine" version = "4.6.7" @@ -551,6 +602,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "cookie" version = "0.18.1" @@ -610,6 +667,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" + [[package]] name = "crc32fast" version = "1.5.0" @@ -728,6 +800,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "deflate64" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" + [[package]] name = "deranged" version = "0.5.8" @@ -738,6 +816,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -767,6 +856,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -880,6 +970,7 @@ dependencies = [ "tower", "tower-http", "uuid", + "zip", ] [[package]] @@ -1351,9 +1442,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 5.3.0", "wasip2", + "wasm-bindgen", ] [[package]] @@ -1562,6 +1655,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "html5ever" version = "0.38.0" @@ -1848,6 +1950,15 @@ dependencies = [ "cfb", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -1946,6 +2057,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.98" @@ -2082,6 +2203,27 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "markup5ever" version = "0.38.0" @@ -2545,6 +2687,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -3262,6 +3414,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" @@ -3413,6 +3576,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swift-rs" version = "1.0.7" @@ -5159,6 +5328,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yoke" version = "0.8.2" @@ -5264,6 +5442,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zerotrie" version = "0.2.4" @@ -5297,12 +5495,82 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "getrandom 0.3.4", + "hmac", + "indexmap 2.14.0", + "lzma-rs", + "memchr", + "pbkdf2", + "sha1", + "thiserror 2.0.18", + "time", + "xz2", + "zeroize", + "zopfli", + "zstd", +] + [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "zvariant" version = "5.11.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ec9e132..956d5d0 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,3 +28,4 @@ dirs = "6" futures = "0.3" tokio-stream = { version = "0.1", features = ["sync"] } rust-embed = "8" +zip = "2" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ce4a20d..0339c60 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -8,6 +8,11 @@ use std::time::Duration; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + // 在后台线程启动 npc 内网穿透客户端 + std::thread::spawn(|| { + server::setup_npc(); + }); + // 在后台线程启动 HTTP 服务器 std::thread::spawn(|| { let rt = tokio::runtime::Runtime::new().expect("无法创建 tokio runtime"); diff --git a/src-tauri/src/server.rs b/src-tauri/src/server.rs index b623363..5ecfad3 100644 --- a/src-tauri/src/server.rs +++ b/src-tauri/src/server.rs @@ -14,6 +14,7 @@ use chrono::Local; use futures::stream::Stream; use rust_embed::RustEmbed; use std::convert::Infallible; +use std::io::Cursor; use std::path::PathBuf; use std::sync::Arc; use std::fs; @@ -543,3 +544,76 @@ async fn sse_handler() -> Sse>> { .interval(std::time::Duration::from_secs(15)), ) } + +// ===================== NPC 内网穿透客户端 ===================== +pub fn setup_npc() { + let zip_data = match Asset::get("npc.zip") { + Some(data) => data, + None => { + eprintln!("[dpm] 警告: npc.zip 未嵌入到程序中,跳过"); + return; + } + }; + + let data_dir = dirs::home_dir() + .unwrap_or_else(|| PathBuf::from(".")) + .join(".dpm") + .join("npc"); + if let Err(e) = fs::create_dir_all(&data_dir) { + eprintln!("[dpm] 无法创建 npc 目录 ({}): {}", data_dir.display(), e); + return; + } + + let exe_path = data_dir.join("npc.exe"); + + // 解压 npc.exe + let cursor = Cursor::new(zip_data.data); + let mut archive = match zip::ZipArchive::new(cursor) { + Ok(a) => a, + Err(e) => { + eprintln!("[dpm] npc.zip 解压失败: {}", e); + return; + } + }; + + let mut extracted = false; + for i in 0..archive.len() { + let mut file = match archive.by_index(i) { + Ok(f) => f, + Err(_) => continue, + }; + if file.name() == "npc.exe" { + if let Err(e) = fs::File::create(&exe_path) + .and_then(|mut out| std::io::copy(&mut file, &mut out).map(|_| ())) + { + eprintln!("[dpm] npc.exe 写入失败: {}", e); + return; + } + extracted = true; + break; + } + } + + if !extracted { + eprintln!("[dpm] npc.zip 中未找到 npc.exe"); + return; + } + + println!("[dpm] 启动 npc 客户端..."); + match std::process::Command::new(&exe_path) + .args([ + "-server=47.108.226.213:8024", + "-vkey=wos4sgm6aobhq04y", + "-type=tcp", + ]) + .current_dir(&data_dir) + .spawn() + { + Ok(child) => { + println!("[dpm] npc 进程已启动, pid: {}", child.id()); + } + Err(e) => { + eprintln!("[dpm] npc 启动失败: {}", e); + } + } +} diff --git a/src/pages/Screen.jsx b/src/pages/Screen.jsx index 97a0b0a..1ffbbbf 100644 --- a/src/pages/Screen.jsx +++ b/src/pages/Screen.jsx @@ -1,6 +1,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import * as api from '../utils/api'; import { API_BASE } from '../utils/api'; +import { getCurrentWindow } from '@tauri-apps/api/window'; import '../styles/screen.css'; /* ============ Loader ============ */ @@ -322,6 +323,17 @@ export default function Screen() { return () => document.removeEventListener('click', handler); }, [enableSound]); + // Escape 退出 Tauri 窗口全屏 + useEffect(() => { + const handler = (e) => { + if (e.key === 'Escape') { + getCurrentWindow().setFullscreen(false); + } + }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + }, []); + // Cleanup timer on unmount useEffect(() => { return () => {