create skeleton

This commit is contained in:
Aiden McClelland
2024-05-31 13:46:58 -06:00
parent fd7c2fbe93
commit 2568bfde5e
25 changed files with 508 additions and 124 deletions

View File

@@ -225,7 +225,7 @@ system-images/binfmt/docker-images/$(ARCH).tar: $(BINFMT_SRC)
cd system-images/binfmt && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar
$(BINS): $(CORE_SRC) $(ENVIRONMENT_FILE)
cd core && ARCH=$(ARCH) ./build-prod.sh
cd core && ARCH=$(ARCH) ./build-startos-bins.sh
touch $(BINS)
web/node_modules/.package-lock.json: web/package.json sdk/dist

42
core/build-analyticsd.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
cd "$(dirname "${BASH_SOURCE[0]}")"
set -e
shopt -s expand_aliases
if [ -z "$ARCH" ]; then
ARCH=$(uname -m)
fi
USE_TTY=
if tty -s; then
USE_TTY="-it"
fi
cd ..
FEATURES="$(echo $ENVIRONMENT | sed 's/-/,/g')"
RUSTFLAGS=""
if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then
RUSTFLAGS="--cfg tokio_unstable"
fi
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
set +e
fail=
echo "FEATURES=\"$FEATURES\""
echo "RUSTFLAGS=\"$RUSTFLAGS\""
if ! rust-musl-builder sh -c "(cd core && cargo build --release --no-default-features --features analyticsd,$FEATURES --locked --bin analyticsd --target=$ARCH-unknown-linux-musl)"; then
fail=true
fi
set -e
cd core
sudo chown -R $USER target
sudo chown -R $USER ~/.cargo
if [ -n "$fail" ]; then
exit 1
fi

View File

@@ -37,12 +37,17 @@ path = "src/main.rs"
name = "registrybox"
path = "src/main.rs"
[[bin]]
name = "analyticsd"
path = "src/main.rs"
[features]
cli = []
container-runtime = []
daemon = []
registry = []
default = ["cli", "daemon", "registry"]
analyticsd = []
default = ["cli", "daemon"]
dev = []
unstable = ["console-subscriber", "tokio/tracing"]
docker = []

View File

@@ -0,0 +1,121 @@
use std::net::{Ipv4Addr, SocketAddr};
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::Arc;
use clap::Parser;
use reqwest::{Client, Proxy};
use rpc_toolkit::yajrc::RpcError;
use rpc_toolkit::{call_remote_http, CallRemote, Context, Empty};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use tokio::sync::broadcast::Sender;
use tracing::instrument;
use url::Url;
use crate::context::config::{ContextConfig, CONFIG_PATH};
use crate::context::RpcContext;
use crate::prelude::*;
use crate::rpc_continuations::RpcContinuations;
#[derive(Debug, Clone, Default, Deserialize, Serialize, Parser)]
#[serde(rename_all = "kebab-case")]
#[command(rename_all = "kebab-case")]
pub struct AnalyticsConfig {
#[arg(short = 'c', long = "config")]
pub config: Option<PathBuf>,
#[arg(short = 'l', long = "listen")]
pub listen: Option<SocketAddr>,
#[arg(short = 'p', long = "proxy")]
pub tor_proxy: Option<Url>,
#[arg(short = 'd', long = "dbconnect")]
pub dbconnect: Option<Url>,
}
impl ContextConfig for AnalyticsConfig {
fn next(&mut self) -> Option<PathBuf> {
self.config.take()
}
fn merge_with(&mut self, other: Self) {
self.listen = self.listen.take().or(other.listen);
self.tor_proxy = self.tor_proxy.take().or(other.tor_proxy);
self.dbconnect = self.dbconnect.take().or(other.dbconnect);
}
}
impl AnalyticsConfig {
pub fn load(mut self) -> Result<Self, Error> {
let path = self.next();
self.load_path_rec(path)?;
self.load_path_rec(Some(CONFIG_PATH))?;
Ok(self)
}
}
pub struct AnalyticsContextSeed {
pub listen: SocketAddr,
pub db: PgPool,
pub rpc_continuations: RpcContinuations,
pub client: Client,
pub shutdown: Sender<()>,
}
#[derive(Clone)]
pub struct AnalyticsContext(Arc<AnalyticsContextSeed>);
impl AnalyticsContext {
#[instrument(skip_all)]
pub async fn init(config: &AnalyticsConfig) -> Result<Self, Error> {
let (shutdown, _) = tokio::sync::broadcast::channel(1);
let dbconnect = config
.dbconnect
.clone()
.unwrap_or_else(|| "postgres://localhost/analytics".parse().unwrap())
.to_owned();
let db = PgPool::connect(dbconnect.as_str()).await?;
let tor_proxy_url = config
.tor_proxy
.clone()
.map(Ok)
.unwrap_or_else(|| "socks5h://localhost:9050".parse())?;
Ok(Self(Arc::new(AnalyticsContextSeed {
listen: config
.listen
.unwrap_or(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 5959)),
db,
rpc_continuations: RpcContinuations::new(),
client: Client::builder()
.proxy(Proxy::custom(move |url| {
if url.host_str().map_or(false, |h| h.ends_with(".onion")) {
Some(tor_proxy_url.clone())
} else {
None
}
}))
.build()
.with_kind(crate::ErrorKind::ParseUrl)?,
shutdown,
})))
}
}
impl AsRef<RpcContinuations> for AnalyticsContext {
fn as_ref(&self) -> &RpcContinuations {
&self.rpc_continuations
}
}
impl Context for AnalyticsContext {}
impl Deref for AnalyticsContext {
type Target = AnalyticsContextSeed;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl CallRemote<AnalyticsContext> for RpcContext {
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
if let Some(analytics_url) = self.analytics_url.clone() {
call_remote_http(&self.client, analytics_url, method, params).await
} else {
Ok(Value::Null)
}
}
}

