mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
reorganize package data and write dependencies rpc (#2571)
* wip * finish dependencies * minor fixes
This commit is contained in:
424
core/startos/src/db/model/package.rs
Normal file
424
core/startos/src/db/model/package.rs
Normal file
@@ -0,0 +1,424 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use imbl_value::InternedString;
|
||||
use models::{DataUrl, HealthCheckId, HostId, PackageId};
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use patch_db::HasModel;
|
||||
use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::net::host::HostInfo;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::FullProgress;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
use crate::status::Status;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct AllPackageData(pub BTreeMap<PackageId, PackageDataEntry>);
|
||||
impl Map for AllPackageData {
|
||||
type Key = PackageId;
|
||||
type Value = PackageDataEntry;
|
||||
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
|
||||
Ok(key)
|
||||
}
|
||||
fn key_string(key: &Self::Key) -> Result<InternedString, Error> {
|
||||
Ok(key.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ManifestPreference {
|
||||
Old,
|
||||
New,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(tag = "state")]
|
||||
#[model = "Model<Self>"]
|
||||
pub enum PackageState {
|
||||
Installing(InstallingState),
|
||||
Restoring(InstallingState),
|
||||
Updating(UpdatingState),
|
||||
Installed(InstalledState),
|
||||
Removing(InstalledState),
|
||||
}
|
||||
impl PackageState {
|
||||
pub fn expect_installed(&self) -> Result<&InstalledState, Error> {
|
||||
match self {
|
||||
Self::Installed(a) => Ok(a),
|
||||
a => Err(Error::new(
|
||||
eyre!(
|
||||
"Package {} is not in installed state",
|
||||
self.as_manifest(ManifestPreference::Old).id
|
||||
),
|
||||
ErrorKind::InvalidRequest,
|
||||
)),
|
||||
}
|
||||
}
|
||||
pub fn into_installing_info(self) -> Option<InstallingInfo> {
|
||||
match self {
|
||||
Self::Installing(InstallingState { installing_info })
|
||||
| Self::Restoring(InstallingState { installing_info }) => Some(installing_info),
|
||||
Self::Updating(UpdatingState {
|
||||
installing_info, ..
|
||||
}) => Some(installing_info),
|
||||
Self::Installed(_) | Self::Removing(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn as_installing_info(&self) -> Option<&InstallingInfo> {
|
||||
match self {
|
||||
Self::Installing(InstallingState { installing_info })
|
||||
| Self::Restoring(InstallingState { installing_info }) => Some(installing_info),
|
||||
Self::Updating(UpdatingState {
|
||||
installing_info, ..
|
||||
}) => Some(installing_info),
|
||||
Self::Installed(_) | Self::Removing(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn as_installing_info_mut(&mut self) -> Option<&mut InstallingInfo> {
|
||||
match self {
|
||||
Self::Installing(InstallingState { installing_info })
|
||||
| Self::Restoring(InstallingState { installing_info }) => Some(installing_info),
|
||||
Self::Updating(UpdatingState {
|
||||
installing_info, ..
|
||||
}) => Some(installing_info),
|
||||
Self::Installed(_) | Self::Removing(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn into_manifest(self, preference: ManifestPreference) -> Manifest {
|
||||
match self {
|
||||
Self::Installing(InstallingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
})
|
||||
| Self::Restoring(InstallingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
}) => new_manifest,
|
||||
Self::Updating(UpdatingState { manifest, .. })
|
||||
if preference == ManifestPreference::Old =>
|
||||
{
|
||||
manifest
|
||||
}
|
||||
Self::Updating(UpdatingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
..
|
||||
}) => new_manifest,
|
||||
Self::Installed(InstalledState { manifest })
|
||||
| Self::Removing(InstalledState { manifest }) => manifest,
|
||||
}
|
||||
}
|
||||
pub fn as_manifest(&self, preference: ManifestPreference) -> &Manifest {
|
||||
match self {
|
||||
Self::Installing(InstallingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
})
|
||||
| Self::Restoring(InstallingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
}) => new_manifest,
|
||||
Self::Updating(UpdatingState { manifest, .. })
|
||||
if preference == ManifestPreference::Old =>
|
||||
{
|
||||
manifest
|
||||
}
|
||||
Self::Updating(UpdatingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
..
|
||||
}) => new_manifest,
|
||||
Self::Installed(InstalledState { manifest })
|
||||
| Self::Removing(InstalledState { manifest }) => manifest,
|
||||
}
|
||||
}
|
||||
pub fn as_manifest_mut(&mut self, preference: ManifestPreference) -> &mut Manifest {
|
||||
match self {
|
||||
Self::Installing(InstallingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
})
|
||||
| Self::Restoring(InstallingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
}) => new_manifest,
|
||||
Self::Updating(UpdatingState { manifest, .. })
|
||||
if preference == ManifestPreference::Old =>
|
||||
{
|
||||
manifest
|
||||
}
|
||||
Self::Updating(UpdatingState {
|
||||
installing_info: InstallingInfo { new_manifest, .. },
|
||||
..
|
||||
}) => new_manifest,
|
||||
Self::Installed(InstalledState { manifest })
|
||||
| Self::Removing(InstalledState { manifest }) => manifest,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Model<PackageState> {
|
||||
pub fn expect_installed(&self) -> Result<&Model<InstalledState>, Error> {
|
||||
match self.as_match() {
|
||||
PackageStateMatchModelRef::Installed(a) => Ok(a),
|
||||
a => Err(Error::new(
|
||||
eyre!(
|
||||
"Package {} is not in installed state",
|
||||
self.as_manifest(ManifestPreference::Old).as_id().de()?
|
||||
),
|
||||
ErrorKind::InvalidRequest,
|
||||
)),
|
||||
}
|
||||
}
|
||||
pub fn into_installing_info(self) -> Option<Model<InstallingInfo>> {
|
||||
match self.into_match() {
|
||||
PackageStateMatchModel::Installing(s) | PackageStateMatchModel::Restoring(s) => {
|
||||
Some(s.into_installing_info())
|
||||
}
|
||||
PackageStateMatchModel::Updating(s) => Some(s.into_installing_info()),
|
||||
PackageStateMatchModel::Installed(_) | PackageStateMatchModel::Removing(_) => None,
|
||||
PackageStateMatchModel::Error(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn as_installing_info(&self) -> Option<&Model<InstallingInfo>> {
|
||||
match self.as_match() {
|
||||
PackageStateMatchModelRef::Installing(s) | PackageStateMatchModelRef::Restoring(s) => {
|
||||
Some(s.as_installing_info())
|
||||
}
|
||||
PackageStateMatchModelRef::Updating(s) => Some(s.as_installing_info()),
|
||||
PackageStateMatchModelRef::Installed(_) | PackageStateMatchModelRef::Removing(_) => {
|
||||
None
|
||||
}
|
||||
PackageStateMatchModelRef::Error(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn as_installing_info_mut(&mut self) -> Option<&mut Model<InstallingInfo>> {
|
||||
match self.as_match_mut() {
|
||||
PackageStateMatchModelMut::Installing(s) | PackageStateMatchModelMut::Restoring(s) => {
|
||||
Some(s.as_installing_info_mut())
|
||||
}
|
||||
PackageStateMatchModelMut::Updating(s) => Some(s.as_installing_info_mut()),
|
||||
PackageStateMatchModelMut::Installed(_) | PackageStateMatchModelMut::Removing(_) => {
|
||||
None
|
||||
}
|
||||
PackageStateMatchModelMut::Error(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn into_manifest(self, preference: ManifestPreference) -> Model<Manifest> {
|
||||
match self.into_match() {
|
||||
PackageStateMatchModel::Installing(s) | PackageStateMatchModel::Restoring(s) => {
|
||||
s.into_installing_info().into_new_manifest()
|
||||
}
|
||||
PackageStateMatchModel::Updating(s) if preference == ManifestPreference::Old => {
|
||||
s.into_manifest()
|
||||
}
|
||||
PackageStateMatchModel::Updating(s) => s.into_installing_info().into_new_manifest(),
|
||||
PackageStateMatchModel::Installed(s) | PackageStateMatchModel::Removing(s) => {
|
||||
s.into_manifest()
|
||||
}
|
||||
PackageStateMatchModel::Error(_) => Value::Null.into(),
|
||||
}
|
||||
}
|
||||
pub fn as_manifest(&self, preference: ManifestPreference) -> &Model<Manifest> {
|
||||
match self.as_match() {
|
||||
PackageStateMatchModelRef::Installing(s) | PackageStateMatchModelRef::Restoring(s) => {
|
||||
s.as_installing_info().as_new_manifest()
|
||||
}
|
||||
PackageStateMatchModelRef::Updating(s) if preference == ManifestPreference::Old => {
|
||||
s.as_manifest()
|
||||
}
|
||||
PackageStateMatchModelRef::Updating(s) => s.as_installing_info().as_new_manifest(),
|
||||
PackageStateMatchModelRef::Installed(s) | PackageStateMatchModelRef::Removing(s) => {
|
||||
s.as_manifest()
|
||||
}
|
||||
PackageStateMatchModelRef::Error(_) => (&Value::Null).into(),
|
||||
}
|
||||
}
|
||||
pub fn as_manifest_mut(
|
||||
&mut self,
|
||||
preference: ManifestPreference,
|
||||
) -> Result<&mut Model<Manifest>, Error> {
|
||||
Ok(match self.as_match_mut() {
|
||||
PackageStateMatchModelMut::Installing(s) | PackageStateMatchModelMut::Restoring(s) => {
|
||||
s.as_installing_info_mut().as_new_manifest_mut()
|
||||
}
|
||||
PackageStateMatchModelMut::Updating(s) if preference == ManifestPreference::Old => {
|
||||
s.as_manifest_mut()
|
||||
}
|
||||
PackageStateMatchModelMut::Updating(s) => {
|
||||
s.as_installing_info_mut().as_new_manifest_mut()
|
||||
}
|
||||
PackageStateMatchModelMut::Installed(s) | PackageStateMatchModelMut::Removing(s) => {
|
||||
s.as_manifest_mut()
|
||||
}
|
||||
PackageStateMatchModelMut::Error(s) => {
|
||||
return Err(Error::new(
|
||||
eyre!("could not determine package state to get manifest"),
|
||||
ErrorKind::Database,
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct InstallingState {
|
||||
pub installing_info: InstallingInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct UpdatingState {
|
||||
pub manifest: Manifest,
|
||||
pub installing_info: InstallingInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct InstalledState {
|
||||
pub manifest: Manifest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct InstallingInfo {
|
||||
pub new_manifest: Manifest,
|
||||
pub progress: FullProgress,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct PackageDataEntry {
|
||||
pub state_info: PackageState,
|
||||
pub status: Status,
|
||||
pub marketplace_url: Option<Url>,
|
||||
#[serde(default)]
|
||||
#[serde(with = "crate::util::serde::ed25519_pubkey")]
|
||||
pub developer_key: ed25519_dalek::VerifyingKey,
|
||||
pub icon: DataUrl<'static>,
|
||||
pub last_backup: Option<DateTime<Utc>>,
|
||||
pub dependency_info: BTreeMap<PackageId, StaticDependencyInfo>,
|
||||
pub current_dependents: CurrentDependents,
|
||||
pub current_dependencies: CurrentDependencies,
|
||||
pub interface_addresses: InterfaceAddressMap,
|
||||
pub hosts: HostInfo,
|
||||
pub store_exposed_ui: Vec<ExposedUI>,
|
||||
pub store_exposed_dependents: Vec<JsonPointer>,
|
||||
}
|
||||
impl AsRef<PackageDataEntry> for PackageDataEntry {
|
||||
fn as_ref(&self) -> &PackageDataEntry {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct ExposedDependent {
|
||||
path: String,
|
||||
title: String,
|
||||
description: Option<String>,
|
||||
masked: Option<bool>,
|
||||
copyable: Option<bool>,
|
||||
qr: Option<bool>,
|
||||
}
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS)]
|
||||
#[model = "Model<Self>"]
|
||||
#[ts(export)]
|
||||
pub struct ExposedUI {
|
||||
#[ts(type = "string")]
|
||||
pub path: JsonPointer,
|
||||
pub title: String,
|
||||
pub description: Option<String>,
|
||||
pub masked: Option<bool>,
|
||||
pub copyable: Option<bool>,
|
||||
pub qr: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct CurrentDependents(pub BTreeMap<PackageId, CurrentDependencyInfo>);
|
||||
impl CurrentDependents {
|
||||
pub fn map(
|
||||
mut self,
|
||||
transform: impl Fn(
|
||||
BTreeMap<PackageId, CurrentDependencyInfo>,
|
||||
) -> BTreeMap<PackageId, CurrentDependencyInfo>,
|
||||
) -> Self {
|
||||
self.0 = transform(self.0);
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Map for CurrentDependents {
|
||||
type Key = PackageId;
|
||||
type Value = CurrentDependencyInfo;
|
||||
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
|
||||
Ok(key)
|
||||
}
|
||||
fn key_string(key: &Self::Key) -> Result<InternedString, Error> {
|
||||
Ok(key.clone().into())
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct CurrentDependencies(pub BTreeMap<PackageId, CurrentDependencyInfo>);
|
||||
impl CurrentDependencies {
|
||||
pub fn map(
|
||||
mut self,
|
||||
transform: impl Fn(
|
||||
BTreeMap<PackageId, CurrentDependencyInfo>,
|
||||
) -> BTreeMap<PackageId, CurrentDependencyInfo>,
|
||||
) -> Self {
|
||||
self.0 = transform(self.0);
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Map for CurrentDependencies {
|
||||
type Key = PackageId;
|
||||
type Value = CurrentDependencyInfo;
|
||||
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
|
||||
Ok(key)
|
||||
}
|
||||
fn key_string(key: &Self::Key) -> Result<InternedString, Error> {
|
||||
Ok(key.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct StaticDependencyInfo {
|
||||
pub title: String,
|
||||
pub icon: DataUrl<'static>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum CurrentDependencyInfo {
|
||||
Exists,
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
Running {
|
||||
#[serde(default)]
|
||||
health_checks: BTreeSet<HealthCheckId>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct InterfaceAddressMap(pub BTreeMap<HostId, InterfaceAddresses>);
|
||||
impl Map for InterfaceAddressMap {
|
||||
type Key = HostId;
|
||||
type Value = InterfaceAddresses;
|
||||
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
|
||||
Ok(key)
|
||||
}
|
||||
fn key_string(key: &Self::Key) -> Result<InternedString, Error> {
|
||||
Ok(key.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct InterfaceAddresses {
|
||||
pub tor_address: Option<String>,
|
||||
pub lan_address: Option<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user