mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
wip: types
This commit is contained in:
@@ -280,7 +280,7 @@ tracing-error = "0.2.0"
|
||||
tracing-futures = "0.2.5"
|
||||
tracing-journald = "0.3.0"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
ts-rs = "9.0.1"
|
||||
ts-rs = { version = "9.0.1", features = ["chrono-impl"] }
|
||||
typed-builder = "0.21.0"
|
||||
unix-named-pipe = "0.2.0"
|
||||
url = { version = "2.4.1", features = ["serde"] }
|
||||
|
||||
@@ -149,14 +149,6 @@ where
|
||||
.no_display()
|
||||
.with_about("Reset password"),
|
||||
)
|
||||
.subcommand(
|
||||
"get-pubkey",
|
||||
from_fn_async(get_pubkey)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.no_display()
|
||||
.with_about("Get public key derived from server private key")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -395,7 +387,7 @@ pub async fn list<C: AuthContext>(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
struct KillSessionId(InternedString);
|
||||
|
||||
impl KillSessionId {
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Map for CifsTargets {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CifsBackupTarget {
|
||||
hostname: String,
|
||||
|
||||
@@ -34,11 +34,11 @@ use crate::util::serde::{
|
||||
|
||||
pub mod cifs;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all_fields = "camelCase")]
|
||||
pub enum BackupTarget {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
Disk {
|
||||
vendor: Option<String>,
|
||||
model: Option<String>,
|
||||
@@ -210,19 +210,21 @@ pub async fn list(ctx: RpcContext) -> Result<BTreeMap<BackupTargetId, BackupTarg
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BackupInfo {
|
||||
#[ts(type = "string")]
|
||||
pub version: Version,
|
||||
pub timestamp: Option<DateTime<Utc>>,
|
||||
pub package_backups: BTreeMap<PackageId, PackageBackupInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PackageBackupInfo {
|
||||
pub title: InternedString,
|
||||
pub version: VersionString,
|
||||
#[ts(type = "string")]
|
||||
pub os_version: Version,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
pub mod model;
|
||||
pub mod prelude;
|
||||
|
||||
use std::panic::UnwindSafe;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -56,9 +55,18 @@ pub fn db<C: Context>() -> ParentHandler<C> {
|
||||
"dump",
|
||||
from_fn_async(cli_dump)
|
||||
.with_display_serializable()
|
||||
.no_ts()
|
||||
.with_about("Filter/query db to display tables and records"),
|
||||
)
|
||||
.subcommand("dump", from_fn_async(dump).no_cli())
|
||||
.subcommand(
|
||||
"dump",
|
||||
from_fn_async(dump)
|
||||
.custom_ts(
|
||||
DumpParams::inline(),
|
||||
format!("{{ id: number; value: unknown }}"),
|
||||
)
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"subscribe",
|
||||
from_fn_async(subscribe)
|
||||
@@ -73,6 +81,7 @@ pub fn db<C: Context>() -> ParentHandler<C> {
|
||||
"apply",
|
||||
from_fn_async(cli_apply)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Update a db record"),
|
||||
)
|
||||
.subcommand("apply", from_fn_async(apply).no_cli())
|
||||
|
||||
@@ -10,6 +10,7 @@ use patch_db::value::InternedString;
|
||||
pub use patch_db::{HasModel, MutateResult, PatchDb};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
@@ -479,6 +480,24 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for JsonKey<T> {
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<T> TS for JsonKey<T> {
|
||||
type WithoutGenerics = JsonKey<ts_rs::Dummy>;
|
||||
fn decl() -> String {
|
||||
format!("type {} = string", Self::name())
|
||||
}
|
||||
fn decl_concrete() -> String {
|
||||
Self::decl()
|
||||
}
|
||||
fn name() -> String {
|
||||
"JsonKey".into()
|
||||
}
|
||||
fn inline() -> String {
|
||||
"string".into()
|
||||
}
|
||||
fn inline_flattened() -> String {
|
||||
Self::inline()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct WithTimeData<T> {
|
||||
|
||||
@@ -28,6 +28,7 @@ pub fn diagnostic<C: Context>() -> ParentHandler<C> {
|
||||
"logs",
|
||||
from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Display OS logs"),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -38,6 +39,7 @@ pub fn diagnostic<C: Context>() -> ParentHandler<C> {
|
||||
"kernel-logs",
|
||||
from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Display kernal logs"),
|
||||
)
|
||||
.subcommand(
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::path::Path;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use tokio::process::Command;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::Error;
|
||||
use crate::disk::fsck::btrfs::{btrfs_check_readonly, btrfs_check_repair};
|
||||
@@ -11,7 +12,7 @@ use crate::util::Invoke;
|
||||
pub mod btrfs;
|
||||
pub mod ext4;
|
||||
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[must_use]
|
||||
pub struct RequiresReboot(pub bool);
|
||||
impl std::ops::BitOrAssign for RequiresReboot {
|
||||
|
||||
@@ -13,6 +13,7 @@ use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use super::mount::filesystem::ReadOnly;
|
||||
use super::mount::filesystem::block_dev::BlockDev;
|
||||
@@ -24,14 +25,14 @@ use crate::util::Invoke;
|
||||
use crate::util::serde::IoFormat;
|
||||
use crate::{Error, ResultExt as _};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum PartitionTable {
|
||||
Mbr,
|
||||
Gpt,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DiskInfo {
|
||||
pub logicalname: PathBuf,
|
||||
@@ -43,7 +44,7 @@ pub struct DiskInfo {
|
||||
pub guid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PartitionInfo {
|
||||
pub logicalname: PathBuf,
|
||||
@@ -54,10 +55,11 @@ pub struct PartitionInfo {
|
||||
pub guid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StartOsRecoveryInfo {
|
||||
pub hostname: Hostname,
|
||||
#[ts(type = "string")]
|
||||
pub version: exver::Version,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
pub password_hash: Option<String>,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use imbl_value::InternedString;
|
||||
use lazy_format::lazy_format;
|
||||
use rand::{Rng, rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::util::Invoke;
|
||||
use crate::{Error, ErrorKind};
|
||||
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||
pub struct Hostname(pub InternedString);
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
@@ -425,6 +425,7 @@ pub fn init_api<C: Context>() -> ParentHandler<C> {
|
||||
"logs",
|
||||
from_fn_async(crate::logs::cli_logs::<InitContext, Empty>)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Display OS logs"),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -435,6 +436,7 @@ pub fn init_api<C: Context>() -> ParentHandler<C> {
|
||||
"kernel-logs",
|
||||
from_fn_async(crate::logs::cli_logs::<InitContext, Empty>)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Display kernel logs"),
|
||||
)
|
||||
.subcommand("subscribe", from_fn_async(init_progress).no_cli())
|
||||
|
||||
@@ -277,7 +277,7 @@ pub fn server<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
.subcommand(
|
||||
"logs",
|
||||
from_fn_async(logs::cli_logs::<RpcContext, Empty>).no_display().with_about("Display OS logs"),
|
||||
from_fn_async(logs::cli_logs::<RpcContext, Empty>).no_display().no_ts().with_about("Display OS logs"),
|
||||
)
|
||||
.subcommand(
|
||||
"kernel-logs",
|
||||
@@ -285,7 +285,7 @@ pub fn server<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
.subcommand(
|
||||
"kernel-logs",
|
||||
from_fn_async(logs::cli_logs::<RpcContext, Empty>).no_display().with_about("Display Kernel logs"),
|
||||
from_fn_async(logs::cli_logs::<RpcContext, Empty>).no_display().no_ts().with_about("Display Kernel logs"),
|
||||
)
|
||||
.subcommand(
|
||||
"metrics",
|
||||
@@ -297,7 +297,7 @@ pub fn server<C: Context>() -> ParentHandler<C> {
|
||||
.with_call_remote::<CliContext>()
|
||||
)
|
||||
.subcommand(
|
||||
"follow",
|
||||
"follow",
|
||||
from_fn_async(system::metrics_follow)
|
||||
.no_cli()
|
||||
)
|
||||
@@ -394,6 +394,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
|
||||
"install",
|
||||
from_fn_async_local(install::cli_install)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Install a package from a marketplace or via sideloading"),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -497,7 +498,6 @@ pub fn package<C: Context>() -> ParentHandler<C> {
|
||||
.with_about("List information related to the lxc containers i.e. CPU, Memory, Disk")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand("logs", logs::package_logs())
|
||||
.subcommand(
|
||||
"logs",
|
||||
logs::package_logs().with_about("Display package logs"),
|
||||
@@ -506,6 +506,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
|
||||
"logs",
|
||||
from_fn_async(logs::cli_logs::<RpcContext, logs::PackageIdParams>)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Display package logs"),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -520,7 +521,16 @@ pub fn package<C: Context>() -> ParentHandler<C> {
|
||||
.with_about("Execute commands within a service container")
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand("attach", from_fn_async(service::cli_attach).no_display())
|
||||
.subcommand(
|
||||
"attach",
|
||||
from_fn_async(service::cli_attach).no_display().no_ts(),
|
||||
)
|
||||
.subcommand(
|
||||
"list-subcontainers",
|
||||
from_fn_async(service::list_subcontainers)
|
||||
.with_about("List all subcontainers for a package")
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"host",
|
||||
net::host::host_api::<C>().with_about("Manage network hosts for a package"),
|
||||
|
||||
@@ -24,6 +24,7 @@ use tokio::process::{Child, Command};
|
||||
use tokio_stream::wrappers::LinesStream;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::error::ResultExt;
|
||||
@@ -109,21 +110,21 @@ async fn ws_handler(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LogResponse {
|
||||
pub entries: Reversible<LogEntry>,
|
||||
start_cursor: Option<String>,
|
||||
end_cursor: Option<String>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LogFollowResponse {
|
||||
start_cursor: Option<String>,
|
||||
guid: Guid,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LogEntry {
|
||||
timestamp: DateTime<Utc>,
|
||||
@@ -142,7 +143,7 @@ impl std::fmt::Display for LogEntry {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, TS)]
|
||||
pub struct JournalctlEntry {
|
||||
#[serde(rename = "__REALTIME_TIMESTAMP")]
|
||||
pub timestamp: String,
|
||||
@@ -228,14 +229,15 @@ pub enum LogSource {
|
||||
|
||||
pub const SYSTEM_UNIT: &str = "startd";
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
pub struct PackageIdParams {
|
||||
id: PackageId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, TS)]
|
||||
#[ts(type = "number | string")]
|
||||
pub enum BootIdentifier {
|
||||
Index(i32),
|
||||
Id(String),
|
||||
@@ -320,10 +322,10 @@ impl From<BootIdentifier> for String {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
pub struct LogsParams<Extra: FromArgMatches + Args = Empty> {
|
||||
pub struct LogsParams<Extra: FromArgMatches + Args + TS = Empty> {
|
||||
#[command(flatten)]
|
||||
#[serde(flatten)]
|
||||
extra: Extra,
|
||||
@@ -354,7 +356,7 @@ pub struct CliLogsParams<Extra: FromArgMatches + Args = Empty> {
|
||||
#[allow(private_bounds)]
|
||||
pub fn logs<
|
||||
C: Context + AsRef<RpcContinuations>,
|
||||
Extra: FromArgMatches + Serialize + DeserializeOwned + Args + Send + Sync + 'static,
|
||||
Extra: FromArgMatches + Serialize + DeserializeOwned + Args + TS + Send + Sync + 'static,
|
||||
>(
|
||||
source: impl for<'a> LogSourceFn<'a, C, Extra>,
|
||||
) -> ParentHandler<C, LogsParams<Extra>> {
|
||||
@@ -379,7 +381,7 @@ pub async fn cli_logs<RemoteContext, Extra>(
|
||||
) -> Result<(), RpcError>
|
||||
where
|
||||
CliContext: CallRemote<RemoteContext>,
|
||||
Extra: FromArgMatches + Args + Serialize + Send + Sync,
|
||||
Extra: FromArgMatches + Args + TS + Serialize + Send + Sync,
|
||||
{
|
||||
let method = parent_method
|
||||
.into_iter()
|
||||
@@ -434,7 +436,7 @@ fn logs_nofollow<C, Extra>(
|
||||
) -> impl HandlerFor<C, Params = LogsParams<Extra>, InheritedParams = Empty, Ok = LogResponse, Err = Error>
|
||||
where
|
||||
C: Context,
|
||||
Extra: FromArgMatches + Args + Send + Sync + 'static,
|
||||
Extra: FromArgMatches + Args + TS + Send + Sync + 'static,
|
||||
{
|
||||
from_fn_async(
|
||||
move |HandlerArgs {
|
||||
@@ -466,7 +468,7 @@ where
|
||||
|
||||
fn logs_follow<
|
||||
C: Context + AsRef<RpcContinuations>,
|
||||
Extra: FromArgMatches + Args + Send + Sync + 'static,
|
||||
Extra: FromArgMatches + Args + TS + Send + Sync + 'static,
|
||||
>(
|
||||
f: impl for<'a> LogSourceFn<'a, C, Extra>,
|
||||
) -> impl HandlerFor<
|
||||
|
||||
@@ -24,6 +24,7 @@ use sha2::Sha256;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::Mutex;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::auth::{Sessions, check_password, write_shadow};
|
||||
use crate::context::RpcContext;
|
||||
@@ -89,7 +90,7 @@ impl AuthContext for RpcContext {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LoginRes {
|
||||
pub session: InternedString,
|
||||
@@ -100,7 +101,7 @@ pub trait AsLogoutSessionId {
|
||||
}
|
||||
|
||||
/// Will need to know when we have logged out from a route
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, TS)]
|
||||
pub struct HasLoggedOutSessions(());
|
||||
|
||||
impl HasLoggedOutSessions {
|
||||
|
||||
@@ -460,7 +460,7 @@ impl ValueParserFactory for AcmeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct InitAcmeParams {
|
||||
#[arg(long)]
|
||||
pub provider: AcmeProvider,
|
||||
@@ -485,7 +485,7 @@ pub async fn init(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct RemoveAcmeParams {
|
||||
#[arg(long)]
|
||||
pub provider: AcmeProvider,
|
||||
|
||||
@@ -32,6 +32,7 @@ use rpc_toolkit::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::net::{TcpListener, UdpSocket};
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::Database;
|
||||
@@ -99,7 +100,7 @@ pub fn dns_api<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct QueryDnsParams {
|
||||
pub fqdn: InternedString,
|
||||
}
|
||||
@@ -138,7 +139,7 @@ pub fn query_dns<C: Context>(
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct SetStaticDnsParams {
|
||||
pub servers: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::mpsc;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
@@ -448,10 +449,10 @@ fn err_has_exited<T>(_: T) -> Error {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
|
||||
pub struct ForwardTable(pub BTreeMap<u16, ForwardTarget>);
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
|
||||
pub struct ForwardTarget {
|
||||
pub target: SocketAddrV4,
|
||||
pub filter: String,
|
||||
|
||||
@@ -51,6 +51,7 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"list",
|
||||
from_fn_async(list_interfaces)
|
||||
.custom_ts("{}".into(), BTreeMap::<GatewayId, NetworkInterfaceInfo>::inline())
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||
use prettytable::*;
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::net::tor::OnionAddress;
|
||||
use crate::prelude::*;
|
||||
use crate::util::serde::{HandlerExtSerde, display_serializable};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all_fields = "camelCase")]
|
||||
#[serde(tag = "kind")]
|
||||
@@ -235,7 +235,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct AddPublicDomainParams {
|
||||
pub fqdn: InternedString,
|
||||
#[arg(long)]
|
||||
@@ -282,7 +282,7 @@ pub async fn add_public_domain<Kind: HostApiKind>(
|
||||
.with_kind(ErrorKind::Unknown)?
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct RemoveDomainParams {
|
||||
pub fqdn: InternedString,
|
||||
}
|
||||
@@ -305,7 +305,7 @@ pub async fn remove_public_domain<Kind: HostApiKind>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct AddPrivateDomainParams {
|
||||
pub fqdn: InternedString,
|
||||
}
|
||||
@@ -347,7 +347,7 @@ pub async fn remove_private_domain<Kind: HostApiKind>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct OnionParams {
|
||||
pub onion: String,
|
||||
}
|
||||
|
||||
@@ -164,12 +164,12 @@ impl Model<Host> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct RequiresPackageId {
|
||||
package: PackageId,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct RequiresHostId {
|
||||
host: HostId,
|
||||
}
|
||||
|
||||
@@ -240,12 +240,16 @@ impl CertPair {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn root_ca_start_time() -> Result<SystemTime, Error> {
|
||||
Ok(if check_time_is_synchronized().await? {
|
||||
pub async fn root_ca_start_time() -> SystemTime {
|
||||
if check_time_is_synchronized()
|
||||
.await
|
||||
.log_err()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
SystemTime::now()
|
||||
} else {
|
||||
*SOURCE_DATE
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1;
|
||||
|
||||
@@ -357,7 +357,7 @@ pub fn display_services(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum OnionServiceState {
|
||||
Shutdown,
|
||||
@@ -383,7 +383,7 @@ impl From<ArtiOnionServiceState> for OnionServiceState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OnionServiceInfo {
|
||||
pub state: OnionServiceState,
|
||||
|
||||
@@ -45,7 +45,8 @@ const TOR_CONTROL: SocketAddr =
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 1, 1), 9051));
|
||||
const TOR_SOCKS: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 1, 1), 9050));
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TS)]
|
||||
#[ts(type = "string")]
|
||||
pub struct OnionAddress(OnionAddressV3);
|
||||
impl std::fmt::Display for OnionAddress {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@@ -255,6 +256,7 @@ pub fn tor_api<C: Context>() -> ParentHandler<C> {
|
||||
"logs",
|
||||
from_fn_async(crate::logs::cli_logs::<RpcContext, Empty>)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Display Tor logs"),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -312,7 +314,7 @@ pub async fn generate_key(ctx: RpcContext) -> Result<OnionAddress, Error> {
|
||||
.result
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct AddKeyParams {
|
||||
pub key: Base64<[u8; 64]>,
|
||||
}
|
||||
|
||||
@@ -134,7 +134,6 @@ impl VHostController {
|
||||
pub fn dump_table(
|
||||
&self,
|
||||
) -> BTreeMap<JsonKey<u16>, BTreeMap<JsonKey<Option<InternedString>>, EqSet<String>>> {
|
||||
let ip_info = self.interfaces.watcher.ip_info();
|
||||
self.servers.peek(|s| {
|
||||
s.iter()
|
||||
.map(|(k, v)| {
|
||||
|
||||
@@ -343,22 +343,23 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result<
|
||||
.result?;
|
||||
Ok(())
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WifiListInfo {
|
||||
ssids: HashMap<Ssid, SignalStrength>,
|
||||
connected: Option<Ssid>,
|
||||
#[ts(type = "sttring | null")]
|
||||
country: Option<CountryCode>,
|
||||
ethernet: bool,
|
||||
available_wifi: Vec<WifiListOut>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WifiListInfoLow {
|
||||
strength: SignalStrength,
|
||||
security: Vec<String>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WifiListOut {
|
||||
ssid: Ssid,
|
||||
@@ -589,7 +590,7 @@ pub struct NetworkId(String);
|
||||
|
||||
/// Ssid are the names of the wifis, usually human readable.
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
||||
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, TS,
|
||||
)]
|
||||
pub struct Ssid(String);
|
||||
|
||||
@@ -606,6 +607,7 @@ pub struct Ssid(String);
|
||||
Hash,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
TS,
|
||||
)]
|
||||
pub struct SignalStrength(u8);
|
||||
|
||||
|
||||
@@ -388,7 +388,7 @@ impl Map for Notifications {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, HasModel)]
|
||||
#[derive(Debug, Serialize, Deserialize, HasModel, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct Notification {
|
||||
@@ -403,7 +403,7 @@ pub struct Notification {
|
||||
pub seen: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NotificationWithId {
|
||||
id: u32,
|
||||
|
||||
@@ -32,6 +32,7 @@ pub fn admin_api<C: Context>() -> ParentHandler<C> {
|
||||
"add",
|
||||
from_fn_async(cli_add_admin)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Add admin signer"),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -72,7 +73,7 @@ fn signers_api<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
.subcommand(
|
||||
"add",
|
||||
from_fn_async(cli_add_signer).with_about("Add signer"),
|
||||
from_fn_async(cli_add_signer).no_ts().with_about("Add signer"),
|
||||
)
|
||||
.subcommand(
|
||||
"edit",
|
||||
|
||||
@@ -22,18 +22,24 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
|
||||
"dump",
|
||||
from_fn_async(cli_dump)
|
||||
.with_display_serializable()
|
||||
.no_ts()
|
||||
.with_about("Filter/query db to display tables and records"),
|
||||
)
|
||||
.subcommand(
|
||||
"dump",
|
||||
from_fn_async(dump)
|
||||
.with_metadata("admin", Value::Bool(true))
|
||||
.no_cli(),
|
||||
.no_cli()
|
||||
.custom_ts(
|
||||
DumpParams::inline(),
|
||||
format!("{{ id: number; value: unknown }}"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
"apply",
|
||||
from_fn_async(cli_apply)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Update a db record"),
|
||||
)
|
||||
.subcommand(
|
||||
|
||||
@@ -11,7 +11,6 @@ use crate::context::CliContext;
|
||||
use crate::middleware::cors::Cors;
|
||||
use crate::middleware::signature::SignatureAuth;
|
||||
use crate::net::static_server::{bad_request, not_found, server_error};
|
||||
use crate::net::web_server::{Accept, WebServer};
|
||||
use crate::prelude::*;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::device_info::DeviceInfoMiddleware;
|
||||
|
||||
@@ -30,6 +30,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
|
||||
"iso",
|
||||
from_fn_async(cli_get_os_asset)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Download iso"),
|
||||
)
|
||||
.subcommand("img", from_fn_async(get_img).no_cli())
|
||||
@@ -37,6 +38,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
|
||||
"img",
|
||||
from_fn_async(cli_get_os_asset)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Download img"),
|
||||
)
|
||||
.subcommand("squashfs", from_fn_async(get_squashfs).no_cli())
|
||||
@@ -44,6 +46,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
|
||||
"squashfs",
|
||||
from_fn_async(cli_get_os_asset)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Download squashfs"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ pub fn asset_api<C: Context>() -> ParentHandler<C> {
|
||||
"add",
|
||||
from_fn_async(add::cli_add_asset)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Add asset to registry"),
|
||||
)
|
||||
.subcommand("remove", add::remove_api::<C>())
|
||||
@@ -19,6 +20,7 @@ pub fn asset_api<C: Context>() -> ParentHandler<C> {
|
||||
"sign",
|
||||
from_fn_async(sign::cli_sign_asset)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Sign file and add to registry index"),
|
||||
)
|
||||
// TODO: remove signature api
|
||||
|
||||
@@ -46,6 +46,10 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
|
||||
"get",
|
||||
from_fn_async(get_version)
|
||||
.with_metadata("get_device_info", Value::Bool(true))
|
||||
.custom_ts(
|
||||
GetOsVersionParams::inline(),
|
||||
BTreeMap::<String, OsVersionInfo>::inline(),
|
||||
)
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|handle, result| {
|
||||
display_version_info(handle.params, result)
|
||||
|
||||
@@ -29,6 +29,7 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
|
||||
"add",
|
||||
from_fn_async(add::cli_add_package)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Add package to registry index"),
|
||||
)
|
||||
.subcommand(
|
||||
|
||||
@@ -25,11 +25,13 @@ pub fn s9pk() -> ParentHandler<CliContext> {
|
||||
"pack",
|
||||
from_fn_async(super::v2::pack::pack)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Package s9pk input files into valid s9pk"),
|
||||
)
|
||||
.subcommand(
|
||||
"list-ingredients",
|
||||
from_fn_async(super::v2::pack::list_ingredients)
|
||||
.no_ts()
|
||||
.with_custom_display_fn(|_, ingredients| {
|
||||
ingredients
|
||||
.into_iter()
|
||||
@@ -49,16 +51,17 @@ pub fn s9pk() -> ParentHandler<CliContext> {
|
||||
)
|
||||
.subcommand(
|
||||
"edit",
|
||||
edit().with_about("Commands to add an image to an s9pk or edit the manifest"),
|
||||
edit().no_ts().with_about("Commands to add an image to an s9pk or edit the manifest"),
|
||||
)
|
||||
.subcommand(
|
||||
"inspect",
|
||||
inspect().with_about("Commands to display file paths, file contents, or manifest"),
|
||||
inspect().no_ts().with_about("Commands to display file paths, file contents, or manifest"),
|
||||
)
|
||||
.subcommand(
|
||||
"convert",
|
||||
from_fn_async(convert)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Convert s9pk from v1 to v2"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,16 +96,23 @@ pub fn handler<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::<C>::new()
|
||||
.subcommand(
|
||||
"launch",
|
||||
from_fn_blocking(subcontainer::launch).no_display(),
|
||||
from_fn_blocking(subcontainer::launch).no_display().no_ts(),
|
||||
)
|
||||
.subcommand(
|
||||
"launch-init",
|
||||
from_fn_blocking(subcontainer::launch_init).no_display(),
|
||||
from_fn_blocking(subcontainer::launch_init)
|
||||
.no_display()
|
||||
.no_ts(),
|
||||
)
|
||||
.subcommand(
|
||||
"exec",
|
||||
from_fn_blocking(subcontainer::exec).no_display().no_ts(),
|
||||
)
|
||||
.subcommand("exec", from_fn_blocking(subcontainer::exec).no_display())
|
||||
.subcommand(
|
||||
"exec-command",
|
||||
from_fn_blocking(subcontainer::exec_command).no_display(),
|
||||
from_fn_blocking(subcontainer::exec_command)
|
||||
.no_display()
|
||||
.no_ts(),
|
||||
)
|
||||
.subcommand(
|
||||
"create-fs",
|
||||
|
||||
@@ -60,13 +60,14 @@ pub fn setup<C: Context>() -> ParentHandler<C> {
|
||||
"get-pubkey",
|
||||
from_fn_async(get_pubkey)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.no_cli(),
|
||||
.no_cli()
|
||||
.custom_ts("{}".to_string(), "unknown".to_string()),
|
||||
)
|
||||
.subcommand("exit", from_fn_async(exit).no_cli())
|
||||
.subcommand("logs", crate::system::logs::<SetupContext>())
|
||||
.subcommand("logs", crate::system::logs::<SetupContext>().no_ts())
|
||||
.subcommand(
|
||||
"logs",
|
||||
from_fn_async(crate::logs::cli_logs::<SetupContext, Empty>).no_display(),
|
||||
from_fn_async(crate::logs::cli_logs::<SetupContext, Empty>).no_display().no_ts(),
|
||||
)
|
||||
.subcommand("restart", from_fn_async(restart).no_cli())
|
||||
}
|
||||
@@ -499,7 +500,7 @@ async fn fresh_setup(
|
||||
..
|
||||
}: SetupExecuteProgress,
|
||||
) -> Result<(SetupResult, RpcContext), Error> {
|
||||
let account = AccountInfo::new(start_os_password, root_ca_start_time().await?)?;
|
||||
let account = AccountInfo::new(start_os_password, root_ca_start_time().await)?;
|
||||
let db = ctx.db().await?;
|
||||
let kiosk = Some(kiosk.unwrap_or(true)).filter(|_| &*PLATFORM != "raspberrypi");
|
||||
sync_kiosk(kiosk).await?;
|
||||
|
||||
@@ -59,7 +59,7 @@ impl ValueParserFactory for SshPubKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SshKeyResponse {
|
||||
pub alg: String,
|
||||
|
||||
@@ -186,7 +186,7 @@ pub async fn governor(
|
||||
Ok(GovernorInfo { current, available })
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, TS)]
|
||||
pub struct TimeInfo {
|
||||
now: String,
|
||||
uptime: u64,
|
||||
@@ -319,8 +319,8 @@ pub fn kiosk<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MetricLeaf<T> {
|
||||
#[derive(Serialize, Deserialize, TS)]
|
||||
pub struct MetricLeaf<T: TS> {
|
||||
value: T,
|
||||
unit: Option<String>,
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use imbl_value::InternedString;
|
||||
use ipnet::Ipv4Net;
|
||||
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
@@ -54,9 +55,10 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubnetParams {
|
||||
#[ts(type = "string")]
|
||||
subnet: Ipv4Net,
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ pub async fn list_keys(ctx: TunnelContext) -> Result<HashMap<AnyVerifyingKey, Si
|
||||
ctx.db.peek().await.into_auth_pubkeys().de()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
|
||||
pub struct SetPasswordParams {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
@@ -69,13 +69,18 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
|
||||
"dump",
|
||||
from_fn_async(cli_dump)
|
||||
.with_display_serializable()
|
||||
.no_ts()
|
||||
.with_about("Filter/query db to display tables and records"),
|
||||
)
|
||||
.subcommand(
|
||||
"dump",
|
||||
from_fn_async(dump)
|
||||
.with_metadata("admin", Value::Bool(true))
|
||||
.no_cli(),
|
||||
.no_cli()
|
||||
.custom_ts(
|
||||
DumpParams::inline(),
|
||||
format!("{{ id: number; value: unknown }}"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
"subscribe",
|
||||
@@ -87,6 +92,7 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
|
||||
"apply",
|
||||
from_fn_async(cli_apply)
|
||||
.no_display()
|
||||
.no_ts()
|
||||
.with_about("Update a db record"),
|
||||
)
|
||||
.subcommand(
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use hickory_client::proto::rr::rdata::cert;
|
||||
use imbl_value::{InternedString, json};
|
||||
use itertools::Itertools;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
@@ -12,7 +11,6 @@ use rpc_toolkit::{
|
||||
Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async, from_fn_async_local,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio_rustls::rustls::ServerConfig;
|
||||
use tokio_rustls::rustls::crypto::CryptoProvider;
|
||||
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||
@@ -20,7 +18,8 @@ use tokio_rustls::rustls::server::ClientHello;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::net::ssl::SANInfo;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::net::ssl::{SANInfo, root_ca_start_time};
|
||||
use crate::net::tls::TlsHandler;
|
||||
use crate::net::web_server::Accept;
|
||||
use crate::prelude::*;
|
||||
@@ -134,7 +133,7 @@ pub fn web_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"generate-certificate",
|
||||
from_fn_async(generate_certificate)
|
||||
.with_about("Generate a self signed certificaet to use for the webserver")
|
||||
.with_about("Generate a certificate to use for the webserver")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -286,11 +285,21 @@ pub struct GenerateCertParams {
|
||||
pub async fn generate_certificate(
|
||||
ctx: TunnelContext,
|
||||
GenerateCertParams { subject }: GenerateCertParams,
|
||||
) -> Result<Pem<X509>, Error> {
|
||||
) -> Result<Pem<Vec<X509>>, Error> {
|
||||
let saninfo = SANInfo::new(&subject.into_iter().collect());
|
||||
|
||||
let root_key = crate::net::ssl::generate_key()?;
|
||||
let root_cert = crate::net::ssl::make_root_cert(
|
||||
&root_key,
|
||||
&Hostname("start-tunnel".into()),
|
||||
root_ca_start_time().await,
|
||||
)?;
|
||||
let int_key = crate::net::ssl::generate_key()?;
|
||||
let int_cert = crate::net::ssl::make_int_cert((&root_key, &root_cert), &int_key)?;
|
||||
|
||||
let key = crate::net::ssl::generate_key()?;
|
||||
let cert = crate::net::ssl::make_self_signed((&key, &saninfo))?;
|
||||
let cert = crate::net::ssl::make_leaf_cert((&int_key, &int_cert), (&key, &saninfo))?;
|
||||
let chain = Pem(vec![cert, int_cert, root_cert]);
|
||||
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
@@ -298,13 +307,13 @@ pub async fn generate_certificate(
|
||||
.as_certificate_mut()
|
||||
.ser(&Some(TunnelCertData {
|
||||
key: Pem(key),
|
||||
cert: Pem(vec![cert.clone()]),
|
||||
cert: chain.clone(),
|
||||
}))
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
|
||||
Ok(Pem(cert))
|
||||
Ok(chain)
|
||||
}
|
||||
|
||||
pub async fn get_certificate(ctx: TunnelContext) -> Result<Option<Pem<Vec<X509>>>, Error> {
|
||||
@@ -501,8 +510,12 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
|
||||
let cert = from_value::<Pem<Vec<X509>>>(
|
||||
ctx.call_remote::<TunnelContext>("web.get-certificate", json!({}))
|
||||
.await?,
|
||||
)?;
|
||||
println!("📝 SSL Certificate:");
|
||||
)?
|
||||
.0
|
||||
.pop()
|
||||
.map(Pem)
|
||||
.or_not_found("certificate in chain")?;
|
||||
println!("📝 Root SSL Certificate:");
|
||||
print!("{cert}");
|
||||
|
||||
println!(concat!(
|
||||
@@ -594,7 +607,7 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
|
||||
impl std::fmt::Display for Choice {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Generate => write!(f, "Generate a Self Signed Certificate"),
|
||||
Self::Generate => write!(f, "Generate an SSL certificate"),
|
||||
Self::Provide => write!(f, "Provide your own certificate and key"),
|
||||
}
|
||||
}
|
||||
@@ -602,7 +615,7 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
|
||||
let options = vec![Choice::Generate, Choice::Provide];
|
||||
let choice = choose(
|
||||
concat!(
|
||||
"Select whether to autogenerate a self-signed SSL certificate ",
|
||||
"Select whether to generate an SSL certificate ",
|
||||
"or provide your own certificate and key:"
|
||||
),
|
||||
&options,
|
||||
|
||||
@@ -209,7 +209,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize, TS)]
|
||||
pub struct ClientConfig {
|
||||
client_config: WgConfig,
|
||||
client_addr: Ipv4Addr,
|
||||
|
||||
@@ -217,7 +217,7 @@ pub async fn cli_update_system(
|
||||
}
|
||||
|
||||
/// What is the status of the updates?
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum UpdateResult {
|
||||
NoUpdates,
|
||||
|
||||
@@ -3,8 +3,9 @@ use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
#[derive(Clone, Serialize, TS)]
|
||||
pub struct EqSet<T: Eq>(Vec<T>);
|
||||
impl<T: Eq> Default for EqSet<T> {
|
||||
fn default() -> Self {
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::path::Path;
|
||||
use clap::Parser;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::CAP_10_MiB;
|
||||
use crate::context::CliContext;
|
||||
@@ -21,7 +22,7 @@ pub fn util<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||
#[derive(Debug, Deserialize, Serialize, Parser, TS)]
|
||||
pub struct B3sumParams {
|
||||
#[arg(long = "no-mmap", action = clap::ArgAction::SetFalse)]
|
||||
allow_mmap: bool,
|
||||
|
||||
@@ -12,7 +12,8 @@ use models::FromStrParser;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::X509;
|
||||
use rpc_toolkit::{
|
||||
CliBindings, Context, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, PrintCliResult,
|
||||
CliBindings, Context, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTS, HandlerTypes,
|
||||
PrintCliResult,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::ser::{SerializeMap, SerializeSeq};
|
||||
@@ -451,6 +452,36 @@ impl<T: CommandFactory> CommandFactory for WithIoFormat<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: TS> TS for WithIoFormat<T> {
|
||||
type WithoutGenerics = T::WithoutGenerics;
|
||||
fn decl() -> String {
|
||||
T::decl()
|
||||
}
|
||||
fn decl_concrete() -> String {
|
||||
T::decl_concrete()
|
||||
}
|
||||
fn name() -> String {
|
||||
T::name()
|
||||
}
|
||||
fn inline() -> String {
|
||||
T::inline()
|
||||
}
|
||||
fn inline_flattened() -> String {
|
||||
T::inline_flattened()
|
||||
}
|
||||
fn visit_dependencies(v: &mut impl ts_rs::TypeVisitor)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
T::visit_dependencies(v);
|
||||
}
|
||||
fn visit_generics(v: &mut impl ts_rs::TypeVisitor)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
T::visit_generics(v);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HandlerExtSerde<C: Context>: HandlerFor<C> {
|
||||
fn with_display_serializable(self) -> DisplaySerializable<Self>;
|
||||
@@ -469,6 +500,11 @@ impl<T: HandlerTypes> HandlerTypes for DisplaySerializable<T> {
|
||||
type Ok = T::Ok;
|
||||
type Err = T::Err;
|
||||
}
|
||||
impl<T: HandlerTS> HandlerTS for DisplaySerializable<T> {
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.0.type_info()
|
||||
}
|
||||
}
|
||||
impl<T: HandlerFor<C>, C: Context> HandlerFor<C> for DisplaySerializable<T> {
|
||||
fn handle_sync(
|
||||
&self,
|
||||
@@ -891,6 +927,48 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TS, Container> TS for Reversible<T, Container>
|
||||
where
|
||||
for<'a> &'a Container: IntoDoubleEndedIterator<&'a T>,
|
||||
Container: TS,
|
||||
{
|
||||
type WithoutGenerics = Reversible<T, Container>;
|
||||
|
||||
fn decl() -> String {
|
||||
Container::decl()
|
||||
}
|
||||
|
||||
fn decl_concrete() -> String {
|
||||
Container::decl_concrete()
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
Container::name()
|
||||
}
|
||||
|
||||
fn inline() -> String {
|
||||
Container::inline()
|
||||
}
|
||||
|
||||
fn inline_flattened() -> String {
|
||||
Container::inline_flattened()
|
||||
}
|
||||
|
||||
fn visit_dependencies(v: &mut impl ts_rs::TypeVisitor)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
Container::visit_dependencies(v);
|
||||
}
|
||||
|
||||
fn visit_generics(v: &mut impl ts_rs::TypeVisitor)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
Container::visit_generics(v);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyVal<K, V> {
|
||||
pub key: K,
|
||||
pub value: V,
|
||||
@@ -929,6 +1007,47 @@ impl<'de, K: Deserialize<'de>, V: Deserialize<'de>> Deserialize<'de> for KeyVal<
|
||||
deserializer.deserialize_map(Visitor(PhantomData))
|
||||
}
|
||||
}
|
||||
impl<K: TS, V: TS> TS for KeyVal<K, V> {
|
||||
type WithoutGenerics = KeyVal<ts_rs::Dummy, ts_rs::Dummy>;
|
||||
fn decl() -> String {
|
||||
format!("type {}<K, V> = {{ [T in K]: V }}", Self::name())
|
||||
}
|
||||
fn decl_concrete() -> String {
|
||||
format!(
|
||||
"type {} = {{ [T in {}]: {} }}",
|
||||
Self::name(),
|
||||
K::name(),
|
||||
V::name()
|
||||
)
|
||||
}
|
||||
fn name() -> String {
|
||||
"KeyVal".into()
|
||||
}
|
||||
fn inline() -> String {
|
||||
format!("{{ [T in {}]: {} }}", K::inline(), V::inline())
|
||||
}
|
||||
fn inline_flattened() -> String {
|
||||
Self::inline()
|
||||
}
|
||||
fn visit_dependencies(v: &mut impl ts_rs::TypeVisitor)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
v.visit::<K>();
|
||||
K::visit_dependencies(v);
|
||||
v.visit::<V>();
|
||||
V::visit_dependencies(v);
|
||||
}
|
||||
fn visit_generics(v: &mut impl ts_rs::TypeVisitor)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
v.visit::<K>();
|
||||
K::visit_generics(v);
|
||||
v.visit::<V>();
|
||||
V::visit_generics(v);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(TS)]
|
||||
#[ts(type = "string", concrete(T = Vec<u8>))]
|
||||
|
||||
@@ -349,22 +349,23 @@ impl<W: AsyncWrite + AsyncSeek> AsyncWrite for MetadataBlocksWriter<W> {
|
||||
*this.size_addr = Some(pos);
|
||||
None
|
||||
};
|
||||
this.output.unwritten_mut()[..2]
|
||||
.copy_from_slice(&u16::to_le_bytes(size)[..]);
|
||||
this.output.unwritten_mut()[..2].copy_from_slice(&u16::to_le_bytes(size)[..]);
|
||||
this.output.advance(2);
|
||||
*this.write_state = WriteState::WritingOutput(Box::new(if let Some(end) = done {
|
||||
WriteState::SeekingToEnd(end)
|
||||
} else {
|
||||
WriteState::EncodingInput
|
||||
}));
|
||||
*this.write_state =
|
||||
WriteState::WritingOutput(Box::new(if let Some(end) = done {
|
||||
WriteState::SeekingToEnd(end)
|
||||
} else {
|
||||
WriteState::EncodingInput
|
||||
}));
|
||||
}
|
||||
|
||||
WriteState::WritingOutput(next) => {
|
||||
if this.output.written().len() > *this.output_flushed {
|
||||
let n = ready!(this
|
||||
.writer
|
||||
.as_mut()
|
||||
.poll_write(cx, &this.output.written()[*this.output_flushed..]))?;
|
||||
let n = ready!(
|
||||
this.writer
|
||||
.as_mut()
|
||||
.poll_write(cx, &this.output.written()[*this.output_flushed..])
|
||||
)?;
|
||||
*this.output_flushed += n;
|
||||
} else {
|
||||
this.output.reset();
|
||||
@@ -375,10 +376,7 @@ impl<W: AsyncWrite + AsyncSeek> AsyncWrite for MetadataBlocksWriter<W> {
|
||||
|
||||
WriteState::EncodingInput => {
|
||||
let encoder = this.zstd.get_or_insert_with(|| ZstdEncoder::new(22));
|
||||
encoder.encode(
|
||||
&mut PartialBuffer::new(this.input.written()),
|
||||
this.output,
|
||||
)?;
|
||||
encoder.encode(&mut PartialBuffer::new(this.input.written()), this.output)?;
|
||||
let compressed = if !encoder.finish(this.output)? {
|
||||
std::mem::swap(this.output, this.input);
|
||||
false
|
||||
@@ -387,12 +385,11 @@ impl<W: AsyncWrite + AsyncSeek> AsyncWrite for MetadataBlocksWriter<W> {
|
||||
};
|
||||
*this.zstd = None;
|
||||
this.input.reset();
|
||||
*this.write_state = WriteState::WritingOutput(Box::new(
|
||||
WriteState::WritingSizeHeader(
|
||||
*this.write_state =
|
||||
WriteState::WritingOutput(Box::new(WriteState::WritingSizeHeader(
|
||||
this.output.written().len() as u16
|
||||
| if compressed { 0 } else { 0x8000 },
|
||||
),
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
WriteState::SeekingToEnd(end_addr) => {
|
||||
@@ -441,10 +438,12 @@ pub struct MetadataBlocksReader<R> {
|
||||
#[pin]
|
||||
reader: R,
|
||||
size_buf: PartialBuffer<[u8; 2]>,
|
||||
compressed: PartialBuffer<[u8; 8192]>,
|
||||
compressed: [u8; 8192],
|
||||
compressed_size: usize,
|
||||
compressed_pos: usize,
|
||||
is_compressed: bool,
|
||||
output: PartialBuffer<[u8; 8192]>,
|
||||
output: [u8; 8192],
|
||||
output_size: usize,
|
||||
output_pos: usize,
|
||||
zstd: Option<ZstdDecoder>,
|
||||
state: ReadState,
|
||||
@@ -464,10 +463,12 @@ impl<R> MetadataBlocksReader<R> {
|
||||
Self {
|
||||
reader,
|
||||
size_buf: PartialBuffer::new([0; 2]),
|
||||
compressed: PartialBuffer::new([0; 8192]),
|
||||
compressed: [0; 8192],
|
||||
compressed_size: 0,
|
||||
compressed_pos: 0,
|
||||
is_compressed: false,
|
||||
output: PartialBuffer::new([0; 8192]),
|
||||
output: [0; 8192],
|
||||
output_size: 0,
|
||||
output_pos: 0,
|
||||
zstd: None,
|
||||
state: ReadState::ReadingSize,
|
||||
@@ -515,14 +516,16 @@ impl<R: Read> Read for MetadataBlocksReader<R> {
|
||||
));
|
||||
}
|
||||
|
||||
self.compressed.reset();
|
||||
self.compressed_pos = 0;
|
||||
self.size_buf.reset();
|
||||
self.state = ReadState::ReadingData;
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadState::ReadingData => {
|
||||
let n = self.reader.read(self.compressed.unwritten_mut())?;
|
||||
let n = self
|
||||
.reader
|
||||
.read(&mut self.compressed[self.compressed_pos..self.compressed_size])?;
|
||||
if n == 0 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::UnexpectedEof,
|
||||
@@ -530,59 +533,67 @@ impl<R: Read> Read for MetadataBlocksReader<R> {
|
||||
));
|
||||
}
|
||||
|
||||
self.compressed.advance(n);
|
||||
self.compressed_pos += n;
|
||||
|
||||
if !self.compressed.unwritten().is_empty() {
|
||||
if self.compressed_pos < self.compressed_size {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.output_pos = 0;
|
||||
self.output.reset();
|
||||
self.output_size = 0;
|
||||
if self.is_compressed {
|
||||
self.zstd = Some(ZstdDecoder::new());
|
||||
self.state = ReadState::Decompressing;
|
||||
} else {
|
||||
self.output
|
||||
.copy_unwritten_from(&mut PartialBuffer::new(self.compressed.written()));
|
||||
// For uncompressed blocks, copy directly to output
|
||||
self.output[..self.compressed_size]
|
||||
.copy_from_slice(&self.compressed[..self.compressed_size]);
|
||||
self.output_size = self.compressed_size;
|
||||
self.state = ReadState::Outputting;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadState::Decompressing => {
|
||||
if self.output.unwritten().is_empty() {
|
||||
let mut output = PartialBuffer::new(&mut self.output);
|
||||
output.advance(self.output_size);
|
||||
|
||||
if output.unwritten().is_empty() {
|
||||
self.state = ReadState::Outputting;
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut input = PartialBuffer::new(self.compressed.written());
|
||||
let mut input = PartialBuffer::new(&self.compressed[..self.compressed_size]);
|
||||
let decoder = self.zstd.as_mut().unwrap();
|
||||
|
||||
if decoder.decode(&mut input, &mut self.output)? {
|
||||
if decoder.decode(&mut input, &mut output)? {
|
||||
self.zstd = None;
|
||||
}
|
||||
self.output_size = output.written().len();
|
||||
|
||||
if self.output_size > self.output_pos {
|
||||
self.state = ReadState::Outputting;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadState::Outputting => {
|
||||
let available = self.output.written().len() - self.output_pos;
|
||||
let available = self.output_size - self.output_pos;
|
||||
if available == 0 {
|
||||
if self.zstd.is_none() {
|
||||
self.state = ReadState::ReadingSize;
|
||||
continue;
|
||||
} else {
|
||||
self.output.reset();
|
||||
self.output_pos = 0;
|
||||
self.output_size = 0;
|
||||
self.state = ReadState::Decompressing;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let to_copy = available.min(buf.len());
|
||||
buf[..to_copy].copy_from_slice(
|
||||
&self.output.written()[self.output_pos..self.output_pos + to_copy],
|
||||
);
|
||||
buf[..to_copy]
|
||||
.copy_from_slice(&self.output[self.output_pos..self.output_pos + to_copy]);
|
||||
self.output_pos += to_copy;
|
||||
return Ok(to_copy);
|
||||
}
|
||||
@@ -629,26 +640,30 @@ impl<R: AsyncRead> AsyncRead for MetadataBlocksReader<R> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let size_header = u16::from_le_bytes(*this.size_buf.written());
|
||||
let size_header = u16::from_le_bytes([
|
||||
this.size_buf.written()[0],
|
||||
this.size_buf.written()[1],
|
||||
]);
|
||||
*this.is_compressed = (size_header & 0x8000) == 0;
|
||||
let size = (size_header & 0x7FFF) as usize;
|
||||
*this.compressed_size = (size_header & 0x7FFF) as usize;
|
||||
|
||||
if size == 0 || size > 8192 {
|
||||
if *this.compressed_size == 0 || *this.compressed_size > 8192 {
|
||||
return Poll::Ready(Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!("Invalid metadata block size: {}", size),
|
||||
format!("Invalid metadata block size: {}", *this.compressed_size),
|
||||
)));
|
||||
}
|
||||
|
||||
this.compressed.reset();
|
||||
this.compressed.reserve(size);
|
||||
*this.compressed_pos = 0;
|
||||
this.size_buf.reset();
|
||||
*this.state = ReadState::ReadingData;
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadState::ReadingData => {
|
||||
let mut read_buf = tokio::io::ReadBuf::new(this.compressed.unwritten_mut());
|
||||
let mut read_buf = tokio::io::ReadBuf::new(
|
||||
&mut this.compressed[*this.compressed_pos..*this.compressed_size],
|
||||
);
|
||||
let before = read_buf.filled().len();
|
||||
ready!(this.reader.as_mut().poll_read(cx, &mut read_buf))?;
|
||||
let n = read_buf.filled().len() - before;
|
||||
@@ -660,59 +675,66 @@ impl<R: AsyncRead> AsyncRead for MetadataBlocksReader<R> {
|
||||
)));
|
||||
}
|
||||
|
||||
this.compressed.advance(n);
|
||||
*this.compressed_pos += n;
|
||||
|
||||
if !this.compressed.unwritten().is_empty() {
|
||||
if *this.compressed_pos < *this.compressed_size {
|
||||
continue;
|
||||
}
|
||||
|
||||
*this.output_pos = 0;
|
||||
this.output.reset();
|
||||
*this.output_size = 0;
|
||||
if *this.is_compressed {
|
||||
*this.zstd = Some(ZstdDecoder::new());
|
||||
*this.state = ReadState::Decompressing;
|
||||
} else {
|
||||
this.output
|
||||
.copy_unwritten_from(&mut PartialBuffer::new(this.compressed.written()));
|
||||
// For uncompressed blocks, copy directly to output
|
||||
this.output[..*this.compressed_size]
|
||||
.copy_from_slice(&this.compressed[..*this.compressed_size]);
|
||||
*this.output_size = *this.compressed_size;
|
||||
*this.state = ReadState::Outputting;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadState::Decompressing => {
|
||||
if this.output.unwritten().is_empty() {
|
||||
let mut output = PartialBuffer::new(this.output);
|
||||
output.advance(*this.output_size);
|
||||
|
||||
if output.unwritten().is_empty() {
|
||||
*this.state = ReadState::Outputting;
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut input = PartialBuffer::new(this.compressed.written());
|
||||
let mut input = PartialBuffer::new(&this.compressed[..*this.compressed_size]);
|
||||
let decoder = this.zstd.as_mut().unwrap();
|
||||
|
||||
if decoder.decode(&mut input, this.output)? {
|
||||
if decoder.decode(&mut input, &mut output)? {
|
||||
*this.zstd = None;
|
||||
}
|
||||
*this.output_size = output.written().len();
|
||||
|
||||
if *this.output_size > *this.output_pos {
|
||||
*this.state = ReadState::Outputting;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadState::Outputting => {
|
||||
let available = this.output.written().len() - *this.output_pos;
|
||||
let available = *this.output_size - *this.output_pos;
|
||||
if available == 0 {
|
||||
if this.zstd.is_none() {
|
||||
*this.state = ReadState::ReadingSize;
|
||||
continue;
|
||||
} else {
|
||||
this.output.reset();
|
||||
*this.output_pos = 0;
|
||||
*this.output_size = 0;
|
||||
*this.state = ReadState::Decompressing;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let to_copy = available.min(buf.remaining());
|
||||
buf.put_slice(
|
||||
&this.output.written()[*this.output_pos..*this.output_pos + to_copy],
|
||||
);
|
||||
buf.put_slice(&this.output[*this.output_pos..*this.output_pos + to_copy]);
|
||||
*this.output_pos += to_copy;
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use futures::future::BoxFuture;
|
||||
use futures::{Future, FutureExt};
|
||||
use imbl_value::{InternedString, to_value};
|
||||
use patch_db::json_ptr::ROOT;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::Error;
|
||||
use crate::context::RpcContext;
|
||||
|
||||
Reference in New Issue
Block a user