View File

@@ -0,0 +1,78 @@
use std::net::SocketAddr;
use axum::Router;
use futures::future::ready;
use rpc_toolkit::{Context, ParentHandler, Server};
use crate::analytics::context::AnalyticsContext;
use crate::middleware::cors::Cors;
use crate::net::static_server::{bad_request, not_found, server_error};
use crate::net::web_server::WebServer;
use crate::rpc_continuations::Guid;
pub mod context;
pub fn analytics_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new() // TODO: FullMetal
}
pub fn analytics_server_router(ctx: AnalyticsContext) -> Router {
use axum::extract as x;
use axum::routing::{any, get, post};
Router::new()
.route("/rpc/*path", {
let ctx = ctx.clone();
post(
Server::new(move || ready(Ok(ctx.clone())), analytics_api())
.middleware(Cors::new())
)
})
.route(
"/ws/rpc/*path",
get({
let ctx = ctx.clone();
move |x::Path(path): x::Path<String>,
ws: axum::extract::ws::WebSocketUpgrade| async move {
match Guid::from(&path) {
None => {
tracing::debug!("No Guid Path");
bad_request()
}
Some(guid) => match ctx.rpc_continuations.get_ws_handler(&guid).await {
Some(cont) => ws.on_upgrade(cont),
_ => not_found(),
},
}
}
}),
)
.route(
"/rest/rpc/*path",
any({
let ctx = ctx.clone();
move |request: x::Request| async move {
let path = request
.uri()
.path()
.strip_prefix("/rest/rpc/")
.unwrap_or_default();
match Guid::from(&path) {
None => {
tracing::debug!("No Guid Path");
bad_request()
}
Some(guid) => match ctx.rpc_continuations.get_rest_handler(&guid).await {
None => not_found(),
Some(cont) => cont(request).await.unwrap_or_else(server_error),
},
}
}
}),
)
}
impl WebServer {
pub fn analytics(bind: SocketAddr, ctx: AnalyticsContext) -> Self {
Self::new(bind, analytics_server_router(ctx))
}
}

View File

