fix patch db types and comment out future domain and proxy features

This commit is contained in:
Matt Hill
2025-02-11 21:17:20 -07:00
parent 7d1096dbd8
commit ce2842d365
12 changed files with 259 additions and 297 deletions

View File

@@ -28,7 +28,7 @@ export class NotificationsToastComponent {
readonly visible$: Observable<boolean> = merge( readonly visible$: Observable<boolean> = merge(
this.dismiss$, this.dismiss$,
inject<PatchDB<DataModel>>(PatchDB) inject<PatchDB<DataModel>>(PatchDB)
.watch$('serverInfo', 'unreadNotifications', 'count') .watch$('serverInfo', 'unreadNotificationCount')
.pipe( .pipe(
pairwise(), pairwise(),
map(([prev, cur]) => cur > prev), map(([prev, cur]) => cur > prev),

View File

@@ -22,14 +22,14 @@ export const REMOVE: Partial<TuiDialogOptions<TuiConfirmData>> = {
export function getClearnetSpec({ export function getClearnetSpec({
domains, domains,
start9ToSubdomain, start9To,
}: NetworkInfo): Promise<IST.InputSpec> { }: NetworkInfo): Promise<IST.InputSpec> {
const start9ToDomain = `${start9ToSubdomain?.value}.start9.to` const start9ToDomain = `${start9To?.subdomain}.start9.to`
const base = start9ToSubdomain ? { [start9ToDomain]: start9ToDomain } : {} const base = start9To ? { [start9ToDomain]: start9ToDomain } : {}
const values = domains.reduce((prev, curr) => { const values = Object.keys(domains).reduce((prev, curr) => {
return { return {
[curr.value]: curr.value, [curr]: curr,
...prev, ...prev,
} }
}, base) }, base)
@@ -38,7 +38,7 @@ export function getClearnetSpec({
ISB.InputSpec.of({ ISB.InputSpec.of({
domain: ISB.Value.select({ domain: ISB.Value.select({
name: 'Domain', name: 'Domain',
default: domains[0].value, default: '',
values, values,
}), }),
subdomain: ISB.Value.text({ subdomain: ISB.Value.text({

View File

@@ -68,23 +68,14 @@ export class SettingsDomainsComponent {
private readonly api = inject(ApiService) private readonly api = inject(ApiService)
private readonly dialogs = inject(TuiDialogService) private readonly dialogs = inject(TuiDialogService)
readonly domains$ = this.patch.watch$('serverInfo', 'network').pipe( private readonly start9To$ = this.patch.watch$(
map(network => { 'serverInfo',
const start9ToSubdomain = network.start9ToSubdomain 'network',
const start9To = !start9ToSubdomain 'start9To',
? []
: [
{
...start9ToSubdomain,
value: `${start9ToSubdomain.value}.start9.to`,
provider: 'Start9',
},
]
return { start9To, custom: network.domains }
}),
) )
readonly domains$ = this.patch.watch$('serverInfo', 'network', 'domains')
delete(hostname?: string) { delete(hostname?: string) {
this.dialogs this.dialogs
.open(TUI_CONFIRM, { .open(TUI_CONFIRM, {

View File

@@ -46,12 +46,12 @@ export class StartOsUiComponent {
readonly ui$: Observable<MappedServiceInterface> = inject<PatchDB<DataModel>>( readonly ui$: Observable<MappedServiceInterface> = inject<PatchDB<DataModel>>(
PatchDB, PatchDB,
) )
.watch$('serverInfo') .watch$('serverInfo', 'network', 'host')
.pipe( .pipe(
map(server => ({ map(host => ({
...iface, ...iface,
public: server.host.bindings[iface.addressInfo.internalPort].net.public, public: host.bindings[iface.addressInfo.internalPort].net.public,
addresses: getAddresses(iface, server.host, this.config), addresses: getAddresses(iface, host, this.config),
})), })),
) )
} }

View File

@@ -19,27 +19,27 @@ export default [
m => m.SettingsEmailComponent, m => m.SettingsEmailComponent,
), ),
}, },
{ // {
path: 'domains', // path: 'domains',
loadComponent: () => // loadComponent: () =>
import('./routes/domains/domains.component').then( // import('./routes/domains/domains.component').then(
m => m.SettingsDomainsComponent, // m => m.SettingsDomainsComponent,
), // ),
}, // },
{ // {
path: 'proxies', // path: 'proxies',
loadComponent: () => // loadComponent: () =>
import('./routes/proxies/proxies.component').then( // import('./routes/proxies/proxies.component').then(
m => m.SettingsProxiesComponent, // m => m.SettingsProxiesComponent,
), // ),
}, // },
{ // {
path: 'router', // path: 'router',
loadComponent: () => // loadComponent: () =>
import('./routes/router/router.component').then( // import('./routes/router/router.component').then(
m => m.SettingsRouterComponent, // m => m.SettingsRouterComponent,
), // ),
}, // },
{ {
path: 'wifi', path: 'wifi',
loadComponent: () => loadComponent: () =>

View File

@@ -63,24 +63,24 @@ export class SettingsService {
}, },
], ],
Network: [ Network: [
{ // {
title: 'Domains', // title: 'Domains',
description: 'Manage domains for clearnet connectivity', // description: 'Manage domains for clearnet connectivity',
icon: '@tui.globe', // icon: '@tui.globe',
routerLink: 'domains', // routerLink: 'domains',
}, // },
{ // {
title: 'Proxies', // title: 'Proxies',
description: 'Manage proxies for inbound and outbound connections', // description: 'Manage proxies for inbound and outbound connections',
icon: '@tui.shuffle', // icon: '@tui.shuffle',
routerLink: 'proxies', // routerLink: 'proxies',
}, // },
{ // {
title: 'Router Config', // title: 'Router Config',
description: 'Connect or configure your router for clearnet', // description: 'Connect or configure your router for clearnet',
icon: '@tui.radio', // icon: '@tui.radio',
routerLink: 'router', // routerLink: 'router',
}, // },
{ {
title: 'WiFi', title: 'WiFi',
description: 'Add or remove WiFi networks', description: 'Add or remove WiFi networks',
@@ -109,12 +109,12 @@ export class SettingsService {
}, },
], ],
'Privacy and Security': [ 'Privacy and Security': [
{ // {
title: 'Outbound Proxy', // title: 'Outbound Proxy',
description: 'Proxy outbound traffic from the StartOS main process', // description: 'Proxy outbound traffic from the StartOS main process',
icon: '@tui.shield', // icon: '@tui.shield',
action: () => this.setOutboundProxy(), // action: () => this.setOutboundProxy(),
}, // },
{ {
title: 'SSH', title: 'SSH',
description: description:
@@ -154,12 +154,12 @@ export class SettingsService {
], ],
} }
private async setOutboundProxy(): Promise<void> { // private async setOutboundProxy(): Promise<void> {
const proxy = await firstValueFrom( // const proxy = await firstValueFrom(
this.patch.watch$('serverInfo', 'network', 'outboundProxy'), // this.patch.watch$('serverInfo', 'network', 'outboundProxy'),
) // )
await this.proxyService.presentModalSetOutboundProxy(proxy) // await this.proxyService.presentModalSetOutboundProxy(proxy)
} // }
private promptResetTor() { private promptResetTor() {
this.wipe = false this.wipe = false

View File

@@ -1,7 +1,4 @@
import { import { DomainInfo } from 'src/app/services/patch-db/data-model'
DomainInfo,
NetworkStrategy,
} from 'src/app/services/patch-db/data-model'
import { FetchLogsReq, FetchLogsRes } from '@start9labs/shared' import { FetchLogsReq, FetchLogsRes } from '@start9labs/shared'
import { Dump } from 'patch-db-client' import { Dump } from 'patch-db-client'
import { DataModel } from 'src/app/services/patch-db/data-model' import { DataModel } from 'src/app/services/patch-db/data-model'
@@ -184,7 +181,7 @@ export module RR {
// domains // domains
export type ClaimStart9ToReq = { networkStrategy: NetworkStrategy } // net.domain.me.claim export type ClaimStart9ToReq = { networkInterfaceId: string } // net.domain.me.claim
export type ClaimStart9ToRes = null export type ClaimStart9ToRes = null
export type DeleteStart9ToReq = {} // net.domain.me.delete export type DeleteStart9ToReq = {} // net.domain.me.delete
@@ -197,7 +194,7 @@ export module RR {
username: string | null username: string | null
password: string | null password: string | null
} }
networkStrategy: NetworkStrategy networkInterfaceId: string
} // net.domain.add } // net.domain.add
export type AddDomainRes = null export type AddDomainRes = null
@@ -677,10 +674,10 @@ export type ServerNotification<T extends number> = {
export type NotificationData<T> = T extends 0 export type NotificationData<T> = T extends 0
? null ? null
: T extends 1 : T extends 1
? BackupReport ? BackupReport
: T extends 2 : T extends 2
? string ? string
: any : any
export type BackupReport = { export type BackupReport = {
server: { server: {

View File

@@ -13,7 +13,6 @@ import {
import { import {
InstallingState, InstallingState,
PackageDataEntry, PackageDataEntry,
Proxy,
StateInfo, StateInfo,
UpdatingState, UpdatingState,
} from 'src/app/services/patch-db/data-model' } from 'src/app/services/patch-db/data-model'
@@ -574,25 +573,22 @@ export class MockApiService extends ApiService {
async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> { async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
await pauseFor(2000) await pauseFor(2000)
const type: Proxy['type'] = 'inbound-outbound'
const patch = [ const patch = [
{ {
op: PatchOp.REPLACE, op: PatchOp.ADD,
path: '/serverInfo/network/proxies', path: `/serverInfo/network/networkInterfaces/wga1`,
value: [ value: {
{ inbound: true,
id: 'abcd-efgh-ijkl-mnop', outbound: true,
ipInfo: {
name: params.name, name: params.name,
createdAt: new Date(), scopeId: 3,
type, deviceType: 'wireguard',
endpoint: '10.25.2.17', subnets: [],
usedBy: { wanIp: '1.1.1.1',
domains: [], ntpServers: [],
services: [],
},
}, },
], },
}, },
] ]
this.mockRevision(patch) this.mockRevision(patch)
@@ -639,12 +635,10 @@ export class MockApiService extends ApiService {
const patch = [ const patch = [
{ {
op: PatchOp.REPLACE, op: PatchOp.REPLACE,
path: '/serverInfo/network/start9ToSubdomain', path: '/serverInfo/network/start9To',
value: { value: {
value: 'xyz', subdomain: 'xyz',
createdAt: new Date(), networkInterfaceId: params.networkInterfaceId,
networkStrategy: params.networkStrategy,
usedBy: [],
}, },
}, },
] ]
@@ -660,7 +654,7 @@ export class MockApiService extends ApiService {
const patch = [ const patch = [
{ {
op: PatchOp.REPLACE, op: PatchOp.REPLACE,
path: '/serverInfo/network/start9ToSubdomain', path: '/serverInfo/network/start9To',
value: null, value: null,
}, },
] ]
@@ -675,16 +669,13 @@ export class MockApiService extends ApiService {
const patch = [ const patch = [
{ {
op: PatchOp.REPLACE, op: PatchOp.REPLACE,
path: '/serverInfo/network/domains', path: `/serverInfo/network/domains`,
value: [ value: {
{ [params.hostname]: {
value: params.hostname, networkInterfaceId: params.networkInterfaceId,
createdAt: new Date(),
provider: params.provider.name, provider: params.provider.name,
networkStrategy: params.networkStrategy,
usedBy: [],
}, },
], },
}, },
] ]
this.mockRevision(patch) this.mockRevision(patch)
@@ -698,7 +689,7 @@ export class MockApiService extends ApiService {
{ {
op: PatchOp.REPLACE, op: PatchOp.REPLACE,
path: '/serverInfo/network/domains', path: '/serverInfo/network/domains',
value: [], value: {},
}, },
] ]
this.mockRevision(patch) this.mockRevision(patch)

View File

@@ -39,37 +39,142 @@ export const mockPatchData: DataModel = {
selected: null, selected: null,
lastRegion: null, lastRegion: null,
}, },
start9ToSubdomain: null, start9To: null,
domains: [], domains: {},
wanConfig: { wanConfig: {
upnp: true, upnp: true,
forwards: [], forwards: [],
}, },
proxies: [],
outboundProxy: null, outboundProxy: null,
}, host: {
networkInterfaces: { bindings: {
eth0: { 80: {
public: false, enabled: true,
ipInfo: { net: {
scopeId: 1, assignedPort: null,
deviceType: 'ethernet', assignedSslPort: 443,
subnets: ['10.0.0.2/24'], public: false,
wanIp: null, },
ntpServers: [], options: {
preferredExternalPort: 80,
addSsl: {
preferredExternalPort: 443,
alpn: { specified: ['http/1.1', 'h2'] },
},
secure: null,
},
},
},
domains: {},
onions: ['myveryownspecialtoraddress'],
hostnameInfo: {
80: [
{
kind: 'ip',
networkInterfaceId: 'eth0',
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'wlan0',
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'eth0',
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.1',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'wlan0',
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'eth0',
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd',
scopeId: 2,
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'wlan0',
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,
},
},
],
}, },
}, },
wlan0: { networkInterfaces: {
public: false, eth0: {
ipInfo: { inbound: false,
scopeId: 2, outbound: true,
deviceType: 'wireless', ipInfo: {
subnets: [ name: 'Wired Connection 1',
'10.0.90.12/24', scopeId: 1,
'fe80::cd00:0000:0cde:1257:0000:211e:72cd/64', deviceType: 'ethernet',
], subnets: ['10.0.0.2/24'],
wanIp: null, wanIp: null,
ntpServers: [], ntpServers: [],
},
},
wlan0: {
inbound: false,
outbound: true,
ipInfo: {
name: 'Wireless Connection 1',
scopeId: 2,
deviceType: 'wireless',
subnets: [
'10.0.90.12/24',
'fe80::cd00:0000:0cde:1257:0000:211e:72cd/64',
],
wanIp: null,
ntpServers: [],
},
}, },
}, },
}, },
@@ -78,10 +183,7 @@ export const mockPatchData: DataModel = {
contact: ['mailto:support@start9.com'], contact: ['mailto:support@start9.com'],
}, },
}, },
unreadNotifications: { unreadNotificationCount: 4,
count: 4,
recent: Mock.Notifications,
},
// password is asdfasdf // password is asdfasdf
passwordHash: passwordHash:
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ', '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
@@ -96,108 +198,6 @@ export const mockPatchData: DataModel = {
backupProgress: {}, backupProgress: {},
}, },
hostname: 'random-words', hostname: 'random-words',
host: {
bindings: {
80: {
enabled: true,
net: {
assignedPort: null,
assignedSslPort: 443,
public: false,
},
options: {
preferredExternalPort: 80,
addSsl: {
preferredExternalPort: 443,
alpn: { specified: ['http/1.1', 'h2'] },
},
secure: null,
},
},
},
domains: {},
onions: ['myveryownspecialtoraddress'],
hostnameInfo: {
80: [
{
kind: 'ip',
networkInterfaceId: 'eth0',
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'wlan0',
public: false,
hostname: {
kind: 'local',
value: 'adjective-noun.local',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'eth0',
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.1',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'wlan0',
public: false,
hostname: {
kind: 'ipv4',
value: '10.0.0.2',
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'eth0',
public: false,
hostname: {
kind: 'ipv6',
value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd',
scopeId: 2,
port: null,
sslPort: 443,
},
},
{
kind: 'ip',
networkInterfaceId: 'wlan0',
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,
},
},
],
},
},
pubkey: 'npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m', pubkey: 'npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m',
caFingerprint: 'SHA-256: 63 2B 11 99 44 40 17 DF 37 FC C3 DF 0F 3D 15', caFingerprint: 'SHA-256: 63 2B 11 99 44 40 17 DF 37 FC C3 DF 0F 3D 15',
ntpSynced: false, ntpSynced: false,

View File

@@ -20,7 +20,7 @@ export class NotificationService {
private readonly localUnreadCount$ = new Subject<number>() private readonly localUnreadCount$ = new Subject<number>()
readonly unreadCount$ = merge( readonly unreadCount$ = merge(
this.patch.watch$('serverInfo', 'unreadNotifications', 'count'), this.patch.watch$('serverInfo', 'unreadNotificationCount'),
this.localUnreadCount$, this.localUnreadCount$,
).pipe(shareReplay(1)) ).pipe(shareReplay(1))

View File

@@ -1,17 +1,13 @@
import { BackupJob, ServerNotifications } from '../api/api.types' import { BackupJob } from '../api/api.types'
import { T } from '@start9labs/start-sdk' import { T } from '@start9labs/start-sdk'
export type DataModel = { export type DataModel = {
ui: UIData ui: UIData
serverInfo: Omit< serverInfo: Omit<
T.Public['serverInfo'], T.Public['serverInfo'],
'wifi' | 'unreadNotificationCount' 'wifi' | 'networkInterfaces' | 'host'
> & { > & {
network: NetworkInfo network: NetworkInfo
unreadNotifications: {
count: number
recent: ServerNotifications
}
} }
packageData: Record<string, PackageDataEntry> packageData: Record<string, PackageDataEntry>
} }
@@ -42,14 +38,30 @@ export type UIStore = {
} }
export type NetworkInfo = { export type NetworkInfo = {
wifi: WiFiInfo wifi: T.WifiInfo & { enabled: boolean }
start9ToSubdomain: Omit<Domain, 'provider'> | null host: T.Host
domains: Domain[] networkInterfaces: {
[id: string]: {
inbound: boolean | null
outbound: boolean | null
ipInfo:
| (T.IpInfo & {
name: string
})
| null
}
}
start9To: {
subdomain: string
networkInterfaceId: string
} | null
domains: {
[key: string]: Domain
}
wanConfig: { wanConfig: {
upnp: boolean upnp: boolean
forwards: PortForward[] forwards: PortForward[]
} }
proxies: Proxy[]
outboundProxy: string | null outboundProxy: string | null
} }
@@ -65,40 +77,9 @@ export type PortForward = {
error: string | null error: string | null
} }
export type WiFiInfo = {
enabled: boolean
lastRegion: string | null
interface: string | null
ssids: Array<string>
selected: string | null
}
export type Domain = { export type Domain = {
value: string
createdAt: string
provider: string provider: string
networkStrategy: NetworkStrategy networkInterfaceId: string
usedBy: {
service: { id: string | null; title: string } // null means startos
interfaces: { id: string | null; title: string }[] // null means startos
}[]
}
export type NetworkStrategy =
| { proxy: string }
| { ipStrategy: 'ipv4' | 'ipv6' | 'dualstack' }
export type Proxy = {
id: string
name: string
createdAt: string
type: 'outbound' | 'inbound-outbound' | 'vlan' | { error: string }
endpoint: string
// below is overlay only
usedBy: {
services: { id: string | null; title: string }[] // implies outbound - null means startos
domains: string[] // implies inbound
}
} }
export interface ServerStatusInfo { export interface ServerStatusInfo {

View File

@@ -26,18 +26,20 @@ export class ProxyService {
) {} ) {}
async presentModalSetOutboundProxy(current: string | null, pkgId?: string) { async presentModalSetOutboundProxy(current: string | null, pkgId?: string) {
const network = await firstValueFrom( const networkInterfaces = await firstValueFrom(
this.patch.watch$('serverInfo', 'network'), this.patch.watch$('serverInfo', 'network', 'networkInterfaces'),
) )
const config = ISB.InputSpec.of({ const config = ISB.InputSpec.of({
proxyId: ISB.Value.select({ proxyId: ISB.Value.select({
name: 'Select Proxy', name: 'Select Proxy',
default: current || '', default: current || '',
values: network.proxies values: Object.entries(networkInterfaces)
.filter(p => p.type === 'outbound' || p.type === 'inbound-outbound') .filter(
([_, n]) => n.outbound && n.ipInfo?.deviceType === 'wireguard',
)
.reduce<Record<string, string>>( .reduce<Record<string, string>>(
(prev, curr) => ({ (prev, [id, n]) => ({
[curr.id]: curr.name, [id]: n.ipInfo!.name,
...prev, ...prev,
}), }),
{}, {},