diff --git a/Makefile b/Makefile index 03803e384..52e0b1b71 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ clean: eos.img: $(EMBASSY_SRC) system-images/compat/compat.tar system-images/utils/utils.tar ! test -f eos.img || rm eos.img - if [ $(NO_KEY) -eq 1 ]; then NO_KEY=1 ./build/make-image.sh; else ./build/make-image.sh; fi + if [ $(NO_KEY) = 1 ]; then NO_KEY=1 ./build/make-image.sh; else ./build/make-image.sh; fi system-images/compat/compat.tar: $(COMPAT_SRC) cd system-images/compat && ./build.sh diff --git a/appmgr/src/context/rpc.rs b/appmgr/src/context/rpc.rs index b9026386f..b7243a835 100644 --- a/appmgr/src/context/rpc.rs +++ b/appmgr/src/context/rpc.rs @@ -206,10 +206,7 @@ impl RpcContext { notification_manager, open_authed_websockets: Mutex::new(BTreeMap::new()), rpc_stream_continuations: Mutex::new(BTreeMap::new()), - wifi_manager: Arc::new(RwLock::new(WpaCli::init( - "wlan0".to_string(), - base.datadir().join("main"), - ))), + wifi_manager: Arc::new(RwLock::new(WpaCli::init("wlan0".to_string()))), }); let metrics_seed = seed.clone(); tokio::spawn(async move { diff --git a/appmgr/src/db/model.rs b/appmgr/src/db/model.rs index a7949bd04..6edf1a373 100644 --- a/appmgr/src/db/model.rs +++ b/appmgr/src/db/model.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use chrono::{DateTime, Utc}; use emver::VersionRange; +use isocountry::CountryCode; use patch_db::json_ptr::JsonPointer; use patch_db::{HasModel, Map, MapModel, OptionModel}; use reqwest::Url; @@ -44,6 +45,7 @@ impl Database { id, version: Current::new().semver().into(), last_backup: None, + last_wifi_region: None, eos_version_compat: Current::new().compat().clone(), lan_address: format!("https://{}.local", hostname).parse().unwrap(), tor_address: format!("http://{}", tor_key.public().get_onion_address()) @@ -85,6 +87,8 @@ pub struct ServerInfo { pub id: String, pub version: Version, pub last_backup: Option>, + /// Used in the wifi to determine the region to set the system to + pub last_wifi_region: Option, pub eos_version_compat: VersionRange, pub lan_address: Url, pub tor_address: Url, diff --git a/appmgr/src/init.rs b/appmgr/src/init.rs index 3f7ebffa8..56eea0923 100644 --- a/appmgr/src/init.rs +++ b/appmgr/src/init.rs @@ -59,12 +59,26 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<(), Error> { crate::ssh::sync_keys_from_db(&secret_store, "/root/.ssh/authorized_keys").await?; tracing::info!("Synced SSH Keys"); - - crate::net::wifi::synchronize_wpa_supplicant_conf(&cfg.datadir().join("main")).await?; - tracing::info!("Synchronized wpa_supplicant.conf"); - let db = cfg.db(&secret_store).await?; + let mut handle = db.handle(); + + crate::net::wifi::synchronize_wpa_supplicant_conf( + &cfg.datadir().join("main"), + &*crate::db::DatabaseModel::new() + .server_info() + .last_wifi_region() + .get(&mut handle, false) + .await + .map_err(|_e| { + Error::new( + color_eyre::eyre::eyre!("Could not find the last wifi region"), + crate::ErrorKind::NotFound, + ) + })?, + ) + .await?; + tracing::info!("Synchronized wpa_supplicant.conf"); let mut info = crate::db::DatabaseModel::new() .server_info() .get_mut(&mut handle) diff --git a/appmgr/src/net/wifi.rs b/appmgr/src/net/wifi.rs index 16601b7a3..ac3dd7247 100644 --- a/appmgr/src/net/wifi.rs +++ b/appmgr/src/net/wifi.rs @@ -1,10 +1,13 @@ -use std::collections::BTreeMap; -use std::path::{Path, PathBuf}; +use std::collections::{BTreeMap, HashMap}; +use std::path::Path; use std::sync::Arc; use std::time::Duration; use clap::ArgMatches; use isocountry::CountryCode; +use lazy_static::lazy_static; +use patch_db::DbHandle; +use regex::Regex; use rpc_toolkit::command; use tokio::process::Command; use tokio::sync::RwLock; @@ -15,13 +18,23 @@ use crate::util::serde::{display_serializable, IoFormat}; use crate::util::{display_none, Invoke}; use crate::{Error, ErrorKind}; -#[command(subcommands(add, connect, delete, get, set_country))] +#[command(subcommands(add, connect, delete, get, country, available))] pub async fn wifi() -> Result<(), Error> { Ok(()) } +#[command(subcommands(get_available))] +pub async fn available() -> Result<(), Error> { + Ok(()) +} + +#[command(subcommands(set_country))] +pub async fn country() -> Result<(), Error> { + Ok(()) +} + #[command(display(display_none))] -#[instrument(skip(ctx))] +#[instrument(skip(ctx, password))] pub async fn add( #[context] ctx: RpcContext, #[arg] ssid: String, @@ -42,48 +55,32 @@ pub async fn add( )); } async fn add_procedure( + db: impl DbHandle, wifi_manager: Arc>, - ssid: &str, - password: &str, + ssid: &Ssid, + password: &Psk, priority: isize, - connect: bool, ) -> Result<(), Error> { - tracing::info!("Adding new WiFi network: '{}'", ssid); + tracing::info!("Adding new WiFi network: '{}'", ssid.0); let mut wpa_supplicant = wifi_manager.write().await; - wpa_supplicant.add_network(ssid, password, priority).await?; + wpa_supplicant + .add_network(db, ssid, password, priority) + .await?; drop(wpa_supplicant); - if connect { - let wpa_supplicant = wifi_manager.read().await; - let current = wpa_supplicant.get_current_network().await?; - drop(wpa_supplicant); - let mut wpa_supplicant = wifi_manager.write().await; - let connected = wpa_supplicant.select_network(ssid).await?; - if !connected { - tracing::info!("Failed to add new WiFi network: '{}'", ssid); - wpa_supplicant.remove_network(ssid).await?; - match current { - None => {} - Some(current) => { - wpa_supplicant.select_network(¤t).await?; - } - } - } - } Ok(()) } tokio::spawn(async move { match add_procedure( + &mut ctx.db.handle(), ctx.wifi_manager.clone(), - &ssid, - &password, + &Ssid(ssid.clone()), + &Psk(password.clone()), priority, - connect, ) .await { Err(e) => { tracing::info!("Failed to add new WiFi network '{}': {}", ssid, e); - tracing::debug!("{:?}", e); } Ok(_) => {} } @@ -101,31 +98,38 @@ pub async fn connect(#[context] ctx: RpcContext, #[arg] ssid: String) -> Result< )); } async fn connect_procedure( + mut db: impl DbHandle, wifi_manager: Arc>, - ssid: &String, + ssid: &Ssid, ) -> Result<(), Error> { let wpa_supplicant = wifi_manager.read().await; let current = wpa_supplicant.get_current_network().await?; drop(wpa_supplicant); let mut wpa_supplicant = wifi_manager.write().await; - let connected = wpa_supplicant.select_network(&ssid).await?; + let connected = wpa_supplicant.select_network(&mut db, &ssid).await?; if connected { - tracing::info!("Successfully connected to WiFi: '{}'", ssid); + tracing::info!("Successfully connected to WiFi: '{}'", ssid.0); } else { - tracing::info!("Failed to connect to WiFi: '{}'", ssid); + tracing::info!("Failed to connect to WiFi: '{}'", ssid.0); match current { None => { tracing::info!("No WiFi to revert to!"); } Some(current) => { - wpa_supplicant.select_network(¤t).await?; + wpa_supplicant.select_network(&mut db, ¤t).await?; } } } Ok(()) } tokio::spawn(async move { - match connect_procedure(ctx.wifi_manager.clone(), &ssid).await { + match connect_procedure( + &mut ctx.db.handle(), + ctx.wifi_manager.clone(), + &Ssid(ssid.clone()), + ) + .await + { Err(e) => { tracing::info!("Failed to connect to WiFi network '{}': {}", &ssid, e); } @@ -148,31 +152,42 @@ pub async fn delete(#[context] ctx: RpcContext, #[arg] ssid: String) -> Result<( let current = wpa_supplicant.get_current_network().await?; drop(wpa_supplicant); let mut wpa_supplicant = ctx.wifi_manager.write().await; - match current { - None => { - wpa_supplicant.remove_network(&ssid).await?; - } - Some(current) => { - if current == ssid { - if interface_connected("eth0").await? { - wpa_supplicant.remove_network(&ssid).await?; - } else { - return Err(Error::new(color_eyre::eyre::eyre!("Forbidden: Deleting this Network would make your Embassy Unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this."), ErrorKind::Wifi)); - } - } - } + let ssid = Ssid(ssid); + let is_current_being_removed = matches!(current, Some(current) if current == ssid); + let is_current_removed_and_no_hardwire = + is_current_being_removed && !interface_connected("eth0").await?; + if is_current_removed_and_no_hardwire { + return Err(Error::new(color_eyre::eyre::eyre!("Forbidden: Deleting this Network would make your Embassy Unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this."), ErrorKind::Wifi)); } + + wpa_supplicant + .remove_network(&mut ctx.db.handle(), &ssid) + .await?; Ok(()) } #[derive(serde::Serialize, serde::Deserialize)] #[serde(rename_all = "kebab-case")] pub struct WiFiInfo { - ssids: Vec, - connected: Option, + ssids: HashMap, + connected: Option, country: CountryCode, ethernet: bool, - signal_strength: Option, + available_wifi: Vec, } +#[derive(serde::Serialize, serde::Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] +pub struct WifiListInfo { + strength: SignalStrength, + security: Vec, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct WifiListOut { + ssid: Ssid, + strength: SignalStrength, + security: Vec, +} +pub type WifiList = HashMap; fn display_wifi_info(info: WiFiInfo, matches: &ArgMatches<'_>) { use prettytable::*; @@ -191,34 +206,74 @@ fn display_wifi_info(info: WiFiInfo, matches: &ArgMatches<'_>) { &info .connected .as_ref() - .map_or("[N/A]".to_owned(), |c| format!("{}", c)), + .map_or("[N/A]".to_owned(), |c| format!("{}", c.0)), &info - .signal_strength + .connected .as_ref() - .map_or("[N/A]".to_owned(), |ss| format!("{}", ss)), + .and_then(|x| info.ssids.get(x)) + .map_or("[N/A]".to_owned(), |ss| format!("{}", ss.0)), &format!("{}", info.country.alpha2()), &format!("{}", info.ethernet) ]); table_global.print_tty(false); let mut table_ssids = Table::new(); - table_ssids.add_row(row![bc => "SSID",]); - for ssid in &info.ssids { - let mut row = row![ssid]; - if Some(ssid) == info.connected.as_ref() { - row.iter_mut() - .map(|c| { - c.style(Attr::ForegroundColor(match &info.signal_strength { - Some(100) => color::GREEN, - Some(0) => color::RED, - _ => color::YELLOW, - })) - }) - .collect::<()>() - } + table_ssids.add_row(row![bc => "SSID", "STRENGTH"]); + for (ssid, signal_strength) in &info.ssids { + let mut row = row![&ssid.0, format!("{}", signal_strength.0)]; + row.iter_mut() + .map(|c| { + c.style(Attr::ForegroundColor(match &signal_strength.0 { + x if x >= &90 => color::GREEN, + x if x == &50 => color::MAGENTA, + x if x == &0 => color::RED, + _ => color::YELLOW, + })) + }) + .for_each(drop); table_ssids.add_row(row); } table_ssids.print_tty(false); + + let mut table_global = Table::new(); + table_global.add_row(row![bc => + "SSID", + "STRENGTH", + "SECURITY", + ]); + for table_info in info.available_wifi { + table_global.add_row(row![ + &table_info.ssid.0, + &format!("{}", table_info.strength.0), + &format!("{}", table_info.security.join(" ")) + ]); + } + + table_global.print_tty(false); +} + +fn display_wifi_list(info: Vec, matches: &ArgMatches<'_>) { + use prettytable::*; + + if matches.is_present("format") { + return display_serializable(info, matches); + } + + let mut table_global = Table::new(); + table_global.add_row(row![bc => + "SSID", + "STRENGTH", + "SECURITY", + ]); + for table_info in info { + table_global.add_row(row![ + &table_info.ssid.0, + &format!("{}", table_info.strength.0), + &format!("{}", table_info.security.join(" ")) + ]); + } + + table_global.print_tty(false); } #[command(display(display_wifi_info))] @@ -230,261 +285,327 @@ pub async fn get( format: Option, ) -> Result { let wpa_supplicant = ctx.wifi_manager.read().await; - let ssids_task = async { - Result::, Error>::Ok( - wpa_supplicant - .list_networks_low() - .await? - .into_keys() - .collect::>(), - ) - }; - let current_task = wpa_supplicant.get_current_network(); - let country_task = wpa_supplicant.get_country_low(); - let ethernet_task = interface_connected("eth0"); // TODO: pull from config - let rssi_task = wpa_supplicant.signal_poll_low(); - let (ssids_res, current_res, country_res, ethernet_res, rssi_res) = tokio::join!( - ssids_task, - current_task, - country_task, - ethernet_task, - rssi_task + let (list_networks, current_res, country_res, ethernet_res, signal_strengths) = tokio::join!( + wpa_supplicant.list_networks_low(), + wpa_supplicant.get_current_network(), + wpa_supplicant.get_country_low(), + interface_connected("eth0"), // TODO: pull from config + wpa_supplicant.list_wifi_low() ); - let current = current_res?; - let signal_strength = match rssi_res? { - None => None, - Some(x) if x <= -100 => Some(0 as usize), - Some(x) if x >= -50 => Some(100 as usize), - Some(x) => Some(2 * (x + 100) as usize), + let signal_strengths = signal_strengths?; + let list_networks = list_networks?; + let available_wifi = { + let mut wifi_list: Vec = signal_strengths + .clone() + .into_iter() + .filter(|(ssid, _)| !list_networks.contains_key(ssid)) + .map(|(ssid, info)| WifiListOut { + ssid, + strength: info.strength, + security: info.security, + }) + .collect(); + wifi_list.sort_by_key(|x| x.strength); + wifi_list.reverse(); + wifi_list }; + let ssids: HashMap = list_networks + .into_keys() + .map(|x| { + let signal_strength = signal_strengths + .get(&x) + .map(|x| x.strength) + .unwrap_or_default(); + (x, signal_strength) + }) + .collect(); + let current = current_res?; Ok(WiFiInfo { - ssids: ssids_res?, - connected: current, + ssids, + connected: current.map(|x| x), country: country_res?, ethernet: ethernet_res?, - signal_strength, + available_wifi, }) } -#[command(display(display_none))] +#[command(rename = "get", display(display_wifi_list))] #[instrument(skip(ctx))] +pub async fn get_available( + #[context] ctx: RpcContext, + #[allow(unused_variables)] + #[arg(long = "format")] + format: Option, +) -> Result, Error> { + let wpa_supplicant = ctx.wifi_manager.read().await; + let (wifi_list, network_list) = tokio::join!( + wpa_supplicant.list_wifi_low(), + wpa_supplicant.list_networks_low() + ); + let network_list = network_list?; + let mut wifi_list: Vec = wifi_list? + .into_iter() + .filter(|(ssid, _)| !network_list.contains_key(ssid)) + .map(|(ssid, info)| WifiListOut { + ssid, + strength: info.strength, + security: info.security, + }) + .collect(); + wifi_list.sort_by_key(|x| x.strength); + wifi_list.reverse(); + Ok(wifi_list) +} + +#[command(rename = "set", display(display_none))] pub async fn set_country( #[context] ctx: RpcContext, #[arg(parse(country_code_parse))] country: CountryCode, ) -> Result<(), Error> { + if !interface_connected("eth0").await? { + return Err(Error::new( + color_eyre::eyre::eyre!("Won't change country without hardwire connection"), + crate::ErrorKind::Wifi, + )); + } let mut wpa_supplicant = ctx.wifi_manager.write().await; - wpa_supplicant.set_country_low(country.alpha2()).await + wpa_supplicant.set_country_low(country.alpha2()).await?; + for (_ssid, network_id) in wpa_supplicant.list_networks_low().await? { + wpa_supplicant.remove_network_low(network_id).await?; + } + wpa_supplicant.remove_all_connections().await?; + + wpa_supplicant.save_config(&mut ctx.db.handle()).await?; + + Ok(()) } #[derive(Debug)] pub struct WpaCli { - datadir: PathBuf, interface: String, } -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct NetworkId(String); -pub enum NetworkAttr { - Ssid(String), - Psk(String), - Priority(isize), - ScanSsid(bool), -} -impl NetworkAttr { - fn name(&self) -> &'static str { - use NetworkAttr::*; - match self { - Ssid(_) => "ssid", - Psk(_) => "psk", - Priority(_) => "priority", - ScanSsid(_) => "scan_ssid", - } - } - fn value(&self) -> String { - use NetworkAttr::*; - match self { - Ssid(s) => format!("\"{}\"", s), - Psk(s) => format!("\"{}\"", s), - Priority(n) => format!("{}", n), - ScanSsid(b) => { - if *b { - String::from("1") - } else { - String::from("0") - } - } + +/// Ssid are the names of the wifis, usually human readable. +#[derive( + Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, +)] +pub struct Ssid(String); + +/// So a signal strength is a number between 0-100, I want the null option to be 0 since there is no signal +#[derive( + Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, +)] +pub struct SignalStrength(u8); + +impl SignalStrength { + fn new(size: Option) -> Self { + let size = match size { + None => return Self(0), + Some(x) => x, + }; + if size >= 100 { + return Self(100); } + Self(size) } } + +impl Default for SignalStrength { + fn default() -> Self { + Self(0) + } +} +#[derive(Clone, Debug)] +pub struct Psk(String); impl WpaCli { - pub fn init(interface: String, datadir: PathBuf) -> Self { - WpaCli { interface, datadir } + pub fn init(interface: String) -> Self { + WpaCli { interface } } - // Low Level - pub async fn add_network_low(&mut self) -> Result { - let r = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("add_network") + #[instrument(skip(self, psk))] + pub async fn set_add_network_low(&mut self, ssid: &Ssid, psk: &Psk) -> Result<(), Error> { + let _ = Command::new("nmcli") + .arg("-a") + .arg("-w") + .arg("30") + .arg("d") + .arg("wifi") + .arg("con") + .arg(&ssid.0) + .arg("password") + .arg(&psk.0) .invoke(ErrorKind::Wifi) .await?; - let s = std::str::from_utf8(&r)?; - Ok(NetworkId(s.trim().to_owned())) + Ok(()) } - pub async fn set_network_low( - &mut self, - id: &NetworkId, - attr: &NetworkAttr, - ) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") + #[instrument(skip(self, psk))] + pub async fn add_network_low(&mut self, ssid: &Ssid, psk: &Psk) -> Result<(), Error> { + let _ = Command::new("nmcli") + .arg("con") + .arg("add") + .arg("con-name") + .arg(&ssid.0) + .arg("ifname") .arg(&self.interface) - .arg("set_network") - .arg(&id.0) - .arg(attr.name()) - .arg(attr.value()) + .arg("type") + .arg("wifi") + .arg("ssid") + .arg(&ssid.0) + .invoke(ErrorKind::Wifi) + .await?; + let _ = Command::new("nmcli") + .arg("con") + .arg("modify") + .arg(&ssid.0) + .arg("wifi-sec.key-mgmt") + .arg("wpa-psk") + .invoke(ErrorKind::Wifi) + .await?; + let _ = Command::new("nmcli") + .arg("con") + .arg("modify") + .arg(&ssid.0) + .arg("wifi-sec.psk") + .arg(&psk.0) .invoke(ErrorKind::Wifi) .await?; Ok(()) } pub async fn set_country_low(&mut self, country_code: &str) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) + let _ = Command::new("iw") + .arg("reg") .arg("set") - .arg("country") .arg(country_code) .invoke(ErrorKind::Wifi) .await?; + Ok(()) } pub async fn get_country_low(&self) -> Result { - let r = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) + let r = Command::new("iw") + .arg("reg") .arg("get") - .arg("country") .invoke(ErrorKind::Wifi) .await?; - Ok(CountryCode::for_alpha2(&String::from_utf8(r)?).unwrap()) - } - pub async fn enable_network_low(&mut self, id: &NetworkId) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("enable_network") - .arg(&id.0) - .invoke(ErrorKind::Wifi) - .await?; - Ok(()) - } - pub async fn save_config_low(&mut self) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("save_config") - .invoke(ErrorKind::Wifi) - .await?; - Ok(()) + let r = String::from_utf8(r)?; + lazy_static! { + static ref RE: Regex = Regex::new("country (\\w+):").unwrap(); + } + let first_country = r + .lines() + .filter(|s| s.contains("country")) + .next() + .ok_or_else(|| { + Error::new( + color_eyre::eyre::eyre!("Could not find a country config lines"), + ErrorKind::Wifi, + ) + })?; + let country = &RE.captures(first_country).ok_or_else(|| { + Error::new( + color_eyre::eyre::eyre!("Could not find a country config with regex"), + ErrorKind::Wifi, + ) + })?[1]; + Ok(CountryCode::for_alpha2(country).or(Err(Error::new( + color_eyre::eyre::eyre!("Invalid Country Code: {}", country), + ErrorKind::Wifi, + )))?) } pub async fn remove_network_low(&mut self, id: NetworkId) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("remove_network") + let _ = Command::new("nmcli") + .arg("c") + .arg("del") .arg(&id.0) .invoke(ErrorKind::Wifi) .await?; Ok(()) } - pub async fn reconfigure_low(&mut self) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("reconfigure") - .invoke(ErrorKind::Wifi) - .await?; - Ok(()) - } #[instrument] - pub async fn list_networks_low(&self) -> Result, Error> { - let r = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("list_networks") + pub async fn list_networks_low(&self) -> Result, Error> { + let r = Command::new("nmcli") + .arg("-t") + .arg("c") + .arg("show") .invoke(ErrorKind::Wifi) .await?; Ok(String::from_utf8(r)? .lines() - .skip(1) .filter_map(|l| { - let mut cs = l.split("\t"); - let nid = NetworkId(cs.next()?.to_owned()); - let ssid = cs.next()?.to_owned(); - Some((ssid, nid)) + let mut cs = l.split(":"); + let name = Ssid(cs.next()?.to_owned()); + let uuid = NetworkId(cs.next()?.to_owned()); + let _connection_type = cs.next()?; + let _device = cs.next()?; + Some((name, uuid)) }) - .collect::>()) - } - pub async fn select_network_low(&mut self, id: &NetworkId) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("select_network") - .arg(&id.0) - .invoke(ErrorKind::Wifi) - .await?; - Ok(()) - } - pub async fn new_password_low(&mut self, id: &NetworkId, pass: &str) -> Result<(), Error> { - let _ = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("new_password") - .arg(&id.0) - .arg(pass) - .invoke(ErrorKind::Wifi) - .await?; - Ok(()) - } - #[instrument] - pub async fn signal_poll_low(&self) -> Result, Error> { - let r = Command::new("wpa_cli") - .arg("-i") - .arg(&self.interface) - .arg("signal_poll") - .invoke(ErrorKind::Wifi) - .await?; - let e = || { - Error::new( - color_eyre::eyre::eyre!("Invalid output from wpa_cli signal_poll"), - ErrorKind::Wifi, - ) - }; - let output = String::from_utf8(r)?; - Ok(if output.trim() == "FAIL" { - None - } else { - let l = output.lines().next().ok_or_else(e)?; - let rssi = l.split("=").nth(1).ok_or_else(e)?.parse()?; - Some(rssi) - }) + .collect::>()) } - // High Level - pub async fn save_config(&mut self) -> Result<(), Error> { - self.save_config_low().await?; - tokio::fs::copy( - "/etc/wpa_supplicant.conf", - self.datadir.join("wpa_supplicant.conf"), - ) - .await?; + #[instrument] + pub async fn list_wifi_low(&self) -> Result { + let r = Command::new("nmcli") + .arg("-g") + .arg("SSID,SIGNAL,security") + .arg("d") + .arg("wifi") + .arg("list") + .invoke(ErrorKind::Wifi) + .await?; + Ok(String::from_utf8(r)? + .lines() + .filter_map(|l| { + let mut values = l.split(":"); + let ssid = Ssid(values.next()?.to_owned()); + let signal = SignalStrength::new(std::str::FromStr::from_str(values.next()?).ok()); + let security: Vec = + values.next()?.split(" ").map(|x| x.to_owned()).collect(); + Some(( + ssid, + WifiListInfo { + strength: signal, + security, + }, + )) + }) + .collect::()) + } + pub async fn select_network_low(&mut self, id: &NetworkId) -> Result<(), Error> { + let _ = Command::new("nmcli") + .arg("c") + .arg("up") + .arg(&id.0) + .invoke(ErrorKind::Wifi) + .await?; Ok(()) } - pub async fn check_network(&self, ssid: &str) -> Result, Error> { + pub async fn remove_all_connections(&mut self) -> Result<(), Error> { + let location_connections = Path::new("/etc/NetworkManager/system-connections"); + let mut connections = tokio::fs::read_dir(&location_connections).await?; + while let Some(connection) = connections.next_entry().await? { + let path = connection.path(); + if path.is_file() { + let _ = tokio::fs::remove_file(&path).await?; + } + } + + Ok(()) + } + pub async fn save_config(&mut self, mut db: impl DbHandle) -> Result<(), Error> { + crate::db::DatabaseModel::new() + .server_info() + .last_wifi_region() + .put(&mut db, &Some(self.get_country_low().await?)) + .await?; + Ok(()) + } + pub async fn check_network(&self, ssid: &Ssid) -> Result, Error> { Ok(self.list_networks_low().await?.remove(ssid)) } - #[instrument] - pub async fn select_network(&mut self, ssid: &str) -> Result { + #[instrument(skip(db))] + pub async fn select_network(&mut self, db: impl DbHandle, ssid: &Ssid) -> Result { let m_id = self.check_network(ssid).await?; match m_id { None => Err(Error::new( @@ -493,14 +614,14 @@ impl WpaCli { )), Some(x) => { self.select_network_low(&x).await?; - self.save_config().await?; + self.save_config(db).await?; let connect = async { let mut current; loop { current = self.get_current_network().await; match ¤t { Ok(Some(ssid)) => { - tracing::debug!("Connected to: {}", ssid); + tracing::debug!("Connected to: {}", ssid.0); break; } _ => {} @@ -517,13 +638,13 @@ impl WpaCli { tracing::debug!("{:?}", res); Ok(match res { None => false, - Some(net) => net == ssid, + Some(net) => &net == ssid, }) } } } #[instrument] - pub async fn get_current_network(&self) -> Result, Error> { + pub async fn get_current_network(&self) -> Result, Error> { let r = Command::new("iwgetid") .arg(&self.interface) .arg("--raw") @@ -535,45 +656,42 @@ impl WpaCli { if network.is_empty() { Ok(None) } else { - Ok(Some(network.to_owned())) + Ok(Some(Ssid(network.to_owned()))) } } - #[instrument] - pub async fn remove_network(&mut self, ssid: &str) -> Result { + #[instrument(skip(db))] + pub async fn remove_network(&mut self, db: impl DbHandle, ssid: &Ssid) -> Result { match self.check_network(ssid).await? { None => Ok(false), Some(x) => { self.remove_network_low(x).await?; - self.save_config().await?; - self.reconfigure_low().await?; + self.save_config(db).await?; Ok(true) } } } - #[instrument] - pub async fn add_network( + #[instrument(skip(psk, db))] + pub async fn set_add_network( &mut self, - ssid: &str, - psk: &str, + db: impl DbHandle, + ssid: &Ssid, + psk: &Psk, priority: isize, ) -> Result<(), Error> { - use NetworkAttr::*; - let nid = match self.check_network(ssid).await? { - None => { - let nid = self.add_network_low().await?; - self.set_network_low(&nid, &Ssid(ssid.to_owned())).await?; - self.set_network_low(&nid, &Psk(psk.to_owned())).await?; - self.set_network_low(&nid, &Priority(priority)).await?; - self.set_network_low(&nid, &ScanSsid(true)).await?; - Result::::Ok(nid) - } - Some(nid) => { - self.new_password_low(&nid, psk).await?; - Ok(nid) - } - }?; - self.enable_network_low(&nid).await?; - self.save_config().await?; + self.set_add_network_low(&ssid, &psk).await?; + self.save_config(db).await?; + Ok(()) + } + #[instrument(skip(psk, db))] + pub async fn add_network( + &mut self, + db: impl DbHandle, + ssid: &Ssid, + psk: &Psk, + priority: isize, + ) -> Result<(), Error> { + self.add_network_low(&ssid, &psk).await?; + self.save_config(db).await?; Ok(()) } } @@ -599,21 +717,26 @@ pub fn country_code_parse(code: &str, _matches: &ArgMatches<'_>) -> Result>(main_datadir: P) -> Result<(), Error> { - let persistent = main_datadir.as_ref().join("wpa_supplicant.conf"); +pub async fn synchronize_wpa_supplicant_conf>( + main_datadir: P, + last_country_code: &Option, +) -> Result<(), Error> { + let persistent = main_datadir.as_ref().join("system-connections"); tracing::debug!("persistent: {:?}", persistent); + let supplicant = Path::new("/etc/wpa_supplicant.conf"); + if tokio::fs::metadata(&persistent).await.is_err() { - tokio::fs::write(&persistent, include_str!("wpa_supplicant.conf.base")).await?; + tokio::fs::create_dir_all(&persistent).await?; } - let volatile = Path::new("/etc/wpa_supplicant.conf"); - tracing::debug!("link: {:?}", volatile); - if tokio::fs::metadata(&volatile).await.is_ok() { - tokio::fs::remove_file(&volatile).await? + crate::disk::mount::util::bind(&persistent, "/etc/NetworkManager/system-connections", false) + .await?; + if tokio::fs::metadata(&supplicant).await.is_err() { + tokio::fs::write(&supplicant, include_str!("wpa_supplicant.conf.base")).await?; } - tokio::fs::copy(&persistent, volatile).await?; + Command::new("systemctl") .arg("restart") - .arg("wpa_supplicant") + .arg("NetworkManager") .invoke(ErrorKind::Wifi) .await?; Command::new("ifconfig") @@ -622,5 +745,14 @@ pub async fn synchronize_wpa_supplicant_conf>(main_datadir: P) -> .invoke(ErrorKind::Wifi) .await?; Command::new("dhclient").invoke(ErrorKind::Wifi).await?; + if let Some(last_country_code) = last_country_code { + tracing::info!("Setting the region"); + let _ = Command::new("iw") + .arg("reg") + .arg("set") + .arg(last_country_code.alpha2()) + .invoke(ErrorKind::Wifi) + .await?; + } Ok(()) } diff --git a/appmgr/src/nginx/main-ui.conf.template b/appmgr/src/nginx/main-ui.conf.template index 7fce08323..a7558be44 100644 --- a/appmgr/src/nginx/main-ui.conf.template +++ b/appmgr/src/nginx/main-ui.conf.template @@ -1,3 +1,5 @@ + + server {{ listen 443 ssl default_server; listen [::]:443 ssl default_server; @@ -19,7 +21,8 @@ server {{ gzip on; gzip_vary on; gzip_min_length 1024; - gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml; + gzip_types text/plain text/css text/xml text/javascript application/javascript image/svg+xml font/tts font/otf font/eot font/openttype application/x-javascript application/xml; + location /rpc/ {{ proxy_pass http://127.0.0.1:5959/; diff --git a/build/initialization.sh b/build/initialization.sh index aaf792eff..e231b4291 100755 --- a/build/initialization.sh +++ b/build/initialization.sh @@ -26,7 +26,8 @@ apt-get install -y \ ecryptfs-utils \ cifs-utils \ samba-common-bin \ - ntp + ntp \ + network-manager apt-get autoremove -y apt-get upgrade -y @@ -60,5 +61,6 @@ EOF passwd -l ubuntu echo 'overlayroot="tmpfs":swap=1,recurse=0' > /etc/overlayroot.local.conf systemctl disable initialization.service +sudo systemctl restart NetworkManager sync reboot diff --git a/ui/package-lock.json b/ui/package-lock.json index 1334e1be0..cb51e7c07 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -24,7 +24,6 @@ "dompurify": "^2.3.3", "fast-json-patch": "^3.1.0", "fuse.js": "^6.4.6", - "lint-staged": "^12.1.2", "marked": "3.0.2", "mustache": "^4.2.0", "ng-qrcode": "^5.0.0", @@ -46,6 +45,7 @@ "@types/node": "^16.7.13", "@types/uuid": "^8.3.1", "husky": "^4.3.8", + "lint-staged": "^12.1.2", "node-html-parser": "^4.1.4", "prettier": "^2.5.1", "raw-loader": "^4.0.2", @@ -3503,6 +3503,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -3596,6 +3597,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, "engines": { "node": ">=6" } @@ -3604,6 +3606,7 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -3630,6 +3633,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -3824,6 +3828,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, "engines": { "node": ">=8" } @@ -4235,6 +4240,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -4656,6 +4662,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, "engines": { "node": ">=6" } @@ -4664,6 +4671,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4687,6 +4695,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" @@ -4702,6 +4711,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -4712,12 +4722,14 @@ "node_modules/cli-truncate/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "engines": { "node": ">=12" }, @@ -4729,6 +4741,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.0.1.tgz", "integrity": "sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==", + "dev": true, "dependencies": { "emoji-regex": "^9.2.2", "is-fullwidth-code-point": "^4.0.0", @@ -4745,6 +4758,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -5843,6 +5857,7 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -6197,7 +6212,8 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/emojis-list": { "version": "3.0.0", @@ -6266,6 +6282,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, "dependencies": { "ansi-colors": "^4.1.1" }, @@ -7116,6 +7133,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -8036,6 +8054,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, "engines": { "node": ">=10.17.0" } @@ -8404,6 +8423,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, "engines": { "node": ">=8" } @@ -8754,6 +8774,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -8789,6 +8810,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -8922,7 +8944,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", @@ -9283,6 +9306,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true, "engines": { "node": ">=10" } @@ -9297,6 +9321,7 @@ "version": "12.1.2", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.1.2.tgz", "integrity": "sha512-bSMcQVqMW98HLLLR2c2tZ+vnDCnx4fd+0QJBQgN/4XkdspGRPc8DGp7UuOEBe1ApCfJ+wXXumYnJmU+wDo7j9A==", + "dev": true, "dependencies": { "cli-truncate": "^3.1.0", "colorette": "^2.0.16", @@ -9326,12 +9351,14 @@ "node_modules/lint-staged/node_modules/colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true }, "node_modules/lint-staged/node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, "engines": { "node": ">= 12" } @@ -9340,6 +9367,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -9353,6 +9381,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -9375,6 +9404,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, "engines": { "node": ">=10" }, @@ -9386,6 +9416,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, "engines": { "node": ">=8" }, @@ -9397,6 +9428,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, "dependencies": { "path-key": "^3.0.0" }, @@ -9408,6 +9440,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -9416,6 +9449,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -9427,6 +9461,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -9435,6 +9470,7 @@ "version": "9.2.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz", "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==", + "dev": true, "engines": { "node": ">=12" }, @@ -9446,6 +9482,7 @@ "version": "3.13.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.13.5.tgz", "integrity": "sha512-3n8heFQDSk+NcwBn3CgxEibZGaRzx+pC64n3YjpMD1qguV4nWus3Al+Oo3KooqFKTQEJ1v7MmnbnyyNspgx3NA==", + "dev": true, "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", @@ -9472,6 +9509,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -9486,6 +9524,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -9501,6 +9540,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -9511,17 +9551,20 @@ "node_modules/listr2/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/listr2/node_modules/colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true }, "node_modules/listr2/node_modules/rxjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "dev": true, "dependencies": { "tslib": "~2.1.0" } @@ -9530,6 +9573,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -9542,7 +9586,8 @@ "node_modules/listr2/node_modules/tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true }, "node_modules/loader-runner": { "version": "4.2.0", @@ -9701,6 +9746,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", @@ -9718,6 +9764,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -9732,6 +9779,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -9742,12 +9790,14 @@ "node_modules/log-update/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -9764,6 +9814,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10027,7 +10078,8 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "node_modules/merge2": { "version": "1.4.1", @@ -10051,6 +10103,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, "dependencies": { "braces": "^3.0.1", "picomatch": "^2.2.3" @@ -10096,6 +10149,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "engines": { "node": ">=6" } @@ -10289,7 +10343,8 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/multicast-dns": { "version": "6.2.3", @@ -10624,6 +10679,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10892,6 +10948,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11003,6 +11060,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -11235,6 +11293,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, "dependencies": { "aggregate-error": "^3.0.0" }, @@ -11496,6 +11555,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -14390,6 +14450,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -14429,7 +14490,8 @@ "node_modules/rfdc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true }, "node_modules/rimraf": { "version": "3.0.2", @@ -14848,7 +14910,8 @@ "node_modules/signal-exit": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true }, "node_modules/slash": { "version": "3.0.0", @@ -14863,6 +14926,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" @@ -14878,6 +14942,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true, "engines": { "node": ">=12" }, @@ -14889,6 +14954,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "engines": { "node": ">=12" }, @@ -15505,6 +15571,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, "engines": { "node": ">=0.6.19" } @@ -15513,6 +15580,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -15526,6 +15594,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -15546,6 +15615,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, "engines": { "node": ">=6" } @@ -15896,7 +15966,8 @@ "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true }, "node_modules/thunky": { "version": "1.1.0", @@ -15974,6 +16045,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -16156,6 +16228,7 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, "engines": { "node": ">=10" }, @@ -17395,6 +17468,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -17435,6 +17509,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -17451,6 +17526,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -17465,6 +17541,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -17475,7 +17552,8 @@ "node_modules/wrap-ansi/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/wrappy": { "version": "1.0.2", @@ -17523,6 +17601,7 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, "engines": { "node": ">= 6" } @@ -20119,6 +20198,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -20190,12 +20270,14 @@ "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, "requires": { "type-fest": "^0.21.3" } @@ -20209,7 +20291,8 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "ansi-styles": { "version": "3.2.1", @@ -20366,7 +20449,8 @@ "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true }, "async": { "version": "2.6.3", @@ -20699,6 +20783,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -21029,12 +21114,14 @@ "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, "requires": { "restore-cursor": "^3.1.0" } @@ -21049,6 +21136,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, "requires": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" @@ -21057,22 +21145,26 @@ "ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "is-fullwidth-code-point": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==" + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true }, "string-width": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.0.1.tgz", "integrity": "sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==", + "dev": true, "requires": { "emoji-regex": "^9.2.2", "is-fullwidth-code-point": "^4.0.0", @@ -21083,6 +21175,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, "requires": { "ansi-regex": "^6.0.1" } @@ -21906,6 +21999,7 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -22201,7 +22295,8 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "emojis-list": { "version": "3.0.0", @@ -22260,6 +22355,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, "requires": { "ansi-colors": "^4.1.1" } @@ -22889,6 +22985,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -23626,7 +23723,8 @@ "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true }, "humanize-ms": { "version": "1.2.1", @@ -23874,7 +23972,8 @@ "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true }, "indexes-of": { "version": "1.0.1", @@ -24142,7 +24241,8 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, "is-glob": { "version": "4.0.3", @@ -24168,7 +24268,8 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-path-cwd": { "version": "2.2.0", @@ -24266,7 +24367,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "3.0.1", @@ -24545,7 +24647,8 @@ "lilconfig": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==" + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true }, "lines-and-columns": { "version": "1.1.6", @@ -24557,6 +24660,7 @@ "version": "12.1.2", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.1.2.tgz", "integrity": "sha512-bSMcQVqMW98HLLLR2c2tZ+vnDCnx4fd+0QJBQgN/4XkdspGRPc8DGp7UuOEBe1ApCfJ+wXXumYnJmU+wDo7j9A==", + "dev": true, "requires": { "cli-truncate": "^3.1.0", "colorette": "^2.0.16", @@ -24577,17 +24681,20 @@ "colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true }, "commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -24598,6 +24705,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, "requires": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -24613,17 +24721,20 @@ "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, "requires": { "path-key": "^3.0.0" } @@ -24631,12 +24742,14 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -24644,12 +24757,14 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true }, "supports-color": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz", - "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==" + "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==", + "dev": true } } }, @@ -24657,6 +24772,7 @@ "version": "3.13.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.13.5.tgz", "integrity": "sha512-3n8heFQDSk+NcwBn3CgxEibZGaRzx+pC64n3YjpMD1qguV4nWus3Al+Oo3KooqFKTQEJ1v7MmnbnyyNspgx3NA==", + "dev": true, "requires": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", @@ -24672,6 +24788,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -24680,6 +24797,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, "requires": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -24689,6 +24807,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -24696,17 +24815,20 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true }, "rxjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "dev": true, "requires": { "tslib": "~2.1.0" } @@ -24715,6 +24837,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, "requires": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -24724,7 +24847,8 @@ "tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true } } }, @@ -24851,6 +24975,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, "requires": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", @@ -24862,6 +24987,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -24870,6 +24996,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -24877,12 +25004,14 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, "requires": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -24893,6 +25022,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -25108,7 +25238,8 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "merge2": { "version": "1.4.1", @@ -25126,6 +25257,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, "requires": { "braces": "^3.0.1", "picomatch": "^2.2.3" @@ -25155,7 +25287,8 @@ "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true }, "mini-css-extract-plugin": { "version": "2.4.2", @@ -25296,7 +25429,8 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "multicast-dns": { "version": "6.2.3", @@ -25552,7 +25686,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "normalize-range": { "version": "0.1.2", @@ -25764,7 +25899,8 @@ "object-inspect": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==" + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", + "dev": true }, "object-is": { "version": "1.1.5", @@ -25846,6 +25982,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "requires": { "mimic-fn": "^2.1.0" } @@ -26012,6 +26149,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, "requires": { "aggregate-error": "^3.0.0" } @@ -26239,7 +26377,8 @@ "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true }, "pify": { "version": "2.3.0", @@ -28382,6 +28521,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, "requires": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -28408,7 +28548,8 @@ "rfdc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true }, "rimraf": { "version": "3.0.2", @@ -28732,7 +28873,8 @@ "signal-exit": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true }, "slash": { "version": "3.0.0", @@ -28744,6 +28886,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, "requires": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" @@ -28752,12 +28895,14 @@ "ansi-styles": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==" + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true }, "is-fullwidth-code-point": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==" + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true } } }, @@ -29255,12 +29400,14 @@ "string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==" + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -29271,6 +29418,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -29284,7 +29432,8 @@ "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true }, "style-loader": { "version": "3.2.1", @@ -29531,7 +29680,8 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true }, "thunky": { "version": "1.1.0", @@ -29596,6 +29746,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "requires": { "is-number": "^7.0.0" } @@ -29724,7 +29875,8 @@ "type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true }, "type-is": { "version": "1.6.18", @@ -30704,6 +30856,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -30738,6 +30891,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -30748,6 +30902,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -30756,6 +30911,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -30763,7 +30919,8 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true } } }, @@ -30795,7 +30952,8 @@ "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true }, "yargs": { "version": "17.2.1", diff --git a/ui/src/app/pages/server-routes/wifi/wifi.page.html b/ui/src/app/pages/server-routes/wifi/wifi.page.html index 4be59341a..0d2314a2f 100644 --- a/ui/src/app/pages/server-routes/wifi/wifi.page.html +++ b/ui/src/app/pages/server-routes/wifi/wifi.page.html @@ -4,6 +4,12 @@ WiFi Settings + + + Refresh + + + @@ -40,25 +46,43 @@ + + Available Networks + + + + + + + + Saved Networks + +
+ + {{ ssid.key }} + + + + +
+ + Available Networks + + + {{ avWifi.ssid }} + + + + + - Add new network - - -
- - {{ ssid }} - - - - - - + Other
diff --git a/ui/src/app/pages/server-routes/wifi/wifi.page.ts b/ui/src/app/pages/server-routes/wifi/wifi.page.ts index 5674ffeb7..e563346f0 100644 --- a/ui/src/app/pages/server-routes/wifi/wifi.page.ts +++ b/ui/src/app/pages/server-routes/wifi/wifi.page.ts @@ -8,6 +8,7 @@ import { ValueSpecObject } from 'src/app/pkg-config/config-types' import { RR } from 'src/app/services/api/api.types' import { pauseFor } from 'src/app/util/misc.util' import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' +import { ConfigService } from 'src/app/services/config.service' @Component({ selector: 'wifi', @@ -27,6 +28,7 @@ export class WifiPage { private readonly modalCtrl: ModalController, private readonly errToast: ErrorToastService, private readonly actionCtrl: ActionSheetController, + private readonly config: ConfigService, ) { } async ngOnInit () { @@ -47,6 +49,23 @@ export class WifiPage { } async presentAlertCountry (): Promise { + if (!this.config.isLan) { + const alert = await this.alertCtrl.create({ + header: 'Cannot Complete Action', + message: 'You must be connected to your Emassy via LAN to change the country.', + buttons: [ + { + text: 'OK', + role: 'cancel', + }, + + ], + cssClass: 'wide-alert enter-click', + }) + await alert.present() + return + } + const inputs: AlertInput[] = Object.entries(this.countries).map(([country, fullName]) => { return { name: fullName, @@ -59,6 +78,7 @@ export class WifiPage { const alert = await this.alertCtrl.create({ header: 'Select Country', + message: 'Warning: Changing the country will delete all saved networks from the Embassy.', inputs, buttons: [ { @@ -77,7 +97,8 @@ export class WifiPage { await alert.present() } - async presentModalAdd () { + async presentModalAdd (ssid?: string, needsPW: boolean = true) { + const wifiSpec = getWifiValueSpec(ssid, needsPW) const modal = await this.modalCtrl.create({ component: GenericFormPage, componentProps: { @@ -104,14 +125,14 @@ export class WifiPage { await modal.present() } - async presentAction (ssid: string, i: number) { + async presentAction (ssid: string) { const buttons: ActionSheetButton[] = [ { text: 'Forget', icon: 'trash', role: 'destructive', handler: () => { - this.delete(ssid, i) + this.delete(ssid) }, }, ] @@ -147,6 +168,7 @@ export class WifiPage { try { await this.api.setWifiCountry({ country }) + await this.getWifi(4000) this.wifi.country = country } catch (e) { this.errToast.present(e) @@ -164,7 +186,7 @@ export class WifiPage { if (attempts > maxAttempts) { this.presentToastFail() if (deleteOnFailure) { - this.wifi.ssids = this.wifi.ssids.filter(s => s !== ssid) + delete this.wifi.ssids[ssid] } break } @@ -243,7 +265,7 @@ export class WifiPage { } } - private async delete (ssid: string, i: number): Promise { + private async delete (ssid: string): Promise { const loader = await this.loadingCtrl.create({ spinner: 'lines', message: 'Deleting...', @@ -253,7 +275,8 @@ export class WifiPage { try { await this.api.deleteWifi({ ssid }) - this.wifi.ssids = this.wifi.ssids.filter((w, index) => index !== i) + await this.getWifi(4000) + delete this.wifi.ssids[ssid] } catch (e) { this.errToast.present(e) } finally { @@ -276,7 +299,7 @@ export class WifiPage { priority: 0, connect: false, }) - await this.getWifi() + await this.getWifi(4000) } catch (e) { this.errToast.present(e) } finally { @@ -310,25 +333,29 @@ export class WifiPage { } } -const wifiSpec: ValueSpecObject = { - type: 'object', - name: 'WiFi Credentials', - description: 'Enter the network SSID and password. You can connect now or save the network for later.', - 'unique-by': null, - spec: { - ssid: { - type: 'string', - name: 'Network SSID', - nullable: false, - masked: false, - copyable: false, +function getWifiValueSpec (ssid?: string, needsPW: boolean = true): ValueSpecObject { + return { + type: 'object', + name: 'WiFi Credentials', + description: 'Enter the network SSID and password. You can connect now or save the network for later.', + 'unique-by': null, + spec: { + ssid: { + type: 'string', + name: 'Network SSID', + nullable: false, + masked: false, + copyable: false, + default: ssid, + }, + password: { + type: 'string', + name: 'Password', + nullable: !needsPW, + masked: true, + copyable: false, + }, }, - password: { - type: 'string', - name: 'Password', - nullable: false, - masked: true, - copyable: false, - }, - }, + } } + diff --git a/ui/src/app/services/api/api.fixures.ts b/ui/src/app/services/api/api.fixures.ts index 76bb78373..7d1e9d170 100644 --- a/ui/src/app/services/api/api.fixures.ts +++ b/ui/src/app/services/api/api.fixures.ts @@ -983,10 +983,24 @@ export module Mock { export const Wifi: RR.GetWifiRes = { ethernet: true, - ssids: ['Goosers', 'Goosers5G'], + ssids: { + 'Goosers': 50, + 'Goosers5G': 0, + }, connected: 'Goosers', country: 'US', - 'signal-strength': 50, + 'available-wifi': [ + { + ssid: 'Goosers a billion', + strength: 40, + security: [], + }, + { + ssid: 'Bill nye the wifi guy', + strength: 99, + security: ['1', '2', '3'], + }, + ], } export const BackupTargets: RR.GetBackupTargetsRes = { diff --git a/ui/src/app/services/api/api.types.ts b/ui/src/app/services/api/api.types.ts index c8ffb1d79..90979e095 100644 --- a/ui/src/app/services/api/api.types.ts +++ b/ui/src/app/services/api/api.types.ts @@ -83,13 +83,15 @@ export module RR { export type SetWifiCountryRes = null export type GetWifiReq = { } - export type GetWifiRes = { // wifi.get - ethernet: boolean - ssids: string[] - connected: string | null - country: string | null - 'signal-strength': number - } + export type GetWifiRes = { + ssids: { + [ssid: string]: number + }, + connected?: string, + country: string, + ethernet: boolean, + 'available-wifi': AvailableWifi[] +} export type AddWifiReq = { // wifi.add ssid: string @@ -442,3 +444,9 @@ export interface BackupReport { } } } + +export interface AvailableWifi { + ssid: string + strength: number + security: string [] +} \ No newline at end of file