@@ -0,0 +1,86 @@
use std::ffi::OsString;
use clap::Parser;
use futures::FutureExt;
use tokio::signal::unix::signal;
use tracing::instrument;
use crate::analytics::context::{AnalyticsConfig, AnalyticsContext};
use crate::net::web_server::WebServer;
use crate::prelude::*;
use crate::util::logger::EmbassyLogger;
#[instrument(skip_all)]
async fn inner_main(config: &AnalyticsConfig) -> Result<(), Error> {
let server = async {
let ctx = AnalyticsContext::init(config).await?;
let server = WebServer::analytics(ctx.listen, ctx.clone());
let mut shutdown_recv = ctx.shutdown.subscribe();
let sig_handler_ctx = ctx;
let sig_handler = tokio::spawn(async move {
use tokio::signal::unix::SignalKind;
futures::future::select_all(
[
SignalKind::interrupt(),
SignalKind::quit(),
SignalKind::terminate(),
]
.iter()
.map(|s| {
async move {
signal(*s)
.unwrap_or_else(|_| panic!("register {:?} handler", s))
.recv()
.await
}
.boxed()
}),
)
.await;
sig_handler_ctx
.shutdown
.send(())
.map_err(|_| ())
.expect("send shutdown signal");
});
shutdown_recv
.recv()
.await
.with_kind(crate::ErrorKind::Unknown)?;
sig_handler.abort();
Ok::<_, Error>(server)
}
.await?;
server.shutdown().await;
Ok(())
}
pub fn main(args: impl IntoIterator<Item = OsString>) {
EmbassyLogger::init();
let config = AnalyticsConfig::parse_from(args).load().unwrap();
let res = {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("failed to initialize runtime");
rt.block_on(inner_main(&config))
};
match res {
Ok(()) => (),
Err(e) => {
eprintln!("{}", e.source);
tracing::debug!("{:?}", e.source);
drop(e.source);
std::process::exit(e.kind as i32)
}
}
}

View File

