mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
use hardware requirements to display conflicts and prevent install (#2700)
* use hardware requirements to display conflicts and prevent install * better messaging and also consider OS compatibility * wip: backend hw requirements * update backend components * migration --------- Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
676
core/Cargo.lock
generated
676
core/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -39,8 +39,7 @@ use crate::service::action::update_requested_actions;
|
|||||||
use crate::service::effects::callbacks::ServiceCallbacks;
|
use crate::service::effects::callbacks::ServiceCallbacks;
|
||||||
use crate::service::ServiceMap;
|
use crate::service::ServiceMap;
|
||||||
use crate::shutdown::Shutdown;
|
use crate::shutdown::Shutdown;
|
||||||
use crate::system::get_mem_info;
|
use crate::util::lshw::LshwDevice;
|
||||||
use crate::util::lshw::{lshw, LshwDevice};
|
|
||||||
use crate::util::sync::SyncMutex;
|
use crate::util::sync::SyncMutex;
|
||||||
|
|
||||||
pub struct RpcContextSeed {
|
pub struct RpcContextSeed {
|
||||||
@@ -67,7 +66,6 @@ pub struct RpcContextSeed {
|
|||||||
pub wifi_manager: Option<Arc<RwLock<WpaCli>>>,
|
pub wifi_manager: Option<Arc<RwLock<WpaCli>>>,
|
||||||
pub current_secret: Arc<Jwk>,
|
pub current_secret: Arc<Jwk>,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub hardware: Hardware,
|
|
||||||
pub start_time: Instant,
|
pub start_time: Instant,
|
||||||
pub crons: SyncMutex<BTreeMap<Guid, NonDetachingJoinHandle<()>>>,
|
pub crons: SyncMutex<BTreeMap<Guid, NonDetachingJoinHandle<()>>>,
|
||||||
// #[cfg(feature = "dev")]
|
// #[cfg(feature = "dev")]
|
||||||
@@ -86,7 +84,6 @@ pub struct Hardware {
|
|||||||
pub struct InitRpcContextPhases {
|
pub struct InitRpcContextPhases {
|
||||||
load_db: PhaseProgressTrackerHandle,
|
load_db: PhaseProgressTrackerHandle,
|
||||||
init_net_ctrl: PhaseProgressTrackerHandle,
|
init_net_ctrl: PhaseProgressTrackerHandle,
|
||||||
read_device_info: PhaseProgressTrackerHandle,
|
|
||||||
cleanup_init: CleanupInitPhases,
|
cleanup_init: CleanupInitPhases,
|
||||||
// TODO: migrations
|
// TODO: migrations
|
||||||
}
|
}
|
||||||
@@ -95,7 +92,6 @@ impl InitRpcContextPhases {
|
|||||||
Self {
|
Self {
|
||||||
load_db: handle.add_phase("Loading database".into(), Some(5)),
|
load_db: handle.add_phase("Loading database".into(), Some(5)),
|
||||||
init_net_ctrl: handle.add_phase("Initializing network".into(), Some(1)),
|
init_net_ctrl: handle.add_phase("Initializing network".into(), Some(1)),
|
||||||
read_device_info: handle.add_phase("Reading device information".into(), Some(1)),
|
|
||||||
cleanup_init: CleanupInitPhases::new(handle),
|
cleanup_init: CleanupInitPhases::new(handle),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,7 +123,6 @@ impl RpcContext {
|
|||||||
InitRpcContextPhases {
|
InitRpcContextPhases {
|
||||||
mut load_db,
|
mut load_db,
|
||||||
mut init_net_ctrl,
|
mut init_net_ctrl,
|
||||||
mut read_device_info,
|
|
||||||
cleanup_init,
|
cleanup_init,
|
||||||
}: InitRpcContextPhases,
|
}: InitRpcContextPhases,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
@@ -179,11 +174,6 @@ impl RpcContext {
|
|||||||
let metrics_cache = RwLock::<Option<crate::system::Metrics>>::new(None);
|
let metrics_cache = RwLock::<Option<crate::system::Metrics>>::new(None);
|
||||||
let tor_proxy_url = format!("socks5h://{tor_proxy}");
|
let tor_proxy_url = format!("socks5h://{tor_proxy}");
|
||||||
|
|
||||||
read_device_info.start();
|
|
||||||
let devices = lshw().await?;
|
|
||||||
let ram = get_mem_info().await?.total.0 as u64 * 1024 * 1024;
|
|
||||||
read_device_info.complete();
|
|
||||||
|
|
||||||
let crons = SyncMutex::new(BTreeMap::new());
|
let crons = SyncMutex::new(BTreeMap::new());
|
||||||
|
|
||||||
if !db
|
if !db
|
||||||
@@ -275,7 +265,6 @@ impl RpcContext {
|
|||||||
}))
|
}))
|
||||||
.build()
|
.build()
|
||||||
.with_kind(crate::ErrorKind::ParseUrl)?,
|
.with_kind(crate::ErrorKind::ParseUrl)?,
|
||||||
hardware: Hardware { devices, ram },
|
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
crons,
|
crons,
|
||||||
// #[cfg(feature = "dev")]
|
// #[cfg(feature = "dev")]
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use crate::prelude::*;
|
|||||||
use crate::progress::FullProgress;
|
use crate::progress::FullProgress;
|
||||||
use crate::system::SmtpValue;
|
use crate::system::SmtpValue;
|
||||||
use crate::util::cpupower::Governor;
|
use crate::util::cpupower::Governor;
|
||||||
|
use crate::util::lshw::LshwDevice;
|
||||||
use crate::version::{Current, VersionT};
|
use crate::version::{Current, VersionT};
|
||||||
use crate::{ARCH, PLATFORM};
|
use crate::{ARCH, PLATFORM};
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ impl Public {
|
|||||||
version: Current::default().semver(),
|
version: Current::default().semver(),
|
||||||
hostname: account.hostname.no_dot_host_name(),
|
hostname: account.hostname.no_dot_host_name(),
|
||||||
last_backup: None,
|
last_backup: None,
|
||||||
version_compat: Current::default().compat().clone(),
|
package_version_compat: Current::default().compat().clone(),
|
||||||
post_init_migration_todos: BTreeSet::new(),
|
post_init_migration_todos: BTreeSet::new(),
|
||||||
lan_address,
|
lan_address,
|
||||||
onion_address: account.tor_key.public().get_onion_address(),
|
onion_address: account.tor_key.public().get_onion_address(),
|
||||||
@@ -78,6 +79,8 @@ impl Public {
|
|||||||
zram: true,
|
zram: true,
|
||||||
governor: None,
|
governor: None,
|
||||||
smtp: None,
|
smtp: None,
|
||||||
|
ram: 0,
|
||||||
|
devices: Vec::new(),
|
||||||
},
|
},
|
||||||
package_data: AllPackageData::default(),
|
package_data: AllPackageData::default(),
|
||||||
ui: serde_json::from_str(include_str!(concat!(
|
ui: serde_json::from_str(include_str!(concat!(
|
||||||
@@ -114,7 +117,7 @@ pub struct ServerInfo {
|
|||||||
#[ts(type = "string")]
|
#[ts(type = "string")]
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
#[ts(type = "string")]
|
#[ts(type = "string")]
|
||||||
pub version_compat: VersionRange,
|
pub package_version_compat: VersionRange,
|
||||||
#[ts(type = "string[]")]
|
#[ts(type = "string[]")]
|
||||||
pub post_init_migration_todos: BTreeSet<Version>,
|
pub post_init_migration_todos: BTreeSet<Version>,
|
||||||
#[ts(type = "string | null")]
|
#[ts(type = "string | null")]
|
||||||
@@ -141,6 +144,9 @@ pub struct ServerInfo {
|
|||||||
pub zram: bool,
|
pub zram: bool,
|
||||||
pub governor: Option<Governor>,
|
pub governor: Option<Governor>,
|
||||||
pub smtp: Option<SmtpValue>,
|
pub smtp: Option<SmtpValue>,
|
||||||
|
#[ts(type = "number")]
|
||||||
|
pub ram: u64,
|
||||||
|
pub devices: Vec<LshwDevice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, HasModel, TS)]
|
#[derive(Debug, Deserialize, Serialize, HasModel, TS)]
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ use crate::progress::{
|
|||||||
use crate::rpc_continuations::{Guid, RpcContinuation};
|
use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||||
use crate::s9pk::v2::pack::{CONTAINER_DATADIR, CONTAINER_TOOL};
|
use crate::s9pk::v2::pack::{CONTAINER_DATADIR, CONTAINER_TOOL};
|
||||||
use crate::ssh::SSH_AUTHORIZED_KEYS_FILE;
|
use crate::ssh::SSH_AUTHORIZED_KEYS_FILE;
|
||||||
|
use crate::system::get_mem_info;
|
||||||
use crate::util::io::{create_file, IOHook};
|
use crate::util::io::{create_file, IOHook};
|
||||||
|
use crate::util::lshw::lshw;
|
||||||
use crate::util::net::WebSocketExt;
|
use crate::util::net::WebSocketExt;
|
||||||
use crate::util::{cpupower, Invoke};
|
use crate::util::{cpupower, Invoke};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
@@ -508,6 +510,8 @@ pub async fn init(
|
|||||||
|
|
||||||
update_server_info.start();
|
update_server_info.start();
|
||||||
server_info.ip_info = crate::net::dhcp::init_ips().await?;
|
server_info.ip_info = crate::net::dhcp::init_ips().await?;
|
||||||
|
server_info.ram = get_mem_info().await?.total.0 as u64 * 1024 * 1024;
|
||||||
|
server_info.devices = lshw().await?;
|
||||||
server_info.status_info = ServerStatus {
|
server_info.status_info = ServerStatus {
|
||||||
updated: false,
|
updated: false,
|
||||||
update_progress: None,
|
update_progress: None,
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ impl CallRemote<RegistryContext, RegistryUrlParams> for RpcContext {
|
|||||||
.header(CONTENT_TYPE, "application/json")
|
.header(CONTENT_TYPE, "application/json")
|
||||||
.header(ACCEPT, "application/json")
|
.header(ACCEPT, "application/json")
|
||||||
.header(CONTENT_LENGTH, body.len())
|
.header(CONTENT_LENGTH, body.len())
|
||||||
.header(DEVICE_INFO_HEADER, DeviceInfo::from(self).to_header_value())
|
// .header(DEVICE_INFO_HEADER, DeviceInfo::from(self).to_header_value())
|
||||||
.body(body)
|
.body(body)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use url::Url;
|
|||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::registry::context::RegistryContext;
|
use crate::registry::context::RegistryContext;
|
||||||
|
use crate::util::lshw::{LshwDevice, LshwDisplay, LshwProcessor};
|
||||||
use crate::util::VersionString;
|
use crate::util::VersionString;
|
||||||
use crate::version::VersionT;
|
use crate::version::VersionT;
|
||||||
|
|
||||||
@@ -26,12 +27,12 @@ pub struct DeviceInfo {
|
|||||||
pub os: OsInfo,
|
pub os: OsInfo,
|
||||||
pub hardware: HardwareInfo,
|
pub hardware: HardwareInfo,
|
||||||
}
|
}
|
||||||
impl From<&RpcContext> for DeviceInfo {
|
impl DeviceInfo {
|
||||||
fn from(value: &RpcContext) -> Self {
|
pub async fn load(ctx: &RpcContext) -> Result<Self, Error> {
|
||||||
Self {
|
Ok(Self {
|
||||||
os: OsInfo::from(value),
|
os: OsInfo::from(ctx),
|
||||||
hardware: HardwareInfo::from(value),
|
hardware: HardwareInfo::load(ctx).await?,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl DeviceInfo {
|
impl DeviceInfo {
|
||||||
@@ -44,11 +45,11 @@ impl DeviceInfo {
|
|||||||
.append_pair("hardware.arch", &*self.hardware.arch)
|
.append_pair("hardware.arch", &*self.hardware.arch)
|
||||||
.append_pair("hardware.ram", &self.hardware.ram.to_string());
|
.append_pair("hardware.ram", &self.hardware.ram.to_string());
|
||||||
|
|
||||||
for (class, products) in &self.hardware.devices {
|
for device in &self.hardware.devices {
|
||||||
for product in products {
|
url.query_pairs_mut().append_pair(
|
||||||
url.query_pairs_mut()
|
&format!("hardware.device.{}", device.class()),
|
||||||
.append_pair(&format!("hardware.device.{}", class), product);
|
device.product(),
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderValue::from_str(url.query().unwrap_or_default()).unwrap()
|
HeaderValue::from_str(url.query().unwrap_or_default()).unwrap()
|
||||||
@@ -80,16 +81,20 @@ impl DeviceInfo {
|
|||||||
devices: identity(query)
|
devices: identity(query)
|
||||||
.split_off("hardware.device.")
|
.split_off("hardware.device.")
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(k, v)| {
|
.filter_map(|(k, v)| match k.strip_prefix("hardware.device.") {
|
||||||
k.strip_prefix("hardware.device.")
|
Some("processor") => Some(LshwDevice::Processor(LshwProcessor {
|
||||||
.map(|k| (k.into(), v.into_owned()))
|
product: v.into_owned(),
|
||||||
|
})),
|
||||||
|
Some("display") => Some(LshwDevice::Display(LshwDisplay {
|
||||||
|
product: v.into_owned(),
|
||||||
|
})),
|
||||||
|
Some(class) => {
|
||||||
|
tracing::warn!("unknown device class: {class}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
})
|
})
|
||||||
.fold(BTreeMap::new(), |mut acc, (k, v)| {
|
.collect(),
|
||||||
let mut devs = acc.remove(&k).unwrap_or_default();
|
|
||||||
devs.push(v);
|
|
||||||
acc.insert(k, devs);
|
|
||||||
acc
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -122,26 +127,16 @@ pub struct HardwareInfo {
|
|||||||
pub arch: InternedString,
|
pub arch: InternedString,
|
||||||
#[ts(type = "number")]
|
#[ts(type = "number")]
|
||||||
pub ram: u64,
|
pub ram: u64,
|
||||||
#[ts(as = "BTreeMap::<String, Vec<String>>")]
|
pub devices: Vec<LshwDevice>,
|
||||||
pub devices: BTreeMap<InternedString, Vec<String>>,
|
|
||||||
}
|
}
|
||||||
|
impl HardwareInfo {
|
||||||
impl From<&RpcContext> for HardwareInfo {
|
pub async fn load(ctx: &RpcContext) -> Result<Self, Error> {
|
||||||
fn from(value: &RpcContext) -> Self {
|
let s = ctx.db.peek().await.into_public().into_server_info();
|
||||||
Self {
|
Ok(Self {
|
||||||
arch: InternedString::intern(crate::ARCH),
|
arch: s.as_arch().de()?,
|
||||||
ram: value.hardware.ram,
|
ram: s.as_ram().de()?,
|
||||||
devices: value
|
devices: s.as_devices().de()?,
|
||||||
.hardware
|
})
|
||||||
.devices
|
|
||||||
.iter()
|
|
||||||
.fold(BTreeMap::new(), |mut acc, dev| {
|
|
||||||
let mut devs = acc.remove(dev.class()).unwrap_or_default();
|
|
||||||
devs.push(dev.product().to_owned());
|
|
||||||
acc.insert(dev.class().into(), devs);
|
|
||||||
acc
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -180,14 +180,13 @@ impl Model<PackageVersionInfo> {
|
|||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (class, regex) in hw.device {
|
for device_filter in hw.device {
|
||||||
if !device_info
|
if !device_info
|
||||||
.hardware
|
.hardware
|
||||||
.devices
|
.devices
|
||||||
.get(&*class)
|
|
||||||
.unwrap_or(&Vec::new())
|
|
||||||
.iter()
|
.iter()
|
||||||
.any(|product| regex.as_ref().is_match(product))
|
.filter(|d| d.class() == &*device_filter.class)
|
||||||
|
.any(|d| device_filter.pattern.as_ref().is_match(d.product()))
|
||||||
{
|
{
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use exver::{Version, VersionRange};
|
use exver::{Version, VersionRange};
|
||||||
|
use imbl_value::InternedString;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
pub use models::PackageId;
|
pub use models::PackageId;
|
||||||
use models::{ActionId, HealthCheckId, ImageId, VolumeId};
|
use models::{ActionId, HealthCheckId, ImageId, VolumeId};
|
||||||
@@ -10,8 +11,8 @@ use url::Url;
|
|||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::s9pk::git_hash::GitHash;
|
use crate::s9pk::git_hash::GitHash;
|
||||||
use crate::s9pk::manifest::{Alerts, Description, HardwareRequirements};
|
use crate::s9pk::manifest::{Alerts, Description};
|
||||||
use crate::util::serde::{Duration, IoFormat};
|
use crate::util::serde::{Duration, IoFormat, Regex};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
@@ -192,6 +193,15 @@ impl DependencyRequirement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct HardwareRequirements {
|
||||||
|
#[serde(default)]
|
||||||
|
pub device: BTreeMap<InternedString, Regex>,
|
||||||
|
pub ram: Option<u64>,
|
||||||
|
pub arch: Option<BTreeSet<InternedString>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Assets {
|
pub struct Assets {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use tokio::process::Command;
|
|||||||
|
|
||||||
use crate::dependencies::{DepInfo, Dependencies};
|
use crate::dependencies::{DepInfo, Dependencies};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::s9pk::manifest::Manifest;
|
use crate::s9pk::manifest::{DeviceFilter, Manifest};
|
||||||
use crate::s9pk::merkle_archive::directory_contents::DirectoryContents;
|
use crate::s9pk::merkle_archive::directory_contents::DirectoryContents;
|
||||||
use crate::s9pk::merkle_archive::source::TmpSource;
|
use crate::s9pk::merkle_archive::source::TmpSource;
|
||||||
use crate::s9pk::merkle_archive::{Entry, MerkleArchive};
|
use crate::s9pk::merkle_archive::{Entry, MerkleArchive};
|
||||||
@@ -246,7 +246,23 @@ impl TryFrom<ManifestV1> for Manifest {
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
hardware_requirements: value.hardware_requirements,
|
hardware_requirements: super::manifest::HardwareRequirements {
|
||||||
|
arch: value.hardware_requirements.arch,
|
||||||
|
ram: value.hardware_requirements.ram,
|
||||||
|
device: value
|
||||||
|
.hardware_requirements
|
||||||
|
.device
|
||||||
|
.into_iter()
|
||||||
|
.map(|(class, product)| DeviceFilter {
|
||||||
|
pattern_description: format!(
|
||||||
|
"a {class} device matching the expression {}",
|
||||||
|
product.as_ref()
|
||||||
|
),
|
||||||
|
class,
|
||||||
|
pattern: product,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
git_hash: value.git_hash,
|
git_hash: value.git_hash,
|
||||||
os_version: value.eos_version,
|
os_version: value.eos_version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -161,14 +161,24 @@ impl Manifest {
|
|||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct HardwareRequirements {
|
pub struct HardwareRequirements {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[ts(type = "{ display?: string, processor?: string }")]
|
pub device: Vec<DeviceFilter>,
|
||||||
pub device: BTreeMap<String, Regex>, // TODO: array
|
|
||||||
#[ts(type = "number | null")]
|
#[ts(type = "number | null")]
|
||||||
pub ram: Option<u64>,
|
pub ram: Option<u64>,
|
||||||
#[ts(type = "string[] | null")]
|
#[ts(type = "string[] | null")]
|
||||||
pub arch: Option<BTreeSet<InternedString>>,
|
pub arch: Option<BTreeSet<InternedString>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct DeviceFilter {
|
||||||
|
#[ts(type = "\"processor\" | \"display\"")]
|
||||||
|
pub class: InternedString,
|
||||||
|
#[ts(type = "string")]
|
||||||
|
pub pattern: Regex,
|
||||||
|
pub pattern_description: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Description {
|
pub struct Description {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const KNOWN_CLASSES: &[&str] = &["processor", "display"];
|
|||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[serde(tag = "class")]
|
#[serde(tag = "class")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
pub enum LshwDevice {
|
pub enum LshwDevice {
|
||||||
Processor(LshwProcessor),
|
Processor(LshwProcessor),
|
||||||
Display(LshwDisplay),
|
Display(LshwDisplay),
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ mod v0_3_6_alpha_3;
|
|||||||
mod v0_3_6_alpha_4;
|
mod v0_3_6_alpha_4;
|
||||||
mod v0_3_6_alpha_5;
|
mod v0_3_6_alpha_5;
|
||||||
mod v0_3_6_alpha_6;
|
mod v0_3_6_alpha_6;
|
||||||
|
mod v0_3_6_alpha_7;
|
||||||
|
|
||||||
pub type Current = v0_3_6_alpha_6::Version; // VERSION_BUMP
|
pub type Current = v0_3_6_alpha_7::Version; // VERSION_BUMP
|
||||||
|
|
||||||
impl Current {
|
impl Current {
|
||||||
#[instrument(skip(self, db))]
|
#[instrument(skip(self, db))]
|
||||||
@@ -102,6 +103,7 @@ enum Version {
|
|||||||
V0_3_6_alpha_4(Wrapper<v0_3_6_alpha_4::Version>),
|
V0_3_6_alpha_4(Wrapper<v0_3_6_alpha_4::Version>),
|
||||||
V0_3_6_alpha_5(Wrapper<v0_3_6_alpha_5::Version>),
|
V0_3_6_alpha_5(Wrapper<v0_3_6_alpha_5::Version>),
|
||||||
V0_3_6_alpha_6(Wrapper<v0_3_6_alpha_6::Version>),
|
V0_3_6_alpha_6(Wrapper<v0_3_6_alpha_6::Version>),
|
||||||
|
V0_3_6_alpha_7(Wrapper<v0_3_6_alpha_7::Version>),
|
||||||
Other(exver::Version),
|
Other(exver::Version),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +134,7 @@ impl Version {
|
|||||||
Self::V0_3_6_alpha_4(v) => DynVersion(Box::new(v.0)),
|
Self::V0_3_6_alpha_4(v) => DynVersion(Box::new(v.0)),
|
||||||
Self::V0_3_6_alpha_5(v) => DynVersion(Box::new(v.0)),
|
Self::V0_3_6_alpha_5(v) => DynVersion(Box::new(v.0)),
|
||||||
Self::V0_3_6_alpha_6(v) => DynVersion(Box::new(v.0)),
|
Self::V0_3_6_alpha_6(v) => DynVersion(Box::new(v.0)),
|
||||||
|
Self::V0_3_6_alpha_7(v) => DynVersion(Box::new(v.0)),
|
||||||
Self::Other(v) => {
|
Self::Other(v) => {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
eyre!("unknown version {v}"),
|
eyre!("unknown version {v}"),
|
||||||
@@ -154,6 +157,7 @@ impl Version {
|
|||||||
Version::V0_3_6_alpha_4(Wrapper(x)) => x.semver(),
|
Version::V0_3_6_alpha_4(Wrapper(x)) => x.semver(),
|
||||||
Version::V0_3_6_alpha_5(Wrapper(x)) => x.semver(),
|
Version::V0_3_6_alpha_5(Wrapper(x)) => x.semver(),
|
||||||
Version::V0_3_6_alpha_6(Wrapper(x)) => x.semver(),
|
Version::V0_3_6_alpha_6(Wrapper(x)) => x.semver(),
|
||||||
|
Version::V0_3_6_alpha_7(Wrapper(x)) => x.semver(),
|
||||||
Version::Other(x) => x.clone(),
|
Version::Other(x) => x.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,15 +176,19 @@ fn version_accessor(db: &mut Value) -> Option<&mut Value> {
|
|||||||
fn version_compat_accessor(db: &mut Value) -> Option<&mut Value> {
|
fn version_compat_accessor(db: &mut Value) -> Option<&mut Value> {
|
||||||
if db.get("public").is_some() {
|
if db.get("public").is_some() {
|
||||||
let server_info = db.get_mut("public")?.get_mut("serverInfo")?;
|
let server_info = db.get_mut("public")?.get_mut("serverInfo")?;
|
||||||
if server_info.get("versionCompat").is_some() {
|
if server_info.get("packageVersionCompat").is_some() {
|
||||||
server_info.get_mut("versionCompat")
|
server_info.get_mut("packageVersionCompat")
|
||||||
} else {
|
} else {
|
||||||
if let Some(prev) = server_info.get("eosVersionCompat").cloned() {
|
if let Some(prev) = server_info.get("eosVersionCompat").cloned() {
|
||||||
server_info
|
server_info
|
||||||
.as_object_mut()?
|
.as_object_mut()?
|
||||||
.insert("versionCompat".into(), prev);
|
.insert("packageVersionCompat".into(), prev);
|
||||||
|
} else if let Some(prev) = server_info.get("versionCompat").cloned() {
|
||||||
|
server_info
|
||||||
|
.as_object_mut()?
|
||||||
|
.insert("packageVersionCompat".into(), prev);
|
||||||
}
|
}
|
||||||
server_info.get_mut("versionCompat")
|
server_info.get_mut("packageVersionCompat")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
db.get_mut("server-info")?.get_mut("eos-version-compat")
|
db.get_mut("server-info")?.get_mut("eos-version-compat")
|
||||||
|
|||||||
50
core/startos/src/version/v0_3_6_alpha_7.rs
Normal file
50
core/startos/src/version/v0_3_6_alpha_7.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use exver::{PreReleaseSegment, VersionRange};
|
||||||
|
use imbl_value::{json, InOMap};
|
||||||
|
|
||||||
|
use super::v0_3_5::V0_3_0_COMPAT;
|
||||||
|
use super::{v0_3_6_alpha_6, VersionT};
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref V0_3_6_alpha_7: exver::Version = exver::Version::new(
|
||||||
|
[0, 3, 6],
|
||||||
|
[PreReleaseSegment::String("alpha".into()), 7.into()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Version;
|
||||||
|
|
||||||
|
impl VersionT for Version {
|
||||||
|
type Previous = v0_3_6_alpha_6::Version;
|
||||||
|
type PreUpRes = ();
|
||||||
|
|
||||||
|
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn semver(self) -> exver::Version {
|
||||||
|
V0_3_6_alpha_7.clone()
|
||||||
|
}
|
||||||
|
fn compat(self) -> &'static VersionRange {
|
||||||
|
&V0_3_0_COMPAT
|
||||||
|
}
|
||||||
|
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
|
||||||
|
let server_info = db["public"]["serverInfo"]
|
||||||
|
.as_object_mut()
|
||||||
|
.or_not_found("public.serverInfo")?;
|
||||||
|
server_info.insert("ram".into(), json!(0));
|
||||||
|
server_info.insert("devices".into(), json!([]));
|
||||||
|
let package_data = db["public"]["packageData"]
|
||||||
|
.as_object_mut()
|
||||||
|
.or_not_found("public.packageData")?;
|
||||||
|
for (_, pde) in package_data.iter_mut() {
|
||||||
|
if let Some(manifest) = pde["stateInfo"].get_mut("manifest") {
|
||||||
|
manifest["hardwareRequirements"]["device"] = json!([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
7
sdk/base/lib/osBindings/DeviceFilter.ts
Normal file
7
sdk/base/lib/osBindings/DeviceFilter.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export type DeviceFilter = {
|
||||||
|
class: "processor" | "display"
|
||||||
|
pattern: string
|
||||||
|
patternDescription: string
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { DeviceFilter } from "./DeviceFilter"
|
||||||
|
|
||||||
export type HardwareRequirements = {
|
export type HardwareRequirements = {
|
||||||
device: { display?: string; processor?: string }
|
device: Array<DeviceFilter>
|
||||||
ram: number | null
|
ram: number | null
|
||||||
arch: string[] | null
|
arch: string[] | null
|
||||||
}
|
}
|
||||||
|
|||||||
7
sdk/base/lib/osBindings/LshwDevice.ts
Normal file
7
sdk/base/lib/osBindings/LshwDevice.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { LshwDisplay } from "./LshwDisplay"
|
||||||
|
import type { LshwProcessor } from "./LshwProcessor"
|
||||||
|
|
||||||
|
export type LshwDevice =
|
||||||
|
| ({ class: "processor" } & LshwProcessor)
|
||||||
|
| ({ class: "display" } & LshwDisplay)
|
||||||
3
sdk/base/lib/osBindings/LshwDisplay.ts
Normal file
3
sdk/base/lib/osBindings/LshwDisplay.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export type LshwDisplay = { product: string }
|
||||||
3
sdk/base/lib/osBindings/LshwProcessor.ts
Normal file
3
sdk/base/lib/osBindings/LshwProcessor.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export type LshwProcessor = { product: string }
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { Governor } from "./Governor"
|
import type { Governor } from "./Governor"
|
||||||
import type { IpInfo } from "./IpInfo"
|
import type { IpInfo } from "./IpInfo"
|
||||||
|
import type { LshwDevice } from "./LshwDevice"
|
||||||
import type { ServerStatus } from "./ServerStatus"
|
import type { ServerStatus } from "./ServerStatus"
|
||||||
import type { SmtpValue } from "./SmtpValue"
|
import type { SmtpValue } from "./SmtpValue"
|
||||||
import type { WifiInfo } from "./WifiInfo"
|
import type { WifiInfo } from "./WifiInfo"
|
||||||
@@ -11,7 +12,7 @@ export type ServerInfo = {
|
|||||||
id: string
|
id: string
|
||||||
hostname: string
|
hostname: string
|
||||||
version: string
|
version: string
|
||||||
versionCompat: string
|
packageVersionCompat: string
|
||||||
postInitMigrationTodos: string[]
|
postInitMigrationTodos: string[]
|
||||||
lastBackup: string | null
|
lastBackup: string | null
|
||||||
lanAddress: string
|
lanAddress: string
|
||||||
@@ -31,4 +32,6 @@ export type ServerInfo = {
|
|||||||
zram: boolean
|
zram: boolean
|
||||||
governor: Governor | null
|
governor: Governor | null
|
||||||
smtp: SmtpValue | null
|
smtp: SmtpValue | null
|
||||||
|
ram: number
|
||||||
|
devices: Array<LshwDevice>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export { DependencyRequirement } from "./DependencyRequirement"
|
|||||||
export { DepInfo } from "./DepInfo"
|
export { DepInfo } from "./DepInfo"
|
||||||
export { Description } from "./Description"
|
export { Description } from "./Description"
|
||||||
export { DestroySubcontainerFsParams } from "./DestroySubcontainerFsParams"
|
export { DestroySubcontainerFsParams } from "./DestroySubcontainerFsParams"
|
||||||
|
export { DeviceFilter } from "./DeviceFilter"
|
||||||
export { Duration } from "./Duration"
|
export { Duration } from "./Duration"
|
||||||
export { EchoParams } from "./EchoParams"
|
export { EchoParams } from "./EchoParams"
|
||||||
export { EncryptedWire } from "./EncryptedWire"
|
export { EncryptedWire } from "./EncryptedWire"
|
||||||
@@ -111,6 +112,9 @@ export { LanInfo } from "./LanInfo"
|
|||||||
export { ListServiceInterfacesParams } from "./ListServiceInterfacesParams"
|
export { ListServiceInterfacesParams } from "./ListServiceInterfacesParams"
|
||||||
export { ListVersionSignersParams } from "./ListVersionSignersParams"
|
export { ListVersionSignersParams } from "./ListVersionSignersParams"
|
||||||
export { LoginParams } from "./LoginParams"
|
export { LoginParams } from "./LoginParams"
|
||||||
|
export { LshwDevice } from "./LshwDevice"
|
||||||
|
export { LshwDisplay } from "./LshwDisplay"
|
||||||
|
export { LshwProcessor } from "./LshwProcessor"
|
||||||
export { MainStatus } from "./MainStatus"
|
export { MainStatus } from "./MainStatus"
|
||||||
export { Manifest } from "./Manifest"
|
export { Manifest } from "./Manifest"
|
||||||
export { MaybeUtf8String } from "./MaybeUtf8String"
|
export { MaybeUtf8String } from "./MaybeUtf8String"
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export type SDKManifest = {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
readonly hardwareRequirements?: {
|
readonly hardwareRequirements?: {
|
||||||
readonly device?: { display?: RegExp; processor?: RegExp }
|
readonly device?: T.DeviceFilter[]
|
||||||
readonly ram?: number | null
|
readonly ram?: number | null
|
||||||
readonly arch?: string[] | null
|
readonly arch?: string[] | null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,11 +73,7 @@ export function buildManifest<
|
|||||||
stop: manifest.alerts?.stop || null,
|
stop: manifest.alerts?.stop || null,
|
||||||
},
|
},
|
||||||
hardwareRequirements: {
|
hardwareRequirements: {
|
||||||
device: Object.fromEntries(
|
device: manifest.hardwareRequirements?.device || [],
|
||||||
Object.entries(manifest.hardwareRequirements?.device || {}).map(
|
|
||||||
([k, v]) => [k, v.source],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ram: manifest.hardwareRequirements?.ram || null,
|
ram: manifest.hardwareRequirements?.ram || null,
|
||||||
arch:
|
arch:
|
||||||
manifest.hardwareRequirements?.arch === undefined
|
manifest.hardwareRequirements?.arch === undefined
|
||||||
|
|||||||
@@ -27,8 +27,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.published {
|
.published {
|
||||||
margin: 0;
|
margin: 0px;
|
||||||
padding: 4px 0 12px 0;
|
padding: 8px 0 8px 0;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,19 @@ import { Pipe, PipeTransform } from '@angular/core'
|
|||||||
})
|
})
|
||||||
export class ConvertBytesPipe implements PipeTransform {
|
export class ConvertBytesPipe implements PipeTransform {
|
||||||
transform(bytes: number): string {
|
transform(bytes: number): string {
|
||||||
if (bytes === 0) return '0 Bytes'
|
return convertBytes(bytes)
|
||||||
|
|
||||||
const k = 1024
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
||||||
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertBytes(bytes: number): string {
|
||||||
|
if (bytes === 0) return '0 Bytes'
|
||||||
|
|
||||||
|
const k = 1024
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
||||||
|
}
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'durationToSeconds',
|
name: 'durationToSeconds',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { VersionRange, ExtendedVersion, Version } from '@start9labs/start-sdk'
|
import { ExtendedVersion, VersionRange } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -29,12 +29,8 @@ export class Exver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compareOsVersion(current: string, other: string) {
|
|
||||||
return Version.parse(current).compare(Version.parse(other))
|
|
||||||
}
|
|
||||||
|
|
||||||
satisfies(version: string, range: string): boolean {
|
satisfies(version: string, range: string): boolean {
|
||||||
return VersionRange.parse(range).satisfiedBy(ExtendedVersion.parse(version))
|
return ExtendedVersion.parse(version).satisfies(VersionRange.parse(range))
|
||||||
}
|
}
|
||||||
|
|
||||||
getFlavor(version: string): string | null {
|
getFlavor(version: string): string | null {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
DiskBackupTarget,
|
DiskBackupTarget,
|
||||||
} from 'src/app/services/api/api.types'
|
} from 'src/app/services/api/api.types'
|
||||||
import { MappedBackupTarget } from 'src/app/types/mapped-backup-target'
|
import { MappedBackupTarget } from 'src/app/types/mapped-backup-target'
|
||||||
import { Exver, getErrorMessage } from '@start9labs/shared'
|
import { getErrorMessage } from '@start9labs/shared'
|
||||||
import { Version } from '@start9labs/start-sdk'
|
import { Version } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -19,10 +19,7 @@ export class BackupService {
|
|||||||
loading = true
|
loading = true
|
||||||
loadingError: string | IonicSafeString = ''
|
loadingError: string | IonicSafeString = ''
|
||||||
|
|
||||||
constructor(
|
constructor(private readonly embassyApi: ApiService) {}
|
||||||
private readonly embassyApi: ApiService,
|
|
||||||
private readonly exver: Exver,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async getBackupTargets(): Promise<void> {
|
async getBackupTargets(): Promise<void> {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
@@ -58,15 +55,16 @@ export class BackupService {
|
|||||||
|
|
||||||
hasAnyBackup(target: BackupTarget): boolean {
|
hasAnyBackup(target: BackupTarget): boolean {
|
||||||
return Object.values(target.startOs).some(
|
return Object.values(target.startOs).some(
|
||||||
s => this.exver.compareOsVersion(s.version, '0.3.6') !== 'less',
|
s => Version.parse(s.version).compare(Version.parse('0.3.6')) !== 'less',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
hasThisBackup(target: BackupTarget, id: string): boolean {
|
hasThisBackup(target: BackupTarget, id: string): boolean {
|
||||||
return (
|
return (
|
||||||
target.startOs[id] &&
|
target.startOs[id] &&
|
||||||
this.exver.compareOsVersion(target.startOs[id].version, '0.3.6') !==
|
Version.parse(target.startOs[id].version).compare(
|
||||||
'less'
|
Version.parse('0.3.6'),
|
||||||
|
) !== 'less'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { endWith, Observable } from 'rxjs'
|
import { endWith, Observable } from 'rxjs'
|
||||||
import { map } from 'rxjs/operators'
|
import { map } from 'rxjs/operators'
|
||||||
import { Exver } from '@start9labs/shared'
|
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { ConfigService } from '../../../services/config.service'
|
import { ConfigService } from '../../../services/config.service'
|
||||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
|
import { Version } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class RefreshAlertService extends Observable<boolean> {
|
export class RefreshAlertService extends Observable<boolean> {
|
||||||
private readonly stream$ = this.patch.watch$('serverInfo', 'version').pipe(
|
private readonly stream$ = this.patch.watch$('serverInfo', 'version').pipe(
|
||||||
map(
|
map(
|
||||||
version =>
|
version =>
|
||||||
this.exver.compareOsVersion(this.config.version, version) !== 'equal',
|
Version.parse(this.config.version).compare(Version.parse(version)) !==
|
||||||
|
'equal',
|
||||||
),
|
),
|
||||||
endWith(false),
|
endWith(false),
|
||||||
)
|
)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly patch: PatchDB<DataModel>,
|
private readonly patch: PatchDB<DataModel>,
|
||||||
private readonly exver: Exver,
|
|
||||||
private readonly config: ConfigService,
|
private readonly config: ConfigService,
|
||||||
) {
|
) {
|
||||||
super(subscriber => this.stream$.subscribe(subscriber))
|
super(subscriber => this.stream$.subscribe(subscriber))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ConfigService } from 'src/app/services/config.service'
|
|||||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { map } from 'rxjs/operators'
|
import { map } from 'rxjs/operators'
|
||||||
|
import { Version } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
export interface AppRecoverOption extends PackageBackupInfo {
|
export interface AppRecoverOption extends PackageBackupInfo {
|
||||||
id: string
|
id: string
|
||||||
@@ -34,7 +35,10 @@ export class ToOptionsPipe implements PipeTransform {
|
|||||||
id,
|
id,
|
||||||
installed: !!packageData[id],
|
installed: !!packageData[id],
|
||||||
checked: false,
|
checked: false,
|
||||||
newerOS: this.compare(packageBackups[id].osVersion),
|
newerOS:
|
||||||
|
Version.parse(packageBackups[id].osVersion).compare(
|
||||||
|
Version.parse(this.config.version),
|
||||||
|
) === 'greater',
|
||||||
}))
|
}))
|
||||||
.sort((a, b) =>
|
.sort((a, b) =>
|
||||||
b.title.toLowerCase() > a.title.toLowerCase() ? -1 : 1,
|
b.title.toLowerCase() > a.title.toLowerCase() ? -1 : 1,
|
||||||
@@ -42,11 +46,4 @@ export class ToOptionsPipe implements PipeTransform {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private compare(version: string): boolean {
|
|
||||||
// checks to see if backup was made on a newer version of startOS
|
|
||||||
return (
|
|
||||||
this.exver.compareOsVersion(version, this.config.version) === 'greater'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,49 +11,51 @@
|
|||||||
: 'View Installing'
|
: 'View Installing'
|
||||||
}}
|
}}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
<ng-container *ngIf="localPkg; else install">
|
<ng-container *ngIf="!conflict">
|
||||||
<ng-container
|
<ng-container *ngIf="localPkg; else install">
|
||||||
*ngIf="
|
<ng-container
|
||||||
localPkg.stateInfo.state === 'installed' &&
|
*ngIf="
|
||||||
(localPkg | toManifest) as manifest
|
localPkg.stateInfo.state === 'installed' &&
|
||||||
"
|
(localPkg | toManifest) as manifest
|
||||||
>
|
"
|
||||||
<ion-button
|
|
||||||
*ngIf="(manifest.version | compareExver : pkg.version) === -1"
|
|
||||||
expand="block"
|
|
||||||
color="success"
|
|
||||||
(click)="tryInstall()"
|
|
||||||
>
|
>
|
||||||
Update
|
|
||||||
</ion-button>
|
|
||||||
<ion-button
|
|
||||||
*ngIf="(manifest.version | compareExver : pkg.version) === 1"
|
|
||||||
expand="block"
|
|
||||||
color="warning"
|
|
||||||
(click)="tryInstall()"
|
|
||||||
>
|
|
||||||
Downgrade
|
|
||||||
</ion-button>
|
|
||||||
<ng-container *ngIf="showDevTools$ | async">
|
|
||||||
<ion-button
|
<ion-button
|
||||||
*ngIf="(manifest.version | compareExver : pkg.version) === 0"
|
*ngIf="(manifest.version | compareExver : pkg.version) === -1"
|
||||||
expand="block"
|
expand="block"
|
||||||
color="success"
|
color="success"
|
||||||
(click)="tryInstall()"
|
(click)="tryInstall()"
|
||||||
>
|
>
|
||||||
Reinstall
|
Update
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
<ion-button
|
||||||
|
*ngIf="(manifest.version | compareExver : pkg.version) === 1"
|
||||||
|
expand="block"
|
||||||
|
color="warning"
|
||||||
|
(click)="tryInstall()"
|
||||||
|
>
|
||||||
|
Downgrade
|
||||||
|
</ion-button>
|
||||||
|
<ng-container *ngIf="showDevTools$ | async">
|
||||||
|
<ion-button
|
||||||
|
*ngIf="(manifest.version | compareExver : pkg.version) === 0"
|
||||||
|
expand="block"
|
||||||
|
color="success"
|
||||||
|
(click)="tryInstall()"
|
||||||
|
>
|
||||||
|
Reinstall
|
||||||
|
</ion-button>
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template #install>
|
||||||
|
<ion-button
|
||||||
|
expand="block"
|
||||||
|
[color]="localFlavor ? 'warning' : 'success'"
|
||||||
|
(click)="tryInstall()"
|
||||||
|
>
|
||||||
|
{{ localFlavor ? 'Switch' : 'Install' }}
|
||||||
|
</ion-button>
|
||||||
|
</ng-template>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #install>
|
|
||||||
<ion-button
|
|
||||||
expand="block"
|
|
||||||
[color]="localFlavor ? 'warning' : 'success'"
|
|
||||||
(click)="tryInstall()"
|
|
||||||
>
|
|
||||||
{{ localFlavor ? 'Switch' : 'Install' }}
|
|
||||||
</ion-button>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ export class MarketplaceShowControlsComponent {
|
|||||||
@Input()
|
@Input()
|
||||||
localFlavor!: boolean
|
localFlavor!: boolean
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
conflict?: string | null
|
||||||
|
|
||||||
readonly showDevTools$ = this.ClientStorageService.showDevTools$
|
readonly showDevTools$ = this.ClientStorageService.showDevTools$
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -13,12 +13,20 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #show>
|
<ng-template #show>
|
||||||
<marketplace-package [pkg]="pkg"></marketplace-package>
|
<marketplace-package [pkg]="pkg">
|
||||||
|
<ion-item *ngIf="conflict$ | async as conflict" color="warning" class="ion-margin-top">
|
||||||
|
<ion-icon slot="start" name="warning"></ion-icon>
|
||||||
|
<ion-label>
|
||||||
|
<h2 style="font-weight: 600" [innerHTML]="conflict"></h2>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</marketplace-package>
|
||||||
<marketplace-show-controls
|
<marketplace-show-controls
|
||||||
[url]="url"
|
[url]="url"
|
||||||
[pkg]="pkg"
|
[pkg]="pkg"
|
||||||
[localPkg]="localPkg$ | async"
|
[localPkg]="localPkg$ | async"
|
||||||
[localFlavor]="!!(localFlavor$ | async)"
|
[localFlavor]="!!(localFlavor$ | async)"
|
||||||
|
[conflict]="conflict$ | async"
|
||||||
></marketplace-show-controls>
|
></marketplace-show-controls>
|
||||||
|
|
||||||
<marketplace-show-dependent [pkg]="pkg"></marketplace-show-dependent>
|
<marketplace-show-dependent [pkg]="pkg"></marketplace-show-dependent>
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { Exver, getPkgId } from '@start9labs/shared'
|
import { convertBytes, Exver, getPkgId } from '@start9labs/shared'
|
||||||
import {
|
import {
|
||||||
AbstractMarketplaceService,
|
AbstractMarketplaceService,
|
||||||
MarketplacePkg,
|
MarketplacePkg,
|
||||||
} from '@start9labs/marketplace'
|
} from '@start9labs/marketplace'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { combineLatest, Observable } from 'rxjs'
|
import { combineLatest, Observable } from 'rxjs'
|
||||||
import { filter, map, shareReplay, startWith, switchMap } from 'rxjs/operators'
|
import {
|
||||||
|
filter,
|
||||||
|
first,
|
||||||
|
map,
|
||||||
|
pairwise,
|
||||||
|
shareReplay,
|
||||||
|
startWith,
|
||||||
|
switchMap,
|
||||||
|
} from 'rxjs/operators'
|
||||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
import { getManifest } from 'src/app/util/get-package-data'
|
import { getManifest } from 'src/app/util/get-package-data'
|
||||||
|
import { Version, VersionRange } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'marketplace-show',
|
selector: 'marketplace-show',
|
||||||
@@ -25,9 +34,10 @@ export class MarketplaceShowPage {
|
|||||||
this.patch.watch$('packageData', this.pkgId).pipe(filter(Boolean)),
|
this.patch.watch$('packageData', this.pkgId).pipe(filter(Boolean)),
|
||||||
this.route.queryParamMap,
|
this.route.queryParamMap,
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(([pkg, paramMap]) =>
|
map(([localPkg, paramMap]) =>
|
||||||
this.exver.getFlavor(getManifest(pkg).version) === paramMap.get('flavor')
|
this.exver.getFlavor(getManifest(localPkg).version) ===
|
||||||
? pkg
|
paramMap.get('flavor')
|
||||||
|
? localPkg
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
shareReplay({ bufferSize: 1, refCount: true }),
|
shareReplay({ bufferSize: 1, refCount: true }),
|
||||||
@@ -49,6 +59,83 @@ export class MarketplaceShowPage {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
readonly conflict$: Observable<string> = combineLatest([
|
||||||
|
this.pkg$,
|
||||||
|
this.patch.watch$('packageData', this.pkgId).pipe(
|
||||||
|
map(pkg => getManifest(pkg).version),
|
||||||
|
pairwise(),
|
||||||
|
filter(([prev, curr]) => prev !== curr),
|
||||||
|
map(([_, curr]) => curr),
|
||||||
|
),
|
||||||
|
this.patch.watch$('serverInfo').pipe(first()),
|
||||||
|
]).pipe(
|
||||||
|
map(([pkg, localVersion, server]) => {
|
||||||
|
let conflicts: string[] = []
|
||||||
|
|
||||||
|
// OS version
|
||||||
|
if (
|
||||||
|
!Version.parse(pkg.osVersion).satisfies(
|
||||||
|
VersionRange.parse(server.packageVersionCompat),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const compare = Version.parse(pkg.osVersion).compare(
|
||||||
|
Version.parse(server.version),
|
||||||
|
)
|
||||||
|
conflicts.push(
|
||||||
|
compare === 'greater'
|
||||||
|
? `Minimum StartOS version ${pkg.osVersion}. Detected ${server.version}`
|
||||||
|
: `Version ${pkg.version} is outdated and cannot run newer versions of StartOS`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// package version
|
||||||
|
if (
|
||||||
|
localVersion &&
|
||||||
|
pkg.sourceVersion &&
|
||||||
|
!this.exver.satisfies(localVersion, pkg.sourceVersion)
|
||||||
|
) {
|
||||||
|
conflicts.push(
|
||||||
|
`Currently installed version ${localVersion} cannot be upgraded to version ${pkg.version}. Try installing an older version first.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { arch, ram, device } = pkg.hardwareRequirements
|
||||||
|
|
||||||
|
// arch
|
||||||
|
if (arch && !arch.includes(server.arch)) {
|
||||||
|
conflicts.push(
|
||||||
|
`Arch ${server.arch} is not supported. Supported: ${arch.join(
|
||||||
|
', ',
|
||||||
|
)}.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ram
|
||||||
|
if (ram && ram > server.ram) {
|
||||||
|
conflicts.push(
|
||||||
|
`Minimum ${convertBytes(
|
||||||
|
ram,
|
||||||
|
)} of RAM required, detected ${convertBytes(server.ram)}.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// devices
|
||||||
|
conflicts.concat(
|
||||||
|
device
|
||||||
|
.filter(d =>
|
||||||
|
server.devices.some(
|
||||||
|
sd =>
|
||||||
|
d.class === sd.class && !new RegExp(d.pattern).test(sd.product),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map(d => d.patternDescription),
|
||||||
|
)
|
||||||
|
|
||||||
|
return conflicts.join(' ')
|
||||||
|
}),
|
||||||
|
shareReplay({ bufferSize: 1, refCount: true }),
|
||||||
|
)
|
||||||
|
|
||||||
readonly flavors$ = this.route.queryParamMap.pipe(
|
readonly flavors$ = this.route.queryParamMap.pipe(
|
||||||
switchMap(paramMap =>
|
switchMap(paramMap =>
|
||||||
this.marketplaceService.getSelectedStore$().pipe(
|
this.marketplaceService.getSelectedStore$().pipe(
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export module Mock {
|
|||||||
assets: [],
|
assets: [],
|
||||||
volumes: ['main'],
|
volumes: ['main'],
|
||||||
hardwareRequirements: {
|
hardwareRequirements: {
|
||||||
device: {},
|
device: [],
|
||||||
arch: null,
|
arch: null,
|
||||||
ram: null,
|
ram: null,
|
||||||
},
|
},
|
||||||
@@ -174,7 +174,7 @@ export module Mock {
|
|||||||
assets: [],
|
assets: [],
|
||||||
volumes: ['main'],
|
volumes: ['main'],
|
||||||
hardwareRequirements: {
|
hardwareRequirements: {
|
||||||
device: {},
|
device: [],
|
||||||
arch: null,
|
arch: null,
|
||||||
ram: null,
|
ram: null,
|
||||||
},
|
},
|
||||||
@@ -224,7 +224,7 @@ export module Mock {
|
|||||||
assets: [],
|
assets: [],
|
||||||
volumes: ['main'],
|
volumes: ['main'],
|
||||||
hardwareRequirements: {
|
hardwareRequirements: {
|
||||||
device: {},
|
device: [],
|
||||||
arch: null,
|
arch: null,
|
||||||
ram: null,
|
ram: null,
|
||||||
},
|
},
|
||||||
@@ -253,7 +253,7 @@ export module Mock {
|
|||||||
'26.1.0:0.1.0': {
|
'26.1.0:0.1.0': {
|
||||||
title: 'Bitcoin Core',
|
title: 'Bitcoin Core',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||||
@@ -286,7 +286,7 @@ export module Mock {
|
|||||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||||
},
|
},
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||||
@@ -329,7 +329,7 @@ export module Mock {
|
|||||||
'26.1.0:0.1.0': {
|
'26.1.0:0.1.0': {
|
||||||
title: 'Bitcoin Core',
|
title: 'Bitcoin Core',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||||
@@ -362,7 +362,7 @@ export module Mock {
|
|||||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||||
},
|
},
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||||
@@ -407,7 +407,7 @@ export module Mock {
|
|||||||
'0.17.5:0': {
|
'0.17.5:0': {
|
||||||
title: 'LND',
|
title: 'LND',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||||
@@ -463,7 +463,7 @@ export module Mock {
|
|||||||
'0.17.4-beta:1.0-alpha': {
|
'0.17.4-beta:1.0-alpha': {
|
||||||
title: 'LND',
|
title: 'LND',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||||
@@ -521,7 +521,7 @@ export module Mock {
|
|||||||
'0.3.2.6:0': {
|
'0.3.2.6:0': {
|
||||||
title: 'Bitcoin Proxy',
|
title: 'Bitcoin Proxy',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
||||||
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||||
@@ -565,7 +565,7 @@ export module Mock {
|
|||||||
'27.0.0:1.0.0': {
|
'27.0.0:1.0.0': {
|
||||||
title: 'Bitcoin Core',
|
title: 'Bitcoin Core',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||||
@@ -598,7 +598,7 @@ export module Mock {
|
|||||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||||
},
|
},
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||||
@@ -641,7 +641,7 @@ export module Mock {
|
|||||||
'0.18.0:0.0.1': {
|
'0.18.0:0.0.1': {
|
||||||
title: 'LND',
|
title: 'LND',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||||
@@ -697,7 +697,7 @@ export module Mock {
|
|||||||
'0.3.2.7:0': {
|
'0.3.2.7:0': {
|
||||||
title: 'Bitcoin Proxy',
|
title: 'Bitcoin Proxy',
|
||||||
description: mockDescription,
|
description: mockDescription,
|
||||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
hardwareRequirements: { arch: null, device: [], ram: null },
|
||||||
license: 'mit',
|
license: 'mit',
|
||||||
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
||||||
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export const mockPatchData: DataModel = {
|
|||||||
// password is asdfasdf
|
// password is asdfasdf
|
||||||
passwordHash:
|
passwordHash:
|
||||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||||
versionCompat: '>=0.3.0 <=0.3.6',
|
packageVersionCompat: '>=0.3.0 <=0.3.6',
|
||||||
postInitMigrationTodos: [],
|
postInitMigrationTodos: [],
|
||||||
statusInfo: {
|
statusInfo: {
|
||||||
backupProgress: null,
|
backupProgress: null,
|
||||||
@@ -83,6 +83,8 @@ export const mockPatchData: DataModel = {
|
|||||||
selected: null,
|
selected: null,
|
||||||
lastRegion: null,
|
lastRegion: null,
|
||||||
},
|
},
|
||||||
|
ram: 8 * 1024 * 1024 * 1024,
|
||||||
|
devices: [],
|
||||||
},
|
},
|
||||||
packageData: {
|
packageData: {
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
|||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { getServerInfo } from 'src/app/util/get-server-info'
|
import { getServerInfo } from 'src/app/util/get-server-info'
|
||||||
import { DataModel } from './patch-db/data-model'
|
import { DataModel } from './patch-db/data-model'
|
||||||
import { Exver } from '@start9labs/shared'
|
import { Version } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -48,14 +48,14 @@ export class EOSService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly api: ApiService,
|
private readonly api: ApiService,
|
||||||
private readonly patch: PatchDB<DataModel>,
|
private readonly patch: PatchDB<DataModel>,
|
||||||
private readonly exver: Exver,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async loadEos(): Promise<void> {
|
async loadEos(): Promise<void> {
|
||||||
const { version, id } = await getServerInfo(this.patch)
|
const { version, id } = await getServerInfo(this.patch)
|
||||||
this.osUpdate = await this.api.checkOSUpdate({ serverId: id })
|
this.osUpdate = await this.api.checkOSUpdate({ serverId: id })
|
||||||
const updateAvailable =
|
const updateAvailable =
|
||||||
this.exver.compareOsVersion(this.osUpdate.version, version) === 'greater'
|
Version.parse(this.osUpdate.version).compare(Version.parse(version)) ===
|
||||||
|
'greater'
|
||||||
this.updateAvailable$.next(updateAvailable)
|
this.updateAvailable$.next(updateAvailable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user