mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
Merge branch 'integration/new-container-runtime' of github.com:Start9Labs/start-os into integration/new-container-runtime
This commit is contained in:
@@ -4,9 +4,7 @@ import {
|
||||
HostInfo,
|
||||
Hostname,
|
||||
HostnameInfo,
|
||||
ServiceInterface,
|
||||
} from "../types"
|
||||
import * as regexes from "./regexes"
|
||||
import { ServiceInterfaceType } from "./utils"
|
||||
|
||||
export type UrlString = string
|
||||
|
||||
1
web/package-lock.json
generated
1
web/package-lock.json
generated
@@ -1972,6 +1972,7 @@
|
||||
}
|
||||
},
|
||||
"../sdk/dist": {
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-rev0.lib0.rc8.beta7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -13,7 +13,7 @@ export type WorkspaceConfig = {
|
||||
community: 'https://community-registry.start9.com/'
|
||||
}
|
||||
mocks: {
|
||||
maskAs: 'tor' | 'local' | 'localhost'
|
||||
maskAs: 'tor' | 'local' | 'ip' | 'localhost'
|
||||
// enables local development in secure mode
|
||||
maskAsHttps: boolean
|
||||
skipStartupAlerts: boolean
|
||||
|
||||
@@ -22,12 +22,6 @@
|
||||
<ion-label class="label montserrat" routerLinkActive="label_selected">
|
||||
{{ page.title }}
|
||||
</ion-label>
|
||||
<ion-icon
|
||||
*ngIf="page.url === '/system' && (warning$ | async)"
|
||||
color="warning"
|
||||
size="small"
|
||||
name="warning"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="page.url === '/system' && (showEOSUpdate$ | async)"
|
||||
color="success"
|
||||
|
||||
@@ -114,11 +114,6 @@ export class MenuComponent {
|
||||
|
||||
readonly theme$ = inject(THEME)
|
||||
|
||||
readonly warning$ = merge(
|
||||
of(this.config.isTorHttp()),
|
||||
this.patch.watch$('server-info', 'ntp-synced').pipe(map(synced => !synced)),
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly eosService: EOSService,
|
||||
|
||||
@@ -8,13 +8,8 @@
|
||||
>
|
||||
{{ (connected$ | async) ? rendering.display : 'Unknown' }}
|
||||
|
||||
<span
|
||||
*ngIf="
|
||||
rendering.display === PR[PS.Stopping].display &&
|
||||
(sigtermTimeout | durationToSeconds) > 30
|
||||
"
|
||||
>
|
||||
this may take a while
|
||||
<span *ngIf="sigtermTimeout && (sigtermTimeout | durationToSeconds) > 30">
|
||||
. This may take a while
|
||||
</span>
|
||||
|
||||
<span *ngIf="installProgress">
|
||||
|
||||
@@ -1,72 +1,44 @@
|
||||
<ion-item *ngIf="interface">
|
||||
<ion-item *ngIf="iFace">
|
||||
<ion-icon
|
||||
slot="start"
|
||||
size="large"
|
||||
[name]="interface.def.ui ? 'desktop-outline' : 'terminal-outline'"
|
||||
[name]="
|
||||
iFace.type === 'ui'
|
||||
? 'desktop-outline'
|
||||
: iFace.type === 'api'
|
||||
? 'terminal-outline'
|
||||
: 'people-outline'
|
||||
"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ interface.def.name }}</h1>
|
||||
<h2>{{ interface.def.description }}</h2>
|
||||
<h1>{{ iFace.name }}</h1>
|
||||
<h2>{{ iFace.description }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<div *ngIf="interface" style="padding-left: 64px">
|
||||
<!-- has tor -->
|
||||
<ion-item *ngIf="interface.addresses['tor-address'] as tor">
|
||||
<div *ngIf="iFace" style="padding-left: 64px">
|
||||
<ion-item *ngFor="let address of iFace.addresses">
|
||||
<ion-label>
|
||||
<h2>Tor Address</h2>
|
||||
<p>{{ tor }}</p>
|
||||
<h2>{{ address.name }}</h2>
|
||||
<p>{{ address.url }}</p>
|
||||
</ion-label>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button *ngIf="interface.def.ui" fill="clear" (click)="launch(tor)">
|
||||
<ion-button
|
||||
*ngIf="iFace.type === 'ui'"
|
||||
fill="clear"
|
||||
(click)="launch(address.url)"
|
||||
>
|
||||
<ion-icon size="small" slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" (click)="showQR(tor)">
|
||||
<ion-button fill="clear" (click)="showQR(address.url)">
|
||||
<ion-icon
|
||||
size="small"
|
||||
slot="icon-only"
|
||||
name="qr-code-outline"
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" (click)="copy(tor)">
|
||||
<ion-button fill="clear" (click)="copy(address.url)">
|
||||
<ion-icon size="small" slot="icon-only" name="copy-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-item>
|
||||
<!-- no tor -->
|
||||
<ion-item *ngIf="!interface.addresses['tor-address']">
|
||||
<ion-label>
|
||||
<h2>Tor Address</h2>
|
||||
<p>Service does not use a Tor Address</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- lan -->
|
||||
<ion-item *ngIf="interface.addresses['lan-address'] as lan">
|
||||
<ion-label>
|
||||
<h2>LAN Address</h2>
|
||||
<p>{{ lan }}</p>
|
||||
</ion-label>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button *ngIf="interface.def.ui" fill="clear" (click)="launch(lan)">
|
||||
<ion-icon size="small" slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" (click)="showQR(lan)">
|
||||
<ion-icon
|
||||
size="small"
|
||||
slot="icon-only"
|
||||
name="qr-code-outline"
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" (click)="copy(lan)">
|
||||
<ion-icon size="small" slot="icon-only" name="copy-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-item>
|
||||
<!-- no lan -->
|
||||
<ion-item *ngIf="!interface.addresses['lan-address']">
|
||||
<ion-label>
|
||||
<h2>LAN Address</h2>
|
||||
<p>N/A</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -8,19 +8,29 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top with-widgets">
|
||||
<ion-item-group>
|
||||
<!-- iff ui -->
|
||||
<ng-container *ngIf="ui">
|
||||
<ion-item-divider>User Interface</ion-item-divider>
|
||||
<app-interfaces-item [interface]="ui"></app-interfaces-item>
|
||||
<ion-item-group *ngIf="serviceInterfaces$ | async as serviceInterfaces">
|
||||
<ng-container *ngIf="serviceInterfaces.ui.length">
|
||||
<ion-item-divider>User Interfaces (UI)</ion-item-divider>
|
||||
<app-interfaces-item
|
||||
*ngFor="let ui of serviceInterfaces.ui"
|
||||
[iFace]="ui"
|
||||
></app-interfaces-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- other interface -->
|
||||
<ng-container *ngIf="other.length">
|
||||
<ion-item-divider>Machine Interfaces</ion-item-divider>
|
||||
<div *ngFor="let interface of other" style="margin-bottom: 30px">
|
||||
<app-interfaces-item [interface]="interface"></app-interfaces-item>
|
||||
</div>
|
||||
<ng-container *ngIf="serviceInterfaces.api.length">
|
||||
<ion-item-divider>Application Program Interfaces (API)</ion-item-divider>
|
||||
<app-interfaces-item
|
||||
*ngFor="let api of serviceInterfaces.api"
|
||||
[iFace]="api"
|
||||
></app-interfaces-item>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="serviceInterfaces.p2p.length">
|
||||
<ion-item-divider>Peer-To-Peer Interfaces (P2P)</ion-item-divider>
|
||||
<app-interfaces-item
|
||||
*ngFor="let p2p of serviceInterfaces.p2p"
|
||||
[iFace]="p2p"
|
||||
></app-interfaces-item>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
</ion-content>
|
||||
|
||||
@@ -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<DataModel>,
|
||||
) {}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -17,16 +17,18 @@
|
||||
[installProgress]="pkg.entry['install-progress']"
|
||||
weight="bold"
|
||||
size="small"
|
||||
[sigtermTimeout]="manifest.main['sigterm-timeout']"
|
||||
[sigtermTimeout]="sigtermTimeout"
|
||||
></status>
|
||||
</ion-label>
|
||||
<ion-button
|
||||
*ngIf="manifest.interfaces | hasUi"
|
||||
*ngIf="
|
||||
pkg.entry.installed && (pkg.entry.installed['service-interfaces'] | hasUi)
|
||||
"
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="primary"
|
||||
(click)="launchUi($event)"
|
||||
[disabled]="!(pkg.entry.state | isLaunchable: status:manifest.interfaces)"
|
||||
(click)="launchUi($event, pkg.entry.installed['service-interfaces'])"
|
||||
[disabled]="!(pkg.entry.state | isLaunchable: pkgMainStatus.status)"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
weight="600"
|
||||
[installProgress]="pkg['install-progress']"
|
||||
[rendering]="PR[status.primary]"
|
||||
[sigtermTimeout]="pkg.manifest.main['sigterm-timeout']"
|
||||
[sigtermTimeout]="sigtermTimeout"
|
||||
></status>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
@@ -56,13 +56,11 @@
|
||||
</ion-button>
|
||||
|
||||
<ion-button
|
||||
*ngIf="pkgStatus && (interfaces | hasUi)"
|
||||
*ngIf="pkgStatus && interfaces && (interfaces | hasUi)"
|
||||
class="action-button"
|
||||
color="primary"
|
||||
[disabled]="
|
||||
!(pkg.state | isLaunchable: pkgStatus.main.status:interfaces)
|
||||
"
|
||||
(click)="launchUi()"
|
||||
[disabled]="!(pkg.state | isLaunchable: pkgStatus.main.status)"
|
||||
(click)="launchUi(interfaces)"
|
||||
>
|
||||
<ion-icon slot="start" name="open-outline"></ion-icon>
|
||||
Launch UI
|
||||
|
||||
@@ -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<string, InterfaceDef> {
|
||||
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<void> {
|
||||
|
||||
@@ -6,30 +6,6 @@
|
||||
|
||||
<!-- not Local HTTP -->
|
||||
<ng-template #notLanHttp>
|
||||
<div *ngIf="config.isTorHttp()" class="banner">
|
||||
<ion-item color="warning">
|
||||
<ion-icon slot="start" name="warning-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2 style="font-weight: bold">Http detected</h2>
|
||||
<p style="font-weight: 600">
|
||||
Tor is faster over https. Your Root CA must be trusted.
|
||||
<a
|
||||
href="https://docs.start9.com/0.3.5.x/user-manual/trust-ca"
|
||||
target="_blank"
|
||||
noreferrer
|
||||
style="color: black"
|
||||
>
|
||||
View instructions
|
||||
</a>
|
||||
</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" color="light" (click)="launchHttps()">
|
||||
Open Https
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<ion-grid class="grid">
|
||||
<ion-row class="row">
|
||||
<ion-col>
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
|
||||
@@ -40,27 +40,6 @@
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngIf="isTorHttp" color="warning" class="ion-margin-bottom">
|
||||
<ion-icon slot="start" name="warning-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2 style="font-weight: bold">Http detected</h2>
|
||||
<p style="font-weight: 600">
|
||||
Tor is faster over https.
|
||||
<a
|
||||
[routerLink]="['/system', 'root-ca']"
|
||||
style="color: var(--ion-color-light)"
|
||||
>
|
||||
Download and trust your server's Root CA
|
||||
</a>
|
||||
, then switch to https.
|
||||
</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" color="light" (click)="launchHttps()">
|
||||
Open Https
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
|
||||
<div *ngFor="let cat of settings | keyvalue : asIsOrder">
|
||||
<ion-item-divider>
|
||||
<ion-text color="dark" (click)="addClick(cat.key)">
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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<string, InterfaceDef>,
|
||||
): boolean {
|
||||
return this.configService.isLaunchable(state, status, interfaces)
|
||||
transform(state: PackageState, status: PackageMainStatus): boolean {
|
||||
return this.configService.isLaunchable(state, status)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string, InterfaceDef>): boolean {
|
||||
return hasUi(interfaces)
|
||||
transform(
|
||||
interfaces: InstalledPackageDataEntry['service-interfaces'],
|
||||
): boolean {
|
||||
return interfaces ? hasUi(interfaces) : false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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': [],
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -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': [],
|
||||
|
||||
@@ -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<string, InterfaceDef>,
|
||||
): 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<string, InterfaceDef>): boolean {
|
||||
const int = getUiInterfaceValue(interfaces)
|
||||
return !!int?.['tor-config']
|
||||
}
|
||||
|
||||
export function hasLocalUi(interfaces: Record<string, InterfaceDef>): 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<string, InterfaceDef>): 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, InterfaceDef>,
|
||||
): string {
|
||||
return Object.keys(interfaces).find(key => interfaces[key].ui) || ''
|
||||
}
|
||||
|
||||
export function getUiInterfaceValue(
|
||||
interfaces: Record<string, InterfaceDef>,
|
||||
): 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')
|
||||
}
|
||||
|
||||
@@ -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<string, ServiceInterfaceWithHostInfo>
|
||||
'marketplace-url': string | null
|
||||
'developer-key': string
|
||||
}
|
||||
@@ -160,7 +159,6 @@ export interface Manifest extends MarketplaceManifest<DependencyConfig | null> {
|
||||
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<DependencyConfig | null> {
|
||||
config: ConfigActions | null
|
||||
volumes: Record<string, Volume>
|
||||
'min-os-version': string
|
||||
interfaces: Record<string, InterfaceDef>
|
||||
backup: BackupActions
|
||||
migrations: Migrations | null
|
||||
actions: Record<string, Action>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user