Merge branch 'feat/preferred-port-design' of github.com:Start9Labs/start-os into feature/outbound-gateways

This commit is contained in:
Matt Hill
2026-02-12 08:26:34 -07:00
80 changed files with 4750 additions and 10798 deletions

View File

@@ -83,32 +83,8 @@ export class InterfaceGatewaysComponent {
readonly gateways = input.required<InterfaceGateway[] | undefined>()
async onToggle(gateway: InterfaceGateway) {
const addressInfo = this.interface.value()!.addressInfo
const pkgId = this.interface.packageId()
const loader = this.loader.open().subscribe()
try {
if (pkgId) {
await this.api.pkgBindingToggleGateway({
gateway: gateway.id,
enabled: !gateway.enabled,
internalPort: addressInfo.internalPort,
host: addressInfo.hostId,
package: pkgId,
})
} else {
await this.api.serverBindingToggleGateway({
gateway: gateway.id,
enabled: !gateway.enabled,
internalPort: 80,
})
}
} catch (e: any) {
this.errorService.handleError(e)
} finally {
loader.unsubscribe()
}
async onToggle(_gateway: InterfaceGateway) {
// TODO: Replace with per-address toggle UI (Section 6 frontend overhaul).
// Gateway-level toggle replaced by set-address-enabled RPC.
}
}

View File

@@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core'
import { tuiButtonOptionsProvider } from '@taiga-ui/core'
import { MappedServiceInterface } from './interface.service'
import { InterfaceGatewaysComponent } from './gateways.component'
import { InterfaceTorDomainsComponent } from './tor-domains.component'
import { PublicDomainsComponent } from './public-domains/pd.component'
import { InterfacePrivateDomainsComponent } from './private-domains.component'
import { InterfaceAddressesComponent } from './addresses/addresses.component'
@@ -16,7 +15,6 @@ import { InterfaceAddressesComponent } from './addresses/addresses.component'
[publicDomains]="value()?.publicDomains"
[addSsl]="value()?.addSsl || false"
></section>
<section [torDomains]="value()?.torDomains"></section>
<section [privateDomains]="value()?.privateDomains"></section>
</div>
<hr [style.width.rem]="10" />
@@ -52,7 +50,6 @@ import { InterfaceAddressesComponent } from './addresses/addresses.component'
providers: [tuiButtonOptionsProvider({ size: 'xs' })],
imports: [
InterfaceGatewaysComponent,
InterfaceTorDomainsComponent,
PublicDomainsComponent,
InterfacePrivateDomainsComponent,
InterfaceAddressesComponent,

View File

@@ -27,17 +27,9 @@ function cmpWithRankedPredicates<T extends AddressWithInfo>(
return 0
}
type TorAddress = AddressWithInfo & { info: { kind: 'onion' } }
function filterTor(a: AddressWithInfo): a is TorAddress {
return a.info.kind === 'onion'
}
function cmpTor(a: TorAddress, b: TorAddress): -1 | 0 | 1 {
return cmpWithRankedPredicates(a, b, [x => !x.showSsl])
}
type LanAddress = AddressWithInfo & { info: { kind: 'ip'; public: false } }
type LanAddress = AddressWithInfo & { info: { public: false } }
function filterLan(a: AddressWithInfo): a is LanAddress {
return a.info.kind === 'ip' && !a.info.public
return !a.info.public
}
function cmpLan(host: T.Host, a: LanAddress, b: LanAddress): -1 | 0 | 1 {
return cmpWithRankedPredicates(a, b, [
@@ -53,15 +45,12 @@ function cmpLan(host: T.Host, a: LanAddress, b: LanAddress): -1 | 0 | 1 {
type VpnAddress = AddressWithInfo & {
info: {
kind: 'ip'
public: false
hostname: { kind: 'ipv4' | 'ipv6' | 'domain' }
}
}
function filterVpn(a: AddressWithInfo): a is VpnAddress {
return (
a.info.kind === 'ip' && !a.info.public && a.info.hostname.kind !== 'local'
)
return !a.info.public && a.info.hostname.kind !== 'local'
}
function cmpVpn(host: T.Host, a: VpnAddress, b: VpnAddress): -1 | 0 | 1 {
return cmpWithRankedPredicates(a, b, [
@@ -76,13 +65,12 @@ function cmpVpn(host: T.Host, a: VpnAddress, b: VpnAddress): -1 | 0 | 1 {
type ClearnetAddress = AddressWithInfo & {
info: {
kind: 'ip'
public: true
hostname: { kind: 'ipv4' | 'ipv6' | 'domain' }
}
}
function filterClearnet(a: AddressWithInfo): a is ClearnetAddress {
return a.info.kind === 'ip' && a.info.public
return a.info.public
}
function cmpClearnet(
host: T.Host,
@@ -142,10 +130,7 @@ export class InterfaceService {
h,
)
const info = h
const gateway =
h.kind === 'ip'
? gateways.find(g => h.gateway.id === g.id)
: undefined
const gateway = gateways.find(g => h.gateway.id === g.id)
const res = []
if (url) {
res.push({
@@ -171,7 +156,6 @@ export class InterfaceService {
},
)
const torAddrs = allAddressesWithInfo.filter(filterTor).sort(cmpTor)
const lanAddrs = allAddressesWithInfo
.filter(filterLan)
.sort((a, b) => cmpLan(host, a, b))
@@ -188,7 +172,6 @@ export class InterfaceService {
clearnetAddrs[0],
lanAddrs[0],
vpnAddrs[0],
torAddrs[0],
]
.filter(a => !!a)
.reduce((acc, x) => {
@@ -214,9 +197,8 @@ export class InterfaceService {
kind: 'domain',
visibility: 'public',
})
const tor = addresses.filter({ kind: 'onion' })
const wanIp = addresses.filter({ kind: 'ipv4', visibility: 'public' })
const bestPublic = [publicDomains, tor, wanIp].flatMap(h =>
const bestPublic = [publicDomains, wanIp].flatMap(h =>
h.format('urlstring'),
)[0]
const privateDomains = addresses.filter({
@@ -254,9 +236,6 @@ export class InterfaceService {
.format('urlstring')[0]
onLan = true
break
case 'tor':
matching = tor.format('urlstring')[0]
break
case 'mdns':
matching = mdns.format('urlstring')[0]
onLan = true
@@ -273,19 +252,23 @@ export class InterfaceService {
serviceInterface: T.ServiceInterface,
host: T.Host,
): T.HostnameInfo[] {
let hostnameInfo =
host.hostnameInfo[serviceInterface.addressInfo.internalPort]
return (
hostnameInfo?.filter(
h =>
this.config.accessType === 'localhost' ||
!(
h.kind === 'ip' &&
((h.hostname.kind === 'ipv6' &&
utils.IPV6_LINK_LOCAL.contains(h.hostname.value)) ||
h.gateway.id === 'lo')
),
) || []
const binding =
host.bindings[serviceInterface.addressInfo.internalPort]
if (!binding) return []
const addr = binding.addresses
const enabled = addr.possible.filter(h =>
h.public
? addr.publicEnabled.some(e => utils.deepEqual(e, h))
: !addr.privateDisabled.some(d => utils.deepEqual(d, h)),
)
return enabled.filter(
h =>
this.config.accessType === 'localhost' ||
!(
(h.hostname.kind === 'ipv6' &&
utils.IPV6_LINK_LOCAL.contains(h.hostname.value)) ||
h.gateway.id === 'lo'
),
)
}
@@ -302,31 +285,7 @@ export class InterfaceService {
"Requires trusting your server's Root CA",
)
// ** Tor **
if (info.kind === 'onion') {
access = null
gatewayName = null
type = 'Tor'
bullets = [
this.i18n.transform('Connections can be slow or unreliable at times'),
this.i18n.transform(
'Public if you share the address publicly, otherwise private',
),
this.i18n.transform('Requires using a Tor-enabled device or browser'),
]
// Tor (SSL)
if (showSsl) {
bullets = [rootCaRequired, ...bullets]
// Tor (NON-SSL)
} else {
bullets.unshift(
this.i18n.transform(
'Ideal for anonymous, censorship-resistant hosting and remote access',
),
)
}
// ** Not Tor **
} else {
{
const port = info.hostname.sslPort || info.hostname.port
gatewayName = info.gateway.name
@@ -479,7 +438,6 @@ export class InterfaceService {
export type MappedServiceInterface = T.ServiceInterface & {
gateways: InterfaceGateway[]
torDomains: string[]
publicDomains: PublicDomain[]
privateDomains: string[]
addresses: {

View File

@@ -1,193 +0,0 @@
import {
ChangeDetectionStrategy,
Component,
inject,
input,
} from '@angular/core'
import {
DialogService,
DocsLinkDirective,
ErrorService,
i18nPipe,
LoadingService,
} from '@start9labs/shared'
import { ISB, utils } from '@start9labs/start-sdk'
import { TuiButton, TuiTitle } from '@taiga-ui/core'
import { TuiSkeleton } from '@taiga-ui/kit'
import { TuiCell } from '@taiga-ui/layout'
import { filter } from 'rxjs'
import {
FormComponent,
FormContext,
} from 'src/app/routes/portal/components/form.component'
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { FormDialogService } from 'src/app/services/form-dialog.service'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
import { InterfaceComponent } from './interface.component'
type OnionForm = {
key: string
}
@Component({
selector: 'section[torDomains]',
template: `
<header>
{{ 'Tor Domains' | i18n }}
<a
tuiIconButton
docsLink
path="/user-manual/connecting-remotely/tor.html"
appearance="icon"
iconStart="@tui.external-link"
>
{{ 'Documentation' | i18n }}
</a>
<button
tuiButton
iconStart="@tui.plus"
[style.margin-inline-start]="'auto'"
(click)="add()"
[disabled]="!torDomains()"
>
{{ 'Add' | i18n }}
</button>
</header>
@for (domain of torDomains(); track domain) {
<div tuiCell="s">
<span tuiTitle>{{ domain }}</span>
<button
tuiIconButton
iconStart="@tui.trash"
appearance="action-destructive"
(click)="remove(domain)"
>
{{ 'Delete' | i18n }}
</button>
</div>
} @empty {
@if (torDomains()) {
<app-placeholder icon="@tui.target">
{{ 'No Tor domains' | i18n }}
</app-placeholder>
} @else {
@for (_ of [0, 1]; track $index) {
<label tuiCell="s">
<span tuiTitle [tuiSkeleton]="true">{{ 'Loading' | i18n }}</span>
</label>
}
}
}
`,
styles: `
:host {
grid-column: span 6;
overflow-wrap: break-word;
}
`,
host: { class: 'g-card' },
imports: [
TuiCell,
TuiTitle,
TuiButton,
PlaceholderComponent,
i18nPipe,
DocsLinkDirective,
TuiSkeleton,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InterfaceTorDomainsComponent {
private readonly dialog = inject(DialogService)
private readonly formDialog = inject(FormDialogService)
private readonly loader = inject(LoadingService)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)
private readonly interface = inject(InterfaceComponent)
private readonly i18n = inject(i18nPipe)
readonly torDomains = input.required<readonly string[] | undefined>()
async remove(onion: string) {
this.dialog
.openConfirm({ label: 'Are you sure?', size: 's' })
.pipe(filter(Boolean))
.subscribe(async () => {
const loader = this.loader.open('Removing').subscribe()
const params = { onion }
try {
if (this.interface.packageId()) {
await this.api.pkgRemoveOnion({
...params,
package: this.interface.packageId(),
host: this.interface.value()?.addressInfo.hostId || '',
})
} else {
await this.api.serverRemoveOnion(params)
}
return true
} catch (e: any) {
this.errorService.handleError(e)
return false
} finally {
loader.unsubscribe()
}
})
}
async add() {
this.formDialog.open<FormContext<OnionForm>>(FormComponent, {
label: 'New Tor domain',
data: {
spec: await configBuilderToSpec(
ISB.InputSpec.of({
key: ISB.Value.text({
name: this.i18n.transform('Private Key (optional)')!,
description: this.i18n.transform(
'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) domain. If not provided, a random key will be generated.',
),
required: false,
default: null,
patterns: [utils.Patterns.base64],
}),
}),
),
buttons: [
{
text: this.i18n.transform('Save')!,
handler: async value => this.save(value.key),
},
],
},
})
}
private async save(key?: string): Promise<boolean> {
const loader = this.loader.open('Saving').subscribe()
try {
const onion = key
? await this.api.addTorKey({ key })
: await this.api.generateTorKey({})
if (this.interface.packageId()) {
await this.api.pkgAddOnion({
onion,
package: this.interface.packageId(),
host: this.interface.value()?.addressInfo.hostId || '',
})
} else {
await this.api.serverAddOnion({ onion })
}
return true
} catch (e: any) {
this.errorService.handleError(e)
return false
} finally {
loader.unsubscribe()
}
}
}

View File

@@ -13,10 +13,6 @@ export const ROUTES: Routes = [
path: 'os',
loadComponent: () => import('./routes/os.component'),
},
{
path: 'tor',
loadComponent: () => import('./routes/tor.component'),
},
]
export default ROUTES

View File

@@ -79,12 +79,6 @@ export default class SystemLogsComponent {
subtitle: 'Raw, unfiltered operating system logs',
icon: '@tui.square-dashed-bottom-code',
},
{
link: 'tor',
title: 'Tor Logs',
subtitle: 'Diagnostics for the Tor daemon on this server',
icon: '@tui.target',
},
{
link: 'kernel',
title: 'Kernel Logs',

View File

@@ -1,32 +0,0 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { i18nPipe } from '@start9labs/shared'
import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component'
import { LogsHeaderComponent } from 'src/app/routes/portal/routes/logs/components/header.component'
import { RR } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@Component({
template: `
<logs-header [title]="'Tor Logs' | i18n">
{{ 'Diagnostics for the Tor daemon on this server' | i18n }}
</logs-header>
<logs context="tor" [followLogs]="follow" [fetchLogs]="fetch" />
`,
styles: `
:host {
padding: 1rem;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [LogsComponent, LogsHeaderComponent, i18nPipe],
host: { class: 'g-page' },
})
export default class SystemTorComponent {
private readonly api = inject(ApiService)
protected readonly follow = (params: RR.FollowServerLogsReq) =>
this.api.followTorLogs(params)
protected readonly fetch = (params: RR.GetServerLogsReq) =>
this.api.getTorLogs(params)
}

View File

@@ -135,11 +135,10 @@ export default class ServiceInterfaceRoute {
gateways.map(g => ({
enabled:
(g.public
? binding?.net.publicEnabled.includes(g.id)
: !binding?.net.privateDisabled.includes(g.id)) ?? false,
? binding?.addresses.publicEnabled.some(a => a.gateway.id === g.id)
: !binding?.addresses.privateDisabled.some(a => a.gateway.id === g.id)) ?? false,
...g,
})) || [],
torDomains: host.onions,
publicDomains: getPublicDomains(host.publicDomains, gateways),
privateDomains: host.privateDomains,
addSsl: !!binding?.options.addSsl,

View File

@@ -13,7 +13,6 @@ import {
TuiFiles,
tuiInputFilesOptionsProvider,
} from '@taiga-ui/kit'
import { ConfigService } from 'src/app/services/config.service'
import { TitleDirective } from 'src/app/services/title.service'
import { SideloadPackageComponent } from './package.component'
@@ -55,11 +54,6 @@ import { MarketplacePkgSideload, validateS9pk } from './sideload.utils'
<div>
<tui-avatar appearance="secondary" src="@tui.upload" />
<p>{{ 'Upload .s9pk package file' | i18n }}</p>
@if (isTor) {
<p class="g-warning">
{{ 'Warning: package upload will be slow over Tor.' | i18n }}
</p>
}
<button tuiButton>{{ 'Select' | i18n }}</button>
</div>
}
@@ -92,8 +86,6 @@ import { MarketplacePkgSideload, validateS9pk } from './sideload.utils'
],
})
export default class SideloadComponent {
readonly isTor = inject(ConfigService).accessType === 'tor'
file: File | null = null
readonly package = signal<MarketplacePkgSideload | null>(null)
readonly error = signal<i18nKey | null>(null)

View File

@@ -47,13 +47,11 @@ import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { filter } from 'rxjs'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service'
import { OSService } from 'src/app/services/os.service'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { TitleDirective } from 'src/app/services/title.service'
import { SnekDirective } from './snek.directive'
import { UPDATE } from './update.component'
import { SystemWipeComponent } from './wipe.component'
import { KeyboardSelectComponent } from './keyboard-select.component'
@Component({
@@ -66,7 +64,7 @@ import { KeyboardSelectComponent } from './keyboard-select.component'
</ng-container>
@if (server(); as server) {
<div tuiCell tuiAppearance="outline-grayscale">
<tui-icon icon="@tui.zap" />
<tui-icon icon="@tui.zap" (click)="count = count + 1" />
<span tuiTitle>
<strong>{{ 'Software Update' | i18n }}</strong>
<span tuiSubtitle [style.flex-wrap]="'wrap'">
@@ -178,18 +176,6 @@ import { KeyboardSelectComponent } from './keyboard-select.component'
</button>
}
</div>
<div tuiCell tuiAppearance="outline-grayscale">
<tui-icon icon="@tui.rotate-cw" (click)="count = count + 1" />
<span tuiTitle>
<strong>{{ 'Restart Tor' | i18n }}</strong>
<span tuiSubtitle>
{{ 'Restart the Tor daemon on your server' | i18n }}
</span>
</span>
<button tuiButton appearance="glass" (click)="onTorRestart()">
{{ 'Restart' | i18n }}
</button>
</div>
@if (count > 4) {
<div tuiCell tuiAppearance="outline-grayscale" tuiAnimated>
<tui-icon icon="@tui.briefcase-medical" />
@@ -283,12 +269,10 @@ export default class SystemGeneralComponent {
private readonly errorService = inject(ErrorService)
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
private readonly api = inject(ApiService)
private readonly isTor = inject(ConfigService).accessType === 'tor'
private readonly dialog = inject(DialogService)
private readonly i18n = inject(i18nPipe)
private readonly injector = inject(INJECTOR)
wipe = false
count = 0
readonly server = toSignal(this.patch.watch$('serverInfo'))
@@ -392,24 +376,6 @@ export default class SystemGeneralComponent {
})
}
onTorRestart() {
this.wipe = false
this.dialog
.openConfirm({
label: this.isTor ? 'Warning' : 'Confirm',
data: {
content: new PolymorpheusComponent(
SystemWipeComponent,
this.injector,
),
yes: 'Restart',
no: 'Cancel',
},
})
.pipe(filter(Boolean))
.subscribe(() => this.resetTor(this.wipe))
}
async onRepair() {
this.dialog
.openConfirm({
@@ -532,19 +498,6 @@ export default class SystemGeneralComponent {
.subscribe(() => this.restart())
}
private async resetTor(wipeState: boolean) {
const loader = this.loader.open().subscribe()
try {
await this.api.resetTor({ wipeState, reason: 'User triggered' })
this.dialog.openAlert('Tor restart in progress').subscribe()
} catch (e: any) {
this.errorService.handleError(e)
} finally {
loader.unsubscribe()
}
}
private update() {
this.dialogs
.open(UPDATE, {

View File

@@ -1,36 +0,0 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { TuiLabel } from '@taiga-ui/core'
import { TuiCheckbox } from '@taiga-ui/kit'
import { ConfigService } from 'src/app/services/config.service'
import SystemGeneralComponent from './general.component'
import { i18nPipe } from '@start9labs/shared'
@Component({
template: `
@if (isTor) {
<p>
{{
'You are currently connected over Tor. If you restart the Tor daemon, you will lose connectivity until it comes back online.'
| i18n
}}
</p>
}
<p>
{{
'Optionally wipe state to forcibly acquire new guard nodes. It is recommended to try without wiping state first.'
| i18n
}}
</p>
<label tuiLabel>
<input type="checkbox" tuiCheckbox [(ngModel)]="component.wipe" />
{{ 'Wipe state' | i18n }}
</label>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiLabel, FormsModule, TuiCheckbox, i18nPipe],
})
export class SystemWipeComponent {
readonly isTor = inject(ConfigService).accessType === 'tor'
readonly component = inject(SystemGeneralComponent)
}

View File

@@ -96,11 +96,10 @@ export default class StartOsUiComponent {
gateways: gateways.map(g => ({
enabled:
(g.public
? binding?.net.publicEnabled.includes(g.id)
: !binding?.net.privateDisabled.includes(g.id)) ?? false,
? binding?.addresses.publicEnabled.some(a => a.gateway.id === g.id)
: !binding?.addresses.privateDisabled.some(a => a.gateway.id === g.id)) ?? false,
...g,
})),
torDomains: network.host.onions,
publicDomains: getPublicDomains(network.host.publicDomains, gateways),
privateDomains: network.host.privateDomains,
addSsl: true,

View File

@@ -2126,8 +2126,74 @@ export namespace Mock {
net: {
assignedPort: 80,
assignedSslPort: 443,
publicEnabled: [],
},
addresses: {
privateDisabled: [],
publicEnabled: [],
possible: [
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '192.168.10.11',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: '[fe80:cd00:0000:0cde:1257:0000:211e:72cd]',
scopeId: 2,
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: '[fe80:cd00:0000:0cde:1257:0000:211e:1234]',
scopeId: 3,
port: null,
sslPort: 1234,
},
},
],
},
options: {
addSsl: null,
@@ -2138,87 +2204,6 @@ export namespace Mock {
},
publicDomains: {},
privateDomains: [],
onions: [],
hostnameInfo: {
80: [
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '192.168.10.11',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: '[fe80:cd00:0000:0cde:1257:0000:211e:72cd]',
scopeId: 2,
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: '[fe80:cd00:0000:0cde:1257:0000:211e:1234]',
scopeId: 3,
port: null,
sslPort: 1234,
},
},
{
kind: 'onion',
hostname: {
value: 'bitcoin-p2p.onion',
port: 80,
sslPort: 443,
},
},
],
},
},
bcdefgh: {
bindings: {
@@ -2227,8 +2212,11 @@ export namespace Mock {
net: {
assignedPort: 8332,
assignedSslPort: null,
publicEnabled: [],
},
addresses: {
privateDisabled: [],
publicEnabled: [],
possible: [],
},
options: {
addSsl: null,
@@ -2239,10 +2227,6 @@ export namespace Mock {
},
publicDomains: {},
privateDomains: [],
onions: [],
hostnameInfo: {
8332: [],
},
},
cdefghi: {
bindings: {
@@ -2251,8 +2235,11 @@ export namespace Mock {
net: {
assignedPort: 8333,
assignedSslPort: null,
publicEnabled: [],
},
addresses: {
privateDisabled: [],
publicEnabled: [],
possible: [],
},
options: {
addSsl: null,
@@ -2263,10 +2250,6 @@ export namespace Mock {
},
publicDomains: {},
privateDomains: [],
onions: [],
hostnameInfo: {
8333: [],
},
},
},
storeExposedDependents: [],

View File

@@ -79,14 +79,14 @@ export namespace RR {
uptime: number // seconds
}
export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs & net.tor.logs
export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs
export type GetServerLogsRes = FetchLogsRes
export type FollowServerLogsReq = {
limit?: number // (optional) default is 50. Ignored if cursor provided
boot?: number | string | null // (optional) number is offset (0: current, -1 prev, +1 first), string is a specific boot id, null is all. Default is undefined
cursor?: string // the last known log. Websocket will return all logs since this log
} // server.logs.follow & server.kernel-logs.follow & net.tor.follow-logs
} // server.logs.follow & server.kernel-logs.follow
export type FollowServerLogsRes = {
startCursor: string
guid: string
@@ -120,12 +120,6 @@ export namespace RR {
} // net.dns.query
export type QueryDnsRes = string | null
export type ResetTorReq = {
wipeState: boolean
reason: string
} // net.tor.reset
export type ResetTorRes = null
export type SetKeyboardReq = FullKeyboard // server.set-keyboard
export type SetKeyboardRes = null
@@ -301,29 +295,13 @@ export namespace RR {
}
export type RemoveAcmeRes = null
export type AddTorKeyReq = {
// net.tor.key.add
key: string
}
export type GenerateTorKeyReq = {} // net.tor.key.generate
export type AddTorKeyRes = string // onion address *with* .onion suffix
export type ServerBindingToggleGatewayReq = {
// server.host.binding.set-gateway-enabled
gateway: T.GatewayId
export type ServerBindingSetAddressEnabledReq = {
// server.host.binding.set-address-enabled
internalPort: 80
enabled: boolean
address: string // JSON-serialized HostnameInfo
enabled: boolean | null // null = reset to default
}
export type ServerBindingToggleGatewayRes = null
export type ServerAddOnionReq = {
// server.host.address.onion.add
onion: string // address *with* .onion suffix
}
export type AddOnionRes = null
export type ServerRemoveOnionReq = ServerAddOnionReq // server.host.address.onion.remove
export type RemoveOnionRes = null
export type ServerBindingSetAddressEnabledRes = null
export type OsUiAddPublicDomainReq = {
// server.host.address.domain.public.add
@@ -351,23 +329,16 @@ export namespace RR {
}
export type OsUiRemovePrivateDomainRes = null
export type PkgBindingToggleGatewayReq = Omit<
ServerBindingToggleGatewayReq,
export type PkgBindingSetAddressEnabledReq = Omit<
ServerBindingSetAddressEnabledReq,
'internalPort'
> & {
// package.host.binding.set-gateway-enabled
// package.host.binding.set-address-enabled
internalPort: number
package: T.PackageId // string
host: T.HostId // string
}
export type PkgBindingToggleGatewayRes = null
export type PkgAddOnionReq = ServerAddOnionReq & {
// package.host.address.onion.add
package: T.PackageId // string
host: T.HostId // string
}
export type PkgRemoveOnionReq = PkgAddOnionReq // package.host.address.onion.remove
export type PkgBindingSetAddressEnabledRes = null
export type PkgAddPublicDomainReq = OsUiAddPublicDomainReq & {
// package.host.address.domain.public.add

View File

@@ -81,8 +81,6 @@ export abstract class ApiService {
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes>
abstract getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes>
abstract getKernelLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes>
@@ -91,10 +89,6 @@ export abstract class ApiService {
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes>
abstract followTorLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes>
abstract followKernelLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes>
@@ -125,8 +119,6 @@ export abstract class ApiService {
abstract queryDns(params: RR.QueryDnsReq): Promise<RR.QueryDnsRes>
abstract resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes>
// smtp
abstract setSmtp(params: RR.SetSMTPReq): Promise<RR.SetSMTPRes>
@@ -352,21 +344,9 @@ export abstract class ApiService {
abstract removeAcme(params: RR.RemoveAcmeReq): Promise<RR.RemoveAcmeRes>
abstract addTorKey(params: RR.AddTorKeyReq): Promise<RR.AddTorKeyRes>
abstract generateTorKey(
params: RR.GenerateTorKeyReq,
): Promise<RR.AddTorKeyRes>
abstract serverBindingToggleGateway(
params: RR.ServerBindingToggleGatewayReq,
): Promise<RR.ServerBindingToggleGatewayRes>
abstract serverAddOnion(params: RR.ServerAddOnionReq): Promise<RR.AddOnionRes>
abstract serverRemoveOnion(
params: RR.ServerRemoveOnionReq,
): Promise<RR.RemoveOnionRes>
abstract serverBindingSetAddressEnabled(
params: RR.ServerBindingSetAddressEnabledReq,
): Promise<RR.ServerBindingSetAddressEnabledRes>
abstract osUiAddPublicDomain(
params: RR.OsUiAddPublicDomainReq,
@@ -384,15 +364,9 @@ export abstract class ApiService {
params: RR.OsUiRemovePrivateDomainReq,
): Promise<RR.OsUiRemovePrivateDomainRes>
abstract pkgBindingToggleGateway(
params: RR.PkgBindingToggleGatewayReq,
): Promise<RR.PkgBindingToggleGatewayRes>
abstract pkgAddOnion(params: RR.PkgAddOnionReq): Promise<RR.AddOnionRes>
abstract pkgRemoveOnion(
params: RR.PkgRemoveOnionReq,
): Promise<RR.RemoveOnionRes>
abstract pkgBindingSetAddressEnabled(
params: RR.PkgBindingSetAddressEnabledReq,
): Promise<RR.PkgBindingSetAddressEnabledRes>
abstract pkgAddPublicDomain(
params: RR.PkgAddPublicDomainReq,

View File

@@ -195,10 +195,6 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'server.logs', params })
}
async getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes> {
return this.rpcRequest({ method: 'net.tor.logs', params })
}
async getKernelLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> {
@@ -211,12 +207,6 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'server.logs.follow', params })
}
async followTorLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
return this.rpcRequest({ method: 'net.tor.logs.follow', params })
}
async followKernelLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
@@ -278,10 +268,6 @@ export class LiveApiService extends ApiService {
})
}
async resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes> {
return this.rpcRequest({ method: 'net.tor.reset', params })
}
// marketplace URLs
async checkOSUpdate(
@@ -633,41 +619,11 @@ export class LiveApiService extends ApiService {
})
}
async addTorKey(params: RR.AddTorKeyReq): Promise<RR.AddTorKeyRes> {
async serverBindingSetAddressEnabled(
params: RR.ServerBindingSetAddressEnabledReq,
): Promise<RR.ServerBindingSetAddressEnabledRes> {
return this.rpcRequest({
method: 'net.tor.key.add',
params,
})
}
async generateTorKey(params: RR.GenerateTorKeyReq): Promise<RR.AddTorKeyRes> {
return this.rpcRequest({
method: 'net.tor.key.generate',
params,
})
}
async serverBindingToggleGateway(
params: RR.ServerBindingToggleGatewayReq,
): Promise<RR.ServerBindingToggleGatewayRes> {
return this.rpcRequest({
method: 'server.host.binding.set-gateway-enabled',
params,
})
}
async serverAddOnion(params: RR.ServerAddOnionReq): Promise<RR.AddOnionRes> {
return this.rpcRequest({
method: 'server.host.address.onion.add',
params,
})
}
async serverRemoveOnion(
params: RR.ServerRemoveOnionReq,
): Promise<RR.RemoveOnionRes> {
return this.rpcRequest({
method: 'server.host.address.onion.remove',
method: 'server.host.binding.set-address-enabled',
params,
})
}
@@ -708,27 +664,11 @@ export class LiveApiService extends ApiService {
})
}
async pkgBindingToggleGateway(
params: RR.PkgBindingToggleGatewayReq,
): Promise<RR.PkgBindingToggleGatewayRes> {
async pkgBindingSetAddressEnabled(
params: RR.PkgBindingSetAddressEnabledReq,
): Promise<RR.PkgBindingSetAddressEnabledRes> {
return this.rpcRequest({
method: 'package.host.binding.set-gateway-enabled',
params,
})
}
async pkgAddOnion(params: RR.PkgAddOnionReq): Promise<RR.AddOnionRes> {
return this.rpcRequest({
method: 'package.host.address.onion.add',
params,
})
}
async pkgRemoveOnion(
params: RR.PkgRemoveOnionReq,
): Promise<RR.RemoveOnionRes> {
return this.rpcRequest({
method: 'package.host.address.onion.remove',
method: 'package.host.binding.set-address-enabled',
params,
})
}

View File

@@ -281,17 +281,6 @@ export class MockApiService extends ApiService {
}
}
async getTorLogs(params: RR.GetServerLogsReq): Promise<RR.GetServerLogsRes> {
await pauseFor(2000)
const entries = this.randomLogs(params.limit)
return {
entries,
startCursor: 'start-cursor',
endCursor: 'end-cursor',
}
}
async getKernelLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> {
@@ -315,16 +304,6 @@ export class MockApiService extends ApiService {
}
}
async followTorLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
await pauseFor(2000)
return {
startCursor: 'start-cursor',
guid: 'logs-guid',
}
}
async followKernelLogs(
params: RR.FollowServerLogsReq,
): Promise<RR.FollowServerLogsRes> {
@@ -504,11 +483,6 @@ export class MockApiService extends ApiService {
return null
}
async resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes> {
await pauseFor(2000)
return null
}
// marketplace URLs
async checkOSUpdate(
@@ -1415,77 +1389,12 @@ export class MockApiService extends ApiService {
return null
}
async addTorKey(params: RR.AddTorKeyReq): Promise<RR.AddTorKeyRes> {
await pauseFor(2000)
return 'vanityabcdefghijklmnop.onion'
}
async generateTorKey(params: RR.GenerateTorKeyReq): Promise<RR.AddTorKeyRes> {
await pauseFor(2000)
return 'abcdefghijklmnopqrstuv.onion'
}
async serverBindingToggleGateway(
params: RR.ServerBindingToggleGatewayReq,
): Promise<RR.ServerBindingToggleGatewayRes> {
async serverBindingSetAddressEnabled(
params: RR.ServerBindingSetAddressEnabledReq,
): Promise<RR.ServerBindingSetAddressEnabledRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: `/serverInfo/network/host/bindings/${params.internalPort}/net/publicEnabled`,
value: params.enabled ? [params.gateway] : [],
},
]
this.mockRevision(patch)
return null
}
async serverAddOnion(params: RR.ServerAddOnionReq): Promise<RR.AddOnionRes> {
await pauseFor(2000)
const patch: Operation<any>[] = [
{
op: PatchOp.ADD,
path: `/serverInfo/host/onions/0`,
value: params.onion,
},
{
op: PatchOp.ADD,
path: `/serverInfo/host/hostnameInfo/80/0`,
value: {
kind: 'onion',
hostname: {
port: 80,
sslPort: 443,
value: params.onion,
},
},
},
]
this.mockRevision(patch)
return null
}
async serverRemoveOnion(
params: RR.ServerRemoveOnionReq,
): Promise<RR.RemoveOnionRes> {
await pauseFor(2000)
const patch: RemoveOperation[] = [
{
op: PatchOp.REMOVE,
path: `/serverInfo/host/onions/0`,
},
{
op: PatchOp.REMOVE,
path: `/serverInfo/host/hostnameInfo/80/-1`,
},
]
this.mockRevision(patch)
// Mock: no-op since address enable/disable modifies DerivedAddressInfo sets
return null
}
@@ -1504,10 +1413,9 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.ADD,
path: `/serverInfo/host/hostnameInfo/80/0`,
path: `/serverInfo/network/host/bindings/80/addresses/possible/0`,
value: {
kind: 'ip',
gatewayId: 'eth0',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: true,
hostname: {
kind: 'domain',
@@ -1536,7 +1444,7 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.REMOVE,
path: `/serverInfo/host/hostnameInfo/80/0`,
path: `/serverInfo/network/host/bindings/80/addresses/possible/0`,
},
]
this.mockRevision(patch)
@@ -1557,10 +1465,9 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.ADD,
path: `/serverInfo/host/hostnameInfo/80/0`,
path: `/serverInfo/network/host/bindings/80/addresses/possible/0`,
value: {
kind: 'ip',
gatewayId: 'eth0',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'domain',
@@ -1590,7 +1497,7 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.REMOVE,
path: `/serverInfo/host/hostnameInfo/80/0`,
path: `/serverInfo/network/host/bindings/80/addresses/possible/0`,
},
]
this.mockRevision(patch)
@@ -1598,67 +1505,12 @@ export class MockApiService extends ApiService {
return null
}
async pkgBindingToggleGateway(
params: RR.PkgBindingToggleGatewayReq,
): Promise<RR.PkgBindingToggleGatewayRes> {
async pkgBindingSetAddressEnabled(
params: RR.PkgBindingSetAddressEnabledReq,
): Promise<RR.PkgBindingSetAddressEnabledRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: `/packageData/${params.package}/hosts/${params.host}/bindings/${params.internalPort}/net/privateDisabled`,
value: params.enabled ? [] : [params.gateway],
},
]
this.mockRevision(patch)
return null
}
async pkgAddOnion(params: RR.PkgAddOnionReq): Promise<RR.AddOnionRes> {
await pauseFor(2000)
const patch: Operation<any>[] = [
{
op: PatchOp.ADD,
path: `/packageData/${params.package}/hosts/${params.host}/onions/0`,
value: params.onion,
},
{
op: PatchOp.ADD,
path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`,
value: {
kind: 'onion',
hostname: {
port: 80,
sslPort: 443,
value: params.onion,
},
},
},
]
this.mockRevision(patch)
return null
}
async pkgRemoveOnion(
params: RR.PkgRemoveOnionReq,
): Promise<RR.RemoveOnionRes> {
await pauseFor(2000)
const patch: RemoveOperation[] = [
{
op: PatchOp.REMOVE,
path: `/packageData/${params.package}/hosts/${params.host}/onions/0`,
},
{
op: PatchOp.REMOVE,
path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`,
},
]
this.mockRevision(patch)
// Mock: no-op since address enable/disable modifies DerivedAddressInfo sets
return null
}
@@ -1677,10 +1529,9 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.ADD,
path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`,
path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`,
value: {
kind: 'ip',
gatewayId: 'eth0',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: true,
hostname: {
kind: 'domain',
@@ -1709,7 +1560,7 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.REMOVE,
path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`,
path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`,
},
]
this.mockRevision(patch)
@@ -1730,10 +1581,9 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.ADD,
path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`,
path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`,
value: {
kind: 'ip',
gatewayId: 'eth0',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'domain',
@@ -1763,7 +1613,7 @@ export class MockApiService extends ApiService {
},
{
op: PatchOp.REMOVE,
path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`,
path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`,
},
]
this.mockRevision(patch)

View File

@@ -38,8 +38,74 @@ export const mockPatchData: DataModel = {
net: {
assignedPort: null,
assignedSslPort: 443,
publicEnabled: [],
},
addresses: {
privateDisabled: [],
publicEnabled: [],
possible: [
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.1',
port: null,
sslPort: 443,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 443,
},
},
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd',
scopeId: 2,
port: null,
sslPort: 443,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234',
scopeId: 3,
port: null,
sslPort: 443,
},
},
],
},
options: {
preferredExternalPort: 80,
@@ -54,87 +120,6 @@ export const mockPatchData: DataModel = {
},
publicDomains: {},
privateDomains: [],
onions: ['myveryownspecialtoraddress'],
hostnameInfo: {
80: [
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.1',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd',
scopeId: 2,
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234',
scopeId: 3,
port: null,
sslPort: 443,
},
},
{
kind: 'onion',
hostname: {
value: 'myveryownspecialtoraddress.onion',
port: 80,
sslPort: 443,
},
},
],
},
},
gateways: {
eth0: {
@@ -529,8 +514,74 @@ export const mockPatchData: DataModel = {
net: {
assignedPort: 80,
assignedSslPort: 443,
publicEnabled: [],
},
addresses: {
privateDisabled: [],
publicEnabled: [],
possible: [
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.1',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd',
scopeId: 2,
port: null,
sslPort: 1234,
},
},
{
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234',
scopeId: 3,
port: null,
sslPort: 1234,
},
},
],
},
options: {
addSsl: null,
@@ -541,87 +592,6 @@ export const mockPatchData: DataModel = {
},
publicDomains: {},
privateDomains: [],
onions: [],
hostnameInfo: {
80: [
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.1',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'eth0', name: 'Ethernet', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd',
scopeId: 2,
port: null,
sslPort: 1234,
},
},
{
kind: 'ip',
gateway: { id: 'wlan0', name: 'Wireless', public: false },
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234',
scopeId: 3,
port: null,
sslPort: 1234,
},
},
{
kind: 'onion',
hostname: {
value: 'bitcoin-p2p.onion',
port: 80,
sslPort: 443,
},
},
],
},
},
bcdefgh: {
bindings: {
@@ -630,8 +600,11 @@ export const mockPatchData: DataModel = {
net: {
assignedPort: 8332,
assignedSslPort: null,
publicEnabled: [],
},
addresses: {
privateDisabled: [],
publicEnabled: [],
possible: [],
},
options: {
addSsl: null,
@@ -642,10 +615,6 @@ export const mockPatchData: DataModel = {
},
publicDomains: {},
privateDomains: [],
onions: [],
hostnameInfo: {
8332: [],
},
},
cdefghi: {
bindings: {
@@ -654,8 +623,11 @@ export const mockPatchData: DataModel = {
net: {
assignedPort: 8333,
assignedSslPort: null,
publicEnabled: [],
},
addresses: {
privateDisabled: [],
publicEnabled: [],
possible: [],
},
options: {
addSsl: null,
@@ -666,10 +638,6 @@ export const mockPatchData: DataModel = {
},
publicDomains: {},
privateDomains: [],
onions: [],
hostnameInfo: {
8333: [],
},
},
},
storeExposedDependents: [],

View File

@@ -32,7 +32,6 @@ export class ConfigService {
private getAccessType = utils.once(() => {
if (useMocks) return mocks.maskAs
if (this.hostname === 'localhost') return 'localhost'
if (this.hostname.endsWith('.onion')) return 'tor'
if (this.hostname.endsWith('.local')) return 'mdns'
let ip = null
try {
@@ -51,7 +50,7 @@ export class ConfigService {
}
isLanHttp(): boolean {
return !this.isHttps() && !['localhost', 'tor'].includes(this.accessType)
return !this.isHttps() && this.accessType !== 'localhost'
}
isHttps(): boolean {