diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.component.html b/web/projects/ui/src/app/components/interface-info/interface-info.component.html
deleted file mode 100644
index 66d773063..000000000
--- a/web/projects/ui/src/app/components/interface-info/interface-info.component.html
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
- {{ iFace.name }}
- {{ iFace.description }}
-
- Add Domain
-
-
- Make {{ iFace.public ? 'Private' : 'Public' }}
-
-
-
-
-
-
- {{ address.name }}
- {{ address.url }}
-
- Remove
-
-
- Remove
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.component.scss b/web/projects/ui/src/app/components/interface-info/interface-info.component.scss
deleted file mode 100644
index 61ead3b94..000000000
--- a/web/projects/ui/src/app/components/interface-info/interface-info.component.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-p {
- font-family: 'Courier New';
-}
\ No newline at end of file
diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.component.ts b/web/projects/ui/src/app/components/interface-info/interface-info.component.ts
deleted file mode 100644
index 932b1f767..000000000
--- a/web/projects/ui/src/app/components/interface-info/interface-info.component.ts
+++ /dev/null
@@ -1,393 +0,0 @@
-import { Component, Inject, Input } from '@angular/core'
-import { WINDOW } from '@ng-web-apis/common'
-import {
- AlertController,
- ModalController,
- ToastController,
-} from '@ionic/angular'
-import {
- copyToClipboard,
- ErrorService,
- LoadingService,
-} from '@start9labs/shared'
-import { DataModel } from 'src/app/services/patch-db/data-model'
-import { PatchDB } from 'patch-db-client'
-import { QRComponent } from 'src/app/components/qr/qr.component'
-import { firstValueFrom } from 'rxjs'
-import { ISB, T, utils } from '@start9labs/start-sdk'
-import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { FormDialogService } from 'src/app/services/form-dialog.service'
-import { FormComponent } from 'src/app/components/form.component'
-import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec'
-import { toAcmeName } from 'src/app/util/acme'
-import { ConfigService } from 'src/app/services/config.service'
-
-export type MappedInterface = T.ServiceInterface & {
- addresses: MappedAddress[]
- public: boolean
-}
-export type MappedAddress = {
- name: string
- url: string
- isDomain: boolean
- isOnion: boolean
- acme: string | null
-}
-
-@Component({
- selector: 'interface-info',
- templateUrl: './interface-info.component.html',
- styleUrls: ['./interface-info.component.scss'],
-})
-export class InterfaceInfoComponent {
- @Input() pkgId?: string
- @Input() iFace!: MappedInterface
-
- constructor(
- private readonly toastCtrl: ToastController,
- private readonly modalCtrl: ModalController,
- private readonly errorService: ErrorService,
- private readonly loader: LoadingService,
- private readonly api: ApiService,
- private readonly formDialog: FormDialogService,
- private readonly alertCtrl: AlertController,
- private readonly patch: PatchDB,
- private readonly config: ConfigService,
- @Inject(WINDOW) private readonly windowRef: Window,
- ) {}
-
- launch(url: string): void {
- this.windowRef.open(url, '_blank', 'noreferrer')
- }
-
- async togglePublic() {
- const loader = this.loader
- .open(`Making ${this.iFace.public ? 'private' : 'public'}`)
- .subscribe()
-
- const params = {
- internalPort: this.iFace.addressInfo.internalPort,
- public: !this.iFace.public,
- }
-
- try {
- if (this.pkgId) {
- await this.api.pkgBindingSetPubic({
- ...params,
- host: this.iFace.addressInfo.hostId,
- package: this.pkgId,
- })
- } else {
- await this.api.serverBindingSetPubic(params)
- }
- } catch (e: any) {
- this.errorService.handleError(e)
- } finally {
- loader.unsubscribe()
- }
- }
-
- async presentDomainForm() {
- const acme = await firstValueFrom(this.patch.watch$('serverInfo', 'acme'))
-
- const spec = getDomainSpec(Object.keys(acme))
-
- this.formDialog.open(FormComponent, {
- label: 'Add Domain',
- data: {
- spec: await configBuilderToSpec(spec),
- buttons: [
- {
- text: 'Save',
- handler: async (val: typeof spec._TYPE) => {
- if (val.type.selection === 'standard') {
- return this.saveStandard(
- val.type.value.domain,
- val.type.value.acme,
- )
- } else {
- return this.saveTor(val.type.value.key)
- }
- },
- },
- ],
- },
- })
- }
-
- async removeStandard(url: string) {
- const loader = this.loader.open('Removing').subscribe()
-
- const params = {
- domain: new URL(url).hostname,
- }
-
- try {
- if (this.pkgId) {
- await this.api.pkgRemoveDomain({
- ...params,
- package: this.pkgId,
- host: this.iFace.addressInfo.hostId,
- })
- } else {
- await this.api.serverRemoveDomain(params)
- }
- return true
- } catch (e: any) {
- this.errorService.handleError(e)
- return false
- } finally {
- loader.unsubscribe()
- }
- }
-
- async removeOnion(url: string) {
- const loader = this.loader.open('Removing').subscribe()
-
- const params = {
- onion: new URL(url).hostname,
- }
-
- try {
- if (this.pkgId) {
- await this.api.pkgRemoveOnion({
- ...params,
- package: this.pkgId,
- host: this.iFace.addressInfo.hostId,
- })
- } else {
- await this.api.serverRemoveOnion(params)
- }
- return true
- } catch (e: any) {
- this.errorService.handleError(e)
- return false
- } finally {
- loader.unsubscribe()
- }
- }
-
- async showAcme(url: string | null): Promise {
- const alert = await this.alertCtrl.create({
- header: 'ACME Provider',
- message: toAcmeName(url),
- })
- await alert.present()
- }
-
- async showQR(text: string): Promise {
- const modal = await this.modalCtrl.create({
- component: QRComponent,
- componentProps: {
- text,
- },
- cssClass: 'qr-modal',
- })
- await modal.present()
- }
-
- async copy(address: string): Promise {
- let message = ''
- await copyToClipboard(address || '').then(success => {
- message = success
- ? 'Copied to clipboard!'
- : 'Failed to copy to clipboard.'
- })
-
- const toast = await this.toastCtrl.create({
- header: message,
- position: 'bottom',
- duration: 1000,
- })
- await toast.present()
- }
-
- private async saveStandard(domain: string, acme: string) {
- const loader = this.loader.open('Saving').subscribe()
-
- const params = {
- domain,
- acme: acme === 'none' ? null : acme,
- private: false,
- }
-
- try {
- if (this.pkgId) {
- await this.api.pkgAddDomain({
- ...params,
- package: this.pkgId,
- host: this.iFace.addressInfo.hostId,
- })
- } else {
- await this.api.serverAddDomain(params)
- }
- return true
- } catch (e: any) {
- this.errorService.handleError(e)
- return false
- } finally {
- loader.unsubscribe()
- }
- }
-
- private async saveTor(key: string | null) {
- const loader = this.loader.open('Creating onion address').subscribe()
-
- try {
- let onion = key
- ? await this.api.addTorKey({ key })
- : await this.api.generateTorKey({})
- onion = `${onion}.onion`
-
- if (this.pkgId) {
- await this.api.pkgAddOnion({
- onion,
- package: this.pkgId,
- host: this.iFace.addressInfo.hostId,
- })
- } else {
- await this.api.serverAddOnion({ onion })
- }
- return true
- } catch (e: any) {
- this.errorService.handleError(e)
- return false
- } finally {
- loader.unsubscribe()
- }
- }
-}
-
-function getDomainSpec(acme: string[]) {
- return ISB.InputSpec.of({
- type: ISB.Value.union(
- { name: 'Type', default: 'standard' },
- ISB.Variants.of({
- standard: {
- name: 'Standard',
- spec: ISB.InputSpec.of({
- domain: ISB.Value.text({
- name: 'Domain',
- description: 'The domain or subdomain you want to use',
- placeholder: `e.g. 'mydomain.com' or 'sub.mydomain.com'`,
- required: true,
- default: null,
- patterns: [utils.Patterns.domain],
- }),
- acme: ISB.Value.select({
- name: 'ACME Provider',
- description:
- 'Select which ACME provider to use for obtaining your SSL certificate. Add new ACME providers in the System tab. Optionally use your system Root CA. Note: only devices that have trusted your Root CA will be able to access the domain without security warnings.',
- values: acme.reduce(
- (obj, url) => ({
- ...obj,
- [url]: toAcmeName(url),
- }),
- { none: 'None (use system Root CA)' } as Record,
- ),
- default: '',
- }),
- }),
- },
- onion: {
- name: 'Onion',
- spec: ISB.InputSpec.of({
- key: ISB.Value.text({
- name: 'Private Key (optional)',
- description:
- 'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) address. If not provided, a random key will be generated and used.',
- required: false,
- default: null,
- patterns: [utils.Patterns.base64],
- }),
- }),
- },
- }),
- ),
- })
-}
-
-export function getAddresses(
- serviceInterface: T.ServiceInterface,
- host: T.Host,
- config: ConfigService,
-): MappedAddress[] {
- const addressInfo = serviceInterface.addressInfo
-
- let hostnames = host.hostnameInfo[addressInfo.internalPort]
-
- hostnames = hostnames.filter(
- h =>
- config.isLocalhost() ||
- h.kind !== 'ip' ||
- h.hostname.kind !== 'ipv6' ||
- !h.hostname.value.startsWith('fe80::'),
- )
- if (config.isLocalhost()) {
- const local = hostnames.find(
- h => h.kind === 'ip' && h.hostname.kind === 'local',
- )
- if (local) {
- hostnames.unshift({
- kind: 'ip',
- networkInterfaceId: 'lo',
- public: false,
- hostname: {
- kind: 'local',
- port: local.hostname.port,
- sslPort: local.hostname.sslPort,
- value: 'localhost',
- },
- })
- }
- }
- const mappedAddresses = hostnames.flatMap(h => {
- let name = ''
- let isDomain = false
- let isOnion = false
- let acme: string | null = null
-
- if (h.kind === 'onion') {
- name = `Tor`
- isOnion = true
- } else {
- const hostnameKind = h.hostname.kind
-
- if (hostnameKind === 'domain') {
- name = 'Domain'
- isDomain = true
- acme = host.domains[h.hostname.domain]?.acme
- } else {
- name =
- hostnameKind === 'local'
- ? 'Local'
- : `${h.networkInterfaceId} (${hostnameKind})`
- }
- }
-
- const addresses = utils.addressHostToUrl(addressInfo, h)
- if (addresses.length > 1) {
- return addresses.map(url => ({
- name: `${name} (${new URL(url).protocol
- .replace(':', '')
- .toUpperCase()})`,
- url,
- isDomain,
- isOnion,
- acme,
- }))
- } else {
- return addresses.map(url => ({
- name,
- url,
- isDomain,
- isOnion,
- acme,
- }))
- }
- })
-
- return mappedAddresses.filter(
- (value, index, self) => index === self.findIndex(t => t.url === value.url),
- )
-}
diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.module.ts b/web/projects/ui/src/app/components/interface-info/interface-info.module.ts
deleted file mode 100644
index c31a6ae07..000000000
--- a/web/projects/ui/src/app/components/interface-info/interface-info.module.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { InterfaceInfoComponent } from './interface-info.component'
-
-@NgModule({
- declarations: [InterfaceInfoComponent],
- imports: [CommonModule, IonicModule],
- exports: [InterfaceInfoComponent],
-})
-export class InterfaceInfoModule {}
diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.module.ts b/web/projects/ui/src/app/pages/server-routes/acme/acme.module.ts
deleted file mode 100644
index f00171f05..000000000
--- a/web/projects/ui/src/app/pages/server-routes/acme/acme.module.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { RouterModule, Routes } from '@angular/router'
-import { ACMEPage } from './acme.page'
-
-const routes: Routes = [
- {
- path: '',
- component: ACMEPage,
- },
-]
-
-@NgModule({
- imports: [CommonModule, IonicModule, RouterModule.forChild(routes)],
- declarations: [ACMEPage],
-})
-export class ACMEPageModule {}
diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.html b/web/projects/ui/src/app/pages/server-routes/acme/acme.page.html
deleted file mode 100644
index ab7ad3a01..000000000
--- a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.html
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
- ACME
-
-
-
-
-
-
-
-
-
- Register with one or more ACME providers such as Let's Encrypt in
- order to generate SSL (https) certificates on-demand for clearnet
- hosting
-
- View instructions
-
-
-
-
-
- Saved Providers
-
-
-
-
-
- Add Provider
-
-
-
-
-
-
- {{ toAcmeName(provider.url) }}
- Contact: {{ provider.contactString }}
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.scss b/web/projects/ui/src/app/pages/server-routes/acme/acme.page.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.module.ts b/web/projects/ui/src/app/pages/server-routes/email/email.module.ts
deleted file mode 100644
index f6b0c735d..000000000
--- a/web/projects/ui/src/app/pages/server-routes/email/email.module.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { Routes, RouterModule } from '@angular/router'
-import { TuiInputModule } from '@taiga-ui/kit'
-import {
- TuiNotificationModule,
- TuiTextfieldControllerModule,
-} from '@taiga-ui/core'
-import { FormsModule, ReactiveFormsModule } from '@angular/forms'
-import { EmailPage } from './email.page'
-import { FormModule } from 'src/app/components/form/form.module'
-import { IonicModule } from '@ionic/angular'
-import { TuiErrorModule, TuiModeModule } from '@taiga-ui/core'
-import { TuiAppearanceModule, TuiButtonModule } from '@taiga-ui/experimental'
-
-const routes: Routes = [
- {
- path: '',
- component: EmailPage,
- },
-]
-
-@NgModule({
- imports: [
- CommonModule,
- IonicModule,
- RouterModule.forChild(routes),
- CommonModule,
- FormsModule,
- ReactiveFormsModule,
- TuiButtonModule,
- TuiInputModule,
- FormModule,
- TuiNotificationModule,
- TuiTextfieldControllerModule,
- TuiAppearanceModule,
- TuiModeModule,
- TuiErrorModule,
- ],
- declarations: [EmailPage],
-})
-export class EmailPageModule {}
diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.page.html b/web/projects/ui/src/app/pages/server-routes/email/email.page.html
deleted file mode 100644
index 5e0e58fa4..000000000
--- a/web/projects/ui/src/app/pages/server-routes/email/email.page.html
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
- Email
-
-
-
-
-
-
-
-
- Fill out the form below to connect to an external SMTP server. With your
- permission, installed services can use the SMTP server to send emails. To
- grant permission to a particular service, visit that service's "Actions"
- page. Not all services support sending emails.
-
- View instructions
-
-
-
-
-
-
-
diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.page.scss b/web/projects/ui/src/app/pages/server-routes/email/email.page.scss
deleted file mode 100644
index b15986fc9..000000000
--- a/web/projects/ui/src/app/pages/server-routes/email/email.page.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-form {
- padding-top: 24px;
- margin: auto;
- max-width: 30rem;
-}
-
-h3 {
- display: flex;
-}
\ No newline at end of file
diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.page.ts b/web/projects/ui/src/app/pages/server-routes/email/email.page.ts
deleted file mode 100644
index e52bf32dd..000000000
--- a/web/projects/ui/src/app/pages/server-routes/email/email.page.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
-import { ErrorService, LoadingService } from '@start9labs/shared'
-import { IST, inputSpec } from '@start9labs/start-sdk'
-import { TuiDialogService } from '@taiga-ui/core'
-import { PatchDB } from 'patch-db-client'
-import { switchMap, tap } from 'rxjs'
-import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { FormService } from 'src/app/services/form.service'
-import { DataModel } from 'src/app/services/patch-db/data-model'
-import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec'
-
-@Component({
- selector: 'email-page',
- templateUrl: './email.page.html',
- styleUrls: ['./email.page.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class EmailPage {
- private readonly dialogs = inject(TuiDialogService)
- private readonly loader = inject(LoadingService)
- private readonly errorService = inject(ErrorService)
- private readonly formService = inject(FormService)
- private readonly patch = inject>(PatchDB)
- private readonly api = inject(ApiService)
-
- isSaved = false
- testAddress = ''
-
- readonly spec: Promise = configBuilderToSpec(
- inputSpec.constants.customSmtp,
- )
- readonly form$ = this.patch.watch$('serverInfo', 'smtp').pipe(
- tap(value => (this.isSaved = !!value)),
- switchMap(async value =>
- this.formService.createForm(await this.spec, value),
- ),
- )
-
- async save(
- value: typeof inputSpec.constants.customSmtp._TYPE | null,
- ): Promise {
- const loader = this.loader.open('Saving...').subscribe()
-
- try {
- if (value) {
- await this.api.setSmtp(value)
- this.isSaved = true
- } else {
- await this.api.clearSmtp({})
- this.isSaved = false
- }
- } catch (e: any) {
- this.errorService.handleError(e)
- } finally {
- loader.unsubscribe()
- }
- }
-
- async sendTestEmail(value: typeof inputSpec.constants.customSmtp._TYPE) {
- const loader = this.loader.open('Sending email...').subscribe()
-
- try {
- await this.api.testSmtp({
- to: this.testAddress,
- ...value,
- })
- } catch (e: any) {
- return this.errorService.handleError(e)
- } finally {
- loader.unsubscribe()
- }
-
- this.dialogs
- .open(
- `A test email has been sent to ${this.testAddress}.Check your spam folder and mark as not spam `,
- {
- label: 'Success',
- size: 's',
- },
- )
- .subscribe()
- }
-}
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts
index adc5172f6..708914c1d 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts
@@ -104,10 +104,10 @@ export class InterfaceComponent {
packageId: string
interfaceId: string
}
- @Input({ required: true }) serviceInterface!: ServiceInterfaceWithAddresses
+ @Input({ required: true }) serviceInterface!: MappedServiceInterface
}
-export type ServiceInterfaceWithAddresses = T.ServiceInterface & {
+export type MappedServiceInterface = T.ServiceInterface & {
addresses: {
clearnet: AddressDetails[]
local: AddressDetails[]
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts
index 78389ce42..930826c4f 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.utils.ts
@@ -1,6 +1,7 @@
import { ISB, IST, T, utils } from '@start9labs/start-sdk'
import { TuiDialogOptions } from '@taiga-ui/core'
import { TuiConfirmData } from '@taiga-ui/kit'
+import { ConfigService } from 'src/app/services/config.service'
import { NetworkInfo } from 'src/app/services/patch-db/data-model'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
@@ -49,44 +50,80 @@ export function getClearnetSpec({
)
}
-export type AddressDetails = {
- label?: string
- url: string
-}
-
-export function getMultihostAddresses(
+// @TODO Aiden audit
+export function getAddresses(
serviceInterface: T.ServiceInterface,
host: T.Host,
+ config: ConfigService,
): {
- clearnet: AddressDetails[]
+ clearnet: (AddressDetails & { acme: string | null })[]
local: AddressDetails[]
tor: AddressDetails[]
} {
const addressInfo = serviceInterface.addressInfo
- const hostnamesInfo = host.hostnameInfo[addressInfo.internalPort]
- const clearnet: AddressDetails[] = []
+ let hostnames = host.hostnameInfo[addressInfo.internalPort]
+
+ hostnames = hostnames.filter(
+ h =>
+ config.isLocalhost() ||
+ h.kind !== 'ip' ||
+ h.hostname.kind !== 'ipv6' ||
+ !h.hostname.value.startsWith('fe80::'),
+ )
+
+ if (config.isLocalhost()) {
+ const local = hostnames.find(
+ h => h.kind === 'ip' && h.hostname.kind === 'local',
+ )
+
+ if (local) {
+ hostnames.unshift({
+ kind: 'ip',
+ networkInterfaceId: 'lo',
+ public: false,
+ hostname: {
+ kind: 'local',
+ port: local.hostname.port,
+ sslPort: local.hostname.sslPort,
+ value: 'localhost',
+ },
+ })
+ }
+ }
+
+ const clearnet: (AddressDetails & { acme: string | null })[] = []
const local: AddressDetails[] = []
const tor: AddressDetails[] = []
- hostnamesInfo.forEach(hostnameInfo => {
- utils.addressHostToUrl(addressInfo, hostnameInfo).forEach(url => {
- // Onion
- if (hostnameInfo.kind === 'onion') {
- tor.push({ url })
- // IP
+ hostnames.forEach(h => {
+ const addresses = utils.addressHostToUrl(addressInfo, h)
+
+ addresses.forEach(url => {
+ if (h.kind === 'onion') {
+ tor.push({
+ label: `Tor${
+ addresses.length > 1
+ ? ` (${new URL(url).protocol.replace(':', '').toUpperCase()})`
+ : ''
+ }`,
+ url,
+ })
} else {
- // Domain
- if (hostnameInfo.hostname.kind === 'domain') {
- clearnet.push({ url })
- // Local
+ const hostnameKind = h.hostname.kind
+
+ if (hostnameKind === 'domain') {
+ clearnet.push({
+ label: 'Domain',
+ url,
+ acme: host.domains[h.hostname.domain]?.acme,
+ })
} else {
- const hostnameKind = hostnameInfo.hostname.kind
local.push({
label:
hostnameKind === 'local'
? 'Local'
- : `${hostnameInfo.networkInterfaceId} (${hostnameKind})`,
+ : `${h.networkInterfaceId} (${hostnameKind})`,
url,
})
}
@@ -99,4 +136,16 @@ export function getMultihostAddresses(
local,
tor,
}
+
+ // @TODO Aiden what was going on here in 036?
+ // return mappedAddresses.filter(
+ // (value, index, self) => index === self.findIndex(t => t.url === value.url),
+ // )
+}
+
+function getLabel(name: string, url: string, multiple: boolean) {}
+
+export type AddressDetails = {
+ label: string
+ url: string
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/dashboard/ui.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/dashboard/ui.component.ts
index 0a03d8e1b..6053ce806 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/dashboard/ui.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/dashboard/ui.component.ts
@@ -79,13 +79,18 @@ export class UILaunchComponent {
@tuiPure
getInterfaces(pkg?: PackageDataEntry): T.ServiceInterface[] {
return pkg
- ? Object.values(pkg.serviceInterfaces).filter(({ type }) => type === 'ui')
+ ? Object.values(pkg.serviceInterfaces).filter(
+ i =>
+ i.type === 'ui' &&
+ (i.addressInfo.scheme === 'http' ||
+ i.addressInfo.sslScheme === 'https'),
+ )
: []
}
- getHref(info?: T.ServiceInterface): string | null {
- return info && this.isRunning
- ? this.config.launchableAddress(info, this.pkg.hosts)
+ getHref(ui?: T.ServiceInterface): string | null {
+ return ui && this.isRunning
+ ? this.config.launchableAddress(ui, this.pkg.hosts)
: null
}
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts b/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts
index 5c5be1786..99643f5ca 100644
--- a/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/service/routes/interface.component.ts
@@ -6,7 +6,8 @@ import { PatchDB } from 'patch-db-client'
import { combineLatest, map } from 'rxjs'
import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component'
import { DataModel } from 'src/app/services/patch-db/data-model'
-import { getMultihostAddresses } from '../../../components/interfaces/interface.utils'
+import { getAddresses } from '../../../components/interfaces/interface.utils'
+import { ConfigService } from 'src/app/services/config.service'
@Component({
template: `
@@ -22,6 +23,7 @@ import { getMultihostAddresses } from '../../../components/interfaces/interface.
})
export class ServiceInterfaceRoute {
private readonly patch = inject>(PatchDB)
+ private readonly config = inject(ConfigService)
readonly context = {
packageId: getPkgId(),
@@ -40,7 +42,11 @@ export class ServiceInterfaceRoute {
]).pipe(
map(([iFace, hosts]) => ({
...iFace,
- addresses: getMultihostAddresses(iFace, hosts[iFace.addressInfo.hostId]),
+ addresses: getAddresses(
+ iFace,
+ hosts[iFace.addressInfo.hostId],
+ this.config,
+ ),
})),
)
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts
index 0843c38ba..c509b02ed 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/status.component.ts
@@ -1,12 +1,5 @@
-import {
- ChangeDetectionStrategy,
- Component,
- inject,
- Input,
-} from '@angular/core'
-import { Exver } from '@start9labs/shared'
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { TuiIcon } from '@taiga-ui/core'
-import { BackupTarget } from 'src/app/services/api/api.types'
import { BackupType } from '../types/backup-type'
@Component({
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts
index 1fecced81..385ee3c5f 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts
@@ -102,7 +102,8 @@ export class BackupsUpcomingComponent {
readonly targets = toSignal(from(this.api.getBackupTargets({})))
readonly current = toSignal(
inject>(PatchDB)
- .watch$('serverInfo', 'statusInfo', 'currentBackup', 'job')
+ // @TODO remove "as any" once this feature is real
+ .watch$('serverInfo', 'statusInfo', 'currentBackup' as any, 'job')
.pipe(map(job => job || {})),
)
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts
index 1291b3aff..8f724246b 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts
@@ -22,7 +22,6 @@ import {
DataModel,
PackageDataEntry,
} from 'src/app/services/patch-db/data-model'
-import { ClientStorageService } from 'src/app/services/client-storage.service'
import { MarketplaceService } from 'src/app/services/marketplace.service'
import { hasCurrentDeps } from 'src/app/utils/has-deps'
import { getAllPackages, getManifest } from 'src/app/utils/get-package-data'
@@ -60,16 +59,14 @@ import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
}
@case (0) {
- @if (showDevTools$ | async) {
-
- Reinstall
-
- }
+
+ Reinstall
+
}
}
}
@@ -114,8 +111,6 @@ export class MarketplaceControlsComponent {
@Input()
localFlavor!: boolean
- readonly showDevTools$ = inject(ClientStorageService).showDevTools$
-
async tryInstall() {
const currentUrl = await firstValueFrom(
this.marketplaceService.getRegistryUrl$(),
diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/acme/acme.component.ts
similarity index 81%
rename from web/projects/ui/src/app/pages/server-routes/acme/acme.page.ts
rename to web/projects/ui/src/app/routes/portal/routes/system/settings/routes/acme/acme.component.ts
index 5416194aa..7d017e5c9 100644
--- a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/acme/acme.component.ts
@@ -1,21 +1,31 @@
-import { Component } from '@angular/core'
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { PatchDB } from 'patch-db-client'
import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { DataModel } from '../../../services/patch-db/data-model'
-import { FormDialogService } from '../../../services/form-dialog.service'
-import { FormComponent } from '../../../components/form.component'
-import { configBuilderToSpec } from '../../../util/configBuilderToSpec'
import { ISB, utils } from '@start9labs/start-sdk'
-import { knownACME, toAcmeName } from 'src/app/util/acme'
+import { knownACME, toAcmeName } from 'src/app/utils/acme'
import { map } from 'rxjs'
+import { CommonModule } from '@angular/common'
+import { DataModel } from 'src/app/services/patch-db/data-model'
+import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
+import { FormDialogService } from 'src/app/services/form-dialog.service'
+import { FormComponent } from 'src/app/routes/portal/components/form.component'
@Component({
selector: 'acme',
- templateUrl: 'acme.page.html',
- styleUrls: ['acme.page.scss'],
+ template: ``,
+ styles: [],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+ imports: [CommonModule],
})
-export class ACMEPage {
+export class SettingsACMEComponent {
+ private readonly formDialog = inject(FormDialogService)
+ private readonly loader = inject(LoadingService)
+ private readonly errorService = inject(ErrorService)
+ private readonly patch = inject>(PatchDB)
+ private readonly api = inject(ApiService)
+
readonly docsUrl = 'https://docs.start9.com/0.3.6/user-manual/acme'
acme$ = this.patch.watch$('serverInfo', 'acme').pipe(
@@ -36,14 +46,6 @@ export class ACMEPage {
toAcmeName = toAcmeName
- constructor(
- private readonly loader: LoadingService,
- private readonly errorService: ErrorService,
- private readonly api: ApiService,
- private readonly patch: PatchDB,
- private readonly formDialog: FormDialogService,
- ) {}
-
async addAcme(
providers: {
url: string
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/email/email.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/email/email.component.ts
index 404008d23..7668fbebd 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/email/email.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/email/email.component.ts
@@ -28,6 +28,16 @@ import { EmailInfoComponent } from './info.component'
*ngIf="spec | async as resolved"
[spec]="resolved"
>
+
+ Delete
+
= configBuilderToSpec(
inputSpec.constants.customSmtp,
)
@@ -91,13 +103,19 @@ export class SettingsEmailComponent {
),
)
- async save(value: unknown): Promise {
+ async save(
+ value: typeof inputSpec.constants.customSmtp._TYPE | null,
+ ): Promise {
const loader = this.loader.open('Saving...').subscribe()
try {
- await this.api.configureEmail(
- inputSpec.constants.customSmtp.validator.unsafeCast(value),
- )
+ if (value) {
+ await this.api.setSmtp(value)
+ this.isSaved = true
+ } else {
+ await this.api.clearSmtp({})
+ this.isSaved = false
+ }
} catch (e: any) {
this.errorService.handleError(e)
} finally {
@@ -109,7 +127,7 @@ export class SettingsEmailComponent {
const loader = this.loader.open('Sending...').subscribe()
try {
- await this.api.testEmail({
+ await this.api.testSmtp({
to: this.testAddress,
...form.value,
})
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/experimental/experimental.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/experimental/experimental.component.ts
deleted file mode 100644
index 949c50a02..000000000
--- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/experimental/experimental.component.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import { CommonModule } from '@angular/common'
-import {
- ChangeDetectionStrategy,
- Component,
- inject,
- TemplateRef,
-} from '@angular/core'
-import { FormsModule } from '@angular/forms'
-import { ErrorService, LoadingService } from '@start9labs/shared'
-import {
- TuiAlertService,
- TuiDialogService,
- TuiIcon,
- TuiLabel,
- TuiTitle,
-} from '@taiga-ui/core'
-import { TUI_CONFIRM, TuiCheckbox } from '@taiga-ui/kit'
-import { PatchDB } from 'patch-db-client'
-import { filter } from 'rxjs'
-import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { ConfigService } from 'src/app/services/config.service'
-import { DataModel } from 'src/app/services/patch-db/data-model'
-
-@Component({
- template: `
-
-
-
-
-
Reset Tor
-
- Resetting the Tor daemon on your server may resolve Tor connectivity
- issues.
-
-
-
-
-
-
-
{{ server.zram ? 'Disable' : 'Enable' }} zram
-
- Zram creates compressed swap in memory, resulting in faster I/O for
- low RAM devices
-
-
-
-
-
-
-
- You are currently connected over Tor. If you reset the Tor daemon, you
- will lose connectivity until it comes back online.
-
- Reset Tor?
-
- Optionally wipe state to forcibly acquire new guard nodes. It is
- recommended to try without wiping state first.
-
-
-
- Wipe state
-
-
- `,
- changeDetection: ChangeDetectionStrategy.OnPush,
- standalone: true,
- imports: [
- CommonModule,
- FormsModule,
- TuiTitle,
- TuiIcon,
- TuiLabel,
- TuiCheckbox,
- ],
-})
-export class SettingsExperimentalComponent {
- private readonly loader = inject(LoadingService)
- private readonly errorService = inject(ErrorService)
- private readonly api = inject(ApiService)
- private readonly dialogs = inject(TuiDialogService)
- private readonly alerts = inject(TuiAlertService)
-
- readonly server$ = inject>(PatchDB).watch$('server-info')
- readonly isTor = inject(ConfigService).isTor()
-
- wipe = false
-
- reset(content: TemplateRef) {
- this.wipe = false
- this.dialogs
- .open(TUI_CONFIRM, {
- label: this.isTor ? 'Warning' : 'Confirm',
- data: {
- content,
- yes: 'Reset',
- no: 'Cancel',
- },
- })
- .pipe(filter(Boolean))
- .subscribe(() => this.resetTor(this.wipe))
- }
-
- private async resetTor(wipeState: boolean) {
- const loader = this.loader.open('Resetting Tor...').subscribe()
-
- try {
- await this.api.resetTor({
- 'wipe-state': wipeState,
- reason: 'User triggered',
- })
- this.alerts.open('Tor reset in progress').subscribe()
- } catch (e: any) {
- this.errorService.handleError(e)
- } finally {
- loader.unsubscribe()
- }
- }
-}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/interfaces/ui.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/interfaces/ui.component.ts
index 188c1118c..f3e913ad0 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/interfaces/ui.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/interfaces/ui.component.ts
@@ -5,11 +5,29 @@ import { PatchDB } from 'patch-db-client'
import { Observable, map } from 'rxjs'
import {
InterfaceComponent,
- ServiceInterfaceWithAddresses,
+ MappedServiceInterface,
} from 'src/app/routes/portal/components/interfaces/interface.component'
-import { getMultihostAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
+import { getAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
+import { ConfigService } from 'src/app/services/config.service'
import { DataModel } from 'src/app/services/patch-db/data-model'
+const iface: T.ServiceInterface = {
+ id: '',
+ name: 'StartOS User Interface',
+ description:
+ 'The primary user interface for your StartOS server, accessible from any browser.',
+ type: 'ui' as const,
+ masked: false,
+ addressInfo: {
+ hostId: '',
+ internalPort: 80,
+ scheme: 'http',
+ sslScheme: 'https',
+ suffix: '',
+ username: null,
+ },
+}
+
@Component({
template: `
= inject(
- PatchDB,
+ private readonly config = inject(ConfigService)
+
+ readonly ui$: Observable = inject>(
+ PatchDB,
)
- .watch$('serverInfo', 'ui')
+ .watch$('serverInfo')
.pipe(
- map(hosts => {
- const serviceInterface: T.ServiceInterface = {
- id: 'startos-ui',
- name: 'StartOS UI',
- description: 'The primary web user interface for StartOS',
- type: 'ui',
- hasPrimary: false,
- masked: false,
- addressInfo: {
- hostId: '',
- username: null,
- internalPort: 80,
- scheme: 'http',
- sslScheme: 'https',
- suffix: '',
- },
- }
-
- // @TODO Aiden confirm this is correct
- const host: T.Host = {
- kind: 'multi',
- bindings: {},
- hostnameInfo: {
- 80: hosts,
- },
- addresses: [],
- }
-
- return {
- ...serviceInterface,
- addresses: getMultihostAddresses(serviceInterface, host),
- }
- }),
+ map(server => ({
+ ...iface,
+ public: server.host.bindings[iface.addressInfo.internalPort].net.public,
+ addresses: getAddresses(iface, server.host, this.config),
+ })),
)
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/router/router.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/router/router.component.ts
index bd365966b..a6f21ab00 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/router/router.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/router/router.component.ts
@@ -12,7 +12,7 @@ import { RouterPortComponent } from './table.component'
+ import('./routes/acme/acme.component').then(
+ m => m.SettingsACMEComponent,
+ ),
+ },
{
path: 'email',
loadComponent: () =>
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/settings.service.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/settings.service.ts
index 32cf7e5fd..19ea7f45e 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/settings/settings.service.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/settings.service.ts
@@ -107,27 +107,6 @@ export class SettingsService {
icon: '@tui.monitor',
routerLink: 'ui',
},
- {
- title: 'Restart',
- icon: '@tui.refresh-cw',
- description: 'Restart Start OS server',
- action: () => this.promptPower('Restart'),
- },
- {
- title: 'Shutdown',
- icon: '@tui.power',
- description: 'Turn Start OS server off',
- action: () => this.promptPower('Shutdown'),
- },
- {
- title: 'Logout',
- icon: '@tui.log-out',
- description: 'Log off from Start OS',
- action: () => {
- this.api.logout({}).catch(e => console.error('Failed to log out', e))
- this.auth.setUnverified()
- },
- },
],
'Privacy and Security': [
{
@@ -150,6 +129,29 @@ export class SettingsService {
routerLink: 'sessions',
},
],
+ Power: [
+ {
+ title: 'Restart',
+ icon: '@tui.refresh-cw',
+ description: 'Restart Start OS server',
+ action: () => this.promptPower('Restart'),
+ },
+ {
+ title: 'Shutdown',
+ icon: '@tui.power',
+ description: 'Turn Start OS server off',
+ action: () => this.promptPower('Shutdown'),
+ },
+ {
+ title: 'Logout',
+ icon: '@tui.log-out',
+ description: 'Log off from Start OS',
+ action: () => {
+ this.api.logout({}).catch(e => console.error('Failed to log out', e))
+ this.auth.setUnverified()
+ },
+ },
+ ],
}
private async setOutboundProxy(): Promise {
diff --git a/web/projects/ui/src/app/routing.module.ts b/web/projects/ui/src/app/routing.module.ts
index 0c6c0c89a..4432ac14d 100644
--- a/web/projects/ui/src/app/routing.module.ts
+++ b/web/projects/ui/src/app/routing.module.ts
@@ -17,7 +17,7 @@ const routes: Routes = [
},
{
path: 'login',
- canActivate: [UnauthGuard],
+ canActivate: [UnauthGuard, stateNot(['error', 'initializing'])],
loadChildren: () =>
import('./routes/login/login.module').then(m => m.LoginPageModule),
},
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 ec0c3c65f..70d34d5b9 100644
--- a/web/projects/ui/src/app/services/api/mock-patch.ts
+++ b/web/projects/ui/src/app/services/api/mock-patch.ts
@@ -7,7 +7,6 @@ export const mockPatchData: DataModel = {
ui: {
name: `Matt's Server`,
theme: 'Dark',
- desktop: ['lnd'],
marketplace: {
selectedUrl: 'https://registry.start9.com/',
knownHosts: {
@@ -32,6 +31,23 @@ export const mockPatchData: DataModel = {
id: 'abcdefgh',
version,
lastBackup: new Date(new Date().valueOf() - 604800001).toISOString(),
+ network: {
+ wifi: {
+ enabled: false,
+ interface: 'wlan0',
+ ssids: [],
+ selected: null,
+ lastRegion: null,
+ },
+ start9ToSubdomain: null,
+ domains: [],
+ wanConfig: {
+ upnp: true,
+ forwards: [],
+ },
+ proxies: [],
+ outboundProxy: null,
+ },
networkInterfaces: {
eth0: {
public: false,
@@ -72,11 +88,12 @@ export const mockPatchData: DataModel = {
packageVersionCompat: '>=0.3.0 <=0.3.6',
postInitMigrationTodos: [],
statusInfo: {
- currentBackup: null,
+ // currentBackup: null,
updated: false,
updateProgress: null,
restarting: false,
shuttingDown: false,
+ backupProgress: {},
},
hostname: 'random-words',
host: {
@@ -194,9 +211,8 @@ export const mockPatchData: DataModel = {
platform: 'x86_64-nonfree',
zram: true,
governor: 'performance',
- passwordHash:
- '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
- arch: 'x86_64',
+ ram: 8 * 1024 * 1024 * 1024,
+ devices: [],
},
packageData: {
bitcoind: {
diff --git a/web/projects/ui/src/app/services/config.service.ts b/web/projects/ui/src/app/services/config.service.ts
index 35509f6f1..b4de3405f 100644
--- a/web/projects/ui/src/app/services/config.service.ts
+++ b/web/projects/ui/src/app/services/config.service.ts
@@ -101,19 +101,7 @@ export class ConfigService {
}
/** ${scheme}://${username}@${host}:${externalPort}${suffix} */
- launchableAddress(
- interfaces: PackageDataEntry['serviceInterfaces'],
- hosts: T.Hosts,
- ): string {
- const ui = Object.values(interfaces).find(
- i =>
- i.type === 'ui' &&
- (i.addressInfo.scheme === 'http' ||
- i.addressInfo.sslScheme === 'https'),
- )
-
- if (!ui) return ''
-
+ launchableAddress(ui: T.ServiceInterface, hosts: T.Hosts): string {
const host = hosts[ui.addressInfo.hostId]
if (!host) return ''
diff --git a/web/projects/ui/src/app/services/eos.service.ts b/web/projects/ui/src/app/services/eos.service.ts
index c3331d1a5..7780261fd 100644
--- a/web/projects/ui/src/app/services/eos.service.ts
+++ b/web/projects/ui/src/app/services/eos.service.ts
@@ -20,7 +20,7 @@ export class EOSService {
)
readonly backingUp$ = this.patch
- .watch$('serverInfo', 'statusInfo', 'currentBackup')
+ .watch$('serverInfo', 'statusInfo', 'backupProgress')
.pipe(
map(obj => !!obj),
distinctUntilChanged(),
diff --git a/web/projects/ui/src/app/services/marketplace.service.ts b/web/projects/ui/src/app/services/marketplace.service.ts
index 3307090c1..b760c50a7 100644
--- a/web/projects/ui/src/app/services/marketplace.service.ts
+++ b/web/projects/ui/src/app/services/marketplace.service.ts
@@ -19,7 +19,6 @@ import {
switchMap,
distinctUntilChanged,
ReplaySubject,
- tap,
} from 'rxjs'
import { RR } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
diff --git a/web/projects/ui/src/app/services/patch-db/data-model.ts b/web/projects/ui/src/app/services/patch-db/data-model.ts
index ec94381ac..678d2448d 100644
--- a/web/projects/ui/src/app/services/patch-db/data-model.ts
+++ b/web/projects/ui/src/app/services/patch-db/data-model.ts
@@ -3,7 +3,16 @@ import { T } from '@start9labs/start-sdk'
export type DataModel = {
ui: UIData
- serverInfo: ServerInfo
+ serverInfo: Omit<
+ T.Public['serverInfo'],
+ 'wifi' | 'unreadNotificationCount'
+ > & {
+ network: NetworkInfo
+ unreadNotifications: {
+ count: number
+ recent: ServerNotifications
+ }
+ }
packageData: Record
}
@@ -17,7 +26,6 @@ export type UIData = {
}
ackInstructions: Record
theme: string
- desktop: readonly string[]
}
export type UIMarketplaceData = {
@@ -33,30 +41,6 @@ export type UIStore = {
name?: string
}
-export type ServerInfo = {
- id: string
- version: string
- country: string
- ui: T.HostnameInfo[]
- network: NetworkInfo
- lastBackup: string | null
- unreadNotifications: {
- count: number
- recent: ServerNotifications
- }
- statusInfo: ServerStatusInfo
- eosVersionCompat: string
- pubkey: string
- caFingerprint: string
- ntpSynced: boolean
- smtp: T.SmtpValue | null
- passwordHash: string
- platform: string
- arch: string
- governor: string | null
- zram: boolean
-}
-
export type NetworkInfo = {
wifi: WiFiInfo
start9ToSubdomain: Omit | null
diff --git a/web/projects/ui/src/app/utils/get-server-info.ts b/web/projects/ui/src/app/utils/get-server-info.ts
index 08eecd451..bf0880f91 100644
--- a/web/projects/ui/src/app/utils/get-server-info.ts
+++ b/web/projects/ui/src/app/utils/get-server-info.ts
@@ -1,9 +1,9 @@
import { PatchDB } from 'patch-db-client'
-import { DataModel, ServerInfo } from 'src/app/services/patch-db/data-model'
+import { DataModel } from 'src/app/services/patch-db/data-model'
import { firstValueFrom } from 'rxjs'
export async function getServerInfo(
patch: PatchDB,
-): Promise {
+): Promise {
return firstValueFrom(patch.watch$('serverInfo'))
}