@@ -2,6 +2,8 @@ use std::collections::VecDeque;
use std::ffi::OsString;
use std::path::Path;
#[cfg(feature = "analytics")]
pub mod analytics;
#[cfg(feature = "container-runtime")]
pub mod container_cli;
pub mod deprecated;
@@ -24,6 +26,8 @@ fn select_executable(name: &str) -> Option<fn(VecDeque<OsString>)> {
"startd" => Some(startd::main),
#[cfg(feature = "registry")]
"registry" => Some(registry::main),
#[cfg(feature = "analyticsd")]
"analyticsd" => Some(analyticsd::main),
"embassy-cli" => Some(|_| deprecated::renamed("embassy-cli", "start-cli")),
"embassy-sdk" => Some(|_| deprecated::renamed("embassy-sdk", "start-sdk")),
"embassyd" => Some(|_| deprecated::renamed("embassyd", "startd")),

View File

@@ -113,6 +113,8 @@ pub struct ServerConfig {
pub datadir: Option<PathBuf>,
#[arg(long = "disable-encryption")]
pub disable_encryption: Option<bool>,
#[arg(short = 'a', long = "analytics-url")]
pub analytics_url: Option<Url>,
}
impl ContextConfig for ServerConfig {
fn next(&mut self) -> Option<PathBuf> {
@@ -131,6 +133,7 @@ impl ContextConfig for ServerConfig {
.or(other.revision_cache_size);
self.datadir = self.datadir.take().or(other.datadir);
self.disable_encryption = self.disable_encryption.take().or(other.disable_encryption);
self.analytics_url = self.analytics_url.take().or(other.analytics_url);
}
}

View File

@@ -13,6 +13,7 @@ use rpc_toolkit::{CallRemote, Context, Empty};
use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
use tokio::time::Instant;
use tracing::instrument;
use url::Url;
use super::setup::CURRENT_SECRET;
use crate::account::AccountInfo;
@@ -55,7 +56,9 @@ pub struct RpcContextSeed {
pub client: Client,
pub hardware: Hardware,
pub start_time: Instant,
#[cfg(feature = "dev")]
pub dev: Dev,
pub analytics_url: Option<Url>,
}
pub struct Dev {
@@ -184,9 +187,11 @@ impl RpcContext {
.with_kind(crate::ErrorKind::ParseUrl)?,
hardware: Hardware { devices, ram },
start_time: Instant::now(),
#[cfg(feature = "dev")]
dev: Dev {
lxc: Mutex::new(BTreeMap::new()),
},
analytics_url: config.analytics_url.clone(),
});
let res = Self(seed.clone());

View File

@@ -24,6 +24,7 @@ lazy_static::lazy_static! {
pub mod account;
pub mod action;
pub mod analytics;
pub mod auth;
pub mod backup;
pub mod bins;
@@ -95,7 +96,7 @@ pub fn echo<C: Context>(_: C, EchoParams { message }: EchoParams) -> Result<Stri
}
pub fn main_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
let api = ParentHandler::new()
.subcommand::<C, _>("git-info", from_fn(version::git_info))
.subcommand(
"echo",
@@ -120,9 +121,11 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
)
.no_cli(),
)
.subcommand("lxc", lxc::lxc::<C>())
.subcommand("s9pk", s9pk::rpc::s9pk())
.subcommand("util", util::rpc::util::<C>())
.subcommand("util", util::rpc::util::<C>());
#[cfg(feature = "dev")]
let api = api.subcommand("lxc", lxc::dev::lxc::<C>());
api
}
pub fn server<C: Context>() -> ParentHandler<C> {

112
core/startos/src/lxc/dev.rs Normal file
View File

@@ -0,0 +1,112 @@
use std::ops::Deref;
use clap::Parser;
use rpc_toolkit::{
from_fn_async, CallRemoteHandler, Context, Empty, HandlerArgs, HandlerExt, HandlerFor,
ParentHandler,
};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::lxc::{ContainerId, LxcConfig};
use crate::prelude::*;
use crate::rpc_continuations::Guid;
pub fn lxc<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"create",
from_fn_async(create).with_call_remote::<CliContext>(),
)
.subcommand(
"list",
from_fn_async(list)
.with_custom_display_fn(|_, res| {
use prettytable::*;
let mut table = table!([bc => "GUID"]);
for guid in res {
table.add_row(row![&*guid]);
}
table.printstd();
Ok(())
})
.with_call_remote::<CliContext>(),
)
.subcommand(
"remove",
from_fn_async(remove)
.no_display()
.with_call_remote::<CliContext>(),
)
.subcommand("connect", from_fn_async(connect_rpc).no_cli())
.subcommand("connect", from_fn_async(connect_rpc_cli).no_display())
}
pub async fn create(ctx: RpcContext) -> Result<ContainerId, Error> {
let container = ctx.lxc_manager.create(None, LxcConfig::default()).await?;
let guid = container.guid.deref().clone();
ctx.dev.lxc.lock().await.insert(guid.clone(), container);
Ok(guid)
}
pub async fn list(ctx: RpcContext) -> Result<Vec<ContainerId>, Error> {
Ok(ctx.dev.lxc.lock().await.keys().cloned().collect())
}
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct RemoveParams {
#[ts(type = "string")]
pub guid: ContainerId,
}
pub async fn remove(ctx: RpcContext, RemoveParams { guid }: RemoveParams) -> Result<(), Error> {
if let Some(container) = ctx.dev.lxc.lock().await.remove(&guid) {
container.exit().await?;
}
Ok(())
}
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct ConnectParams {
#[ts(type = "string")]
pub guid: ContainerId,
}
pub async fn connect_rpc(
ctx: RpcContext,
ConnectParams { guid }: ConnectParams,
) -> Result<Guid, Error> {
super::connect(
&ctx,
ctx.dev.lxc.lock().await.get(&guid).ok_or_else(|| {
Error::new(eyre!("No container with guid: {guid}"), ErrorKind::NotFound)
})?,
)
.await
}
pub async fn connect_rpc_cli(
HandlerArgs {
context,
parent_method,
method,
params,
inherited_params,
raw_params,
}: HandlerArgs<CliContext, ConnectParams>,
) -> Result<(), Error> {
let ctx = context.clone();
let guid = CallRemoteHandler::<CliContext, _, _>::new(from_fn_async(connect_rpc))
.handle_async(HandlerArgs {
context,
parent_method,
method,
params: rpc_toolkit::util::Flat(params, Empty {}),
inherited_params,
raw_params,
})
.await?;
super::connect_cli(&ctx, guid).await
}

View File

