diff --git a/ui/package-lock.json b/ui/package-lock.json
index 69c275baf..afefd7a91 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "embassy-ui",
- "version": "0.2.14",
+ "version": "0.3.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "embassy-ui",
- "version": "0.2.14",
+ "version": "0.3.0",
"dependencies": {
"@angular/common": "^11.0.0",
"@angular/core": "^11.0.0",
diff --git a/ui/package.json b/ui/package.json
index 30d5fa299..d95699bba 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "embassy-ui",
- "version": "0.2.14",
+ "version": "0.3.0",
"description": "GUI for EmbassyOS",
"author": "Start9 Labs",
"homepage": "https://github.com/Start9Labs/embassy-ui",
diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts
index f37485c7e..c47740c7b 100644
--- a/ui/src/app/app.component.ts
+++ b/ui/src/app/app.component.ts
@@ -3,7 +3,7 @@ import { Storage } from '@ionic/storage'
import { AuthService, AuthState } from './services/auth.service'
import { ApiService } from './services/api/embassy/embassy-api.service'
import { Router, RoutesRecognized } from '@angular/router'
-import { debounceTime, distinctUntilChanged, filter, finalize, takeWhile } from 'rxjs/operators'
+import { debounceTime, distinctUntilChanged, filter, finalize, skip, take, takeWhile } from 'rxjs/operators'
import { AlertController, IonicSafeString, ToastController } from '@ionic/angular'
import { LoaderService } from './services/loader.service'
import { Emver } from './services/emver.service'
@@ -14,6 +14,8 @@ import { HttpService } from './services/http.service'
import { ServerStatus } from './services/patch-db/data-model'
import { ConnectionFailure, ConnectionService } from './services/connection.service'
import { StartupAlertsService } from './services/startup-alerts.service'
+import { ConfigService } from './services/config.service'
+import { isEmptyObject } from './util/misc.util'
@Component({
selector: 'app-root',
@@ -63,6 +65,7 @@ export class AppComponent {
private readonly startupAlertsService: StartupAlertsService,
private readonly toastCtrl: ToastController,
private readonly patch: PatchDbService,
+ private readonly config: ConfigService,
readonly splitPane: SplitPaneTracker,
) {
// set dark theme
@@ -83,23 +86,33 @@ export class AppComponent {
.subscribe(auth => {
// VERIFIED
if (auth === AuthState.VERIFIED) {
- this.http.authReqEnabled = true
- this.showMenu = true
this.patch.start()
- this.connectionService.start()
- // watch connection to display connectivity issues
- this.watchConnection(auth)
- // watch router to highlight selected menu item
- this.watchRouter(auth)
- // watch status to display/hide maintenance page
- this.watchStatus(auth)
- // watch unread notification count to display toast
- this.watchNotifications(auth)
- // run startup alerts
- this.startupAlertsService.runChecks()
+
+ this.patch.watch$()
+ .pipe(
+ filter(data => !isEmptyObject(data)),
+ take(1),
+ )
+ .subscribe(_ => {
+ this.showMenu = true
+ this.router.navigate([''], { replaceUrl: true })
+ this.connectionService.start()
+ // watch connection to display connectivity issues
+ this.watchConnection(auth)
+ // watch router to highlight selected menu item
+ this.watchRouter(auth)
+ // watch status to display/hide maintenance page
+ this.watchStatus(auth)
+ // watch version to refresh browser window
+ this.watchVersion(auth)
+ // watch unread notification count to display toast
+ this.watchNotifications(auth)
+ // run startup alerts
+ this.startupAlertsService.runChecks()
+ })
+
// UNVERIFIED
} else if (auth === AuthState.UNVERIFIED) {
- this.http.authReqEnabled = false
this.showMenu = false
this.connectionService.stop()
this.patch.stop()
@@ -178,16 +191,30 @@ export class AppComponent {
)
.subscribe(status => {
const maintenance = '/maintenance'
- const url = this.router.url
- if (status === ServerStatus.Running && url.startsWith(maintenance)) {
+ const route = this.router.url
+ console.log('STATUS', status, 'URL', route)
+ if (status === ServerStatus.Running && route.startsWith(maintenance)) {
this.router.navigate([''], { replaceUrl: true })
}
- if ([ServerStatus.Updating, ServerStatus.BackingUp].includes(status) && !url.startsWith(maintenance)) {
+ if ([ServerStatus.Updating, ServerStatus.BackingUp].includes(status) && !route.startsWith(maintenance)) {
+ this.showMenu = false
this.router.navigate([maintenance], { replaceUrl: true })
}
})
}
+ private watchVersion (auth: AuthState): void {
+ this.patch.watch$('server-info', 'version')
+ .pipe(
+ takeWhile(() => auth === AuthState.VERIFIED),
+ )
+ .subscribe(version => {
+ if (this.emver.compare(this.config.version, version) !== 0) {
+ this.presentAlertRefreshNeeded()
+ }
+ })
+ }
+
private watchNotifications (auth: AuthState): void {
let previous: number
this.patch.watch$('server-info', 'unread-notification-count')
@@ -202,7 +229,25 @@ export class AppComponent {
})
}
+ async presentAlertRefreshNeeded () {
+ const alert = await this.alertCtrl.create({
+ backdropDismiss: false,
+ header: 'Refresh Needed',
+ message: 'Your EmbassyOS UI is out of date. Hard refresh the page to get the latest UI.',
+ buttons: [
+ {
+ text: 'Refresh Page',
+ handler: () => {
+ location.reload()
+ },
+ },
+ ],
+ })
+ await alert.present()
+ }
+
async presentAlertLogout () {
+ // @TODO warn user no way to recover Embassy if logout and forget password. Maybe require password to logout?
const alert = await this.alertCtrl.create({
backdropDismiss: false,
header: 'Caution',
diff --git a/ui/src/app/guards/maintenance.guard.ts b/ui/src/app/guards/maintenance.guard.ts
index b234b48af..206e7eaa1 100644
--- a/ui/src/app/guards/maintenance.guard.ts
+++ b/ui/src/app/guards/maintenance.guard.ts
@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core'
import { CanActivate, Router, CanActivateChild } from '@angular/router'
-import { tap } from 'rxjs/operators'
import { ServerStatus } from '../services/patch-db/data-model'
import { PatchDbService } from '../services/patch-db/patch-db.service'
diff --git a/ui/src/app/guards/unmaintenance.guard.ts b/ui/src/app/guards/unmaintenance.guard.ts
index 884946ba2..aa10dd0e4 100644
--- a/ui/src/app/guards/unmaintenance.guard.ts
+++ b/ui/src/app/guards/unmaintenance.guard.ts
@@ -14,8 +14,8 @@ export class UnmaintenanceGuard implements CanActivate {
private readonly router: Router,
private readonly patch: PatchDbService,
) {
- this.patch.sequence$.subscribe(_ => {
- this.serverStatus = this.patch.data['server-info'].status
+ this.patch.watch$('server-info', 'status').subscribe(status => {
+ this.serverStatus = status
})
}
diff --git a/ui/src/app/modals/os-welcome/os-welcome.page.html b/ui/src/app/modals/os-welcome/os-welcome.page.html
index dfdb6e784..8558ffb0b 100644
--- a/ui/src/app/modals/os-welcome/os-welcome.page.html
+++ b/ui/src/app/modals/os-welcome/os-welcome.page.html
@@ -1,7 +1,7 @@
- Welcome to 0.2.14!
+ Welcome to {{ version }}!
@@ -14,7 +14,7 @@
-
+
Begin
diff --git a/ui/src/app/pages/login/login.page.ts b/ui/src/app/pages/login/login.page.ts
index 5f50be739..f0efb80d1 100644
--- a/ui/src/app/pages/login/login.page.ts
+++ b/ui/src/app/pages/login/login.page.ts
@@ -1,7 +1,6 @@
import { Component } from '@angular/core'
-import { NavController } from '@ionic/angular'
+import { LoadingController } from '@ionic/angular'
import { AuthService } from 'src/app/services/auth.service'
-import { LoaderService } from 'src/app/services/loader.service'
@Component({
selector: 'login',
@@ -12,15 +11,18 @@ export class LoginPage {
password = ''
unmasked = false
error = ''
+ loader: HTMLIonLoadingElement
constructor (
private readonly authService: AuthService,
- private readonly loader: LoaderService,
- private readonly navCtrl: NavController,
+ private readonly loadingCtrl: LoadingController,
) { }
- ionViewDidEnter () {
- this.error = ''
+ ngOnDestroy () {
+ if (this.loader) {
+ this.loader.dismiss()
+ this.loader = undefined
+ }
}
toggleMask () {
@@ -28,14 +30,21 @@ export class LoginPage {
}
async submit () {
+ this.error = ''
+
+ this.loader = await this.loadingCtrl.create({
+ message: 'Authenticating',
+ spinner: 'lines',
+ })
+ await this.loader.present()
+
try {
- await this.loader.displayDuringP(
- this.authService.login(this.password),
- )
+ await this.authService.login(this.password)
+ this.loader.message = 'Loading Embassy Data'
this.password = ''
- await this.navCtrl.navigateForward(['/'])
} catch (e) {
this.error = e.message
+ this.loader.dismiss()
}
}
}
diff --git a/ui/src/app/pages/maintenance/maintenance.page.html b/ui/src/app/pages/maintenance/maintenance.page.html
index f9717e20d..b7258715e 100644
--- a/ui/src/app/pages/maintenance/maintenance.page.html
+++ b/ui/src/app/pages/maintenance/maintenance.page.html
@@ -1,10 +1,10 @@
-
+
- Updating Embassy
+ Embassy is updating
Embassy is backing up
diff --git a/ui/src/app/services/api/api.fixures.ts b/ui/src/app/services/api/api.fixures.ts
index c6a80ea4a..c2cc6315f 100644
--- a/ui/src/app/services/api/api.fixures.ts
+++ b/ui/src/app/services/api/api.fixures.ts
@@ -1,12 +1,15 @@
-import { DependencyErrorType, DockerIoFormat, Manifest, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/services/patch-db/data-model'
-import { MarketplacePkg, Metric, NotificationLevel, RR, ServerNotification, ServerNotifications } from './api.types'
+import { DockerIoFormat, Manifest, PackageDataEntry, PackageMainStatus, PackageState } from 'src/app/services/patch-db/data-model'
+import { MarketplacePkg, Metric, NotificationLevel, RR, ServerNotifications } from './api.types'
export module Mock {
export const MarketplaceEos: RR.GetMarketplaceEOSRes = {
- version: '1.0.0',
+ version: '0.3.1',
headline: 'Our biggest release ever.',
- 'release-notes': { '1.0.0': 'Some **Markdown** release _notes_' },
+ 'release-notes': {
+ '0.3.1': 'Some **Markdown** release _notes_ for 0.3.1',
+ '0.3.0': 'Some **Markdown** release _notes_ from a prior version',
+ },
}
export const ReleaseNotes: RR.GetReleaseNotesRes = {
@@ -375,7 +378,7 @@ export module Mock {
},
}
- export const AvailableShow: {
+ export const MarketplacePkgs: {
[id: string]: {
[version: string]: MarketplacePkg
}
@@ -513,108 +516,7 @@ export module Mock {
},
}
- export const AvailableList: RR.GetMarketplacePackagesRes = Object.values(Mock.AvailableShow).map(service => service['latest'])
-
- 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 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,
- title: Mock.MockManifestBitcoinProxy.title,
- icon: 'assets/img/service-icons/bitcoin-proxy.png',
- },
- },
- },
- 'interface-info': {
- ip: '10.0.0.1',
- 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': [],
- },
- },
- },
- 'install-progress': undefined,
- }
+ export const MarketplacePkgsList: RR.GetMarketplacePackagesRes = Object.values(Mock.MarketplacePkgs).map(service => service['latest'])
export const bitcoinproxy: PackageDataEntry = {
state: PackageState.Installed,
@@ -660,68 +562,27 @@ export module Mock {
'install-progress': undefined,
}
- export const DbDump: RR.GetDumpRes = {
- id: 1,
- expireId: null,
- value: {
- 'server-info': {
- id: 'start9-abcdefgmm',
- version: '1.0.0',
- status: ServerStatus.Running,
- 'lan-address': 'start9-abcdefgh.local',
- 'tor-address': 'myveryownspecialtoraddress.onion',
- wifi: {
- ssids: ['Goosers', 'Goosers5G'],
- selected: 'Goosers5G',
- connected: 'Goosers5G',
- },
- 'package-marketplace': 'https://registry.start9.com',
- 'eos-marketplace': 'https://registry.start9.com',
- 'unread-notification-count': 4,
- specs: {
- CPU: 'Cortex-A72: 4 Cores @1500MHz',
- Disk: '1TB SSD',
- Memory: '8GB',
- },
- 'connection-addresses': {
- tor: [],
- clearnet: [],
- },
- },
- 'package-data': {
- 'bitcoind': bitcoind,
- 'lnd': lnd,
- },
- ui: {
- 'welcome-ack': '1.0.0',
- 'auto-check-updates': true,
- },
- },
- }
-
- export const notification1: ServerNotification<1> = {
- id: '123e4567-e89b-12d3-a456-426655440000',
- 'package-id': null,
- 'created-at': '2019-12-26T14:20:30.872Z',
- code: 1,
- level: NotificationLevel.Success,
- title: 'Backup Complete',
- message: 'Embassy and services have been successfully backed up.',
- data: {
- server: {
- attempted: true,
- error: null,
- },
- packages: {
- 'bitcoind': {
+ export const Notifications: ServerNotifications = [
+ {
+ id: '123e4567-e89b-12d3-a456-426655440000',
+ 'package-id': null,
+ 'created-at': '2019-12-26T14:20:30.872Z',
+ code: 1,
+ level: NotificationLevel.Success,
+ title: 'Backup Complete',
+ message: 'Embassy and services have been successfully backed up.',
+ data: {
+ server: {
+ attempted: true,
error: null,
},
+ packages: {
+ 'bitcoind': {
+ error: null,
+ },
+ },
},
},
- }
-
- export const Notifications: ServerNotifications = [
- notification1,
{
id: '123e4567-e89b-12d3-a456-426655440001',
'package-id': 'bitcoind',
@@ -1435,4 +1296,144 @@ export module Mock {
rpcallowip: [],
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 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,
+ // title: Mock.MockManifestBitcoinProxy.title,
+ // icon: 'assets/img/service-icons/bitcoin-proxy.png',
+ // },
+ // },
+ // },
+ // 'interface-info': {
+ // ip: '10.0.0.1',
+ // 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': [],
+ // },
+ // },
+ // },
+ // 'install-progress': undefined,
+ // }
+
+ // export const DbDump: RR.GetDumpRes = {
+ // id: 1,
+ // expireId: null,
+ // value: {
+ // 'server-info': {
+ // id: 'start9-abcdefgmm',
+ // version: '1.0.0',
+ // status: ServerStatus.Running,
+ // 'lan-address': 'start9-abcdefgh.local',
+ // 'tor-address': 'myveryownspecialtoraddress.onion',
+ // wifi: {
+ // ssids: ['Goosers', 'Goosers5G'],
+ // selected: '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',
+ // },
+ // 'connection-addresses': {
+ // tor: ['http://privacy34kn4ez3y3nijweec6w4g54i3g54sdv7r5mr6soma3w4begyd.onion'],
+ // clearnet: ['https://start9.com'],
+ // },
+ // },
+ // 'package-data': {
+ // 'bitcoind': bitcoind,
+ // 'lnd': lnd,
+ // },
+ // ui: {
+ // 'welcome-ack': '1.0.0',
+ // 'auto-check-updates': true,
+ // },
+ // },
+ // }
}
diff --git a/ui/src/app/services/api/embassy/embassy-api.service.ts b/ui/src/app/services/api/embassy/embassy-api.service.ts
index 12ca20593..2d52620eb 100644
--- a/ui/src/app/services/api/embassy/embassy-api.service.ts
+++ b/ui/src/app/services/api/embassy/embassy-api.service.ts
@@ -66,7 +66,7 @@ export abstract class ApiService implements Source, Http {
// )()
// password
- abstract updatePassword (params: RR.UpdatePasswordReq): Promise
+ // abstract updatePassword (params: RR.UpdatePasswordReq): Promise
// notification
diff --git a/ui/src/app/services/api/embassy/embassy-live-api.service.ts b/ui/src/app/services/api/embassy/embassy-live-api.service.ts
index 1a0826fea..10bc5b3d3 100644
--- a/ui/src/app/services/api/embassy/embassy-live-api.service.ts
+++ b/ui/src/app/services/api/embassy/embassy-live-api.service.ts
@@ -83,9 +83,9 @@ export class LiveApiService extends ApiService {
// }
// password
- async updatePassword (params: RR.UpdatePasswordReq): Promise {
- return this.http.rpcRequest({ method: 'password.set', params })
- }
+ // async updatePassword (params: RR.UpdatePasswordReq): Promise {
+ // return this.http.rpcRequest({ method: 'password.set', params })
+ // }
// notification
diff --git a/ui/src/app/services/api/embassy/embassy-mock-api.service.ts b/ui/src/app/services/api/embassy/embassy-mock-api.service.ts
index 515a7c65b..f384cf0eb 100644
--- a/ui/src/app/services/api/embassy/embassy-mock-api.service.ts
+++ b/ui/src/app/services/api/embassy/embassy-mock-api.service.ts
@@ -1,7 +1,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 { Operation, PatchOp } from 'patch-db-client'
import { 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'
@@ -12,7 +12,7 @@ import { ConfigService } from '../../config.service'
@Injectable()
export class MockApiService extends ApiService {
- welcomeAck = false
+ private readonly revertTime = 4000
constructor (
private readonly http: HttpService,
@@ -32,7 +32,7 @@ export class MockApiService extends ApiService {
// ...Mock.DbDump,
// id: this.nextSequence(),
// }
- return this.http.rpcRequest({ method: 'db.revisions', params: { since } })
+ return this.http.rpcRequest({ method: 'db.revisions', params: { since } })
}
async getDump (): Promise {
@@ -41,7 +41,7 @@ export class MockApiService extends ApiService {
// ...Mock.DbDump,
// id: this.nextSequence(),
// }
- return this.http.rpcRequest({ method: 'db.dump' })
+ return this.http.rpcRequest({ method: 'db.dump' })
}
async setDbValueRaw (params: RR.SetDBValueReq): Promise {
@@ -60,7 +60,7 @@ export class MockApiService extends ApiService {
// expireId: null,
// },
// }
- return this.http.rpcRequest({ method: 'db.put.ui', params })
+ return this.http.rpcRequest>({ method: 'db.put.ui', params })
}
// auth
@@ -94,14 +94,41 @@ export class MockApiService extends ApiService {
async updateServerRaw (params: RR.UpdateServerReq): Promise {
await pauseFor(2000)
+ const path = '/server-info/status'
const patch = [
{
op: PatchOp.REPLACE,
- path: '/server-info/status',
+ path,
value: ServerStatus.Updating,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ const res = await this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ setTimeout(() => {
+ const patch = [
+ {
+ op: PatchOp.REPLACE,
+ path,
+ value: ServerStatus.Running,
+ },
+ {
+ op: PatchOp.REPLACE,
+ path: '/server-info/version',
+ value: this.config.version + '.1',
+ },
+ ]
+ this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ // quickly revert patch to proper version to prevent infinite refresh loop
+ const patch2 = [
+ {
+ op: PatchOp.REPLACE,
+ path: '/server-info/version',
+ value: this.config.version,
+ },
+ ]
+ this.http.rpcRequest>({ method: 'db.patch', params: { patch: patch2 } })
+ }, this.revertTime)
+
+ return res
}
async restartServer (params: RR.RestartServerReq): Promise {
@@ -135,7 +162,7 @@ export class MockApiService extends ApiService {
value: params.url,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ return this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
}
// async setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise {
@@ -147,14 +174,14 @@ export class MockApiService extends ApiService {
// value: params.url,
// },
// ]
- // return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ // return this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
// }
// password
- async updatePassword (params: RR.UpdatePasswordReq): Promise {
- await pauseFor(2000)
- return null
- }
+ // async updatePassword (params: RR.UpdatePasswordReq): Promise {
+ // await pauseFor(2000)
+ // return null
+ // }
// notification
@@ -167,7 +194,7 @@ export class MockApiService extends ApiService {
value: 0,
},
]
- const { revision } = await this.http.rpcRequest({ method: 'db.patch', params: { patch } }) as WithRevision
+ const { revision } = await this.http.rpcRequest>({ method: 'db.patch', params: { patch } }) as WithRevision
return {
response: Mock.Notifications,
revision,
@@ -200,24 +227,28 @@ export class MockApiService extends ApiService {
value: params.ssid,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ return this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
}
async deleteWifiRaw (params: RR.DeleteWifiReq): Promise {
await pauseFor(2000)
- const patch = [
+ const patch: Operation[] = [
{
- op: PatchOp.REPLACE,
- path: '/server-info/wifi/selected',
- value: null,
- },
- {
- op: PatchOp.REPLACE,
- path: '/server-info/wifi/connected',
- value: null,
+ op: PatchOp.REMOVE,
+ path: `/server-info/wifi/ssids/${params.ssid}`,
},
+ // {
+ // op: PatchOp.REPLACE,
+ // path: '/server-info/wifi/selected',
+ // value: null,
+ // },
+ // {
+ // op: PatchOp.REPLACE,
+ // path: '/server-info/wifi/connected',
+ // value: null,
+ // },
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ return this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
}
// ssh
@@ -241,14 +272,26 @@ export class MockApiService extends ApiService {
async createBackupRaw (params: RR.CreateBackupReq): Promise {
await pauseFor(2000)
+ const path = '/server-info/status'
const patch = [
{
op: PatchOp.REPLACE,
- path: '/server-info/status',
+ path,
value: ServerStatus.BackingUp,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ const res = await this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ setTimeout(() => {
+ const patch = [
+ {
+ op: PatchOp.REPLACE,
+ path,
+ value: ServerStatus.Running,
+ },
+ ]
+ this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ }, this.revertTime)
+ return res
}
async restoreBackupRaw (params: RR.RestoreBackupReq): Promise {
@@ -302,7 +345,7 @@ export class MockApiService extends ApiService {
value: pkg,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ return this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
}
async dryUpdatePackage (params: RR.DryUpdatePackageReq): Promise {
@@ -328,25 +371,32 @@ export class MockApiService extends ApiService {
path: `/package-data/${params.id}/installed/status/configured`,
value: true,
},
- {
- op: PatchOp.REPLACE,
- path: `/package-data/${params.id}/installed/status/main/status`,
- value: PackageMainStatus.Running,
- },
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ return this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
}
async restorePackageRaw (params: RR.RestorePackageReq): Promise {
await pauseFor(2000)
+ const path = `/package-data/${params.id}/installed/status/main/status`
const patch = [
{
op: PatchOp.REPLACE,
- path: `/package-data/${params.id}/installed/status/main/status`,
+ path,
value: PackageMainStatus.Restoring,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ const res = await this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ setTimeout(() => {
+ const patch = [
+ {
+ op: PatchOp.REPLACE,
+ path,
+ value: PackageMainStatus.Stopped,
+ },
+ ]
+ this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ }, this.revertTime)
+ return res
}
async executePackageAction (params: RR.ExecutePackageActionReq): Promise {
@@ -361,14 +411,15 @@ 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 patch = [
{
op: PatchOp.REPLACE,
- path: `/package-data/${params.id}/installed/status/main/status`,
+ path,
value: PackageMainStatus.Running,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ return this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
}
async dryStopPackage (params: RR.DryStopPackageReq): Promise {
@@ -378,10 +429,11 @@ export class MockApiService extends ApiService {
async stopPackageRaw (params: RR.StopPackageReq): Promise {
await pauseFor(2000)
+ const path = `/package-data/${params.id}/installed/status/main/status`
const patch = [
{
op: PatchOp.REPLACE,
- path: `/package-data/${params.id}/installed/status/main/status`,
+ path,
value: PackageMainStatus.Stopping,
},
]
@@ -390,12 +442,12 @@ export class MockApiService extends ApiService {
const patch = [
{
op: PatchOp.REPLACE,
- path: `/package-data/${params.id}/installed/status/main/status`,
+ path,
value: PackageMainStatus.Stopped,
},
]
this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
- }, 2000)
+ }, this.revertTime)
return res
}
@@ -414,7 +466,17 @@ export class MockApiService extends ApiService {
value: PackageState.Removing,
},
]
- return this.http.rpcRequest({ method: 'db.patch', params: { patch } })
+ const res = await this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ setTimeout(async () => {
+ const patch = [
+ {
+ op: PatchOp.REMOVE,
+ path: `/package-data/${params.id}`,
+ },
+ ]
+ this.http.rpcRequest>({ method: 'db.patch', params: { patch } })
+ }, this.revertTime)
+ return res
}
async dryConfigureDependency (params: RR.DryConfigureDependencyReq): Promise {
diff --git a/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts b/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts
index 51a0bf128..3be443a2b 100644
--- a/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts
+++ b/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
-import { HttpService } from '../../http.service'
+import { HttpService, Method } from '../../http.service'
import { RR } from '../api.types'
import { MarketplaceApiService } from './marketplace-api.service'
import { PatchDbService } from '../../patch-db/patch-db.service'
@@ -17,29 +17,55 @@ export class MarketplaceLiveApiService extends MarketplaceApiService {
}
async getEos (params: RR.GetMarketplaceEOSReq): Promise {
- return this.http.simpleGet(this.getMarketplaceURL('eos'), params)
+ const url = this.getMarketplaceURL('eos')
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: url + '/eos',
+ params,
+ withCredentials: false,
+ })
}
async getMarketplaceData (params: RR.GetMarketplaceDataReq): Promise {
- return this.http.simpleGet(this.getMarketplaceURL('package'), params)
+ const url = this.getMarketplaceURL('package')
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: url + '/data',
+ params,
+ withCredentials: false,
+ })
}
async getMarketplacePkgs (params: RR.GetMarketplacePackagesReq): Promise {
const url = this.getMarketplaceURL('package', params.ids?.length > 1)
- const threadParams = {
- ...params,
- ids: JSON.stringify(params.ids),
- }
-
- return this.http.simpleGet(url, threadParams)
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: url + '/packages',
+ params: {
+ ...params,
+ ids: JSON.stringify(params.ids),
+ },
+ withCredentials: false,
+ })
}
async getReleaseNotes (params: RR.GetReleaseNotesReq): Promise {
- return this.http.simpleGet(this.getMarketplaceURL('package'), params)
+ const url = this.getMarketplaceURL('package')
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: url + + '/release-notes',
+ params,
+ withCredentials: false,
+ })
}
async getLatestVersion (params: RR.GetLatestVersionReq): Promise {
const url = this.getMarketplaceURL('package', params.ids?.length > 1)
- return this.http.simpleGet(url, params)
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: url + '/latest-version',
+ params,
+ withCredentials: false,
+ })
}
}
diff --git a/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts b/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts
index bacbc1ee5..a945b8ac0 100644
--- a/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts
+++ b/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'
import { pauseFor } from '../../../util/misc.util'
import { RR } from '../api.types'
import { Mock } from '../api.fixures'
-import { HttpService } from '../../http.service'
+import { HttpService, Method } from '../../http.service'
import { MarketplaceApiService } from './marketplace-api.service'
import { PatchDbService } from '../../patch-db/patch-db.service'
import { ConfigService } from '../../config.service'
@@ -20,53 +20,68 @@ export class MarketplaceMockApiService extends MarketplaceApiService {
// marketplace
async getEos (params: RR.GetMarketplaceEOSReq): Promise {
- let url = this.getMarketplaceURL('eos')
+ const url = this.getMarketplaceURL('eos')
if (this.useLocal(url)) {
await pauseFor(2000)
return Mock.MarketplaceEos
}
- url = `${url}/sys/version/eos`
- return this.http.simpleGet(url)
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: `${url}/eos`,
+ params,
+ withCredentials: false,
+ })
}
async getMarketplaceData (params: RR.GetMarketplaceDataReq): Promise {
- let url = this.getMarketplaceURL('package')
+ const url = this.getMarketplaceURL('package')
if (this.useLocal(url)) {
await pauseFor(2000)
return {
categories: ['featured', 'bitcoin', 'lightning', 'data', 'messaging', 'social', 'alt coin'],
}
}
- url = `${url}/data`
- return this.http.simpleGet(url)
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: `${url}/data`,
+ params,
+ withCredentials: false,
+ })
}
async getMarketplacePkgs (params: RR.GetMarketplacePackagesReq): Promise {
- let url = this.getMarketplaceURL('package', params.ids?.length > 1)
- const threadParams = {
- ...params,
- ids: JSON.stringify(params.ids),
- }
+ const url = this.getMarketplaceURL('package', params.ids?.length > 1)
if (this.useLocal(url)) {
await pauseFor(2000)
- return Mock.AvailableList
+ return Mock.MarketplacePkgsList
}
- url = `${url}/packages`
- return this.http.simpleGet(url, threadParams)
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: `${url}/packages`,
+ params: {
+ ...params,
+ ids: JSON.stringify(params.ids),
+ },
+ withCredentials: false,
+ })
}
async getReleaseNotes (params: RR.GetReleaseNotesReq): Promise {
- let url = this.getMarketplaceURL('package')
+ const url = this.getMarketplaceURL('package')
if (this.useLocal(url)) {
await pauseFor(2000)
return Mock.ReleaseNotes
}
- url = `${url}/release-notes`
- return this.http.simpleGet(url, params)
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: `${url}/release-notes`,
+ params,
+ withCredentials: false,
+ })
}
async getLatestVersion (params: RR.GetLatestVersionReq): Promise {
- let url = this.getMarketplaceURL('package', params.ids?.length > 1)
+ const url = this.getMarketplaceURL('package', params.ids?.length > 1)
if (this.useLocal(url)) {
await pauseFor(2000)
return params.ids.reduce((obj, id) => {
@@ -74,8 +89,13 @@ export class MarketplaceMockApiService extends MarketplaceApiService {
return obj
}, { })
}
- url = `${url}/latest-version`
- return this.http.simpleGet(url)
+
+ return this.http.httpRequest({
+ method: Method.GET,
+ url: `${url}/latest-version`,
+ params,
+ withCredentials: false,
+ })
}
private useLocal (url: string): boolean {
diff --git a/ui/src/app/services/connection.service.ts b/ui/src/app/services/connection.service.ts
index c8e20a160..351e0a2ff 100644
--- a/ui/src/app/services/connection.service.ts
+++ b/ui/src/app/services/connection.service.ts
@@ -32,7 +32,7 @@ export class ConnectionService {
combineLatest([this.networkState$.pipe(distinctUntilChanged()), this.patch.watchConnection$().pipe(distinctUntilChanged())])
.subscribe(async ([network, connectionStatus]) => {
- const addrs = this.patch.data['server-info']?.['connection-addresses']
+ const addrs = this.patch.data['server-info']['connection-addresses']
if (connectionStatus !== ConnectionStatus.Disconnected) {
this.connectionFailure$.next(ConnectionFailure.None)
} else if (!network) {
@@ -42,12 +42,12 @@ export class ConnectionService {
} else {
// diagnosing
this.connectionFailure$.next(ConnectionFailure.Diagnosing)
- const torSuccess = await this.testAddrs(addrs?.tor || [])
+ const torSuccess = await this.testAddrs(addrs.tor)
if (torSuccess) {
// TOR SUCCESS, EMBASSY IS PROBLEM
this.connectionFailure$.next(ConnectionFailure.Embassy)
} else {
- const clearnetSuccess = await this.testAddrs(addrs?.clearnet || [])
+ const clearnetSuccess = await this.testAddrs(addrs.clearnet)
if (clearnetSuccess) {
// CLEARNET SUCCESS, TOR IS PROBLEM
this.connectionFailure$.next(ConnectionFailure.Tor)
@@ -76,6 +76,7 @@ export class ConnectionService {
await this.httpService.httpRequest({
method: Method.GET,
url: addr,
+ withCredentials: false,
})
return true
} catch (e) {
diff --git a/ui/src/app/services/http.service.ts b/ui/src/app/services/http.service.ts
index b6a5c203f..e7c554b99 100644
--- a/ui/src/app/services/http.service.ts
+++ b/ui/src/app/services/http.service.ts
@@ -10,7 +10,6 @@ import { Revision } from 'patch-db-client'
})
export class HttpService {
private unauthorizedApiResponse$ = new Subject()
- authReqEnabled: boolean = false
fullUrl: string
constructor (
@@ -44,22 +43,15 @@ export class HttpService {
if (isRpcSuccess(res)) return res.result
}
- async simpleGet (url: string, params: { [param: string]: string | string[] } = { }): Promise {
- Object.keys(params).forEach(key => {
- if (!params[key]) delete params[key]
- })
- return this.http.get(url, { params }).toPromise()
- }
-
async httpRequest (httpOpts: HttpOptions): Promise {
- let { body, timeout, ...rest} = this.translateOptions(httpOpts)
+ let { body, timeout, url, ...rest} = this.translateOptions(httpOpts)
let req: Observable<{ body: T }>
switch (httpOpts.method){
- case Method.GET: req = this.http.get(this.fullUrl, rest) as any; break
- case Method.POST: req = this.http.post(this.fullUrl, body, rest) as any; break
- case Method.PUT: req = this.http.put(this.fullUrl, body, rest) as any; break
- case Method.PATCH: req = this.http.patch(this.fullUrl, body, rest) as any; break
- case Method.DELETE: req = this.http.delete(this.fullUrl, rest) as any; break
+ case Method.GET: req = this.http.get(url, rest) as any; break
+ case Method.POST: req = this.http.post(url, body, rest) as any; break
+ case Method.PUT: req = this.http.put(url, body, rest) as any; break
+ case Method.PATCH: req = this.http.patch(url, body, rest) as any; break
+ case Method.DELETE: req = this.http.delete(url, rest) as any; break
}
return (timeout ? withTimeout(req, timeout) : req)
@@ -68,16 +60,25 @@ export class HttpService {
.catch(e => { throw new HttpError(e) })
}
- translateOptions (httpOpts: HttpOptions): HttpJsonOptions {
+ private translateOptions (httpOpts: HttpOptions): HttpJsonOptions {
+ if (httpOpts.withCredentials !== false) {
+ httpOpts.withCredentials = this.config.mocks.enabled ? false : true
+ }
+
+ const urlIsRelative = !httpOpts.url || httpOpts.url.startsWith('/')
+ const url = urlIsRelative ?
+ this.fullUrl + httpOpts.url :
+ httpOpts.url
+
return {
observe: 'events',
responseType: 'json',
reportProgress: false,
- withCredentials: this.config.mocks.enabled ? false : true,
+ withCredentials: httpOpts.withCredentials,
headers: httpOpts.headers,
params: httpOpts.params,
body: httpOpts.data || { },
- url: httpOpts.url,
+ url,
timeout: httpOpts.readTimeout,
}
}
diff --git a/ui/src/app/services/patch-db/patch-db.service.ts b/ui/src/app/services/patch-db/patch-db.service.ts
index 1460debdc..a703ee08d 100644
--- a/ui/src/app/services/patch-db/patch-db.service.ts
+++ b/ui/src/app/services/patch-db/patch-db.service.ts
@@ -20,7 +20,6 @@ export enum ConnectionStatus {
})
export class PatchDbService {
connectionStatus$ = new BehaviorSubject(ConnectionStatus.Initializing)
- sequence$: Observable
data: DataModel
private patchDb: PatchDB
private patchSub: Subscription
@@ -33,10 +32,7 @@ export class PatchDbService {
async init (): Promise {
const cache = await this.bootstrapper.init()
- console.log('CACHECACHE', cache)
this.patchDb = new PatchDB([this.source, this.http], this.http, cache)
-
- this.sequence$ = this.patchDb.store.sequence$.asObservable()
this.data = this.patchDb.store.cache.data
}
diff --git a/ui/src/app/services/startup-alerts.service.ts b/ui/src/app/services/startup-alerts.service.ts
index 6f8813ace..9e9128103 100644
--- a/ui/src/app/services/startup-alerts.service.ts
+++ b/ui/src/app/services/startup-alerts.service.ts
@@ -28,8 +28,8 @@ export class StartupAlertsService {
private readonly wizardBaker: WizardBaker,
private readonly patch: PatchDbService,
) {
- const welcome: Check = {
- name: 'welcome',
+ const osWelcome: Check = {
+ name: 'osWelcome',
shouldRun: () => this.shouldRunOsWelcome(),
check: async () => true,
display: () => this.displayOsWelcome(),
@@ -42,14 +42,14 @@ export class StartupAlertsService {
display: pkg => this.displayOsUpdateCheck(pkg),
hasRun: this.config.skipStartupAlerts,
}
- const apps: Check = {
- name: 'apps',
+ const pkgsUpdate: Check = {
+ name: 'pkgsUpdate',
shouldRun: () => this.shouldRunAppsCheck(),
check: () => this.appsCheck(),
display: () => this.displayAppsCheck(),
hasRun: this.config.skipStartupAlerts,
}
- this.checks = [welcome, osUpdate, apps]
+ this.checks = [osWelcome, osUpdate, pkgsUpdate]
}
// This takes our three checks and filters down to those that should run.
@@ -79,8 +79,7 @@ export class StartupAlertsService {
}
private shouldRunOsWelcome (): boolean {
- const data = this.patch.data
- return !data.ui['welcome-ack'] && data['server-info'].version === this.config.version
+ return this.patch.data.ui['welcome-ack'] !== this.config.version
}
private shouldRunOsUpdateCheck (): boolean {
@@ -94,7 +93,7 @@ export class StartupAlertsService {
private async osUpdateCheck (): Promise {
const res = await this.marketplaceApi.getEos({ })
- if (this.emver.compare(this.patch.data['server-info'].version, res.version) === -1) {
+ if (this.emver.compare(this.config.version, res.version) === -1) {
return res
} else {
return undefined
@@ -113,7 +112,7 @@ export class StartupAlertsService {
component: OSWelcomePage,
presentingElement: await this.modalCtrl.getTop(),
componentProps: {
- version: this.patch.data['server-info'].version,
+ version: this.config.version,
},
})