mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
208 lines
6.1 KiB
Rust
208 lines
6.1 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::panic::UnwindSafe;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use clap::Parser;
|
|
use exver::Version;
|
|
use imbl_value::{InternedString, json};
|
|
use itertools::Itertools;
|
|
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
|
use serde::{Deserialize, Serialize};
|
|
use ts_rs::TS;
|
|
|
|
use crate::context::CliContext;
|
|
use crate::prelude::*;
|
|
use crate::progress::{FullProgressTracker, ProgressUnits};
|
|
use crate::registry::asset::RegistryAsset;
|
|
use crate::registry::context::RegistryContext;
|
|
use crate::registry::os::SIG_CONTEXT;
|
|
use crate::registry::os::index::OsVersionInfo;
|
|
use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile;
|
|
use crate::sign::commitment::Commitment;
|
|
use crate::sign::commitment::blake3::Blake3Commitment;
|
|
use crate::util::io::{AtomicFile, open_file};
|
|
|
|
pub fn get_api<C: Context>() -> ParentHandler<C> {
|
|
ParentHandler::new()
|
|
.subcommand(
|
|
"iso",
|
|
from_fn_async(get_iso)
|
|
.with_metadata("authenticated", Value::Bool(false))
|
|
.no_cli(),
|
|
)
|
|
.subcommand(
|
|
"iso",
|
|
from_fn_async(cli_get_os_asset)
|
|
.no_display()
|
|
.with_about("about.download-iso"),
|
|
)
|
|
.subcommand(
|
|
"img",
|
|
from_fn_async(get_img)
|
|
.with_metadata("authenticated", Value::Bool(false))
|
|
.no_cli(),
|
|
)
|
|
.subcommand(
|
|
"img",
|
|
from_fn_async(cli_get_os_asset)
|
|
.no_display()
|
|
.with_about("about.download-img"),
|
|
)
|
|
.subcommand(
|
|
"squashfs",
|
|
from_fn_async(get_squashfs)
|
|
.with_metadata("authenticated", Value::Bool(false))
|
|
.no_cli(),
|
|
)
|
|
.subcommand(
|
|
"squashfs",
|
|
from_fn_async(cli_get_os_asset)
|
|
.no_display()
|
|
.with_about("about.download-squashfs"),
|
|
)
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export)]
|
|
pub struct GetOsAssetParams {
|
|
#[ts(type = "string")]
|
|
pub version: Version,
|
|
#[ts(type = "string")]
|
|
pub platform: InternedString,
|
|
}
|
|
|
|
async fn get_os_asset(
|
|
ctx: RegistryContext,
|
|
GetOsAssetParams { version, platform }: GetOsAssetParams,
|
|
accessor: impl FnOnce(
|
|
&Model<OsVersionInfo>,
|
|
) -> &Model<BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>>
|
|
+ UnwindSafe
|
|
+ Send,
|
|
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
|
accessor(
|
|
ctx.db
|
|
.peek()
|
|
.await
|
|
.as_index()
|
|
.as_os()
|
|
.as_versions()
|
|
.as_idx(&version)
|
|
.or_not_found(&version)?,
|
|
)
|
|
.as_idx(&platform)
|
|
.or_not_found(&platform)?
|
|
.de()
|
|
}
|
|
|
|
pub async fn get_iso(
|
|
ctx: RegistryContext,
|
|
params: GetOsAssetParams,
|
|
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
|
get_os_asset(ctx, params, |info| info.as_iso()).await
|
|
}
|
|
|
|
pub async fn get_img(
|
|
ctx: RegistryContext,
|
|
params: GetOsAssetParams,
|
|
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
|
get_os_asset(ctx, params, |info| info.as_img()).await
|
|
}
|
|
|
|
pub async fn get_squashfs(
|
|
ctx: RegistryContext,
|
|
params: GetOsAssetParams,
|
|
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
|
get_os_asset(ctx, params, |info| info.as_squashfs()).await
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Parser)]
|
|
#[command(rename_all = "kebab-case")]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct CliGetOsAssetParams {
|
|
#[arg(help = "help.arg.os-version")]
|
|
pub version: Version,
|
|
#[arg(help = "help.arg.platform")]
|
|
pub platform: InternedString,
|
|
#[arg(long = "download", short = 'd', help = "help.arg.download-directory")]
|
|
pub download: Option<PathBuf>,
|
|
#[arg(long = "reverify", short = 'r', help = "help.arg.reverify-hash")]
|
|
pub reverify: bool,
|
|
}
|
|
|
|
async fn cli_get_os_asset(
|
|
HandlerArgs {
|
|
context: ctx,
|
|
parent_method,
|
|
method,
|
|
params:
|
|
CliGetOsAssetParams {
|
|
version,
|
|
platform,
|
|
download,
|
|
reverify,
|
|
},
|
|
..
|
|
}: HandlerArgs<CliContext, CliGetOsAssetParams>,
|
|
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
|
let ext = method
|
|
.iter()
|
|
.last()
|
|
.or_else(|| parent_method.iter().last())
|
|
.unwrap_or(&"bin");
|
|
|
|
let res = from_value::<RegistryAsset<Blake3Commitment>>(
|
|
ctx.call_remote::<RegistryContext>(
|
|
&parent_method.iter().chain(&method).join("."),
|
|
json!({
|
|
"version": version,
|
|
"platform": platform,
|
|
}),
|
|
)
|
|
.await?,
|
|
)?;
|
|
|
|
res.validate(SIG_CONTEXT, res.all_signers())?;
|
|
|
|
if let Some(download) = download {
|
|
let download = download.join(format!("startos-{version}_{platform}.{ext}"));
|
|
let mut file = AtomicFile::new(&download, None::<&Path>).await?;
|
|
|
|
let progress = FullProgressTracker::new();
|
|
let mut download_phase =
|
|
progress.add_phase(InternedString::intern("Downloading File"), Some(100));
|
|
download_phase.set_total(res.commitment.size);
|
|
download_phase.set_units(Some(ProgressUnits::Bytes));
|
|
let reverify_phase = if reverify {
|
|
Some(progress.add_phase(InternedString::intern("Reverifying File"), Some(10)))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let progress_task = progress.progress_bar_task("Downloading...");
|
|
|
|
download_phase.start();
|
|
let mut download_writer = download_phase.writer(&mut *file);
|
|
res.download(ctx.client.clone(), &mut download_writer)
|
|
.await?;
|
|
let (_, mut download_phase) = download_writer.into_inner();
|
|
file.save().await?;
|
|
download_phase.complete();
|
|
|
|
if let Some(mut reverify_phase) = reverify_phase {
|
|
reverify_phase.start();
|
|
res.commitment
|
|
.check(&MultiCursorFile::from(open_file(download).await?))
|
|
.await?;
|
|
reverify_phase.complete();
|
|
}
|
|
|
|
progress.complete();
|
|
|
|
progress_task.await.with_kind(ErrorKind::Unknown)?;
|
|
}
|
|
|
|
Ok(res)
|
|
}
|