@@ -1,20 +1,15 @@
use std::collections::BTreeSet;
use std::net::Ipv4Addr;
use std::ops::Deref;
use std::path::Path;
use std::sync::{Arc, Weak};
use std::time::Duration;
use clap::builder::ValueParserFactory;
use clap::Parser;
use futures::{AsyncWriteExt, FutureExt, StreamExt};
use imbl_value::{InOMap, InternedString};
use models::InvalidId;
use rpc_toolkit::yajrc::{RpcError, RpcResponse};
use rpc_toolkit::{
from_fn_async, CallRemoteHandler, Context, Empty, GenericRpcMethod, HandlerArgs, HandlerExt,
HandlerFor, ParentHandler, RpcRequest,
};
use rpc_toolkit::yajrc::RpcError;
use rpc_toolkit::{GenericRpcMethod, RpcRequest, RpcResponse};
use rustyline_async::{ReadlineEvent, SharedWriter};
use serde::{Deserialize, Serialize};
use tokio::fs::File;
@@ -38,6 +33,9 @@ use crate::util::clap::FromStrParser;
use crate::util::rpc_client::UnixRpcClient;
use crate::util::{new_guid, Invoke};
#[cfg(feature = "dev")]
pub mod dev;
const LXC_CONTAINER_DIR: &str = "/var/lib/lxc";
const RPC_DIR: &str = "media/startos/rpc"; // must not be absolute path
pub const CONTAINER_RPC_SERVER_SOCKET: &str = "service.sock"; // must not be absolute path
@@ -369,80 +367,6 @@ impl Drop for LxcContainer {
#[derive(Default, Serialize)]
pub struct LxcConfig {}
pub fn lxc<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"create",
from_fn_async(create).with_call_remote::<CliContext>(),
)
.subcommand(
"list",
from_fn_async(list)
.with_custom_display_fn(|_, res| {
use prettytable::*;
let mut table = table!([bc => "GUID"]);
for guid in res {
table.add_row(row![&*guid]);
}
table.printstd();
Ok(())
})
.with_call_remote::<CliContext>(),
)
.subcommand(
"remove",
from_fn_async(remove)
.no_display()
.with_call_remote::<CliContext>(),
)
.subcommand("connect", from_fn_async(connect_rpc).no_cli())
.subcommand("connect", from_fn_async(connect_rpc_cli).no_display())
}
pub async fn create(ctx: RpcContext) -> Result<ContainerId, Error> {
let container = ctx.lxc_manager.create(None, LxcConfig::default()).await?;
let guid = container.guid.deref().clone();
ctx.dev.lxc.lock().await.insert(guid.clone(), container);
Ok(guid)
}
pub async fn list(ctx: RpcContext) -> Result<Vec<ContainerId>, Error> {
Ok(ctx.dev.lxc.lock().await.keys().cloned().collect())
}
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct RemoveParams {
#[ts(type = "string")]
pub guid: ContainerId,
}
pub async fn remove(ctx: RpcContext, RemoveParams { guid }: RemoveParams) -> Result<(), Error> {
if let Some(container) = ctx.dev.lxc.lock().await.remove(&guid) {
container.exit().await?;
}
Ok(())
}
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct ConnectParams {
#[ts(type = "string")]
pub guid: ContainerId,
}
pub async fn connect_rpc(
ctx: RpcContext,
ConnectParams { guid }: ConnectParams,
) -> Result<Guid, Error> {
connect(
&ctx,
ctx.dev.lxc.lock().await.get(&guid).ok_or_else(|| {
Error::new(eyre!("No container with guid: {guid}"), ErrorKind::NotFound)
})?,
)
.await
}
pub async fn connect(ctx: &RpcContext, container: &LxcContainer) -> Result<Guid, Error> {
use axum::extract::ws::Message;
@@ -473,10 +397,8 @@ pub async fn connect(ctx: &RpcContext, container: &LxcContainer) -> Result<Guid,
}
.await;
ws.send(Message::Text(
serde_json::to_string(
&RpcResponse::<GenericRpcMethod> { id, result },
)
.with_kind(ErrorKind::Serialization)?,
serde_json::to_string(&RpcResponse { id, result })
.with_kind(ErrorKind::Serialization)?,
))
.await
.with_kind(ErrorKind::Network)?;
@@ -612,28 +534,3 @@ pub async fn connect_cli(ctx: &CliContext, guid: Guid) -> Result<(), Error> {
Ok(())
}
pub async fn connect_rpc_cli(
HandlerArgs {
context,
parent_method,
method,
params,
inherited_params,
raw_params,
}: HandlerArgs<CliContext, ConnectParams>,
) -> Result<(), Error> {
let ctx = context.clone();
let guid = CallRemoteHandler::<CliContext, _, _>::new(from_fn_async(connect_rpc))
.handle_async(HandlerArgs {
context,
parent_method,
method,
params: rpc_toolkit::util::Flat(params, Empty {}),
inherited_params,
raw_params,
})
.await?;
connect_cli(&ctx, guid).await
}

View File

@@ -281,6 +281,10 @@ pub async fn execute<C: Context>(
IoFormat::Yaml.to_vec(&ServerConfig {
os_partitions: Some(part_info.clone()),
ethernet_interface: Some(eth_iface),
#[cfg(feature = "dev")]
analytics_url: None,
#[cfg(not(feature = "dev"))]
analytics_url: Some("https://analytics.start9.com".parse()?), // TODO: FullMetal
..Default::default()
})?,
)

