-
-
+
+
- Tor Address
- {{ tor }}
+ {{ address.name }}
+ {{ address.url }}
-
+
-
+
-
+
-
-
-
- Tor Address
- Service does not use a Tor Address
-
-
-
-
-
-
- LAN Address
- {{ lan }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- LAN Address
- N/A
-
-
diff --git a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html
index 16c6a5bd6..ea83aedb1 100644
--- a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html
+++ b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html
@@ -8,19 +8,29 @@
-
-
-
- User Interface
-
+
+
+ User Interfaces (UI)
+
-
-
- Machine Interfaces
-
+
+ Application Program Interfaces (API)
+
+
+
+
+ Peer-To-Peer Interfaces (P2P)
+
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 825d6536d..3678474e6 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
@@ -3,19 +3,21 @@ import { WINDOW } from '@ng-web-apis/common'
import { ActivatedRoute } from '@angular/router'
import { ModalController, ToastController } from '@ionic/angular'
import { copyToClipboard, getPkgId } from '@start9labs/shared'
-import { getUiInterfaceKey } from 'src/app/services/config.service'
-import {
- DataModel,
- InstalledPackageDataEntry,
- InterfaceDef,
-} from 'src/app/services/patch-db/data-model'
+import { DataModel } from 'src/app/services/patch-db/data-model'
import { PatchDB } from 'patch-db-client'
import { QRComponent } from 'src/app/components/qr/qr.component'
-import { getPackage } from '../../../util/get-package-data'
+import { map } from 'rxjs'
+import {
+ ServiceInterface,
+ ServiceInterfaceWithHostInfo,
+} from '@start9labs/start-sdk/mjs/lib/types'
-interface LocalInterface {
- def: InterfaceDef
- addresses: InstalledPackageDataEntry['interface-addresses'][string]
+type MappedInterface = ServiceInterface & {
+ addresses: MappedAddress[]
+}
+type MappedAddress = {
+ name: string
+ url: string
}
@Component({
@@ -24,60 +26,33 @@ interface LocalInterface {
styleUrls: ['./app-interfaces.page.scss'],
})
export class AppInterfacesPage {
- ui?: LocalInterface
- other: LocalInterface[] = []
readonly pkgId = getPkgId(this.route)
+ readonly serviceInterfaces$ = this.patch
+ .watch$('package-data', this.pkgId, 'installed', 'service-interfaces')
+ .pipe(
+ map(interfaces => {
+ const sorted = Object.values(interfaces)
+ .sort(iface =>
+ iface.name.toLowerCase() > iface.name.toLowerCase() ? -1 : 1,
+ )
+ .map(iface => ({
+ ...iface,
+ addresses: getAddresses(iface),
+ }))
+
+ return {
+ ui: sorted.filter(val => val.type === 'ui'),
+ api: sorted.filter(val => val.type === 'api'),
+ p2p: sorted.filter(val => val.type === 'p2p'),
+ }
+ }),
+ )
+
constructor(
private readonly route: ActivatedRoute,
private readonly patch: PatchDB,
) {}
-
- async ngOnInit() {
- const pkg = await getPackage(this.patch, this.pkgId)
- if (!pkg) return
-
- const interfaces = pkg.manifest.interfaces
- const uiKey = getUiInterfaceKey(interfaces)
-
- if (!pkg.installed) return
-
- const addressesMap = pkg.installed['interface-addresses']
-
- if (uiKey) {
- const uiAddresses = addressesMap[uiKey]
- this.ui = {
- def: interfaces[uiKey],
- addresses: {
- 'lan-address': uiAddresses['lan-address']
- ? 'https://' + uiAddresses['lan-address']
- : '',
- // leave http for services
- 'tor-address': uiAddresses['tor-address']
- ? 'http://' + uiAddresses['tor-address']
- : '',
- },
- }
- }
-
- this.other = Object.keys(interfaces)
- .filter(key => key !== uiKey)
- .map(key => {
- const addresses = addressesMap[key]
- return {
- def: interfaces[key],
- addresses: {
- 'lan-address': addresses['lan-address']
- ? 'https://' + addresses['lan-address']
- : '',
- 'tor-address': addresses['tor-address']
- ? // leave http for services
- 'http://' + addresses['tor-address']
- : '',
- },
- }
- })
- }
}
@Component({
@@ -86,8 +61,7 @@ export class AppInterfacesPage {
styleUrls: ['./app-interfaces.page.scss'],
})
export class AppInterfacesItemComponent {
- @Input()
- interface!: LocalInterface
+ @Input() iFace!: MappedInterface
constructor(
private readonly toastCtrl: ToastController,
@@ -126,3 +100,65 @@ export class AppInterfacesItemComponent {
await toast.present()
}
}
+
+function getAddresses(
+ serviceInterface: ServiceInterfaceWithHostInfo,
+): 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
+ : host.hostname
+ ? [host.hostname]
+ : []
+
+ return hostnames
+ .map(h => {
+ const addresses: MappedAddress[] = []
+
+ let name = ''
+ let hostname = ''
+
+ if (h.kind === 'onion') {
+ name = 'Tor'
+ hostname = h.hostname.value
+ } else {
+ name = h.hostname.kind
+ hostname =
+ h.hostname.kind === 'domain'
+ ? `${h.hostname.subdomain}.${h.hostname.domain}`
+ : 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,
+ 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,
+ url: `${scheme}${username}${hostname}${port}${suffix}`,
+ })
+ }
+
+ return addresses
+ })
+ .flat()
+}
diff --git a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html
index 037f7cc07..71988ba5b 100644
--- a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html
+++ b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html
@@ -17,16 +17,18 @@
[installProgress]="pkg.entry['install-progress']"
weight="bold"
size="small"
- [sigtermTimeout]="manifest.main['sigterm-timeout']"
+ [sigtermTimeout]="sigtermTimeout"
>
diff --git a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts
index 3302e182e..76559a8b1 100644
--- a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts
+++ b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts
@@ -1,5 +1,9 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
-import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
+import {
+ InstalledPackageDataEntry,
+ MainStatus,
+ PackageMainStatus,
+} from 'src/app/services/patch-db/data-model'
import { PkgInfo } from 'src/app/util/get-package-info'
import { UiLauncherService } from 'src/app/services/ui-launcher.service'
@@ -14,15 +18,26 @@ export class AppListPkgComponent {
constructor(private readonly launcherService: UiLauncherService) {}
- get status(): PackageMainStatus {
+ get pkgMainStatus(): MainStatus {
return (
- this.pkg.entry.installed?.status.main.status || PackageMainStatus.Stopped
+ this.pkg.entry.installed?.status.main || {
+ status: PackageMainStatus.Stopped,
+ }
)
}
- launchUi(e: Event): void {
+ get sigtermTimeout(): string | null {
+ return this.pkgMainStatus.status === PackageMainStatus.Stopping
+ ? this.pkgMainStatus.timeout
+ : null
+ }
+
+ launchUi(
+ e: Event,
+ interfaces: InstalledPackageDataEntry['service-interfaces'],
+ ): void {
e.stopPropagation()
e.preventDefault()
- this.launcherService.launch(this.pkg.entry)
+ this.launcherService.launch(interfaces)
}
}
diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html
index 6a8676ebc..e2379713b 100644
--- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html
+++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html
@@ -6,7 +6,7 @@
weight="600"
[installProgress]="pkg['install-progress']"
[rendering]="PR[status.primary]"
- [sigtermTimeout]="pkg.manifest.main['sigterm-timeout']"
+ [sigtermTimeout]="sigtermTimeout"
>
@@ -56,13 +56,11 @@
Launch UI
diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts
index f67a2b2fa..dd8832c55 100644
--- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts
+++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts
@@ -6,8 +6,9 @@ import {
PrimaryStatus,
} from 'src/app/services/pkg-status-rendering.service'
import {
- InterfaceDef,
+ InstalledPackageDataEntry,
PackageDataEntry,
+ PackageMainStatus,
PackageState,
Status,
} from 'src/app/services/patch-db/data-model'
@@ -45,8 +46,10 @@ export class AppShowStatusComponent {
private readonly connectionService: ConnectionService,
) {}
- get interfaces(): Record {
- return this.pkg.manifest.interfaces || {}
+ get interfaces():
+ | InstalledPackageDataEntry['service-interfaces']
+ | undefined {
+ return this.pkg.installed?.['service-interfaces']
}
get pkgStatus(): Status | null {
@@ -73,8 +76,14 @@ export class AppShowStatusComponent {
return this.status.primary === PrimaryStatus.Stopped
}
- launchUi(): void {
- this.launcherService.launch(this.pkg)
+ get sigtermTimeout(): string | null {
+ return this.pkgStatus?.main.status === PackageMainStatus.Stopping
+ ? this.pkgStatus.main.timeout
+ : null
+ }
+
+ launchUi(interfaces: InstalledPackageDataEntry['service-interfaces']): void {
+ this.launcherService.launch(interfaces)
}
async presentModalConfig(): Promise {
diff --git a/web/projects/ui/src/app/pages/login/login.page.html b/web/projects/ui/src/app/pages/login/login.page.html
index 99f6abbe8..7e27a16c3 100644
--- a/web/projects/ui/src/app/pages/login/login.page.html
+++ b/web/projects/ui/src/app/pages/login/login.page.html
@@ -6,30 +6,6 @@
-
-
-
-
- Http detected
-
- Tor is faster over https. Your Root CA must be trusted.
-
- View instructions
-
-
-
-
- Open Https
-
-
-
-
-
diff --git a/web/projects/ui/src/app/pages/login/login.page.ts b/web/projects/ui/src/app/pages/login/login.page.ts
index 15f7d588e..db0d1c2c4 100644
--- a/web/projects/ui/src/app/pages/login/login.page.ts
+++ b/web/projects/ui/src/app/pages/login/login.page.ts
@@ -5,7 +5,6 @@ import { AuthService } from 'src/app/services/auth.service'
import { Router } from '@angular/router'
import { ConfigService } from 'src/app/services/config.service'
import { DOCUMENT } from '@angular/common'
-import { WINDOW } from '@ng-web-apis/common'
@Component({
selector: 'login',
@@ -24,14 +23,8 @@ export class LoginPage {
private readonly api: ApiService,
public readonly config: ConfigService,
@Inject(DOCUMENT) public readonly document: Document,
- @Inject(WINDOW) private readonly windowRef: Window,
) {}
- launchHttps() {
- const host = this.config.getHost()
- this.windowRef.open(`https://${host}`, '_self')
- }
-
async submit() {
this.error = ''
diff --git a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html
index 760a013e6..f16066874 100644
--- a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html
+++ b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.html
@@ -40,27 +40,6 @@
-
-
-
- Http detected
-
- Tor is faster over https.
-
- Download and trust your server's Root CA
-
- , then switch to https.
-
-
-
- Open Https
-
-
-
-
diff --git a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts
index 2b64fcf1c..38087b354 100644
--- a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts
+++ b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts
@@ -41,8 +41,6 @@ export class ServerShowPage {
readonly showUpdate$ = this.eosService.showUpdate$
readonly showDiskRepair$ = this.ClientStorageService.showDiskRepair$
- readonly isTorHttp = this.config.isTorHttp()
-
constructor(
private readonly alertCtrl: AlertController,
private readonly modalCtrl: ModalController,
@@ -56,7 +54,6 @@ export class ServerShowPage {
private readonly ClientStorageService: ClientStorageService,
private readonly authService: AuthService,
private readonly toastCtrl: ToastController,
- private readonly config: ConfigService,
@Inject(WINDOW) private readonly windowRef: Window,
) {}
@@ -305,11 +302,6 @@ export class ServerShowPage {
await alert.present()
}
- async launchHttps() {
- const { 'tor-address': torAddress } = await getServerInfo(this.patch)
- this.windowRef.open(torAddress, '_self')
- }
-
addClick(title: string) {
switch (title) {
case 'Manage':
diff --git a/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts b/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts
index be1d5218d..668328820 100644
--- a/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts
+++ b/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts
@@ -1,6 +1,5 @@
import { Pipe, PipeTransform } from '@angular/core'
import {
- InterfaceDef,
PackageMainStatus,
PackageState,
} from 'src/app/services/patch-db/data-model'
@@ -12,11 +11,7 @@ import { ConfigService } from '../../services/config.service'
export class LaunchablePipe implements PipeTransform {
constructor(private configService: ConfigService) {}
- transform(
- state: PackageState,
- status: PackageMainStatus,
- interfaces: Record,
- ): boolean {
- return this.configService.isLaunchable(state, status, interfaces)
+ transform(state: PackageState, status: PackageMainStatus): boolean {
+ return this.configService.isLaunchable(state, status)
}
}
diff --git a/web/projects/ui/src/app/pipes/ui/ui.pipe.ts b/web/projects/ui/src/app/pipes/ui/ui.pipe.ts
index 9d46bfd86..03ec11df0 100644
--- a/web/projects/ui/src/app/pipes/ui/ui.pipe.ts
+++ b/web/projects/ui/src/app/pipes/ui/ui.pipe.ts
@@ -1,12 +1,14 @@
import { Pipe, PipeTransform } from '@angular/core'
-import { InterfaceDef } from '../../services/patch-db/data-model'
+import { InstalledPackageDataEntry } from '../../services/patch-db/data-model'
import { hasUi } from '../../services/config.service'
@Pipe({
name: 'hasUi',
})
export class UiPipe implements PipeTransform {
- transform(interfaces: Record): boolean {
- return hasUi(interfaces)
+ transform(
+ interfaces: InstalledPackageDataEntry['service-interfaces'],
+ ): boolean {
+ return interfaces ? hasUi(interfaces) : false
}
}
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 17460609a..ea83a4f0a 100644
--- a/web/projects/ui/src/app/services/api/api.fixures.ts
+++ b/web/projects/ui/src/app/services/api/api.fixures.ts
@@ -78,18 +78,6 @@ export module Mock {
start: 'Starting Bitcoin is good for your health.',
stop: null,
},
- main: {
- type: 'docker',
- image: '',
- system: true,
- entrypoint: '',
- args: [],
- mounts: {},
- 'io-format': DockerIoFormat.Yaml,
- inject: false,
- 'shm-size': '',
- 'sigterm-timeout': '1ms',
- },
'health-checks': {},
config: {
get: null,
@@ -97,40 +85,6 @@ export module Mock {
},
volumes: {},
'min-os-version': '0.2.12',
- interfaces: {
- ui: {
- name: 'Node Visualizer',
- description:
- 'Web application for viewing information about your node and the Bitcoin network.',
- ui: true,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {},
- protocols: [],
- },
- rpc: {
- name: 'RPC',
- description: 'Used by wallets to interact with your Bitcoin Core node.',
- ui: false,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {},
- protocols: [],
- },
- p2p: {
- name: 'P2P',
- description:
- 'Used by other Bitcoin nodes to communicate and interact with your node.',
- ui: false,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {},
- protocols: [],
- },
- },
backup: {
create: {
type: 'docker',
@@ -382,18 +336,6 @@ export module Mock {
start: 'Starting LND is good for your health.',
stop: null,
},
- main: {
- type: 'docker',
- image: '',
- system: true,
- entrypoint: '',
- args: [],
- mounts: {},
- 'io-format': DockerIoFormat.Yaml,
- inject: false,
- 'shm-size': '',
- 'sigterm-timeout': '10000µs',
- },
'health-checks': {},
config: {
get: null,
@@ -401,38 +343,6 @@ export module Mock {
},
volumes: {},
'min-os-version': '0.2.12',
- interfaces: {
- rpc: {
- name: 'RPC interface',
- description: 'Good for connecting to your node at a distance.',
- ui: true,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {
- '44': {
- ssl: true,
- mapping: 33,
- },
- },
- protocols: [],
- },
- grpc: {
- name: 'GRPC',
- description: 'Certain wallet use grpc.',
- ui: false,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {
- '66': {
- ssl: true,
- mapping: 55,
- },
- },
- protocols: [],
- },
- },
backup: {
create: {
type: 'docker',
@@ -535,39 +445,10 @@ export module Mock {
start: null,
stop: null,
},
- main: {
- type: 'docker',
- image: '',
- system: true,
- entrypoint: '',
- args: [''],
- mounts: {},
- 'io-format': DockerIoFormat.Yaml,
- inject: false,
- 'shm-size': '',
- 'sigterm-timeout': '1m',
- },
'health-checks': {},
config: { get: {} as any, set: {} as any },
volumes: {},
'min-os-version': '0.2.12',
- interfaces: {
- rpc: {
- name: 'RPC interface',
- description: 'Good for connecting to your node at a distance.',
- ui: false,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {
- 44: {
- ssl: true,
- mapping: 33,
- },
- },
- protocols: [],
- },
- },
backup: {
create: {
type: 'docker',
@@ -1887,18 +1768,219 @@ export module Mock {
},
'dependency-config-errors': {},
},
- 'interface-addresses': {
+ 'service-interfaces': {
ui: {
- 'tor-address': 'bitcoind-ui-address.onion',
- 'lan-address': 'bitcoind-ui-address.local',
+ id: 'ui',
+ hasPrimary: false,
+ disabled: false,
+ masked: false,
+ name: 'Web UI',
+ description:
+ 'A launchable web app for you to interact with your Bitcoin node',
+ type: 'ui',
+ addressInfo: {
+ username: null,
+ hostId: 'abcdefg',
+ bindOptions: {
+ scheme: 'http',
+ preferredExternalPort: 80,
+ addSsl: {
+ preferredExternalPort: 443,
+ scheme: 'https',
+ },
+ secure: false,
+ ssl: false,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'abcdefg',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'bitcoin-ui-address.onion',
+ port: 80,
+ sslPort: 443,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: null,
+ sslPort: 1234,
+ },
+ },
+ {
+ 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: {
- 'tor-address': 'bitcoind-rpc-address.onion',
- 'lan-address': 'bitcoind-rpc-address.local',
+ id: 'rpc',
+ hasPrimary: false,
+ disabled: false,
+ masked: false,
+ name: 'RPC',
+ description:
+ 'Used by dependent services and client wallets for connecting to your node',
+ type: 'api',
+ addressInfo: {
+ username: null,
+ hostId: 'bcdefgh',
+ bindOptions: {
+ scheme: 'http',
+ preferredExternalPort: 80,
+ addSsl: {
+ preferredExternalPort: 443,
+ scheme: 'https',
+ },
+ secure: false,
+ ssl: false,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'bcdefgh',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'bitcoin-rpc-address.onion',
+ port: 80,
+ sslPort: 443,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: null,
+ sslPort: 2345,
+ },
+ },
+ {
+ 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: {
- 'tor-address': 'bitcoind-p2p-address.onion',
- 'lan-address': 'bitcoind-p2p-address.local',
+ id: 'p2p',
+ hasPrimary: true,
+ disabled: false,
+ masked: false,
+ name: 'P2P',
+ description:
+ 'Used for connecting to other nodes on the Bitcoin network',
+ type: 'p2p',
+ addressInfo: {
+ username: null,
+ hostId: 'cdefghi',
+ bindOptions: {
+ scheme: 'bitcoin',
+ preferredExternalPort: 8333,
+ addSsl: null,
+ secure: true,
+ ssl: false,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'cdefghi',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'bitcoin-p2p-address.onion',
+ port: 8333,
+ sslPort: null,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: 3456,
+ 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,
+ },
+ },
+ ],
+ },
},
},
'system-pointers': [],
@@ -1934,10 +2016,110 @@ export module Mock {
'dependency-config-errors': {},
},
manifest: MockManifestBitcoinProxy,
- 'interface-addresses': {
- rpc: {
- 'tor-address': 'bitcoinproxy-rpc-address.onion',
- 'lan-address': 'bitcoinproxy-rpc-address.local',
+ 'service-interfaces': {
+ ui: {
+ id: 'ui',
+ hasPrimary: false,
+ disabled: false,
+ masked: false,
+ name: 'Web UI',
+ description: 'A launchable web app for Bitcoin Proxy',
+ type: 'ui',
+ addressInfo: {
+ username: null,
+ hostId: 'hijklmnop',
+ bindOptions: {
+ scheme: 'http',
+ preferredExternalPort: 80,
+ addSsl: {
+ preferredExternalPort: 443,
+ scheme: 'https',
+ },
+ secure: true,
+ ssl: true,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'hijklmnop',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'proxy-ui-address.onion',
+ port: 80,
+ sslPort: 443,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: null,
+ sslPort: 4567,
+ },
+ },
+ {
+ 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,
+ },
+ },
+ ],
+ },
},
},
'system-pointers': [],
@@ -1985,14 +2167,213 @@ export module Mock {
},
},
manifest: MockManifestLnd,
- 'interface-addresses': {
- rpc: {
- 'tor-address': 'lnd-rpc-address.onion',
- 'lan-address': 'lnd-rpc-address.local',
- },
+ 'service-interfaces': {
grpc: {
- 'tor-address': 'lnd-grpc-address.onion',
- 'lan-address': 'lnd-grpc-address.local',
+ id: 'grpc',
+ hasPrimary: false,
+ disabled: false,
+ masked: false,
+ name: 'GRPC',
+ description:
+ 'Used by dependent services and client wallets for connecting to your node',
+ type: 'api',
+ addressInfo: {
+ username: null,
+ hostId: 'qrstuv',
+ bindOptions: {
+ scheme: 'grpc',
+ preferredExternalPort: 10009,
+ addSsl: null,
+ secure: true,
+ ssl: true,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'qrstuv',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'lnd-grpc-address.onion',
+ port: 10009,
+ sslPort: null,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: 5678,
+ 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',
+ hasPrimary: false,
+ disabled: false,
+ masked: true,
+ name: 'LND Connect',
+ description:
+ 'Used by client wallets adhering to LND Connect protocol to connect to your node',
+ type: 'api',
+ addressInfo: {
+ username: null,
+ hostId: 'qrstuv',
+ bindOptions: {
+ scheme: 'lndconnect',
+ preferredExternalPort: 10009,
+ addSsl: null,
+ secure: true,
+ ssl: true,
+ },
+ suffix: 'cert=askjdfbjadnaskjnd&macaroon=ksjbdfnhjasbndjksand',
+ },
+ hostInfo: {
+ id: 'qrstuv',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'lnd-grpc-address.onion',
+ port: 10009,
+ sslPort: null,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: 5678,
+ 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',
+ hasPrimary: true,
+ disabled: false,
+ masked: false,
+ name: 'P2P',
+ description:
+ 'Used for connecting to other nodes on the Bitcoin network',
+ type: 'p2p',
+ addressInfo: {
+ username: null,
+ hostId: 'rstuvw',
+ bindOptions: {
+ scheme: null,
+ preferredExternalPort: 9735,
+ addSsl: null,
+ secure: true,
+ ssl: true,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'rstuvw',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'lnd-p2p-address.onion',
+ port: 9735,
+ sslPort: null,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: 6789,
+ 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,
+ },
+ },
+ ],
+ },
},
},
'system-pointers': [],
diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts
index c98a4bd87..a2e5324a1 100644
--- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts
+++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts
@@ -919,8 +919,10 @@ export class MockApiService extends ApiService {
const patch2 = [
{
op: PatchOp.REPLACE,
- path: path + '/status',
- value: PackageMainStatus.Stopped,
+ path: path,
+ value: {
+ status: PackageMainStatus.Stopped,
+ },
},
]
this.mockRevision(patch2)
@@ -929,13 +931,11 @@ export class MockApiService extends ApiService {
const patch = [
{
op: PatchOp.REPLACE,
- path: path + '/status',
- value: PackageMainStatus.Stopping,
- },
- {
- op: PatchOp.REPLACE,
- path: path + '/health',
- value: {},
+ path: path,
+ value: {
+ status: PackageMainStatus.Stopping,
+ timeout: '35s',
+ },
},
]
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 1dc7abd66..35942e92f 100644
--- a/web/projects/ui/src/app/services/api/mock-patch.ts
+++ b/web/projects/ui/src/app/services/api/mock-patch.ts
@@ -116,18 +116,6 @@ export const mockPatchData: DataModel = {
start: 'Starting Bitcoin is good for your health.',
stop: null,
},
- main: {
- type: 'docker',
- image: '',
- system: true,
- entrypoint: '',
- args: [],
- mounts: {},
- 'io-format': DockerIoFormat.Yaml,
- inject: false,
- 'shm-size': '',
- 'sigterm-timeout': '.49m',
- },
'health-checks': {
'chain-state': {
name: 'Chain State',
@@ -152,41 +140,6 @@ export const mockPatchData: DataModel = {
} as any,
volumes: {},
'min-os-version': '0.2.12',
- interfaces: {
- ui: {
- name: 'Node Visualizer',
- description:
- 'Web application for viewing information about your node and the Bitcoin network.',
- ui: true,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {},
- protocols: [],
- },
- rpc: {
- name: 'RPC',
- description:
- 'Used by wallets to interact with your Bitcoin Core node.',
- ui: false,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {},
- protocols: [],
- },
- p2p: {
- name: 'P2P',
- description:
- 'Used by other Bitcoin nodes to communicate and interact with your node.',
- ui: false,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {},
- protocols: [],
- },
- },
backup: {
create: {
type: 'docker',
@@ -441,18 +394,110 @@ export const mockPatchData: DataModel = {
},
'dependency-config-errors': {},
},
- 'interface-addresses': {
+ 'service-interfaces': {
ui: {
- 'tor-address': 'bitcoind-ui-address.onion',
- 'lan-address': 'bitcoind-ui-address.local',
- },
- rpc: {
- 'tor-address': 'bitcoind-rpc-address.onion',
- 'lan-address': 'bitcoind-rpc-address.local',
- },
- p2p: {
- 'tor-address': 'bitcoind-p2p-address.onion',
- 'lan-address': 'bitcoind-p2p-address.local',
+ id: 'ui',
+ hasPrimary: false,
+ disabled: false,
+ masked: false,
+ name: 'Web UI',
+ description: 'A launchable web app for Bitcoin Proxy',
+ type: 'ui',
+ addressInfo: {
+ username: null,
+ hostId: 'hijklmnop',
+ bindOptions: {
+ scheme: 'http',
+ preferredExternalPort: 80,
+ addSsl: {
+ preferredExternalPort: 443,
+ scheme: 'https',
+ },
+ secure: true,
+ ssl: true,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'hijklmnop',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'proxy-ui-address.onion',
+ port: 80,
+ sslPort: 443,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: null,
+ sslPort: 4567,
+ },
+ },
+ {
+ 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,
+ },
+ },
+ ],
+ },
},
},
'system-pointers': [],
@@ -506,18 +551,6 @@ export const mockPatchData: DataModel = {
start: 'Starting LND is good for your health.',
stop: null,
},
- main: {
- type: 'docker',
- image: '',
- system: true,
- entrypoint: '',
- args: [],
- mounts: {},
- 'io-format': DockerIoFormat.Yaml,
- inject: false,
- 'shm-size': '',
- 'sigterm-timeout': '0.5s',
- },
'health-checks': {},
config: {
get: null,
@@ -525,38 +558,6 @@ export const mockPatchData: DataModel = {
},
volumes: {},
'min-os-version': '0.2.12',
- interfaces: {
- rpc: {
- name: 'RPC interface',
- description: 'Good for connecting to your node at a distance.',
- ui: true,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {
- '44': {
- ssl: true,
- mapping: 33,
- },
- },
- protocols: [],
- },
- grpc: {
- name: 'GRPC',
- description: 'Certain wallet use grpc.',
- ui: false,
- 'tor-config': {
- 'port-mapping': {},
- },
- 'lan-config': {
- '66': {
- ssl: true,
- mapping: 55,
- },
- },
- protocols: [],
- },
- },
backup: {
create: {
type: 'docker',
@@ -642,14 +643,213 @@ export const mockPatchData: DataModel = {
'btc-rpc-proxy': 'This is a config unsatisfied error',
},
},
- 'interface-addresses': {
- rpc: {
- 'tor-address': 'lnd-rpc-address.onion',
- 'lan-address': 'lnd-rpc-address.local',
- },
+ 'service-interfaces': {
grpc: {
- 'tor-address': 'lnd-grpc-address.onion',
- 'lan-address': 'lnd-grpc-address.local',
+ id: 'grpc',
+ hasPrimary: false,
+ disabled: false,
+ masked: false,
+ name: 'GRPC',
+ description:
+ 'Used by dependent services and client wallets for connecting to your node',
+ type: 'api',
+ addressInfo: {
+ username: null,
+ hostId: 'qrstuv',
+ bindOptions: {
+ scheme: 'grpc',
+ preferredExternalPort: 10009,
+ addSsl: null,
+ secure: true,
+ ssl: true,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'qrstuv',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'lnd-grpc-address.onion',
+ port: 10009,
+ sslPort: null,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: 5678,
+ 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',
+ hasPrimary: false,
+ disabled: false,
+ masked: true,
+ name: 'LND Connect',
+ description:
+ 'Used by client wallets adhering to LND Connect protocol to connect to your node',
+ type: 'api',
+ addressInfo: {
+ username: null,
+ hostId: 'qrstuv',
+ bindOptions: {
+ scheme: 'lndconnect',
+ preferredExternalPort: 10009,
+ addSsl: null,
+ secure: true,
+ ssl: true,
+ },
+ suffix: 'cert=askjdfbjadnaskjnd&macaroon=ksjbdfnhjasbndjksand',
+ },
+ hostInfo: {
+ id: 'qrstuv',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'lnd-grpc-address.onion',
+ port: 10009,
+ sslPort: null,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: 5678,
+ 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',
+ hasPrimary: true,
+ disabled: false,
+ masked: false,
+ name: 'P2P',
+ description:
+ 'Used for connecting to other nodes on the Bitcoin network',
+ type: 'p2p',
+ addressInfo: {
+ username: null,
+ hostId: 'rstuvw',
+ bindOptions: {
+ scheme: null,
+ preferredExternalPort: 9735,
+ addSsl: null,
+ secure: true,
+ ssl: true,
+ },
+ suffix: '',
+ },
+ hostInfo: {
+ id: 'rstuvw',
+ kind: 'multi',
+ hostnames: [
+ {
+ kind: 'onion',
+ hostname: {
+ value: 'lnd-p2p-address.onion',
+ port: 9735,
+ sslPort: null,
+ },
+ },
+ {
+ kind: 'ip',
+ networkInterfaceId: 'elan0',
+ public: false,
+ hostname: {
+ kind: 'local',
+ value: 'adjective-noun.local',
+ port: 6789,
+ 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,
+ },
+ },
+ ],
+ },
},
},
'system-pointers': [],
diff --git a/web/projects/ui/src/app/services/config.service.ts b/web/projects/ui/src/app/services/config.service.ts
index f8fef3d60..50f228e95 100644
--- a/web/projects/ui/src/app/services/config.service.ts
+++ b/web/projects/ui/src/app/services/config.service.ts
@@ -2,8 +2,11 @@ import { DOCUMENT } from '@angular/common'
import { Inject, Injectable } from '@angular/core'
import { WorkspaceConfig } from '@start9labs/shared'
import {
- InterfaceDef,
- PackageDataEntry,
+ HostnameInfoIp,
+ HostnameInfoOnion,
+} from '@start9labs/start-sdk/mjs/lib/types'
+import {
+ InstalledPackageDataEntry,
PackageMainStatus,
PackageState,
} from 'src/app/services/patch-db/data-model'
@@ -45,10 +48,6 @@ export class ConfigService {
: this.hostname.endsWith('.local')
}
- isTorHttp(): boolean {
- return this.isTor() && !this.isHttps()
- }
-
isLanHttp(): boolean {
return !this.isTor() && !this.isLocalhost() && !this.isHttps()
}
@@ -57,24 +56,60 @@ export class ConfigService {
return window.isSecureContext || this.isTor()
}
- isLaunchable(
- state: PackageState,
- status: PackageMainStatus,
- interfaces: Record,
- ): boolean {
+ isLaunchable(state: PackageState, status: PackageMainStatus): boolean {
return (
- state === PackageState.Installed &&
- status === PackageMainStatus.Running &&
- hasUi(interfaces)
+ state === PackageState.Installed && status === PackageMainStatus.Running
)
}
- launchableURL(pkg: PackageDataEntry): string {
- if (!this.isTor() && hasLocalUi(pkg.manifest.interfaces)) {
- return `https://${lanUiAddress(pkg)}`
+ /** ${scheme}://${username}@${host}:${externalPort}${suffix} */
+ launchableAddress(
+ interfaces: InstalledPackageDataEntry['service-interfaces'],
+ ): 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 + '@' : ''
+ const suffix = addressInfo.suffix || ''
+ const url = new URL(`${scheme}://${username}placeholder${suffix}`)
+
+ if (host.kind === 'multi') {
+ const onionHostname = host.hostnames.find(
+ h => h.kind === 'onion',
+ ) as HostnameInfoOnion
+
+ if (this.isTor() && onionHostname) {
+ url.hostname = onionHostname.hostname.value
+ } else {
+ const ipHostname = host.hostnames.find(
+ h => h.kind === 'ip',
+ ) as HostnameInfoIp
+
+ if (!ipHostname) return ''
+
+ url.hostname = this.hostname
+ url.port = String(
+ ipHostname.hostname.sslPort || ipHostname.hostname.port,
+ )
+ }
} else {
- return `http://${torUiAddress(pkg)}`
+ const hostname = host.hostname
+
+ if (!hostname) return ''
+
+ if (this.isTor() && hostname.kind === 'onion') {
+ url.hostname = hostname.hostname.value
+ } else {
+ url.hostname = this.hostname
+ url.port = String(hostname.hostname.sslPort || hostname.hostname.port)
+ }
}
+
+ return url.href
}
getHost(): string {
@@ -92,54 +127,8 @@ export class ConfigService {
}
}
-export function hasTorUi(interfaces: Record): boolean {
- const int = getUiInterfaceValue(interfaces)
- return !!int?.['tor-config']
-}
-
-export function hasLocalUi(interfaces: Record): boolean {
- const int = getUiInterfaceValue(interfaces)
- return !!int?.['lan-config']
-}
-
-export function torUiAddress({
- manifest,
- installed,
-}: PackageDataEntry): string {
- const key = getUiInterfaceKey(manifest.interfaces)
- return installed ? installed['interface-addresses'][key]['tor-address'] : ''
-}
-
-export function lanUiAddress({
- manifest,
- installed,
-}: PackageDataEntry): string {
- const key = getUiInterfaceKey(manifest.interfaces)
- return installed ? installed['interface-addresses'][key]['lan-address'] : ''
-}
-
-export function hasUi(interfaces: Record): boolean {
- return hasTorUi(interfaces) || hasLocalUi(interfaces)
-}
-
-export function removeProtocol(str: string): string {
- if (str.startsWith('http://')) return str.slice(7)
- if (str.startsWith('https://')) return str.slice(8)
- return str
-}
-
-export function removePort(str: string): string {
- return str.split(':')[0]
-}
-
-export function getUiInterfaceKey(
- interfaces: Record,
-): string {
- return Object.keys(interfaces).find(key => interfaces[key].ui) || ''
-}
-
-export function getUiInterfaceValue(
- interfaces: Record,
-): InterfaceDef | null {
- return Object.values(interfaces).find(i => i.ui) || null
+export function hasUi(
+ interfaces: InstalledPackageDataEntry['service-interfaces'],
+): boolean {
+ return Object.values(interfaces).some(iface => iface.type === 'ui')
}
diff --git a/web/projects/ui/src/app/services/patch-db/data-model.ts b/web/projects/ui/src/app/services/patch-db/data-model.ts
index e4ea729d9..e00350528 100644
--- a/web/projects/ui/src/app/services/patch-db/data-model.ts
+++ b/web/projects/ui/src/app/services/patch-db/data-model.ts
@@ -2,6 +2,7 @@ import { ConfigSpec } from 'src/app/pkg-config/config-types'
import { Url } from '@start9labs/shared'
import { MarketplaceManifest } from '@start9labs/marketplace'
import { BasicInfo } from 'src/app/pages/developer-routes/developer-menu/form-info'
+import { ServiceInterfaceWithHostInfo } from '@start9labs/start-sdk/mjs/lib/types'
export interface DataModel {
'server-info': ServerInfo
@@ -139,9 +140,7 @@ export interface InstalledPackageDataEntry {
icon: Url
}
}
- 'interface-addresses': {
- [id: string]: { 'tor-address': string; 'lan-address': string }
- }
+ 'service-interfaces': Record
'marketplace-url': string | null
'developer-key': string
}
@@ -160,7 +159,6 @@ export interface Manifest extends MarketplaceManifest {
assets: string // path to assets folder
scripts: string // path to scripts folder
}
- main: ActionImpl
'health-checks': Record<
string,
ActionImpl & { name: string; 'success-message': string | null }
@@ -168,7 +166,6 @@ export interface Manifest extends MarketplaceManifest {
config: ConfigActions | null
volumes: Record
'min-os-version': string
- interfaces: Record
backup: BackupActions
migrations: Migrations | null
actions: Record
@@ -241,15 +238,6 @@ export enum VolumeType {
Backup = 'backup',
}
-export interface InterfaceDef {
- name: string
- description: string
- 'tor-config': TorConfig | null
- 'lan-config': LanConfig | null
- ui: boolean
- protocols: string[]
-}
-
export interface TorConfig {
'port-mapping': { [port: number]: number }
}
@@ -297,6 +285,7 @@ export interface MainStatusStopped {
export interface MainStatusStopping {
status: PackageMainStatus.Stopping
+ timeout: string
}
export interface MainStatusStarting {
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 55559bcd3..70666e264 100644
--- a/web/projects/ui/src/app/services/ui-launcher.service.ts
+++ b/web/projects/ui/src/app/services/ui-launcher.service.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@angular/core'
import { WINDOW } from '@ng-web-apis/common'
-import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
+import { InstalledPackageDataEntry } from 'src/app/services/patch-db/data-model'
import { ConfigService } from './config.service'
@Injectable({
@@ -12,7 +12,11 @@ export class UiLauncherService {
private readonly config: ConfigService,
) {}
- launch(pkg: PackageDataEntry): void {
- this.windowRef.open(this.config.launchableURL(pkg), '_blank', 'noreferrer')
+ launch(interfaces: InstalledPackageDataEntry['service-interfaces']): void {
+ this.windowRef.open(
+ this.config.launchableAddress(interfaces),
+ '_blank',
+ 'noreferrer',
+ )
}
}