From 2c12af5af8668cb82c2ffc85eb6a5bbc6e455d4c Mon Sep 17 00:00:00 2001 From: Jade <2364004+Blu-J@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:39:54 -0600 Subject: [PATCH] Feature/network (#2622) * Feature: Add in the clear bindings * wip: Working on network * fix: Make it so the config gives the url * chore: Remove the repeated types * chore: Add in the todo's here * chore: UPdate and remove some poorly name var * chore: Remove the clear-bindings impl * chore: Remove the wrapper * handle HostnameInfo for Host bindings Co-authored-by: Jade * ?? * chore: Make the install work * Fix: Url's not being created * chore: Fix the local onion in url * include port in hostname * Chore of adding a comment just to modify. --------- Co-authored-by: Aiden McClelland Co-authored-by: Jade --- container-runtime/package-lock.json | 16 +- container-runtime/package.json | 2 +- .../src/Adapters/HostSystemStartOs.ts | 10 +- .../Systems/SystemForEmbassy/MainLoop.ts | 2 - .../Systems/SystemForEmbassy/index.ts | 140 +++++- .../Systems/SystemForEmbassy/matchManifest.ts | 1 + .../SystemForEmbassy/oldEmbassyTypes.ts | 1 + core/models/src/id/mod.rs | 2 +- core/startos/src/db/model/package.rs | 8 +- core/startos/src/disk/mod.rs | 4 +- core/startos/src/net/dhcp.rs | 2 +- core/startos/src/net/host/binding.rs | 65 ++- core/startos/src/net/host/mod.rs | 19 +- core/startos/src/net/net_controller.rs | 264 ++++++---- core/startos/src/net/service_interface.rs | 39 +- core/startos/src/net/vhost.rs | 2 +- core/startos/src/registry/auth.rs | 1 - core/startos/src/service/mod.rs | 7 +- .../src/service/persistent_container.rs | 2 +- .../src/service/service_effect_handler.rs | 208 +++----- sdk/lib/StartSdk.ts | 10 +- sdk/lib/interfaces/Host.ts | 86 ++-- sdk/lib/interfaces/Origin.ts | 19 +- sdk/lib/osBindings/AddAssetParams.ts | 1 - sdk/lib/osBindings/AddPackageParams.ts | 9 + sdk/lib/osBindings/AddSslOptions.ts | 3 +- sdk/lib/osBindings/AddressInfo.ts | 5 +- sdk/lib/osBindings/BindInfo.ts | 3 +- sdk/lib/osBindings/BindOptions.ts | 1 - sdk/lib/osBindings/BindParams.ts | 1 - .../ExportServiceInterfaceParams.ts | 4 - sdk/lib/osBindings/ExportedHostInfo.ts | 10 - sdk/lib/osBindings/ExportedHostnameInfo.ts | 12 - sdk/lib/osBindings/GetHostInfoParams.ts | 5 +- sdk/lib/osBindings/GetHostInfoParamsKind.ts | 3 - sdk/lib/osBindings/GetPackageResponse.ts | 1 + sdk/lib/osBindings/GetPackageResponseFull.ts | 1 + sdk/lib/osBindings/GetPrimaryUrlParams.ts | 5 +- .../osBindings/GetServiceInterfaceParams.ts | 3 +- sdk/lib/osBindings/HardwareRequirements.ts | 2 +- sdk/lib/osBindings/Host.ts | 6 +- sdk/lib/osBindings/HostnameInfo.ts | 12 + sdk/lib/osBindings/{HostInfo.ts => Hosts.ts} | 2 +- .../{ExportedIpHostname.ts => IpHostname.ts} | 2 +- ...{ReverseProxyDestination.ts => LanInfo.ts} | 7 +- ...ortedOnionHostname.ts => OnionHostname.ts} | 2 +- sdk/lib/osBindings/OsVersionInfo.ts | 2 +- sdk/lib/osBindings/PackageDataEntry.ts | 8 +- sdk/lib/osBindings/PackageInfo.ts | 3 +- sdk/lib/osBindings/PackageVersionInfo.ts | 1 - sdk/lib/osBindings/ReverseProxyBind.ts | 3 - sdk/lib/osBindings/ReverseProxyHttp.ts | 3 - sdk/lib/osBindings/ReverseProxyParams.ts | 10 - .../ServiceInterfaceWithHostInfo.ts | 17 - sdk/lib/osBindings/index.ts | 17 +- sdk/lib/test/host.test.ts | 1 + sdk/lib/test/startosTypeValidation.test.ts | 2 - sdk/lib/types.ts | 97 +--- sdk/lib/util/Hostname.ts | 25 + sdk/lib/util/getServiceInterface.ts | 178 +++---- sdk/lib/util/getServiceInterfaces.ts | 40 +- sdk/lib/util/index.ts | 2 + .../app-interfaces/app-interfaces.page.ts | 57 +-- .../ui/src/app/services/api/api.fixures.ts | 450 +----------------- .../ui/src/app/services/api/mock-patch.ts | 352 +------------- .../ui/src/app/services/config.service.ts | 31 +- .../src/app/services/ui-launcher.service.ts | 5 +- 67 files changed, 798 insertions(+), 1516 deletions(-) create mode 100644 sdk/lib/osBindings/AddPackageParams.ts delete mode 100644 sdk/lib/osBindings/ExportedHostInfo.ts delete mode 100644 sdk/lib/osBindings/ExportedHostnameInfo.ts delete mode 100644 sdk/lib/osBindings/GetHostInfoParamsKind.ts create mode 100644 sdk/lib/osBindings/HostnameInfo.ts rename sdk/lib/osBindings/{HostInfo.ts => Hosts.ts} (79%) rename sdk/lib/osBindings/{ExportedIpHostname.ts => IpHostname.ts} (94%) rename sdk/lib/osBindings/{ReverseProxyDestination.ts => LanInfo.ts} (55%) rename sdk/lib/osBindings/{ExportedOnionHostname.ts => OnionHostname.ts} (82%) delete mode 100644 sdk/lib/osBindings/ReverseProxyBind.ts delete mode 100644 sdk/lib/osBindings/ReverseProxyHttp.ts delete mode 100644 sdk/lib/osBindings/ReverseProxyParams.ts delete mode 100644 sdk/lib/osBindings/ServiceInterfaceWithHostInfo.ts create mode 100644 sdk/lib/util/Hostname.ts diff --git a/container-runtime/package-lock.json b/container-runtime/package-lock.json index dd8ba7855..838dcb769 100644 --- a/container-runtime/package-lock.json +++ b/container-runtime/package-lock.json @@ -14,7 +14,7 @@ "filebrowser": "^1.0.0", "isomorphic-fetch": "^3.0.0", "node-fetch": "^3.1.0", - "ts-matches": "^5.4.1", + "ts-matches": "^5.5.1", "tslib": "^2.5.3", "typescript": "^5.1.3", "yaml": "^2.3.1" @@ -29,7 +29,7 @@ }, "../sdk/dist": { "name": "@start9labs/start-sdk", - "version": "0.4.0-rev0.lib0.rc8.beta10", + "version": "0.3.6-alpha1", "license": "MIT", "dependencies": { "isomorphic-fetch": "^3.0.0", @@ -2428,9 +2428,9 @@ } }, "node_modules/ts-matches": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.4.1.tgz", - "integrity": "sha512-kXrY75F0s0WD15N2bWKDScKlKgwnusN6dTRzGs1N7LlxQRnazrsBISC1HL4sy2adsyk65Zbx3Ui3IGN8leAFOQ==" + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", + "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" }, "node_modules/tslib": { "version": "2.6.2", @@ -4195,9 +4195,9 @@ } }, "ts-matches": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.4.1.tgz", - "integrity": "sha512-kXrY75F0s0WD15N2bWKDScKlKgwnusN6dTRzGs1N7LlxQRnazrsBISC1HL4sy2adsyk65Zbx3Ui3IGN8leAFOQ==" + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", + "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" }, "tslib": { "version": "2.6.2", diff --git a/container-runtime/package.json b/container-runtime/package.json index f5e34a618..e2c56afff 100644 --- a/container-runtime/package.json +++ b/container-runtime/package.json @@ -22,7 +22,7 @@ "filebrowser": "^1.0.0", "isomorphic-fetch": "^3.0.0", "node-fetch": "^3.1.0", - "ts-matches": "^5.4.1", + "ts-matches": "^5.5.1", "tslib": "^2.5.3", "typescript": "^5.1.3", "yaml": "^2.3.1" diff --git a/container-runtime/src/Adapters/HostSystemStartOs.ts b/container-runtime/src/Adapters/HostSystemStartOs.ts index b745799e1..ba2076f52 100644 --- a/container-runtime/src/Adapters/HostSystemStartOs.ts +++ b/container-runtime/src/Adapters/HostSystemStartOs.ts @@ -96,7 +96,10 @@ export class HostSystemStartOs implements Effects { } bind(...[options]: Parameters) { - return this.rpcRound("bind", options) as ReturnType + return this.rpcRound("bind", { + ...options, + stack: new Error().stack, + }) as ReturnType } clearBindings(...[]: Parameters) { return this.rpcRound("clearBindings", null) as ReturnType< @@ -228,11 +231,6 @@ export class HostSystemStartOs implements Effects { restart(...[]: Parameters) { return this.rpcRound("restart", null) } - reverseProxy(...[options]: Parameters) { - return this.rpcRound("reverseProxy", options) as ReturnType< - T.Effects["reverseProxy"] - > - } running(...[packageId]: Parameters) { return this.rpcRound("running", { packageId }) as ReturnType< T.Effects["running"] diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index 917bfff83..08bf944ed 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -97,11 +97,9 @@ export class MainLoop { id: interfaceId, internalPort, preferredExternalPort: torConf?.external || internalPort, - scheme: "http", secure: null, addSsl: lanConf?.ssl ? { - scheme: "https", preferredExternalPort: lanConf.external, alpn: { specified: ["http/1.1"] }, } diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index ada893b2b..8e0cb28c5 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -31,6 +31,16 @@ import { HostSystemStartOs } from "../../HostSystemStartOs" import { JsonPath, unNestPath } from "../../../Models/JsonPath" import { RpcResult, matchRpcResult } from "../../RpcListener" import { CT } from "@start9labs/start-sdk" +import { + AddSslOptions, + BindOptions, +} from "@start9labs/start-sdk/cjs/lib/osBindings" +import { + BindOptionsByProtocol, + Host, + MultiHost, +} from "@start9labs/start-sdk/cjs/lib/interfaces/Host" +import { ServiceInterfaceBuilder } from "@start9labs/start-sdk/cjs/lib/interfaces/ServiceInterfaceBuilder" type Optional = A | undefined | null function todo(): never { @@ -335,6 +345,85 @@ export class SystemForEmbassy implements System { await this.migration(effects, previousVersion, timeoutMs) await effects.setMainStatus({ status: "stopped" }) await this.exportActions(effects) + await this.exportNetwork(effects) + } + async exportNetwork(effects: HostSystemStartOs) { + for (const [id, interfaceValue] of Object.entries( + this.manifest.interfaces, + )) { + const host = new MultiHost({ effects, id }) + const internalPorts = new Set( + Object.values(interfaceValue["tor-config"]?.["port-mapping"] ?? {}) + .map(Number.parseInt) + .concat( + ...Object.values(interfaceValue["lan-config"] ?? {}).map( + (c) => c.internal, + ), + ) + .filter(Boolean), + ) + const bindings = Array.from(internalPorts).map< + [number, BindOptionsByProtocol] + >((port) => { + const lanPort = Object.entries(interfaceValue["lan-config"] ?? {}).find( + ([external, internal]) => internal.internal === port, + )?.[0] + const torPort = Object.entries( + interfaceValue["tor-config"]?.["port-mapping"] ?? {}, + ).find( + ([external, internal]) => Number.parseInt(internal) === port, + )?.[0] + let addSsl: AddSslOptions | null = null + if (lanPort) { + const lanPortNum = Number.parseInt(lanPort) + if (lanPortNum === 443) { + return [port, { protocol: "http", preferredExternalPort: 80 }] + } + addSsl = { + preferredExternalPort: lanPortNum, + alpn: { specified: [] }, + } + } + return [ + port, + { + secure: null, + preferredExternalPort: Number.parseInt( + torPort || lanPort || String(port), + ), + addSsl, + }, + ] + }) + + await Promise.all( + bindings.map(async ([internal, options]) => { + if (internal == null) { + return + } + if (options?.preferredExternalPort == null) { + return + } + const origin = await host.bindPort(internal, options) + await origin.export([ + new ServiceInterfaceBuilder({ + effects, + name: interfaceValue.name, + id: `${id}-${internal}`, + description: interfaceValue.description, + hasPrimary: false, + disabled: false, + type: "api", + masked: false, + path: "", + schemeOverride: null, + search: {}, + username: null, + }), + ]) + }), + ) + } } async exportActions(effects: HostSystemStartOs) { const manifest = this.manifest @@ -486,6 +575,7 @@ export class SystemForEmbassy implements System { const newConfig = structuredClone(newConfigWithoutPointers) await updateConfig( effects, + this.manifest, await this.getConfigUncleaned(effects, timeoutMs).then((x) => x.spec), newConfig, ) @@ -866,6 +956,7 @@ function cleanConfigFromPointers( async function updateConfig( effects: HostSystemStartOs, + manifest: Manifest, spec: unknown, mutConfigValue: unknown, ) { @@ -877,7 +968,12 @@ async function updateConfig( const newConfigValue = mutConfigValue[key] if (matchSpec.test(specValue)) { const updateObject = { spec: null } - await updateConfig(effects, { spec: specValue.spec }, updateObject) + await updateConfig( + effects, + manifest, + { spec: specValue.spec }, + updateObject, + ) mutConfigValue[key] = updateObject.spec } if ( @@ -899,20 +995,48 @@ async function updateConfig( if (matchPointerPackage.test(specValue)) { if (specValue.target === "tor-key") throw new Error("This service uses an unsupported target TorKey") + + const specInterface = specValue.interface + const serviceInterfaceId = extractServiceInterfaceId( + manifest, + specInterface, + ) const filled = await utils .getServiceInterface(effects, { packageId: specValue["package-id"], - id: specValue.interface, + id: serviceInterfaceId, }) .once() - .catch(() => null) - - mutConfigValue[key] = + .catch((x) => { + console.error("Could not get the service interface", x) + return null + }) + const catchFn = (fn: () => X) => { + try { + return fn() + } catch (e) { + return undefined + } + } + const url: string = filled === null ? "" - : specValue.target === "lan-address" - ? filled.addressInfo.localHostnames[0] - : filled.addressInfo.onionHostnames[0] + : catchFn(() => + utils.hostnameInfoToAddress( + specValue.target === "lan-address" + ? filled.addressInfo.localHostnames[0] || + filled.addressInfo.onionHostnames[0] + : filled.addressInfo.onionHostnames[0] || + filled.addressInfo.localHostnames[0], + ), + ) || "" + mutConfigValue[key] = url } } } +function extractServiceInterfaceId(manifest: Manifest, specInterface: string) { + let serviceInterfaceId + const lanConfig = manifest.interfaces[specInterface]?.["lan-config"] || {} + serviceInterfaceId = `${specInterface}-${Object.entries(lanConfig)[0]?.[1]?.internal}` + return serviceInterfaceId +} diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts index d4758669b..8ce6cabbc 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts @@ -72,6 +72,7 @@ export const matchManifest = object( object( { name: string, + description: string, "tor-config": object({ "port-mapping": dictionary([string, string]), }), diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/oldEmbassyTypes.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/oldEmbassyTypes.ts index 35b95e095..0d7521626 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/oldEmbassyTypes.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/oldEmbassyTypes.ts @@ -99,6 +99,7 @@ export type Effects = { /** Sandbox mode lets us read but not write */ is_sandboxed(): boolean + // Does a volume and path exist? exists(input: { volumeId: string; path: string }): Promise fetch( diff --git a/core/models/src/id/mod.rs b/core/models/src/id/mod.rs index 10882d4f1..85c9d8255 100644 --- a/core/models/src/id/mod.rs +++ b/core/models/src/id/mod.rs @@ -24,7 +24,7 @@ pub use service_interface::ServiceInterfaceId; pub use volume::VolumeId; lazy_static::lazy_static! { - static ref ID_REGEX: Regex = Regex::new("^[a-z]+(-[a-z]+)*$").unwrap(); + static ref ID_REGEX: Regex = Regex::new("^[a-z]+(-[a-z0-9]+)*$").unwrap(); pub static ref SYSTEM_ID: Id = Id(InternedString::intern("x_system")); } diff --git a/core/startos/src/db/model/package.rs b/core/startos/src/db/model/package.rs index 3fad0ad9f..8bb5a9517 100644 --- a/core/startos/src/db/model/package.rs +++ b/core/startos/src/db/model/package.rs @@ -10,8 +10,8 @@ use reqwest::Url; use serde::{Deserialize, Serialize}; use ts_rs::TS; -use crate::net::host::HostInfo; -use crate::net::service_interface::ServiceInterfaceWithHostInfo; +use crate::net::host::Hosts; +use crate::net::service_interface::ServiceInterface; use crate::prelude::*; use crate::progress::FullProgress; use crate::s9pk::manifest::Manifest; @@ -333,8 +333,8 @@ pub struct PackageDataEntry { pub last_backup: Option>, pub current_dependencies: CurrentDependencies, pub actions: BTreeMap, - pub service_interfaces: BTreeMap, - pub hosts: HostInfo, + pub service_interfaces: BTreeMap, + pub hosts: Hosts, #[ts(type = "string[]")] pub store_exposed_dependents: Vec, } diff --git a/core/startos/src/disk/mod.rs b/core/startos/src/disk/mod.rs index 68eb2b187..705a34f98 100644 --- a/core/startos/src/disk/mod.rs +++ b/core/startos/src/disk/mod.rs @@ -1,8 +1,6 @@ use std::path::{Path, PathBuf}; -use rpc_toolkit::{ - from_fn_async, CallRemoteHandler, Context, Empty, HandlerExt, ParentHandler, -}; +use rpc_toolkit::{from_fn_async, CallRemoteHandler, Context, Empty, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use crate::context::{CliContext, RpcContext}; diff --git a/core/startos/src/net/dhcp.rs b/core/startos/src/net/dhcp.rs index 104cda961..ffcb9774b 100644 --- a/core/startos/src/net/dhcp.rs +++ b/core/startos/src/net/dhcp.rs @@ -3,7 +3,7 @@ use std::net::IpAddr; use clap::Parser; use futures::TryStreamExt; -use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; +use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; use ts_rs::TS; diff --git a/core/startos/src/net/host/binding.rs b/core/startos/src/net/host/binding.rs index 8301821f5..76dd04059 100644 --- a/core/startos/src/net/host/binding.rs +++ b/core/startos/src/net/host/binding.rs @@ -1,4 +1,3 @@ -use imbl_value::InternedString; use serde::{Deserialize, Serialize}; use ts_rs::TS; @@ -11,17 +10,31 @@ use crate::prelude::*; #[ts(export)] pub struct BindInfo { pub options: BindOptions, - pub assigned_lan_port: Option, + pub lan: LanInfo, +} +#[derive(Clone, Copy, Debug, Deserialize, Serialize, TS, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct LanInfo { + pub assigned_port: Option, + pub assigned_ssl_port: Option, } impl BindInfo { pub fn new(available_ports: &mut AvailablePorts, options: BindOptions) -> Result { - let mut assigned_lan_port = None; - if options.add_ssl.is_some() || options.secure.is_some() { - assigned_lan_port = Some(available_ports.alloc()?); + let mut assigned_port = None; + let mut assigned_ssl_port = None; + if options.secure.is_some() { + assigned_port = Some(available_ports.alloc()?); + } + if options.add_ssl.is_some() { + assigned_ssl_port = Some(available_ports.alloc()?); } Ok(Self { options, - assigned_lan_port, + lan: LanInfo { + assigned_port, + assigned_ssl_port, + }, }) } pub fn update( @@ -29,29 +42,38 @@ impl BindInfo { available_ports: &mut AvailablePorts, options: BindOptions, ) -> Result { - let Self { - mut assigned_lan_port, - .. - } = self; - if options.add_ssl.is_some() || options.secure.is_some() { - assigned_lan_port = if let Some(port) = assigned_lan_port.take() { + let Self { mut lan, .. } = self; + if options + .secure + .map_or(false, |s| !(s.ssl && options.add_ssl.is_some())) + // doesn't make sense to have 2 listening ports, both with ssl + { + lan.assigned_port = if let Some(port) = lan.assigned_port.take() { Some(port) } else { Some(available_ports.alloc()?) }; } else { - if let Some(port) = assigned_lan_port.take() { + if let Some(port) = lan.assigned_port.take() { available_ports.free([port]); } } - Ok(Self { - options, - assigned_lan_port, - }) + if options.add_ssl.is_some() { + lan.assigned_ssl_port = if let Some(port) = lan.assigned_ssl_port.take() { + Some(port) + } else { + Some(available_ports.alloc()?) + }; + } else { + if let Some(port) = lan.assigned_ssl_port.take() { + available_ports.free([port]); + } + } + Ok(Self { options, lan }) } } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] pub struct Security { @@ -62,8 +84,6 @@ pub struct Security { #[serde(rename_all = "camelCase")] #[ts(export)] pub struct BindOptions { - #[ts(type = "string | null")] - pub scheme: Option, pub preferred_external_port: u16, pub add_ssl: Option, pub secure: Option, @@ -73,11 +93,8 @@ pub struct BindOptions { #[serde(rename_all = "camelCase")] #[ts(export)] pub struct AddSslOptions { - #[ts(type = "string | null")] - pub scheme: Option, pub preferred_external_port: u16, // #[serde(default)] // pub add_x_forwarded_headers: bool, // TODO - #[serde(default)] - pub alpn: AlpnInfo, + pub alpn: Option, } diff --git a/core/startos/src/net/host/mod.rs b/core/startos/src/net/host/mod.rs index 2d8599ba9..6cbb2dfd5 100644 --- a/core/startos/src/net/host/mod.rs +++ b/core/startos/src/net/host/mod.rs @@ -3,13 +3,13 @@ use std::collections::{BTreeMap, BTreeSet}; use imbl_value::InternedString; use models::{HostId, PackageId}; use serde::{Deserialize, Serialize}; -use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use ts_rs::TS; use crate::db::model::DatabaseModel; use crate::net::forward::AvailablePorts; use crate::net::host::address::HostAddress; use crate::net::host::binding::{BindInfo, BindOptions}; +use crate::net::service_interface::HostnameInfo; use crate::prelude::*; pub mod address; @@ -23,7 +23,8 @@ pub struct Host { pub kind: HostKind, pub bindings: BTreeMap, pub addresses: BTreeSet, - pub primary: Option, + /// COMPUTED: NetService::update + pub hostname_info: BTreeMap>, // internal port -> Hostnames } impl AsRef for Host { fn as_ref(&self) -> &Host { @@ -36,7 +37,7 @@ impl Host { kind, bindings: BTreeMap::new(), addresses: BTreeSet::new(), - primary: None, + hostname_info: BTreeMap::new(), } } } @@ -53,9 +54,9 @@ pub enum HostKind { #[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)] #[model = "Model"] #[ts(export)] -pub struct HostInfo(BTreeMap); +pub struct Hosts(pub BTreeMap); -impl Map for HostInfo { +impl Map for Hosts { type Key = HostId; type Value = Host; fn key_str(key: &Self::Key) -> Result, Error> { @@ -75,7 +76,7 @@ pub fn host_for<'a>( fn host_info<'a>( db: &'a mut DatabaseModel, package_id: &PackageId, - ) -> Result<&'a mut Model, Error> { + ) -> Result<&'a mut Model, Error> { Ok::<_, Error>( db.as_public_mut() .as_package_data_mut() @@ -129,9 +130,3 @@ impl Model { }) } } - -impl HostInfo { - pub fn get_host_primary(&self, host_id: &HostId) -> Option { - self.0.get(&host_id).and_then(|h| h.primary.clone()) - } -} diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index 205cc4e36..69c5c7940 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -4,7 +4,6 @@ use std::sync::{Arc, Weak}; use color_eyre::eyre::eyre; use imbl::OrdMap; -use lazy_format::lazy_format; use models::{HostId, OptionExt, PackageId}; use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use tracing::instrument; @@ -15,8 +14,9 @@ use crate::hostname::Hostname; use crate::net::dns::DnsController; use crate::net::forward::LanPortForwardController; use crate::net::host::address::HostAddress; -use crate::net::host::binding::{AddSslOptions, BindOptions}; +use crate::net::host::binding::{AddSslOptions, BindOptions, LanInfo}; use crate::net::host::{host_for, Host, HostKind}; +use crate::net::service_interface::{HostnameInfo, IpHostname, OnionHostname}; use crate::net::tor::TorController; use crate::net::vhost::{AlpnInfo, VHostController}; use crate::prelude::*; @@ -164,7 +164,7 @@ impl NetController { #[derive(Default, Debug)] struct HostBinds { - lan: BTreeMap, Arc<()>)>, + lan: BTreeMap, Vec>)>, tor: BTreeMap, Vec>)>, } @@ -209,105 +209,173 @@ impl NetService { .await?; self.update(id, host).await } + pub async fn clear_bindings(&mut self) -> Result<(), Error> { + // TODO BLUJ + Ok(()) + } async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> { - dbg!(&host); - dbg!(&self.binds); let ctrl = self.net_controller()?; + let mut hostname_info = BTreeMap::new(); let binds = { if !self.binds.contains_key(&id) { self.binds.insert(id.clone(), Default::default()); } self.binds.get_mut(&id).unwrap() }; - if true - // TODO: if should listen lan - { - for (port, bind) in &host.bindings { - let old_lan_bind = binds.lan.remove(port); - let old_lan_port = old_lan_bind.as_ref().map(|(external, _, _)| *external); - let lan_bind = old_lan_bind.filter(|(external, ssl, _)| { - ssl == &bind.options.add_ssl - && bind.assigned_lan_port.as_ref() == Some(external) - }); // only keep existing binding if relevant details match - if let Some(external) = bind.assigned_lan_port { - let new_lan_bind = if let Some(b) = lan_bind { - b - } else { - if let Some(ssl) = &bind.options.add_ssl { - let rc = ctrl - .vhost + let peek = ctrl.db.peek().await; + + // LAN + let server_info = peek.as_public().as_server_info(); + let ip_info = server_info.as_ip_info().de()?; + let hostname = server_info.as_hostname().de()?; + for (port, bind) in &host.bindings { + let old_lan_bind = binds.lan.remove(port); + let old_lan_port = old_lan_bind.as_ref().map(|(external, _, _)| *external); + let lan_bind = old_lan_bind + .filter(|(external, ssl, _)| ssl == &bind.options.add_ssl && bind.lan == *external); // only keep existing binding if relevant details match + if bind.lan.assigned_port.is_some() || bind.lan.assigned_ssl_port.is_some() { + let new_lan_bind = if let Some(b) = lan_bind { + b + } else { + let mut rcs = Vec::with_capacity(2); + if let Some(ssl) = &bind.options.add_ssl { + let external = bind + .lan + .assigned_ssl_port + .or_not_found("assigned ssl port")?; + rcs.push( + ctrl.vhost .add( None, external, (self.ip, *port).into(), - if bind.options.secure.as_ref().map_or(false, |s| s.ssl) { - Ok(()) + if let Some(alpn) = ssl.alpn.clone() { + Err(alpn) } else { - Err(ssl.alpn.clone()) + if bind.options.secure.as_ref().map_or(false, |s| s.ssl) { + Ok(()) + } else { + Err(AlpnInfo::Reflect) + } }, ) - .await?; - (*port, Some(ssl.clone()), rc) + .await?, + ); + } + if let Some(security) = bind.options.secure { + if bind.options.add_ssl.is_some() && security.ssl { + // doesn't make sense to have 2 listening ports, both with ssl } else { - let rc = ctrl.forward.add(external, (self.ip, *port).into()).await?; - (*port, None, rc) + let external = + bind.lan.assigned_port.or_not_found("assigned lan port")?; + rcs.push(ctrl.forward.add(external, (self.ip, *port).into()).await?); } - }; - binds.lan.insert(*port, new_lan_bind); + } + (bind.lan, bind.options.add_ssl.clone(), rcs) + }; + let mut bind_hostname_info: Vec = + hostname_info.remove(port).unwrap_or_default(); + for (interface, ip_info) in &ip_info { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public: false, + hostname: IpHostname::Local { + value: format!("{hostname}.local"), + port: new_lan_bind.0.assigned_port, + ssl_port: new_lan_bind.0.assigned_ssl_port, + }, + }); + if let Some(ipv4) = ip_info.ipv4 { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public: false, + hostname: IpHostname::Ipv4 { + value: ipv4, + port: new_lan_bind.0.assigned_port, + ssl_port: new_lan_bind.0.assigned_ssl_port, + }, + }); + } + if let Some(ipv6) = ip_info.ipv6 { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public: false, + hostname: IpHostname::Ipv6 { + value: ipv6, + port: new_lan_bind.0.assigned_port, + ssl_port: new_lan_bind.0.assigned_ssl_port, + }, + }); + } } - if let Some(external) = old_lan_port { + hostname_info.insert(*port, bind_hostname_info); + binds.lan.insert(*port, new_lan_bind); + } + if let Some(lan) = old_lan_port { + if let Some(external) = lan.assigned_ssl_port { ctrl.vhost.gc(None, external).await?; + } + if let Some(external) = lan.assigned_port { ctrl.forward.gc(external).await?; } } - let mut removed = BTreeSet::new(); - let mut removed_ssl = BTreeSet::new(); - binds.lan.retain(|internal, (external, ssl, _)| { - if host.bindings.contains_key(internal) { - true - } else { - if ssl.is_some() { - removed_ssl.insert(*external); - } else { - removed.insert(*external); - } - false - } - }); - for external in removed { - ctrl.forward.gc(external).await?; + } + let mut removed = BTreeSet::new(); + binds.lan.retain(|internal, (external, _, _)| { + if host.bindings.contains_key(internal) { + true + } else { + removed.insert(*external); + + false } - for external in removed_ssl { + }); + for lan in removed { + if let Some(external) = lan.assigned_ssl_port { ctrl.vhost.gc(None, external).await?; } + if let Some(external) = lan.assigned_port { + ctrl.forward.gc(external).await?; + } } - let tor_binds: OrdMap = host - .bindings - .iter() - .flat_map(|(internal, info)| { - let non_ssl = ( - info.options.preferred_external_port, - SocketAddr::from((self.ip, *internal)), + + struct TorHostnamePorts { + non_ssl: Option, + ssl: Option, + } + let mut tor_hostname_ports = BTreeMap::::new(); + let mut tor_binds = OrdMap::::new(); + for (internal, info) in &host.bindings { + tor_binds.insert( + info.options.preferred_external_port, + SocketAddr::from((self.ip, *internal)), + ); + if let (Some(ssl), Some(ssl_internal)) = + (&info.options.add_ssl, info.lan.assigned_ssl_port) + { + tor_binds.insert( + ssl.preferred_external_port, + SocketAddr::from(([127, 0, 0, 1], ssl_internal)), ); - if let (Some(ssl), Some(ssl_internal)) = - (&info.options.add_ssl, info.assigned_lan_port) - { - itertools::Either::Left( - [ - ( - ssl.preferred_external_port, - SocketAddr::from(([127, 0, 0, 1], ssl_internal)), - ), - non_ssl, - ] - .into_iter(), - ) - } else { - itertools::Either::Right([non_ssl].into_iter()) - } - }) - .collect(); + tor_hostname_ports.insert( + *internal, + TorHostnamePorts { + non_ssl: Some(info.options.preferred_external_port) + .filter(|p| *p != ssl.preferred_external_port), + ssl: Some(ssl.preferred_external_port), + }, + ); + } else { + tor_hostname_ports.insert( + *internal, + TorHostnamePorts { + non_ssl: Some(info.options.preferred_external_port), + ssl: None, + }, + ); + } + } let mut keep_tor_addrs = BTreeSet::new(); for addr in match host.kind { HostKind::Multi => { @@ -324,13 +392,10 @@ impl NetService { let new_tor_bind = if let Some(tor_bind) = tor_bind { tor_bind } else { - let key = ctrl - .db - .peek() - .await - .into_private() - .into_key_store() - .into_onion() + let key = peek + .as_private() + .as_key_store() + .as_onion() .get_key(address)?; let rcs = ctrl .tor @@ -338,6 +403,18 @@ impl NetService { .await?; (tor_binds.clone(), rcs) }; + for (internal, ports) in &tor_hostname_ports { + let mut bind_hostname_info = + hostname_info.remove(internal).unwrap_or_default(); + bind_hostname_info.push(HostnameInfo::Onion { + hostname: OnionHostname { + value: address.to_string(), + port: ports.non_ssl, + ssl_port: ports.ssl, + }, + }); + hostname_info.insert(*internal, bind_hostname_info); + } binds.tor.insert(address.clone(), new_tor_bind); } } @@ -347,6 +424,14 @@ impl NetService { ctrl.tor.gc(Some(addr.clone()), None).await?; } } + self.net_controller()? + .db + .mutate(|db| { + host_for(db, &self.id, &id, host.kind)? + .as_hostname_info_mut() + .ser(&hostname_info) + }) + .await?; Ok(()) } @@ -355,12 +440,13 @@ impl NetService { let mut errors = ErrorCollection::new(); if let Some(ctrl) = Weak::upgrade(&self.controller) { for (_, binds) in std::mem::take(&mut self.binds) { - for (_, (external, ssl, rc)) in binds.lan { + for (_, (lan, _, rc)) in binds.lan { drop(rc); - if ssl.is_some() { - errors.handle(ctrl.vhost.gc(None, external).await); - } else { - errors.handle(ctrl.forward.gc(external).await); + if let Some(external) = lan.assigned_ssl_port { + ctrl.vhost.gc(None, external).await?; + } + if let Some(external) = lan.assigned_port { + ctrl.forward.gc(external).await?; } } for (addr, (_, rcs)) in binds.tor { @@ -384,12 +470,12 @@ impl NetService { self.ip } - pub fn get_ext_port(&self, host_id: HostId, internal_port: u16) -> Result { + pub fn get_ext_port(&self, host_id: HostId, internal_port: u16) -> Result { let host_id_binds = self.binds.get_key_value(&host_id); match host_id_binds { Some((_, binds)) => { - if let Some(ext_port_info) = binds.lan.get(&internal_port) { - Ok(ext_port_info.0) + if let Some((lan, _, _)) = binds.lan.get(&internal_port) { + Ok(*lan) } else { Err(Error::new( eyre!( diff --git a/core/startos/src/net/service_interface.rs b/core/startos/src/net/service_interface.rs index 9a4659cfd..e905be545 100644 --- a/core/startos/src/net/service_interface.rs +++ b/core/startos/src/net/service_interface.rs @@ -1,51 +1,32 @@ use std::net::{Ipv4Addr, Ipv6Addr}; +use imbl_value::InternedString; use models::{HostId, ServiceInterfaceId}; use serde::{Deserialize, Serialize}; use ts_rs::TS; -use crate::net::host::binding::BindOptions; -use crate::net::host::HostKind; -use crate::prelude::*; - -#[derive(Clone, Debug, Deserialize, Serialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -pub struct ServiceInterfaceWithHostInfo { - #[serde(flatten)] - pub service_interface: ServiceInterface, - pub host_info: ExportedHostInfo, -} - -#[derive(Clone, Debug, Deserialize, Serialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -pub struct ExportedHostInfo { - pub id: HostId, - pub kind: HostKind, - pub hostnames: Vec, -} +use crate::net::host::address::HostAddress; #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] #[serde(rename_all_fields = "camelCase")] #[serde(tag = "kind")] -pub enum ExportedHostnameInfo { +pub enum HostnameInfo { Ip { network_interface_id: String, public: bool, - hostname: ExportedIpHostname, + hostname: IpHostname, }, Onion { - hostname: ExportedOnionHostname, + hostname: OnionHostname, }, } #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] -pub struct ExportedOnionHostname { +pub struct OnionHostname { pub value: String, pub port: Option, pub ssl_port: Option, @@ -56,7 +37,7 @@ pub struct ExportedOnionHostname { #[serde(rename_all = "camelCase")] #[serde(rename_all_fields = "camelCase")] #[serde(tag = "kind")] -pub enum ExportedIpHostname { +pub enum IpHostname { Ipv4 { value: Ipv4Addr, port: Option, @@ -110,6 +91,10 @@ pub enum ServiceInterfaceType { pub struct AddressInfo { pub username: Option, pub host_id: HostId, - pub bind_options: BindOptions, + pub internal_port: u16, + #[ts(type = "string | null")] + pub scheme: Option, + #[ts(type = "string | null")] + pub ssl_scheme: Option, pub suffix: String, } diff --git a/core/startos/src/net/vhost.rs b/core/startos/src/net/vhost.rs index 8dc1e6da3..b4f5715ae 100644 --- a/core/startos/src/net/vhost.rs +++ b/core/startos/src/net/vhost.rs @@ -205,7 +205,7 @@ impl VHostServer { .into_entries()? .into_iter() .flat_map(|(_, ips)| [ - ips.as_ipv4().de().map(|ip| ip.map(IpAddr::V4)), + ips.as_ipv4().de().map(|ip| ip.map(IpAddr::V4)), ips.as_ipv6().de().map(|ip| ip.map(IpAddr::V6)) ]) .filter_map(|a| a.transpose()) diff --git a/core/startos/src/registry/auth.rs b/core/startos/src/registry/auth.rs index 741090b4d..27655c4a8 100644 --- a/core/startos/src/registry/auth.rs +++ b/core/startos/src/registry/auth.rs @@ -138,7 +138,6 @@ impl Middleware for Auth { if request.headers().contains_key(AUTH_SIG_HEADER) { self.signer = Some( async { - let request = request; let SignatureHeader { commitment, signer, diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index e6ccdd131..da641468b 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -10,7 +10,8 @@ use persistent_container::PersistentContainer; use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, HandlerArgs, HandlerFor}; use serde::{Deserialize, Serialize}; use start_stop::StartStop; -use tokio::{fs::File, sync::Notify}; +use tokio::fs::File; +use tokio::sync::Notify; use ts_rs::TS; use crate::context::{CliContext, RpcContext}; @@ -308,7 +309,7 @@ impl Service { .send(transition::restore::Restore { path: backup_source.path().to_path_buf(), }) - .await?; + .await??; Ok(service) } @@ -370,7 +371,7 @@ impl Service { .send(transition::backup::Backup { path: guard.path().to_path_buf(), }) - .await?; + .await??; Ok(()) } diff --git a/core/startos/src/service/persistent_container.rs b/core/startos/src/service/persistent_container.rs index 06dcdc28e..03203633a 100644 --- a/core/startos/src/service/persistent_container.rs +++ b/core/startos/src/service/persistent_container.rs @@ -10,7 +10,7 @@ use imbl_value::InternedString; use models::{ProcedureName, VolumeId}; use rpc_toolkit::{Empty, Server, ShutdownHandle}; use serde::de::DeserializeOwned; -use tokio::fs::{ File}; +use tokio::fs::File; use tokio::process::Command; use tokio::sync::{oneshot, watch, Mutex, OnceCell}; use tracing::instrument; diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index 31c5a8026..aedf4c194 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -9,7 +9,6 @@ use std::sync::{Arc, Weak}; use clap::builder::ValueParserFactory; use clap::Parser; use emver::VersionRange; -use imbl::OrdMap; use imbl_value::{json, InternedString}; use itertools::Itertools; use models::{ @@ -30,12 +29,9 @@ use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::loop_dev::LoopDev; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; use crate::net::host::address::HostAddress; -use crate::net::host::binding::BindOptions; -use crate::net::host::{self, HostKind}; -use crate::net::service_interface::{ - AddressInfo, ExportedHostInfo, ExportedHostnameInfo, ServiceInterface, ServiceInterfaceType, - ServiceInterfaceWithHostInfo, -}; +use crate::net::host::binding::{BindOptions, LanInfo}; +use crate::net::host::{Host, HostKind}; +use crate::net::service_interface::{AddressInfo, ServiceInterface, ServiceInterfaceType}; use crate::prelude::*; use crate::s9pk::merkle_archive::source::http::HttpSource; use crate::s9pk::rpc::SKIP_ENV; @@ -193,7 +189,6 @@ pub fn service_effect_handler() -> ParentHandler { .subcommand("removeAddress", from_fn_async(remove_address).no_cli()) .subcommand("exportAction", from_fn_async(export_action).no_cli()) .subcommand("removeAction", from_fn_async(remove_action).no_cli()) - .subcommand("reverseProxy", from_fn_async(reverse_proxy).no_cli()) .subcommand("mount", from_fn_async(mount).no_cli()) // TODO Callbacks @@ -233,8 +228,6 @@ struct ExportServiceInterfaceParams { masked: bool, address_info: AddressInfo, r#type: ServiceInterfaceType, - host_kind: HostKind, - hostnames: Vec, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] @@ -242,9 +235,8 @@ struct ExportServiceInterfaceParams { struct GetPrimaryUrlParams { #[ts(type = "string | null")] package_id: Option, - service_interface_id: String, + service_interface_id: ServiceInterfaceId, callback: Callback, - host_id: HostId, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] @@ -276,37 +268,7 @@ struct RemoveActionParams { #[ts(type = "string")] id: ActionId, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -struct ReverseProxyBind { - ip: Option, - port: u32, - ssl: bool, -} -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -struct ReverseProxyDestination { - ip: Option, - port: u32, - ssl: bool, -} -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -struct ReverseProxyHttp { - #[ts(type = "null | {[key: string]: string}")] - headers: Option>, -} -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -struct ReverseProxyParams { - bind: ReverseProxyBind, - dst: ReverseProxyDestination, - http: ReverseProxyHttp, -} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] @@ -367,7 +329,7 @@ async fn get_container_ip(context: EffectContext, _: Empty) -> Result Result { +) -> Result { let internal_port = data.internal_port as u16; let context = context.deref()?; @@ -404,13 +366,10 @@ async fn export_service_interface( masked, address_info, r#type, - host_kind, - hostnames, }: ExportServiceInterfaceParams, ) -> Result<(), Error> { let context = context.deref()?; let package_id = context.id.clone(); - let host_id = address_info.host_id.clone(); let service_interface = ServiceInterface { id: id.clone(), @@ -422,15 +381,7 @@ async fn export_service_interface( address_info, interface_type: r#type, }; - let host_info = ExportedHostInfo { - id: host_id, - kind: host_kind, - hostnames, - }; - let svc_interface_with_host_info = ServiceInterfaceWithHostInfo { - service_interface, - host_info, - }; + let svc_interface_with_host_info = service_interface; context .ctx @@ -449,35 +400,26 @@ async fn export_service_interface( } async fn get_primary_url( context: EffectContext, - data: GetPrimaryUrlParams, -) -> Result { + GetPrimaryUrlParams { + package_id, + service_interface_id, + callback, + }: GetPrimaryUrlParams, +) -> Result, Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = package_id.unwrap_or_else(|| context.id.clone()); - let db_model = context.ctx.db.peek().await; - - let pkg_data_model = db_model - .as_public() - .as_package_data() - .as_idx(&package_id) - .or_not_found(&package_id)?; - - let host = pkg_data_model.de()?.hosts.get_host_primary(&data.host_id); - - match host { - Some(host_address) => Ok(host_address), - None => Err(Error::new( - eyre!("Primary Url not found for {}", data.host_id), - crate::ErrorKind::NotFound, - )), - } + Ok(None) // TODO } async fn list_service_interfaces( context: EffectContext, - data: ListServiceInterfacesParams, -) -> Result, Error> { + ListServiceInterfacesParams { + package_id, + callback, + }: ListServiceInterfacesParams, +) -> Result, Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = package_id.unwrap_or_else(|| context.id.clone()); context .ctx @@ -553,10 +495,8 @@ async fn remove_action(context: EffectContext, data: RemoveActionParams) -> Resu .await?; Ok(()) } -async fn reverse_proxy(context: EffectContext, data: ReverseProxyParams) -> Result { - todo!() -} async fn mount(context: EffectContext, data: MountParams) -> Result { + // TODO todo!() } @@ -564,49 +504,42 @@ async fn mount(context: EffectContext, data: MountParams) -> Result, - service_interface_id: String, + host_id: HostId, #[ts(type = "string | null")] package_id: Option, callback: Callback, } async fn get_host_info( ctx: EffectContext, - GetHostInfoParams { .. }: GetHostInfoParams, -) -> Result { + GetHostInfoParams { + callback, + package_id, + host_id, + }: GetHostInfoParams, +) -> Result { let ctx = ctx.deref()?; - Ok(json!({ - "id": "fakeId1", - "kind": "multi", - "hostnames": [{ - "kind": "ip", - "networkInterfaceId": "fakeNetworkInterfaceId1", - "public": true, - "hostname":{ - "kind": "domain", - "domain": format!("{}", ctx.id), - "subdomain": (), - "port": (), - "sslPort": () - } - } + let db = ctx.ctx.db.peek().await; + let package_id = package_id.unwrap_or_else(|| ctx.id.clone()); - ] - })) + db.as_public() + .as_package_data() + .as_idx(&package_id) + .or_not_found(&package_id)? + .as_hosts() + .as_idx(&host_id) + .or_not_found(&host_id)? + .de() } -async fn clear_bindings(context: EffectContext, _: Empty) -> Result { - todo!() +async fn clear_bindings(context: EffectContext, _: Empty) -> Result<(), Error> { + let ctx = context.deref()?; + let mut svc = ctx.persistent_container.net_service.lock().await; + svc.clear_bindings().await?; + Ok(()) } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] @@ -619,15 +552,13 @@ struct BindParams { #[serde(flatten)] options: BindOptions, } -async fn bind( - context: EffectContext, - BindParams { +async fn bind(context: EffectContext, bind_params: Value) -> Result<(), Error> { + let BindParams { kind, id, internal_port, options, - }: BindParams, -) -> Result<(), Error> { + } = from_value(bind_params)?; let ctx = context.deref()?; let mut svc = ctx.persistent_container.net_service.lock().await; svc.bind(kind, id, internal_port, options).await @@ -639,39 +570,32 @@ async fn bind( struct GetServiceInterfaceParams { #[ts(type = "string | null")] package_id: Option, - service_interface_id: String, + service_interface_id: ServiceInterfaceId, callback: Callback, } + async fn get_service_interface( - _: EffectContext, + ctx: EffectContext, GetServiceInterfaceParams { callback, package_id, service_interface_id, }: GetServiceInterfaceParams, -) -> Result { - // TODO @Dr_Bonez - Ok(json!({ - "id": service_interface_id, - "name": service_interface_id, - "description": "This is a fake", - "hasPrimary": true, - "disabled": false, - "masked": false, - "addressInfo": json!({ - "username": Value::Null, - "hostId": "HostId?", - "options": json!({ - "scheme": Value::Null, - "preferredExternalPort": 80, - "addSsl":Value::Null, - "secure": false, - "ssl": false - }), - "suffix": "http" - }), - "type": "api" - })) +) -> Result { + let ctx = ctx.deref()?; + let package_id = package_id.unwrap_or_else(|| ctx.id.clone()); + let db = ctx.ctx.db.peek().await; + + let interface = db + .as_public() + .as_package_data() + .as_idx(&package_id) + .or_not_found(&package_id)? + .as_service_interfaces() + .as_idx(&service_interface_id) + .or_not_found(&service_interface_id)? + .de()?; + Ok(interface) } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Parser, TS)] @@ -764,6 +688,7 @@ async fn get_ssl_certificate( host_id, }: GetSslCertificateParams, ) -> Result { + // TODO let fake = include_str!("./fake.cert.pem"); Ok(json!([fake, fake, fake])) } @@ -785,6 +710,7 @@ async fn get_ssl_key( algorithm, }: GetSslKeyParams, ) -> Result { + // TODO let fake = include_str!("./fake.cert.key"); Ok(json!(fake)) } diff --git a/sdk/lib/StartSdk.ts b/sdk/lib/StartSdk.ts index 381914c13..f5959a084 100644 --- a/sdk/lib/StartSdk.ts +++ b/sdk/lib/StartSdk.ts @@ -59,7 +59,7 @@ import { } from "./interfaces/setupInterfaces" import { successFailure } from "./trigger/successFailure" import { HealthReceipt } from "./health/HealthReceipt" -import { MultiHost, Scheme, SingleHost, StaticHost } from "./interfaces/Host" +import { MultiHost, Scheme } from "./interfaces/Host" import { ServiceInterfaceBuilder } from "./interfaces/ServiceInterfaceBuilder" import { GetSystemSmtp } from "./util/GetSystemSmtp" import nullIfEmpty from "./util/nullIfEmpty" @@ -178,10 +178,10 @@ export class StartSdk { }, host: { - static: (effects: Effects, id: string) => - new StaticHost({ id, effects }), - single: (effects: Effects, id: string) => - new SingleHost({ id, effects }), + // static: (effects: Effects, id: string) => + // new StaticHost({ id, effects }), + // single: (effects: Effects, id: string) => + // new SingleHost({ id, effects }), multi: (effects: Effects, id: string) => new MultiHost({ id, effects }), }, nullIfEmpty, diff --git a/sdk/lib/interfaces/Host.ts b/sdk/lib/interfaces/Host.ts index 2f1353cc1..96a76628a 100644 --- a/sdk/lib/interfaces/Host.ts +++ b/sdk/lib/interfaces/Host.ts @@ -1,14 +1,14 @@ -import { object, string } from "ts-matches" +import { number, object, string } from "ts-matches" import { Effects } from "../types" import { Origin } from "./Origin" -import { AddSslOptions } from ".././osBindings" +import { AddSslOptions, BindParams } from ".././osBindings" import { Security } from ".././osBindings" import { BindOptions } from ".././osBindings" import { AlpnInfo } from ".././osBindings" export { AddSslOptions, Security, BindOptions } -const knownProtocols = { +export const knownProtocols = { http: { secure: null, defaultPort: 80, @@ -69,19 +69,17 @@ type NotProtocolsWithSslVariants = Exclude< type BindOptionsByKnownProtocol = | { protocol: ProtocolsWithSslVariants - preferredExternalPort?: number - scheme?: Scheme + preferredExternalPort: number addSsl?: Partial } | { protocol: NotProtocolsWithSslVariants - preferredExternalPort?: number - scheme?: Scheme + preferredExternalPort: number addSsl?: AddSslOptions } -type BindOptionsByProtocol = BindOptionsByKnownProtocol | BindOptions +export type BindOptionsByProtocol = BindOptionsByKnownProtocol | BindOptions -export type HostKind = "static" | "single" | "multi" +export type HostKind = BindParams["kind"] const hasStringProtocol = object({ protocol: string, @@ -110,66 +108,62 @@ export class Host { private async bindPortForUnknown( internalPort: number, options: { - scheme: Scheme preferredExternalPort: number addSsl: AddSslOptions | null secure: { ssl: boolean } | null }, ) { - await this.options.effects.bind({ + const binderOptions = { kind: this.options.kind, id: this.options.id, - internalPort: internalPort, + internalPort, ...options, - }) + } + await this.options.effects.bind(binderOptions) - return new Origin(this, options) + return new Origin(this, internalPort, null, null) } private async bindPortForKnown( options: BindOptionsByKnownProtocol, internalPort: number, ) { - const scheme = - options.scheme === undefined ? options.protocol : options.scheme const protoInfo = knownProtocols[options.protocol] const preferredExternalPort = options.preferredExternalPort || knownProtocols[options.protocol].defaultPort - const addSsl = this.getAddSsl(options, protoInfo) + const sslProto = this.getSslProto(options, protoInfo) + const addSsl = + sslProto && "alpn" in protoInfo + ? { + // addXForwardedHeaders: null, + preferredExternalPort: knownProtocols[sslProto].defaultPort, + scheme: sslProto, + alpn: protoInfo.alpn, + ...("addSsl" in options ? options.addSsl : null), + } + : null const secure: Security | null = !protoInfo.secure ? null : { ssl: false } - const newOptions = { - scheme, - preferredExternalPort, - addSsl, - secure, - } - await this.options.effects.bind({ kind: this.options.kind, id: this.options.id, internalPort, - ...newOptions, + preferredExternalPort, + addSsl, + secure, }) - return new Origin(this, newOptions) + return new Origin(this, internalPort, options.protocol, sslProto) } - private getAddSsl( + private getSslProto( options: BindOptionsByKnownProtocol, protoInfo: KnownProtocols[keyof KnownProtocols], - ): AddSslOptions | null { + ) { if (inObject("noAddSsl", options) && options.noAddSsl) return null - if ("withSsl" in protoInfo && protoInfo.withSsl) - return { - // addXForwardedHeaders: null, - preferredExternalPort: knownProtocols[protoInfo.withSsl].defaultPort, - scheme: protoInfo.withSsl, - alpn: protoInfo.alpn, - ...("addSsl" in options ? options.addSsl : null), - } + if ("withSsl" in protoInfo && protoInfo.withSsl) return protoInfo.withSsl return null } } @@ -181,17 +175,17 @@ function inObject( return key in obj } -export class StaticHost extends Host { - constructor(options: { effects: Effects; id: string }) { - super({ ...options, kind: "static" }) - } -} +// export class StaticHost extends Host { +// constructor(options: { effects: Effects; id: string }) { +// super({ ...options, kind: "static" }) +// } +// } -export class SingleHost extends Host { - constructor(options: { effects: Effects; id: string }) { - super({ ...options, kind: "single" }) - } -} +// export class SingleHost extends Host { +// constructor(options: { effects: Effects; id: string }) { +// super({ ...options, kind: "single" }) +// } +// } export class MultiHost extends Host { constructor(options: { effects: Effects; id: string }) { diff --git a/sdk/lib/interfaces/Origin.ts b/sdk/lib/interfaces/Origin.ts index aaadbea50..52afe1ed3 100644 --- a/sdk/lib/interfaces/Origin.ts +++ b/sdk/lib/interfaces/Origin.ts @@ -6,7 +6,9 @@ import { ServiceInterfaceBuilder } from "./ServiceInterfaceBuilder" export class Origin { constructor( readonly host: T, - readonly options: BindOptions, + readonly internalPort: number, + readonly scheme: string | null, + readonly sslScheme: string | null, ) {} build({ username, path, search, schemeOverride }: BuildOptions): AddressInfo { @@ -20,18 +22,9 @@ export class Origin { return { hostId: this.host.options.id, - bindOptions: { - ...this.options, - scheme: schemeOverride ? schemeOverride.noSsl : this.options.scheme, - addSsl: this.options.addSsl - ? { - ...this.options.addSsl, - scheme: schemeOverride - ? schemeOverride.ssl - : this.options.addSsl.scheme, - } - : null, - }, + internalPort: this.internalPort, + scheme: schemeOverride ? schemeOverride.noSsl : this.scheme, + sslScheme: schemeOverride ? schemeOverride.ssl : this.sslScheme, suffix: `${path}${qp}`, username, } diff --git a/sdk/lib/osBindings/AddAssetParams.ts b/sdk/lib/osBindings/AddAssetParams.ts index ce6128cf7..ffd7db675 100644 --- a/sdk/lib/osBindings/AddAssetParams.ts +++ b/sdk/lib/osBindings/AddAssetParams.ts @@ -6,7 +6,6 @@ import type { Version } from "./Version" export type AddAssetParams = { version: Version platform: string - upload: boolean url: string signature: AnySignature commitment: Blake3Commitment diff --git a/sdk/lib/osBindings/AddPackageParams.ts b/sdk/lib/osBindings/AddPackageParams.ts new file mode 100644 index 000000000..4395b9b8a --- /dev/null +++ b/sdk/lib/osBindings/AddPackageParams.ts @@ -0,0 +1,9 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AnySignature } from "./AnySignature" +import type { MerkleArchiveCommitment } from "./MerkleArchiveCommitment" + +export type AddPackageParams = { + url: string + commitment: MerkleArchiveCommitment + signature: AnySignature +} diff --git a/sdk/lib/osBindings/AddSslOptions.ts b/sdk/lib/osBindings/AddSslOptions.ts index daa9aee0e..35071aff3 100644 --- a/sdk/lib/osBindings/AddSslOptions.ts +++ b/sdk/lib/osBindings/AddSslOptions.ts @@ -2,7 +2,6 @@ import type { AlpnInfo } from "./AlpnInfo" export type AddSslOptions = { - scheme: string | null preferredExternalPort: number - alpn: AlpnInfo + alpn: AlpnInfo | null } diff --git a/sdk/lib/osBindings/AddressInfo.ts b/sdk/lib/osBindings/AddressInfo.ts index 818b570bb..c7a1c1af1 100644 --- a/sdk/lib/osBindings/AddressInfo.ts +++ b/sdk/lib/osBindings/AddressInfo.ts @@ -1,10 +1,11 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { BindOptions } from "./BindOptions" import type { HostId } from "./HostId" export type AddressInfo = { username: string | null hostId: HostId - bindOptions: BindOptions + internalPort: number + scheme: string | null + sslScheme: string | null suffix: string } diff --git a/sdk/lib/osBindings/BindInfo.ts b/sdk/lib/osBindings/BindInfo.ts index d7b37a70f..221b1c37c 100644 --- a/sdk/lib/osBindings/BindInfo.ts +++ b/sdk/lib/osBindings/BindInfo.ts @@ -1,4 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { BindOptions } from "./BindOptions" +import type { LanInfo } from "./LanInfo" -export type BindInfo = { options: BindOptions; assignedLanPort: number | null } +export type BindInfo = { options: BindOptions; lan: LanInfo } diff --git a/sdk/lib/osBindings/BindOptions.ts b/sdk/lib/osBindings/BindOptions.ts index a462a8cfc..49d9ecbf2 100644 --- a/sdk/lib/osBindings/BindOptions.ts +++ b/sdk/lib/osBindings/BindOptions.ts @@ -3,7 +3,6 @@ import type { AddSslOptions } from "./AddSslOptions" import type { Security } from "./Security" export type BindOptions = { - scheme: string | null preferredExternalPort: number addSsl: AddSslOptions | null secure: Security | null diff --git a/sdk/lib/osBindings/BindParams.ts b/sdk/lib/osBindings/BindParams.ts index 544f65a40..fcc450476 100644 --- a/sdk/lib/osBindings/BindParams.ts +++ b/sdk/lib/osBindings/BindParams.ts @@ -8,7 +8,6 @@ export type BindParams = { kind: HostKind id: HostId internalPort: number - scheme: string | null preferredExternalPort: number addSsl: AddSslOptions | null secure: Security | null diff --git a/sdk/lib/osBindings/ExportServiceInterfaceParams.ts b/sdk/lib/osBindings/ExportServiceInterfaceParams.ts index 847a6090a..b93e83f7c 100644 --- a/sdk/lib/osBindings/ExportServiceInterfaceParams.ts +++ b/sdk/lib/osBindings/ExportServiceInterfaceParams.ts @@ -1,7 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AddressInfo } from "./AddressInfo" -import type { ExportedHostnameInfo } from "./ExportedHostnameInfo" -import type { HostKind } from "./HostKind" import type { ServiceInterfaceId } from "./ServiceInterfaceId" import type { ServiceInterfaceType } from "./ServiceInterfaceType" @@ -14,6 +12,4 @@ export type ExportServiceInterfaceParams = { masked: boolean addressInfo: AddressInfo type: ServiceInterfaceType - hostKind: HostKind - hostnames: Array } diff --git a/sdk/lib/osBindings/ExportedHostInfo.ts b/sdk/lib/osBindings/ExportedHostInfo.ts deleted file mode 100644 index 5e8a25ad6..000000000 --- a/sdk/lib/osBindings/ExportedHostInfo.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ExportedHostnameInfo } from "./ExportedHostnameInfo" -import type { HostId } from "./HostId" -import type { HostKind } from "./HostKind" - -export type ExportedHostInfo = { - id: HostId - kind: HostKind - hostnames: Array -} diff --git a/sdk/lib/osBindings/ExportedHostnameInfo.ts b/sdk/lib/osBindings/ExportedHostnameInfo.ts deleted file mode 100644 index 42f849529..000000000 --- a/sdk/lib/osBindings/ExportedHostnameInfo.ts +++ /dev/null @@ -1,12 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ExportedIpHostname } from "./ExportedIpHostname" -import type { ExportedOnionHostname } from "./ExportedOnionHostname" - -export type ExportedHostnameInfo = - | { - kind: "ip" - networkInterfaceId: string - public: boolean - hostname: ExportedIpHostname - } - | { kind: "onion"; hostname: ExportedOnionHostname } diff --git a/sdk/lib/osBindings/GetHostInfoParams.ts b/sdk/lib/osBindings/GetHostInfoParams.ts index 1ffb95de0..120b4cfe1 100644 --- a/sdk/lib/osBindings/GetHostInfoParams.ts +++ b/sdk/lib/osBindings/GetHostInfoParams.ts @@ -1,10 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Callback } from "./Callback" -import type { GetHostInfoParamsKind } from "./GetHostInfoParamsKind" +import type { HostId } from "./HostId" export type GetHostInfoParams = { - kind: GetHostInfoParamsKind | null - serviceInterfaceId: string + hostId: HostId packageId: string | null callback: Callback } diff --git a/sdk/lib/osBindings/GetHostInfoParamsKind.ts b/sdk/lib/osBindings/GetHostInfoParamsKind.ts deleted file mode 100644 index 482eb7177..000000000 --- a/sdk/lib/osBindings/GetHostInfoParamsKind.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type GetHostInfoParamsKind = "multi" diff --git a/sdk/lib/osBindings/GetPackageResponse.ts b/sdk/lib/osBindings/GetPackageResponse.ts index 5bf24bfc0..3e1dd4e9d 100644 --- a/sdk/lib/osBindings/GetPackageResponse.ts +++ b/sdk/lib/osBindings/GetPackageResponse.ts @@ -4,6 +4,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo" import type { Version } from "./Version" export type GetPackageResponse = { + categories: string[] best: { [key: Version]: PackageVersionInfo } otherVersions?: { [key: Version]: PackageInfoShort } } diff --git a/sdk/lib/osBindings/GetPackageResponseFull.ts b/sdk/lib/osBindings/GetPackageResponseFull.ts index 579924291..e375dd489 100644 --- a/sdk/lib/osBindings/GetPackageResponseFull.ts +++ b/sdk/lib/osBindings/GetPackageResponseFull.ts @@ -3,6 +3,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo" import type { Version } from "./Version" export type GetPackageResponseFull = { + categories: string[] best: { [key: Version]: PackageVersionInfo } otherVersions: { [key: Version]: PackageVersionInfo } } diff --git a/sdk/lib/osBindings/GetPrimaryUrlParams.ts b/sdk/lib/osBindings/GetPrimaryUrlParams.ts index 1a68ecc7b..dbafa4152 100644 --- a/sdk/lib/osBindings/GetPrimaryUrlParams.ts +++ b/sdk/lib/osBindings/GetPrimaryUrlParams.ts @@ -1,10 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Callback } from "./Callback" -import type { HostId } from "./HostId" +import type { ServiceInterfaceId } from "./ServiceInterfaceId" export type GetPrimaryUrlParams = { packageId: string | null - serviceInterfaceId: string + serviceInterfaceId: ServiceInterfaceId callback: Callback - hostId: HostId } diff --git a/sdk/lib/osBindings/GetServiceInterfaceParams.ts b/sdk/lib/osBindings/GetServiceInterfaceParams.ts index ee3a0c03d..0a8bdfcb2 100644 --- a/sdk/lib/osBindings/GetServiceInterfaceParams.ts +++ b/sdk/lib/osBindings/GetServiceInterfaceParams.ts @@ -1,8 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Callback } from "./Callback" +import type { ServiceInterfaceId } from "./ServiceInterfaceId" export type GetServiceInterfaceParams = { packageId: string | null - serviceInterfaceId: string + serviceInterfaceId: ServiceInterfaceId callback: Callback } diff --git a/sdk/lib/osBindings/HardwareRequirements.ts b/sdk/lib/osBindings/HardwareRequirements.ts index 4964bc66f..0e1da1f36 100644 --- a/sdk/lib/osBindings/HardwareRequirements.ts +++ b/sdk/lib/osBindings/HardwareRequirements.ts @@ -3,5 +3,5 @@ export type HardwareRequirements = { device: { [key: string]: string } ram: number | null - arch: Array | null + arch: string[] | null } diff --git a/sdk/lib/osBindings/Host.ts b/sdk/lib/osBindings/Host.ts index a476c0adc..7d8cf3a90 100644 --- a/sdk/lib/osBindings/Host.ts +++ b/sdk/lib/osBindings/Host.ts @@ -2,10 +2,14 @@ import type { BindInfo } from "./BindInfo" import type { HostAddress } from "./HostAddress" import type { HostKind } from "./HostKind" +import type { HostnameInfo } from "./HostnameInfo" export type Host = { kind: HostKind bindings: { [key: number]: BindInfo } addresses: Array - primary: HostAddress | null + /** + * COMPUTED: NetService::update + */ + hostnameInfo: { [key: number]: Array } } diff --git a/sdk/lib/osBindings/HostnameInfo.ts b/sdk/lib/osBindings/HostnameInfo.ts new file mode 100644 index 000000000..ef8bafac0 --- /dev/null +++ b/sdk/lib/osBindings/HostnameInfo.ts @@ -0,0 +1,12 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { IpHostname } from "./IpHostname" +import type { OnionHostname } from "./OnionHostname" + +export type HostnameInfo = + | { + kind: "ip" + networkInterfaceId: string + public: boolean + hostname: IpHostname + } + | { kind: "onion"; hostname: OnionHostname } diff --git a/sdk/lib/osBindings/HostInfo.ts b/sdk/lib/osBindings/Hosts.ts similarity index 79% rename from sdk/lib/osBindings/HostInfo.ts rename to sdk/lib/osBindings/Hosts.ts index d39b56ebe..c7aa84996 100644 --- a/sdk/lib/osBindings/HostInfo.ts +++ b/sdk/lib/osBindings/Hosts.ts @@ -2,4 +2,4 @@ import type { Host } from "./Host" import type { HostId } from "./HostId" -export type HostInfo = { [key: HostId]: Host } +export type Hosts = { [key: HostId]: Host } diff --git a/sdk/lib/osBindings/ExportedIpHostname.ts b/sdk/lib/osBindings/IpHostname.ts similarity index 94% rename from sdk/lib/osBindings/ExportedIpHostname.ts rename to sdk/lib/osBindings/IpHostname.ts index ea06fe05b..4a6b5e87c 100644 --- a/sdk/lib/osBindings/ExportedIpHostname.ts +++ b/sdk/lib/osBindings/IpHostname.ts @@ -1,6 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type ExportedIpHostname = +export type IpHostname = | { kind: "ipv4"; value: string; port: number | null; sslPort: number | null } | { kind: "ipv6"; value: string; port: number | null; sslPort: number | null } | { diff --git a/sdk/lib/osBindings/ReverseProxyDestination.ts b/sdk/lib/osBindings/LanInfo.ts similarity index 55% rename from sdk/lib/osBindings/ReverseProxyDestination.ts rename to sdk/lib/osBindings/LanInfo.ts index 88b5dd650..59b8a5519 100644 --- a/sdk/lib/osBindings/ReverseProxyDestination.ts +++ b/sdk/lib/osBindings/LanInfo.ts @@ -1,7 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type ReverseProxyDestination = { - ip: string | null - port: number - ssl: boolean +export type LanInfo = { + assignedPort: number | null + assignedSslPort: number | null } diff --git a/sdk/lib/osBindings/ExportedOnionHostname.ts b/sdk/lib/osBindings/OnionHostname.ts similarity index 82% rename from sdk/lib/osBindings/ExportedOnionHostname.ts rename to sdk/lib/osBindings/OnionHostname.ts index 2c7d67cec..0bea8245e 100644 --- a/sdk/lib/osBindings/ExportedOnionHostname.ts +++ b/sdk/lib/osBindings/OnionHostname.ts @@ -1,6 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type ExportedOnionHostname = { +export type OnionHostname = { value: string port: number | null sslPort: number | null diff --git a/sdk/lib/osBindings/OsVersionInfo.ts b/sdk/lib/osBindings/OsVersionInfo.ts index 7cd0fde9e..a88115350 100644 --- a/sdk/lib/osBindings/OsVersionInfo.ts +++ b/sdk/lib/osBindings/OsVersionInfo.ts @@ -7,7 +7,7 @@ export type OsVersionInfo = { headline: string releaseNotes: string sourceVersion: string - signers: Array + authorized: Array iso: { [key: string]: RegistryAsset } squashfs: { [key: string]: RegistryAsset } img: { [key: string]: RegistryAsset } diff --git a/sdk/lib/osBindings/PackageDataEntry.ts b/sdk/lib/osBindings/PackageDataEntry.ts index f88c6d7be..ef805741b 100644 --- a/sdk/lib/osBindings/PackageDataEntry.ts +++ b/sdk/lib/osBindings/PackageDataEntry.ts @@ -3,10 +3,10 @@ import type { ActionId } from "./ActionId" import type { ActionMetadata } from "./ActionMetadata" import type { CurrentDependencies } from "./CurrentDependencies" import type { DataUrl } from "./DataUrl" -import type { HostInfo } from "./HostInfo" +import type { Hosts } from "./Hosts" import type { PackageState } from "./PackageState" +import type { ServiceInterface } from "./ServiceInterface" import type { ServiceInterfaceId } from "./ServiceInterfaceId" -import type { ServiceInterfaceWithHostInfo } from "./ServiceInterfaceWithHostInfo" import type { Status } from "./Status" export type PackageDataEntry = { @@ -18,7 +18,7 @@ export type PackageDataEntry = { lastBackup: string | null currentDependencies: CurrentDependencies actions: { [key: ActionId]: ActionMetadata } - serviceInterfaces: { [key: ServiceInterfaceId]: ServiceInterfaceWithHostInfo } - hosts: HostInfo + serviceInterfaces: { [key: ServiceInterfaceId]: ServiceInterface } + hosts: Hosts storeExposedDependents: string[] } diff --git a/sdk/lib/osBindings/PackageInfo.ts b/sdk/lib/osBindings/PackageInfo.ts index af340424f..6d07cd43e 100644 --- a/sdk/lib/osBindings/PackageInfo.ts +++ b/sdk/lib/osBindings/PackageInfo.ts @@ -4,6 +4,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo" import type { Version } from "./Version" export type PackageInfo = { - signers: Array + authorized: Array versions: { [key: Version]: PackageVersionInfo } + categories: string[] } diff --git a/sdk/lib/osBindings/PackageVersionInfo.ts b/sdk/lib/osBindings/PackageVersionInfo.ts index da82540cd..bdded46bd 100644 --- a/sdk/lib/osBindings/PackageVersionInfo.ts +++ b/sdk/lib/osBindings/PackageVersionInfo.ts @@ -17,7 +17,6 @@ export type PackageVersionInfo = { upstreamRepo: string supportSite: string marketingSite: string - categories: string[] osVersion: Version hardwareRequirements: HardwareRequirements sourceVersion: string | null diff --git a/sdk/lib/osBindings/ReverseProxyBind.ts b/sdk/lib/osBindings/ReverseProxyBind.ts deleted file mode 100644 index bd07b9489..000000000 --- a/sdk/lib/osBindings/ReverseProxyBind.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type ReverseProxyBind = { ip: string | null; port: number; ssl: boolean } diff --git a/sdk/lib/osBindings/ReverseProxyHttp.ts b/sdk/lib/osBindings/ReverseProxyHttp.ts deleted file mode 100644 index ba49e81dc..000000000 --- a/sdk/lib/osBindings/ReverseProxyHttp.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type ReverseProxyHttp = { headers: null | { [key: string]: string } } diff --git a/sdk/lib/osBindings/ReverseProxyParams.ts b/sdk/lib/osBindings/ReverseProxyParams.ts deleted file mode 100644 index 00062cbdf..000000000 --- a/sdk/lib/osBindings/ReverseProxyParams.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ReverseProxyBind } from "./ReverseProxyBind" -import type { ReverseProxyDestination } from "./ReverseProxyDestination" -import type { ReverseProxyHttp } from "./ReverseProxyHttp" - -export type ReverseProxyParams = { - bind: ReverseProxyBind - dst: ReverseProxyDestination - http: ReverseProxyHttp -} diff --git a/sdk/lib/osBindings/ServiceInterfaceWithHostInfo.ts b/sdk/lib/osBindings/ServiceInterfaceWithHostInfo.ts deleted file mode 100644 index 979e6b500..000000000 --- a/sdk/lib/osBindings/ServiceInterfaceWithHostInfo.ts +++ /dev/null @@ -1,17 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { AddressInfo } from "./AddressInfo" -import type { ExportedHostInfo } from "./ExportedHostInfo" -import type { ServiceInterfaceId } from "./ServiceInterfaceId" -import type { ServiceInterfaceType } from "./ServiceInterfaceType" - -export type ServiceInterfaceWithHostInfo = { - hostInfo: ExportedHostInfo - id: ServiceInterfaceId - name: string - description: string - hasPrimary: boolean - disabled: boolean - masked: boolean - addressInfo: AddressInfo - type: ServiceInterfaceType -} diff --git a/sdk/lib/osBindings/index.ts b/sdk/lib/osBindings/index.ts index 16fc56a74..efd7a5efb 100644 --- a/sdk/lib/osBindings/index.ts +++ b/sdk/lib/osBindings/index.ts @@ -3,6 +3,7 @@ export { ActionId } from "./ActionId" export { ActionMetadata } from "./ActionMetadata" export { AddAdminParams } from "./AddAdminParams" export { AddAssetParams } from "./AddAssetParams" +export { AddPackageParams } from "./AddPackageParams" export { AddressInfo } from "./AddressInfo" export { AddSslOptions } from "./AddSslOptions" export { AddVersionParams } from "./AddVersionParams" @@ -40,15 +41,10 @@ export { Duration } from "./Duration" export { EncryptedWire } from "./EncryptedWire" export { ExecuteAction } from "./ExecuteAction" export { ExportActionParams } from "./ExportActionParams" -export { ExportedHostInfo } from "./ExportedHostInfo" -export { ExportedHostnameInfo } from "./ExportedHostnameInfo" -export { ExportedIpHostname } from "./ExportedIpHostname" -export { ExportedOnionHostname } from "./ExportedOnionHostname" export { ExportServiceInterfaceParams } from "./ExportServiceInterfaceParams" export { ExposeForDependentsParams } from "./ExposeForDependentsParams" export { FullIndex } from "./FullIndex" export { FullProgress } from "./FullProgress" -export { GetHostInfoParamsKind } from "./GetHostInfoParamsKind" export { GetHostInfoParams } from "./GetHostInfoParams" export { GetOsAssetParams } from "./GetOsAssetParams" export { GetPackageParams } from "./GetPackageParams" @@ -69,14 +65,17 @@ export { HealthCheckId } from "./HealthCheckId" export { HealthCheckResult } from "./HealthCheckResult" export { HostAddress } from "./HostAddress" export { HostId } from "./HostId" -export { HostInfo } from "./HostInfo" export { HostKind } from "./HostKind" +export { HostnameInfo } from "./HostnameInfo" +export { Hosts } from "./Hosts" export { Host } from "./Host" export { ImageId } from "./ImageId" export { InstalledState } from "./InstalledState" export { InstallingInfo } from "./InstallingInfo" export { InstallingState } from "./InstallingState" +export { IpHostname } from "./IpHostname" export { IpInfo } from "./IpInfo" +export { LanInfo } from "./LanInfo" export { ListServiceInterfacesParams } from "./ListServiceInterfacesParams" export { ListVersionSignersParams } from "./ListVersionSignersParams" export { MainStatus } from "./MainStatus" @@ -86,6 +85,7 @@ export { MerkleArchiveCommitment } from "./MerkleArchiveCommitment" export { MountParams } from "./MountParams" export { MountTarget } from "./MountTarget" export { NamedProgress } from "./NamedProgress" +export { OnionHostname } from "./OnionHostname" export { OsIndex } from "./OsIndex" export { OsVersionInfo } from "./OsVersionInfo" export { PackageDataEntry } from "./PackageDataEntry" @@ -106,10 +106,6 @@ export { RemoveActionParams } from "./RemoveActionParams" export { RemoveAddressParams } from "./RemoveAddressParams" export { RemoveVersionParams } from "./RemoveVersionParams" export { RequestCommitment } from "./RequestCommitment" -export { ReverseProxyBind } from "./ReverseProxyBind" -export { ReverseProxyDestination } from "./ReverseProxyDestination" -export { ReverseProxyHttp } from "./ReverseProxyHttp" -export { ReverseProxyParams } from "./ReverseProxyParams" export { Security } from "./Security" export { ServerInfo } from "./ServerInfo" export { ServerSpecs } from "./ServerSpecs" @@ -117,7 +113,6 @@ export { ServerStatus } from "./ServerStatus" export { ServiceInterfaceId } from "./ServiceInterfaceId" export { ServiceInterface } from "./ServiceInterface" export { ServiceInterfaceType } from "./ServiceInterfaceType" -export { ServiceInterfaceWithHostInfo } from "./ServiceInterfaceWithHostInfo" export { SessionList } from "./SessionList" export { Sessions } from "./Sessions" export { Session } from "./Session" diff --git a/sdk/lib/test/host.test.ts b/sdk/lib/test/host.test.ts index 82372f61b..a8ae317ed 100644 --- a/sdk/lib/test/host.test.ts +++ b/sdk/lib/test/host.test.ts @@ -8,6 +8,7 @@ describe("host", () => { const foo = sdk.host.multi(effects, "foo") const fooOrigin = await foo.bindPort(80, { protocol: "http" as const, + preferredExternalPort: 80, }) const fooInterface = new ServiceInterfaceBuilder({ effects, diff --git a/sdk/lib/test/startosTypeValidation.test.ts b/sdk/lib/test/startosTypeValidation.test.ts index 4f6d50f53..79ec62106 100644 --- a/sdk/lib/test/startosTypeValidation.test.ts +++ b/sdk/lib/test/startosTypeValidation.test.ts @@ -25,7 +25,6 @@ import { ListServiceInterfacesParams } from ".././osBindings" import { RemoveAddressParams } from ".././osBindings" import { ExportActionParams } from ".././osBindings" import { RemoveActionParams } from ".././osBindings" -import { ReverseProxyParams } from ".././osBindings" import { MountParams } from ".././osBindings" function typeEquality(_a: ExpectedType) {} describe("startosTypeValidation ", () => { @@ -66,7 +65,6 @@ describe("startosTypeValidation ", () => { removeAddress: {} as RemoveAddressParams, exportAction: {} as ExportActionParams, removeAction: {} as RemoveActionParams, - reverseProxy: {} as ReverseProxyParams, mount: {} as MountParams, checkDependencies: {} as CheckDependenciesParam, getDependencies: undefined, diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts index 7db42e5a9..2da92cd56 100644 --- a/sdk/lib/types.ts +++ b/sdk/lib/types.ts @@ -5,6 +5,12 @@ import { SetHealth, HealthCheckResult, SetMainStatus, + ServiceInterface, + Host, + ExportServiceInterfaceParams, + GetPrimaryUrlParams, + LanInfo, + BindParams, } from "./osBindings" import { MainEffects, ServiceInterfaceType, Signals } from "./StartSdk" @@ -12,7 +18,7 @@ import { InputSpec } from "./config/configTypes" import { DependenciesReceipt } from "./config/setupConfig" import { BindOptions, Scheme } from "./interfaces/Host" import { Daemons } from "./mainFn/Daemons" -import { PathBuilder, StorePath } from "./store/PathBuilder" +import { StorePath } from "./store/PathBuilder" import { ExposedStorePaths } from "./store/setupExposeStore" import { UrlString } from "./util/getServiceInterface" export * from "./osBindings" @@ -184,14 +190,6 @@ export declare const hostName: unique symbol // asdflkjadsf.onion | 1.2.3.4 export type Hostname = string & { [hostName]: never } -/** ${scheme}://${username}@${host}:${externalPort}${suffix} */ -export type AddressInfo = { - username: string | null - hostId: string - bindOptions: BindOptions - suffix: string -} - export type HostnameInfoIp = { kind: "ip" networkInterfaceId: string @@ -219,44 +217,9 @@ export type HostnameInfoOnion = { export type HostnameInfo = HostnameInfoIp | HostnameInfoOnion -export type SingleHost = { - id: string - kind: "single" | "static" - hostname: HostnameInfo | null -} - -export type MultiHost = { - id: string - kind: "multi" - hostnames: HostnameInfo[] -} - -export type HostInfo = SingleHost | MultiHost - export type ServiceInterfaceId = string -export type ServiceInterface = { - id: ServiceInterfaceId - /** The title of this field to be displayed */ - name: string - /** Human readable description, used as tooltip usually */ - description: string - /** Whether or not one address must be the primary address */ - hasPrimary: boolean - /** Disabled interfaces do not serve, but they retain their metadata and addresses */ - disabled: boolean - /** Whether or not to mask the URIs for this interface. Useful if the URIs contain sensitive information, such as a password, macaroon, or API key */ - masked: boolean - /** URI Information */ - addressInfo: AddressInfo - /** The network interface could be several types, something like ui, p2p, or network */ - type: ServiceInterfaceType -} - -export type ServiceInterfaceWithHostInfo = ServiceInterface & { - hostInfo: HostInfo -} - +export { ServiceInterface } export type ExposeServicePaths = { /** The path to the value in the Store. [JsonPath](https://jsonpath.com/) */ paths: ExposedStorePaths @@ -326,13 +289,7 @@ export type Effects = { /** Removes all network bindings */ clearBindings(): Promise /** Creates a host connected to the specified port with the provided options */ - bind( - options: { - kind: "static" | "single" | "multi" - id: string - internalPort: number - } & BindOptions, - ): Promise + bind(options: BindParams): Promise /** Retrieves the current hostname(s) associated with a host id */ // getHostInfo(options: { // kind: "static" | "single" @@ -341,11 +298,10 @@ export type Effects = { // callback: () => void // }): Promise getHostInfo(options: { - kind: "multi" | null - serviceInterfaceId: string + hostId: string packageId: string | null callback: () => void - }): Promise + }): Promise // /** // * Run rsync between two volumes. This is used to backup data between volumes. @@ -395,14 +351,14 @@ export type Effects = { getServicePortForward(options: { internalPort: number packageId: string | null - }): Promise + }): Promise /** Removes all network interfaces */ clearServiceInterfaces(): Promise /** When we want to create a link in the front end interfaces, and example is * exposing a url to view a web service */ - exportServiceInterface(options: ServiceInterface): Promise + exportServiceInterface(options: ExportServiceInterfaceParams): Promise exposeForDependents(options: { paths: string[] }): Promise @@ -422,11 +378,7 @@ export type Effects = { * The user sets the primary url for a interface * @param options */ - getPrimaryUrl(options: { - packageId: PackageId | null - serviceInterfaceId: ServiceInterfaceId - callback: () => void - }): Promise + getPrimaryUrl(options: GetPrimaryUrlParams): Promise /** * There are times that we want to see the addresses that where exported @@ -437,7 +389,7 @@ export type Effects = { listServiceInterfaces(options: { packageId: PackageId | null callback: () => void - }): Promise + }): Promise> /** *Remove an address that was exported. Used problably during main or during setConfig. @@ -501,25 +453,6 @@ export type Effects = { /** Exists could be useful during the runtime to know if some service is running, option dep */ running(options: { packageId: PackageId }): Promise - /** Instead of creating proxies with nginx, we have a utility to create and maintain a proxy in the lifetime of this running. */ - reverseProxy(options: { - bind: { - /** Optional, default is 0.0.0.0 */ - ip: string | null - port: number - ssl: boolean - } - dst: { - /** Optional: default is 127.0.0.1 */ - ip: string | null // optional, default 127.0.0.1 - port: number - ssl: boolean - } - http: { - // optional, will do TCP layer proxy only if not present - headers: Record | null - } | null - }): Promise<{ stop(): Promise }> restart(): void shutdown(): void diff --git a/sdk/lib/util/Hostname.ts b/sdk/lib/util/Hostname.ts new file mode 100644 index 000000000..ee68abe4f --- /dev/null +++ b/sdk/lib/util/Hostname.ts @@ -0,0 +1,25 @@ +import { HostnameInfo } from "../types" + +export function hostnameInfoToAddress(hostInfo: HostnameInfo): string { + if (hostInfo.kind === "onion") { + return `${hostInfo.hostname.value}` + } + if (hostInfo.kind !== "ip") { + throw Error("Expecting that the kind is ip.") + } + const hostname = hostInfo.hostname + if (hostname.kind === "domain") { + return `${hostname.subdomain ? `${hostname.subdomain}.` : ""}${hostname.domain}` + } + const port = hostname.sslPort || hostname.port + const portString = port ? `:${port}` : "" + if ("ipv4" === hostname.kind || "ipv6" === hostname.kind) { + return `${hostname.value}${portString}` + } + if ("local" === hostname.kind) { + return `${hostname.value}${portString}` + } + throw Error( + "Expecting to have a valid hostname kind." + JSON.stringify(hostname), + ) +} diff --git a/sdk/lib/util/getServiceInterface.ts b/sdk/lib/util/getServiceInterface.ts index 3b7af1c41..737cbbc16 100644 --- a/sdk/lib/util/getServiceInterface.ts +++ b/sdk/lib/util/getServiceInterface.ts @@ -1,10 +1,15 @@ import { ServiceInterfaceType } from "../StartSdk" +import { knownProtocols } from "../interfaces/Host" import { AddressInfo, Effects, - HostInfo, + Host, + HostAddress, Hostname, HostnameInfo, + HostnameInfoIp, + HostnameInfoOnion, + IpInfo, } from "../types" export type UrlString = string @@ -20,13 +25,13 @@ export const getHostname = (url: string): Hostname | null => { } export type Filled = { - hostnames: Hostname[] - onionHostnames: Hostname[] - localHostnames: Hostname[] - ipHostnames: Hostname[] - ipv4Hostnames: Hostname[] - ipv6Hostnames: Hostname[] - nonIpHostnames: Hostname[] + hostnames: HostnameInfo[] + onionHostnames: HostnameInfo[] + localHostnames: HostnameInfo[] + ipHostnames: HostnameInfo[] + ipv4Hostnames: HostnameInfo[] + ipv6Hostnames: HostnameInfo[] + nonIpHostnames: HostnameInfo[] urls: UrlString[] onionUrls: UrlString[] @@ -50,7 +55,7 @@ export type ServiceInterfaceFilled = { /** Whether or not to mask the URIs for this interface. Useful if the URIs contain sensitive information, such as a password, macaroon, or API key */ masked: boolean /** Information about the host for this binding */ - hostInfo: HostInfo + host: Host /** URI information */ addressInfo: FilledAddressInfo /** Indicates if we are a ui/p2p/api for the kind of interface that this is representing */ @@ -69,110 +74,103 @@ const negate = (a: A) => !fn(a) const unique = (values: A[]) => Array.from(new Set(values)) -function stringifyHostname(info: HostnameInfo): Hostname { - let base: string - if ("kind" in info.hostname && info.hostname.kind === "domain") { - base = info.hostname.subdomain - ? `${info.hostname.subdomain}.${info.hostname.domain}` - : info.hostname.domain - } else { - base = info.hostname.value +export const addressHostToUrl = ( + { scheme, sslScheme, username, suffix }: AddressInfo, + host: HostnameInfo, +): UrlString[] => { + const res = [] + const fmt = (scheme: string | null, host: HostnameInfo, port: number) => { + const includePort = + scheme && + scheme in knownProtocols && + port === knownProtocols[scheme as keyof typeof knownProtocols].defaultPort + let hostname + if (host.kind === "onion") { + hostname = host.hostname.value + } else if (host.kind === "ip") { + if (host.hostname.kind === "domain") { + hostname = `${host.hostname.subdomain ? `${host.hostname.subdomain}.` : ""}${host.hostname.domain}` + } else { + hostname = host.hostname.value + } + } + return `${scheme ? `${scheme}://` : ""}${ + username ? `${username}@` : "" + }${hostname}${includePort ? `:${port}` : ""}${suffix}` } - if (info.hostname.port && info.hostname.sslPort) { - return `${base}:${info.hostname.port}` as Hostname - } else if (info.hostname.sslPort) { - return `${base}:${info.hostname.sslPort}` as Hostname - } else if (info.hostname.port) { - return `${base}:${info.hostname.port}` as Hostname + if (host.hostname.sslPort !== null) { + res.push(fmt(sslScheme, host, host.hostname.sslPort)) } - return base as Hostname -} -const addressHostToUrl = ( - { bindOptions, username, suffix }: AddressInfo, - host: Hostname, -): UrlString => { - const scheme = host.endsWith(".onion") - ? bindOptions.scheme - : bindOptions.addSsl - ? bindOptions.addSsl.scheme - : bindOptions.scheme // TODO: encode whether hostname transport is "secure"? - return `${scheme ? `${scheme}//` : ""}${ - username ? `${username}@` : "" - }${host}${suffix}` + if (host.hostname.port !== null) { + res.push(fmt(scheme, host, host.hostname.port)) + } + + return res } + export const filledAddress = ( - hostInfo: HostInfo, + host: Host, addressInfo: AddressInfo, ): FilledAddressInfo => { const toUrl = addressHostToUrl.bind(null, addressInfo) - const hostnameInfo = - hostInfo.kind == "multi" - ? hostInfo.hostnames - : hostInfo.hostname - ? [hostInfo.hostname] - : [] + const hostnames = host.hostnameInfo[addressInfo.internalPort] + return { ...addressInfo, - hostnames: hostnameInfo.flatMap((h) => stringifyHostname(h)), + hostnames, get onionHostnames() { - return hostnameInfo - .filter((h) => h.kind === "onion") - .map((h) => stringifyHostname(h)) + return hostnames.filter((h) => h.kind === "onion") }, get localHostnames() { - return hostnameInfo - .filter((h) => h.kind === "ip" && h.hostname.kind === "local") - .map((h) => stringifyHostname(h)) + return hostnames.filter( + (h) => h.kind === "ip" && h.hostname.kind === "local", + ) }, get ipHostnames() { - return hostnameInfo - .filter( - (h) => - h.kind === "ip" && - (h.hostname.kind === "ipv4" || h.hostname.kind === "ipv6"), - ) - .map((h) => stringifyHostname(h)) + return hostnames.filter( + (h) => + h.kind === "ip" && + (h.hostname.kind === "ipv4" || h.hostname.kind === "ipv6"), + ) }, get ipv4Hostnames() { - return hostnameInfo - .filter((h) => h.kind === "ip" && h.hostname.kind === "ipv4") - .map((h) => stringifyHostname(h)) + return hostnames.filter( + (h) => h.kind === "ip" && h.hostname.kind === "ipv4", + ) }, get ipv6Hostnames() { - return hostnameInfo - .filter((h) => h.kind === "ip" && h.hostname.kind === "ipv6") - .map((h) => stringifyHostname(h)) + return hostnames.filter( + (h) => h.kind === "ip" && h.hostname.kind === "ipv6", + ) }, get nonIpHostnames() { - return hostnameInfo - .filter( - (h) => - h.kind === "ip" && - h.hostname.kind !== "ipv4" && - h.hostname.kind !== "ipv6", - ) - .map((h) => stringifyHostname(h)) + return hostnames.filter( + (h) => + h.kind === "ip" && + h.hostname.kind !== "ipv4" && + h.hostname.kind !== "ipv6", + ) }, get urls() { - return this.hostnames.map(toUrl) + return this.hostnames.flatMap(toUrl) }, get onionUrls() { - return this.onionHostnames.map(toUrl) + return this.onionHostnames.flatMap(toUrl) }, get localUrls() { - return this.localHostnames.map(toUrl) + return this.localHostnames.flatMap(toUrl) }, get ipUrls() { - return this.ipHostnames.map(toUrl) + return this.ipHostnames.flatMap(toUrl) }, get ipv4Urls() { - return this.ipv4Hostnames.map(toUrl) + return this.ipv4Hostnames.flatMap(toUrl) }, get ipv6Urls() { - return this.ipv6Hostnames.map(toUrl) + return this.ipv6Hostnames.flatMap(toUrl) }, get nonIpUrls() { - return this.nonIpHostnames.map(toUrl) + return this.nonIpHostnames.flatMap(toUrl) }, } } @@ -193,23 +191,25 @@ const makeInterfaceFilled = async ({ packageId, callback, }) - const hostInfo = await effects.getHostInfo({ - packageId, - kind: null, - serviceInterfaceId: serviceInterfaceValue.id, - callback, - }) - const primaryUrl = await effects.getPrimaryUrl({ - serviceInterfaceId: id, + const hostId = serviceInterfaceValue.addressInfo.hostId + const host = await effects.getHostInfo({ packageId, + hostId, callback, }) + const primaryUrl = await effects + .getPrimaryUrl({ + serviceInterfaceId: id, + packageId, + callback, + }) + .catch((e) => null) const interfaceFilled: ServiceInterfaceFilled = { ...serviceInterfaceValue, primaryUrl: primaryUrl, - hostInfo, - addressInfo: filledAddress(hostInfo, serviceInterfaceValue.addressInfo), + host, + addressInfo: filledAddress(host, serviceInterfaceValue.addressInfo), get primaryHostname() { if (primaryUrl == null) return null return getHostname(primaryUrl) diff --git a/sdk/lib/util/getServiceInterfaces.ts b/sdk/lib/util/getServiceInterfaces.ts index a7106568b..c4cdc6b59 100644 --- a/sdk/lib/util/getServiceInterfaces.ts +++ b/sdk/lib/util/getServiceInterfaces.ts @@ -18,41 +18,27 @@ const makeManyInterfaceFilled = async ({ packageId, callback, }) - const hostIdsRecord = Object.fromEntries( - await Promise.all( - Array.from(new Set(serviceInterfaceValues.map((x) => x.id))).map( - async (id) => - [ - id, - await effects.getHostInfo({ - kind: null, - packageId, - serviceInterfaceId: id, - callback, - }), - ] as const, - ), - ), - ) const serviceInterfacesFilled: ServiceInterfaceFilled[] = await Promise.all( - serviceInterfaceValues.map(async (serviceInterfaceValue) => { - const hostInfo = await effects.getHostInfo({ - kind: null, - packageId, - serviceInterfaceId: serviceInterfaceValue.id, - callback, - }) - const primaryUrl = await effects.getPrimaryUrl({ - serviceInterfaceId: serviceInterfaceValue.id, + Object.values(serviceInterfaceValues).map(async (serviceInterfaceValue) => { + const hostId = serviceInterfaceValue.addressInfo.hostId + const host = await effects.getHostInfo({ packageId, + hostId, callback, }) + const primaryUrl = await effects + .getPrimaryUrl({ + serviceInterfaceId: serviceInterfaceValue.id, + packageId, + callback, + }) + .catch(() => null) return { ...serviceInterfaceValue, primaryUrl: primaryUrl, - hostInfo, - addressInfo: filledAddress(hostInfo, serviceInterfaceValue.addressInfo), + host, + addressInfo: filledAddress(host, serviceInterfaceValue.addressInfo), get primaryHostname() { if (primaryUrl == null) return null return getHostname(primaryUrl) diff --git a/sdk/lib/util/index.ts b/sdk/lib/util/index.ts index bd144f35a..9b19aeb38 100644 --- a/sdk/lib/util/index.ts +++ b/sdk/lib/util/index.ts @@ -10,6 +10,8 @@ import "./once" export { GetServiceInterface, getServiceInterface } from "./getServiceInterface" export { getServiceInterfaces } from "./getServiceInterfaces" +export { addressHostToUrl } from "./getServiceInterface" +export { hostnameInfoToAddress } from "./Hostname" // prettier-ignore export type FlattenIntersection = T extends ArrayLike ? T : diff --git a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts index 8baa0bfde..f3fe7a0cc 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts @@ -8,6 +8,7 @@ import { PatchDB } from 'patch-db-client' import { QRComponent } from 'src/app/components/qr/qr.component' import { map } from 'rxjs' import { T } from '@start9labs/start-sdk' +import { addressHostToUrl } from '@start9labs/start-sdk/cjs/lib/util/getServiceInterface' type MappedInterface = T.ServiceInterface & { addresses: MappedAddress[] @@ -33,10 +34,14 @@ export class AppInterfacesPage { .sort(iface => iface.name.toLowerCase() > iface.name.toLowerCase() ? -1 : 1, ) - .map(iface => ({ - ...iface, - addresses: getAddresses(iface), - })) + .map(iface => { + // TODO @Matt + const host = {} as any + return { + ...iface, + addresses: getAddresses(iface, host), + } + }) return { ui: sorted.filter(val => val.type === 'ui'), @@ -99,66 +104,40 @@ export class AppInterfacesItemComponent { } function getAddresses( - serviceInterface: T.ServiceInterfaceWithHostInfo, + serviceInterface: T.ServiceInterface, + host: T.Host, ): MappedAddress[] { - const host = serviceInterface.hostInfo const addressInfo = serviceInterface.addressInfo const username = addressInfo.username ? addressInfo.username + '@' : '' const suffix = addressInfo.suffix || '' - const hostnames = host.kind === 'multi' ? host.hostnames : [] // TODO: non-multi + const hostnames = + host.kind === 'multi' ? host.hostnameInfo[addressInfo.internalPort] : [] // TODO: non-multi /* host.hostname ? [host.hostname] : [] */ - const addresses: MappedAddress[] = [] - - hostnames.forEach(h => { + return hostnames.flatMap(h => { let name = '' - let hostname = '' if (h.kind === 'onion') { name = 'Tor' - hostname = h.hostname.value } else { const hostnameKind = h.hostname.kind if (hostnameKind === 'domain') { name = 'Domain' - hostname = `${h.hostname.subdomain}.${h.hostname.domain}` } else { name = hostnameKind === 'local' ? 'Local' : `${h.networkInterfaceId} (${hostnameKind})` - hostname = h.hostname.value } } - if (h.hostname.sslPort) { - const port = h.hostname.sslPort === 443 ? '' : `:${h.hostname.sslPort}` - const scheme = addressInfo.bindOptions.addSsl?.scheme - ? `${addressInfo.bindOptions.addSsl.scheme}://` - : '' - - addresses.push({ - name: name === 'Tor' ? 'Tor (HTTPS)' : name, - url: `${scheme}${username}${hostname}${port}${suffix}`, - }) - } - - if (h.hostname.port) { - const port = h.hostname.port === 80 ? '' : `:${h.hostname.port}` - const scheme = addressInfo.bindOptions.scheme - ? `${addressInfo.bindOptions.scheme}://` - : '' - - addresses.push({ - name: name === 'Tor' ? 'Tor (HTTP)' : name, - url: `${scheme}${username}${hostname}${port}${suffix}`, - }) - } + return addressHostToUrl(addressInfo, h).map(url => ({ + name, + url, + })) }) - - return addresses } diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index 2b0b96d23..11d56ff15 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -1422,66 +1422,11 @@ export module Mock { addressInfo: { username: null, hostId: 'abcdefg', - bindOptions: { - scheme: 'http', - preferredExternalPort: 80, - addSsl: { - // addXForwardedHeaders: false, - preferredExternalPort: 443, - scheme: 'https', - alpn: { specified: ['http/1.1', 'h2'] }, - }, - secure: null, - }, + internalPort: 80, + scheme: 'http', + sslScheme: 'https', suffix: '', }, - hostInfo: { - id: 'abcdefg', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-ui-address.onion', - port: 80, - sslPort: 443, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: null, - sslPort: 1234, - }, - }, - ], - }, }, rpc: { id: 'rpc', @@ -1495,66 +1440,11 @@ export module Mock { addressInfo: { username: null, hostId: 'bcdefgh', - bindOptions: { - scheme: 'http', - preferredExternalPort: 80, - addSsl: { - // addXForwardedHeaders: false, - preferredExternalPort: 443, - scheme: 'https', - alpn: { specified: ['http/1.1'] }, - }, - secure: null, - }, + internalPort: 8332, + scheme: 'http', + sslScheme: 'https', suffix: '', }, - hostInfo: { - id: 'bcdefgh', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 2345, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-rpc-address.onion', - port: 80, - sslPort: 443, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: null, - sslPort: 2345, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: null, - sslPort: 2345, - }, - }, - ], - }, }, p2p: { id: 'p2p', @@ -1568,63 +1458,11 @@ export module Mock { addressInfo: { username: null, hostId: 'cdefghi', - bindOptions: { - scheme: 'bitcoin', - preferredExternalPort: 8333, - addSsl: null, - secure: { - ssl: false, - }, - }, + internalPort: 8333, + scheme: 'bitcoin', + sslScheme: null, suffix: '', }, - hostInfo: { - id: 'cdefghi', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 3456, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-p2p-address.onion', - port: 8333, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 3456, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 3456, - sslPort: null, - }, - }, - ], - }, }, }, currentDependencies: {}, @@ -1660,101 +1498,11 @@ export module Mock { addressInfo: { username: null, hostId: 'hijklmnop', - bindOptions: { - scheme: 'http', - preferredExternalPort: 80, - addSsl: { - // addXForwardedHeaders: false, - preferredExternalPort: 443, - scheme: 'https', - alpn: { specified: ['http/1.1', 'h2'] }, - }, - secure: { - ssl: true, - }, - }, + internalPort: 80, + scheme: 'http', + sslScheme: 'https', suffix: '', }, - hostInfo: { - id: 'hijklmnop', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 4567, - }, - }, - { - kind: 'onion', - hostname: { - value: 'proxy-ui-address.onion', - port: 80, - sslPort: 443, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: null, - sslPort: 4567, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: null, - sslPort: 4567, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'wlan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 4567, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'wlan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.7', - port: null, - sslPort: 4567, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'wlan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: null, - sslPort: 4567, - }, - }, - ], - }, }, }, currentDependencies: { @@ -1801,63 +1549,11 @@ export module Mock { addressInfo: { username: null, hostId: 'qrstuv', - bindOptions: { - scheme: 'grpc', - preferredExternalPort: 10009, - addSsl: null, - secure: { - ssl: true, - }, - }, + internalPort: 10009, + scheme: null, + sslScheme: 'grpc', suffix: '', }, - hostInfo: { - id: 'qrstuv', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'lnd-grpc-address.onion', - port: 10009, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 5678, - sslPort: null, - }, - }, - ], - }, }, lndconnect: { id: 'lndconnect', @@ -1871,63 +1567,11 @@ export module Mock { addressInfo: { username: null, hostId: 'qrstuv', - bindOptions: { - scheme: 'lndconnect', - preferredExternalPort: 10009, - addSsl: null, - secure: { - ssl: true, - }, - }, + internalPort: 10009, + scheme: null, + sslScheme: 'lndconnect', suffix: 'cert=askjdfbjadnaskjnd&macaroon=ksjbdfnhjasbndjksand', }, - hostInfo: { - id: 'qrstuv', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'lnd-grpc-address.onion', - port: 10009, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 5678, - sslPort: null, - }, - }, - ], - }, }, p2p: { id: 'p2p', @@ -1941,63 +1585,11 @@ export module Mock { addressInfo: { username: null, hostId: 'rstuvw', - bindOptions: { - scheme: null, - preferredExternalPort: 9735, - addSsl: null, - secure: { - ssl: true, - }, - }, + internalPort: 9735, + scheme: 'lightning', + sslScheme: null, suffix: '', }, - hostInfo: { - id: 'rstuvw', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 6789, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'lnd-p2p-address.onion', - port: 9735, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 6789, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 6789, - sslPort: null, - }, - }, - ], - }, }, }, currentDependencies: { diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index a4ed61278..ee96f9fe2 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -141,66 +141,11 @@ export const mockPatchData: DataModel = { addressInfo: { username: null, hostId: 'abcdefg', - bindOptions: { - scheme: 'http', - preferredExternalPort: 80, - addSsl: { - // addXForwardedHeaders: false, - preferredExternalPort: 443, - scheme: 'https', - alpn: { specified: ['http/1.1', 'h2'] }, - }, - secure: null, - }, + internalPort: 80, + scheme: 'http', + sslScheme: 'https', suffix: '', }, - hostInfo: { - id: 'abcdefg', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-ui-address.onion', - port: 80, - sslPort: 443, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: null, - sslPort: 1234, - }, - }, - ], - }, }, rpc: { id: 'rpc', @@ -214,66 +159,11 @@ export const mockPatchData: DataModel = { addressInfo: { username: null, hostId: 'bcdefgh', - bindOptions: { - scheme: 'http', - preferredExternalPort: 80, - addSsl: { - // addXForwardedHeaders: false, - preferredExternalPort: 443, - scheme: 'https', - alpn: { specified: ['http/1.1'] }, - }, - secure: null, - }, + internalPort: 8332, + scheme: 'http', + sslScheme: 'https', suffix: '', }, - hostInfo: { - id: 'bcdefgh', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 2345, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-rpc-address.onion', - port: 80, - sslPort: 443, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: null, - sslPort: 2345, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: null, - sslPort: 2345, - }, - }, - ], - }, }, p2p: { id: 'p2p', @@ -287,63 +177,11 @@ export const mockPatchData: DataModel = { addressInfo: { username: null, hostId: 'cdefghi', - bindOptions: { - scheme: 'bitcoin', - preferredExternalPort: 8333, - addSsl: null, - secure: { - ssl: false, - }, - }, + internalPort: 8333, + scheme: 'bitcoin', + sslScheme: null, suffix: '', }, - hostInfo: { - id: 'cdefghi', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 3456, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-p2p-address.onion', - port: 8333, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 3456, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 3456, - sslPort: null, - }, - }, - ], - }, }, }, currentDependencies: {}, @@ -382,63 +220,11 @@ export const mockPatchData: DataModel = { addressInfo: { username: null, hostId: 'qrstuv', - bindOptions: { - scheme: 'grpc', - preferredExternalPort: 10009, - addSsl: null, - secure: { - ssl: true, - }, - }, + internalPort: 10009, + scheme: null, + sslScheme: 'grpc', suffix: '', }, - hostInfo: { - id: 'qrstuv', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'lnd-grpc-address.onion', - port: 10009, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 5678, - sslPort: null, - }, - }, - ], - }, }, lndconnect: { id: 'lndconnect', @@ -452,63 +238,11 @@ export const mockPatchData: DataModel = { addressInfo: { username: null, hostId: 'qrstuv', - bindOptions: { - scheme: 'lndconnect', - preferredExternalPort: 10009, - addSsl: null, - secure: { - ssl: true, - }, - }, + internalPort: 10009, + scheme: null, + sslScheme: 'lndconnect', suffix: 'cert=askjdfbjadnaskjnd&macaroon=ksjbdfnhjasbndjksand', }, - hostInfo: { - id: 'qrstuv', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'lnd-grpc-address.onion', - port: 10009, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 5678, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 5678, - sslPort: null, - }, - }, - ], - }, }, p2p: { id: 'p2p', @@ -522,61 +256,11 @@ export const mockPatchData: DataModel = { addressInfo: { username: null, hostId: 'rstuvw', - bindOptions: { - scheme: null, - preferredExternalPort: 9735, - addSsl: null, - secure: { ssl: true }, - }, + internalPort: 8333, + scheme: 'bitcoin', + sslScheme: null, suffix: '', }, - hostInfo: { - id: 'rstuvw', - kind: 'multi', - hostnames: [ - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: 6789, - sslPort: null, - }, - }, - { - kind: 'onion', - hostname: { - value: 'lnd-p2p-address.onion', - port: 9735, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.1.5', - port: 6789, - sslPort: null, - }, - }, - { - kind: 'ip', - networkInterfaceId: 'elan0', - public: false, - hostname: { - kind: 'ipv6', - value: '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - port: 6789, - sslPort: null, - }, - }, - ], - }, }, }, currentDependencies: { diff --git a/web/projects/ui/src/app/services/config.service.ts b/web/projects/ui/src/app/services/config.service.ts index 3eca4f48d..42116bdd8 100644 --- a/web/projects/ui/src/app/services/config.service.ts +++ b/web/projects/ui/src/app/services/config.service.ts @@ -57,12 +57,14 @@ export class ConfigService { } /** ${scheme}://${username}@${host}:${externalPort}${suffix} */ - launchableAddress(interfaces: PackageDataEntry['serviceInterfaces']): string { + launchableAddress( + interfaces: PackageDataEntry['serviceInterfaces'], + host: T.Host, + ): string { const ui = Object.values(interfaces).find(i => i.type === 'ui') if (!ui) return '' - const host = ui.hostInfo const addressInfo = ui.addressInfo const scheme = this.isHttps() ? 'https' : 'http' const username = addressInfo.username ? addressInfo.username + '@' : '' @@ -70,20 +72,25 @@ export class ConfigService { const url = new URL(`${scheme}://${username}placeholder${suffix}`) if (host.kind === 'multi') { - const onionHostname = host.hostnames.find(h => h.kind === 'onion') - ?.hostname as T.ExportedOnionHostname + const onionHostname = host.addresses.find(h => h.kind === 'onion') + ?.address as T.OnionHostname | undefined + + if (!onionHostname) + throw new Error('Expecting that there is an onion hostname') if (this.isTor() && onionHostname) { url.hostname = onionHostname.value - } else { - const ipHostname = host.hostnames.find(h => h.kind === 'ip') - ?.hostname as T.ExportedIpHostname - - if (!ipHostname) return '' - - url.hostname = this.hostname - url.port = String(ipHostname.sslPort || ipHostname.port) } + // TODO Handle single + // else { + // const ipHostname = host.addresses.find(h => h.kind === 'ip') + // ?.hostname as T.ExportedIpHostname + + // if (!ipHostname) return '' + + // url.hostname = this.hostname + // url.port = String(ipHostname.sslPort || ipHostname.port) + // } } else { throw new Error('unimplemented') // const hostname = {} as T.ExportedHostnameInfo // host.hostname diff --git a/web/projects/ui/src/app/services/ui-launcher.service.ts b/web/projects/ui/src/app/services/ui-launcher.service.ts index badbc7a3e..9c36036f3 100644 --- a/web/projects/ui/src/app/services/ui-launcher.service.ts +++ b/web/projects/ui/src/app/services/ui-launcher.service.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@angular/core' import { WINDOW } from '@ng-web-apis/common' import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { ConfigService } from './config.service' +import { T } from '@start9labs/start-sdk' @Injectable({ providedIn: 'root', @@ -13,8 +14,10 @@ export class UiLauncherService { ) {} launch(interfaces: PackageDataEntry['serviceInterfaces']): void { + // TODO @Matt + const host = {} as any this.windowRef.open( - this.config.launchableAddress(interfaces), + this.config.launchableAddress(interfaces, host), '_blank', 'noreferrer', )