diff --git a/web/projects/shared/src/i18n/dictionaries/de.ts b/web/projects/shared/src/i18n/dictionaries/de.ts index 7646ea8d9..8f865edbe 100644 --- a/web/projects/shared/src/i18n/dictionaries/de.ts +++ b/web/projects/shared/src/i18n/dictionaries/de.ts @@ -528,4 +528,12 @@ export default { 530: 'StartOS-Paket', 531: 'Fehler beim Initialisieren des Servers', 532: 'Abgeschlossen', + 533: 'Eingehende Proxys', + 534: 'Eingehende Proxys ermöglichen den Fernzugriff auf Ihren Server und installierte Dienste.', + 535: 'Gespeicherte Proxys', + 536: 'Proxy hinzufügen', + 537: 'Bezeichnung', + 538: 'Keine Proxys', + 539: 'Bezeichnung aktualisieren', + 540: 'Umbenennen', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/en.ts b/web/projects/shared/src/i18n/dictionaries/en.ts index 40b93c12f..0e34f02d4 100644 --- a/web/projects/shared/src/i18n/dictionaries/en.ts +++ b/web/projects/shared/src/i18n/dictionaries/en.ts @@ -527,4 +527,12 @@ export const ENGLISH = { 'StartOS package': 530, // as in, the URL of the source code for the StartOS package 'Error initializing server': 531, 'Finished': 532, // an in, complete + 'Inbound Proxies': 533, // as in a service used to proxy internet traffic + 'Inbound proxies provide remote access to your server and installed services.': 534, + 'Saved Proxies': 535, // as in, a list of proxies already added to StartOS + 'Add Proxy': 536, // as in, add a new proxy to StartOS + 'Label': 537, // as in, a name given to something + 'No proxies': 538, + 'Update Label': 539, + 'Rename': 540 } as const diff --git a/web/projects/shared/src/i18n/dictionaries/es.ts b/web/projects/shared/src/i18n/dictionaries/es.ts index dc2c60413..fbe4cd85f 100644 --- a/web/projects/shared/src/i18n/dictionaries/es.ts +++ b/web/projects/shared/src/i18n/dictionaries/es.ts @@ -528,4 +528,12 @@ export default { 530: 'Paquete StartOS', 531: 'Error al inicializar el servidor', 532: 'Finalizado', + 533: 'Proxies entrantes', + 534: 'Los proxies entrantes proporcionan acceso remoto a su servidor y servicios instalados.', + 535: 'Proxies guardados', + 536: 'Agregar proxy', + 537: 'Etiqueta', + 538: 'Sin proxies', + 539: 'Actualizar etiqueta', + 540: 'Renombrar', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/fr.ts b/web/projects/shared/src/i18n/dictionaries/fr.ts index d0948c90f..5137d7dc9 100644 --- a/web/projects/shared/src/i18n/dictionaries/fr.ts +++ b/web/projects/shared/src/i18n/dictionaries/fr.ts @@ -528,4 +528,12 @@ export default { 530: 'Paquet StartOS', 531: "Erreur lors de l'initialisation du serveur", 532: 'Terminé', + 533: 'Proxies entrants', + 534: 'Les proxies entrants permettent un accès à distance à votre serveur et aux services installés.', + 535: 'Proxies enregistrés', + 536: 'Ajouter un proxy', + 537: 'Étiquette', + 538: 'Aucun proxy', + 539: 'Mettre à jour l’étiquette', + 540: 'Renommer', } satisfies i18n diff --git a/web/projects/shared/src/i18n/dictionaries/pl.ts b/web/projects/shared/src/i18n/dictionaries/pl.ts index e579aca60..7f922a77b 100644 --- a/web/projects/shared/src/i18n/dictionaries/pl.ts +++ b/web/projects/shared/src/i18n/dictionaries/pl.ts @@ -528,4 +528,12 @@ export default { 530: 'Pakiet StartOS', 531: 'Błąd inicjalizacji serwera', 532: 'Zakończono', + 533: 'Proksy przychodzące', + 534: 'Proksy przychodzące zapewniają zdalny dostęp do twojego serwera i zainstalowanych usług.', + 535: 'Zapisane proksy', + 536: 'Dodaj proksy', + 537: 'Etykieta', + 538: 'Brak proksy', + 539: 'Aktualizuj etykietę', + 540: 'Zmień nazwę', } satisfies i18n diff --git a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts index bafc87c23..a72f53feb 100644 --- a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts @@ -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 { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/constants.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/constants.ts deleted file mode 100644 index bde66ebdb..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/constants.ts +++ /dev/null @@ -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> = { - 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 -} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/info.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/info.component.ts deleted file mode 100644 index 58b8a7d9c..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/info.component.ts +++ /dev/null @@ -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: ` - - Currently, StartOS only supports Wireguard proxies, which can be used for: -
    -
  1. - Proxying - outbound - traffic to mask your home/business IP from other servers accessed by - your server/services -
  2. -
  3. - Proxying - inbound - traffic to mask your home/business IP from anyone accessing your - server/services over clearnet -
  4. -
  5. - Creating a Virtual Local Area Network (VLAN) to enable private, remote - VPN access to your server/services -
  6. -
- View instructions -
- `, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [TuiNotification, TuiLink, DocsLinkDirective], -}) -export class ProxiesInfoComponent {} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/item.component.ts new file mode 100644 index 000000000..8e426d2f9 --- /dev/null +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/item.component.ts @@ -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: ` + {{ proxy().ipInfo.name }} + + {{ proxy().public ? ('Public' | i18n) : ('Private' | i18n) }} + + + + + + + + + + + + + `, + 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() + + onRename = output() + onRemove = output() + + open = false +} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/proxies.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/proxies.component.ts index 9f92f9116..9cb4d7f72 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/proxies.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/proxies.component.ts @@ -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 - -

-
+ + + {{ 'Back' | i18n }} + + {{ 'Inbound Proxies' | i18n }} + +
+
+

{{ 'Inbound Proxies' | i18n }}

+

+ {{ + 'Inbound proxies provide remote access to your server and installed services.' + | i18n + }} + +

+
+
+ +
+
+ {{ 'Saved Proxies' | i18n }} + +
+
+
`, 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 = inject>( - PatchDB, - ).watch$('serverInfo', 'network', 'proxies') + readonly proxies$ = inject>(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>> = { + 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 { + private async save( + input: typeof this.wireguardSpec._TYPE & { + config: { hash: string; file: File } + }, + ): Promise { 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) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/table.component.ts index da100539a..39cbdc11d 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/proxies/table.component.ts @@ -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: ` - - - Name - Type - Used By - - - - - @for (proxy of proxies; track $index) { - - {{ proxy.name }} - {{ proxy.type }} - - @if (getLength(proxy); as length) { - - } @else { - N/A - } - - - - - - + + @for (proxy of proxies(); track $index) { + } @empty { - @if (proxies) { - + @if (proxies()) { + + + } @else { - + } } - +
No proxies added
{{ 'No proxies' | i18n }}
Loading
+
{{ 'Loading' | i18n }}
+
`, 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 { + readonly proxies = input(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 => `
  • ${d}
  • `) - const services = usedBy.services.map(s => `
  • ${s.title}
  • `) - - if (usedBy.domains.length) { - message = `${message}

    Domains (inbound)

      ${domains}
    ` - } - - if (usedBy.services.length) { - message = `${message}

    Services (outbound)

    ${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>> = { - 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 { + private async update(id: string, label: string): Promise { 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) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts b/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts index 4fff10115..c3ab09203 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/system.const.ts @@ -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', diff --git a/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts b/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts index 635b524b1..435a78bd6 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts @@ -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') // }, diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index 72f36b33d..864a264f9 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -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 diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts index 603de8879..80401dae4 100644 --- a/web/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts @@ -6,8 +6,8 @@ import { WebSocketSubject } from 'rxjs/webSocket' export abstract class ApiService { // http - // for sideloading packages - abstract uploadPackage(guid: string, body: Blob): Promise + // for uploading files + abstract uploadFile(guid: string, body: Blob): Promise // 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 - // smtp abstract setSmtp(params: RR.SetSMTPReq): Promise @@ -182,13 +176,17 @@ export abstract class ApiService { // ** proxies ** + abstract addProxy(params: RR.AddProxyReq): Promise + + abstract updateProxy(params: RR.UpdateProxyReq): Promise + + abstract removeProxy(params: RR.RemoveProxyReq): Promise + // @TODO 041 - // abstract addProxy(params: RR.AddProxyReq): Promise - - // abstract updateProxy(params: RR.UpdateProxyReq): Promise - - // abstract deleteProxy(params: RR.DeleteProxyReq): Promise + // abstract setOutboundProxy( + // params: RR.SetOutboundProxyReq, + // ): Promise // ** domains ** diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index 32edfa9ca..d6565e9ce 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -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 { + async uploadFile(guid: string, body: Blob): Promise { 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 { - // 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 { - // return this.rpcRequest({ method: 'net.proxy.add', params }) - // } + async addProxy(params: RR.AddProxyReq): Promise { + return this.rpcRequest({ method: 'net.proxy.add', params }) + } - // async updateProxy(params: RR.UpdateProxyReq): Promise { - // return this.rpcRequest({ method: 'net.proxy.update', params }) - // } + async updateProxy(params: RR.UpdateProxyReq): Promise { + return this.rpcRequest({ method: 'net.netwok-interface.set-label', params }) + } - // async deleteProxy(params: RR.DeleteProxyReq): Promise { - // return this.rpcRequest({ method: 'net.proxy.delete', params }) + async removeProxy(params: RR.RemoveProxyReq): Promise { + return this.rpcRequest({ method: 'net.proxy.remove', params }) + } + + // async setOutboundProxy( + // params: RR.SetOutboundProxyReq, + // ): Promise { + // return this.rpcRequest({ method: 'server.proxy.set-outbound', params }) // } // domains diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index efb1a4f3d..54c7e937b 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -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 { + async uploadFile(guid: string, body: Blob): Promise { await pauseFor(2000) } @@ -467,23 +467,6 @@ export class MockApiService extends ApiService { return null } - // async setOsOutboundProxy( - // params: RR.SetOsOutboundProxyReq, - // ): Promise { - // 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 { + async addProxy(params: RR.AddProxyReq): Promise { + await pauseFor(2000) + + const id = `wga-${params.label}` + + const patch: AddOperation[] = [ + { + 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 { + await pauseFor(2000) + + const patch: ReplaceOperation[] = [ + { + op: PatchOp.REPLACE, + path: `/serverInfo/network/networkInterfaces/${params.id}/label`, + value: params.label, + }, + ] + this.mockRevision(patch) + + return null + } + + async removeProxy(params: RR.RemoveProxyReq): Promise { + 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 { // 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 { - // await pauseFor(2000) - - // const patch = [ + // const patch: ReplaceOperation[] = [ // { // op: PatchOp.REPLACE, - // path: `/serverInfo/network/proxies/0/name`, - // value: params.name, - // }, - // ] - // this.mockRevision(patch) - - // return null - // } - - // async deleteProxy(params: RR.DeleteProxyReq): Promise { - // await pauseFor(2000) - // const patch = [ - // { - // op: PatchOp.REPLACE, - // path: '/serverInfo/network/proxies', - // value: [], + // path: '/serverInfo/network/outboundInterface', + // value: params.id, // }, // ] // this.mockRevision(patch) diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index 8f94b168d..012a2d301 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -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, diff --git a/web/projects/ui/src/app/services/proxy.service.ts b/web/projects/ui/src/app/services/proxy.service.ts index 75ab54a99..3d2ec71a6 100644 --- a/web/projects/ui/src/app/services/proxy.service.ts +++ b/web/projects/ui/src/app/services/proxy.service.ts @@ -1,5 +1,3 @@ -// @TODO 041 - // import { Injectable } from '@angular/core' // import { ErrorService, LoadingService } from '@start9labs/shared' // import { TuiDialogOptions } from '@taiga-ui/core'