mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
chore: flatten HostnameInfo from enum to struct
HostnameInfo only had one variant (Ip) after removing Tor. Flatten it into a plain struct with fields gateway, public, hostname. Remove all kind === 'ip' type guards and narrowing across SDK, frontend, and container runtime. Update DB migration to strip the kind field.
This commit is contained in:
@@ -93,7 +93,7 @@ export class DockerProcedureContainer extends Drop {
|
||||
)?.hostnameInfo || {},
|
||||
)
|
||||
.flatMap((h) => h)
|
||||
.flatMap((h) => (h.kind === "onion" ? [h.hostname.value] : [])),
|
||||
.map((h) => h.hostname.value),
|
||||
).values(),
|
||||
]
|
||||
const certChain = await effects.getSslCertificate({
|
||||
|
||||
@@ -1244,12 +1244,8 @@ async function updateConfig(
|
||||
? ""
|
||||
: catchFn(
|
||||
() =>
|
||||
(specValue.target === "lan-address"
|
||||
? filled.addressInfo!.filter({ kind: "mdns" }) ||
|
||||
filled.addressInfo!.onion
|
||||
: filled.addressInfo!.onion ||
|
||||
filled.addressInfo!.filter({ kind: "mdns" })
|
||||
).hostnames[0].hostname.value,
|
||||
filled.addressInfo!.filter({ kind: "mdns" })!
|
||||
.hostnames[0].hostname.value,
|
||||
) || ""
|
||||
mutConfigValue[key] = url
|
||||
}
|
||||
|
||||
@@ -481,7 +481,7 @@ impl NetServiceData {
|
||||
i.device_type != Some(NetworkInterfaceType::Wireguard)
|
||||
})
|
||||
{
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
bind_hostname_info.push(HostnameInfo {
|
||||
gateway: gateway.clone(),
|
||||
public: false,
|
||||
hostname: IpHostname::Local {
|
||||
@@ -514,7 +514,7 @@ impl NetServiceData {
|
||||
.as_ref()
|
||||
.map_or(false, |ssl| ssl.preferred_external_port == 443)
|
||||
{
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
bind_hostname_info.push(HostnameInfo {
|
||||
gateway: gateway.clone(),
|
||||
public,
|
||||
hostname: IpHostname::Domain {
|
||||
@@ -524,7 +524,7 @@ impl NetServiceData {
|
||||
},
|
||||
});
|
||||
} else {
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
bind_hostname_info.push(HostnameInfo {
|
||||
gateway: gateway.clone(),
|
||||
public,
|
||||
hostname: IpHostname::Domain {
|
||||
@@ -540,7 +540,7 @@ impl NetServiceData {
|
||||
if let Some(ip_info) = &info.ip_info {
|
||||
let public = info.public();
|
||||
if let Some(wan_ip) = ip_info.wan_ip {
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
bind_hostname_info.push(HostnameInfo {
|
||||
gateway: gateway.clone(),
|
||||
public: true,
|
||||
hostname: IpHostname::Ipv4 {
|
||||
@@ -554,7 +554,7 @@ impl NetServiceData {
|
||||
match ipnet {
|
||||
IpNet::V4(net) => {
|
||||
if !public {
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
bind_hostname_info.push(HostnameInfo {
|
||||
gateway: gateway.clone(),
|
||||
public,
|
||||
hostname: IpHostname::Ipv4 {
|
||||
@@ -566,7 +566,7 @@ impl NetServiceData {
|
||||
}
|
||||
}
|
||||
IpNet::V6(net) => {
|
||||
bind_hostname_info.push(HostnameInfo::Ip {
|
||||
bind_hostname_info.push(HostnameInfo {
|
||||
gateway: gateway.clone(),
|
||||
public: public && !ipv6_is_local(net.addr()),
|
||||
hostname: IpHostname::Ipv6 {
|
||||
|
||||
@@ -9,20 +9,14 @@ use crate::{GatewayId, HostId, ServiceInterfaceId};
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all_fields = "camelCase")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum HostnameInfo {
|
||||
Ip {
|
||||
gateway: GatewayInfo,
|
||||
public: bool,
|
||||
hostname: IpHostname,
|
||||
},
|
||||
pub struct HostnameInfo {
|
||||
pub gateway: GatewayInfo,
|
||||
pub public: bool,
|
||||
pub hostname: IpHostname,
|
||||
}
|
||||
impl HostnameInfo {
|
||||
pub fn to_san_hostname(&self) -> InternedString {
|
||||
match self {
|
||||
Self::Ip { hostname, .. } => hostname.to_san_hostname(),
|
||||
}
|
||||
self.hostname.to_san_hostname()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ impl VersionT for Version {
|
||||
}
|
||||
|
||||
// Remove onion entries from hostnameInfo in server host
|
||||
remove_onion_hostname_info(
|
||||
migrate_hostname_info(
|
||||
db.get_mut("public")
|
||||
.and_then(|p| p.get_mut("serverInfo"))
|
||||
.and_then(|s| s.get_mut("network"))
|
||||
@@ -74,7 +74,7 @@ impl VersionT for Version {
|
||||
for (_, package) in packages.iter_mut() {
|
||||
if let Some(hosts) = package.get_mut("hosts").and_then(|h| h.as_object_mut()) {
|
||||
for (_, host) in hosts.iter_mut() {
|
||||
remove_onion_hostname_info(Some(host));
|
||||
migrate_hostname_info(Some(host));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,14 +96,21 @@ impl VersionT for Version {
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_onion_hostname_info(host: Option<&mut Value>) {
|
||||
fn migrate_hostname_info(host: Option<&mut Value>) {
|
||||
if let Some(hostname_info) = host
|
||||
.and_then(|h| h.get_mut("hostnameInfo"))
|
||||
.and_then(|h| h.as_object_mut())
|
||||
{
|
||||
for (_, infos) in hostname_info.iter_mut() {
|
||||
if let Some(arr) = infos.as_array_mut() {
|
||||
// Remove onion entries
|
||||
arr.retain(|info| info.get("kind").and_then(|k| k.as_str()) != Some("onion"));
|
||||
// Strip "kind" field from remaining entries (HostnameInfo flattened from enum to struct)
|
||||
for info in arr.iter_mut() {
|
||||
if let Some(obj) = info.as_object_mut() {
|
||||
obj.remove("kind");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { GatewayInfo } from './GatewayInfo'
|
||||
import type { IpHostname } from './IpHostname'
|
||||
|
||||
export type HostnameInfo = {
|
||||
kind: 'ip'
|
||||
gateway: GatewayInfo
|
||||
public: boolean
|
||||
hostname: IpHostname
|
||||
|
||||
@@ -43,19 +43,19 @@ type VisibilityFilter<V extends 'public' | 'private'> = V extends 'public'
|
||||
: never
|
||||
type KindFilter<K extends FilterKinds> = K extends 'mdns'
|
||||
?
|
||||
| (HostnameInfo & { kind: 'ip'; hostname: { kind: 'local' } })
|
||||
| (HostnameInfo & { hostname: { kind: 'local' } })
|
||||
| KindFilter<Exclude<K, 'mdns'>>
|
||||
: K extends 'domain'
|
||||
?
|
||||
| (HostnameInfo & { kind: 'ip'; hostname: { kind: 'domain' } })
|
||||
| (HostnameInfo & { hostname: { kind: 'domain' } })
|
||||
| KindFilter<Exclude<K, 'domain'>>
|
||||
: K extends 'ipv4'
|
||||
?
|
||||
| (HostnameInfo & { kind: 'ip'; hostname: { kind: 'ipv4' } })
|
||||
| (HostnameInfo & { hostname: { kind: 'ipv4' } })
|
||||
| KindFilter<Exclude<K, 'ipv4'>>
|
||||
: K extends 'ipv6'
|
||||
?
|
||||
| (HostnameInfo & { kind: 'ip'; hostname: { kind: 'ipv6' } })
|
||||
| (HostnameInfo & { hostname: { kind: 'ipv6' } })
|
||||
| KindFilter<Exclude<K, 'ipv6'>>
|
||||
: K extends 'ip'
|
||||
? KindFilter<Exclude<K, 'ip'> | 'ipv4' | 'ipv6'>
|
||||
@@ -154,16 +154,14 @@ export const addressHostToUrl = (
|
||||
scheme in knownProtocols &&
|
||||
port === knownProtocols[scheme as keyof typeof knownProtocols].defaultPort
|
||||
let hostname
|
||||
if (host.kind === 'ip') {
|
||||
if (host.hostname.kind === 'domain') {
|
||||
hostname = host.hostname.value
|
||||
} else if (host.hostname.kind === 'ipv6') {
|
||||
hostname = IPV6_LINK_LOCAL.contains(host.hostname.value)
|
||||
? `[${host.hostname.value}%${host.hostname.scopeId}]`
|
||||
: `[${host.hostname.value}]`
|
||||
} else {
|
||||
hostname = host.hostname.value
|
||||
}
|
||||
if (host.hostname.kind === 'domain') {
|
||||
hostname = host.hostname.value
|
||||
} else if (host.hostname.kind === 'ipv6') {
|
||||
hostname = IPV6_LINK_LOCAL.contains(host.hostname.value)
|
||||
? `[${host.hostname.value}%${host.hostname.scopeId}]`
|
||||
: `[${host.hostname.value}]`
|
||||
} else {
|
||||
hostname = host.hostname.value
|
||||
}
|
||||
return `${scheme ? `${scheme}://` : ''}${
|
||||
username ? `${username}@` : ''
|
||||
@@ -205,16 +203,13 @@ function filterRec(
|
||||
hostnames = hostnames.filter(
|
||||
(h) =>
|
||||
invert !==
|
||||
((kind.has('mdns') && h.kind === 'ip' && h.hostname.kind === 'local') ||
|
||||
(kind.has('domain') &&
|
||||
h.kind === 'ip' &&
|
||||
h.hostname.kind === 'domain') ||
|
||||
(kind.has('ipv4') && h.kind === 'ip' && h.hostname.kind === 'ipv4') ||
|
||||
(kind.has('ipv6') && h.kind === 'ip' && h.hostname.kind === 'ipv6') ||
|
||||
((kind.has('mdns') && h.hostname.kind === 'local') ||
|
||||
(kind.has('domain') && h.hostname.kind === 'domain') ||
|
||||
(kind.has('ipv4') && h.hostname.kind === 'ipv4') ||
|
||||
(kind.has('ipv6') && h.hostname.kind === 'ipv6') ||
|
||||
(kind.has('localhost') &&
|
||||
['localhost', '127.0.0.1', '::1'].includes(h.hostname.value)) ||
|
||||
(kind.has('link-local') &&
|
||||
h.kind === 'ip' &&
|
||||
h.hostname.kind === 'ipv6' &&
|
||||
IPV6_LINK_LOCAL.contains(IpAddress.parse(h.hostname.value)))),
|
||||
)
|
||||
|
||||
@@ -27,9 +27,9 @@ function cmpWithRankedPredicates<T extends AddressWithInfo>(
|
||||
return 0
|
||||
}
|
||||
|
||||
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, [
|
||||
@@ -45,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, [
|
||||
@@ -68,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,
|
||||
@@ -134,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({
|
||||
@@ -266,10 +259,9 @@ export class InterfaceService {
|
||||
h =>
|
||||
this.config.accessType === 'localhost' ||
|
||||
!(
|
||||
h.kind === 'ip' &&
|
||||
((h.hostname.kind === 'ipv6' &&
|
||||
(h.hostname.kind === 'ipv6' &&
|
||||
utils.IPV6_LINK_LOCAL.contains(h.hostname.value)) ||
|
||||
h.gateway.id === 'lo')
|
||||
h.gateway.id === 'lo'
|
||||
),
|
||||
) || []
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user