looking good

This commit is contained in:
Matt Hill
2026-02-14 16:37:04 -07:00
parent 3a63f3b840
commit 2f19188dae
22 changed files with 4009 additions and 6738 deletions

View File

@@ -120,6 +120,12 @@ export namespace RR {
} // net.dns.query
export type QueryDnsRes = string | null
export type TestPortForwardReq = {
gateway: string
port: number
} // net.port-forward.test
export type TestPortForwardRes = boolean
export type SetKeyboardReq = FullKeyboard // server.set-keyboard
export type SetKeyboardRes = null

View File

@@ -1,5 +1,3 @@
import { MarketplacePkg } from '@start9labs/marketplace'
import { T } from '@start9labs/start-sdk'
import { RR } from './api.types'
import { WebSocketSubject } from 'rxjs/webSocket'
@@ -119,6 +117,10 @@ export abstract class ApiService {
abstract queryDns(params: RR.QueryDnsReq): Promise<RR.QueryDnsRes>
abstract testPortForward(
params: RR.TestPortForwardReq,
): Promise<RR.TestPortForwardRes>
// smtp
abstract setSmtp(params: RR.SetSMTPReq): Promise<RR.SetSMTPRes>

View File

@@ -1,6 +1,5 @@
import { DOCUMENT, Inject, Injectable } from '@angular/core'
import { blake3 } from '@noble/hashes/blake3'
import { MarketplacePkg } from '@start9labs/marketplace'
import {
HttpOptions,
HttpService,
@@ -8,7 +7,6 @@ import {
RpcError,
RPCOptions,
} from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { Dump, pathFromArray } from 'patch-db-client'
import { filter, firstValueFrom, Observable } from 'rxjs'
import { webSocket, WebSocketSubject } from 'rxjs/webSocket'
@@ -268,6 +266,15 @@ export class LiveApiService extends ApiService {
})
}
async testPortForward(
params: RR.TestPortForwardReq,
): Promise<RR.TestPortForwardRes> {
return this.rpcRequest({
method: 'net.port-forward.test',
params,
})
}
// marketplace URLs
async checkOSUpdate(
@@ -358,7 +365,10 @@ export class LiveApiService extends ApiService {
async setDefaultOutbound(
params: RR.SetDefaultOutboundReq,
): Promise<RR.SetDefaultOutboundRes> {
return this.rpcRequest({ method: 'net.gateway.set-default-outbound', params })
return this.rpcRequest({
method: 'net.gateway.set-default-outbound',
params,
})
}
async setServiceOutbound(

View File

@@ -483,6 +483,14 @@ export class MockApiService extends ApiService {
return null
}
async testPortForward(
params: RR.TestPortForwardReq,
): Promise<RR.TestPortForwardRes> {
await pauseFor(2000)
return false
}
// marketplace URLs
async checkOSUpdate(
@@ -589,7 +597,7 @@ export class MockApiService extends ApiService {
]
if (params.setAsDefaultOutbound) {
;(patch as any[]).push({
(patch as any[]).push({
op: PatchOp.REPLACE,
path: '/serverInfo/network/defaultOutbound',
value: id,
@@ -1394,7 +1402,9 @@ export class MockApiService extends ApiService {
): Promise<RR.ServerBindingSetAddressEnabledRes> {
await pauseFor(2000)
// Mock: no-op since address enable/disable modifies DerivedAddressInfo sets
const basePath = `/serverInfo/network/host/bindings/${params.internalPort}/addresses`
this.mockSetAddressEnabled(basePath, params.address, params.enabled)
return null
}
@@ -1493,7 +1503,9 @@ export class MockApiService extends ApiService {
): Promise<RR.PkgBindingSetAddressEnabledRes> {
await pauseFor(2000)
// Mock: no-op since address enable/disable modifies DerivedAddressInfo sets
const basePath = `/packageData/${params.package}/hosts/${params.host}/bindings/${params.internalPort}/addresses`
this.mockSetAddressEnabled(basePath, params.address, params.enabled)
return null
}
@@ -1813,6 +1825,63 @@ export class MockApiService extends ApiService {
}, 1000)
}
private mockSetAddressEnabled(
basePath: string,
addressJson: string,
enabled: boolean | null,
): void {
const h: T.HostnameInfo = JSON.parse(addressJson)
const isPublicIp =
h.public && (h.metadata.kind === 'ipv4' || h.metadata.kind === 'ipv6')
const current = this.mockData(basePath) as T.DerivedAddressInfo
if (isPublicIp) {
if (h.port === null) return
const sa =
h.metadata.kind === 'ipv6'
? `[${h.host}]:${h.port}`
: `${h.host}:${h.port}`
const arr = [...current.enabled]
if (enabled) {
if (!arr.includes(sa)) arr.push(sa)
} else {
const idx = arr.indexOf(sa)
if (idx >= 0) arr.splice(idx, 1)
}
current.enabled = arr
this.mockRevision([
{ op: PatchOp.REPLACE, path: `${basePath}/enabled`, value: arr },
])
} else {
const port = h.port ?? 0
const arr = current.disabled.filter(
([dHost, dPort]) => !(dHost === h.host && dPort === port),
)
if (!enabled) {
arr.push([h.host, port])
}
current.disabled = arr
this.mockRevision([
{ op: PatchOp.REPLACE, path: `${basePath}/disabled`, value: arr },
])
}
}
private mockData(path: string): any {
const parts = path.split('/').filter(Boolean)
let obj: any = mockPatchData
for (const part of parts) {
obj = obj[part]
}
return obj
}
private async mockRevision<T>(patch: Operation<T>[]): Promise<void> {
const revision = {
id: ++this.sequence,

View File

@@ -588,9 +588,13 @@ export const mockPatchData: DataModel = {
],
},
options: {
addSsl: null,
preferredExternalPort: 443,
secure: { ssl: true },
addSsl: {
preferredExternalPort: 443,
alpn: { specified: ['http/1.1', 'h2'] },
addXForwardedHeaders: false,
},
secure: null,
},
},
},