add support for inbound proxies

This commit is contained in:
Matt Hill
2025-07-22 16:40:31 -06:00
parent 377b7b12ce
commit 4d9709eb1c
19 changed files with 450 additions and 381 deletions

View File

@@ -221,8 +221,8 @@ export class MarketplaceControlsComponent {
const loader = this.loader.open('Starting upload').subscribe()
try {
const { upload } = await this.api.sideloadPackage()
this.api.uploadPackage(upload, file).catch(console.error)
const res = await this.api.sideloadPackage()
this.api.uploadFile(res.upload, file).catch(console.error)
} catch (e: any) {
this.errorService.handleError(e)
} finally {

View File

@@ -1,33 +0,0 @@
import { ISB } from '@start9labs/start-sdk'
import { TuiDialogOptions } from '@taiga-ui/core'
import { TuiConfirmData } from '@taiga-ui/kit'
export const DELETE_OPTIONS: Partial<TuiDialogOptions<TuiConfirmData>> = {
label: 'Confirm',
size: 's',
data: {
content: 'Delete proxy? This action cannot be undone.',
yes: 'Delete',
no: 'Cancel',
},
}
export const wireguardSpec = ISB.InputSpec.of({
name: ISB.Value.text({
name: 'Name',
description: 'A friendly name to help you remember and identify this proxy',
required: true,
default: null,
}),
// @TODO Matt same here
// config: ISB.Value.file({
// name: 'Wiregaurd Config',
// required: { default: null },
// extensions: ['.conf'],
// }),
})
export type WireguardSpec = typeof wireguardSpec.validator._TYPE
export type ProxyUpdate = {
name: string
}

View File

@@ -1,34 +0,0 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { TuiLink, TuiNotification } from '@taiga-ui/core'
import { DocsLinkDirective } from 'projects/shared/src/public-api'
@Component({
selector: 'proxies-info',
template: `
<tui-notification>
Currently, StartOS only supports Wireguard proxies, which can be used for:
<ol>
<li>
Proxying
<i>outbound</i>
traffic to mask your home/business IP from other servers accessed by
your server/services
</li>
<li>
Proxying
<i>inbound</i>
traffic to mask your home/business IP from anyone accessing your
server/services over clearnet
</li>
<li>
Creating a Virtual Local Area Network (VLAN) to enable private, remote
VPN access to your server/services
</li>
</ol>
<a tuiLink docsLink href="/@TODO">View instructions</a>
</tui-notification>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiNotification, TuiLink, DocsLinkDirective],
})
export class ProxiesInfoComponent {}

View File

@@ -0,0 +1,99 @@
import {
ChangeDetectionStrategy,
Component,
input,
output,
} from '@angular/core'
import { i18nPipe } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import {
TuiButton,
TuiDataList,
TuiDropdown,
TuiOptGroup,
} from '@taiga-ui/core'
export type WireguardProxy = T.NetworkInterfaceInfo & {
id: string
ipInfo: WireguardIpInfo
}
export type WireguardIpInfo = T.IpInfo & {
deviceType: 'wireguard'
}
@Component({
selector: 'tr[proxy]',
template: `
<td class="label">{{ proxy().ipInfo.name }}</td>
<td class="type">
{{ proxy().public ? ('Public' | i18n) : ('Private' | i18n) }}
</td>
<td class="actions">
<button
tuiIconButton
iconStart="@tui.ellipsis"
appearance="icon"
[tuiDropdown]="content"
[(tuiDropdownOpen)]="open"
[tuiDropdownMaxHeight]="9999"
>
<img [style.max-width.%]="60" src="assets/img/icon.png" alt="StartOS" />
</button>
<ng-template #content>
<tui-data-list [style.width.rem]="13">
<tui-opt-group>
<button
tuiOption
iconStart="@tui.pencil"
(click)="onRename.emit(proxy())"
>
{{ 'Rename' | i18n }}
</button>
<button
tuiOption
appearance="negative"
iconStart="@tui.trash-2"
(click)="onRemove.emit(proxy())"
>
{{ 'Delete' | i18n }}
</button>
</tui-opt-group>
</tui-data-list>
</ng-template>
</td>
`,
styles: `
td:last-child {
grid-area: 3 / span 4;
white-space: nowrap;
text-align: right;
flex-direction: row-reverse;
justify-content: flex-end;
gap: 0.5rem;
}
:host-context(tui-root._mobile) {
display: grid;
grid-template-columns: repeat(3, min-content) 1fr;
align-items: center;
padding: 1rem 0.5rem;
gap: 0.5rem;
td {
display: flex;
padding: 0;
}
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiButton, i18nPipe, TuiDropdown, TuiDataList, TuiOptGroup],
})
export class ProxiesItemComponent {
readonly proxy = input.required<WireguardProxy>()
onRename = output<WireguardProxy>()
onRemove = output<WireguardProxy>()
open = false
}

View File

@@ -1,72 +1,158 @@
import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { TuiDialogOptions, TuiButton } from '@taiga-ui/core'
import { PatchDB } from 'patch-db-client'
import { Observable } from 'rxjs'
import {
FormComponent,
FormContext,
} from 'src/app/routes/portal/components/form.component'
import { DataModel, Proxy } from 'src/app/services/patch-db/data-model'
DocsLinkDirective,
ErrorService,
i18nPipe,
LoadingService,
} from '@start9labs/shared'
import { TuiButton, TuiLink } from '@taiga-ui/core'
import { PatchDB } from 'patch-db-client'
import { FormComponent } from 'src/app/routes/portal/components/form.component'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { FormDialogService } from 'src/app/services/form-dialog.service'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ProxiesTableComponent } from './table.component'
import { ProxiesInfoComponent } from './info.component'
import { wireguardSpec, WireguardSpec } from './constants'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
import { TitleDirective } from 'src/app/services/title.service'
import { TuiHeader } from '@taiga-ui/layout'
import { map } from 'rxjs'
import { ISB, T } from '@start9labs/start-sdk'
import { WireguardIpInfo, WireguardProxy } from './item.component'
@Component({
template: `
<proxies-info />
<h3 class="g-title">
Proxies
<button tuiButton size="xs" iconStart="@tui.plus" (click)="add()">
Add Proxy
</button>
</h3>
<table class="g-table" [proxies]="proxies$ | async"></table>
<ng-container *title>
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
{{ 'Back' | i18n }}
</a>
{{ 'Inbound Proxies' | i18n }}
</ng-container>
<header tuiHeader>
<hgroup tuiTitle>
<h3>{{ 'Inbound Proxies' | i18n }}</h3>
<p tuiSubtitle>
{{
'Inbound proxies provide remote access to your server and installed services.'
| i18n
}}
<a
tuiLink
docsLink
href="/user-manual/inbound-proxies"
appearance="action-grayscale"
iconEnd="@tui.external-link"
[pseudo]="true"
[textContent]="'View instructions'"
></a>
</p>
</hgroup>
</header>
<section class="g-card">
<header>
{{ 'Saved Proxies' | i18n }}
<button tuiButton size="xs" iconStart="@tui.plus" (click)="add()">
Add
</button>
</header>
<div #table [proxies]="proxies$ | async"></div>
</section>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
TuiButton,
ProxiesInfoComponent,
ProxiesTableComponent,
TuiHeader,
TitleDirective,
i18nPipe,
TuiLink,
DocsLinkDirective,
],
})
export default class SystemProxiesComponent {
export default class ProxiesComponent {
private readonly loader = inject(LoadingService)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)
private readonly formDialog = inject(FormDialogService)
readonly proxies$: Observable<Proxy[]> = inject<PatchDB<DataModel>>(
PatchDB,
).watch$('serverInfo', 'network', 'proxies')
readonly proxies$ = inject<PatchDB<DataModel>>(PatchDB)
.watch$('serverInfo', 'network')
.pipe(
map(network =>
Object.entries(network.networkInterfaces)
.filter(
(
record,
): record is [
string,
T.NetworkInterfaceInfo & { ipInfo: WireguardIpInfo },
] => record[1].ipInfo?.deviceType === 'wireguard',
)
.map(
([id, val]) =>
({
...val,
id,
}) as WireguardProxy,
),
),
)
readonly wireguardSpec = ISB.InputSpec.of({
label: ISB.Value.text({
name: 'Label',
description: 'To help identify this proxy',
required: true,
default: null,
}),
type: ISB.Value.select({
name: 'Type',
description:
'-**Private**: a private inbound proxy is used to access your server and installed services privately. Only clients configured and authorized to use the proxy will be granted access.\n-**Public**: a public inbound proxy is used to expose service interfaces on a case-by-case basis to the public Internet without exposing your home IP address. Only service interfaces explicitly marked "Public" will be accessible via the proxy.',
default: 'private',
values: {
private: 'Private',
public: 'Public',
},
}),
config: ISB.Value.file({
name: 'Wiregaurd Config',
required: true,
extensions: ['.conf'],
}),
})
async add() {
const options: Partial<TuiDialogOptions<FormContext<WireguardSpec>>> = {
this.formDialog.open(FormComponent, {
label: 'Add Proxy',
data: {
spec: await wireguardSpec.build({} as any),
spec: await configBuilderToSpec(this.wireguardSpec),
buttons: [
{
text: 'Save',
handler: value => this.save(value).then(() => true),
handler: (input: typeof this.wireguardSpec._TYPE) =>
this.save(input),
},
],
},
}
this.formDialog.open(FormComponent, options)
})
}
// @TODO 041 fix type to be WireguardSpec
private async save({ name, config }: any): Promise<boolean> {
private async save(
input: typeof this.wireguardSpec._TYPE & {
config: { hash: string; file: File }
},
): Promise<boolean> {
const loader = this.loader.open('Saving').subscribe()
try {
await this.api.addProxy({ name, config: config?.filePath || '' })
await this.api.addProxy({
label: input.label,
config: input.config,
public: input.type === 'public',
})
return true
} catch (e: any) {
this.errorService.handleError(e)

View File

@@ -2,165 +2,76 @@ import {
ChangeDetectionStrategy,
Component,
inject,
Input,
input,
} from '@angular/core'
import { ErrorService, LoadingService } from '@start9labs/shared'
import {
DialogService,
ErrorService,
i18nPipe,
LoadingService,
} from '@start9labs/shared'
import { ISB } from '@start9labs/start-sdk'
import {
TuiButton,
TuiDialogOptions,
TuiDialogService,
TuiLink,
} from '@taiga-ui/core'
import { TUI_CONFIRM, TuiSkeleton } from '@taiga-ui/kit'
import { TuiSkeleton } from '@taiga-ui/kit'
import { filter } from 'rxjs'
import {
FormComponent,
FormContext,
} from 'src/app/routes/portal/components/form.component'
import {
DELETE_OPTIONS,
ProxyUpdate,
} from 'src/app/routes/portal/routes/system/routes/proxies/constants'
import { FormComponent } from 'src/app/routes/portal/components/form.component'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { FormDialogService } from 'src/app/services/form-dialog.service'
import { Proxy } from 'src/app/services/patch-db/data-model'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
import { TableComponent } from 'src/app/routes/portal/components/table.component'
import { WireguardProxy } from './item.component'
import { ProxiesItemComponent } from './item.component'
@Component({
selector: 'table[proxies]',
selector: '[proxies]',
template: `
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Used By</th>
<th [style.width.rem]="3.5"></th>
</tr>
</thead>
<tbody>
@for (proxy of proxies; track $index) {
<tr>
<td class="title">{{ proxy.name }}</td>
<td class="type">{{ proxy.type }}</td>
<td class="used">
@if (getLength(proxy); as length) {
<button tuiLink (click)="onUsedBy(proxy)">
Used by: {{ length }}
</button>
} @else {
N/A
}
</td>
<td class="actions">
<button
tuiIconButton
appearance="icon"
size="xs"
iconStart="@tui.pencil"
(click)="rename(proxy)"
>
Rename
</button>
<button
tuiIconButton
appearance="icon"
size="xs"
iconStart="@tui.trash-2"
(click)="delete(proxy)"
>
Delete
</button>
</td>
</tr>
<table [appTable]="['Label', 'Type', null]">
@for (proxy of proxies(); track $index) {
<tr
[proxy]="proxy"
(onRename)="rename($event)"
(onRemove)="remove($event.id)"
></tr>
} @empty {
@if (proxies) {
<tr><td colspan="5">No proxies added</td></tr>
@if (proxies()) {
<tr>
<td colspan="5">{{ 'No proxies' | i18n }}</td>
</tr>
} @else {
<tr>
<td colspan="5"><div [tuiSkeleton]="true">Loading</div></td>
<td colspan="5">
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
</td>
</tr>
}
}
</tbody>
</table>
`,
styles: `
:host-context(tui-root._mobile) {
tr {
grid-template-columns: 1fr 1fr;
}
td:only-child {
grid-column: span 2;
}
.title {
order: 1;
font-weight: bold;
text-transform: uppercase;
}
.actions {
order: 2;
padding: 0;
text-align: right;
}
.type {
order: 3;
}
.used {
order: 4;
text-align: right;
&:not(:has(button)) {
display: none;
}
}
:host {
grid-column: span 6;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiLink, TuiButton, TuiSkeleton],
imports: [TuiSkeleton, i18nPipe, TableComponent, ProxiesItemComponent],
})
export class ProxiesTableComponent {
private readonly dialogs = inject(TuiDialogService)
export class ProxiesTableComponent<T extends WireguardProxy> {
readonly proxies = input<readonly T[] | null>(null)
private readonly dialog = inject(DialogService)
private readonly loader = inject(LoadingService)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)
private readonly formDialog = inject(FormDialogService)
@Input()
proxies: readonly Proxy[] | null = null
getLength({ usedBy }: Proxy) {
return usedBy.domains.length + usedBy.services.length
}
onUsedBy({ name, usedBy }: Proxy) {
let message = `Proxy "${name}" is currently used by:`
const domains = usedBy.domains.map(d => `<li>${d}</li>`)
const services = usedBy.services.map(s => `<li>${s.title}</li>`)
if (usedBy.domains.length) {
message = `${message}<h2>Domains (inbound)</h2><ul>${domains}</ul>`
}
if (usedBy.services.length) {
message = `${message}<h2>Services (outbound)</h2>${services}`
}
this.dialogs.open(message, { label: 'Used by', size: 's' }).subscribe()
}
delete({ id }: Proxy) {
this.dialogs
.open(TUI_CONFIRM, DELETE_OPTIONS)
remove(id: string) {
this.dialog
.openConfirm({ label: 'Are you sure?', size: 's' })
.pipe(filter(Boolean))
.subscribe(async () => {
const loader = this.loader.open('Deleting').subscribe()
try {
await this.api.deleteProxy({ id })
await this.api.removeProxy({ id })
} catch (e: any) {
this.errorService.handleError(e)
} finally {
@@ -169,30 +80,35 @@ export class ProxiesTableComponent {
})
}
async rename(proxy: Proxy) {
const spec = { name: 'Name', required: true, default: proxy.name }
const name = await ISB.Value.text(spec).build({} as any)
const options: Partial<TuiDialogOptions<FormContext<{ name: string }>>> = {
label: `Rename ${proxy.name}`,
async rename(proxy: WireguardProxy) {
const renameSpec = ISB.InputSpec.of({
label: ISB.Value.text({
name: 'Label',
required: true,
default: proxy.ipInfo?.name || null,
}),
})
this.formDialog.open(FormComponent, {
label: 'Update Label',
data: {
spec: { name },
spec: await configBuilderToSpec(renameSpec),
buttons: [
{
text: 'Save',
handler: value => this.update(value),
handler: (value: typeof renameSpec._TYPE) =>
this.update(proxy.id, value.label),
},
],
},
}
this.formDialog.open(FormComponent, options)
})
}
private async update(value: ProxyUpdate): Promise<boolean> {
private async update(id: string, label: string): Promise<boolean> {
const loader = this.loader.open('Saving').subscribe()
try {
await this.api.updateProxy(value)
await this.api.updateProxy({ id, label })
return true
} catch (e: any) {
this.errorService.handleError(e)

View File

@@ -26,11 +26,6 @@ export const SYSTEM_MENU = [
item: 'StartOS UI',
link: 'interfaces',
},
{
icon: '@tui.award',
item: 'ACME',
link: 'acme',
},
{
icon: '@tui.mail',
item: 'Email',
@@ -42,6 +37,18 @@ export const SYSTEM_MENU = [
link: 'wifi',
},
],
[
{
icon: '@tui.award',
item: 'ACME',
link: 'acme',
},
{
icon: '@tui.hard-drive-download',
item: 'Inbound Proxies',
link: 'proxies',
},
],
[
{
icon: '@tui.clock',

View File

@@ -72,15 +72,15 @@ export default [
title: titleResolver,
loadComponent: () => import('./routes/password/password.component'),
},
{
path: 'proxies',
loadComponent: () => import('./routes/proxies/proxies.component'),
},
// {
// path: 'domains',
// loadComponent: () => import('./routes/domains/domains.component')
// },
// {
// path: 'proxies',
// loadComponent: () => import('./routes/proxies/proxies.component')
// },
// {
// path: 'router',
// loadComponent: () => import('./routes/router/router.component')
// },

View File

@@ -234,7 +234,30 @@ export namespace RR {
}
export type CreateBackupRes = null
// package
// proxy
export type AddProxyReq = {
label: string
config: string // hash of file
public: boolean
} // net.proxy.add
export type AddProxyRes = {
id: string
}
export type UpdateProxyReq = {
id: string
label: string
} // net.netwok-interface.set-label
export type UpdateProxyRes = null
export type RemoveProxyReq = { id: string } // net.proxy.remove
export type RemoveProxyRes = null
// export type SetOutboundProxyReq = {
// id: string | null
// } // net.proxy.set-outbound
// export type SetOutboundProxyRes = null
export type InitAcmeReq = {
provider: 'letsencrypt' | 'letsencrypt-staging' | string
@@ -374,7 +397,7 @@ export namespace RR {
icon: string // base64
}
export type SideloadPackageRes = {
upload: string // guid
upload: string
progress: string // guid
}
@@ -631,35 +654,6 @@ export type DependencyErrorTransitive = {
// export type OverridePortReq = { target: number; port: number } // net.port-forwards.override
// export type OverridePortRes = null
// // ** proxies **
// export type AddProxyReq = {
// name: string
// config: string
// } // net.proxy.add
// export type AddProxyRes = null
// export type UpdateProxyReq = {
// name: string
// } // net.proxy.update
// export type UpdateProxyRes = null
// export type DeleteProxyReq = { id: string } // net.proxy.delete
// export type DeleteProxyRes = null
// // ** set outbound proxies **
// export type SetOsOutboundProxyReq = {
// proxy: string | null
// } // server.proxy.set-outbound
// export type SetOsOutboundProxyRes = null
// export type SetServiceOutboundProxyReq = {
// packageId: string
// proxy: string | null
// } // package.proxy.set-outbound
// export type SetServiceOutboundProxyRes = null
// // ** automated backups **
// export type GetBackupTargetsReq = {} // backup.target.list

View File

@@ -6,8 +6,8 @@ import { WebSocketSubject } from 'rxjs/webSocket'
export abstract class ApiService {
// http
// for sideloading packages
abstract uploadPackage(guid: string, body: Blob): Promise<void>
// for uploading files
abstract uploadFile(guid: string, body: Blob): Promise<void>
// for getting static files: ex license
abstract getStaticProxy(
@@ -126,12 +126,6 @@ export abstract class ApiService {
// @TODO 041
// ** server outbound proxy **
// abstract setOsOutboundProxy(
// params: RR.SetOsOutboundProxyReq,
// ): Promise<RR.SetOsOutboundProxyRes>
// smtp
abstract setSmtp(params: RR.SetSMTPReq): Promise<RR.SetSMTPRes>
@@ -182,13 +176,17 @@ export abstract class ApiService {
// ** proxies **
abstract addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes>
abstract updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes>
abstract removeProxy(params: RR.RemoveProxyReq): Promise<RR.RemoveProxyRes>
// @TODO 041
// abstract addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes>
// abstract updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes>
// abstract deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes>
// abstract setOutboundProxy(
// params: RR.SetOutboundProxyReq,
// ): Promise<RR.SetOutboundProxyRes>
// ** domains **

View File

@@ -33,9 +33,9 @@ export class LiveApiService extends ApiService {
this.document.defaultView.rpcClient = this
}
// for sideloading packages
// for uploading files
async uploadPackage(guid: string, body: Blob): Promise<void> {
async uploadFile(guid: string, body: Blob): Promise<void> {
await this.httpRequest({
method: Method.POST,
body,
@@ -271,12 +271,6 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'net.tor.reset', params })
}
// async setOsOutboundProxy(
// params: RR.SetOsOutboundProxyReq,
// ): Promise<RR.SetOsOutboundProxyRes> {
// return this.rpcRequest({ method: 'server.proxy.set-outbound', params })
// }
// marketplace URLs
async checkOSUpdate(
@@ -352,16 +346,22 @@ export class LiveApiService extends ApiService {
// proxies
// async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
// return this.rpcRequest({ method: 'net.proxy.add', params })
// }
async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
return this.rpcRequest({ method: 'net.proxy.add', params })
}
// async updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes> {
// return this.rpcRequest({ method: 'net.proxy.update', params })
// }
async updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes> {
return this.rpcRequest({ method: 'net.netwok-interface.set-label', params })
}
// async deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes> {
// return this.rpcRequest({ method: 'net.proxy.delete', params })
async removeProxy(params: RR.RemoveProxyReq): Promise<RR.RemoveProxyRes> {
return this.rpcRequest({ method: 'net.proxy.remove', params })
}
// async setOutboundProxy(
// params: RR.SetOutboundProxyReq,
// ): Promise<RR.SetOutboundProxyRes> {
// return this.rpcRequest({ method: 'server.proxy.set-outbound', params })
// }
// domains

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { pauseFor, Log, RPCErrorDetails, RPCOptions } from '@start9labs/shared'
import { pauseFor, Log, RPCErrorDetails } from '@start9labs/shared'
import { ApiService } from './embassy-api.service'
import {
AddOperation,
@@ -71,7 +71,7 @@ export class MockApiService extends ApiService {
.subscribe()
}
async uploadPackage(guid: string, body: Blob): Promise<void> {
async uploadFile(guid: string, body: Blob): Promise<void> {
await pauseFor(2000)
}
@@ -467,23 +467,6 @@ export class MockApiService extends ApiService {
return null
}
// async setOsOutboundProxy(
// params: RR.SetOsOutboundProxyReq,
// ): Promise<RR.SetOsOutboundProxyRes> {
// await pauseFor(2000)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/outboundProxy',
// value: params.proxy,
// },
// ]
// this.mockRevision(patch)
// return null
// }
// marketplace URLs
async checkOSUpdate(
@@ -559,56 +542,73 @@ export class MockApiService extends ApiService {
return null
}
// network
// proxies
// async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
async addProxy(params: RR.AddProxyReq): Promise<RR.AddProxyRes> {
await pauseFor(2000)
const id = `wga-${params.label}`
const patch: AddOperation<T.NetworkInterfaceInfo>[] = [
{
op: PatchOp.ADD,
path: `/serverInfo/network/networkInterfaces/${id}`,
value: {
public: params.public,
ipInfo: {
name: params.label,
scopeId: 3,
deviceType: 'wireguard',
subnets: [],
wanIp: '1.1.1.1',
ntpServers: [],
},
},
},
]
this.mockRevision(patch)
return { id }
}
async updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes> {
await pauseFor(2000)
const patch: ReplaceOperation<string>[] = [
{
op: PatchOp.REPLACE,
path: `/serverInfo/network/networkInterfaces/${params.id}/label`,
value: params.label,
},
]
this.mockRevision(patch)
return null
}
async removeProxy(params: RR.RemoveProxyReq): Promise<RR.RemoveProxyRes> {
await pauseFor(2000)
const patch: RemoveOperation[] = [
{
op: PatchOp.REMOVE,
path: `/serverInfo/network/networkInterfaces/${params.id}`,
},
]
this.mockRevision(patch)
return null
}
// async setOutboundProxy(
// params: RR.SetOutboundProxyReq,
// ): Promise<RR.SetOutboundProxyRes> {
// await pauseFor(2000)
// const patch = [
// {
// op: PatchOp.ADD,
// path: `/serverInfo/network/networkInterfaces/wga1`,
// value: {
// inbound: true,
// outbound: true,
// ipInfo: {
// name: params.name,
// scopeId: 3,
// deviceType: 'wireguard',
// subnets: [],
// wanIp: '1.1.1.1',
// ntpServers: [],
// },
// },
// },
// ]
// this.mockRevision(patch)
// return null
// }
// async updateProxy(params: RR.UpdateProxyReq): Promise<RR.UpdateProxyRes> {
// await pauseFor(2000)
// const patch = [
// const patch: ReplaceOperation<string | null>[] = [
// {
// op: PatchOp.REPLACE,
// path: `/serverInfo/network/proxies/0/name`,
// value: params.name,
// },
// ]
// this.mockRevision(patch)
// return null
// }
// async deleteProxy(params: RR.DeleteProxyReq): Promise<RR.DeleteProxyRes> {
// await pauseFor(2000)
// const patch = [
// {
// op: PatchOp.REPLACE,
// path: '/serverInfo/network/proxies',
// value: [],
// path: '/serverInfo/network/outboundInterface',
// value: params.id,
// },
// ]
// this.mockRevision(patch)

View File

@@ -136,8 +136,7 @@ export const mockPatchData: DataModel = {
},
networkInterfaces: {
eth0: {
inbound: false,
outbound: true,
public: false,
ipInfo: {
name: 'Wired Connection 1',
scopeId: 1,
@@ -148,8 +147,7 @@ export const mockPatchData: DataModel = {
},
},
wlan0: {
inbound: false,
outbound: true,
public: false,
ipInfo: {
name: 'Wireless Connection 1',
scopeId: 2,

View File

@@ -1,5 +1,3 @@
// @TODO 041
// import { Injectable } from '@angular/core'
// import { ErrorService, LoadingService } from '@start9labs/shared'
// import { TuiDialogOptions } from '@taiga-ui/core'