mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
take marketplace as an argument from the frontend
This commit is contained in:
committed by
Aiden McClelland
parent
9d3f0a9d2b
commit
5c7c23459d
@@ -18,7 +18,6 @@ use super::PackageBackupReport;
|
||||
use crate::auth::check_password_against_db;
|
||||
use crate::backup::{BackupReport, ServerBackupReport};
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::ServerStatus;
|
||||
use crate::db::util::WithRevision;
|
||||
use crate::disk::mount::backup::BackupMountGuard;
|
||||
use crate::disk::mount::guard::TmpMountGuard;
|
||||
@@ -134,7 +133,10 @@ pub async fn backup_all(
|
||||
let revision = assure_backing_up(&mut db).await?;
|
||||
tokio::task::spawn(async move {
|
||||
let backup_res = perform_backup(&ctx, &mut db, backup_guard).await;
|
||||
let status_model = crate::db::DatabaseModel::new().server_info().status();
|
||||
let status_model = crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.status_info()
|
||||
.backing_up();
|
||||
status_model
|
||||
.clone()
|
||||
.lock(&mut db, LockType::Write)
|
||||
@@ -203,7 +205,7 @@ pub async fn backup_all(
|
||||
}
|
||||
}
|
||||
status_model
|
||||
.put(&mut db, &ServerStatus::Running)
|
||||
.put(&mut db, &false)
|
||||
.await
|
||||
.expect("failed to change server status");
|
||||
});
|
||||
@@ -216,33 +218,21 @@ pub async fn backup_all(
|
||||
#[instrument(skip(db))]
|
||||
async fn assure_backing_up(db: &mut PatchDbHandle) -> Result<Option<Arc<Revision>>, Error> {
|
||||
let mut tx = db.begin().await?;
|
||||
let mut info = crate::db::DatabaseModel::new()
|
||||
let mut backing_up = crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.status_info()
|
||||
.backing_up()
|
||||
.get_mut(&mut tx)
|
||||
.await?;
|
||||
match &info.status {
|
||||
ServerStatus::Updating => {
|
||||
return Err(Error::new(
|
||||
eyre!("Server is updating!"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
))
|
||||
}
|
||||
ServerStatus::Updated => {
|
||||
return Err(Error::new(
|
||||
eyre!("Server is updated and needs to be reset"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
))
|
||||
}
|
||||
ServerStatus::BackingUp => {
|
||||
return Err(Error::new(
|
||||
eyre!("Server is already backing up!"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
))
|
||||
}
|
||||
ServerStatus::Running => (),
|
||||
|
||||
if *backing_up {
|
||||
return Err(Error::new(
|
||||
eyre!("Server is already backing up!"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
info.status = ServerStatus::BackingUp;
|
||||
info.save(&mut tx).await?;
|
||||
*backing_up = true;
|
||||
backing_up.save(&mut tx).await?;
|
||||
Ok(tx.commit(None).await?)
|
||||
}
|
||||
|
||||
|
||||
@@ -377,7 +377,7 @@ async fn restore_package<'a>(
|
||||
Ok((
|
||||
progress.clone(),
|
||||
async move {
|
||||
download_install_s9pk(&ctx, &manifest, progress, file).await?;
|
||||
download_install_s9pk(&ctx, &manifest, None, progress, file).await?;
|
||||
|
||||
guard.unmount().await?;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use tracing::instrument;
|
||||
|
||||
use crate::core::rpc_continuations::{RequestGuid, RpcContinuation};
|
||||
use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry};
|
||||
use crate::hostname::{derive_hostname, derive_id, get_hostname, get_id, get_product_key};
|
||||
use crate::hostname::{derive_hostname, derive_id, get_product_key};
|
||||
use crate::install::cleanup::{cleanup_failed, uninstall};
|
||||
use crate::manager::ManagerMap;
|
||||
use crate::middleware::auth::HashSessionToken;
|
||||
@@ -230,31 +230,6 @@ impl RpcContext {
|
||||
tracing::info!("Initialized Package Managers");
|
||||
Ok(res)
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
pub async fn package_registry_url(&self) -> Result<Url, Error> {
|
||||
Ok(
|
||||
if let Some(market) = crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.package_marketplace()
|
||||
.get(&mut self.db.handle(), false)
|
||||
.await?
|
||||
.to_owned()
|
||||
{
|
||||
market
|
||||
} else {
|
||||
self.eos_registry_url().await?
|
||||
},
|
||||
)
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
pub async fn eos_registry_url(&self) -> Result<Url, Error> {
|
||||
Ok(crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.eos_marketplace()
|
||||
.get(&mut self.db.handle(), false)
|
||||
.await?
|
||||
.to_owned())
|
||||
}
|
||||
#[instrument(skip(self, db))]
|
||||
pub async fn set_nginx_conf<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
|
||||
tokio::fs::write("/etc/nginx/sites-available/default", {
|
||||
@@ -266,11 +241,6 @@ impl RpcContext {
|
||||
include_str!("../nginx/main-ui.conf.template"),
|
||||
lan_hostname = info.lan_address.host_str().unwrap(),
|
||||
tor_hostname = info.tor_address.host_str().unwrap(),
|
||||
eos_marketplace = info.eos_marketplace,
|
||||
package_marketplace = info
|
||||
.package_marketplace
|
||||
.as_ref()
|
||||
.unwrap_or(&info.eos_marketplace),
|
||||
)
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -18,7 +18,7 @@ use tracing::instrument;
|
||||
use url::Host;
|
||||
|
||||
use crate::db::model::Database;
|
||||
use crate::hostname::{derive_hostname, derive_id, get_hostname, get_id, get_product_key};
|
||||
use crate::hostname::{derive_hostname, derive_id, get_product_key};
|
||||
use crate::net::tor::os_key;
|
||||
use crate::setup::{password_hash, RecoveryStatus};
|
||||
use crate::util::io::from_toml_async_reader;
|
||||
|
||||
@@ -50,12 +50,11 @@ impl Database {
|
||||
tor_address: format!("http://{}", tor_key.public().get_onion_address())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
status: ServerStatus::Running {},
|
||||
#[cfg(not(feature = "beta"))]
|
||||
eos_marketplace: "https://marketplace.start9.com".parse().unwrap(),
|
||||
#[cfg(feature = "beta")]
|
||||
eos_marketplace: "https://beta-registry-0-3.start9labs.com".parse().unwrap(),
|
||||
package_marketplace: None,
|
||||
status_info: ServerStatus {
|
||||
backing_up: false,
|
||||
updated: false,
|
||||
update_progress: None,
|
||||
},
|
||||
wifi: WifiInfo {
|
||||
ssids: Vec::new(),
|
||||
connected: None,
|
||||
@@ -67,7 +66,6 @@ impl Database {
|
||||
clearnet: Vec::new(),
|
||||
},
|
||||
share_stats: false,
|
||||
update_progress: None,
|
||||
password_hash,
|
||||
},
|
||||
package_data: AllPackageData::default(),
|
||||
@@ -93,25 +91,23 @@ pub struct ServerInfo {
|
||||
pub eos_version_compat: VersionRange,
|
||||
pub lan_address: Url,
|
||||
pub tor_address: Url,
|
||||
pub status: ServerStatus,
|
||||
pub eos_marketplace: Url,
|
||||
pub package_marketplace: Option<Url>, // None implies use eos_marketplace
|
||||
#[model]
|
||||
#[serde(default)]
|
||||
pub status_info: ServerStatus,
|
||||
pub wifi: WifiInfo,
|
||||
pub unread_notification_count: u64,
|
||||
pub connection_addresses: ConnectionAddresses,
|
||||
pub share_stats: bool,
|
||||
#[model]
|
||||
pub update_progress: Option<UpdateProgress>,
|
||||
pub password_hash: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ServerStatus {
|
||||
Running,
|
||||
Updating,
|
||||
Updated,
|
||||
BackingUp,
|
||||
pub struct ServerStatus {
|
||||
pub backing_up: bool,
|
||||
pub updated: bool,
|
||||
#[model]
|
||||
pub update_progress: Option<UpdateProgress>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
@@ -257,6 +253,9 @@ impl PackageDataEntryModel {
|
||||
pub struct InstalledPackageDataEntry {
|
||||
#[model]
|
||||
pub status: Status,
|
||||
pub marketplace_url: Option<Url>,
|
||||
#[serde(default)]
|
||||
pub developer_key: ed25519_dalek::PublicKey,
|
||||
#[model]
|
||||
pub manifest: Manifest,
|
||||
pub last_backup: Option<DateTime<Utc>>,
|
||||
|
||||
@@ -93,15 +93,11 @@ pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error
|
||||
.server_info()
|
||||
.get_mut(&mut handle)
|
||||
.await?;
|
||||
match info.status {
|
||||
ServerStatus::Running | ServerStatus::Updated | ServerStatus::BackingUp => {
|
||||
info.status = ServerStatus::Running;
|
||||
}
|
||||
ServerStatus::Updating => {
|
||||
info.update_progress = None;
|
||||
info.status = ServerStatus::Running;
|
||||
}
|
||||
}
|
||||
info.status_info = ServerStatus {
|
||||
backing_up: false,
|
||||
updated: false,
|
||||
update_progress: None,
|
||||
};
|
||||
info.save(&mut handle).await?;
|
||||
|
||||
crate::version::init(&mut handle).await?;
|
||||
|
||||
@@ -15,6 +15,7 @@ use http::header::CONTENT_LENGTH;
|
||||
use http::{Request, Response, StatusCode};
|
||||
use hyper::Body;
|
||||
use patch_db::{DbHandle, LockType};
|
||||
use reqwest::Url;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{command, Context};
|
||||
use tokio::fs::{File, OpenOptions};
|
||||
@@ -85,18 +86,23 @@ pub async fn list(#[context] ctx: RpcContext) -> Result<Vec<(PackageId, Version)
|
||||
pub async fn install(
|
||||
#[context] ctx: RpcContext,
|
||||
#[arg] id: String,
|
||||
#[arg(rename = "version-spec")] version_spec: Option<String>,
|
||||
#[arg(short = "m", long = "marketplace-url", rename = "marketplace-url")]
|
||||
marketplace_url: Option<Url>,
|
||||
#[arg(short = "v", long = "version-spec", rename = "version-spec")] version_spec: Option<
|
||||
String,
|
||||
>,
|
||||
) -> Result<WithRevision<()>, Error> {
|
||||
let version_str = match &version_spec {
|
||||
None => "*",
|
||||
Some(v) => &*v,
|
||||
};
|
||||
let version: VersionRange = version_str.parse()?;
|
||||
let reg_url = ctx.package_registry_url().await?;
|
||||
let marketplace_url =
|
||||
marketplace_url.unwrap_or_else(|| crate::DEFAULT_MARKETPLACE.parse().unwrap());
|
||||
let (man_res, s9pk) = tokio::try_join!(
|
||||
reqwest::get(format!(
|
||||
"{}/package/manifest/{}?spec={}&eos-version-compat={}&arch={}",
|
||||
reg_url,
|
||||
marketplace_url,
|
||||
id,
|
||||
version,
|
||||
Current::new().compat(),
|
||||
@@ -104,7 +110,7 @@ pub async fn install(
|
||||
)),
|
||||
reqwest::get(format!(
|
||||
"{}/package/{}.s9pk?spec={}&eos-version-compat={}&arch={}",
|
||||
reg_url,
|
||||
marketplace_url,
|
||||
id,
|
||||
version,
|
||||
Current::new().compat(),
|
||||
@@ -112,7 +118,15 @@ pub async fn install(
|
||||
))
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Registry)?;
|
||||
let man: Manifest = man_res.json().await.with_kind(crate::ErrorKind::Registry)?;
|
||||
let man: Manifest = man_res
|
||||
.error_for_status()
|
||||
.with_kind(crate::ErrorKind::Registry)?
|
||||
.json()
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Registry)?;
|
||||
let s9pk = s9pk
|
||||
.error_for_status()
|
||||
.with_kind(crate::ErrorKind::Registry)?;
|
||||
|
||||
if man.id.as_str() != id || !man.version.satisfies(&version) {
|
||||
return Err(Error::new(
|
||||
@@ -166,6 +180,7 @@ pub async fn install(
|
||||
if let Err(e) = download_install_s9pk(
|
||||
&ctx,
|
||||
&man,
|
||||
Some(marketplace_url),
|
||||
InstallProgress::new(s9pk.content_length()),
|
||||
tokio_util::io::StreamReader::new(s9pk.bytes_stream().map_err(|e| {
|
||||
std::io::Error::new(
|
||||
@@ -286,6 +301,7 @@ pub async fn sideload(
|
||||
download_install_s9pk(
|
||||
&new_ctx,
|
||||
&manifest,
|
||||
None,
|
||||
progress,
|
||||
tokio_util::io::StreamReader::new(req.into_body().map_err(|e| {
|
||||
std::io::Error::new(
|
||||
@@ -330,6 +346,7 @@ pub async fn sideload(
|
||||
async fn cli_install(
|
||||
ctx: CliContext,
|
||||
target: String,
|
||||
marketplace_url: Option<Url>,
|
||||
version_spec: Option<String>,
|
||||
) -> Result<(), RpcError> {
|
||||
if target.ends_with(".s9pk") {
|
||||
@@ -373,7 +390,9 @@ async fn cli_install(
|
||||
}
|
||||
} else {
|
||||
let params = match (target.split_once("@"), version_spec) {
|
||||
(Some((pkg, v)), None) => serde_json::json!({ "id": pkg, "version-spec": v }),
|
||||
(Some((pkg, v)), None) => {
|
||||
serde_json::json!({ "id": pkg, "marketplace-url": marketplace_url, "version-spec": v })
|
||||
}
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(crate::Error::new(
|
||||
eyre!("Invalid package id {}", target),
|
||||
@@ -381,8 +400,10 @@ async fn cli_install(
|
||||
)
|
||||
.into())
|
||||
}
|
||||
(None, Some(v)) => serde_json::json!({ "id": target, "version-spec": v }),
|
||||
(None, None) => serde_json::json!({ "id": target }),
|
||||
(None, Some(v)) => {
|
||||
serde_json::json!({ "id": target, "marketplace-url": marketplace_url, "version-spec": v })
|
||||
}
|
||||
(None, None) => serde_json::json!({ "id": target, "marketplace-url": marketplace_url }),
|
||||
};
|
||||
tracing::debug!("calling package.install");
|
||||
rpc_toolkit::command_helpers::call_remote(
|
||||
@@ -489,6 +510,7 @@ pub async fn uninstall_impl(ctx: RpcContext, id: PackageId) -> Result<WithRevisi
|
||||
pub async fn download_install_s9pk(
|
||||
ctx: &RpcContext,
|
||||
temp_manifest: &Manifest,
|
||||
marketplace_url: Option<Url>,
|
||||
progress: Arc<InstallProgress>,
|
||||
mut s9pk: impl AsyncRead + Unpin,
|
||||
) -> Result<(), Error> {
|
||||
@@ -537,7 +559,15 @@ pub async fn download_install_s9pk(
|
||||
})
|
||||
.await?;
|
||||
|
||||
install_s9pk(&ctx, pkg_id, version, &mut s9pk_reader, progress).await?;
|
||||
install_s9pk(
|
||||
&ctx,
|
||||
pkg_id,
|
||||
version,
|
||||
marketplace_url,
|
||||
&mut s9pk_reader,
|
||||
progress,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -563,11 +593,13 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
||||
ctx: &RpcContext,
|
||||
pkg_id: &PackageId,
|
||||
version: &Version,
|
||||
marketplace_url: Option<Url>,
|
||||
rdr: &mut S9pkReader<InstallProgressTracker<R>>,
|
||||
progress: Arc<InstallProgress>,
|
||||
) -> Result<(), Error> {
|
||||
rdr.validate().await?;
|
||||
rdr.validated();
|
||||
let developer_key = rdr.developer_key().clone();
|
||||
rdr.reset().await?;
|
||||
let model = crate::db::DatabaseModel::new()
|
||||
.package_data()
|
||||
@@ -582,78 +614,97 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
||||
|
||||
tracing::info!("Install {}@{}: Fetching Dependency Info", pkg_id, version);
|
||||
let mut dependency_info = BTreeMap::new();
|
||||
let reg_url = ctx.package_registry_url().await?;
|
||||
for (dep, info) in &manifest.dependencies.0 {
|
||||
let manifest: Option<Manifest> = match reqwest::get(format!(
|
||||
"{}/package/manifest/{}?spec={}&eos-version-compat={}&arch={}",
|
||||
reg_url,
|
||||
dep,
|
||||
info.version,
|
||||
Current::new().compat(),
|
||||
platforms::TARGET_ARCH,
|
||||
))
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Registry)?
|
||||
.error_for_status()
|
||||
let manifest: Option<Manifest> = if let Some(local_man) = crate::db::DatabaseModel::new()
|
||||
.package_data()
|
||||
.idx_model(dep)
|
||||
.map::<_, Manifest>(|pde| pde.manifest())
|
||||
.get(&mut ctx.db.handle(), false)
|
||||
.await?
|
||||
.into_owned()
|
||||
{
|
||||
Ok(a) => Ok(Some(
|
||||
a.json()
|
||||
Some(local_man)
|
||||
} else if let Some(marketplace_url) = &marketplace_url {
|
||||
match reqwest::get(format!(
|
||||
"{}/package/manifest/{}?spec={}&eos-version-compat={}&arch={}",
|
||||
marketplace_url,
|
||||
dep,
|
||||
info.version,
|
||||
Current::new().compat(),
|
||||
platforms::TARGET_ARCH,
|
||||
))
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Registry)?
|
||||
.error_for_status()
|
||||
{
|
||||
Ok(a) => Ok(Some(
|
||||
a.json()
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Deserialization)?,
|
||||
)),
|
||||
Err(e) if e.status() == Some(StatusCode::BAD_REQUEST) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
.with_kind(crate::ErrorKind::Registry)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(marketplace_url) = &marketplace_url {
|
||||
if let Some(manifest) = &manifest {
|
||||
let dir = ctx
|
||||
.datadir
|
||||
.join(PKG_PUBLIC_DIR)
|
||||
.join(&manifest.id)
|
||||
.join(manifest.version.as_str());
|
||||
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
|
||||
if tokio::fs::metadata(&icon_path).await.is_err() {
|
||||
tokio::fs::create_dir_all(&dir).await?;
|
||||
let icon = reqwest::get(format!(
|
||||
"{}/package/icon/{}?spec={}&eos-version-compat={}&arch={}",
|
||||
marketplace_url,
|
||||
dep,
|
||||
info.version,
|
||||
Current::new().compat(),
|
||||
platforms::TARGET_ARCH,
|
||||
))
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Deserialization)?,
|
||||
)),
|
||||
Err(e) if e.status() == Some(StatusCode::BAD_REQUEST) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
.with_kind(crate::ErrorKind::Registry)?;
|
||||
if let Some(manifest) = manifest {
|
||||
let dir = ctx
|
||||
.datadir
|
||||
.join(PKG_PUBLIC_DIR)
|
||||
.join(&manifest.id)
|
||||
.join(manifest.version.as_str());
|
||||
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
|
||||
if tokio::fs::metadata(&icon_path).await.is_err() {
|
||||
tokio::fs::create_dir_all(&dir).await?;
|
||||
let icon = reqwest::get(format!(
|
||||
"{}/package/icon/{}?spec={}&eos-version-compat={}&arch={}",
|
||||
reg_url,
|
||||
dep,
|
||||
info.version,
|
||||
Current::new().compat(),
|
||||
platforms::TARGET_ARCH,
|
||||
))
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Registry)?;
|
||||
let mut dst = File::create(&icon_path).await?;
|
||||
tokio::io::copy(
|
||||
&mut tokio_util::io::StreamReader::new(icon.bytes_stream().map_err(|e| {
|
||||
std::io::Error::new(
|
||||
if e.is_connect() {
|
||||
std::io::ErrorKind::ConnectionRefused
|
||||
} else if e.is_timeout() {
|
||||
std::io::ErrorKind::TimedOut
|
||||
} else {
|
||||
std::io::ErrorKind::Other
|
||||
},
|
||||
e,
|
||||
)
|
||||
})),
|
||||
&mut dst,
|
||||
)
|
||||
.await?;
|
||||
dst.sync_all().await?;
|
||||
.with_kind(crate::ErrorKind::Registry)?;
|
||||
let mut dst = File::create(&icon_path).await?;
|
||||
tokio::io::copy(
|
||||
&mut tokio_util::io::StreamReader::new(icon.bytes_stream().map_err(|e| {
|
||||
std::io::Error::new(
|
||||
if e.is_connect() {
|
||||
std::io::ErrorKind::ConnectionRefused
|
||||
} else if e.is_timeout() {
|
||||
std::io::ErrorKind::TimedOut
|
||||
} else {
|
||||
std::io::ErrorKind::Other
|
||||
},
|
||||
e,
|
||||
)
|
||||
})),
|
||||
&mut dst,
|
||||
)
|
||||
.await?;
|
||||
dst.sync_all().await?;
|
||||
}
|
||||
}
|
||||
|
||||
dependency_info.insert(
|
||||
dep.clone(),
|
||||
StaticDependencyInfo {
|
||||
icon: format!(
|
||||
"/public/package-data/{}/{}/icon.{}",
|
||||
manifest.id,
|
||||
manifest.version,
|
||||
manifest.assets.icon_type()
|
||||
),
|
||||
manifest: Some(manifest),
|
||||
icon: if let Some(manifest) = &manifest {
|
||||
format!(
|
||||
"/public/package-data/{}/{}/icon.{}",
|
||||
manifest.id,
|
||||
manifest.version,
|
||||
manifest.assets.icon_type()
|
||||
)
|
||||
} else {
|
||||
"/assets/img/package-icon.png".to_owned()
|
||||
},
|
||||
manifest,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -842,13 +893,36 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
||||
})
|
||||
.collect();
|
||||
let current_dependents = {
|
||||
// search required dependencies
|
||||
let mut deps = BTreeMap::new();
|
||||
for package in crate::db::DatabaseModel::new()
|
||||
.package_data()
|
||||
.keys(&mut tx, true)
|
||||
.await?
|
||||
{
|
||||
// update dependency_info on dependents
|
||||
if let Some(dep_info_model) = crate::db::DatabaseModel::new()
|
||||
.package_data()
|
||||
.idx_model(&package)
|
||||
.expect(&mut tx)
|
||||
.await?
|
||||
.installed()
|
||||
.and_then(|i| i.dependency_info().idx_model(pkg_id))
|
||||
.check(&mut tx)
|
||||
.await?
|
||||
{
|
||||
let mut dep_info = dep_info_model.get_mut(&mut tx).await?;
|
||||
*dep_info = StaticDependencyInfo {
|
||||
icon: format!(
|
||||
"/public/package-data/{}/{}/icon.{}",
|
||||
manifest.id,
|
||||
manifest.version,
|
||||
manifest.assets.icon_type()
|
||||
),
|
||||
manifest: Some(manifest.clone()),
|
||||
};
|
||||
}
|
||||
|
||||
// search required dependencies
|
||||
if let Some(dep) = crate::db::DatabaseModel::new()
|
||||
.package_data()
|
||||
.idx_model(&package)
|
||||
@@ -877,6 +951,8 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
||||
main: MainStatus::Stopped,
|
||||
dependency_errors: DependencyErrors::default(),
|
||||
},
|
||||
marketplace_url,
|
||||
developer_key,
|
||||
manifest: manifest.clone(),
|
||||
last_backup: match &*pde {
|
||||
PackageDataEntry::Updating {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
pub const CONFIG_PATH: &'static str = "/etc/embassy/config.toml";
|
||||
pub const CONFIG_PATH: &str = "/etc/embassy/config.yaml";
|
||||
#[cfg(not(feature = "beta"))]
|
||||
pub const DEFAULT_MARKETPLACE: &str = "https://marketplace.start9.com";
|
||||
#[cfg(feature = "beta")]
|
||||
pub const DEFAULT_MARKETPLACE: &str = "https://beta-registry-0-3.start9labs.com";
|
||||
pub const BUFFER_SIZE: usize = 1024;
|
||||
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
|
||||
|
||||
@@ -78,7 +82,6 @@ pub fn main_api() -> Result<(), RpcError> {
|
||||
shutdown::restart,
|
||||
shutdown::rebuild,
|
||||
update::update_system,
|
||||
marketplace::set_eos_url,
|
||||
))]
|
||||
pub fn server() -> Result<(), RpcError> {
|
||||
Ok(())
|
||||
@@ -98,7 +101,6 @@ pub fn server() -> Result<(), RpcError> {
|
||||
properties::properties,
|
||||
dependencies::dependency,
|
||||
backup::package_backup,
|
||||
marketplace::set_package_url,
|
||||
))]
|
||||
pub fn package() -> Result<(), RpcError> {
|
||||
Ok(())
|
||||
|
||||
@@ -1,43 +1,35 @@
|
||||
use patch_db::{DbHandle, LockType};
|
||||
use reqwest::Url;
|
||||
use color_eyre::eyre::eyre;
|
||||
use reqwest::{StatusCode, Url};
|
||||
use rpc_toolkit::command;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::util::display_none;
|
||||
use crate::Error;
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
#[command(rename = "set-marketplace", display(display_none))]
|
||||
pub async fn set_eos_url(#[context] ctx: RpcContext, #[arg] url: Url) -> Result<(), Error> {
|
||||
let mut db = ctx.db.handle();
|
||||
let mut tx = db.begin().await?;
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.lock(&mut tx, LockType::Write)
|
||||
.await?;
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.eos_marketplace()
|
||||
.put(&mut tx, &url)
|
||||
.await?;
|
||||
ctx.set_nginx_conf(&mut tx).await?;
|
||||
tx.commit(None).await?;
|
||||
#[command(subcommands(get))]
|
||||
pub fn marketplace() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(rename = "set-marketplace", display(display_none))]
|
||||
pub async fn set_package_url(#[context] ctx: RpcContext, #[arg] url: Url) -> Result<(), Error> {
|
||||
let mut db = ctx.db.handle();
|
||||
let mut tx = db.begin().await?;
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.lock(&mut tx, LockType::Write)
|
||||
.await?;
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.package_marketplace()
|
||||
.put(&mut tx, &Some(url))
|
||||
.await?;
|
||||
ctx.set_nginx_conf(&mut tx).await?;
|
||||
tx.commit(None).await?;
|
||||
Ok(())
|
||||
#[command]
|
||||
pub async fn get(#[arg] url: Url) -> Result<Value, Error> {
|
||||
let response = reqwest::get(url)
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Network)?;
|
||||
let status = response.status();
|
||||
if status.is_success() {
|
||||
response
|
||||
.json()
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Deserialization)
|
||||
} else {
|
||||
let message = response.text().await.with_kind(crate::ErrorKind::Network)?;
|
||||
Err(Error::new(
|
||||
eyre!("{}", message),
|
||||
match status {
|
||||
StatusCode::BAD_REQUEST => crate::ErrorKind::InvalidRequest,
|
||||
StatusCode::NOT_FOUND => crate::ErrorKind::NotFound,
|
||||
_ => crate::ErrorKind::Registry,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,18 +45,6 @@ server {{
|
||||
proxy_pass http://127.0.0.1:5961/;
|
||||
}}
|
||||
|
||||
location /marketplace/eos/ {{
|
||||
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
|
||||
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
|
||||
proxy_pass {eos_marketplace}eos/;
|
||||
}}
|
||||
|
||||
location /marketplace/package/ {{
|
||||
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
|
||||
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
|
||||
proxy_pass {package_marketplace}package/;
|
||||
}}
|
||||
|
||||
location / {{
|
||||
try_files $uri $uri/ =404;
|
||||
}}
|
||||
@@ -111,18 +99,6 @@ server {{
|
||||
proxy_pass http://127.0.0.1:5961/;
|
||||
}}
|
||||
|
||||
location /marketplace/eos/ {{
|
||||
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
|
||||
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
|
||||
proxy_pass {eos_marketplace}eos/;
|
||||
}}
|
||||
|
||||
location /marketplace/package/ {{
|
||||
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
|
||||
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
|
||||
proxy_pass {package_marketplace}package/;
|
||||
}}
|
||||
|
||||
location / {{
|
||||
try_files $uri $uri/ =404;
|
||||
}}
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use digest::Output;
|
||||
use ed25519_dalek::PublicKey;
|
||||
use sha2::{Digest, Sha512};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, ReadBuf, Take};
|
||||
@@ -47,6 +48,7 @@ impl<'a, R: AsyncRead + AsyncSeek + Unpin> AsyncRead for ReadHandle<'a, R> {
|
||||
pub struct S9pkReader<R: AsyncRead + AsyncSeek + Unpin = File> {
|
||||
hash: Option<Output<Sha512>>,
|
||||
hash_string: Option<String>,
|
||||
developer_key: PublicKey,
|
||||
toc: TableOfContents,
|
||||
pos: u64,
|
||||
rdr: R,
|
||||
@@ -105,6 +107,7 @@ impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
|
||||
Ok(S9pkReader {
|
||||
hash_string,
|
||||
hash,
|
||||
developer_key: header.pubkey,
|
||||
toc: header.table_of_contents,
|
||||
pos,
|
||||
rdr,
|
||||
@@ -119,6 +122,10 @@ impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
|
||||
self.hash_string.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
pub fn developer_key(&self) -> &PublicKey {
|
||||
&self.developer_key
|
||||
}
|
||||
|
||||
pub async fn reset(&mut self) -> Result<(), Error> {
|
||||
self.rdr.seek(SeekFrom::Start(0)).await?;
|
||||
Ok(())
|
||||
|
||||
@@ -23,7 +23,7 @@ use tokio_stream::StreamExt;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::{ServerStatus, UpdateProgress};
|
||||
use crate::db::model::UpdateProgress;
|
||||
use crate::db::util::WithRevision;
|
||||
use crate::disk::mount::filesystem::block_dev::BlockDev;
|
||||
use crate::disk::mount::filesystem::FileSystem;
|
||||
@@ -48,6 +48,7 @@ lazy_static! {
|
||||
#[instrument(skip(ctx))]
|
||||
pub async fn update_system(
|
||||
#[context] ctx: RpcContext,
|
||||
#[arg(rename = "marketplace-url")] marketplace_url: Url,
|
||||
) -> Result<WithRevision<UpdateResult>, Error> {
|
||||
let noop = WithRevision {
|
||||
response: UpdateResult::NoUpdates,
|
||||
@@ -56,7 +57,7 @@ pub async fn update_system(
|
||||
if UPDATED.load(Ordering::SeqCst) {
|
||||
return Ok(noop);
|
||||
}
|
||||
match maybe_do_update(ctx).await? {
|
||||
match maybe_do_update(ctx, marketplace_url).await? {
|
||||
None => Ok(noop),
|
||||
Some(r) => Ok(WithRevision {
|
||||
response: UpdateResult::Updating,
|
||||
@@ -127,11 +128,14 @@ lazy_static! {
|
||||
}
|
||||
|
||||
#[instrument(skip(ctx))]
|
||||
async fn maybe_do_update(ctx: RpcContext) -> Result<Option<Arc<Revision>>, Error> {
|
||||
async fn maybe_do_update(
|
||||
ctx: RpcContext,
|
||||
marketplace_url: Url,
|
||||
) -> Result<Option<Arc<Revision>>, Error> {
|
||||
let mut db = ctx.db.handle();
|
||||
let latest_version = reqwest::get(format!(
|
||||
"{}/eos/latest?eos-version={}&arch={}",
|
||||
ctx.eos_registry_url().await?,
|
||||
marketplace_url,
|
||||
Current::new().semver(),
|
||||
platforms::TARGET_ARCH,
|
||||
))
|
||||
@@ -154,67 +158,58 @@ async fn maybe_do_update(ctx: RpcContext) -> Result<Option<Arc<Revision>>, Error
|
||||
return Ok(None);
|
||||
}
|
||||
let mut tx = db.begin().await?;
|
||||
let mut info = crate::db::DatabaseModel::new()
|
||||
let mut status = crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.status_info()
|
||||
.get_mut(&mut tx)
|
||||
.await?;
|
||||
match &info.status {
|
||||
ServerStatus::Updating => {
|
||||
return Err(Error::new(
|
||||
eyre!("Server is already updating!"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
))
|
||||
}
|
||||
ServerStatus::Updated => {
|
||||
return Ok(None);
|
||||
}
|
||||
ServerStatus::BackingUp => {
|
||||
return Err(Error::new(
|
||||
eyre!("Server is backing up!"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
))
|
||||
}
|
||||
ServerStatus::Running => (),
|
||||
if status.update_progress.is_some() {
|
||||
return Err(Error::new(
|
||||
eyre!("Server is already updating!"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
if status.updated {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let (new_label, _current_label) = query_mounted_label().await?;
|
||||
let (size, download) = download_file(
|
||||
ctx.db.handle(),
|
||||
&EosUrl {
|
||||
base: info.eos_marketplace.clone(),
|
||||
base: marketplace_url,
|
||||
version: latest_version.clone(),
|
||||
},
|
||||
new_label,
|
||||
)
|
||||
.await?;
|
||||
info.status = ServerStatus::Updating;
|
||||
info.update_progress = Some(UpdateProgress {
|
||||
status.update_progress = Some(UpdateProgress {
|
||||
size,
|
||||
downloaded: 0,
|
||||
});
|
||||
info.save(&mut tx).await?;
|
||||
status.save(&mut tx).await?;
|
||||
let rev = tx.commit(None).await?;
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut db = ctx.db.handle();
|
||||
let res = do_update(download, new_label).await;
|
||||
let mut info = crate::db::DatabaseModel::new()
|
||||
let mut status = crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.status_info()
|
||||
.get_mut(&mut db)
|
||||
.await
|
||||
.expect("could not access status");
|
||||
info.update_progress = None;
|
||||
status.update_progress = None;
|
||||
match res {
|
||||
Ok(()) => {
|
||||
info.status = ServerStatus::Updated;
|
||||
info.save(&mut db).await.expect("could not save status");
|
||||
status.updated = true;
|
||||
status.save(&mut db).await.expect("could not save status");
|
||||
BEP.play().await.expect("could not bep");
|
||||
BEP.play().await.expect("could not bep");
|
||||
BEP.play().await.expect("could not bep");
|
||||
}
|
||||
Err(e) => {
|
||||
info.status = ServerStatus::Running;
|
||||
info.save(&mut db).await.expect("could not save status");
|
||||
status.save(&mut db).await.expect("could not save status");
|
||||
ctx.notification_manager
|
||||
.notify(
|
||||
&mut db,
|
||||
@@ -365,6 +360,7 @@ async fn write_stream_to_label<Db: DbHandle>(
|
||||
last_progress_update = Instant::now();
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.status_info()
|
||||
.update_progress()
|
||||
.put(db, &UpdateProgress { size, downloaded })
|
||||
.await?;
|
||||
|
||||
Reference in New Issue
Block a user