diff --git a/Makefile b/Makefile index a00a5c1c9..2f3e17bdb 100644 --- a/Makefile +++ b/Makefile @@ -81,6 +81,8 @@ clean: rm -rf container-runtime/dist rm -rf container-runtime/node_modules rm -f container-runtime/*.squashfs + if [ -d container-runtime/tmp/combined ] && mountpoint container-runtime/tmp/combined; then sudo umount container-runtime/tmp/combined; fi + if [ -d container-runtime/tmp/lower ] && mountpoint container-runtime/tmp/lower; then sudo umount container-runtime/tmp/lower; fi rm -rf container-runtime/tmp (cd sdk && make clean) rm -f ENVIRONMENT.txt diff --git a/build/lib/scripts/enable-kiosk b/build/lib/scripts/enable-kiosk index cd48fc032..548542d23 100755 --- a/build/lib/scripts/enable-kiosk +++ b/build/lib/scripts/enable-kiosk @@ -101,7 +101,9 @@ done killall firefox-esr ) & matchbox-window-manager -use_titlebar no & -firefox-esr http://localhost --profile /home/kiosk/fx-profile +cp -r /home/kiosk/fx-profile /home/kiosk/fx-profile-tmp +firefox-esr http://localhost --profile /home/kiosk/fx-profile-tmp +rm -rf /home/kiosk/fx-profile-tmp EOF chmod +x /home/kiosk/kiosk.sh diff --git a/container-runtime/update-image.sh b/container-runtime/update-image.sh index 61429821c..a64b4371e 100755 --- a/container-runtime/update-image.sh +++ b/container-runtime/update-image.sh @@ -18,6 +18,13 @@ sudo mount -t overlay -olowerdir=tmp/lower,upperdir=tmp/upper,workdir=tmp/work o QEMU= if [ "$ARCH" != "$(uname -m)" ]; then QEMU=/usr/bin/qemu-${ARCH}-static + if ! which qemu-$ARCH-static > /dev/null; then + >&2 echo qemu-user-static is required for cross-platform builds + sudo umount tmp/combined + sudo umount tmp/lower + sudo rm -rf tmp + exit 1 + fi sudo cp $(which qemu-$ARCH-static) tmp/combined${QEMU} fi diff --git a/core/Cargo.lock b/core/Cargo.lock index 3810d0d8d..a1e796a15 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -5933,7 +5933,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "start-os" -version = "0.3.6-alpha.12" +version = "0.3.6-alpha.13" dependencies = [ "aes 0.7.5", "async-acme", diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index 909389c12..df1622851 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -14,7 +14,7 @@ keywords = [ name = "start-os" readme = "README.md" repository = "https://github.com/Start9Labs/start-os" -version = "0.3.6-alpha.12" +version = "0.3.6-alpha.13" # VERSION_BUMP license = "MIT" [lib] diff --git a/core/startos/src/bins/startd.rs b/core/startos/src/bins/startd.rs index 2bf32724e..b7574b7c7 100644 --- a/core/startos/src/bins/startd.rs +++ b/core/startos/src/bins/startd.rs @@ -12,6 +12,7 @@ use tracing::instrument; use crate::context::config::ServerConfig; use crate::context::rpc::InitRpcContextPhases; use crate::context::{DiagnosticContext, InitContext, RpcContext}; +use crate::net::network_interface::SelfContainedNetworkInterfaceListener; use crate::net::utils::ipv6_is_local; use crate::net::web_server::{Acceptor, UpgradableListener, WebServer}; use crate::shutdown::Shutdown; @@ -150,17 +151,9 @@ pub fn main(args: impl IntoIterator) { .expect("failed to initialize runtime"); rt.block_on(async { let addrs = crate::net::utils::all_socket_addrs_for(80).await?; - let mut server = WebServer::new( - Acceptor::bind_upgradable(addrs.into_iter().filter(|addr| match addr.ip() { - IpAddr::V4(ip4) => { - ip4.is_loopback() - || (ip4.is_private() && !ip4.octets().starts_with(&[10, 59])) // reserving 10.59 for public wireguard configurations - || ip4.is_link_local() - } - IpAddr::V6(ip6) => ipv6_is_local(ip6), - })) - .await?, - ); + let mut server = WebServer::new(Acceptor::bind_upgradable( + SelfContainedNetworkInterfaceListener::bind(80), + )); match inner_main(&mut server, &config).await { Ok(a) => { server.shutdown().await; diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index 1eca857f4..dfaa02b01 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -85,7 +85,7 @@ pub struct InitRpcContextPhases { load_db: PhaseProgressTrackerHandle, init_net_ctrl: PhaseProgressTrackerHandle, cleanup_init: CleanupInitPhases, - // TODO: migrations + run_migrations: PhaseProgressTrackerHandle, } impl InitRpcContextPhases { pub fn new(handle: &FullProgressTracker) -> Self { @@ -93,6 +93,7 @@ impl InitRpcContextPhases { load_db: handle.add_phase("Loading database".into(), Some(5)), init_net_ctrl: handle.add_phase("Initializing network".into(), Some(1)), cleanup_init: CleanupInitPhases::new(handle), + run_migrations: handle.add_phase("Running migrations".into(), Some(10)), } } } @@ -125,6 +126,7 @@ impl RpcContext { mut load_db, mut init_net_ctrl, cleanup_init, + run_migrations, }: InitRpcContextPhases, ) -> Result { let tor_proxy = config.tor_socks.unwrap_or(SocketAddr::V4(SocketAddrV4::new( @@ -276,7 +278,9 @@ impl RpcContext { let res = Self(seed.clone()); res.cleanup_and_initialize(cleanup_init).await?; tracing::info!("Cleaned up transient states"); - crate::version::post_init(&res).await?; + + crate::version::post_init(&res, run_migrations).await?; + tracing::info!("Completed migrations"); Ok(res) } @@ -286,7 +290,6 @@ impl RpcContext { self.services.shutdown_all().await?; self.is_closed.store(true, Ordering::SeqCst); tracing::info!("RPC Context is shutdown"); - // TODO: shutdown http servers Ok(()) } diff --git a/core/startos/src/db/model/public.rs b/core/startos/src/db/model/public.rs index bb92bbaf9..1f764e0c9 100644 --- a/core/startos/src/db/model/public.rs +++ b/core/startos/src/db/model/public.rs @@ -16,9 +16,9 @@ use ts_rs::TS; use crate::account::AccountInfo; use crate::db::model::package::AllPackageData; use crate::net::acme::AcmeProvider; -use crate::net::host::address::DomainConfig; use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, NetInfo}; use crate::net::host::Host; +use crate::net::utils::ipv6_is_local; use crate::net::vhost::AlpnInfo; use crate::prelude::*; use crate::progress::FullProgress; @@ -190,14 +190,29 @@ impl NetworkInterfaceInfo { pub fn public(&self) -> bool { self.public.unwrap_or_else(|| { !self.ip_info.as_ref().map_or(true, |ip_info| { - ip_info.subnets.iter().all(|ipnet| { - match ipnet.addr() { - IpAddr::V4(ip4) => { - ip4.is_loopback() - || (ip4.is_private() && !ip4.octets().starts_with(&[10, 59])) // reserving 10.59 for public wireguard configurations - || ip4.is_link_local() + let ip4s = ip_info + .subnets + .iter() + .filter_map(|ipnet| { + if let IpAddr::V4(ip4) = ipnet.addr() { + Some(ip4) + } else { + None } - IpAddr::V6(_) => true, + }) + .collect::>(); + if !ip4s.is_empty() { + return ip4s.iter().all(|ip4| { + ip4.is_loopback() + || (ip4.is_private() && !ip4.octets().starts_with(&[10, 59])) // reserving 10.59 for public wireguard configurations + || ip4.is_link_local() + }); + } + ip_info.subnets.iter().all(|ipnet| { + if let IpAddr::V6(ip6) = ipnet.addr() { + ipv6_is_local(ip6) + } else { + true } }) }) diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index 1edcd3efe..63642dfb3 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -219,7 +219,6 @@ pub struct InitPhases { enable_zram: PhaseProgressTrackerHandle, update_server_info: PhaseProgressTrackerHandle, launch_service_network: PhaseProgressTrackerHandle, - run_migrations: PhaseProgressTrackerHandle, validate_db: PhaseProgressTrackerHandle, postinit: Option, } @@ -244,7 +243,6 @@ impl InitPhases { enable_zram: handle.add_phase("Enabling ZRAM".into(), Some(1)), update_server_info: handle.add_phase("Updating server info".into(), Some(1)), launch_service_network: handle.add_phase("Launching service intranet".into(), Some(1)), - run_migrations: handle.add_phase("Running migrations".into(), Some(10)), validate_db: handle.add_phase("Validating database".into(), Some(1)), postinit: if Path::new("/media/startos/config/postinit.sh").exists() { Some(handle.add_phase("Running postinit.sh".into(), Some(5))) @@ -297,7 +295,6 @@ pub async fn init( mut enable_zram, mut update_server_info, mut launch_service_network, - run_migrations, mut validate_db, postinit, }: InitPhases, @@ -412,20 +409,6 @@ pub async fn init( Command::new("update-ca-certificates") .invoke(crate::ErrorKind::OpenSsl) .await?; - if tokio::fs::metadata("/home/kiosk/profile").await.is_ok() { - Command::new("certutil") - .arg("-A") - .arg("-n") - .arg("StartOS Local Root CA") - .arg("-t") - .arg("TCu,Cuw,Tuw") - .arg("-i") - .arg("/usr/local/share/ca-certificates/startos-root-ca.crt") - .arg("-d") - .arg("/home/kiosk/fx-profile") - .invoke(ErrorKind::OpenSsl) - .await?; - } load_ca_cert.complete(); load_wifi.start(); diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index 8c3fe6ae1..c3d0e8676 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -219,7 +219,7 @@ impl NetServiceData { // LAN let server_info = peek.as_public().as_server_info(); - let net_ifaces = server_info.as_network_interfaces().de()?; + let net_ifaces = ctrl.net_iface.ip_info(); let hostname = server_info.as_hostname().de()?; for (port, bind) in &host.bindings { if !bind.enabled { @@ -586,21 +586,24 @@ impl NetServiceData { async fn update_all(&mut self) -> Result<(), Error> { let ctrl = self.net_controller()?; - if let Some(id) = &self.id { + if let Some(id) = self.id.clone() { for (host_id, host) in ctrl .db .peek() .await .as_public() .as_package_data() - .as_idx(id) - .or_not_found(id)? + .as_idx(&id) + .or_not_found(&id)? .as_hosts() .as_entries()? { - self.update(&*ctrl, host_id, host.de()?).await?; + tracing::info!("Updating host {host_id} for {id}"); + self.update(&*ctrl, host_id.clone(), host.de()?).await?; + tracing::info!("Updated host {host_id} for {id}"); } } else { + tracing::info!("Updating host for Main UI"); self.update( &*ctrl, HostId::default(), @@ -613,6 +616,7 @@ impl NetServiceData { .de()?, ) .await?; + tracing::info!("Updated host for Main UI"); } Ok(()) } @@ -710,6 +714,7 @@ impl NetService { drop(ctrl); Ok(()) } else { + self.shutdown = true; tracing::warn!("NetService dropped after NetController is shutdown"); Err(Error::new( eyre!("NetController is shutdown"), diff --git a/core/startos/src/net/network_interface.rs b/core/startos/src/net/network_interface.rs index bda34fc40..8c99f0de9 100644 --- a/core/startos/src/net/network_interface.rs +++ b/core/startos/src/net/network_interface.rs @@ -29,6 +29,7 @@ use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceTyp use crate::db::model::Database; use crate::net::forward::START9_BRIDGE_IFACE; use crate::net::utils::{ipv6_is_link_local, ipv6_is_local}; +use crate::net::web_server::Accept; use crate::prelude::*; use crate::util::future::Until; use crate::util::io::open_file; @@ -411,8 +412,10 @@ async fn watcher( } async fn get_wan_ipv4(iface: &str) -> Result, Error> { - Ok(reqwest::Client::builder() - .interface(iface) + let client = reqwest::Client::builder(); + #[cfg(target_os = "linux")] + let client = client.interface(iface); + Ok(client .build()? .get("http://ip4only.me/api/") .timeout(Duration::from_secs(10)) @@ -647,6 +650,10 @@ impl NetworkInterfaceController { self.ip_info.clone_unseen() } + pub fn ip_info(&self) -> BTreeMap { + self.ip_info.read() + } + async fn sync( db: &TypedPatchDb, info: &BTreeMap, @@ -790,11 +797,13 @@ impl NetworkInterfaceController { pub fn upgrade_listener( &self, - listener: impl IntoIterator, + SelfContainedNetworkInterfaceListener { + mut listener, + .. + }: SelfContainedNetworkInterfaceListener, ) -> Result { - let listeners = ListenerMap::from_listener(listener)?; - let port = listeners.port; - let arc = Arc::new(()); + let port = listener.listeners.port; + let arc = &listener._arc; self.listeners.mutate(|l| { if l.get(&port).filter(|w| w.strong_count() > 0).is_some() { return Err(Error::new( @@ -802,16 +811,13 @@ impl NetworkInterfaceController { ErrorKind::Network, )); } - l.insert(port, Arc::downgrade(&arc)); + l.insert(port, Arc::downgrade(arc)); Ok(()) })?; let ip_info = self.ip_info.clone_unseen(); ip_info.mark_changed(); - Ok(NetworkInterfaceListener { - _arc: arc, - ip_info, - listeners, - }) + listener.change_ip_info_source(ip_info); + Ok(listener) } pub async fn set_public( @@ -1050,6 +1056,26 @@ impl NetworkInterfaceListener { self.listeners.poll_accept(cx) } + pub(super) fn new( + mut ip_info: Watch>, + port: u16, + ) -> Self { + ip_info.mark_unseen(); + Self { + ip_info, + listeners: ListenerMap::new(port), + _arc: Arc::new(()), + } + } + + pub fn change_ip_info_source( + &mut self, + mut ip_info: Watch>, + ) { + ip_info.mark_unseen(); + self.ip_info = ip_info; + } + pub async fn accept(&mut self, public: bool) -> Result { futures::future::poll_fn(|cx| self.poll_accept(cx, public)).await } @@ -1063,82 +1089,25 @@ pub struct Accepted { pub bind: SocketAddr, } -// async fn _ips() -> Result, Error> { -// Ok(init_ips() -// .await? -// .values() -// .flat_map(|i| { -// std::iter::empty() -// .chain(i.ipv4.map(IpAddr::from)) -// .chain(i.ipv6.map(IpAddr::from)) -// }) -// .collect()) -// } - -// pub async fn ips() -> Result, Error> { -// let ips = CACHED_IPS.read().await.clone(); -// if !ips.is_empty() { -// return Ok(ips); -// } -// let ips = _ips().await?; -// *CACHED_IPS.write().await = ips.clone(); -// Ok(ips) -// } - -// pub async fn init_ips() -> Result, Error> { -// let mut res = BTreeMap::new(); -// let mut ifaces = list_interfaces(); -// while let Some(iface) = ifaces.try_next().await? { -// if iface_is_physical(&iface).await { -// let ip_info = IpInfo::for_interface(&iface).await?; -// res.insert(iface, ip_info); -// } -// } -// Ok(res) -// } - -// // #[command(subcommands(update))] -// pub fn dhcp() -> ParentHandler { -// ParentHandler::new().subcommand( -// "update", -// from_fn_async::<_, _, (), Error, (RpcContext, UpdateParams)>(update) -// .no_display() -// .with_about("Update IP assigned by dhcp") -// .with_call_remote::(), -// ) -// } -// #[derive(Deserialize, Serialize, Parser, TS)] -// #[serde(rename_all = "camelCase")] -// #[command(rename_all = "kebab-case")] -// pub struct UpdateParams { -// interface: String, -// } - -// pub async fn update( -// ctx: RpcContext, -// UpdateParams { interface }: UpdateParams, -// ) -> Result<(), Error> { -// if iface_is_physical(&interface).await { -// let ip_info = IpInfo::for_interface(&interface).await?; -// ctx.db -// .mutate(|db| { -// db.as_public_mut() -// .as_server_info_mut() -// .as_ip_info_mut() -// .insert(&interface, &ip_info) -// }) -// .await?; - -// let mut cached = CACHED_IPS.write().await; -// if cached.is_empty() { -// *cached = _ips().await?; -// } else { -// cached.extend( -// std::iter::empty() -// .chain(ip_info.ipv4.map(IpAddr::from)) -// .chain(ip_info.ipv6.map(IpAddr::from)), -// ); -// } -// } -// Ok(()) -// } +pub struct SelfContainedNetworkInterfaceListener { + _watch_thread: NonDetachingJoinHandle<()>, + listener: NetworkInterfaceListener, +} +impl SelfContainedNetworkInterfaceListener { + pub fn bind(port: u16) -> Self { + let ip_info = Watch::new(BTreeMap::new()); + let _watch_thread = tokio::spawn(watcher(ip_info.clone(), Watch::new(false))).into(); + Self { + _watch_thread, + listener: NetworkInterfaceListener::new(ip_info, port), + } + } +} +impl Accept for SelfContainedNetworkInterfaceListener { + fn poll_accept( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Accept::poll_accept(&mut self.listener, cx) + } +} diff --git a/core/startos/src/net/utils.rs b/core/startos/src/net/utils.rs index d6dcdde15..8799fd6cd 100644 --- a/core/startos/src/net/utils.rs +++ b/core/startos/src/net/utils.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}; use std::path::Path; @@ -5,12 +6,18 @@ use async_stream::try_stream; use color_eyre::eyre::eyre; use futures::stream::BoxStream; use futures::{StreamExt, TryStreamExt}; +use helpers::NonDetachingJoinHandle; +use imbl_value::InternedString; use ipnet::{IpNet, Ipv4Net, Ipv6Net}; use nix::net::if_::if_nametoindex; use tokio::net::{TcpListener, TcpStream}; use tokio::process::Command; +use crate::db::model::public::NetworkInterfaceInfo; +use crate::net::network_interface::NetworkInterfaceListener; +use crate::net::web_server::Accept; use crate::prelude::*; +use crate::util::sync::Watch; use crate::util::Invoke; pub fn ipv6_is_link_local(addr: Ipv6Addr) -> bool { @@ -121,7 +128,7 @@ pub async fn find_eth_iface() -> Result { )) } -pub async fn all_socket_addrs_for(port: u16) -> Result, Error> { +pub async fn all_socket_addrs_for(port: u16) -> Result, Error> { let mut res = Vec::new(); let raw = String::from_utf8( @@ -153,14 +160,17 @@ pub async fn all_socket_addrs_for(port: u16) -> Result, Error> { .parse::() .with_ctx(|_| (ErrorKind::ParseSysInfo, err("ipnet", idx, ipnet_str)))?; match ipnet.addr() { - IpAddr::V4(ip4) => res.push(SocketAddr::new(ip4.into(), port)), - IpAddr::V6(ip6) => res.push(SocketAddr::V6(SocketAddrV6::new( - ip6, - port, - 0, - if_nametoindex(ifname) - .with_ctx(|_| (ErrorKind::ParseSysInfo, "reading scope_id"))?, - ))), + IpAddr::V4(ip4) => res.push((ifname.into(), SocketAddr::new(ip4.into(), port))), + IpAddr::V6(ip6) => res.push(( + ifname.into(), + SocketAddr::V6(SocketAddrV6::new( + ip6, + port, + 0, + if_nametoindex(ifname) + .with_ctx(|_| (ErrorKind::ParseSysInfo, "reading scope_id"))?, + )), + )), } } diff --git a/core/startos/src/net/web_server.rs b/core/startos/src/net/web_server.rs index 468b5d5db..641a6f08d 100644 --- a/core/startos/src/net/web_server.rs +++ b/core/startos/src/net/web_server.rs @@ -15,7 +15,9 @@ use tokio::net::{TcpListener, TcpStream}; use tokio::sync::oneshot; use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext}; -use crate::net::network_interface::NetworkInterfaceListener; +use crate::net::network_interface::{ + NetworkInterfaceListener, SelfContainedNetworkInterfaceListener, +}; use crate::net::static_server::{ diagnostic_ui_router, init_ui_router, install_ui_router, main_ui_router, redirecter, refresher, setup_ui_router, @@ -106,15 +108,12 @@ impl Acceptor> { } } -pub type UpgradableListener = Option, NetworkInterfaceListener>>; +pub type UpgradableListener = + Option>; impl Acceptor { - pub async fn bind_upgradable( - listen: impl IntoIterator, - ) -> Result { - Ok(Self::new(Some(Either::Left( - futures::future::try_join_all(listen.into_iter().map(TcpListener::bind)).await?, - )))) + pub fn bind_upgradable(listener: SelfContainedNetworkInterfaceListener) -> Self { + Self::new(Some(Either::Left(listener))) } } diff --git a/core/startos/src/version/mod.rs b/core/startos/src/version/mod.rs index 966379470..2ff43fec2 100644 --- a/core/startos/src/version/mod.rs +++ b/core/startos/src/version/mod.rs @@ -12,6 +12,7 @@ use patch_db::json_ptr::ROOT; use crate::context::RpcContext; use crate::db::model::Database; use crate::prelude::*; +use crate::progress::PhaseProgressTrackerHandle; use crate::Error; mod v0_3_5; @@ -31,8 +32,9 @@ mod v0_3_6_alpha_9; mod v0_3_6_alpha_10; mod v0_3_6_alpha_11; mod v0_3_6_alpha_12; +mod v0_3_6_alpha_13; -pub type Current = v0_3_6_alpha_12::Version; // VERSION_BUMP +pub type Current = v0_3_6_alpha_13::Version; // VERSION_BUMP impl Current { #[instrument(skip(self, db))] @@ -55,8 +57,7 @@ impl Current { let pre_ups = PreUps::load(&from, &self).await?; db.apply_function(|mut db| { migrate_from_unchecked(&from, &self, pre_ups, &mut db)?; - from_value::(db.clone())?; - Ok::<_, Error>((db, ())) + Ok::<_, Error>((to_value(&from_value::(db.clone())?)?, ())) }) .await?; } @@ -66,31 +67,44 @@ impl Current { } } -pub async fn post_init(ctx: &RpcContext) -> Result<(), Error> { - let mut peek; - while let Some(version) = { - peek = ctx.db.peek().await; - peek.as_public() - .as_server_info() - .as_post_init_migration_todos() - .de()? - .first() - .cloned() - .map(Version::from_exver_version) - .as_ref() - .map(Version::as_version_t) - .transpose()? - } { - version.0.post_up(ctx).await?; - ctx.db - .mutate(|db| { - db.as_public_mut() - .as_server_info_mut() - .as_post_init_migration_todos_mut() - .mutate(|m| Ok(m.remove(&version.0.semver()))) - }) - .await?; +pub async fn post_init( + ctx: &RpcContext, + mut progress: PhaseProgressTrackerHandle, +) -> Result<(), Error> { + let mut peek = ctx.db.peek().await; + let todos = peek + .as_public() + .as_server_info() + .as_post_init_migration_todos() + .de()?; + if !todos.is_empty() { + progress.set_total(todos.len() as u64); + while let Some(version) = { + peek = ctx.db.peek().await; + peek.as_public() + .as_server_info() + .as_post_init_migration_todos() + .de()? + .first() + .cloned() + .map(Version::from_exver_version) + .as_ref() + .map(Version::as_version_t) + .transpose()? + } { + version.0.post_up(ctx).await?; + ctx.db + .mutate(|db| { + db.as_public_mut() + .as_server_info_mut() + .as_post_init_migration_todos_mut() + .mutate(|m| Ok(m.remove(&version.0.semver()))) + }) + .await?; + progress += 1; + } } + progress.complete(); Ok(()) } @@ -115,6 +129,7 @@ enum Version { V0_3_6_alpha_10(Wrapper), V0_3_6_alpha_11(Wrapper), V0_3_6_alpha_12(Wrapper), + V0_3_6_alpha_13(Wrapper), // VERSION_BUMP Other(exver::Version), } @@ -151,6 +166,7 @@ impl Version { Self::V0_3_6_alpha_10(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_11(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_12(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_13(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP Self::Other(v) => { return Err(Error::new( eyre!("unknown version {v}"), @@ -179,6 +195,7 @@ impl Version { Version::V0_3_6_alpha_10(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_11(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_12(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_13(Wrapper(x)) => x.semver(), // VERSION_BUMP Version::Other(x) => x.clone(), } } diff --git a/core/startos/src/version/v0_3_6_alpha_0.rs b/core/startos/src/version/v0_3_6_alpha_0.rs index b7f2ba2e4..6ed3fc316 100644 --- a/core/startos/src/version/v0_3_6_alpha_0.rs +++ b/core/startos/src/version/v0_3_6_alpha_0.rs @@ -193,7 +193,7 @@ pub struct Version; impl VersionT for Version { type Previous = v0_3_5_2::Version; - type PreUpRes = (AccountInfo, SshKeys, CifsTargets, Notifications); + type PreUpRes = (AccountInfo, SshKeys, CifsTargets); fn semver(self) -> exver::Version { V0_3_6_alpha_0.clone() } @@ -208,15 +208,9 @@ impl VersionT for Version { let cifs = previous_cifs(&pg).await?; - let notifications = previous_notifications(pg).await?; - - Ok((account, ssh_keys, cifs, notifications)) + Ok((account, ssh_keys, cifs)) } - fn up( - self, - db: &mut Value, - (account, ssh_keys, cifs, notifications): Self::PreUpRes, - ) -> Result<(), Error> { + fn up(self, db: &mut Value, (account, ssh_keys, cifs): Self::PreUpRes) -> Result<(), Error> { let wifi = json!({ "infterface": db["server-info"]["wifi"]["interface"], "ssids": db["server-info"]["wifi"]["ssids"], @@ -298,7 +292,7 @@ impl VersionT for Version { value["sshPubkeys"] = to_value(&ssh_keys)?; value["availablePorts"] = to_value(&AvailablePorts::new())?; value["sessions"] = to_value(&Sessions::new())?; - value["notifications"] = to_value(¬ifications)?; + value["notifications"] = to_value(&Notifications::new())?; value["cifs"] = to_value(&cifs)?; value["packageStores"] = json!({}); value @@ -375,64 +369,6 @@ impl VersionT for Version { } } -async fn previous_notifications(pg: sqlx::Pool) -> Result { - let notification_cursor = sqlx::query(r#"SELECT * FROM notifications"#) - .fetch_all(&pg) - .await?; - let notifications = { - let mut notifications = Notifications::default(); - for row in notification_cursor { - let package_id = serde_json::from_str::( - row.try_get("package_id") - .with_ctx(|_| (ErrorKind::Database, "package_id"))?, - ) - .ok(); - - let created_at = row - .try_get("created_at") - .with_ctx(|_| (ErrorKind::Database, "created_at"))?; - let code = row - .try_get::("code") - .with_ctx(|_| (ErrorKind::Database, "code"))? as u32; - let id = row - .try_get::("id") - .with_ctx(|_| (ErrorKind::Database, "id"))? as u32; - let level = serde_json::from_str( - row.try_get("level") - .with_ctx(|_| (ErrorKind::Database, "level"))?, - ) - .with_kind(ErrorKind::Database) - .with_ctx(|_| (ErrorKind::Database, "level: serde_json "))?; - let title = row - .try_get("title") - .with_ctx(|_| (ErrorKind::Database, "title"))?; - let message = row - .try_get("message") - .with_ctx(|_| (ErrorKind::Database, "message"))?; - let data = serde_json::from_str( - row.try_get("data") - .with_ctx(|_| (ErrorKind::Database, "data"))?, - ) - .unwrap_or_default(); - - notifications.0.insert( - id, - Notification { - package_id, - created_at, - code, - level, - title, - message, - data, - }, - ); - } - notifications - }; - Ok(notifications) -} - #[tracing::instrument(skip_all)] async fn previous_cifs(pg: &sqlx::Pool) -> Result { let cifs = sqlx::query(r#"SELECT * FROM cifs_shares"#) @@ -440,16 +376,17 @@ async fn previous_cifs(pg: &sqlx::Pool) -> Result(( id, Cifs { hostname: row .try_get("hostname") .with_ctx(|_| (ErrorKind::Database, "hostname"))?, - path: serde_json::from_str(row.try_get("path")?) - .with_kind(ErrorKind::Database) - .with_ctx(|_| (ErrorKind::Database, "path"))?, + path: row + .try_get::("path") + .with_ctx(|_| (ErrorKind::Database, "path"))? + .into(), username: row .try_get("username") .with_ctx(|_| (ErrorKind::Database, "username"))?, diff --git a/core/startos/src/version/v0_3_6_alpha_13.rs b/core/startos/src/version/v0_3_6_alpha_13.rs new file mode 100644 index 000000000..3508a27ac --- /dev/null +++ b/core/startos/src/version/v0_3_6_alpha_13.rs @@ -0,0 +1,68 @@ +use std::collections::BTreeMap; + +use exver::{PreReleaseSegment, VersionRange}; +use imbl_value::json; + +use super::v0_3_5::V0_3_0_COMPAT; +use super::{v0_3_6_alpha_11, VersionT}; +use crate::prelude::*; + +lazy_static::lazy_static! { + static ref V0_3_6_alpha_12: exver::Version = exver::Version::new( + [0, 3, 6], + [PreReleaseSegment::String("alpha".into()), 12.into()] + ); +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Version; + +impl VersionT for Version { + type Previous = v0_3_6_alpha_11::Version; + type PreUpRes = (); + + async fn pre_up(self) -> Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_12.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + let bindings: BTreeMap = [( + 80, + json!({ + "enabled": false, + "options": { + "preferredExternalPort": 80, + "addSsl": { + "preferredExternalPort": 443, + "alpn": { "specified": [ "http/1.1", "h2" ] }, + }, + "secure": null, + }, + "net": { + "assignedPort": null, + "assignedSslPort": 443, + "public": false, + } + }), + )] + .into_iter() + .collect(); + let onion = db["public"]["serverInfo"]["onionAddress"].clone(); + db["public"]["serverInfo"]["host"] = json!({ + "bindings": bindings, + "onions": [onion], + "domains": {}, + "hostnameInfo": {}, + }); + + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/image-recipe/build.sh b/image-recipe/build.sh index 9e61999e6..eaf5e8382 100755 --- a/image-recipe/build.sh +++ b/image-recipe/build.sh @@ -206,8 +206,8 @@ if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then echo "Configuring raspi kernel '\$v'" extract-ikconfig "/usr/lib/modules/\$v/kernel/kernel/configs.ko.xz" > /boot/config-\$v done - mkinitramfs -c gzip -o /boot/initramfs8 6.6.62-v8+ - mkinitramfs -c gzip -o /boot/initramfs_2712 6.6.62-v8-16k+ + mkinitramfs -c gzip -o /boot/initramfs8 6.6.74-v8+ + mkinitramfs -c gzip -o /boot/initramfs_2712 6.6.74-v8-16k+ fi useradd --shell /bin/bash -G startos -m start9 diff --git a/web/package-lock.json b/web/package-lock.json index f218a6d82..2faf0f318 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.12", + "version": "0.3.6-alpha.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "startos-ui", - "version": "0.3.6-alpha.12", + "version": "0.3.6-alpha.13", "license": "MIT", "dependencies": { "@angular/animations": "^14.1.0", diff --git a/web/package.json b/web/package.json index 3355c46cc..3254fcd17 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.12", + "version": "0.3.6-alpha.13", "author": "Start9 Labs, Inc", "homepage": "https://start9.com/", "license": "MIT", diff --git a/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts b/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts index 2386c09ec..b95351538 100644 --- a/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts +++ b/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts @@ -76,14 +76,18 @@ export class LoadingPage { guid: string progress: T.FullProgress } | void> { - const res = await this.api.getStatus() + try { + const res = await this.api.getStatus() - if (!res) { - this.navCtrl.navigateRoot('/home') - } else if (res.status === 'complete') { - this.navCtrl.navigateForward(`/success`) - } else { - return res + if (!res) { + this.navCtrl.navigateRoot('/home') + } else if (res.status === 'complete') { + this.navCtrl.navigateForward(`/success`) + } else { + return res + } + } catch (e: any) { + this.errorService.handleError(e) } }