diff --git a/ui/src/app/components/install-wizard/alert/alert.component.ts b/ui/src/app/components/install-wizard/alert/alert.component.ts index ba50dce1e..8e2427961 100644 --- a/ui/src/app/components/install-wizard/alert/alert.component.ts +++ b/ui/src/app/components/install-wizard/alert/alert.component.ts @@ -1,24 +1,16 @@ -import { Component, Input, OnInit } from '@angular/core' -import { BehaviorSubject, Subject } from 'rxjs' -import { Loadable } from '../loadable' +import { Component, Input } from '@angular/core' @Component({ selector: 'alert', templateUrl: './alert.component.html', styleUrls: ['../install-wizard.component.scss'], }) -export class AlertComponent implements OnInit, Loadable { +export class AlertComponent { @Input() params: { title: string message: string titleColor: string } - loading$ = new BehaviorSubject(false) - cancel$ = new Subject() - load () { } - - constructor () { } - ngOnInit () { } } diff --git a/ui/src/app/components/install-wizard/complete/complete.component.html b/ui/src/app/components/install-wizard/complete/complete.component.html index 800cb3d51..d918c3833 100644 --- a/ui/src/app/components/install-wizard/complete/complete.component.html +++ b/ui/src/app/components/install-wizard/complete/complete.component.html @@ -1,17 +1,4 @@ -
-
-
- - {{ successText }} - -
-
- {{ summary }} -
-
-
-
- {{ label }} + {{ message }}
\ No newline at end of file diff --git a/ui/src/app/components/install-wizard/complete/complete.component.module.ts b/ui/src/app/components/install-wizard/complete/complete.component.module.ts index cd509591a..fd0ac3ddd 100644 --- a/ui/src/app/components/install-wizard/complete/complete.component.module.ts +++ b/ui/src/app/components/install-wizard/complete/complete.component.module.ts @@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common' import { CompleteComponent } from './complete.component' import { IonicModule } from '@ionic/angular' import { RouterModule } from '@angular/router' -import { SharingModule } from 'src/app/modules/sharing.module' @NgModule({ declarations: [ @@ -13,7 +12,6 @@ import { SharingModule } from 'src/app/modules/sharing.module' CommonModule, IonicModule, RouterModule.forChild([]), - SharingModule, ], exports: [CompleteComponent], }) diff --git a/ui/src/app/components/install-wizard/complete/complete.component.ts b/ui/src/app/components/install-wizard/complete/complete.component.ts index a815b9b88..721d05f19 100644 --- a/ui/src/app/components/install-wizard/complete/complete.component.ts +++ b/ui/src/app/components/install-wizard/complete/complete.component.ts @@ -1,8 +1,8 @@ -import { Component, Input, OnInit } from '@angular/core' +import { Component, Input } from '@angular/core' import { BehaviorSubject, from, Subject } from 'rxjs' import { takeUntil } from 'rxjs/operators' import { capitalizeFirstLetter } from 'src/app/util/misc.util' -import { Loadable, markAsLoadingDuring$ } from '../loadable' +import { markAsLoadingDuring$ } from '../loadable' import { WizardAction } from '../wizard-types' @Component({ @@ -10,13 +10,12 @@ import { WizardAction } from '../wizard-types' templateUrl: './complete.component.html', styleUrls: ['../install-wizard.component.scss'], }) -export class CompleteComponent implements OnInit, Loadable { +export class CompleteComponent { @Input() params: { action: WizardAction verb: string // loader verb: '*stopping* ...' title: string executeAction: () => Promise - skipCompletionDialogue?: boolean } @Input() transitions: { @@ -27,61 +26,20 @@ export class CompleteComponent implements OnInit, Loadable { } loading$ = new BehaviorSubject(false) - color$ = new BehaviorSubject('medium') cancel$ = new Subject() - label: string - summary: string - successText: string + message: string load () { markAsLoadingDuring$(this.loading$, from(this.params.executeAction())).pipe(takeUntil(this.cancel$)).subscribe( { error: e => this.transitions.error(new Error(`${this.params.action} failed: ${e.message || e}`)), - complete: () => this.params.skipCompletionDialogue && this.transitions.final(), + complete: () => this.transitions.final(), }, ) } - constructor () { } ngOnInit () { - switch (this.params.action) { - case 'install': - this.summary = `Installation of ${this.params.title} is now in progress. You will receive a notification when the installation has completed.` - this.label = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...` - this.color$.next('primary') - this.successText = 'In Progress' - break - case 'downgrade': - this.summary = `Downgrade for ${this.params.title} is now in progress. You will receive a notification when the downgrade has completed.` - this.label = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...` - this.color$.next('primary') - this.successText = 'In Progress' - break - case 'update': - this.summary = `Update for ${this.params.title} is now in progress. You will receive a notification when the update has completed.` - this.label = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...` - this.color$.next('primary') - this.successText = 'In Progress' - break - case 'uninstall': - this.summary = `${capitalizeFirstLetter(this.params.title)} has been successfully uninstalled.` - this.label = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...` - this.color$.next('success') - this.successText = 'Success' - break - case 'stop': - this.summary = `${capitalizeFirstLetter(this.params.title)} has been successfully stopped.` - this.label = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...` - this.color$.next('success') - this.successText = 'Success' - break - case 'configure': - this.summary = `New config for ${this.params.title} has been successfully saved.` - this.label = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...` - this.color$.next('success') - this.successText = 'Success' - break - } + this.message = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...` } } diff --git a/ui/src/app/components/install-wizard/dependents/dependents.component.html b/ui/src/app/components/install-wizard/dependents/dependents.component.html index 0671f3c99..98fa8c3ab 100644 --- a/ui/src/app/components/install-wizard/dependents/dependents.component.html +++ b/ui/src/app/components/install-wizard/dependents/dependents.component.html @@ -16,11 +16,11 @@ -
- {{ longMessage }} +
+ {{ message }}
-
-
+
+
Affected Services
Promise, - skipConfirmationDialogue?: boolean + fetchBreakages: () => Promise } @Input() transitions: { cancel: () => any @@ -29,33 +28,28 @@ export class DependentsComponent implements OnInit, Loadable { dependentBreakages: Breakages hasDependentViolation: boolean - longMessage: string | null = null - color$ = new BehaviorSubject('medium') // this will display disabled while loading + message: string | null = null loading$ = new BehaviorSubject(false) cancel$ = new Subject() constructor ( public readonly patch: PatchDbService, ) { } - ngOnInit () { } load () { - this.color$.next('medium') - markAsLoadingDuring$(this.loading$, from(this.params.fetchBreakages())).pipe( + markAsLoadingDuring$(this.loading$, from(this.params.fetchBreakages())) + .pipe( takeUntil(this.cancel$), tap(breakages => this.dependentBreakages = breakages), - ).subscribe( + ) + .subscribe( { complete: () => { this.hasDependentViolation = this.dependentBreakages && !isEmptyObject(this.dependentBreakages) if (this.hasDependentViolation) { - this.longMessage = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title} will prohibit the following services from functioning properly and will cause them to stop if they are currently running.` - this.color$.next('warning') - } else if (this.params.skipConfirmationDialogue) { - this.transitions.next() + this.message = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title} will prohibit the following services from functioning properly and will cause them to stop if they are currently running.` } else { - this.longMessage = `No other services installed on your Embassy will be affected by this action.` - this.color$.next('success') + this.message = `No other services installed on your Embassy will be affected by this action.` } }, error: (e: Error) => this.transitions.error(new Error(`Fetching dependent service information failed: ${e.message || e}`)), diff --git a/ui/src/app/components/install-wizard/loadable.ts b/ui/src/app/components/install-wizard/loadable.ts index fbeb8ba4b..fd8db92d1 100644 --- a/ui/src/app/components/install-wizard/loadable.ts +++ b/ui/src/app/components/install-wizard/loadable.ts @@ -9,15 +9,15 @@ export interface Loadable { cancel$: Subject // will cancel load function } -export function markAsLoadingDuring$ ($trigger$: Subject, o: Observable): Observable { +export function markAsLoadingDuring$ (trigger$: Subject, o: Observable): Observable { let shouldBeOn = true const displayIfItsBeenAtLeast = 5 // ms return fromSync$(() => { - emitAfter$(displayIfItsBeenAtLeast).subscribe(() => { if (shouldBeOn) $trigger$.next(true) }) + emitAfter$(displayIfItsBeenAtLeast).subscribe(() => { if (shouldBeOn) trigger$.next(true) }) }).pipe( concatMap(() => o), finalize(() => { - $trigger$.next(false) + trigger$.next(false) shouldBeOn = false }), ) diff --git a/ui/src/app/components/install-wizard/notes/notes.component.ts b/ui/src/app/components/install-wizard/notes/notes.component.ts index bdf913f4e..1d5ce8941 100644 --- a/ui/src/app/components/install-wizard/notes/notes.component.ts +++ b/ui/src/app/components/install-wizard/notes/notes.component.ts @@ -1,13 +1,11 @@ -import { Component, Input, OnInit } from '@angular/core' -import { BehaviorSubject, Subject } from 'rxjs' -import { Loadable } from '../loadable' +import { Component, Input } from '@angular/core' @Component({ selector: 'notes', templateUrl: './notes.component.html', styleUrls: ['../install-wizard.component.scss'], }) -export class NotesComponent implements OnInit, Loadable { +export class NotesComponent { @Input() params: { notes: { [version: string]: string } title: string @@ -15,14 +13,8 @@ export class NotesComponent implements OnInit, Loadable { headline: string } - loading$ = new BehaviorSubject(false) - cancel$ = new Subject() - load () { } - constructor () { } - ngOnInit () { } - asIsOrder () { return 0 } diff --git a/ui/src/app/components/install-wizard/prebaked-wizards.ts b/ui/src/app/components/install-wizard/prebaked-wizards.ts index c928e00f4..f52b655b5 100644 --- a/ui/src/app/components/install-wizard/prebaked-wizards.ts +++ b/ui/src/app/components/install-wizard/prebaked-wizards.ts @@ -41,7 +41,6 @@ export class WizardBaker { slide: { selector: 'dependents', params: { - skipConfirmationDialogue: true, action, verb: 'updating', title, @@ -143,7 +142,6 @@ export class WizardBaker { { slide: { selector: 'dependents', params: { - skipConfirmationDialogue: true, action, verb: 'downgrading', title, diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.html b/ui/src/app/pages/apps-routes/app-show/app-show.page.html index 0ff560e14..67e85f596 100644 --- a/ui/src/app/pages/apps-routes/app-show/app-show.page.html +++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.html @@ -104,9 +104,9 @@ Menu - + -

{{ button.title }}

+

{{ button.title }}

{{ button.description }}

diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts index dbde1f305..ba60ce73c 100644 --- a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts +++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts @@ -64,7 +64,6 @@ export class AppShowPage { async ngOnInit () { this.pkgId = this.route.snapshot.paramMap.get('pkgId') - // this.setServiceValues(this.patch.data['package-data']) this.subs = [ // 1 diff --git a/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts b/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts index cfbce9de0..f48fe0fd8 100644 --- a/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts +++ b/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts @@ -48,8 +48,10 @@ export class ServerMetricsPage { async getMetrics (): Promise { try { const metrics = await this.embassyApi.getServerMetrics({ }) - Object.entries(metrics).forEach(([key, val]) => { - this.metrics[key] = val + Object.entries(metrics).forEach(([groupKey, groupVal]) => { + Object.entries(groupVal).forEach(([key, val]) => { + this.metrics[groupKey][key] = val + }) }) } catch (e) { this.errToast.present(e) diff --git a/ui/src/app/services/api/api.fixures.ts b/ui/src/app/services/api/api.fixures.ts index 4d81d7617..48e5c1859 100644 --- a/ui/src/app/services/api/api.fixures.ts +++ b/ui/src/app/services/api/api.fixures.ts @@ -19,313 +19,455 @@ export module Mock { } export const MockManifestBitcoind: Manifest = { - id: 'bitcoind', - title: 'Bitcoin Core', - version: '0.21.0', - description: { - short: 'A Bitcoin full node by Bitcoin Core.', - long: 'Bitcoin is a decentralized consensus protocol and settlement network.', + 'id': 'bitcoind', + 'title': 'Bitcoin Core', + 'version': '0.20.0', + 'description': { + 'short': 'A Bitcoin full node by Bitcoin Core.', + 'long': 'Bitcoin is a decentralized consensus protocol and settlement network.', }, 'release-notes': 'Taproot, Schnorr, and more.', - license: 'MIT', + 'license': 'MIT', 'wrapper-repo': 'https://github.com/start9labs/bitcoind-wrapper', 'upstream-repo': 'https://github.com/bitcoin/bitcoin', 'support-site': 'https://bitcoin.org', 'marketing-site': 'https://bitcoin.org', 'donation-url': 'https://start9.com', - alerts: { - install: 'Bitcoin can take over a week to sync.', - uninstall: 'Chain state will be lost, as will any funds stored on your Bitcoin Core waller that have not been backed up.', - restore: null, - start: null, - stop: 'Stopping Bitcoin is bad for your health.', + 'alerts': { + 'install': 'Bitcoin can take over a week to sync.', + 'uninstall': 'Chain state will be lost, as will any funds stored on your Bitcoin Core waller that have not been backed up.', + 'restore': null, + 'start': null, + 'stop': 'Stopping Bitcoin is bad for your health.', }, - main: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', + 'main': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', }, 'health-checks': { }, - config: { get: { } as any, set: { } as any }, - volumes: { }, + 'config': { + 'get': null, + 'set': null, + }, + 'volumes': { }, 'min-os-version': '0.2.12', - interfaces: { - ui: { - name: 'Node Visualizer', - description: 'Web application for viewing information about your node and the Bitcoin network.', - ui: true, - 'tor-config': { - 'port-mapping': { }, + 'interfaces': { + 'ui': { + 'name': 'Node Visualizer', + 'description': 'Web application for viewing information about your node and the Bitcoin network.', + 'ui': true, + 'tor-config': { + 'port-mapping': { }, + }, + 'lan-config': { }, + 'protocols': [], }, - 'lan-config': { }, - protocols: [], - }, - rpc: { - name: 'RPC', - description: 'Used by wallets to interact with your Bitcoin Core node.', - ui: false, - 'tor-config': { - 'port-mapping': { }, + 'rpc': { + 'name': 'RPC', + 'description': 'Used by wallets to interact with your Bitcoin Core node.', + 'ui': false, + 'tor-config': { + 'port-mapping': { }, + }, + 'lan-config': { }, + 'protocols': [], }, - 'lan-config': { }, - protocols: [], - }, - p2p: { - name: 'P2P', - description: 'Used by other Bitcoin nodes to communicate and interact with your node.', - ui: false, - 'tor-config': { - 'port-mapping': { }, + 'p2p': { + 'name': 'P2P', + 'description': 'Used by other Bitcoin nodes to communicate and interact with your node.', + 'ui': false, + 'tor-config': { + 'port-mapping': { }, + }, + 'lan-config': { }, + 'protocols': [], }, - 'lan-config': { }, - protocols: [], - }, }, - backup: { - create: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - }, - restore: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - }, - }, - migrations: null, - actions: { - resync: { - name: 'Resync Blockchain', - description: 'Use this to resync the Bitcoin blockchain from genesis', - warning: 'This will take a couple of days.', - 'allowed-statuses': [PackageMainStatus.Running, PackageMainStatus.Stopped], - implementation: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', + 'backup': { + 'create': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', + }, + 'restore': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', }, - 'input-spec': null, - }, }, - permissions: { }, - dependencies: { }, + 'migrations': null, + 'actions': { + 'resync': { + 'name': 'Resync Blockchain', + 'description': 'Use this to resync the Bitcoin blockchain from genesis', + 'warning': 'This will take a couple of days.', + 'allowed-statuses': [ + PackageMainStatus.Running, + PackageMainStatus.Stopped, + ], + 'implementation': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', + }, + 'input-spec': { + 'reason': { + 'type': 'string', + 'name': 'Re-sync Reason', + 'description': 'Your reason for re-syncing. Why are you doing this?', + 'nullable': false, + 'masked': false, + 'copyable': false, + 'pattern': '^[a-zA-Z]+$', + 'pattern-description': 'Must contain only letters.', + }, + 'name': { + 'type': 'string', + 'name': 'Your Name', + 'description': 'Tell the class your name.', + 'nullable': true, + 'masked': false, + 'copyable': false, + 'pattern': null, + 'pattern-description': null, + 'warning': 'You may loose all your money by providing your name.', + }, + 'notifications': { + 'name': 'Notification Preferences', + 'type': 'list', + 'subtype': 'enum', + 'description': 'how you want to be notified', + 'range': '[1,3]', + 'default': [ + 'email', + ], + 'spec': { + 'value-names': { + 'email': 'Email', + 'text': 'Text', + 'call': 'Call', + 'push': 'Push', + 'webhook': 'Webhook', + }, + 'values': [ + 'email', + 'text', + 'call', + 'push', + 'webhook', + ], + }, + }, + 'days-ago': { + 'type': 'number', + 'name': 'Days Ago', + 'description': 'Number of days to re-sync.', + 'nullable': false, + 'default': 100, + 'range': '[0, 9999]', + 'integral': true, + }, + 'top-speed': { + 'type': 'number', + 'name': 'Top Speed', + 'description': 'The fastest you can possibly run.', + 'nullable': false, + 'default': null, + 'range': '[-1000, 1000]', + 'integral': false, + 'units': 'm/s', + }, + 'testnet': { + 'name': 'Testnet', + 'type': 'boolean', + 'description': 'determines whether your node is running on testnet or mainnet', + 'warning': 'Chain will have to resync!', + 'default': false, + }, + 'randomEnum': { + 'name': 'Random Enum', + 'type': 'enum', + 'value-names': { + 'null': 'Null', + 'good': 'Good', + 'bad': 'Bad', + 'ugly': 'Ugly', + }, + 'default': 'null', + 'description': 'This is not even real.', + 'warning': 'Be careful changing this!', + 'values': [ + 'null', + 'good', + 'bad', + 'ugly', + ], + }, + 'emergency-contact': { + 'name': 'Emergency Contact', + 'type': 'object', + 'unique-by': null, + 'description': 'The person to contact in case of emergency.', + 'spec': { + 'name': { + 'type': 'string', + 'name': 'Name', + 'description': null, + 'nullable': false, + 'masked': false, + 'copyable': false, + 'pattern': '^[a-zA-Z]+$', + 'pattern-description': 'Must contain only letters.', + }, + 'email': { + 'type': 'string', + 'name': 'Email', + 'description': null, + 'nullable': false, + 'masked': false, + 'copyable': true, + }, + }, + }, + 'ips': { + 'name': 'Whitelist IPs', + 'type': 'list', + 'subtype': 'string', + 'description': 'external ip addresses that are authorized to access your Bitcoin node', + 'warning': 'Any IP you allow here will have RPC access to your Bitcoin node.', + 'range': '[1,10]', + 'default': [ + '192.168.1.1', + ], + 'spec': { + 'pattern': '^[0-9]{1,3}([,.][0-9]{1,3})?$', + 'pattern-description': 'Must be a valid IP address', + masked: false, + copyable: false, + }, + }, + 'bitcoinNode': { + 'name': 'Bitcoin Node Settings', + 'type': 'union', + 'unique-by': null, + 'description': 'The node settings', + 'default': 'internal', + 'warning': 'Careful changing this', + 'tag': { + 'id': 'type', + 'name': 'Type', + 'variant-names': { + 'internal': 'Internal', + 'external': 'External', + }, + }, + 'variants': { + 'internal': { + 'lan-address': { + 'name': 'LAN Address', + 'type': 'pointer', + 'subtype': 'app', + 'target': 'lan-address', + 'app-id': 'bitcoind', + 'description': 'the lan address', + }, + 'friendly-name': { + 'name': 'Friendly Name', + 'type': 'string', + 'description': 'the lan address', + 'nullable': true, + 'masked': false, + 'copyable': false, + }, + }, + 'external': { + 'public-domain': { + 'name': 'Public Domain', + 'type': 'string', + 'description': 'the public address of the node', + 'nullable': false, + 'default': 'bitcoinnode.com', + 'pattern': '.*', + 'pattern-description': 'anything', + 'masked': false, + 'copyable': true, + }, + }, + }, + }, + }, + }, + }, + 'permissions': { }, + 'dependencies': { }, } export const MockManifestLnd: Manifest = { - id: 'lnd', - title: 'LND', - version: '0.11.1', - description: { - short: 'A bolt spec compliant client.', - long: 'More info about LND. More info about LND. More info about LND.', + 'id': 'lnd', + 'title': 'Lightning Network Daemon', + 'version': '0.11.1', + 'description': { + 'short': 'A bolt spec compliant client.', + 'long': 'More info about LND. More info about LND. More info about LND.', }, 'release-notes': 'Dual funded channels!', - license: 'MIT', + 'license': 'MIT', 'wrapper-repo': 'https://github.com/start9labs/lnd-wrapper', 'upstream-repo': 'https://github.com/lightningnetwork/lnd', 'support-site': 'https://lightning.engineering/', 'marketing-site': 'https://lightning.engineering/', 'donation-url': null, - alerts: { - install: null, - uninstall: null, - restore: 'If this is a duplicate instance of the same LND node, you may loose your funds.', - start: 'Starting LND is good for your health.', - stop: null, + 'alerts': { + 'install': null, + 'uninstall': null, + 'restore': 'If this is a duplicate instance of the same LND node, you may loose your funds.', + 'start': 'Starting LND is good for your health.', + 'stop': null, }, - main: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', + 'main': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', }, 'health-checks': { }, - config: null, - volumes: { }, + 'config': { + 'get': null, + 'set': null, + }, + 'volumes': { }, 'min-os-version': '0.2.12', - interfaces: { - rpc: { - name: 'RPC interface', - description: 'Good for connecting to your node at a distance.', - ui: true, - 'tor-config': { - 'port-mapping': { }, + 'interfaces': { + 'rpc': { + 'name': 'RPC interface', + 'description': 'Good for connecting to your node at a distance.', + 'ui': true, + 'tor-config': { + 'port-mapping': { }, + }, + 'lan-config': { + '44': { + 'ssl': true, + 'mapping': 33, + }, + }, + 'protocols': [], }, - 'lan-config': { - 44: { - ssl: true, - mapping: 33, - }, + 'grpc': { + 'name': 'GRPC', + 'description': 'Certain wallet use grpc.', + 'ui': false, + 'tor-config': { + 'port-mapping': { }, + }, + 'lan-config': { + '66': { + 'ssl': true, + 'mapping': 55, + }, + }, + 'protocols': [], }, - protocols: [], - }, - grpc: { - name: 'GRPC', - description: 'Certain wallet use grpc.', - ui: false, - 'tor-config': { - 'port-mapping': { }, - }, - 'lan-config': { - 66: { - ssl: true, - mapping: 55, - }, - }, - protocols: [], - }, }, - backup: { - create: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - }, - restore: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - }, + 'backup': { + 'create': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', + }, + 'restore': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', + }, }, - migrations: null, - actions: { - resync: { - name: 'Resync Network Graph', - description: 'Your node will resync its network graph.', - warning: 'This will take a couple hours.', - 'allowed-statuses': [PackageMainStatus.Running], - implementation: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: { }, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', + 'migrations': null, + 'actions': { + 'resync': { + 'name': 'Resync Network Graph', + 'description': 'Your node will resync its network graph.', + 'warning': 'This will take a couple hours.', + 'allowed-statuses': [ + PackageMainStatus.Running, + ], + 'implementation': { + 'type': 'docker', + 'image': '', + 'system': true, + 'entrypoint': '', + 'args': [], + 'mounts': { }, + 'io-format': DockerIoFormat.Yaml, + 'inject': false, + 'shm-size': '', + }, + 'input-spec': null, }, - 'input-spec': { - label: { - type: 'string', - name: 'Name of Resync', - nullable: false, - masked: false, - copyable: false, - }, - }, - }, }, - permissions: { }, - dependencies: { - 'bitcoind': { - version: '=0.21.0', - requirement: { - type: 'opt-out', - how: 'You can use an external node if you prefer.', + 'permissions': { }, + 'dependencies': { + 'bitcoind': { + 'version': '=0.21.0', + 'description': 'LND needs bitcoin to live.', + 'requirement': { + 'type': 'opt-out', + 'how': 'You can use an external node from your Embassy if you prefer.', + }, + 'config': null, + 'critical': true, }, - description: 'LND needs bitcoin to live.', - critical: true, - config: { - check: { - type: 'docker', - image: 'alpine', - system: true, - entrypoint: 'true', - args: [], - mounts: { }, - 'io-format': DockerIoFormat.Cbor, - inject: false, - 'shm-size': '10m', - }, - 'auto-configure': { - type: 'docker', - image: 'alpine', - system: true, - entrypoint: 'cat', - args: [], - mounts: { }, - 'io-format': DockerIoFormat.Cbor, - inject: false, - 'shm-size': '10m', - }, + 'bitcoin-proxy': { + 'version': '>=0.2.2', + 'description': 'As long as Bitcoin is pruned, LND needs Bitcoin Proxy to fetch block over the P2P network.', + 'requirement': { + 'type': 'opt-in', + 'how': 'To use Proxy\'s user management system, go to LND config and select Bitcoin Proxy under Bitcoin config.', + }, + 'config': null, + 'critical': true, }, - }, - 'bitcoin-proxy': { - version: '>=0.2.2', - description: 'As long as Bitcoin is pruned, LND needs Bitcoin Proxy to fetch block over the P2P network.', - requirement: { - type: 'opt-in', - how: 'You can choose to use Bitcoin Proxy for permission management', - }, - critical: true, - config: { - check: { - type: 'docker', - image: 'alpine', - system: true, - entrypoint: 'true', - args: [], - mounts: { }, - 'io-format': DockerIoFormat.Cbor, - inject: false, - 'shm-size': '10m', - }, - 'auto-configure': { - type: 'docker', - image: 'alpine', - system: true, - entrypoint: 'cat', - args: [], - mounts: { }, - 'io-format': DockerIoFormat.Cbor, - inject: false, - 'shm-size': '10m', - }, - }, - }, }, } @@ -586,60 +728,6 @@ export module Mock { export const MarketplacePkgsList: RR.GetMarketplacePackagesRes = Object.values(Mock.MarketplacePkgs).map(service => service['latest']) - export const Pkgs: { [key: string]: PackageDataEntry } = { - 'bitcoin-proxy': { - state: PackageState.Installed, - 'static-files': { - license: 'licenseUrl', // /public/package-data/bitcoinproxy/0.21.1/LICENSE.md, - icon: 'assets/img/service-icons/bitcoin-proxy.png', - instructions: 'instructionsUrl', // /public/package-data/bitcoinproxy/0.2.2/INSTRUCTIONS.md - }, - manifest: MockManifestBitcoinProxy, - installed: { - status: { - configured: true, - main: { - status: PackageMainStatus.Running, - started: new Date().toISOString(), - health: { }, - }, - 'dependency-errors': { }, - }, - manifest: MockManifestBitcoinProxy, - 'interface-addresses': { - rpc: { - 'tor-address': 'bitcoinproxy-rpc-address.onion', - 'lan-address': 'bitcoinproxy-rpc-address.local', - }, - }, - 'system-pointers': [], - 'current-dependents': { - 'lnd': { - pointers: [], - 'health-checks': [], - }, - }, - 'current-dependencies': { - 'bitcoind': { - pointers: [], - 'health-checks': [], - }, - }, - 'dependency-info': { - 'lnd': { - manifest: Mock.MockManifestLnd, - icon: 'assets/img/service-icons/lnd.png', - }, - 'bitcoind': { - manifest: Mock.MockManifestBitcoind, - icon: 'assets/img/service-icons/bitcoind.png', - }, - }, - }, - 'install-progress': undefined, - }, - } - export const Notifications: ServerNotifications = [ { id: 1, @@ -1424,144 +1512,156 @@ export module Mock { rpcauth: ['matt: 8273gr8qwoidm1uid91jeh8y23gdio1kskmwejkdnm'], } - // export const bitcoind: PackageDataEntry = { - // state: PackageState.Installed, - // 'static-files': { - // license: 'licenseUrl', // /public/package-data/bitcoind/0.21.1/LICENSE.md, - // icon: 'assets/img/service-icons/bitcoind.png', - // instructions: 'instructionsUrl', // /public/package-data/bitcoind/0.21.1/INSTRUCTIONS.md - // }, - // manifest: { - // ...MockManifestBitcoind, - // version: '0.20.0', - // }, - // installed: { - // status: { - // configured: true, - // main: { - // status: PackageMainStatus.Running, - // started: new Date().toISOString(), - // health: { }, - // }, - // 'dependency-errors': { }, - // }, - // 'interface-info': { - // ip: '10.0.0.1', - // addresses: { - // ui: { - // 'tor-address': 'bitcoind-ui-address.onion', - // 'lan-address': 'bitcoind-ui-address.local', - // }, - // rpc: { - // 'tor-address': 'bitcoind-rpc-address.onion', - // 'lan-address': 'bitcoind-rpc-address.local', - // }, - // p2p: { - // 'tor-address': 'bitcoind-p2p-address.onion', - // 'lan-address': 'bitcoind-p2p-address.local', - // }, - // }, - // }, - // 'system-pointers': [], - // 'current-dependents': { - // 'lnd': { - // pointers: [], - // 'health-checks': [], - // }, - // }, - // 'current-dependencies': { }, - // }, - // 'install-progress': undefined, - // } + export const bitcoind: PackageDataEntry = { + state: PackageState.Installed, + 'static-files': { + 'license': '/public/package-data/bitcoind/0.20.0/LICENSE.md', + 'icon': '/assets/img/service-icons/bitcoind.png', + 'instructions': '/public/package-data/bitcoind/0.20.0/INSTRUCTIONS.md', + }, + manifest: MockManifestBitcoind, + installed: { + manifest: MockManifestBitcoind, + status: { + configured: true, + main: { + status: PackageMainStatus.Running, + started: new Date().toISOString(), + health: { }, + }, + 'dependency-errors': { }, + }, + 'interface-addresses': { + rpc: { + 'tor-address': 'bitcoinproxy-rpc-address.onion', + 'lan-address': 'bitcoinproxy-rpc-address.local', + }, + }, + 'system-pointers': [], + 'current-dependents': { + 'lnd': { + pointers: [], + 'health-checks': [], + }, + }, + 'current-dependencies': { }, + 'dependency-info': { }, + }, + 'install-progress': undefined, + } - // export const lnd: PackageDataEntry = { - // state: PackageState.Installed, - // 'static-files': { - // license: 'licenseUrl', // /public/package-data/lnd/0.21.1/LICENSE.md, - // icon: 'assets/img/service-icons/lnd.png', - // instructions: 'instructionsUrl', // /public/package-data/lnd/0.21.1/INSTRUCTIONS.md - // }, - // manifest: MockManifestLnd, - // installed: { - // status: { - // configured: true, - // main: { - // status: PackageMainStatus.Stopped, - // }, - // 'dependency-errors': { - // 'bitcoin-proxy': { - // type: DependencyErrorType.NotInstalled, - // }, - // }, - // }, - // manifest: MockManifestLnd, - // 'interface-addresses': { - // rpc: { - // 'tor-address': 'lnd-rpc-address.onion', - // 'lan-address': 'lnd-rpc-address.local', - // }, - // grpc: { - // 'tor-address': 'lnd-grpc-address.onion', - // 'lan-address': 'lnd-grpc-address.local', - // }, - // }, - // 'system-pointers': [], - // 'current-dependents': { }, - // 'current-dependencies': { - // 'bitcoind': { - // pointers: [], - // 'health-checks': [], - // }, - // 'bitcoin-proxy': { - // pointers: [], - // 'health-checks': [], - // }, - // }, - // 'dependency-info': { - // 'bitcoind': { - // manifest: Mock.MockManifestBitcoind, - // icon: 'assets/img/service-icons/bitcoind.png', - // }, - // 'bitcoin-proxy': { - // manifest: Mock.MockManifestBitcoinProxy, - // icon: 'assets/img/service-icons/bitcoin-proxy.png', - // }, - // }, - // }, - // 'install-progress': undefined, - // } + export const bitcoinProxy: PackageDataEntry = { + state: PackageState.Installed, + 'static-files': { + 'license': '/public/package-data/bitcoin-proxy/0.20.0/LICENSE.md', + 'icon': '/assets/img/service-icons/bitcoin-proxy.png', + 'instructions': '/public/package-data/bitcoin-proxy/0.20.0/INSTRUCTIONS.md', + }, + manifest: MockManifestBitcoinProxy, + installed: { + status: { + configured: true, + main: { + status: PackageMainStatus.Running, + started: new Date().toISOString(), + health: { }, + }, + 'dependency-errors': { }, + }, + manifest: MockManifestBitcoinProxy, + 'interface-addresses': { + rpc: { + 'tor-address': 'bitcoinproxy-rpc-address.onion', + 'lan-address': 'bitcoinproxy-rpc-address.local', + }, + }, + 'system-pointers': [], + 'current-dependents': { + 'lnd': { + pointers: [], + 'health-checks': [], + }, + }, + 'current-dependencies': { + 'bitcoind': { + pointers: [], + 'health-checks': [], + }, + }, + 'dependency-info': { + 'lnd': { + manifest: Mock.MockManifestLnd, + icon: 'assets/img/service-icons/lnd.png', + }, + 'bitcoind': { + manifest: Mock.MockManifestBitcoind, + icon: 'assets/img/service-icons/bitcoind.png', + }, + }, + }, + 'install-progress': undefined, + } - // export const DbDump: RR.GetDumpRes = { - // id: 1, - // expireId: null, - // value: { - // 'server-info': { - // id: 'start9-abcdefgm', - // version: '1.0.0', - // status: ServerStatus.Running, - // 'lan-address': 'start9-abcdefgh.local', - // 'tor-address': 'myveryownspecialtoraddress.onion', - // wifi: { - // ssids: ['Goosers', 'Goosers5G'], - // connected: 'Goosers5G', - // }, - // 'eos-marketplace': 'https://registry.start9.com', - // 'package-marketplace': 'https://registry.start9.com', - // 'unread-notification-count': 4, - // specs: { - // cpu: 'Cortex-A72: 4 Cores @1500MHz', - // disk: '1TB SSD', - // memory: '8GB', - // }, - // }, - // 'package-data': { - // 'bitcoind': bitcoind, - // 'lnd': lnd, - // }, - // ui: { - // 'welcome-ack': '1.0.0', - // 'auto-check-updates': true, - // }, - // }, - // } + export const lnd: PackageDataEntry = { + state: PackageState.Installed, + 'static-files': { + 'license': '/public/package-data/lnd/0.20.0/LICENSE.md', + 'icon': '/assets/img/service-icons/lnd.png', + 'instructions': '/public/package-data/lnd/0.20.0/INSTRUCTIONS.md', + }, + manifest: MockManifestLnd, + installed: { + status: { + configured: true, + main: { + status: PackageMainStatus.Stopped, + }, + 'dependency-errors': { + 'bitcoin-proxy': { + type: DependencyErrorType.NotInstalled, + }, + }, + }, + manifest: MockManifestLnd, + 'interface-addresses': { + rpc: { + 'tor-address': 'lnd-rpc-address.onion', + 'lan-address': 'lnd-rpc-address.local', + }, + grpc: { + 'tor-address': 'lnd-grpc-address.onion', + 'lan-address': 'lnd-grpc-address.local', + }, + }, + 'system-pointers': [], + 'current-dependents': { }, + 'current-dependencies': { + 'bitcoind': { + pointers: [], + 'health-checks': [], + }, + 'bitcoin-proxy': { + pointers: [], + 'health-checks': [], + }, + }, + 'dependency-info': { + 'bitcoind': { + manifest: Mock.MockManifestBitcoind, + icon: 'assets/img/service-icons/bitcoind.png', + }, + 'bitcoin-proxy': { + manifest: Mock.MockManifestBitcoinProxy, + icon: 'assets/img/service-icons/bitcoin-proxy.png', + }, + }, + }, + 'install-progress': undefined, + } + + export const LocalPkgs: { [key: string]: PackageDataEntry } = { + 'bitcoind': bitcoind, + 'bitcoin-proxy': bitcoinProxy, + 'lnd': lnd, + } } diff --git a/ui/src/app/services/api/embassy-mock-api.service.ts b/ui/src/app/services/api/embassy-mock-api.service.ts index 7f0b6ee9f..eba735be3 100644 --- a/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/ui/src/app/services/api/embassy-mock-api.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core' import { pauseFor } from '../../util/misc.util' import { ApiService } from './embassy-api.service' import { PatchOp } from 'patch-db-client' -import { InstallProgress, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/services/patch-db/data-model' +import { DependencyErrorType, InstallProgress, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/services/patch-db/data-model' import { RR, WithRevision } from './api.types' import { parsePropertiesPermissive } from 'src/app/util/properties.util' import { Mock } from './api.fixures' @@ -333,12 +333,14 @@ export class MockApiService extends ApiService { unpacked: 0, 'unpack-complete': false, } + const pkg: PackageDataEntry = { - ...Mock.Pkgs[params.id], + ...Mock.LocalPkgs[params.id], state: PackageState.Installing, 'install-progress': initialProgress, installed: undefined, } + const patch = [ { op: PatchOp.ADD, @@ -346,6 +348,7 @@ export class MockApiService extends ApiService { value: pkg, }, ] + const res = await this.http.rpcRequest>({ method: 'db.patch', params: { patch } }) setTimeout(async () => { this.updateProgress(params.id, initialProgress) @@ -411,12 +414,16 @@ export class MockApiService extends ApiService { async startPackageRaw (params: RR.StartPackageReq): Promise { await pauseFor(2000) - const path = `/package-data/${params.id}/installed/status/main/status` + const path = `/package-data/${params.id}/installed/status/main` const patch = [ { op: PatchOp.REPLACE, path, - value: PackageMainStatus.Running, + value: { + status: PackageMainStatus.Running, + started: new Date().toISOString(), // UTC date string + health: { }, + }, }, ] return this.http.rpcRequest>({ method: 'db.patch', params: { patch } }) @@ -424,17 +431,26 @@ export class MockApiService extends ApiService { async dryStopPackage (params: RR.DryStopPackageReq): Promise { await pauseFor(2000) - return { } + return { + 'lnd': { + dependency: 'bitcoind', + error: { + type: DependencyErrorType.NotRunning, + }, + }, + } } async stopPackageRaw (params: RR.StopPackageReq): Promise { await pauseFor(2000) - const path = `/package-data/${params.id}/installed/status/main/status` + const path = `/package-data/${params.id}/installed/status/main` const patch = [ { op: PatchOp.REPLACE, path, - value: PackageMainStatus.Stopping, + value: { + status: PackageMainStatus.Stopping, + }, }, ] const res = await this.http.rpcRequest>({ method: 'db.patch', params: { patch } }) @@ -442,7 +458,7 @@ export class MockApiService extends ApiService { const patch = [ { op: PatchOp.REPLACE, - path, + path: path + '/status', value: PackageMainStatus.Stopped, }, ] @@ -540,7 +556,7 @@ export class MockApiService extends ApiService { { op: PatchOp.REPLACE, path: `/package-data/${id}`, - value: Mock.Pkgs[id], + value: Mock.LocalPkgs[id], }, ] this.http.rpcRequest>({ method: 'db.patch', params: { patch } })