1
This commit is contained in:
1
src-tauri/resources/data/agent
Normal file
1
src-tauri/resources/data/agent
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
src-tauri/resources/data/oem
Normal file
1
src-tauri/resources/data/oem
Normal file
@@ -0,0 +1 @@
|
||||
3
|
||||
115
src-tauri/src/agent_id.rs
Normal file
115
src-tauri/src/agent_id.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
//! 从应用目录 `resources/data/agent` 读取代理编号。
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Manager, path::BaseDirectory};
|
||||
|
||||
/// 相对可执行文件目录的代理配置路径:`resources/data/agent`。
|
||||
pub const AGENT_RESOURCE_RELATIVE: &[&str] = &["resources", "data", "agent"];
|
||||
|
||||
/// 开发时源码树中的代理配置路径。
|
||||
pub const AGENT_DEV_RELATIVE: &str = "src-tauri/resources/data/agent";
|
||||
|
||||
pub fn read_agent_id_file(path: &Path) -> Option<i64> {
|
||||
let content = std::fs::read_to_string(path).ok()?;
|
||||
parse_agent_id_content(&content)
|
||||
}
|
||||
|
||||
/// 解析代理配置文件路径(打包资源 → 应用目录 → 开发目录)。
|
||||
pub fn resolve_agent_id_path(handle: &AppHandle) -> Option<PathBuf> {
|
||||
if let Ok(path) = handle.path().resolve("data/agent", BaseDirectory::Resource) {
|
||||
if path.is_file() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
locate_agent_id_file()
|
||||
}
|
||||
|
||||
/// 读取当前应用的代理编号;无配置文件时返回 `None`。
|
||||
pub fn load_agent_id(handle: &AppHandle) -> Option<i64> {
|
||||
resolve_agent_id_path(handle).and_then(|path| read_agent_id_file(&path))
|
||||
}
|
||||
|
||||
/// 按可执行文件旁 `resources/data/agent` 或开发目录定位配置文件。
|
||||
pub fn locate_agent_id_file() -> Option<PathBuf> {
|
||||
if let Ok(path) = std::env::var("AICLIENT_AGENT_FILE") {
|
||||
let custom = PathBuf::from(path);
|
||||
if custom.is_file() {
|
||||
return Some(custom);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(dir) = exe.parent() {
|
||||
let bundled = dir.join(path_from_parts(AGENT_RESOURCE_RELATIVE));
|
||||
if bundled.is_file() {
|
||||
return Some(bundled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dev = PathBuf::from(AGENT_DEV_RELATIVE);
|
||||
if dev.is_file() {
|
||||
return Some(dev);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn path_from_parts(parts: &[&str]) -> PathBuf {
|
||||
let mut path = PathBuf::new();
|
||||
for part in parts {
|
||||
path.push(part);
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
fn parse_agent_id_content(content: &str) -> Option<i64> {
|
||||
let trimmed = content.trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Ok(value) = serde_json::from_str::<Value>(trimmed) {
|
||||
if let Some(id) = value.get("agent_id").and_then(|v| v.as_i64()) {
|
||||
if id >= 1 {
|
||||
return Some(id);
|
||||
}
|
||||
}
|
||||
if let Some(id) = value.as_i64() {
|
||||
if id >= 1 {
|
||||
return Some(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let first_line = trimmed.lines().next()?.trim();
|
||||
let id: i64 = first_line.parse().ok()?;
|
||||
if id >= 1 {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_integer_line() {
|
||||
assert_eq!(parse_agent_id_content("5\n"), Some(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_json_object() {
|
||||
assert_eq!(parse_agent_id_content(r#"{"agent_id":7}"#), Some(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_returns_none() {
|
||||
assert_eq!(parse_agent_id_content("abc"), None);
|
||||
assert_eq!(parse_agent_id_content(""), None);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ pub mod ffmpeg;
|
||||
pub mod http;
|
||||
pub mod js_runtime;
|
||||
pub mod nodejs;
|
||||
pub mod agent_id;
|
||||
pub mod oem_context;
|
||||
pub mod oem_id;
|
||||
pub mod oss;
|
||||
@@ -40,12 +41,9 @@ pub fn run() {
|
||||
.setup(|app| {
|
||||
let handle = app.handle().clone();
|
||||
let heartbeat_handle = handle.clone();
|
||||
let oem_id = handle
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map(|dir| oem_id::read_oem_id_file(&dir.join(oem_id::OEM_FILE_NAME)))
|
||||
.unwrap_or(oem_id::DEFAULT_OEM_ID);
|
||||
app.manage(oem_context::OemContext { oem_id });
|
||||
let oem_id = oem_id::load_oem_id(&handle);
|
||||
let agent_id = agent_id::load_agent_id(&handle);
|
||||
app.manage(oem_context::OemContext { oem_id, agent_id });
|
||||
|
||||
tauri::async_runtime::block_on(async move {
|
||||
let app_config = handle.state::<app_config::AppConfig>();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! 启动时解析的 OEM 上下文。
|
||||
//! 启动时从 `resources/data/oem`、`resources/data/agent` 解析的安装配置。
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OemContext {
|
||||
pub oem_id: i64,
|
||||
/// 代理版安装包配置;无 `agent` 文件时为 `None`。
|
||||
pub agent_id: Option<i64>,
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
//! 从应用数据目录的 `oem` 文件读取 OEM 编号。
|
||||
//! 从应用目录 `resources/data/oem` 读取 OEM 编号。
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Manager, path::BaseDirectory};
|
||||
|
||||
pub const DEFAULT_OEM_ID: i64 = 1;
|
||||
|
||||
/// 应用数据目录下的 OEM 配置文件名。
|
||||
pub const OEM_FILE_NAME: &str = "oem";
|
||||
/// 相对可执行文件目录的 OEM 配置路径:`resources/data/oem`。
|
||||
pub const OEM_RESOURCE_RELATIVE: &[&str] = &["resources", "data", "oem"];
|
||||
|
||||
/// 开发时源码树中的 OEM 配置路径。
|
||||
pub const OEM_DEV_RELATIVE: &str = "src-tauri/resources/data/oem";
|
||||
|
||||
pub fn read_oem_id_file(path: &Path) -> i64 {
|
||||
let content = match std::fs::read_to_string(path) {
|
||||
@@ -17,6 +21,57 @@ pub fn read_oem_id_file(path: &Path) -> i64 {
|
||||
parse_oem_id_content(&content).unwrap_or(DEFAULT_OEM_ID)
|
||||
}
|
||||
|
||||
/// 解析 OEM 配置文件路径(打包资源 → 应用目录 → 开发目录)。
|
||||
pub fn resolve_oem_id_path(handle: &AppHandle) -> Option<PathBuf> {
|
||||
if let Ok(path) = handle.path().resolve("data/oem", BaseDirectory::Resource) {
|
||||
if path.is_file() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
locate_oem_id_file()
|
||||
}
|
||||
|
||||
/// 读取当前应用的 OEM 编号。
|
||||
pub fn load_oem_id(handle: &AppHandle) -> i64 {
|
||||
resolve_oem_id_path(handle)
|
||||
.map(|path| read_oem_id_file(&path))
|
||||
.unwrap_or(DEFAULT_OEM_ID)
|
||||
}
|
||||
|
||||
/// 按可执行文件旁 `resources/data/oem` 或开发目录定位配置文件。
|
||||
pub fn locate_oem_id_file() -> Option<PathBuf> {
|
||||
if let Ok(path) = std::env::var("AICLIENT_OEM_FILE") {
|
||||
let custom = PathBuf::from(path);
|
||||
if custom.is_file() {
|
||||
return Some(custom);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(dir) = exe.parent() {
|
||||
let bundled = dir.join(path_from_parts(OEM_RESOURCE_RELATIVE));
|
||||
if bundled.is_file() {
|
||||
return Some(bundled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dev = PathBuf::from(OEM_DEV_RELATIVE);
|
||||
if dev.is_file() {
|
||||
return Some(dev);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn path_from_parts(parts: &[&str]) -> PathBuf {
|
||||
let mut path = PathBuf::new();
|
||||
for part in parts {
|
||||
path.push(part);
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
fn parse_oem_id_content(content: &str) -> Option<i64> {
|
||||
let trimmed = content.trim();
|
||||
if trimmed.is_empty() {
|
||||
|
||||
Reference in New Issue
Block a user