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 <Blu-J@users.noreply.github.com>

* ??

* 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 <me@drbonez.dev>
Co-authored-by: Jade <Blu-J@users.noreply.github.com>
This commit is contained in:
Jade
2024-06-06 15:39:54 -06:00
committed by GitHub
parent 412c5d68cc
commit 2c12af5af8
67 changed files with 798 additions and 1516 deletions

View File

@@ -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<Manifest extends SDKManifest, Store> {
},
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,

View File

@@ -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<AddSslOptions>
}
| {
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<Key extends string>(
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 }) {

View File

@@ -6,7 +6,9 @@ import { ServiceInterfaceBuilder } from "./ServiceInterfaceBuilder"
export class Origin<T extends Host> {
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<T extends Host> {
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,
}

View File

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

View File

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

View File

@@ -2,7 +2,6 @@
import type { AlpnInfo } from "./AlpnInfo"
export type AddSslOptions = {
scheme: string | null
preferredExternalPort: number
alpn: AlpnInfo
alpn: AlpnInfo | null
}

View File

@@ -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
}

View File

@@ -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 }

View File

@@ -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

View File

@@ -8,7 +8,6 @@ export type BindParams = {
kind: HostKind
id: HostId
internalPort: number
scheme: string | null
preferredExternalPort: number
addSsl: AddSslOptions | null
secure: Security | null

View File

@@ -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<ExportedHostnameInfo>
}

View File

@@ -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<ExportedHostnameInfo>
}

View File

@@ -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 }

View File

@@ -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
}

View File

@@ -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"

View File

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

View File

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

View File

@@ -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
}

View File

@@ -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
}

View File

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

View File

@@ -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<HostAddress>
primary: HostAddress | null
/**
* COMPUTED: NetService::update
*/
hostnameInfo: { [key: number]: Array<HostnameInfo> }
}

View File

@@ -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 }

View File

@@ -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 }

View File

@@ -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 }
| {

View File

@@ -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
}

View File

@@ -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

View File

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

View File

@@ -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[]
}

View File

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

View File

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

View File

@@ -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 }

View File

@@ -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 } }

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -3,6 +3,7 @@ export { ActionId } from "./ActionId"
export { ActionMetadata } from "./ActionMetadata"
export { AddAdminParams } from "./AddAdminParams"
export { AddAssetParams } from "./AddAssetParams"
export { AddPackageParams } from "./AddPackageParams"
export { AddressInfo } from "./AddressInfo"
export { AddSslOptions } from "./AddSslOptions"
export { AddVersionParams } from "./AddVersionParams"
@@ -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"

View File

@@ -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,

View File

@@ -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<ExpectedType>(_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,

View File

@@ -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<Store = never> = {
/** 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<void>
/** Creates a host connected to the specified port with the provided options */
bind(
options: {
kind: "static" | "single" | "multi"
id: string
internalPort: number
} & BindOptions,
): Promise<void>
bind(options: BindParams): Promise<void>
/** 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<SingleHost>
getHostInfo(options: {
kind: "multi" | null
serviceInterfaceId: string
hostId: string
packageId: string | null
callback: () => void
}): Promise<MultiHost>
}): Promise<Host>
// /**
// * 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<number>
}): Promise<LanInfo>
/** Removes all network interfaces */
clearServiceInterfaces(): Promise<void>
/** 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<string>
exportServiceInterface(options: ExportServiceInterfaceParams): Promise<string>
exposeForDependents(options: { paths: string[] }): Promise<void>
@@ -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<UrlString | null>
getPrimaryUrl(options: GetPrimaryUrlParams): Promise<UrlString | null>
/**
* 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<ServiceInterface[]>
}): Promise<Record<ServiceInterfaceId, ServiceInterface>>
/**
*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<boolean>
/** 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<string, string> | null
} | null
}): Promise<{ stop(): Promise<void> }>
restart(): void
shutdown(): void

25
sdk/lib/util/Hostname.ts Normal file
View File

@@ -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),
)
}

View File

@@ -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 = <A>(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)

View File

@@ -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)

View File

@@ -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> =
T extends ArrayLike<any> ? T :