mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
reorganize package data and write dependencies rpc (#2571)
* wip * finish dependencies * minor fixes
This commit is contained in:
48
core/startos/src/db/model/mod.rs
Normal file
48
core/startos/src/db/model/mod.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use patch_db::HasModel;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::account::AccountInfo;
|
||||
use crate::auth::Sessions;
|
||||
use crate::backup::target::cifs::CifsTargets;
|
||||
use crate::db::model::private::Private;
|
||||
use crate::db::model::public::Public;
|
||||
use crate::net::forward::AvailablePorts;
|
||||
use crate::net::keys::KeyStore;
|
||||
use crate::notifications::Notifications;
|
||||
use crate::prelude::*;
|
||||
use crate::ssh::SshKeys;
|
||||
use crate::util::serde::Pem;
|
||||
|
||||
pub mod package;
|
||||
pub mod private;
|
||||
pub mod public;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct Database {
|
||||
pub public: Public,
|
||||
pub private: Private,
|
||||
}
|
||||
impl Database {
|
||||
pub fn init(account: &AccountInfo) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
public: Public::init(account)?,
|
||||
private: Private {
|
||||
key_store: KeyStore::new(account)?,
|
||||
password: account.password.clone(),
|
||||
ssh_privkey: Pem(account.ssh_key.clone()),
|
||||
ssh_pubkeys: SshKeys::new(),
|
||||
available_ports: AvailablePorts::new(),
|
||||
sessions: Sessions::new(),
|
||||
notifications: Notifications::new(),
|
||||
cifs: CifsTargets::new(),
|
||||
package_stores: BTreeMap::new(),
|
||||
}, // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub type DatabaseModel = Model<Database>;
|
||||
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>,
|
||||
}
|
||||
30
core/startos/src/db/model/private.rs
Normal file
30
core/startos/src/db/model/private.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use models::PackageId;
|
||||
use patch_db::{HasModel, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::auth::Sessions;
|
||||
use crate::backup::target::cifs::CifsTargets;
|
||||
use crate::net::forward::AvailablePorts;
|
||||
use crate::net::keys::KeyStore;
|
||||
use crate::notifications::Notifications;
|
||||
use crate::prelude::*;
|
||||
use crate::ssh::SshKeys;
|
||||
use crate::util::serde::Pem;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct Private {
|
||||
pub key_store: KeyStore,
|
||||
pub password: String, // argon2 hash
|
||||
pub ssh_privkey: Pem<ssh_key::PrivateKey>,
|
||||
pub ssh_pubkeys: SshKeys,
|
||||
pub available_ports: AvailablePorts,
|
||||
pub sessions: Sessions,
|
||||
pub notifications: Notifications,
|
||||
pub cifs: CifsTargets,
|
||||
#[serde(default)]
|
||||
pub package_stores: BTreeMap<PackageId, Value>,
|
||||
}
|
||||
210
core/startos/src/db/model/public.rs
Normal file
210
core/startos/src/db/model/public.rs
Normal file
@@ -0,0 +1,210 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use emver::VersionRange;
|
||||
use imbl_value::InternedString;
|
||||
use ipnet::{Ipv4Net, Ipv6Net};
|
||||
use isocountry::CountryCode;
|
||||
use itertools::Itertools;
|
||||
use models::PackageId;
|
||||
use openssl::hash::MessageDigest;
|
||||
use patch_db::{HasModel, Value};
|
||||
use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use torut::onion::OnionAddressV3;
|
||||
|
||||
use crate::account::AccountInfo;
|
||||
use crate::db::model::package::AllPackageData;
|
||||
use crate::net::utils::{get_iface_ipv4_addr, get_iface_ipv6_addr};
|
||||
use crate::prelude::*;
|
||||
use crate::util::cpupower::Governor;
|
||||
use crate::util::Version;
|
||||
use crate::version::{Current, VersionT};
|
||||
use crate::{ARCH, PLATFORM};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
// #[macro_debug]
|
||||
pub struct Public {
|
||||
pub server_info: ServerInfo,
|
||||
pub package_data: AllPackageData,
|
||||
pub ui: Value,
|
||||
}
|
||||
impl Public {
|
||||
pub fn init(account: &AccountInfo) -> Result<Self, Error> {
|
||||
let lan_address = account.hostname.lan_address().parse().unwrap();
|
||||
Ok(Self {
|
||||
server_info: ServerInfo {
|
||||
arch: get_arch(),
|
||||
platform: get_platform(),
|
||||
id: account.server_id.clone(),
|
||||
version: Current::new().semver().into(),
|
||||
hostname: account.hostname.no_dot_host_name(),
|
||||
last_backup: None,
|
||||
last_wifi_region: None,
|
||||
eos_version_compat: Current::new().compat().clone(),
|
||||
lan_address,
|
||||
onion_address: account.tor_key.public().get_onion_address(),
|
||||
tor_address: format!("https://{}", account.tor_key.public().get_onion_address())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
ip_info: BTreeMap::new(),
|
||||
status_info: ServerStatus {
|
||||
backup_progress: None,
|
||||
updated: false,
|
||||
update_progress: None,
|
||||
shutting_down: false,
|
||||
restarting: false,
|
||||
},
|
||||
wifi: WifiInfo {
|
||||
ssids: Vec::new(),
|
||||
connected: None,
|
||||
selected: None,
|
||||
},
|
||||
unread_notification_count: 0,
|
||||
connection_addresses: ConnectionAddresses {
|
||||
tor: Vec::new(),
|
||||
clearnet: Vec::new(),
|
||||
},
|
||||
password_hash: account.password.clone(),
|
||||
pubkey: ssh_key::PublicKey::from(&account.ssh_key)
|
||||
.to_openssh()
|
||||
.unwrap(),
|
||||
ca_fingerprint: account
|
||||
.root_ca_cert
|
||||
.digest(MessageDigest::sha256())
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| format!("{x:X}"))
|
||||
.join(":"),
|
||||
ntp_synced: false,
|
||||
zram: true,
|
||||
governor: None,
|
||||
},
|
||||
package_data: AllPackageData::default(),
|
||||
ui: serde_json::from_str(include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../web/patchdb-ui-seed.json"
|
||||
)))
|
||||
.with_kind(ErrorKind::Deserialization)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_arch() -> InternedString {
|
||||
(*ARCH).into()
|
||||
}
|
||||
|
||||
fn get_platform() -> InternedString {
|
||||
(&*PLATFORM).into()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct ServerInfo {
|
||||
#[serde(default = "get_arch")]
|
||||
pub arch: InternedString,
|
||||
#[serde(default = "get_platform")]
|
||||
pub platform: InternedString,
|
||||
pub id: String,
|
||||
pub hostname: String,
|
||||
pub version: Version,
|
||||
pub last_backup: Option<DateTime<Utc>>,
|
||||
/// Used in the wifi to determine the region to set the system to
|
||||
pub last_wifi_region: Option<CountryCode>,
|
||||
pub eos_version_compat: VersionRange,
|
||||
pub lan_address: Url,
|
||||
pub onion_address: OnionAddressV3,
|
||||
/// for backwards compatibility
|
||||
pub tor_address: Url,
|
||||
pub ip_info: BTreeMap<String, IpInfo>,
|
||||
#[serde(default)]
|
||||
pub status_info: ServerStatus,
|
||||
pub wifi: WifiInfo,
|
||||
pub unread_notification_count: u64,
|
||||
pub connection_addresses: ConnectionAddresses,
|
||||
pub password_hash: String,
|
||||
pub pubkey: String,
|
||||
pub ca_fingerprint: String,
|
||||
#[serde(default)]
|
||||
pub ntp_synced: bool,
|
||||
#[serde(default)]
|
||||
pub zram: bool,
|
||||
pub governor: Option<Governor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct IpInfo {
|
||||
pub ipv4_range: Option<Ipv4Net>,
|
||||
pub ipv4: Option<Ipv4Addr>,
|
||||
pub ipv6_range: Option<Ipv6Net>,
|
||||
pub ipv6: Option<Ipv6Addr>,
|
||||
}
|
||||
impl IpInfo {
|
||||
pub async fn for_interface(iface: &str) -> Result<Self, Error> {
|
||||
let (ipv4, ipv4_range) = get_iface_ipv4_addr(iface).await?.unzip();
|
||||
let (ipv6, ipv6_range) = get_iface_ipv6_addr(iface).await?.unzip();
|
||||
Ok(Self {
|
||||
ipv4_range,
|
||||
ipv4,
|
||||
ipv6_range,
|
||||
ipv6,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel)]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct BackupProgress {
|
||||
pub complete: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct ServerStatus {
|
||||
pub backup_progress: Option<BTreeMap<PackageId, BackupProgress>>,
|
||||
pub updated: bool,
|
||||
pub update_progress: Option<UpdateProgress>,
|
||||
#[serde(default)]
|
||||
pub shutting_down: bool,
|
||||
#[serde(default)]
|
||||
pub restarting: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct UpdateProgress {
|
||||
pub size: Option<u64>,
|
||||
pub downloaded: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct WifiInfo {
|
||||
pub ssids: Vec<String>,
|
||||
pub selected: Option<String>,
|
||||
pub connected: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ServerSpecs {
|
||||
pub cpu: String,
|
||||
pub disk: String,
|
||||
pub memory: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ConnectionAddresses {
|
||||
pub tor: Vec<String>,
|
||||
pub clearnet: Vec<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user