View File

@@ -32,8 +32,8 @@ pub struct RegistryConfig {
#[arg(short = 'l', long = "listen")]
pub listen: Option<SocketAddr>,
#[arg(short = 'h', long = "hostname")]
pub hostname: InternedString,
#[arg(short = 'p', long = "proxy")]
pub hostname: Option<InternedString>,
#[arg(short = 'p', long = "tor-proxy")]
pub tor_proxy: Option<Url>,
#[arg(short = 'd', long = "datadir")]
pub datadir: Option<PathBuf>,
@@ -43,6 +43,9 @@ impl ContextConfig for RegistryConfig {
self.config.take()
}
fn merge_with(&mut self, other: Self) {
self.listen = self.listen.take().or(other.listen);
self.hostname = self.hostname.take().or(other.hostname);
self.tor_proxy = self.tor_proxy.take().or(other.tor_proxy);
self.datadir = self.datadir.take().or(other.datadir);
}
}
@@ -92,7 +95,16 @@ impl RegistryContext {
.map(Ok)
.unwrap_or_else(|| "socks5h://localhost:9050".parse())?;
Ok(Self(Arc::new(RegistryContextSeed {
hostname: config.hostname.clone(),
hostname: config
.hostname
.as_ref()
.ok_or_else(|| {
Error::new(
eyre!("missing required configuration: hostname"),
ErrorKind::NotFound,
)
})?
.clone(),
listen: config
.listen
.unwrap_or(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 5959)),
@@ -169,7 +181,8 @@ impl CallRemote<RegistryContext> for CliContext {
&AnySigningKey::Ed25519(self.developer_key()?.clone()),
&body,
&host,
)?.to_header(),
)?
.to_header(),
)
.body(body)
.send()

View File

@@ -6,7 +6,6 @@ import type { Version } from "./Version"
export type AddAssetParams = {
version: Version
platform: string
upload: boolean
url: string
signature: AnySignature
commitment: Blake3Commitment

View File

@@ -0,0 +1,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AnySignature } from "./AnySignature"
import type { MerkleArchiveCommitment } from "./MerkleArchiveCommitment"
export type AddPackageParams = {
url: string
commitment: MerkleArchiveCommitment
signature: AnySignature
}

View File

@@ -4,6 +4,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo"
import type { Version } from "./Version"
export type GetPackageResponse = {
categories: string[]
best: { [key: Version]: PackageVersionInfo }
otherVersions?: { [key: Version]: PackageInfoShort }
}

View File

@@ -3,6 +3,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo"
import type { Version } from "./Version"
export type GetPackageResponseFull = {
categories: string[]
best: { [key: Version]: PackageVersionInfo }
otherVersions: { [key: Version]: PackageVersionInfo }
}

View File

@@ -3,5 +3,5 @@
export type HardwareRequirements = {
device: { [key: string]: string }
ram: number | null
arch: Array<string> | null
arch: string[] | null
}

View File

@@ -7,7 +7,7 @@ export type OsVersionInfo = {
headline: string
releaseNotes: string
sourceVersion: string
signers: Array<Guid>
authorized: Array<Guid>
iso: { [key: string]: RegistryAsset<Blake3Commitment> }
squashfs: { [key: string]: RegistryAsset<Blake3Commitment> }
img: { [key: string]: RegistryAsset<Blake3Commitment> }

View File

@@ -4,6 +4,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo"
import type { Version } from "./Version"
export type PackageInfo = {
signers: Array<Guid>
authorized: Array<Guid>
versions: { [key: Version]: PackageVersionInfo }
categories: string[]
}

View File

@@ -17,7 +17,6 @@ export type PackageVersionInfo = {
upstreamRepo: string
supportSite: string
marketingSite: string
categories: string[]
osVersion: Version
hardwareRequirements: HardwareRequirements
sourceVersion: string | null

View File

@@ -3,6 +3,7 @@ export { ActionId } from "./ActionId"
export { ActionMetadata } from "./ActionMetadata"
export { AddAdminParams } from "./AddAdminParams"
export { AddAssetParams } from "./AddAssetParams"
export { AddPackageParams } from "./AddPackageParams"
export { AddressInfo } from "./AddressInfo"
export { AddSslOptions } from "./AddSslOptions"
export { AddVersionParams } from "./AddVersionParams"