fix: disconnect VPN when sleep

This commit is contained in:
Kevin Yue
2025-01-12 14:41:03 +08:00
parent ec85e857bc
commit b188d61be1
22 changed files with 526 additions and 238 deletions

View File

@@ -18,7 +18,7 @@ roxmltree.workspace = true
serde.workspace = true
specta = { workspace = true, features = ["derive"] }
urlencoding.workspace = true
tokio.workspace = true
tokio = { workspace = true, features = ["process", "signal", "macros"] }
serde_json.workspace = true
whoami.workspace = true
tempfile.workspace = true

View File

@@ -7,4 +7,5 @@ use super::vpn_state::VpnState;
pub enum WsEvent {
VpnState(VpnState),
ActiveGui,
ResumeConnection,
}

View File

@@ -1,10 +1,9 @@
use tokio::fs;
use crate::GP_SERVICE_LOCK_FILE;
use super::lock_file::gpservice_lock_info;
async fn read_port() -> anyhow::Result<String> {
let port = fs::read_to_string(GP_SERVICE_LOCK_FILE).await?;
Ok(port.trim().to_string())
let lock_info = gpservice_lock_info().await?;
Ok(lock_info.port.to_string())
}
pub async fn http_endpoint() -> anyhow::Result<String> {

View File

@@ -1,19 +1,24 @@
use std::path::PathBuf;
use thiserror::Error;
use tokio::fs;
pub struct LockFile {
path: PathBuf,
pid: u32,
}
impl LockFile {
pub fn new<P: Into<PathBuf>>(path: P) -> Self {
Self { path: path.into() }
pub fn new<P: Into<PathBuf>>(path: P, pid: u32) -> Self {
Self { path: path.into(), pid }
}
pub fn exists(&self) -> bool {
self.path.exists()
}
pub fn lock(&self, content: impl AsRef<[u8]>) -> anyhow::Result<()> {
pub fn lock(&self, content: &str) -> anyhow::Result<()> {
let content = format!("{}:{}", self.pid, content);
std::fs::write(&self.path, content)?;
Ok(())
}
@@ -37,3 +42,87 @@ impl LockFile {
}
}
}
#[derive(Error, Debug)]
pub enum LockFileError {
#[error("Failed to read lock file: {0}")]
IoError(#[from] std::io::Error),
#[error("Invalid lock file format: expected 'pid:port'")]
InvalidFormat,
#[error("Invalid PID value: {0}")]
InvalidPid(std::num::ParseIntError),
#[error("Invalid port value: {0}")]
InvalidPort(std::num::ParseIntError),
}
pub struct LockInfo {
pub pid: u32,
pub port: u32,
}
impl LockInfo {
async fn from_file(path: impl AsRef<std::path::Path>) -> Result<Self, LockFileError> {
let content = fs::read_to_string(path).await?;
Self::parse(&content)
}
fn parse(content: &str) -> Result<Self, LockFileError> {
let mut parts = content.trim().split(':');
let pid = parts
.next()
.ok_or(LockFileError::InvalidFormat)?
.parse()
.map_err(LockFileError::InvalidPid)?;
let port = parts
.next()
.ok_or(LockFileError::InvalidFormat)?
.parse()
.map_err(LockFileError::InvalidPort)?;
// Ensure there are no extra parts after pid:port
if parts.next().is_some() {
return Err(LockFileError::InvalidFormat);
}
Ok(Self { pid, port })
}
}
pub async fn gpservice_lock_info() -> Result<LockInfo, LockFileError> {
LockInfo::from_file(crate::GP_SERVICE_LOCK_FILE).await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_valid_input() {
let info = LockInfo::parse("1234:8080").unwrap();
assert_eq!(info.pid, 1234);
assert_eq!(info.port, 8080);
}
#[test]
fn test_parse_invalid_format() {
assert!(matches!(
LockInfo::parse("123:456:789"),
Err(LockFileError::InvalidFormat)
));
}
#[test]
fn test_parse_invalid_numbers() {
assert!(matches!(LockInfo::parse("abc:8080"), Err(LockFileError::InvalidPid(_))));
assert!(matches!(
LockInfo::parse("1234:abc"),
Err(LockFileError::InvalidPort(_))
));
}
}