miscellaneous bugfixes for alpha12 (#2823)

* miscellaneous bugfixes for alpha12

* fix deserialization of path in cifs share

* catch error in setup.status

* actually reserialize db after migration

* better progress reporting for migrations

* fix infinite drop

* fix raspi build

* fix race condition

* version bump

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
Aiden McClelland
2025-01-28 23:02:52 +00:00
committed by GitHub
parent b83eeeb131
commit 446b37793b
20 changed files with 280 additions and 266 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

2
core/Cargo.lock generated
View File

@@ -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",

View File

@@ -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]

View File

@@ -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<Item = OsString>) {
.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;

View File

@@ -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<Self, Error> {
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(())
}

View File

@@ -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) => {
let ip4s = ip_info
.subnets
.iter()
.filter_map(|ipnet| {
if let IpAddr::V4(ip4) = ipnet.addr() {
Some(ip4)
} else {
None
}
})
.collect::<BTreeSet<_>>();
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()
});
}
IpAddr::V6(_) => true,
ip_info.subnets.iter().all(|ipnet| {
if let IpAddr::V6(ip6) = ipnet.addr() {
ipv6_is_local(ip6)
} else {
true
}
})
})

View File

@@ -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<PhaseProgressTrackerHandle>,
}
@@ -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();

View File

@@ -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"),

View File

@@ -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<Option<Ipv4Addr>, 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<InternedString, NetworkInterfaceInfo> {
self.ip_info.read()
}
async fn sync(
db: &TypedPatchDb<Database>,
info: &BTreeMap<InternedString, NetworkInterfaceInfo>,
@@ -790,11 +797,13 @@ impl NetworkInterfaceController {
pub fn upgrade_listener(
&self,
listener: impl IntoIterator<Item = TcpListener>,
SelfContainedNetworkInterfaceListener {
mut listener,
..
}: SelfContainedNetworkInterfaceListener,
) -> Result<NetworkInterfaceListener, Error> {
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<BTreeMap<InternedString, NetworkInterfaceInfo>>,
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<BTreeMap<InternedString, NetworkInterfaceInfo>>,
) {
ip_info.mark_unseen();
self.ip_info = ip_info;
}
pub async fn accept(&mut self, public: bool) -> Result<Accepted, Error> {
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<BTreeSet<IpAddr>, 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<BTreeSet<IpAddr>, 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<BTreeMap<String, IpInfo>, 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<C: Context>() -> ParentHandler<C> {
// ParentHandler::new().subcommand(
// "update",
// from_fn_async::<_, _, (), Error, (RpcContext, UpdateParams)>(update)
// .no_display()
// .with_about("Update IP assigned by dhcp")
// .with_call_remote::<CliContext>(),
// )
// }
// #[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<Result<super::web_server::Accepted, Error>> {
Accept::poll_accept(&mut self.listener, cx)
}
}

View File

@@ -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<String, Error> {
))
}
pub async fn all_socket_addrs_for(port: u16) -> Result<Vec<SocketAddr>, Error> {
pub async fn all_socket_addrs_for(port: u16) -> Result<Vec<(InternedString, SocketAddr)>, 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<Vec<SocketAddr>, Error> {
.parse::<IpNet>()
.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(
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"))?,
))),
)),
)),
}
}

View File

@@ -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<Vec<TcpListener>> {
}
}
pub type UpgradableListener = Option<Either<Vec<TcpListener>, NetworkInterfaceListener>>;
pub type UpgradableListener =
Option<Either<SelfContainedNetworkInterfaceListener, NetworkInterfaceListener>>;
impl Acceptor<UpgradableListener> {
pub async fn bind_upgradable(
listen: impl IntoIterator<Item = SocketAddr>,
) -> Result<Self, Error> {
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)))
}
}

View File

@@ -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::<Database>(db.clone())?;
Ok::<_, Error>((db, ()))
Ok::<_, Error>((to_value(&from_value::<Database>(db.clone())?)?, ()))
})
.await?;
}
@@ -66,8 +67,18 @@ impl Current {
}
}
pub async fn post_init(ctx: &RpcContext) -> Result<(), Error> {
let mut peek;
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()
@@ -90,7 +101,10 @@ pub async fn post_init(ctx: &RpcContext) -> Result<(), Error> {
.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_10::Version>),
V0_3_6_alpha_11(Wrapper<v0_3_6_alpha_11::Version>),
V0_3_6_alpha_12(Wrapper<v0_3_6_alpha_12::Version>),
V0_3_6_alpha_13(Wrapper<v0_3_6_alpha_13::Version>), // 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(),
}
}

View File

@@ -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(&notifications)?;
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<sqlx::Postgres>) -> Result<Notifications, Error> {
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::<PackageId>(
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::<i64, _>("code")
.with_ctx(|_| (ErrorKind::Database, "code"))? as u32;
let id = row
.try_get::<i64, _>("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<sqlx::Postgres>) -> Result<CifsTargets, Error> {
let cifs = sqlx::query(r#"SELECT * FROM cifs_shares"#)
@@ -440,16 +376,17 @@ async fn previous_cifs(pg: &sqlx::Pool<sqlx::Postgres>) -> Result<CifsTargets, E
.await?
.into_iter()
.map(|row| {
let id: i64 = row.try_get("id")?;
let id: i32 = row.try_get("id")?;
Ok::<_, Error>((
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::<String, _>("path")
.with_ctx(|_| (ErrorKind::Database, "path"))?
.into(),
username: row
.try_get("username")
.with_ctx(|_| (ErrorKind::Database, "username"))?,

View File

@@ -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<Self::PreUpRes, Error> {
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<u16, Value> = [(
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(())
}
}

View File

@@ -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

4
web/package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -76,6 +76,7 @@ export class LoadingPage {
guid: string
progress: T.FullProgress
} | void> {
try {
const res = await this.api.getStatus()
if (!res) {
@@ -85,6 +86,9 @@ export class LoadingPage {
} else {
return res
}
} catch (e: any) {
this.errorService.handleError(e)
}
}
private getRunningStatus$(): Observable<{