round out adding new domains

This commit is contained in:
Matt Hill
2023-07-14 12:53:26 -06:00
parent 19f5e92a74
commit bd0ddafcd0
6 changed files with 162 additions and 57 deletions

View File

@@ -2,7 +2,7 @@ import { Config } from '@start9labs/start-sdk/lib/config/builder/config'
import { Value } from '@start9labs/start-sdk/lib/config/builder/value' import { Value } from '@start9labs/start-sdk/lib/config/builder/value'
import { Variants } from '@start9labs/start-sdk/lib/config/builder/variants' import { Variants } from '@start9labs/start-sdk/lib/config/builder/variants'
const ddnsOptions = Config.of({ const auth = Config.of({
username: Value.text({ username: Value.text({
name: 'Username', name: 'Username',
required: { default: null }, required: { default: null },
@@ -14,7 +14,46 @@ const ddnsOptions = Config.of({
}), }),
}) })
export const domainSpec = Config.of({ const strategyUnion = Value.union(
{
name: 'Networking Strategy',
required: { default: 'router' },
},
Variants.of({
router: {
name: 'Router',
spec: Config.of({
ip: Value.select({
name: 'IP Strategy',
description: `
<h5>IPv6 Only</h5><b>Pros</b>: Ready for IPv6 Internet. Enhanced privacy, as IPv6 addresses are less correlated with geographic area
<b>Cons</b>: Your website is only accessible to people who's ISP supports IPv6
<h5>IPv6 and IPv4</h5><b>Pros</b>: Ready for IPv6 Internet. Anyone can access your website
<b>Cons</b>: IPv4 addresses are closely correlated with geographic areas
<h5>IPv4 Only</h5><b>Pros</b>: Anyone can access your website
<b>Cons</b>: IPv4 addresses are closely correlated with geographic areas
`,
required: { default: 'ipv6' },
values: {
ipv6: 'IPv6 Only',
both: 'IPv6 and IPv4',
ipv4: 'IPv4 Only',
},
}),
}),
},
reverseProxy: {
name: 'Reverse Proxy',
spec: Config.of({}),
},
}),
)
export const start9MeSpec = Config.of({
strategy: strategyUnion,
})
export const customSpec = Config.of({
hostname: Value.text({ hostname: Value.text({
name: 'Hostname', name: 'Hostname',
required: { default: null }, required: { default: null },
@@ -32,30 +71,32 @@ export const domainSpec = Config.of({
}, },
duckdns: { duckdns: {
name: 'Duck DNS', name: 'Duck DNS',
spec: ddnsOptions, spec: auth,
}, },
dyn: { dyn: {
name: 'DynDNS', name: 'DynDNS',
spec: ddnsOptions, spec: auth,
}, },
easydns: { easydns: {
name: 'easyDNS', name: 'easyDNS',
spec: ddnsOptions, spec: auth,
}, },
googledomains: { googledomains: {
name: 'Google Domains', name: 'Google Domains',
spec: ddnsOptions, spec: auth,
}, },
namecheap: { namecheap: {
name: 'Namecheap (IPv4 only)', name: 'Namecheap (IPv4 only)',
spec: ddnsOptions, spec: auth,
}, },
zoneedit: { zoneedit: {
name: 'Zoneedit', name: 'Zoneedit',
spec: ddnsOptions, spec: auth,
}, },
}), }),
), ),
strategy: strategyUnion,
}) })
export type DomainSpec = typeof domainSpec.validator._TYPE export type Start9MeSpec = typeof start9MeSpec.validator._TYPE
export type CustomSpec = typeof customSpec.validator._TYPE

View File

