GPS feature webapp side: GPS mode selector, fixed mode lat/lon, API endpoint. Merging with Wifi client and webdav features

This commit is contained in:
Carlos Guerra
2026-03-28 17:55:39 +01:00
committed by Will Greenberg
parent ac33ebaf53
commit c107314194
8 changed files with 255 additions and 1 deletions
+52
View File
@@ -36,6 +36,12 @@ pub struct Config {
pub min_space_to_start_recording_mb: u64,
/// Minimum disk space required to continue a recording
pub min_space_to_continue_recording_mb: u64,
/// GPS mode: 0=Disabled, 1=Fixed coordinates, 2=API endpoint
pub gps_mode: u8,
/// Fixed latitude used when gps_mode=1
pub gps_fixed_latitude: Option<f64>,
/// Fixed longitude used when gps_mode=1
pub gps_fixed_longitude: Option<f64>,
/// Wifi client SSID
pub wifi_ssid: Option<String>,
/// Wifi client password
@@ -100,6 +106,9 @@ impl Default for Config {
enabled_notifications: vec![NotificationType::Warning, NotificationType::LowBattery],
min_space_to_start_recording_mb: 1,
min_space_to_continue_recording_mb: 1,
gps_mode: 0,
gps_fixed_latitude: None,
gps_fixed_longitude: None,
wifi_ssid: None,
wifi_password: None,
wifi_security: None,
@@ -153,6 +162,49 @@ fn resolve_bin(name: &str) -> Option<String> {
None
}
impl Config {
pub fn wifi_config(&self) -> wifi_station::WifiConfig {
let (wpa_bin, hostapd_conf, ctrl_interface) = match self.device {
Device::Tmobile | Device::Wingtech => (
Some("/usr/sbin/wpa_supplicant".into()),
Some("/data/configs/hostapd.conf".into()),
None,
),
Device::Uz801 => (
Some("/system/bin/wpa_supplicant".into()),
Some("/data/misc/wifi/hostapd.conf".into()),
Some("/data/misc/wifi/sockets".into()),
),
_ => (None, None, None),
};
wifi_station::WifiConfig {
wifi_enabled: self.wifi_enabled,
dns_servers: self.dns_servers.clone(),
wifi_ssid: self.wifi_ssid.clone(),
wifi_password: self.wifi_password.clone(),
security_type: self.wifi_security,
wpa_supplicant_bin: wpa_bin.or_else(|| resolve_bin("wpa_supplicant")),
hostapd_conf,
ctrl_interface,
udhcpc_hook_path: Some("/data/rayhunter/udhcpc-hook.sh".into()),
dhcp_lease_path: Some("/data/rayhunter/dhcp_lease".into()),
wpa_conf_path: Some("/data/rayhunter/wpa_sta.conf".into()),
iw_bin: resolve_bin("iw"),
udhcpc_bin: resolve_bin("udhcpc"),
crash_log_dir: Some("/data/rayhunter/crash-logs".into()),
wakelock_name: Some("rayhunter".into()),
}
}
}
fn resolve_bin(name: &str) -> Option<String> {
let local = format!("/data/rayhunter/bin/{name}");
if std::path::Path::new(&local).exists() {
return Some(local);
}
None
}
pub async fn parse_config<P>(path: P) -> Result<Config, RayhunterError>
where
P: AsRef<std::path::Path>,
+39
View File
@@ -0,0 +1,39 @@
use axum::Json;
use axum::extract::State;
use axum::http::StatusCode;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::server::ServerState;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GpsData {
pub latitude: f64,
pub longitude: f64,
pub timestamp: String,
}
pub async fn post_gps(
State(state): State<Arc<ServerState>>,
Json(gps_data): Json<GpsData>,
) -> Result<StatusCode, (StatusCode, String)> {
if state.config.gps_mode != 2 {
return Err((
StatusCode::FORBIDDEN,
"GPS API endpoint is disabled. Set gps_mode to 2 in configuration.".to_string(),
));
}
let mut gps = state.gps_state.write().await;
*gps = Some(gps_data);
Ok(StatusCode::OK)
}
pub async fn get_gps(
State(state): State<Arc<ServerState>>,
) -> Result<Json<GpsData>, StatusCode> {
let gps = state.gps_state.read().await;
match gps.as_ref() {
Some(data) => Ok(Json(data.clone())),
None => Err(StatusCode::NOT_FOUND),
}
}
+1
View File
@@ -5,6 +5,7 @@ pub mod crypto_provider;
pub mod diag;
pub mod display;
pub mod error;
pub mod gps;
pub mod key_input;
pub mod notifications;
pub mod pcap;
+17
View File
@@ -5,6 +5,7 @@ mod crypto_provider;
mod diag;
mod display;
mod error;
mod gps;
mod key_input;
mod notifications;
mod pcap;
@@ -23,6 +24,7 @@ use crate::error::RayhunterError;
use crate::notifications::{NotificationService, run_notification_worker};
use crate::pcap::get_pcap;
use crate::qmdl_store::RecordingStore;
use crate::gps::{get_gps, post_gps};
use crate::server::{
ServerState, debug_set_display_state, get_config, get_qmdl, get_time, get_wifi_status, get_zip,
scan_wifi, serve_static, set_config, set_time_offset, test_notification,
@@ -78,6 +80,8 @@ fn get_router() -> AppRouter {
.route("/api/time", get(get_time))
.route("/api/time-offset", post(set_time_offset))
.route("/api/debug/display-state", post(debug_set_display_state))
.route("/api/gps", get(get_gps))
.route("/api/gps", post(post_gps))
.route("/", get(|| async { Redirect::permanent("/index.html") }))
.route("/{*path}", get(serve_static))
}
@@ -296,6 +300,18 @@ async fn run_with_config(
config.webdav.clone().into(),
);
}
let initial_gps = if config.gps_mode == 1 {
match (config.gps_fixed_latitude, config.gps_fixed_longitude) {
(Some(lat), Some(lon)) => Some(gps::GpsData {
latitude: lat,
longitude: lon,
timestamp: "fixed".to_string(),
}),
_ => None,
}
} else {
None
};
let state = Arc::new(ServerState {
config_path: args.config_path.clone(),
@@ -308,6 +324,7 @@ async fn run_with_config(
ui_update_sender: Some(ui_update_tx),
wifi_status,
wifi_scan_lock: tokio::sync::Mutex::new(()),
gps_state: Arc::new(tokio::sync::RwLock::new(initial_gps)),
});
run_server(&task_tracker, state, shutdown_token.clone()).await;
+3
View File
@@ -26,6 +26,7 @@ use crate::config::Config;
use crate::diag::DiagDeviceCtrlMessage;
use crate::display::DisplayState;
use crate::notifications::DEFAULT_NOTIFICATION_TIMEOUT;
use crate::gps::GpsData;
use crate::pcap::generate_pcap_data;
use crate::qmdl_store::RecordingStore;
@@ -40,6 +41,7 @@ pub struct ServerState {
pub ui_update_sender: Option<Sender<DisplayState>>,
pub wifi_status: Arc<RwLock<wifi_station::WifiStatus>>,
pub wifi_scan_lock: tokio::sync::Mutex<()>,
pub gps_state: Arc<RwLock<Option<GpsData>>>,
}
#[cfg_attr(feature = "apidocs", utoipa::path(
@@ -566,6 +568,7 @@ mod tests {
ui_update_sender: None,
wifi_status: Arc::new(RwLock::new(wifi_station::WifiStatus::default())),
wifi_scan_lock: tokio::sync::Mutex::new(()),
gps_state: Arc::new(RwLock::new(None)),
})
}