Add device HWID support to sing-box subscriptions
This commit is contained in:
@@ -232,6 +232,8 @@ pub struct LocalSingBoxStatusResponse {
|
||||
pub struct LocalSingBoxConfigDto {
|
||||
pub subscription_display_url: Option<String>,
|
||||
pub has_subscription: bool,
|
||||
pub device_hwid_display: Option<String>,
|
||||
pub has_device_hwid: bool,
|
||||
pub selected_server_tag: Option<String>,
|
||||
pub listen_host: String,
|
||||
pub listen_port: u16,
|
||||
@@ -262,6 +264,15 @@ pub struct SubscriptionServerDto {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveSingBoxSubscriptionInputDto {
|
||||
pub subscription_url: String,
|
||||
#[serde(default)]
|
||||
pub device_hwid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveSingBoxDeviceHwidInputDto {
|
||||
#[serde(default)]
|
||||
pub device_hwid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -491,6 +502,7 @@ pub trait SubscriptionFetcher {
|
||||
fn fetch_subscription(
|
||||
&self,
|
||||
url: &str,
|
||||
identity: &subscription::SubscriptionFetchIdentity,
|
||||
) -> Result<SubscriptionCache, subscription::SubscriptionError>;
|
||||
}
|
||||
|
||||
@@ -500,8 +512,9 @@ impl SubscriptionFetcher for SystemSubscriptionFetcher {
|
||||
fn fetch_subscription(
|
||||
&self,
|
||||
url: &str,
|
||||
identity: &subscription::SubscriptionFetchIdentity,
|
||||
) -> Result<SubscriptionCache, subscription::SubscriptionError> {
|
||||
subscription::fetch_subscription(url)
|
||||
subscription::fetch_subscription_with_identity(url, identity)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,6 +684,14 @@ pub fn save_singbox_subscription(
|
||||
save_singbox_subscription_to_storage(&state.storage(), input, &SystemClock)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn save_singbox_device_hwid(
|
||||
state: tauri::State<'_, CommandState>,
|
||||
input: SaveSingBoxDeviceHwidInputDto,
|
||||
) -> Result<LocalSingBoxStatusResponse, CommandError> {
|
||||
save_singbox_device_hwid_to_storage(&state.storage(), input, &SystemClock)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn fetch_singbox_subscription(
|
||||
state: tauri::State<'_, CommandState>,
|
||||
@@ -1411,6 +1432,9 @@ pub fn save_singbox_subscription_to_storage(
|
||||
|
||||
let mut config = storage.read_local_singbox_config().map_err(storage_error)?;
|
||||
config.subscription_url = Some(subscription_url);
|
||||
if let Some(device_hwid) = input.device_hwid {
|
||||
config.device_hwid = normalize_device_hwid(&device_hwid)?;
|
||||
}
|
||||
config.updated_at = Some(clock.now());
|
||||
storage
|
||||
.write_local_singbox_config(&config)
|
||||
@@ -1438,8 +1462,10 @@ pub fn fetch_singbox_subscription_with_fetcher(
|
||||
)
|
||||
})?;
|
||||
|
||||
let identity =
|
||||
subscription::SubscriptionFetchIdentity::with_device_hwid(config.device_hwid.as_deref());
|
||||
let cache = fetcher
|
||||
.fetch_subscription(&subscription_url)
|
||||
.fetch_subscription(&subscription_url, &identity)
|
||||
.map_err(|error| CommandError::new("singbox_subscription_fetch_failed", error.message))?;
|
||||
let selected_tag = config
|
||||
.selected_server_tag
|
||||
@@ -1475,6 +1501,7 @@ pub fn forget_singbox_subscription_in_storage(
|
||||
) -> Result<LocalSingBoxStatusResponse, CommandError> {
|
||||
let mut config = storage.read_local_singbox_config().map_err(storage_error)?;
|
||||
config.subscription_url = None;
|
||||
config.device_hwid = None;
|
||||
config.selected_server_tag = None;
|
||||
config.updated_at = Some(clock.now());
|
||||
storage
|
||||
@@ -1487,6 +1514,24 @@ pub fn forget_singbox_subscription_in_storage(
|
||||
read_singbox_status(storage)
|
||||
}
|
||||
|
||||
pub fn save_singbox_device_hwid_to_storage(
|
||||
storage: &JsonStorage,
|
||||
input: SaveSingBoxDeviceHwidInputDto,
|
||||
clock: &impl Clock,
|
||||
) -> Result<LocalSingBoxStatusResponse, CommandError> {
|
||||
let mut config = storage.read_local_singbox_config().map_err(storage_error)?;
|
||||
config.device_hwid = match input.device_hwid {
|
||||
Some(device_hwid) => normalize_device_hwid(&device_hwid)?,
|
||||
None => None,
|
||||
};
|
||||
config.updated_at = Some(clock.now());
|
||||
storage
|
||||
.write_local_singbox_config(&config)
|
||||
.map_err(storage_error)?;
|
||||
|
||||
read_singbox_status(storage)
|
||||
}
|
||||
|
||||
pub fn select_singbox_server_in_storage(
|
||||
storage: &JsonStorage,
|
||||
input: SelectSingBoxServerInputDto,
|
||||
@@ -1689,6 +1734,29 @@ fn validate_subscription_url(subscription_url: &str) -> Result<(), CommandError>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn normalize_device_hwid(raw_hwid: &str) -> Result<Option<String>, CommandError> {
|
||||
let device_hwid = raw_hwid.trim();
|
||||
if device_hwid.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if device_hwid.chars().count() > 256 {
|
||||
return Err(CommandError::new(
|
||||
"singbox_device_hwid_invalid",
|
||||
"HWID устройства должен быть не длиннее 256 символов.",
|
||||
));
|
||||
}
|
||||
|
||||
if !device_hwid.chars().all(|ch| ch.is_ascii_graphic()) {
|
||||
return Err(CommandError::new(
|
||||
"singbox_device_hwid_invalid",
|
||||
"HWID устройства может содержать только печатные ASCII-символы без пробелов.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Some(device_hwid.to_string()))
|
||||
}
|
||||
|
||||
fn ping_subscription_server(server: &SubscriptionServer) -> PingServerResponse {
|
||||
ping_endpoint(&server.tag, &server.server, server.server_port)
|
||||
}
|
||||
@@ -4166,6 +4234,11 @@ impl From<&LocalSingBoxConfig> for LocalSingBoxConfigDto {
|
||||
.subscription_url
|
||||
.as_deref()
|
||||
.is_some_and(|value| !value.trim().is_empty()),
|
||||
device_hwid_display: config.device_hwid_display(),
|
||||
has_device_hwid: config
|
||||
.device_hwid
|
||||
.as_deref()
|
||||
.is_some_and(|value| !value.trim().is_empty()),
|
||||
selected_server_tag: config.selected_server_tag.clone(),
|
||||
listen_host: config.listen_host.clone(),
|
||||
listen_port: config.listen_port,
|
||||
|
||||
Reference in New Issue
Block a user