@@ -24,7 +24,7 @@
class="ion-padding-start" class="ion-padding-start"
strong strong
size="small" size="small"
(click)="presentAlertClaimStart9MeDomain()" (click)="presentModalClaimStart9Me()"
> >
<ion-icon slot="start" name="add-outline"></ion-icon> <ion-icon slot="start" name="add-outline"></ion-icon>
Claim Claim
@@ -34,20 +34,24 @@
<div class="grid-fixed"> <div class="grid-fixed">
<ion-grid class="ion-padding"> <ion-grid class="ion-padding">
<ion-row class="grid-headings"> <ion-row class="grid-headings">
<ion-col size="3">Domain</ion-col> <ion-col size="2">Domain</ion-col>
<ion-col size="2.5">Added</ion-col> <ion-col size="2">Added</ion-col>
<ion-col size="2.5">DDNS Provider</ion-col> <ion-col size="2">DDNS Provider</ion-col>
<ion-col size="2">In Use</ion-col> <ion-col size="1.5">Network Strategy</ion-col>
<ion-col size="2"></ion-col> <ion-col size="2">IP Strategy</ion-col>
<ion-col size="1.5">In Use</ion-col>
<ion-col size="1"></ion-col>
</ion-row> </ion-row>
<ion-row <ion-row
*ngIf="domains.start9Me as start9Me" *ngIf="domains.start9Me as start9Me"
class="ion-align-items-center grid-row-border" class="ion-align-items-center grid-row-border"
> >
<ion-col size="3">{{ start9Me.value }}</ion-col> <ion-col size="2">{{ start9Me.value }}</ion-col>
<ion-col size="2.5">{{ start9Me.createdAt| date: 'medium' }}</ion-col> <ion-col size="2">{{ start9Me.createdAt| date: 'short' }}</ion-col>
<ion-col size="2.5">Start9</ion-col> <ion-col size="2">Start9</ion-col>
<ion-col size="2" *ngIf="start9Me.usedBy as usedBy"> <ion-col size="1.5">{{ start9Me.networkStrategy }}</ion-col>
<ion-col size="2">{{ start9Me.ipStrategy || 'N/A' }}</ion-col>
<ion-col size="1.5" *ngIf="start9Me.usedBy as usedBy">
<a <a
*ngIf="usedBy.length as qty; else unused" *ngIf="usedBy.length as qty; else unused"
(click)="presentAlertUsedBy(start9Me.value, usedBy)" (click)="presentAlertUsedBy(start9Me.value, usedBy)"
@@ -58,7 +62,7 @@
<span>N/A</span> <span>N/A</span>
</ng-template> </ng-template>
</ion-col> </ion-col>
<ion-col size="2"> <ion-col size="1">
<ion-buttons style="float: right"> <ion-buttons style="float: right">
<ion-button size="small" (click)="presentAlertDeleteStart9Me()"> <ion-button size="small" (click)="presentAlertDeleteStart9Me()">
<ion-icon name="trash"></ion-icon> <ion-icon name="trash"></ion-icon>
@@ -85,20 +89,24 @@
<div class="grid-fixed"> <div class="grid-fixed">
<ion-grid class="ion-padding"> <ion-grid class="ion-padding">
<ion-row class="grid-headings"> <ion-row class="grid-headings">
<ion-col size="3">Domain</ion-col> <ion-col size="2">Domain</ion-col>
<ion-col size="2.5">Added</ion-col> <ion-col size="2">Added</ion-col>
<ion-col size="2.5">DDNS Provider</ion-col> <ion-col size="2">DDNS Provider</ion-col>
<ion-col size="2">In Use</ion-col> <ion-col size="1.5">Network Strategy</ion-col>
<ion-col size="2"></ion-col> <ion-col size="2">IP Strategy</ion-col>
<ion-col size="1.5">In Use</ion-col>
<ion-col size="1"></ion-col>
</ion-row> </ion-row>
<ion-row <ion-row
*ngFor="let domain of domains.custom" *ngFor="let domain of domains.custom"
class="ion-align-items-center grid-row-border" class="ion-align-items-center grid-row-border"
> >
<ion-col size="3">{{ domain.value }}</ion-col> <ion-col size="2">{{ domain.value }}</ion-col>
<ion-col size="2.5">{{ domain.createdAt| date: 'medium' }}</ion-col> <ion-col size="2">{{ domain.createdAt| date: 'short' }}</ion-col>
<ion-col size="2.5">{{ domain.provider.unionSelectKey }}</ion-col> <ion-col size="2">{{ domain.provider }}</ion-col>
<ion-col size="2" *ngIf="domain.usedBy as usedBy"> <ion-col size="1.5">{{ domain.networkStrategy }}</ion-col>
<ion-col size="2">{{ domain.ipStrategy || 'N/A' }}</ion-col>
<ion-col size="1.5" *ngIf="domain.usedBy as usedBy">
<a <a
*ngIf="usedBy.length as qty; else unused" *ngIf="usedBy.length as qty; else unused"
(click)="presentAlertUsedBy(domain.value, usedBy)" (click)="presentAlertUsedBy(domain.value, usedBy)"
@@ -109,7 +117,7 @@
<span>N/A</span> <span>N/A</span>
</ng-template> </ng-template>
</ion-col> </ion-col>
<ion-col size="2"> <ion-col size="1">
<ion-buttons style="float: right"> <ion-buttons style="float: right">
<ion-button <ion-button
size="small" size="small"

View File

