mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
domains api + migration
This commit is contained in:
47
core/Cargo.lock
generated
47
core/Cargo.lock
generated
@@ -1923,6 +1923,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.6.1"
|
||||
@@ -3811,6 +3817,15 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.3"
|
||||
@@ -4700,6 +4715,16 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||
dependencies = [
|
||||
"endian-type",
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
@@ -6060,7 +6085,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "start-os"
|
||||
version = "0.4.0-alpha.9"
|
||||
version = "0.4.0-alpha.10"
|
||||
dependencies = [
|
||||
"aes 0.7.5",
|
||||
"async-acme",
|
||||
@@ -6191,6 +6216,7 @@ dependencies = [
|
||||
"tracing-futures",
|
||||
"tracing-journald",
|
||||
"tracing-subscriber",
|
||||
"trust-dns-client",
|
||||
"trust-dns-server",
|
||||
"ts-rs",
|
||||
"typed-builder",
|
||||
@@ -6932,6 +6958,25 @@ dependencies = [
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-client"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14135e72c7e6d4c9b6902d4437881a8598f0145dbb2e3f86f92dbad845b61e63"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"data-encoding",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"once_cell",
|
||||
"radix_trie",
|
||||
"rand 0.8.5",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"trust-dns-proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-proto"
|
||||
version = "0.23.2"
|
||||
|
||||
@@ -14,7 +14,7 @@ keywords = [
|
||||
name = "start-os"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Start9Labs/start-os"
|
||||
version = "0.4.0-alpha.9" # VERSION_BUMP
|
||||
version = "0.4.0-alpha.10" # VERSION_BUMP
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
@@ -228,7 +228,8 @@ tracing-error = "0.2.0"
|
||||
tracing-futures = "0.2.5"
|
||||
tracing-journald = "0.3.0"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
trust-dns-server = "0.23.1"
|
||||
trust-dns-server = "0.23.2"
|
||||
trust-dns-client = "0.23.2"
|
||||
ts-rs = { git = "https://github.com/dr-bonez/ts-rs.git", branch = "feature/top-level-as" } # "8.1.0"
|
||||
typed-builder = "0.21.0"
|
||||
unix-named-pipe = "0.2.0"
|
||||
|
||||
@@ -12,7 +12,7 @@ use tracing::instrument;
|
||||
use crate::context::config::ServerConfig;
|
||||
use crate::context::rpc::InitRpcContextPhases;
|
||||
use crate::context::{DiagnosticContext, InitContext, RpcContext};
|
||||
use crate::net::network_interface::SelfContainedNetworkInterfaceListener;
|
||||
use crate::net::gateway::SelfContainedNetworkInterfaceListener;
|
||||
use crate::net::web_server::{Acceptor, UpgradableListener, WebServer};
|
||||
use crate::shutdown::Shutdown;
|
||||
use crate::system::launch_metrics_task;
|
||||
|
||||
@@ -92,8 +92,9 @@ impl Public {
|
||||
enabled: true,
|
||||
..Default::default()
|
||||
},
|
||||
network_interfaces: OrdMap::new(),
|
||||
gateways: OrdMap::new(),
|
||||
acme: BTreeMap::new(),
|
||||
domains: BTreeMap::new(),
|
||||
},
|
||||
status_info: ServerStatus {
|
||||
backup_progress: None,
|
||||
@@ -191,9 +192,12 @@ pub struct NetworkInfo {
|
||||
pub host: Host,
|
||||
#[ts(as = "BTreeMap::<GatewayId, NetworkInterfaceInfo>")]
|
||||
#[serde(default)]
|
||||
pub network_interfaces: OrdMap<GatewayId, NetworkInterfaceInfo>,
|
||||
pub gateways: OrdMap<GatewayId, NetworkInterfaceInfo>,
|
||||
#[serde(default)]
|
||||
pub acme: BTreeMap<AcmeProvider, AcmeSettings>,
|
||||
#[serde(default)]
|
||||
#[ts(as = "BTreeMap::<String, DomainSettings>")]
|
||||
pub domains: BTreeMap<InternedString, DomainSettings>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
@@ -303,6 +307,14 @@ pub struct AcmeSettings {
|
||||
pub contact: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[model = "Model<Self>"]
|
||||
#[ts(export)]
|
||||
pub struct DomainSettings {
|
||||
pub gateway: GatewayId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
#[model = "Model<Self>"]
|
||||
#[ts(export)]
|
||||
|
||||
@@ -150,7 +150,7 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
.subcommand(
|
||||
"net",
|
||||
net::net::<C>().with_about("Network commands related to tor and dhcp"),
|
||||
net::net_api::<C>().with_about("Network commands related to tor and dhcp"),
|
||||
)
|
||||
.subcommand(
|
||||
"auth",
|
||||
|
||||
@@ -174,7 +174,7 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acme<C: Context>() -> ParentHandler<C> {
|
||||
pub fn acme_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"init",
|
||||
|
||||
204
core/startos/src/net/domain.rs
Normal file
204
core/startos/src/net/domain.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use clap::Parser;
|
||||
use futures::TryFutureExt;
|
||||
use helpers::NonDetachingJoinHandle;
|
||||
use imbl_value::InternedString;
|
||||
use models::GatewayId;
|
||||
use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::DomainSettings;
|
||||
use crate::prelude::*;
|
||||
use crate::util::new_guid;
|
||||
use crate::util::serde::{display_serializable, HandlerExtSerde};
|
||||
|
||||
pub fn domain_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"list",
|
||||
from_fn_async(list)
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||
use prettytable::*;
|
||||
|
||||
if let Some(format) = params.format {
|
||||
return display_serializable(format, res);
|
||||
}
|
||||
|
||||
let mut table = Table::new();
|
||||
table.add_row(row![bc => "DOMAIN", "GATEWAY"]);
|
||||
for (domain, info) in res {
|
||||
table.add_row(row![domain, info.gateway]);
|
||||
}
|
||||
|
||||
table.print_tty(false)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.with_about("List domains available to StartOS")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"add",
|
||||
from_fn_async(add)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Add a domain for use with StartOS")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"remove",
|
||||
from_fn_async(remove)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Remove a domain for use with StartOS")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"test-dns",
|
||||
from_fn_async(test_dns)
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||
use prettytable::*;
|
||||
|
||||
if let Some(format) = params.format {
|
||||
return display_serializable(format, res);
|
||||
}
|
||||
|
||||
let mut table = Table::new();
|
||||
table.add_row(row![bc -> "ROOT", if res.root { "✅️" } else { "❌️" }]);
|
||||
table.add_row(row![bc -> "WILDCARD", if res.wildcard { "✅️" } else { "❌️" }]);
|
||||
|
||||
table.print_tty(false)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.with_about("Test the DNS configuration for a domain"),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn list(ctx: RpcContext) -> Result<BTreeMap<InternedString, DomainSettings>, Error> {
|
||||
ctx.db
|
||||
.peek()
|
||||
.await
|
||||
.into_public()
|
||||
.into_server_info()
|
||||
.into_network()
|
||||
.into_domains()
|
||||
.de()
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
pub struct AddDomainParams {
|
||||
pub fqdn: InternedString,
|
||||
pub gateway: GatewayId,
|
||||
}
|
||||
|
||||
pub async fn add(
|
||||
ctx: RpcContext,
|
||||
AddDomainParams { fqdn, gateway }: AddDomainParams,
|
||||
) -> Result<(), Error> {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
db.as_public_mut()
|
||||
.as_server_info_mut()
|
||||
.as_network_mut()
|
||||
.as_domains_mut()
|
||||
.insert(&fqdn, &DomainSettings { gateway })
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
pub struct RemoveDomainParams {
|
||||
pub fqdn: InternedString,
|
||||
}
|
||||
|
||||
pub async fn remove(
|
||||
ctx: RpcContext,
|
||||
RemoveDomainParams { fqdn }: RemoveDomainParams,
|
||||
) -> Result<(), Error> {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
db.as_public_mut()
|
||||
.as_server_info_mut()
|
||||
.as_network_mut()
|
||||
.as_domains_mut()
|
||||
.remove(&fqdn)
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct TestDnsResult {
|
||||
pub root: bool,
|
||||
pub wildcard: bool,
|
||||
}
|
||||
|
||||
pub async fn test_dns(
|
||||
ctx: RpcContext,
|
||||
AddDomainParams { fqdn, ref gateway }: AddDomainParams,
|
||||
) -> Result<TestDnsResult, Error> {
|
||||
use tokio::net::UdpSocket;
|
||||
use trust_dns_client::client::{AsyncClient, ClientHandle};
|
||||
use trust_dns_client::op::DnsResponse;
|
||||
use trust_dns_client::proto::error::ProtoError;
|
||||
use trust_dns_client::rr::{DNSClass, Name, RecordType};
|
||||
use trust_dns_client::udp::UdpClientStream;
|
||||
|
||||
let wan_ip = ctx
|
||||
.db
|
||||
.peek()
|
||||
.await
|
||||
.into_public()
|
||||
.into_server_info()
|
||||
.into_network()
|
||||
.into_gateways()
|
||||
.into_idx(&gateway)
|
||||
.or_not_found(&gateway)?
|
||||
.into_ip_info()
|
||||
.transpose()
|
||||
.and_then(|i| i.into_wan_ip().transpose())
|
||||
.or_not_found(lazy_format!("WAN IP for {gateway}"))?
|
||||
.de()?;
|
||||
let stream = UdpClientStream::<UdpSocket>::new(([127, 0, 0, 53], 53).into());
|
||||
let (mut client, bg) = AsyncClient::connect(stream.map_err(ProtoError::from))
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
let bg: NonDetachingJoinHandle<_> = tokio::spawn(bg).into();
|
||||
|
||||
let root = fqdn.parse::<Name>().with_kind(ErrorKind::Network)?;
|
||||
let wildcard = new_guid()
|
||||
.parse::<Name>()
|
||||
.with_kind(ErrorKind::Network)?
|
||||
.append_domain(&root)
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
let q_root = client
|
||||
.query(root, DNSClass::IN, RecordType::A)
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
let q_wildcard = client
|
||||
.query(wildcard, DNSClass::IN, RecordType::A)
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
|
||||
bg.abort();
|
||||
|
||||
let check_q = |q: DnsResponse| {
|
||||
q.answers().iter().any(|a| {
|
||||
a.data()
|
||||
.and_then(|d| d.as_a())
|
||||
.map_or(false, |d| d.0 == wan_ip)
|
||||
})
|
||||
};
|
||||
Ok(TestDnsResult {
|
||||
root: check_q(q_root),
|
||||
wildcard: check_q(q_wildcard),
|
||||
})
|
||||
}
|
||||
@@ -12,7 +12,7 @@ use tokio::process::Command;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::net::network_interface::{DynInterfaceFilter, InterfaceFilter};
|
||||
use crate::net::gateway::{DynInterfaceFilter, InterfaceFilter};
|
||||
use crate::net::utils::ipv6_is_link_local;
|
||||
use crate::prelude::*;
|
||||
use crate::util::sync::Watch;
|
||||
|
||||
@@ -32,7 +32,7 @@ use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceType};
|
||||
use crate::db::model::Database;
|
||||
use crate::net::forward::START9_BRIDGE_IFACE;
|
||||
use crate::net::network_interface::device::DeviceProxy;
|
||||
use crate::net::gateway::device::DeviceProxy;
|
||||
use crate::net::utils::ipv6_is_link_local;
|
||||
use crate::net::web_server::Accept;
|
||||
use crate::prelude::*;
|
||||
@@ -43,7 +43,7 @@ use crate::util::serde::{display_serializable, HandlerExtSerde};
|
||||
use crate::util::sync::{SyncMutex, Watch};
|
||||
use crate::util::Invoke;
|
||||
|
||||
pub fn network_interface_api<C: Context>() -> ParentHandler<C> {
|
||||
pub fn gateway_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"list",
|
||||
@@ -88,7 +88,7 @@ pub fn network_interface_api<C: Context>() -> ParentHandler<C> {
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.with_about("Show network interfaces StartOS can listen on")
|
||||
.with_about("Show gateways StartOS can listen on")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -96,26 +96,26 @@ pub fn network_interface_api<C: Context>() -> ParentHandler<C> {
|
||||
from_fn_async(set_public)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Indicate whether this interface has inbound access from the WAN")
|
||||
.with_about("Indicate whether this gateway has inbound access from the WAN")
|
||||
.with_call_remote::<CliContext>(),
|
||||
).subcommand(
|
||||
"unset-inbound",
|
||||
"unset-public",
|
||||
from_fn_async(unset_public)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Allow this interface to infer whether it has inbound access from the WAN based on its IPv4 address")
|
||||
.with_about("Allow this gateway to infer whether it has inbound access from the WAN based on its IPv4 address")
|
||||
.with_call_remote::<CliContext>(),
|
||||
).subcommand("forget",
|
||||
from_fn_async(forget_iface)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Forget a disconnected interface")
|
||||
.with_about("Forget a disconnected gateway")
|
||||
.with_call_remote::<CliContext>()
|
||||
).subcommand("set-name",
|
||||
from_fn_async(set_name)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Rename an interface")
|
||||
.with_about("Rename a gateway")
|
||||
.with_call_remote::<CliContext>()
|
||||
)
|
||||
}
|
||||
@@ -814,7 +814,7 @@ impl NetworkInterfaceController {
|
||||
db.as_public_mut()
|
||||
.as_server_info_mut()
|
||||
.as_network_mut()
|
||||
.as_network_interfaces_mut()
|
||||
.as_gateways_mut()
|
||||
.ser(info)
|
||||
})
|
||||
.await
|
||||
@@ -881,7 +881,7 @@ impl NetworkInterfaceController {
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_network_interfaces()
|
||||
.as_gateways()
|
||||
.de()
|
||||
{
|
||||
Ok(mut info) => {
|
||||
@@ -940,7 +940,7 @@ impl NetworkInterfaceController {
|
||||
let mut sub = self
|
||||
.db
|
||||
.subscribe(
|
||||
"/public/serverInfo/network/networkInterfaces"
|
||||
"/public/serverInfo/network/gateways"
|
||||
.parse::<JsonPointer<_, _>>()
|
||||
.with_kind(ErrorKind::Database)?,
|
||||
)
|
||||
@@ -973,7 +973,7 @@ impl NetworkInterfaceController {
|
||||
let mut sub = self
|
||||
.db
|
||||
.subscribe(
|
||||
"/public/serverInfo/network/networkInterfaces"
|
||||
"/public/serverInfo/network/gateways"
|
||||
.parse::<JsonPointer<_, _>>()
|
||||
.with_kind(ErrorKind::Database)?,
|
||||
)
|
||||
@@ -1043,7 +1043,7 @@ impl NetworkInterfaceController {
|
||||
let (dump, mut sub) = self
|
||||
.db
|
||||
.dump_and_sub(
|
||||
"/public/serverInfo/network/networkInterfaces"
|
||||
"/public/serverInfo/network/gateways"
|
||||
.parse::<JsonPointer<_, _>>()
|
||||
.with_kind(ErrorKind::Database)?
|
||||
.join_end(interface.as_str())
|
||||
@@ -34,6 +34,8 @@ pub enum HostAddress {
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, TS)]
|
||||
pub struct DomainConfig {
|
||||
#[ts(type = "string")]
|
||||
pub root: InternedString,
|
||||
pub public: bool,
|
||||
pub acme: Option<AcmeProvider>,
|
||||
}
|
||||
@@ -177,7 +179,7 @@ pub struct AddDomainParams {
|
||||
pub async fn add_domain<Kind: HostApiKind>(
|
||||
ctx: RpcContext,
|
||||
AddDomainParams {
|
||||
domain,
|
||||
ref domain,
|
||||
private,
|
||||
acme,
|
||||
}: AddDomainParams,
|
||||
@@ -185,24 +187,41 @@ pub async fn add_domain<Kind: HostApiKind>(
|
||||
) -> Result<(), Error> {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
let root = db
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_domains()
|
||||
.keys()?
|
||||
.into_iter()
|
||||
.find(|root| domain.ends_with(&**root))
|
||||
.or_not_found(lazy_format!("root domain for {domain}"))?;
|
||||
|
||||
if let Some(acme) = &acme {
|
||||
if !db.as_public().as_server_info().as_network().as_acme().contains_key(&acme)? {
|
||||
if !db
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_acme()
|
||||
.contains_key(&acme)?
|
||||
{
|
||||
return Err(Error::new(eyre!("unknown acme provider {}, please run acme.init for this provider first", acme.0), ErrorKind::InvalidRequest));
|
||||
}
|
||||
}
|
||||
|
||||
Kind::host_for(&inheritance, db)?
|
||||
.as_domains_mut()
|
||||
.insert(
|
||||
&domain,
|
||||
&DomainConfig {
|
||||
public: !private,
|
||||
acme,
|
||||
},
|
||||
)?;
|
||||
|
||||
Kind::host_for(&inheritance, db)?.as_domains_mut().insert(
|
||||
domain,
|
||||
&DomainConfig {
|
||||
root,
|
||||
public: !private,
|
||||
acme,
|
||||
},
|
||||
)?;
|
||||
check_duplicates(db)
|
||||
})
|
||||
.await.result?;
|
||||
.await
|
||||
.result?;
|
||||
Kind::sync_host(&ctx, inheritance).await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::net::forward::AvailablePorts;
|
||||
use crate::net::host::HostApiKind;
|
||||
use crate::net::network_interface::InterfaceFilter;
|
||||
use crate::net::gateway::InterfaceFilter;
|
||||
use crate::net::vhost::AlpnInfo;
|
||||
use crate::prelude::*;
|
||||
use crate::util::serde::{display_serializable, HandlerExtSerde};
|
||||
|
||||
@@ -53,7 +53,7 @@ impl Host {
|
||||
self.domains
|
||||
.iter()
|
||||
.map(
|
||||
|(address, DomainConfig { public, acme })| HostAddress::Domain {
|
||||
|(address, DomainConfig { public, acme, .. })| HostAddress::Domain {
|
||||
address: address.clone(),
|
||||
public: *public,
|
||||
acme: acme.clone(),
|
||||
|
||||
@@ -2,12 +2,13 @@ use rpc_toolkit::{Context, HandlerExt, ParentHandler};
|
||||
|
||||
pub mod acme;
|
||||
pub mod dns;
|
||||
pub mod domain;
|
||||
pub mod forward;
|
||||
pub mod gateway;
|
||||
pub mod host;
|
||||
pub mod keys;
|
||||
pub mod mdns;
|
||||
pub mod net_controller;
|
||||
pub mod network_interface;
|
||||
pub mod service_interface;
|
||||
pub mod ssl;
|
||||
pub mod static_server;
|
||||
@@ -18,20 +19,23 @@ pub mod vhost;
|
||||
pub mod web_server;
|
||||
pub mod wifi;
|
||||
|
||||
pub fn net<C: Context>() -> ParentHandler<C> {
|
||||
pub fn net_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"tor",
|
||||
tor::tor::<C>().with_about("Tor commands such as list-services, logs, and reset"),
|
||||
tor::tor_api::<C>().with_about("Tor commands such as list-services, logs, and reset"),
|
||||
)
|
||||
.subcommand(
|
||||
"acme",
|
||||
acme::acme::<C>().with_about("Setup automatic clearnet certificate acquisition"),
|
||||
acme::acme_api::<C>().with_about("Setup automatic clearnet certificate acquisition"),
|
||||
)
|
||||
.subcommand(
|
||||
"network-interface",
|
||||
network_interface::network_interface_api::<C>()
|
||||
.with_about("View and edit network interface configurations"),
|
||||
"domain",
|
||||
domain::domain_api::<C>().with_about("Setup clearnet domains"),
|
||||
)
|
||||
.subcommand(
|
||||
"gateway",
|
||||
gateway::gateway_api::<C>().with_about("View and edit gateway configurations"),
|
||||
)
|
||||
.subcommand(
|
||||
"tunnel",
|
||||
|
||||
@@ -17,13 +17,13 @@ use crate::error::ErrorCollection;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::net::dns::DnsController;
|
||||
use crate::net::forward::{PortForwardController, START9_BRIDGE_IFACE};
|
||||
use crate::net::host::address::HostAddress;
|
||||
use crate::net::host::binding::{AddSslOptions, BindId, BindOptions};
|
||||
use crate::net::host::{host_for, Host, Hosts};
|
||||
use crate::net::network_interface::{
|
||||
use crate::net::gateway::{
|
||||
AndFilter, DynInterfaceFilter, InterfaceFilter, LoopbackFilter, NetworkInterfaceController,
|
||||
SecureFilter,
|
||||
};
|
||||
use crate::net::host::address::HostAddress;
|
||||
use crate::net::host::binding::{AddSslOptions, BindId, BindOptions};
|
||||
use crate::net::host::{host_for, Host, Hosts};
|
||||
use crate::net::service_interface::{HostnameInfo, IpHostname, OnionHostname};
|
||||
use crate::net::tor::TorController;
|
||||
use crate::net::utils::ipv6_is_local;
|
||||
|
||||
@@ -84,7 +84,7 @@ lazy_static! {
|
||||
static ref PROGRESS_REGEX: Regex = Regex::new("PROGRESS=([0-9]+)").unwrap();
|
||||
}
|
||||
|
||||
pub fn tor<C: Context>() -> ParentHandler<C> {
|
||||
pub fn tor_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"list-services",
|
||||
|
||||
@@ -53,7 +53,7 @@ pub async fn add_tunnel(
|
||||
.into_public()
|
||||
.into_server_info()
|
||||
.into_network()
|
||||
.into_network_interfaces()
|
||||
.into_gateways()
|
||||
.keys()?;
|
||||
let mut iface = GatewayId::from("wg0");
|
||||
for id in 1.. {
|
||||
@@ -105,7 +105,7 @@ pub async fn remove_tunnel(
|
||||
.into_public()
|
||||
.into_server_info()
|
||||
.into_network()
|
||||
.into_network_interfaces()
|
||||
.into_gateways()
|
||||
.into_idx(&id)
|
||||
.and_then(|e| e.into_ip_info().transpose())
|
||||
else {
|
||||
|
||||
@@ -36,7 +36,7 @@ use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::db::model::Database;
|
||||
use crate::net::acme::{AcmeCertCache, AcmeProvider};
|
||||
use crate::net::network_interface::{
|
||||
use crate::net::gateway::{
|
||||
Accepted, AnyFilter, DynInterfaceFilter, InterfaceFilter, NetworkInterfaceController,
|
||||
NetworkInterfaceListener,
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext};
|
||||
use crate::net::network_interface::{
|
||||
use crate::net::gateway::{
|
||||
lookup_info_by_addr, NetworkInterfaceListener, SelfContainedNetworkInterfaceListener,
|
||||
};
|
||||
use crate::net::static_server::{
|
||||
|
||||
@@ -90,7 +90,7 @@ pub async fn get_ssl_certificate(
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_network_interfaces()
|
||||
.as_gateways()
|
||||
.as_entries()?
|
||||
.into_iter()
|
||||
.flat_map(|(_, net)| net.as_ip_info().transpose_ref())
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::context::CliContext;
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::signature::SignatureAuthContext;
|
||||
use crate::net::forward::PortForwardController;
|
||||
use crate::net::network_interface::NetworkInterfaceWatcher;
|
||||
use crate::net::gateway::NetworkInterfaceWatcher;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::{OpenAuthedContinuations, RpcContinuations};
|
||||
use crate::tunnel::db::TunnelDatabase;
|
||||
|
||||
@@ -49,7 +49,9 @@ mod v0_4_0_alpha_7;
|
||||
mod v0_4_0_alpha_8;
|
||||
mod v0_4_0_alpha_9;
|
||||
|
||||
pub type Current = v0_4_0_alpha_9::Version; // VERSION_BUMP
|
||||
mod v0_4_0_alpha_10;
|
||||
|
||||
pub type Current = v0_4_0_alpha_10::Version; // VERSION_BUMP
|
||||
|
||||
impl Current {
|
||||
#[instrument(skip(self, db))]
|
||||
@@ -161,7 +163,8 @@ enum Version {
|
||||
V0_4_0_alpha_6(Wrapper<v0_4_0_alpha_6::Version>),
|
||||
V0_4_0_alpha_7(Wrapper<v0_4_0_alpha_7::Version>),
|
||||
V0_4_0_alpha_8(Wrapper<v0_4_0_alpha_8::Version>),
|
||||
V0_4_0_alpha_9(Wrapper<v0_4_0_alpha_9::Version>), // VERSION_BUMP
|
||||
V0_4_0_alpha_9(Wrapper<v0_4_0_alpha_9::Version>),
|
||||
V0_4_0_alpha_10(Wrapper<v0_4_0_alpha_10::Version>), // VERSION_BUMP
|
||||
Other(exver::Version),
|
||||
}
|
||||
|
||||
@@ -213,7 +216,8 @@ impl Version {
|
||||
Self::V0_4_0_alpha_6(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_7(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_8(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_9(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::V0_4_0_alpha_9(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_10(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::Other(v) => {
|
||||
return Err(Error::new(
|
||||
eyre!("unknown version {v}"),
|
||||
@@ -257,7 +261,8 @@ impl Version {
|
||||
Version::V0_4_0_alpha_6(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_7(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_8(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_9(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::V0_4_0_alpha_9(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_10(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::Other(x) => x.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,9 @@ impl VersionT for Version {
|
||||
}
|
||||
HostAddress::Domain { address } => {
|
||||
domains.insert(
|
||||
address,
|
||||
address.clone(),
|
||||
DomainConfig {
|
||||
root: address,
|
||||
public: true,
|
||||
acme: None,
|
||||
},
|
||||
|
||||
108
core/startos/src/version/v0_4_0_alpha_10.rs
Normal file
108
core/startos/src/version/v0_4_0_alpha_10.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use exver::{PreReleaseSegment, VersionRange};
|
||||
use imbl_value::json;
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{v0_4_0_alpha_9, VersionT};
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_4_0_alpha_10: exver::Version = exver::Version::new(
|
||||
[0, 4, 0],
|
||||
[PreReleaseSegment::String("alpha".into()), 10.into()]
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_4_0_alpha_9::Version;
|
||||
type PreUpRes = ();
|
||||
|
||||
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_4_0_alpha_10.clone()
|
||||
}
|
||||
fn compat(self) -> &'static VersionRange {
|
||||
&V0_3_0_COMPAT
|
||||
}
|
||||
#[instrument]
|
||||
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<Value, Error> {
|
||||
let default_gateway = db["public"]["serverInfo"]["network"]["networkInterfaces"]
|
||||
.as_object()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.find(|(_, i)| i["ipInfo"]["wanIp"].is_string())
|
||||
.map(|(g, _)| g.clone());
|
||||
let mut roots = BTreeSet::new();
|
||||
for (_, package) in db["public"]["packageData"]
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("expected public.packageData to be an object"),
|
||||
ErrorKind::Database,
|
||||
)
|
||||
})?
|
||||
.iter_mut()
|
||||
{
|
||||
for (_, host) in package["hosts"]
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("expected public.packageData[id].hosts to be an object"),
|
||||
ErrorKind::Database,
|
||||
)
|
||||
})?
|
||||
.iter_mut()
|
||||
{
|
||||
if default_gateway.is_none() {
|
||||
host["domains"] = json!({});
|
||||
continue;
|
||||
}
|
||||
for (domain, info) in host["domains"]
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!(
|
||||
"expected public.packageData[id].hosts[id].domains to be an object"
|
||||
),
|
||||
ErrorKind::Database,
|
||||
)
|
||||
})?
|
||||
.iter_mut()
|
||||
{
|
||||
let Some(info) = info.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
let root = domain.clone();
|
||||
info.insert("root".into(), Value::String(Arc::new((&*root).to_owned())));
|
||||
roots.insert(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
let network = db["public"]["serverInfo"]["network"]
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("expected public.serverInfo.network to be an object"),
|
||||
ErrorKind::Database,
|
||||
)
|
||||
})?;
|
||||
network["gateways"] = network["networkInterfaces"].clone();
|
||||
if let Some(gateway) = default_gateway {
|
||||
for root in roots {
|
||||
network["domains"][&*root] = json!({ "gateway": gateway });
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Null)
|
||||
}
|
||||
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AcmeProvider } from "./AcmeProvider"
|
||||
|
||||
export type DomainConfig = { public: boolean; acme: AcmeProvider | null }
|
||||
export type DomainConfig = {
|
||||
root: string
|
||||
public: boolean
|
||||
acme: AcmeProvider | null
|
||||
}
|
||||
|
||||
4
sdk/base/lib/osBindings/DomainSettings.ts
Normal file
4
sdk/base/lib/osBindings/DomainSettings.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { GatewayId } from "./GatewayId"
|
||||
|
||||
export type DomainSettings = { gateway: GatewayId }
|
||||
@@ -1,6 +1,7 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AcmeProvider } from "./AcmeProvider"
|
||||
import type { AcmeSettings } from "./AcmeSettings"
|
||||
import type { DomainSettings } from "./DomainSettings"
|
||||
import type { GatewayId } from "./GatewayId"
|
||||
import type { Host } from "./Host"
|
||||
import type { NetworkInterfaceInfo } from "./NetworkInterfaceInfo"
|
||||
@@ -9,6 +10,7 @@ import type { WifiInfo } from "./WifiInfo"
|
||||
export type NetworkInfo = {
|
||||
wifi: WifiInfo
|
||||
host: Host
|
||||
networkInterfaces: { [key: GatewayId]: NetworkInterfaceInfo }
|
||||
gateways: { [key: GatewayId]: NetworkInterfaceInfo }
|
||||
acme: { [key: AcmeProvider]: AcmeSettings }
|
||||
domains: { [key: string]: DomainSettings }
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ export { Description } from "./Description"
|
||||
export { DestroySubcontainerFsParams } from "./DestroySubcontainerFsParams"
|
||||
export { DeviceFilter } from "./DeviceFilter"
|
||||
export { DomainConfig } from "./DomainConfig"
|
||||
export { DomainSettings } from "./DomainSettings"
|
||||
export { Duration } from "./Duration"
|
||||
export { EchoParams } from "./EchoParams"
|
||||
export { EditSignerParams } from "./EditSignerParams"
|
||||
|
||||
@@ -12,8 +12,9 @@ export class IpAddress {
|
||||
this.octets[octIdx++] = num & 255
|
||||
idx += 1
|
||||
}
|
||||
if (idx < 7) {
|
||||
idx = segs.length - 1
|
||||
const lastSegIdx = segs.length - 1
|
||||
if (idx < lastSegIdx) {
|
||||
idx = lastSegIdx
|
||||
octIdx = 15
|
||||
while (segs[idx]) {
|
||||
const num = parseInt(segs[idx], 16)
|
||||
|
||||
@@ -61,7 +61,7 @@ import {
|
||||
} from "../../base/lib/inits"
|
||||
import { DropGenerator } from "../../base/lib/util/Drop"
|
||||
|
||||
export const OSVersion = testTypeVersion("0.4.0-alpha.9")
|
||||
export const OSVersion = testTypeVersion("0.4.0-alpha.10")
|
||||
|
||||
// prettier-ignore
|
||||
type AnyNeverCond<T extends any[], Then, Else> =
|
||||
|
||||
4
web/package-lock.json
generated
4
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-alpha.9",
|
||||
"version": "0.4.0-alpha.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-alpha.9",
|
||||
"version": "0.4.0-alpha.10",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^20.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-alpha.9",
|
||||
"version": "0.4.0-alpha.10",
|
||||
"author": "Start9 Labs, Inc",
|
||||
"homepage": "https://start9.com/",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -47,10 +47,8 @@ export class DomainService {
|
||||
|
||||
readonly data = toSignal(
|
||||
this.patch.watch$('serverInfo', 'network').pipe(
|
||||
map(({ networkInterfaces, domains, acme }) => ({
|
||||
gateways: Object.entries(networkInterfaces).reduce<
|
||||
Record<string, string>
|
||||
>(
|
||||
map(({ gateways, domains, acme }) => ({
|
||||
gateways: Object.entries(gateways).reduce<Record<string, string>>(
|
||||
(obj, [id, n]) => ({
|
||||
...obj,
|
||||
[id]: n.ipInfo?.name || '',
|
||||
@@ -64,7 +62,7 @@ export class DomainService {
|
||||
subdomain: parse(fqdn).subdomain,
|
||||
gateway: {
|
||||
id: gateway,
|
||||
ipInfo: networkInterfaces[gateway]?.ipInfo || null,
|
||||
ipInfo: gateways[gateway]?.ipInfo || null,
|
||||
},
|
||||
authority: {
|
||||
url: acme,
|
||||
|
||||
@@ -84,7 +84,7 @@ export default class GatewaysComponent {
|
||||
private readonly formDialog = inject(FormDialogService)
|
||||
|
||||
readonly gateways$ = inject<PatchDB<DataModel>>(PatchDB)
|
||||
.watch$('serverInfo', 'network', 'networkInterfaces')
|
||||
.watch$('serverInfo', 'network', 'gateways')
|
||||
.pipe(
|
||||
map(gateways =>
|
||||
Object.entries(gateways)
|
||||
|
||||
@@ -110,7 +110,7 @@ export namespace Mock {
|
||||
squashfs: {
|
||||
aarch64: {
|
||||
publishedAt: '2025-04-21T20:58:48.140749883Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.9/startos-0.4.0-alpha.9-33ae46f~dev_aarch64.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_aarch64.squashfs',
|
||||
commitment: {
|
||||
hash: '4elBFVkd/r8hNadKmKtLIs42CoPltMvKe2z3LRqkphk=',
|
||||
size: 1343500288,
|
||||
@@ -122,7 +122,7 @@ export namespace Mock {
|
||||
},
|
||||
'aarch64-nonfree': {
|
||||
publishedAt: '2025-04-21T21:07:00.249285116Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.9/startos-0.4.0-alpha.9-33ae46f~dev_aarch64-nonfree.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_aarch64-nonfree.squashfs',
|
||||
commitment: {
|
||||
hash: 'MrCEi4jxbmPS7zAiGk/JSKlMsiuKqQy6RbYOxlGHOIQ=',
|
||||
size: 1653075968,
|
||||
@@ -134,7 +134,7 @@ export namespace Mock {
|
||||
},
|
||||
raspberrypi: {
|
||||
publishedAt: '2025-04-21T21:16:12.933319237Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.9/startos-0.4.0-alpha.9-33ae46f~dev_raspberrypi.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_raspberrypi.squashfs',
|
||||
commitment: {
|
||||
hash: '/XTVQRCqY3RK544PgitlKu7UplXjkmzWoXUh2E4HCw0=',
|
||||
size: 1490731008,
|
||||
@@ -146,7 +146,7 @@ export namespace Mock {
|
||||
},
|
||||
x86_64: {
|
||||
publishedAt: '2025-04-21T21:14:20.246908903Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.9/startos-0.4.0-alpha.9-33ae46f~dev_x86_64.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_x86_64.squashfs',
|
||||
commitment: {
|
||||
hash: '/6romKTVQGSaOU7FqSZdw0kFyd7P+NBSYNwM3q7Fe44=',
|
||||
size: 1411657728,
|
||||
@@ -158,7 +158,7 @@ export namespace Mock {
|
||||
},
|
||||
'x86_64-nonfree': {
|
||||
publishedAt: '2025-04-21T21:15:17.955265284Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.9/startos-0.4.0-alpha.9-33ae46f~dev_x86_64-nonfree.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_x86_64-nonfree.squashfs',
|
||||
commitment: {
|
||||
hash: 'HCRq9sr/0t85pMdrEgNBeM4x11zVKHszGnD1GDyZbSE=',
|
||||
size: 1731035136,
|
||||
|
||||
@@ -269,7 +269,7 @@ export namespace RR {
|
||||
export type UpdateTunnelReq = {
|
||||
id: string
|
||||
name: string
|
||||
} // net.netwok-interface.set-name
|
||||
} // net.gateway.set-name
|
||||
export type UpdateTunnelRes = null
|
||||
|
||||
export type RemoveTunnelReq = { id: string } // net.tunnel.remove
|
||||
|
||||
@@ -351,7 +351,7 @@ export class LiveApiService extends ApiService {
|
||||
}
|
||||
|
||||
async updateTunnel(params: RR.UpdateTunnelReq): Promise<RR.UpdateTunnelRes> {
|
||||
return this.rpcRequest({ method: 'net.netwok-interface.set-name', params })
|
||||
return this.rpcRequest({ method: 'net.gateway.set-name', params })
|
||||
}
|
||||
|
||||
async removeTunnel(params: RR.RemoveTunnelReq): Promise<RR.RemoveTunnelRes> {
|
||||
|
||||
@@ -553,7 +553,7 @@ export class MockApiService extends ApiService {
|
||||
const patch: AddOperation<T.NetworkInterfaceInfo>[] = [
|
||||
{
|
||||
op: PatchOp.ADD,
|
||||
path: `/serverInfo/network/networkInterfaces/${id}`,
|
||||
path: `/serverInfo/network/gateways/${id}`,
|
||||
value: {
|
||||
public: params.public,
|
||||
secure: false,
|
||||
@@ -579,7 +579,7 @@ export class MockApiService extends ApiService {
|
||||
const patch: ReplaceOperation<string>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/serverInfo/network/networkInterfaces/${params.id}/label`,
|
||||
path: `/serverInfo/network/gateways/${params.id}/label`,
|
||||
value: params.name,
|
||||
},
|
||||
]
|
||||
@@ -593,7 +593,7 @@ export class MockApiService extends ApiService {
|
||||
const patch: RemoveOperation[] = [
|
||||
{
|
||||
op: PatchOp.REMOVE,
|
||||
path: `/serverInfo/network/networkInterfaces/${params.id}`,
|
||||
path: `/serverInfo/network/gateways/${params.id}`,
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch)
|
||||
|
||||
@@ -145,7 +145,7 @@ export const mockPatchData: DataModel = {
|
||||
],
|
||||
},
|
||||
},
|
||||
networkInterfaces: {
|
||||
gateways: {
|
||||
eth0: {
|
||||
public: null,
|
||||
secure: null,
|
||||
|
||||
@@ -26,14 +26,14 @@
|
||||
// ) {}
|
||||
|
||||
// async presentModalSetOutboundProxy(current: string | null, pkgId?: string) {
|
||||
// const networkInterfaces = await firstValueFrom(
|
||||
// this.patch.watch$('serverInfo', 'network', 'networkInterfaces'),
|
||||
// const gateways = await firstValueFrom(
|
||||
// this.patch.watch$('serverInfo', 'network', 'gateways'),
|
||||
// )
|
||||
// const config = ISB.InputSpec.of({
|
||||
// proxyId: ISB.Value.select({
|
||||
// name: 'Select Proxy',
|
||||
// default: current || '',
|
||||
// values: Object.entries(networkInterfaces)
|
||||
// values: Object.entries(gateways)
|
||||
// .filter(
|
||||
// ([_, n]) => n.outbound && n.ipInfo?.deviceType === 'wireguard',
|
||||
// )
|
||||
|
||||
Reference in New Issue
Block a user