mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
* update marketplace url to reflect build version * adjust marketplace config * use helper function to compare urls * rework some registry stuff * #2900, #2899, and other registry changes * alpha.1 * trailing / * add startosRegistry * fix migration --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>
257 lines
7.0 KiB
TypeScript
257 lines
7.0 KiB
TypeScript
import { DOCUMENT } from '@angular/common'
|
|
import { Inject, Injectable } from '@angular/core'
|
|
import { WorkspaceConfig } from '@start9labs/shared'
|
|
import { T, utils } from '@start9labs/start-sdk'
|
|
import { PackageDataEntry } from './patch-db/data-model'
|
|
|
|
const {
|
|
gitHash,
|
|
useMocks,
|
|
ui: { api, mocks },
|
|
} = require('../../../../../config.json') as WorkspaceConfig
|
|
|
|
@Injectable({
|
|
providedIn: 'root',
|
|
})
|
|
export class ConfigService {
|
|
constructor(@Inject(DOCUMENT) private readonly document: Document) {}
|
|
|
|
hostname = this.document.location.hostname
|
|
// includes port
|
|
host = this.document.location.host
|
|
// includes ":" (e.g. "http:")
|
|
protocol = this.document.location.protocol
|
|
version = require('../../../../../package.json').version as string
|
|
useMocks = useMocks
|
|
mocks = mocks
|
|
gitHash = gitHash
|
|
api = api
|
|
skipStartupAlerts = useMocks && mocks.skipStartupAlerts
|
|
supportsWebSockets = !!window.WebSocket
|
|
|
|
isTor(): boolean {
|
|
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)
|
|
}
|
|
|
|
isLanIpv4(): boolean {
|
|
return useMocks
|
|
? mocks.maskAs === 'ipv4'
|
|
: new RegExp(utils.Patterns.ipv4.regex).test(this.hostname) &&
|
|
(this.hostname.startsWith('192.168.') ||
|
|
this.hostname.startsWith('10.') ||
|
|
(this.hostname.startsWith('172.') &&
|
|
!![this.hostname.split('.').map(Number)[1] || NaN].filter(
|
|
n => n >= 16 && n < 32,
|
|
).length))
|
|
}
|
|
|
|
isIpv6(): boolean {
|
|
return useMocks
|
|
? mocks.maskAs === 'ipv6'
|
|
: new RegExp(utils.Patterns.ipv6.regex).test(this.hostname)
|
|
}
|
|
|
|
isClearnet(): boolean {
|
|
return useMocks
|
|
? mocks.maskAs === 'clearnet'
|
|
: this.isHttps() &&
|
|
!this.isTor() &&
|
|
!this.isLocal() &&
|
|
!this.isLocalhost() &&
|
|
!this.isLanIpv4() &&
|
|
!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',
|
|
networkInterfaceId: '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')
|
|
}
|