@@ -7,7 +7,12 @@ import { PatchDB } from 'patch-db-client'
import { ApiService } from 'src/app/services/api/embassy-api.service' import { ApiService } from 'src/app/services/api/embassy-api.service'
import { DataModel } from 'src/app/services/patch-db/data-model' import { DataModel } from 'src/app/services/patch-db/data-model'
import { FormDialogService } from 'src/app/services/form-dialog.service' import { FormDialogService } from 'src/app/services/form-dialog.service'
import { DomainSpec, domainSpec } from './domain.const' import {
start9MeSpec,
Start9MeSpec,
customSpec,
CustomSpec,
} from './domain.const'
import { ConnectionService } from 'src/app/services/connection.service' import { ConnectionService } from 'src/app/services/connection.service'
import { FormContext, FormPage } from '../../../modals/form/form.page' import { FormContext, FormPage } from '../../../modals/form/form.page'
import { getClearnetAddress } from 'src/app/util/clearnetAddress' import { getClearnetAddress } from 'src/app/util/clearnetAddress'
@@ -23,7 +28,7 @@ export class DomainsPage {
readonly server$ = this.patch.watch$('server-info') readonly server$ = this.patch.watch$('server-info')
readonly pkgs$ = this.patch.watch$('package-data').pipe(first()) readonly pkgs$ = this.patch.watch$('package-data').pipe(first())
readonly domains$ = this.connectionService.connected$.pipe( readonly domains$ = this.connectionService.websocketConnected$.pipe(
filter(Boolean), filter(Boolean),
switchMap(() => switchMap(() =>
combineLatest([this.server$, this.pkgs$]).pipe( combineLatest([this.server$, this.pkgs$]).pipe(
@@ -35,6 +40,8 @@ export class DomainsPage {
value: `${start9MeSubdomain.value}.start9.me`, value: `${start9MeSubdomain.value}.start9.me`,
createdAt: start9MeSubdomain.createdAt, createdAt: start9MeSubdomain.createdAt,
provider: 'Start9', provider: 'Start9',
networkStrategy: start9MeSubdomain.networkStrategy,
ipStrategy: start9MeSubdomain.ipStrategy,
usedBy: usedBy( usedBy: usedBy(
start9MeSubdomain.value, start9MeSubdomain.value,
getClearnetAddress('https', ui.domainInfo), getClearnetAddress('https', ui.domainInfo),
@@ -45,6 +52,8 @@ export class DomainsPage {
value: domain.value, value: domain.value,
createdAt: domain.createdAt, createdAt: domain.createdAt,
provider: domain.provider, provider: domain.provider,
networkStrategy: domain.networkStrategy,
ipStrategy: domain.ipStrategy,
usedBy: usedBy( usedBy: usedBy(
domain.value, domain.value,
getClearnetAddress('https', ui.domainInfo), getClearnetAddress('https', ui.domainInfo),
@@ -69,10 +78,10 @@ export class DomainsPage {
) {} ) {}
async presentModalAdd() { async presentModalAdd() {
const options: Partial<TuiDialogOptions<FormContext<DomainSpec>>> = { const options: Partial<TuiDialogOptions<FormContext<CustomSpec>>> = {
label: 'Custom Domain', label: 'Custom Domain',
data: { data: {
spec: await domainSpec.build({} as any), spec: await customSpec.build({} as any),
buttons: [ buttons: [
{ {
text: 'Save', text: 'Save',
@@ -84,19 +93,20 @@ export class DomainsPage {
this.formDialog.open(FormPage, options) this.formDialog.open(FormPage, options)
} }
presentAlertClaimStart9MeDomain() { async presentModalClaimStart9Me() {
this.dialogs const options: Partial<TuiDialogOptions<FormContext<Start9MeSpec>>> = {
.open(TUI_PROMPT, { label: 'start9.me',
label: 'Confirm', data: {
size: 's', spec: await start9MeSpec.build({} as any),
data: { buttons: [
content: 'Claim your start9.me domain?', {
yes: 'Claim', text: 'Save',
no: 'Cancel', handler: async value => this.claimStart9MeDomain(value),
}, },
}) ],
.pipe(filter(Boolean)) },
.subscribe(() => this.claimStart9MeDomain()) }
this.formDialog.open(FormPage, options)
} }
presentAlertDelete(hostname: string) { presentAlertDelete(hostname: string) {
@@ -143,11 +153,17 @@ export class DomainsPage {
.subscribe() .subscribe()
} }
private async claimStart9MeDomain(): Promise<boolean> { private async claimStart9MeDomain(value: Start9MeSpec): Promise<boolean> {
const loader = this.loader.open('Saving...').subscribe() const loader = this.loader.open('Saving...').subscribe()
const networkStrategy = value.strategy.unionSelectKey
try { try {
await this.api.claimStart9MeDomain({}) await this.api.claimStart9MeDomain({
networkStrategy,
ipStrategy:
networkStrategy === 'router' ? value.strategy.unionValueKey.ip : null,
})
return true return true
} catch (e: any) { } catch (e: any) {
this.errorService.handleError(e) this.errorService.handleError(e)
@@ -157,11 +173,30 @@ export class DomainsPage {
} }
} }
private async save(value: DomainSpec): Promise<boolean> { private async save(value: CustomSpec): Promise<boolean> {
const loader = this.loader.open('Saving...').subscribe() const loader = this.loader.open('Saving...').subscribe()
const networkStrategy = value.strategy.unionSelectKey
const providerName = value.provider.unionSelectKey
try { try {
await this.api.addDomain(value) await this.api.addDomain({
hostname: value.hostname,
provider: {
name: providerName,
username:
providerName === 'start9'
? null
: value.provider.unionValueKey.username,
password:
providerName === 'start9'
? null
: value.provider.unionValueKey.password,
},
networkStrategy,
ipStrategy:
networkStrategy === 'router' ? value.strategy.unionValueKey.ip : null,
})
return true return true
} catch (e: any) { } catch (e: any) {
this.errorService.handleError(e) this.errorService.handleError(e)

View File

@@ -8,7 +8,10 @@ import {
} from 'src/app/services/patch-db/data-model' } from 'src/app/services/patch-db/data-model'
import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared' import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared'
import { customSmtp } from '@start9labs/start-sdk/lib/config/configConstants' import { customSmtp } from '@start9labs/start-sdk/lib/config/configConstants'
import { DomainSpec } from 'src/app/apps/ui/pages/system/domains/domain.const' import {
CustomSpec,
Start9MeSpec,
} from 'src/app/apps/ui/pages/system/domains/domain.const'
export module RR { export module RR {
// DB // DB
@@ -112,13 +115,25 @@ export module RR {
// domains // domains
export type ClaimStart9MeReq = {} // net.domain.me.claim export type ClaimStart9MeReq = {
networkStrategy: string
ipStrategy: string | null
} // net.domain.me.claim
export type ClaimStart9MeRes = null export type ClaimStart9MeRes = null
export type DeleteStart9MeReq = {} // net.domain.me.delete export type DeleteStart9MeReq = {} // net.domain.me.delete
export type DeleteStart9MeRes = null export type DeleteStart9MeRes = null
export type AddDomainReq = DomainSpec // net.domain.add export type AddDomainReq = {
hostname: string
provider: {
name: string
username: string | null
password: string | null
}
networkStrategy: string
ipStrategy: string | null
} // net.domain.add
export type AddDomainRes = null export type AddDomainRes = null
export type DeleteDomainReq = { hostname: string } // net.domain.delete export type DeleteDomainReq = { hostname: string } // net.domain.delete

View File

@@ -452,6 +452,8 @@ export class MockApiService extends ApiService {
value: { value: {
value: 'xyz', value: 'xyz',
createdAt: new Date(), createdAt: new Date(),
networkStrategy: params.networkStrategy,
ipStrategy: params.ipStrategy,
}, },
}, },
] ]
@@ -482,7 +484,9 @@ export class MockApiService extends ApiService {
value: [ value: [
{ {
value: params.hostname, value: params.hostname,
provider: params.provider, provider: params.provider.name,
networkStrategy: params.networkStrategy,
ipStrategy: params.ipStrategy,
createdAt: new Date(), createdAt: new Date(),
}, },
], ],

View File

@@ -3,7 +3,7 @@ import { Url } from '@start9labs/shared'
import { Manifest } from '@start9labs/marketplace' import { Manifest } from '@start9labs/marketplace'
import { BackupJob } from '../api/api.types' import { BackupJob } from '../api/api.types'
import { customSmtp } from '@start9labs/start-sdk/lib/config/configConstants' import { customSmtp } from '@start9labs/start-sdk/lib/config/configConstants'
import { DomainSpec } from 'src/app/apps/ui/pages/system/domains/domain.const' import { CustomSpec } from 'src/app/apps/ui/pages/system/domains/domain.const'
export interface DataModel { export interface DataModel {
'server-info': ServerInfo 'server-info': ServerInfo
@@ -104,7 +104,9 @@ export type WiFiInfo = {
export type Domain = { export type Domain = {
value: string value: string
provider: DomainSpec['provider'] provider: string
networkStrategy: string
ipStrategy: string
createdAt: string createdAt: string
} }