mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
start sorting addresses
This commit is contained in:
@@ -37,7 +37,7 @@ export class CAWizardComponent {
|
||||
}
|
||||
|
||||
launchHttps() {
|
||||
this.document.defaultView?.open(`https://${this.config.getHost()}`, '_self')
|
||||
this.document.defaultView?.open(`https://${this.config.host}`, '_self')
|
||||
}
|
||||
|
||||
private async testHttps() {
|
||||
|
||||
@@ -26,7 +26,7 @@ import { InterfaceGateway } from './interface.utils'
|
||||
[showIcons]="false"
|
||||
[ngModel]="gateway.enabled"
|
||||
(ngModelChange)="onToggle(gateway)"
|
||||
[disabled]="osUi() && !gateway.public"
|
||||
[disabled]="isOs() && !gateway.public"
|
||||
/>
|
||||
</label>
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import { InterfaceGateway } from './interface.utils'
|
||||
})
|
||||
export class InterfaceGatewaysComponent {
|
||||
readonly gateways = input.required<InterfaceGateway[]>()
|
||||
readonly osUi = input.required<boolean>()
|
||||
readonly isOs = input.required<boolean>()
|
||||
|
||||
async onToggle(gateway: InterfaceGateway) {}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { InterfaceAddressesComponent } from './addresses/addresses.component'
|
||||
template: `
|
||||
<!-- @TODO Alex / Matt translation in all nested components -->
|
||||
<div [style.display]="'grid'">
|
||||
<section [gateways]="value().gateways" [osUi]="osUi()"></section>
|
||||
<section [gateways]="value().gateways" [isOs]="value().isOs"></section>
|
||||
<section [torDomains]="value().torDomains"></section>
|
||||
<section [clearnetDomains]="value().clearnetDomains"></section>
|
||||
</div>
|
||||
@@ -48,5 +48,4 @@ export class InterfaceComponent {
|
||||
readonly packageId = input('')
|
||||
readonly value = input.required<MappedServiceInterface>()
|
||||
readonly isRunning = input.required<boolean>()
|
||||
readonly osUi = input(false)
|
||||
}
|
||||
|
||||
@@ -1,113 +1,245 @@
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { inject, Injectable } from '@angular/core'
|
||||
import { T, utils } from '@start9labs/start-sdk'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
|
||||
export function getAddresses(
|
||||
serviceInterface: T.ServiceInterface,
|
||||
host: T.Host,
|
||||
config: ConfigService,
|
||||
): MappedServiceInterface['addresses'] {
|
||||
const addressInfo = serviceInterface.addressInfo
|
||||
const hostnames =
|
||||
host.hostnameInfo[addressInfo.internalPort]?.filter(
|
||||
h =>
|
||||
config.isLocalhost() ||
|
||||
h.kind !== 'ip' ||
|
||||
h.hostname.kind !== 'ipv6' ||
|
||||
!h.hostname.value.startsWith('fe80::'),
|
||||
) || []
|
||||
type AddressWithInfo = {
|
||||
address: URL
|
||||
info: T.HostnameInfo
|
||||
}
|
||||
|
||||
if (config.isLocalhost()) {
|
||||
const local = hostnames.find(
|
||||
h => h.kind === 'ip' && h.hostname.kind === 'local',
|
||||
)
|
||||
function filterTor(a: AddressWithInfo): a is (AddressWithInfo & { info: { kind: 'onion' } }) {
|
||||
return a.info.kind === 'onion'
|
||||
}
|
||||
|
||||
if (local) {
|
||||
hostnames.unshift({
|
||||
kind: 'ip',
|
||||
gatewayId: 'lo',
|
||||
public: false,
|
||||
hostname: {
|
||||
kind: 'local',
|
||||
port: local.hostname.port,
|
||||
sslPort: local.hostname.sslPort,
|
||||
value: 'localhost',
|
||||
},
|
||||
function cmpTor(a: AddressWithInfo, b: AddressWithInfo): -1 | 0 | 1 {
|
||||
if (!filterTor(a) || !filterTor(b)) return 0
|
||||
for (let [x, y, sign] of [[a, b, 1] as const, [b, a, -1] as const]) {
|
||||
if (x.address.protocol === 'http:' && y.address.protocol === 'https:')
|
||||
return sign
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function filterLan(a: AddressWithInfo): a is (AddressWithInfo & { info: { kind: 'ip', public: false } }) {
|
||||
return a.info.kind === 'ip' && !a.info.public
|
||||
}
|
||||
|
||||
function cmpLan(host: T.Host, a: AddressWithInfo, b: AddressWithInfo): -1 | 0 | 1 {
|
||||
if (!filterLan(a) || !filterLan(b)) return 0
|
||||
for (let [x, y, sign] of [[a, b, 1] as const, [b, a, -1] as const]) {
|
||||
if (x.info.kind === 'domain' && host.domains.)
|
||||
return sign
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class InterfaceService {
|
||||
private readonly config = inject(ConfigService)
|
||||
|
||||
getAddresses(
|
||||
serviceInterface: T.ServiceInterface,
|
||||
host: T.Host,
|
||||
): MappedServiceInterface['addresses'] {
|
||||
const hostnamesInfos = this.hostnameInfo(serviceInterface, host)
|
||||
|
||||
const addresses = {
|
||||
common: [],
|
||||
uncommon: [],
|
||||
}
|
||||
|
||||
if (!hostnamesInfos.length) return addresses
|
||||
|
||||
hostnamesInfos.forEach(h => {
|
||||
const addresses = utils.addressHostToUrl(serviceInterface.addressInfo, h)
|
||||
|
||||
addresses.forEach(url => {
|
||||
if (h.kind === 'onion') {
|
||||
tor.push({
|
||||
protocol: /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(url)
|
||||
? new URL(url).protocol.replace(':', '').toUpperCase()
|
||||
: null,
|
||||
url,
|
||||
})
|
||||
} else {
|
||||
const hostnameKind = h.hostname.kind
|
||||
|
||||
if (
|
||||
h.public ||
|
||||
(hostnameKind === 'domain' &&
|
||||
host.domains[h.hostname.domain]?.public)
|
||||
) {
|
||||
clearnet.push({
|
||||
url,
|
||||
disabled: !h.public,
|
||||
isDomain: hostnameKind == 'domain',
|
||||
authority:
|
||||
hostnameKind == 'domain'
|
||||
? host.domains[h.hostname.domain]?.acme || null
|
||||
: null,
|
||||
})
|
||||
} else {
|
||||
local.push({
|
||||
nid:
|
||||
hostnameKind === 'local'
|
||||
? 'Local'
|
||||
: `${h.gatewayId} (${hostnameKind})`,
|
||||
url,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
common: common.filter(
|
||||
(value, index, self) =>
|
||||
index === self.findIndex(t => t.url === value.url),
|
||||
),
|
||||
uncommon: uncommon.filter(
|
||||
(value, index, self) =>
|
||||
index === self.findIndex(t => t.url === value.url),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const common: Address[] = [
|
||||
{
|
||||
type: 'Local',
|
||||
description: '',
|
||||
gateway: 'Wired Connection 1',
|
||||
url: 'https://test.local:1234',
|
||||
},
|
||||
{
|
||||
type: 'IPv4 (LAN)',
|
||||
description: '',
|
||||
gateway: 'Wired Connection 1',
|
||||
url: 'https://192.168.1.10.local:1234',
|
||||
},
|
||||
]
|
||||
const uncommon: Address[] = [
|
||||
{
|
||||
type: 'IPv4 (WAN)',
|
||||
description: '',
|
||||
gateway: 'Wired Connection 1',
|
||||
url: 'https://72.72.72.72',
|
||||
},
|
||||
]
|
||||
/** ${scheme}://${username}@${host}:${externalPort}${suffix} */
|
||||
launchableAddress(ui: T.ServiceInterface, host: T.Host): string {
|
||||
const hostnameInfos = this.hostnameInfo(ui, host)
|
||||
|
||||
// hostnames.forEach(h => {
|
||||
// const addresses = utils.addressHostToUrl(addressInfo, h)
|
||||
if (!hostnameInfos.length) return ''
|
||||
|
||||
// addresses.forEach(url => {
|
||||
// if (h.kind === 'onion') {
|
||||
// tor.push({
|
||||
// protocol: /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(url)
|
||||
// ? new URL(url).protocol.replace(':', '').toUpperCase()
|
||||
// : null,
|
||||
// url,
|
||||
// })
|
||||
// } else {
|
||||
// const hostnameKind = h.hostname.kind
|
||||
const addressInfo = ui.addressInfo
|
||||
const username = addressInfo.username ? addressInfo.username + '@' : ''
|
||||
const suffix = addressInfo.suffix || ''
|
||||
const url = new URL(`https://${username}placeholder${suffix}`)
|
||||
const use = (hostname: {
|
||||
value: string
|
||||
port: number | null
|
||||
sslPort: number | null
|
||||
}) => {
|
||||
url.hostname = hostname.value
|
||||
const useSsl =
|
||||
hostname.port && hostname.sslPort
|
||||
? this.config.isHttps()
|
||||
: !!hostname.sslPort
|
||||
url.protocol = useSsl
|
||||
? `${addressInfo.sslScheme || 'https'}:`
|
||||
: `${addressInfo.scheme || 'http'}:`
|
||||
const port = useSsl ? hostname.sslPort : hostname.port
|
||||
const omitPort = useSsl
|
||||
? ui.addressInfo.sslScheme === 'https' && port === 443
|
||||
: ui.addressInfo.scheme === 'http' && port === 80
|
||||
if (!omitPort && port) url.port = String(port)
|
||||
}
|
||||
const useFirst = (
|
||||
hostnames: (
|
||||
| {
|
||||
value: string
|
||||
port: number | null
|
||||
sslPort: number | null
|
||||
}
|
||||
| undefined
|
||||
)[],
|
||||
) => {
|
||||
const first = hostnames.find(h => h)
|
||||
if (first) {
|
||||
use(first)
|
||||
}
|
||||
return !!first
|
||||
}
|
||||
|
||||
// if (
|
||||
// h.public ||
|
||||
// (hostnameKind === 'domain' && host.domains[h.hostname.domain]?.public)
|
||||
// ) {
|
||||
// clearnet.push({
|
||||
// url,
|
||||
// disabled: !h.public,
|
||||
// isDomain: hostnameKind == 'domain',
|
||||
// authority:
|
||||
// hostnameKind == 'domain'
|
||||
// ? host.domains[h.hostname.domain]?.acme || null
|
||||
// : null,
|
||||
// })
|
||||
// } else {
|
||||
// local.push({
|
||||
// nid:
|
||||
// hostnameKind === 'local'
|
||||
// ? 'Local'
|
||||
// : `${h.gatewayId} (${hostnameKind})`,
|
||||
// url,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
const ipHostnames = hostnameInfos
|
||||
.filter(h => h.kind === 'ip')
|
||||
.map(h => h.hostname) as T.IpHostname[]
|
||||
const domainHostname = ipHostnames
|
||||
.filter(h => h.kind === 'domain')
|
||||
.map(h => h as T.IpHostname & { kind: 'domain' })
|
||||
.map(h => ({
|
||||
value: h.domain,
|
||||
sslPort: h.sslPort,
|
||||
port: h.port,
|
||||
}))[0]
|
||||
const wanIpHostname = hostnameInfos
|
||||
.filter(h => h.kind === 'ip' && h.public && h.hostname.kind !== 'domain')
|
||||
.map(h => h.hostname as Exclude<T.IpHostname, { kind: 'domain' }>)
|
||||
.map(h => ({
|
||||
value: h.value,
|
||||
sslPort: h.sslPort,
|
||||
port: h.port,
|
||||
}))[0]
|
||||
const onionHostname = hostnameInfos
|
||||
.filter(h => h.kind === 'onion')
|
||||
.map(h => h as T.HostnameInfo & { kind: 'onion' })
|
||||
.map(h => ({
|
||||
value: h.hostname.value,
|
||||
sslPort: h.hostname.sslPort,
|
||||
port: h.hostname.port,
|
||||
}))[0]
|
||||
const localHostname = ipHostnames
|
||||
.filter(h => h.kind === 'local')
|
||||
.map(h => h as T.IpHostname & { kind: 'local' })
|
||||
.map(h => ({ value: h.value, sslPort: h.sslPort, port: h.port }))[0]
|
||||
|
||||
return {
|
||||
common: common.filter(
|
||||
(value, index, self) =>
|
||||
index === self.findIndex(t => t.url === value.url),
|
||||
),
|
||||
uncommon: uncommon.filter(
|
||||
(value, index, self) =>
|
||||
index === self.findIndex(t => t.url === value.url),
|
||||
),
|
||||
if (this.config.isClearnet()) {
|
||||
if (
|
||||
!useFirst([domainHostname, wanIpHostname, onionHostname, localHostname])
|
||||
) {
|
||||
return ''
|
||||
}
|
||||
} else if (this.config.isTor()) {
|
||||
if (
|
||||
!useFirst([onionHostname, domainHostname, wanIpHostname, localHostname])
|
||||
) {
|
||||
return ''
|
||||
}
|
||||
} else if (this.config.isIpv6()) {
|
||||
const ipv6Hostname = ipHostnames.find(h => h.kind === 'ipv6') as {
|
||||
kind: 'ipv6'
|
||||
value: string
|
||||
scopeId: number
|
||||
port: number | null
|
||||
sslPort: number | null
|
||||
}
|
||||
|
||||
if (!useFirst([ipv6Hostname, localHostname])) {
|
||||
return ''
|
||||
}
|
||||
} else {
|
||||
// ipv4 or .local or localhost
|
||||
|
||||
if (!localHostname) return ''
|
||||
|
||||
use({
|
||||
value: this.config.hostname,
|
||||
port: localHostname.port,
|
||||
sslPort: localHostname.sslPort,
|
||||
})
|
||||
}
|
||||
|
||||
return url.href
|
||||
}
|
||||
|
||||
private hostnameInfo(
|
||||
serviceInterface: T.ServiceInterface,
|
||||
host: T.Host,
|
||||
): T.HostnameInfo[] {
|
||||
let hostnameInfo =
|
||||
host.hostnameInfo[serviceInterface.addressInfo.internalPort]
|
||||
return (
|
||||
hostnameInfo?.filter(
|
||||
h =>
|
||||
this.config.isLocalhost() ||
|
||||
!(
|
||||
h.kind === 'ip' &&
|
||||
((h.hostname.kind === 'ipv6' &&
|
||||
h.hostname.value.startsWith('fe80::')) ||
|
||||
h.gatewayId === 'lo')
|
||||
),
|
||||
) || []
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +251,7 @@ export type MappedServiceInterface = T.ServiceInterface & {
|
||||
common: Address[]
|
||||
uncommon: Address[]
|
||||
}
|
||||
isOs: boolean
|
||||
}
|
||||
|
||||
export type InterfaceGateway = {
|
||||
|
||||
@@ -108,7 +108,9 @@ export class ServiceInterfaceItemComponent {
|
||||
}
|
||||
|
||||
get href() {
|
||||
return this.config.launchableAddress(this.info, this.pkg.hosts)
|
||||
const host = this.pkg.hosts[this.info.addressInfo.hostId]
|
||||
if (!host) return ''
|
||||
return this.config.launchableAddress(this.info, host)
|
||||
}
|
||||
|
||||
openUI() {
|
||||
|
||||
@@ -86,7 +86,9 @@ export class UILaunchComponent {
|
||||
}
|
||||
|
||||
getHref(ui: T.ServiceInterface): string {
|
||||
return this.config.launchableAddress(ui, this.pkg.hosts)
|
||||
const host = this.pkg.hosts[ui.addressInfo.hostId]
|
||||
if (!host) return ''
|
||||
return this.config.launchableAddress(ui, host)
|
||||
}
|
||||
|
||||
openUI(ui: T.ServiceInterface) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import { TuiBadge, TuiBreadcrumbs } from '@taiga-ui/kit'
|
||||
import { TuiHeader } from '@taiga-ui/layout'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component'
|
||||
import { getAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { TitleDirective } from 'src/app/services/title.service'
|
||||
@@ -120,10 +119,11 @@ export default class ServiceInterfaceRoute {
|
||||
|
||||
return {
|
||||
...item,
|
||||
addresses: getAddresses(item, host, this.config),
|
||||
addresses: this.config.getAddresses(item, host),
|
||||
gateways: [],
|
||||
torDomains: [],
|
||||
clearnetDomains: [],
|
||||
isOs: false,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import { TitleDirective } from 'src/app/services/title.service'
|
||||
</hgroup>
|
||||
</header>
|
||||
@if (ui(); as ui) {
|
||||
<service-interface [value]="ui" [isRunning]="true" [osUi]="true" />
|
||||
<service-interface [value]="ui" [isRunning]="true" />
|
||||
}
|
||||
`,
|
||||
host: { class: 'g-subpage' },
|
||||
@@ -90,6 +90,7 @@ export default class StartOsUiComponent {
|
||||
],
|
||||
torDomains: [],
|
||||
clearnetDomains: [],
|
||||
isOs: true,
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Inject, Injectable, DOCUMENT } from '@angular/core'
|
||||
import { WorkspaceConfig } from '@start9labs/shared'
|
||||
import { T, utils } from '@start9labs/start-sdk'
|
||||
import { PackageDataEntry } from './patch-db/data-model'
|
||||
|
||||
const {
|
||||
gitHash,
|
||||
@@ -32,25 +31,23 @@ export class ConfigService {
|
||||
return useMocks ? mocks.maskAs === 'tor' : this.hostname.endsWith('.onion')
|
||||
}
|
||||
|
||||
isLocal(): boolean {
|
||||
return useMocks
|
||||
? mocks.maskAs === 'local'
|
||||
: this.hostname.endsWith('.local')
|
||||
}
|
||||
|
||||
isLocalhost(): boolean {
|
||||
return useMocks
|
||||
? mocks.maskAs === 'localhost'
|
||||
: this.hostname === 'localhost' || this.hostname === '127.0.0.1'
|
||||
}
|
||||
|
||||
isIpv4(): boolean {
|
||||
return useMocks
|
||||
? mocks.maskAs === 'ipv4'
|
||||
: new RegExp(utils.Patterns.ipv4.regex).test(this.hostname)
|
||||
isLanHttp(): boolean {
|
||||
return !this.isTor() && !this.isLocalhost() && !this.isHttps()
|
||||
}
|
||||
|
||||
isLanIpv4(): boolean {
|
||||
private isLocal(): boolean {
|
||||
return useMocks
|
||||
? mocks.maskAs === 'local'
|
||||
: this.hostname.endsWith('.local')
|
||||
}
|
||||
|
||||
private isLanIpv4(): boolean {
|
||||
return useMocks
|
||||
? mocks.maskAs === 'ipv4'
|
||||
: new RegExp(utils.Patterns.ipv4.regex).test(this.hostname) &&
|
||||
@@ -79,177 +76,7 @@ export class ConfigService {
|
||||
!this.isIpv6()
|
||||
}
|
||||
|
||||
isLanHttp(): boolean {
|
||||
return !this.isTor() && !this.isLocalhost() && !this.isHttps()
|
||||
}
|
||||
|
||||
isHttps(): boolean {
|
||||
return useMocks ? mocks.maskAsHttps : this.protocol === 'https:'
|
||||
}
|
||||
|
||||
isSecure(): boolean {
|
||||
return window.isSecureContext || this.isTor()
|
||||
}
|
||||
|
||||
isLaunchable(
|
||||
state: T.PackageState['state'],
|
||||
status: T.MainStatus['main'],
|
||||
): boolean {
|
||||
return state === 'installed' && status === 'running'
|
||||
}
|
||||
|
||||
/** ${scheme}://${username}@${host}:${externalPort}${suffix} */
|
||||
launchableAddress(ui: T.ServiceInterface, hosts: T.Hosts): string {
|
||||
const host = hosts[ui.addressInfo.hostId]
|
||||
|
||||
if (!host) return ''
|
||||
|
||||
let hostnameInfo = host.hostnameInfo[ui.addressInfo.internalPort]
|
||||
hostnameInfo =
|
||||
hostnameInfo?.filter(
|
||||
h =>
|
||||
this.isLocalhost() ||
|
||||
h.kind !== 'ip' ||
|
||||
h.hostname.kind !== 'ipv6' ||
|
||||
!h.hostname.value.startsWith('fe80::'),
|
||||
) || []
|
||||
if (this.isLocalhost()) {
|
||||
const local = hostnameInfo.find(
|
||||
h => h.kind === 'ip' && h.hostname.kind === 'local',
|
||||
)
|
||||
if (local) {
|
||||
hostnameInfo.unshift({
|
||||
kind: 'ip',
|
||||
gatewayId: 'lo',
|
||||
public: false,
|
||||
hostname: {
|
||||
kind: 'local',
|
||||
port: local.hostname.port,
|
||||
sslPort: local.hostname.sslPort,
|
||||
value: 'localhost',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!hostnameInfo) return ''
|
||||
|
||||
const addressInfo = ui.addressInfo
|
||||
const username = addressInfo.username ? addressInfo.username + '@' : ''
|
||||
const suffix = addressInfo.suffix || ''
|
||||
const url = new URL(`https://${username}placeholder${suffix}`)
|
||||
const use = (hostname: {
|
||||
value: string
|
||||
port: number | null
|
||||
sslPort: number | null
|
||||
}) => {
|
||||
url.hostname = hostname.value
|
||||
const useSsl =
|
||||
hostname.port && hostname.sslPort ? this.isHttps() : !!hostname.sslPort
|
||||
url.protocol = useSsl
|
||||
? `${addressInfo.sslScheme || 'https'}:`
|
||||
: `${addressInfo.scheme || 'http'}:`
|
||||
const port = useSsl ? hostname.sslPort : hostname.port
|
||||
const omitPort = useSsl
|
||||
? ui.addressInfo.sslScheme === 'https' && port === 443
|
||||
: ui.addressInfo.scheme === 'http' && port === 80
|
||||
if (!omitPort && port) url.port = String(port)
|
||||
}
|
||||
const useFirst = (
|
||||
hostnames: (
|
||||
| {
|
||||
value: string
|
||||
port: number | null
|
||||
sslPort: number | null
|
||||
}
|
||||
| undefined
|
||||
)[],
|
||||
) => {
|
||||
const first = hostnames.find(h => h)
|
||||
if (first) {
|
||||
use(first)
|
||||
}
|
||||
return !!first
|
||||
}
|
||||
|
||||
const ipHostnames = hostnameInfo
|
||||
.filter(h => h.kind === 'ip')
|
||||
.map(h => h.hostname) as T.IpHostname[]
|
||||
const domainHostname = ipHostnames
|
||||
.filter(h => h.kind === 'domain')
|
||||
.map(h => h as T.IpHostname & { kind: 'domain' })
|
||||
.map(h => ({
|
||||
value: h.domain,
|
||||
sslPort: h.sslPort,
|
||||
port: h.port,
|
||||
}))[0]
|
||||
const wanIpHostname = hostnameInfo
|
||||
.filter(h => h.kind === 'ip' && h.public && h.hostname.kind !== 'domain')
|
||||
.map(h => h.hostname as Exclude<T.IpHostname, { kind: 'domain' }>)
|
||||
.map(h => ({
|
||||
value: h.value,
|
||||
sslPort: h.sslPort,
|
||||
port: h.port,
|
||||
}))[0]
|
||||
const onionHostname = hostnameInfo
|
||||
.filter(h => h.kind === 'onion')
|
||||
.map(h => h as T.HostnameInfo & { kind: 'onion' })
|
||||
.map(h => ({
|
||||
value: h.hostname.value,
|
||||
sslPort: h.hostname.sslPort,
|
||||
port: h.hostname.port,
|
||||
}))[0]
|
||||
const localHostname = ipHostnames
|
||||
.filter(h => h.kind === 'local')
|
||||
.map(h => h as T.IpHostname & { kind: 'local' })
|
||||
.map(h => ({ value: h.value, sslPort: h.sslPort, port: h.port }))[0]
|
||||
|
||||
if (this.isClearnet()) {
|
||||
if (
|
||||
!useFirst([domainHostname, wanIpHostname, onionHostname, localHostname])
|
||||
) {
|
||||
return ''
|
||||
}
|
||||
} else if (this.isTor()) {
|
||||
if (
|
||||
!useFirst([onionHostname, domainHostname, wanIpHostname, localHostname])
|
||||
) {
|
||||
return ''
|
||||
}
|
||||
} else if (this.isIpv6()) {
|
||||
const ipv6Hostname = ipHostnames.find(h => h.kind === 'ipv6') as {
|
||||
kind: 'ipv6'
|
||||
value: string
|
||||
scopeId: number
|
||||
port: number | null
|
||||
sslPort: number | null
|
||||
}
|
||||
|
||||
if (!useFirst([ipv6Hostname, localHostname])) {
|
||||
return ''
|
||||
}
|
||||
} else {
|
||||
// ipv4 or .local or localhost
|
||||
|
||||
if (!localHostname) return ''
|
||||
|
||||
use({
|
||||
value: this.hostname,
|
||||
port: localHostname.port,
|
||||
sslPort: localHostname.sslPort,
|
||||
})
|
||||
}
|
||||
|
||||
return url.href
|
||||
}
|
||||
|
||||
getHost(): string {
|
||||
return this.host
|
||||
}
|
||||
}
|
||||
|
||||
export function hasUi(
|
||||
interfaces: PackageDataEntry['serviceInterfaces'],
|
||||
): boolean {
|
||||
return Object.values(interfaces).some(iface => iface.type === 'ui')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user