remove share-stats, cosmetic improvements

This commit is contained in:
Matt Hill
2022-02-09 11:55:12 -07:00
committed by Keagan McClelland
parent 5326de9b84
commit 20acd8efea
17 changed files with 125 additions and 276 deletions

View File

@@ -15,9 +15,10 @@
"build:setup-wizard": "ng run setup-wizard:build", "build:setup-wizard": "ng run setup-wizard:build",
"build:ui": "ng run ui:build && tsc projects/ui/postprocess.ts && node projects/ui/postprocess.js && git log | head -n1 > dist/ui/git-hash.txt", "build:ui": "ng run ui:build && tsc projects/ui/postprocess.ts && node projects/ui/postprocess.js && git log | head -n1 > dist/ui/git-hash.txt",
"build:all": "npm run build:deps && npm run build:diagnostic-ui && npm run build:setup-wizard && npm run build:ui", "build:all": "npm run build:deps && npm run build:diagnostic-ui && npm run build:setup-wizard && npm run build:ui",
"start:diagnostic-ui": "ionic serve --project diagnostic-ui", "start:diagnostic-ui": "npm run copy-git-hash && ionic serve --project diagnostic-ui",
"start:setup-wizard": "ionic serve --project setup-wizard", "start:setup-wizard": "npm run copy-git-hash && ionic serve --project setup-wizard",
"start:ui": "ionic serve --project ui" "start:ui": "npm run copy-git-hash && ionic serve --project ui",
"copy-git-hash": "./update-git-hash.sh"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "^13.2.0", "@angular/animations": "^13.2.0",

View File

@@ -49,7 +49,7 @@
<ion-icon <ion-icon
*ngIf="eosService.updateAvailable$ | async" *ngIf="eosService.updateAvailable$ | async"
color="success" color="success"
name="repeat" name="rocket-outline"
></ion-icon> ></ion-icon>
</ng-container> </ng-container>
<ion-badge <ion-badge
@@ -162,7 +162,7 @@
<ion-icon name="reload"></ion-icon> <ion-icon name="reload"></ion-icon>
<ion-icon name="remove"></ion-icon> <ion-icon name="remove"></ion-icon>
<ion-icon name="remove-circle-outline"></ion-icon> <ion-icon name="remove-circle-outline"></ion-icon>
<ion-icon name="repeat"></ion-icon> <ion-icon name="rocket-outline"></ion-icon>
<ion-icon name="save-outline"></ion-icon> <ion-icon name="save-outline"></ion-icon>
<ion-icon name="shield-checkmark-outline"></ion-icon> <ion-icon name="shield-checkmark-outline"></ion-icon>
<ion-icon name="storefront-outline"></ion-icon> <ion-icon name="storefront-outline"></ion-icon>

View File

@@ -13,6 +13,7 @@ import {
AlertController, AlertController,
IonicSafeString, IonicSafeString,
LoadingController, LoadingController,
ModalController,
ToastController, ToastController,
} from '@ionic/angular' } from '@ionic/angular'
import { Emver } from './services/emver.service' import { Emver } from './services/emver.service'
@@ -24,7 +25,6 @@ import {
ConnectionFailure, ConnectionFailure,
ConnectionService, ConnectionService,
} from './services/connection.service' } from './services/connection.service'
import { StartupAlertsService } from './services/startup-alerts.service'
import { ConfigService } from './services/config.service' import { ConfigService } from './services/config.service'
import { debounce, isEmptyObject } from './util/misc.util' import { debounce, isEmptyObject } from './util/misc.util'
import { ErrorToastService } from './services/error-toast.service' import { ErrorToastService } from './services/error-toast.service'
@@ -32,6 +32,7 @@ import { Subscription } from 'rxjs'
import { LocalStorageService } from './services/local-storage.service' import { LocalStorageService } from './services/local-storage.service'
import { EOSService } from './services/eos.service' import { EOSService } from './services/eos.service'
import { MarketplaceService } from './pages/marketplace-routes/marketplace.service' import { MarketplaceService } from './pages/marketplace-routes/marketplace.service'
import { OSWelcomePage } from './modals/os-welcome/os-welcome.page'
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@@ -41,7 +42,7 @@ import { MarketplaceService } from './pages/marketplace-routes/marketplace.servi
export class AppComponent { export class AppComponent {
@HostListener('document:keydown.enter', ['$event']) @HostListener('document:keydown.enter', ['$event'])
@debounce() @debounce()
handleKeyboardEvent () { handleKeyboardEvent() {
const elems = document.getElementsByClassName('enter-click') const elems = document.getElementsByClassName('enter-click')
const elem = elems[elems.length - 1] as HTMLButtonElement const elem = elems[elems.length - 1] as HTMLButtonElement
if (!elem || elem.classList.contains('no-click') || elem.disabled) return if (!elem || elem.classList.contains('no-click') || elem.disabled) return
@@ -86,7 +87,7 @@ export class AppComponent {
}, },
] ]
constructor ( constructor(
private readonly storage: Storage, private readonly storage: Storage,
private readonly authService: AuthService, private readonly authService: AuthService,
private readonly router: Router, private readonly router: Router,
@@ -95,7 +96,7 @@ export class AppComponent {
private readonly loadingCtrl: LoadingController, private readonly loadingCtrl: LoadingController,
private readonly emver: Emver, private readonly emver: Emver,
private readonly connectionService: ConnectionService, private readonly connectionService: ConnectionService,
private readonly startupAlertsService: StartupAlertsService, private readonly modalCtrl: ModalController,
private readonly marketplaceService: MarketplaceService, private readonly marketplaceService: MarketplaceService,
private readonly toastCtrl: ToastController, private readonly toastCtrl: ToastController,
private readonly errToast: ErrorToastService, private readonly errToast: ErrorToastService,
@@ -109,7 +110,7 @@ export class AppComponent {
this.init() this.init()
} }
async init () { async init() {
await this.storage.create() await this.storage.create()
await this.authService.init() await this.authService.init()
await this.localStorageService.init() await this.localStorageService.init()
@@ -146,6 +147,8 @@ export class AppComponent {
.subscribe(data => { .subscribe(data => {
// check for updates to EOS // check for updates to EOS
this.checkForEosUpdate(data.ui) this.checkForEosUpdate(data.ui)
// show eos welcome message
this.showEosWelcome(data.ui['ack-welcome'])
this.subscriptions = this.subscriptions.concat([ this.subscriptions = this.subscriptions.concat([
// watch status to present toast for updated state // watch status to present toast for updated state
@@ -158,8 +161,6 @@ export class AppComponent {
this.watchNotifications(), this.watchNotifications(),
// watch marketplace URL for changes // watch marketplace URL for changes
this.marketplaceService.init(), this.marketplaceService.init(),
// run startup alerts
this.startupAlertsService.runChecks(),
]) ])
}) })
// UNVERIFIED // UNVERIFIED
@@ -180,7 +181,7 @@ export class AppComponent {
}) })
} }
async goToWebsite (): Promise<void> { async goToWebsite(): Promise<void> {
let url: string let url: string
if (this.config.isTor()) { if (this.config.isTor()) {
url = url =
@@ -191,7 +192,7 @@ export class AppComponent {
window.open(url, '_blank', 'noreferrer') window.open(url, '_blank', 'noreferrer')
} }
async presentAlertLogout () { async presentAlertLogout() {
const alert = await this.alertCtrl.create({ const alert = await this.alertCtrl.create({
header: 'Caution', header: 'Caution',
message: message:
@@ -214,19 +215,37 @@ export class AppComponent {
await alert.present() await alert.present()
} }
private async checkForEosUpdate (ui: UIData): Promise<void> { private checkForEosUpdate(ui: UIData): void {
if (ui['auto-check-updates']) { if (ui['auto-check-updates']) {
await this.eosService.getEOS() this.eosService.getEOS()
}
}
private async showEosWelcome(ackVersion: string): Promise<void> {
if (!this.config.skipStartupAlerts && ackVersion !== this.config.version) {
const modal = await this.modalCtrl.create({
component: OSWelcomePage,
presentingElement: await this.modalCtrl.getTop(),
componentProps: {
version: this.config.version,
},
})
modal.onWillDismiss().then(() => {
this.embassyApi
.setDbValue({ pointer: '/ack-welcome', value: this.config.version })
.catch()
})
modal.present()
} }
} }
// should wipe cache independant of actual BE logout // should wipe cache independant of actual BE logout
private async logout () { private async logout() {
this.embassyApi.logout({}) this.embassyApi.logout({})
this.authService.setUnverified() this.authService.setUnverified()
} }
private watchConnection (): Subscription { private watchConnection(): Subscription {
return this.connectionService return this.connectionService
.watchFailure$() .watchFailure$()
.pipe(distinctUntilChanged(), debounceTime(500)) .pipe(distinctUntilChanged(), debounceTime(500))
@@ -259,7 +278,7 @@ export class AppComponent {
}) })
} }
private watchRouter (): Subscription { private watchRouter(): Subscription {
return this.router.events return this.router.events
.pipe(filter((e: RoutesRecognized) => !!e.urlAfterRedirects)) .pipe(filter((e: RoutesRecognized) => !!e.urlAfterRedirects))
.subscribe(e => { .subscribe(e => {
@@ -270,7 +289,7 @@ export class AppComponent {
}) })
} }
private watchStatus (): Subscription { private watchStatus(): Subscription {
return this.patch.watch$('server-info', 'status').subscribe(status => { return this.patch.watch$('server-info', 'status').subscribe(status => {
if (status === ServerStatus.Updated && !this.updateToast) { if (status === ServerStatus.Updated && !this.updateToast) {
this.presentToastUpdated() this.presentToastUpdated()
@@ -278,7 +297,7 @@ export class AppComponent {
}) })
} }
private watchUpdateProgress (): Subscription { private watchUpdateProgress(): Subscription {
return this.patch return this.patch
.watch$('server-info', 'update-progress') .watch$('server-info', 'update-progress')
.subscribe(progress => { .subscribe(progress => {
@@ -286,7 +305,7 @@ export class AppComponent {
}) })
} }
private watchVersion (): Subscription { private watchVersion(): Subscription {
return this.patch.watch$('server-info', 'version').subscribe(version => { return this.patch.watch$('server-info', 'version').subscribe(version => {
if (this.emver.compare(this.config.version, version) !== 0) { if (this.emver.compare(this.config.version, version) !== 0) {
this.presentAlertRefreshNeeded() this.presentAlertRefreshNeeded()
@@ -294,7 +313,7 @@ export class AppComponent {
}) })
} }
private watchNotifications (): Subscription { private watchNotifications(): Subscription {
let previous: number let previous: number
return this.patch return this.patch
.watch$('server-info', 'unread-notification-count') .watch$('server-info', 'unread-notification-count')
@@ -306,7 +325,7 @@ export class AppComponent {
}) })
} }
private async presentAlertRefreshNeeded () { private async presentAlertRefreshNeeded() {
const alert = await this.alertCtrl.create({ const alert = await this.alertCtrl.create({
backdropDismiss: false, backdropDismiss: false,
header: 'Refresh Needed', header: 'Refresh Needed',
@@ -325,7 +344,7 @@ export class AppComponent {
await alert.present() await alert.present()
} }
private async presentToastUpdated () { private async presentToastUpdated() {
if (this.updateToast) return if (this.updateToast) return
this.updateToast = await this.toastCtrl.create({ this.updateToast = await this.toastCtrl.create({
@@ -355,7 +374,7 @@ export class AppComponent {
await this.updateToast.present() await this.updateToast.present()
} }
private async presentToastNotifications () { private async presentToastNotifications() {
if (this.notificationToast) return if (this.notificationToast) return
this.notificationToast = await this.toastCtrl.create({ this.notificationToast = await this.toastCtrl.create({
@@ -385,7 +404,7 @@ export class AppComponent {
await this.notificationToast.present() await this.notificationToast.present()
} }
private async presentToastOffline ( private async presentToastOffline(
message: string | IonicSafeString, message: string | IonicSafeString,
link?: string, link?: string,
) { ) {
@@ -426,7 +445,7 @@ export class AppComponent {
await this.offlineToast.present() await this.offlineToast.present()
} }
private async restart (): Promise<void> { private async restart(): Promise<void> {
const loader = await this.loadingCtrl.create({ const loader = await this.loadingCtrl.create({
spinner: 'lines', spinner: 'lines',
message: 'Restarting...', message: 'Restarting...',
@@ -443,7 +462,7 @@ export class AppComponent {
} }
} }
splitPaneVisible (e: any) { splitPaneVisible(e: any) {
this.splitPane.sidebarOpen$.next(e.detail.visible) this.splitPane.sidebarOpen$.next(e.detail.visible)
} }
} }

View File

@@ -15,16 +15,6 @@
<ion-note slot="end">{{ patch.data.ui.name || defaultName }}</ion-note> <ion-note slot="end">{{ patch.data.ui.name || defaultName }}</ion-note>
</ion-item> </ion-item>
<ion-item
button
(click)="serverConfig.presentAlert('share-stats', server['share-stats'])"
>
<ion-label>Auto Report Bugs</ion-label>
<ion-note slot="end"
>{{ server['share-stats'] ? 'Enabled' : 'Disabled' }}</ion-note
>
</ion-item>
<ion-item-divider>Marketplace</ion-item-divider> <ion-item-divider>Marketplace</ion-item-divider>
<ion-item <ion-item
button button

View File

@@ -28,9 +28,9 @@ export class PreferencesPage {
private readonly loadingCtrl: LoadingController, private readonly loadingCtrl: LoadingController,
private readonly modalCtrl: ModalController, private readonly modalCtrl: ModalController,
private readonly api: ApiService, private readonly api: ApiService,
public readonly serverConfig: ServerConfigService,
private readonly toastCtrl: ToastController, private readonly toastCtrl: ToastController,
private readonly localStorageService: LocalStorageService, private readonly localStorageService: LocalStorageService,
public readonly serverConfig: ServerConfigService,
public readonly patch: PatchDbService, public readonly patch: PatchDbService,
) {} ) {}

View File

@@ -59,19 +59,21 @@
</span> </span>
</ng-container> </ng-container>
</p> </p>
<!-- "Software Update" button only --> <!-- "Software Update" button only -->
<p *ngIf="button.title === 'Software Update'"> <p *ngIf="button.title === 'Software Update'">
<ng-container <ng-container
*ngIf="eosService.updateAvailable$ | async; else check" *ngIf="eosService.updateAvailable$ | async; else check"
> >
<ion-text class="inline" color="success"> <ion-text class="inline" color="success">
<ion-icon name="repeat"></ion-icon> <ion-icon name="rocket-outline"></ion-icon>
Update Available Update Available
</ion-text> </ion-text>
</ng-container> </ng-container>
<ng-template #check> <ng-template #check>
<i>Check for updates</i> <ion-text class="inline" color="dark">
<ion-icon name="refresh"></ion-icon>
Check for updates
</ion-text>
</ng-template> </ng-template>
</p> </p>
</ion-label> </ion-label>

View File

@@ -204,7 +204,10 @@ export class ServerShowPage {
await loader.present() await loader.present()
try { try {
await this.eosService.getEOS() const updateAvailable = await this.eosService.getEOS()
if (updateAvailable) {
this.updateEos()
}
} catch (e) { } catch (e) {
this.errToast.present(e) this.errToast.present(e)
} finally { } finally {

View File

@@ -28,9 +28,6 @@ export module RR {
// server // server
export type SetShareStatsReq = WithExpire<{ value: boolean }> // server.config.share-stats
export type SetShareStatsRes = WithRevision<null>
export type GetServerLogsReq = { export type GetServerLogsReq = {
cursor?: string cursor?: string
before_flag?: boolean before_flag?: boolean

View File

@@ -51,12 +51,6 @@ export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
// server // server
protected abstract setShareStatsRaw(
params: RR.SetShareStatsReq,
): Promise<RR.SetShareStatsRes>
setShareStats = (params: RR.SetShareStatsReq) =>
this.syncResponse(() => this.setShareStatsRaw(params))()
abstract getServerLogs( abstract getServerLogs(
params: RR.GetServerLogsReq, params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> ): Promise<RR.GetServerLogsRes>

View File

@@ -57,12 +57,6 @@ export class LiveApiService extends ApiService {
// server // server
async setShareStatsRaw(
params: RR.SetShareStatsReq,
): Promise<RR.SetShareStatsRes> {
return this.http.rpcRequest({ method: 'server.config.share-stats', params })
}
async getServerLogs( async getServerLogs(
params: RR.GetServerLogsReq, params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> { ): Promise<RR.GetServerLogsRes> {

View File

@@ -25,22 +25,22 @@ export class MockApiService extends ApiService {
private readonly revertTime = 4000 private readonly revertTime = 4000
sequence: number sequence: number
constructor (private readonly bootstrapper: LocalStorageBootstrap) { constructor(private readonly bootstrapper: LocalStorageBootstrap) {
super() super()
} }
async getStatic (url: string): Promise<string> { async getStatic(url: string): Promise<string> {
await pauseFor(2000) await pauseFor(2000)
return markdown return markdown
} }
// db // db
async getRevisions (since: number): Promise<RR.GetRevisionsRes> { async getRevisions(since: number): Promise<RR.GetRevisionsRes> {
return this.getDump() return this.getDump()
} }
async getDump (): Promise<RR.GetDumpRes> { async getDump(): Promise<RR.GetDumpRes> {
const cache = await this.bootstrapper.init() const cache = await this.bootstrapper.init()
return { return {
id: cache.sequence, id: cache.sequence,
@@ -49,7 +49,7 @@ export class MockApiService extends ApiService {
} }
} }
async setDbValueRaw (params: RR.SetDBValueReq): Promise<RR.SetDBValueRes> { async setDbValueRaw(params: RR.SetDBValueReq): Promise<RR.SetDBValueRes> {
await pauseFor(2000) await pauseFor(2000)
const patch = [ const patch = [
{ {
@@ -63,7 +63,7 @@ export class MockApiService extends ApiService {
// auth // auth
async login (params: RR.LoginReq): Promise<RR.loginRes> { async login(params: RR.LoginReq): Promise<RR.loginRes> {
await pauseFor(2000) await pauseFor(2000)
setTimeout(() => { setTimeout(() => {
@@ -73,39 +73,24 @@ export class MockApiService extends ApiService {
return null return null
} }
async logout (params: RR.LogoutReq): Promise<RR.LogoutRes> { async logout(params: RR.LogoutReq): Promise<RR.LogoutRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async getSessions (params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> { async getSessions(params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.Sessions return Mock.Sessions
} }
async killSessions (params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> { async killSessions(params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
// server // server
async setShareStatsRaw ( async getServerLogs(
params: RR.SetShareStatsReq,
): Promise<RR.SetShareStatsRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/server-info/share-stats',
value: params.value,
},
]
return this.withRevision(patch)
}
async getServerLogs (
params: RR.GetServerLogsReq, params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> { ): Promise<RR.GetServerLogsRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -127,21 +112,21 @@ export class MockApiService extends ApiService {
} }
} }
async getServerMetrics ( async getServerMetrics(
params: RR.GetServerMetricsReq, params: RR.GetServerMetricsReq,
): Promise<RR.GetServerMetricsRes> { ): Promise<RR.GetServerMetricsRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.getServerMetrics() return Mock.getServerMetrics()
} }
async getPkgMetrics ( async getPkgMetrics(
params: RR.GetServerMetricsReq, params: RR.GetServerMetricsReq,
): Promise<RR.GetPackageMetricsRes> { ): Promise<RR.GetPackageMetricsRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.getAppMetrics() return Mock.getAppMetrics()
} }
async updateServerRaw ( async updateServerRaw(
params: RR.UpdateServerReq, params: RR.UpdateServerReq,
): Promise<RR.UpdateServerRes> { ): Promise<RR.UpdateServerRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -165,21 +150,21 @@ export class MockApiService extends ApiService {
return this.withRevision(patch, 'updating') return this.withRevision(patch, 'updating')
} }
async restartServer ( async restartServer(
params: RR.RestartServerReq, params: RR.RestartServerReq,
): Promise<RR.RestartServerRes> { ): Promise<RR.RestartServerRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async shutdownServer ( async shutdownServer(
params: RR.ShutdownServerReq, params: RR.ShutdownServerReq,
): Promise<RR.ShutdownServerRes> { ): Promise<RR.ShutdownServerRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async systemRebuild ( async systemRebuild(
params: RR.RestartServerReq, params: RR.RestartServerReq,
): Promise<RR.RestartServerRes> { ): Promise<RR.RestartServerRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -188,7 +173,7 @@ export class MockApiService extends ApiService {
// marketplace URLs // marketplace URLs
async marketplaceProxy (path: string, params: {}, url: string): Promise<any> { async marketplaceProxy(path: string, params: {}, url: string): Promise<any> {
await pauseFor(2000) await pauseFor(2000)
if (path === '/package/v0/info') { if (path === '/package/v0/info') {
@@ -211,7 +196,7 @@ export class MockApiService extends ApiService {
} }
} }
async getEos ( async getEos(
params: RR.GetMarketplaceEOSReq, params: RR.GetMarketplaceEOSReq,
): Promise<RR.GetMarketplaceEOSRes> { ): Promise<RR.GetMarketplaceEOSRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -226,7 +211,7 @@ export class MockApiService extends ApiService {
// notification // notification
async getNotificationsRaw ( async getNotificationsRaw(
params: RR.GetNotificationsReq, params: RR.GetNotificationsReq,
): Promise<RR.GetNotificationsRes> { ): Promise<RR.GetNotificationsRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -241,14 +226,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch, Mock.Notifications) return this.withRevision(patch, Mock.Notifications)
} }
async deleteNotification ( async deleteNotification(
params: RR.DeleteNotificationReq, params: RR.DeleteNotificationReq,
): Promise<RR.DeleteNotificationRes> { ): Promise<RR.DeleteNotificationRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async deleteAllNotifications ( async deleteAllNotifications(
params: RR.DeleteAllNotificationsReq, params: RR.DeleteAllNotificationsReq,
): Promise<RR.DeleteAllNotificationsRes> { ): Promise<RR.DeleteAllNotificationsRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -257,60 +242,60 @@ export class MockApiService extends ApiService {
// wifi // wifi
async getWifi (params: RR.GetWifiReq): Promise<RR.GetWifiRes> { async getWifi(params: RR.GetWifiReq): Promise<RR.GetWifiRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.Wifi return Mock.Wifi
} }
async setWifiCountry ( async setWifiCountry(
params: RR.SetWifiCountryReq, params: RR.SetWifiCountryReq,
): Promise<RR.SetWifiCountryRes> { ): Promise<RR.SetWifiCountryRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async addWifi (params: RR.AddWifiReq): Promise<RR.AddWifiRes> { async addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async connectWifi (params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> { async connectWifi(params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async deleteWifi (params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> { async deleteWifi(params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
// ssh // ssh
async getSshKeys (params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> { async getSshKeys(params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.SshKeys return Mock.SshKeys
} }
async addSshKey (params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> { async addSshKey(params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.SshKey return Mock.SshKey
} }
async deleteSshKey (params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> { async deleteSshKey(params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
// backup // backup
async getBackupTargets ( async getBackupTargets(
params: RR.GetBackupTargetsReq, params: RR.GetBackupTargetsReq,
): Promise<RR.GetBackupTargetsRes> { ): Promise<RR.GetBackupTargetsRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.BackupTargets return Mock.BackupTargets
} }
async addBackupTarget ( async addBackupTarget(
params: RR.AddBackupTargetReq, params: RR.AddBackupTargetReq,
): Promise<RR.AddBackupTargetRes> { ): Promise<RR.AddBackupTargetRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -327,7 +312,7 @@ export class MockApiService extends ApiService {
} }
} }
async updateBackupTarget ( async updateBackupTarget(
params: RR.UpdateBackupTargetReq, params: RR.UpdateBackupTargetReq,
): Promise<RR.UpdateBackupTargetRes> { ): Promise<RR.UpdateBackupTargetRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -342,21 +327,21 @@ export class MockApiService extends ApiService {
} }
} }
async removeBackupTarget ( async removeBackupTarget(
params: RR.RemoveBackupTargetReq, params: RR.RemoveBackupTargetReq,
): Promise<RR.RemoveBackupTargetRes> { ): Promise<RR.RemoveBackupTargetRes> {
await pauseFor(2000) await pauseFor(2000)
return null return null
} }
async getBackupInfo ( async getBackupInfo(
params: RR.GetBackupInfoReq, params: RR.GetBackupInfoReq,
): Promise<RR.GetBackupInfoRes> { ): Promise<RR.GetBackupInfoRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.BackupInfo return Mock.BackupInfo
} }
async createBackupRaw ( async createBackupRaw(
params: RR.CreateBackupReq, params: RR.CreateBackupReq,
): Promise<RR.CreateBackupRes> { ): Promise<RR.CreateBackupRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -407,14 +392,14 @@ export class MockApiService extends ApiService {
// package // package
async getPackageProperties ( async getPackageProperties(
params: RR.GetPackagePropertiesReq, params: RR.GetPackagePropertiesReq,
): Promise<RR.GetPackagePropertiesRes<2>['data']> { ): Promise<RR.GetPackagePropertiesRes<2>['data']> {
await pauseFor(2000) await pauseFor(2000)
return parsePropertiesPermissive(Mock.PackageProperties) return parsePropertiesPermissive(Mock.PackageProperties)
} }
async getPackageLogs ( async getPackageLogs(
params: RR.GetPackageLogsReq, params: RR.GetPackageLogsReq,
): Promise<RR.GetPackageLogsRes> { ): Promise<RR.GetPackageLogsRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -436,7 +421,7 @@ export class MockApiService extends ApiService {
} }
} }
async installPackageRaw ( async installPackageRaw(
params: RR.InstallPackageReq, params: RR.InstallPackageReq,
): Promise<RR.InstallPackageRes> { ): Promise<RR.InstallPackageRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -471,14 +456,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch) return this.withRevision(patch)
} }
async dryUpdatePackage ( async dryUpdatePackage(
params: RR.DryUpdatePackageReq, params: RR.DryUpdatePackageReq,
): Promise<RR.DryUpdatePackageRes> { ): Promise<RR.DryUpdatePackageRes> {
await pauseFor(2000) await pauseFor(2000)
return {} return {}
} }
async getPackageConfig ( async getPackageConfig(
params: RR.GetPackageConfigReq, params: RR.GetPackageConfigReq,
): Promise<RR.GetPackageConfigRes> { ): Promise<RR.GetPackageConfigRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -488,14 +473,14 @@ export class MockApiService extends ApiService {
} }
} }
async drySetPackageConfig ( async drySetPackageConfig(
params: RR.DrySetPackageConfigReq, params: RR.DrySetPackageConfigReq,
): Promise<RR.DrySetPackageConfigRes> { ): Promise<RR.DrySetPackageConfigRes> {
await pauseFor(2000) await pauseFor(2000)
return {} return {}
} }
async setPackageConfigRaw ( async setPackageConfigRaw(
params: RR.SetPackageConfigReq, params: RR.SetPackageConfigReq,
): Promise<RR.SetPackageConfigRes> { ): Promise<RR.SetPackageConfigRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -509,7 +494,7 @@ export class MockApiService extends ApiService {
return this.withRevision(patch) return this.withRevision(patch)
} }
async restorePackagesRaw ( async restorePackagesRaw(
params: RR.RestorePackagesReq, params: RR.RestorePackagesReq,
): Promise<RR.RestorePackagesRes> { ): Promise<RR.RestorePackagesRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -545,14 +530,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch) return this.withRevision(patch)
} }
async executePackageAction ( async executePackageAction(
params: RR.ExecutePackageActionReq, params: RR.ExecutePackageActionReq,
): Promise<RR.ExecutePackageActionRes> { ): Promise<RR.ExecutePackageActionRes> {
await pauseFor(2000) await pauseFor(2000)
return Mock.ActionResponse return Mock.ActionResponse
} }
async startPackageRaw ( async startPackageRaw(
params: RR.StartPackageReq, params: RR.StartPackageReq,
): Promise<RR.StartPackageRes> { ): Promise<RR.StartPackageRes> {
const path = `/package-data/${params.id}/installed/status/main` const path = `/package-data/${params.id}/installed/status/main`
@@ -631,7 +616,7 @@ export class MockApiService extends ApiService {
return this.withRevision(originalPatch) return this.withRevision(originalPatch)
} }
async dryStopPackage ( async dryStopPackage(
params: RR.DryStopPackageReq, params: RR.DryStopPackageReq,
): Promise<RR.DryStopPackageRes> { ): Promise<RR.DryStopPackageRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -645,7 +630,7 @@ export class MockApiService extends ApiService {
} }
} }
async stopPackageRaw (params: RR.StopPackageReq): Promise<RR.StopPackageRes> { async stopPackageRaw(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
await pauseFor(2000) await pauseFor(2000)
const path = `/package-data/${params.id}/installed/status/main` const path = `/package-data/${params.id}/installed/status/main`
@@ -676,14 +661,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch) return this.withRevision(patch)
} }
async dryUninstallPackage ( async dryUninstallPackage(
params: RR.DryUninstallPackageReq, params: RR.DryUninstallPackageReq,
): Promise<RR.DryUninstallPackageRes> { ): Promise<RR.DryUninstallPackageRes> {
await pauseFor(2000) await pauseFor(2000)
return {} return {}
} }
async uninstallPackageRaw ( async uninstallPackageRaw(
params: RR.UninstallPackageReq, params: RR.UninstallPackageReq,
): Promise<RR.UninstallPackageRes> { ): Promise<RR.UninstallPackageRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -709,7 +694,7 @@ export class MockApiService extends ApiService {
return this.withRevision(patch) return this.withRevision(patch)
} }
async deleteRecoveredPackageRaw ( async deleteRecoveredPackageRaw(
params: RR.DeleteRecoveredPackageReq, params: RR.DeleteRecoveredPackageReq,
): Promise<RR.DeleteRecoveredPackageRes> { ): Promise<RR.DeleteRecoveredPackageRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -722,7 +707,7 @@ export class MockApiService extends ApiService {
return this.withRevision(patch) return this.withRevision(patch)
} }
async dryConfigureDependency ( async dryConfigureDependency(
params: RR.DryConfigureDependencyReq, params: RR.DryConfigureDependencyReq,
): Promise<RR.DryConfigureDependencyRes> { ): Promise<RR.DryConfigureDependencyRes> {
await pauseFor(2000) await pauseFor(2000)
@@ -733,7 +718,7 @@ export class MockApiService extends ApiService {
} }
} }
private async updateProgress ( private async updateProgress(
id: string, id: string,
initialProgress: InstallProgress, initialProgress: InstallProgress,
): Promise<void> { ): Promise<void> {
@@ -779,7 +764,7 @@ export class MockApiService extends ApiService {
}, 1000) }, 1000)
} }
private async updateOSProgress (size: number) { private async updateOSProgress(size: number) {
let downloaded = 0 let downloaded = 0
while (downloaded < size) { while (downloaded < size) {
await pauseFor(250) await pauseFor(250)
@@ -829,7 +814,7 @@ export class MockApiService extends ApiService {
}, 1000) }, 1000)
} }
private async updateMock (patch: Operation[]): Promise<void> { private async updateMock(patch: Operation[]): Promise<void> {
if (!this.sequence) { if (!this.sequence) {
const { sequence } = await this.bootstrapper.init() const { sequence } = await this.bootstrapper.init()
this.sequence = sequence this.sequence = sequence
@@ -842,7 +827,7 @@ export class MockApiService extends ApiService {
this.mockPatch$.next(revision) this.mockPatch$.next(revision)
} }
private async withRevision<T> ( private async withRevision<T>(
patch: Operation[], patch: Operation[],
response: T = null, response: T = null,
): Promise<WithRevision<T>> { ): Promise<WithRevision<T>> {

View File

@@ -15,7 +15,6 @@ export const mockPatchData: DataModel = {
'auto-check-updates': true, 'auto-check-updates': true,
'pkg-order': [], 'pkg-order': [],
'ack-welcome': '1.0.0', 'ack-welcome': '1.0.0',
'ack-share-stats': false,
marketplace: undefined, marketplace: undefined,
}, },
'server-info': { 'server-info': {
@@ -25,9 +24,6 @@ export const mockPatchData: DataModel = {
status: ServerStatus.Running, status: ServerStatus.Running,
'lan-address': 'https://embassy-abcdefgh.local', 'lan-address': 'https://embassy-abcdefgh.local',
'tor-address': 'http://myveryownspecialtoraddress.onion', 'tor-address': 'http://myveryownspecialtoraddress.onion',
'eos-marketplace': 'https://beta-registry-0-3.start9labs.com',
'package-marketplace': null,
'share-stats': false,
'unread-notification-count': 4, 'unread-notification-count': 4,
'password-hash': 'password-hash':
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ', '$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',

View File

@@ -18,7 +18,7 @@ export class EOSService {
private readonly patch: PatchDbService, private readonly patch: PatchDbService,
) {} ) {}
async getEOS(): Promise<void> { async getEOS(): Promise<boolean> {
this.eos = await this.api.getEos({ this.eos = await this.api.getEos({
'eos-version-compat': 'eos-version-compat':
this.patch.getData()['server-info']['eos-version-compat'], this.patch.getData()['server-info']['eos-version-compat'],
@@ -29,5 +29,6 @@ export class EOSService {
this.patch.data['server-info'].version, this.patch.data['server-info'].version,
) === 1 ) === 1
this.updateAvailable$.next(updateAvailable) this.updateAvailable$.next(updateAvailable)
return updateAvailable
} }
} }

View File

@@ -12,7 +12,6 @@ export interface UIData {
'auto-check-updates': boolean 'auto-check-updates': boolean
'pkg-order': string[] 'pkg-order': string[]
'ack-welcome': string // EOS version 'ack-welcome': string // EOS version
'ack-share-stats': boolean
marketplace: UIMarketplaceData marketplace: UIMarketplaceData
} }
@@ -33,9 +32,6 @@ export interface ServerInfo {
'lan-address': URL 'lan-address': URL
'tor-address': URL 'tor-address': URL
status: ServerStatus status: ServerStatus
'eos-marketplace': URL
'package-marketplace': URL | null // uses EOS marketplace if null
'share-stats': boolean
'unread-notification-count': number 'unread-notification-count': number
'update-progress'?: { 'update-progress'?: {
size: number size: number

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { AlertInput, AlertButton, IonicSafeString } from '@ionic/core' import { AlertInput, AlertButton } from '@ionic/core'
import { ApiService } from './api/embassy-api.service' import { ApiService } from './api/embassy-api.service'
import { ConfigSpec } from '../pkg-config/config-types' import { ConfigSpec } from '../pkg-config/config-types'
import { AlertController, LoadingController } from '@ionic/angular' import { AlertController, LoadingController } from '@ionic/angular'
@@ -104,9 +104,6 @@ export class ServerConfigService {
value: enabled, value: enabled,
}) })
}, },
'share-stats': async (enabled: boolean) => {
return this.embassyApi.setShareStats({ value: enabled })
},
} }
} }
@@ -115,15 +112,7 @@ export const serverConfig: ConfigSpec = {
type: 'boolean', type: 'boolean',
name: 'Auto Check for Updates', name: 'Auto Check for Updates',
description: description:
'If enabled, EmbassyOS will automatically check for updates of itself and any installed services. Updating will still require your approval and action. Updates will never be performed automatically.', 'If enabled, EmbassyOS will automatically check for updates of itself. Updating will still require your approval and action. Updates will never be performed automatically.',
default: true, default: true,
}, },
'share-stats': {
type: 'boolean',
name: 'Report Bugs',
description: new IonicSafeString(
`If enabled, generic error codes will be anonymously transmitted over Tor to the Start9 team. This helps us identify and fix bugs quickly. <a href="https://docs.start9.com/user-manual/general/user-preferences/report-bugs.html" target="_blank" rel="noreferrer">Read more</a> `,
) as any,
default: false,
},
} }

View File

@@ -1,125 +0,0 @@
import { Injectable } from '@angular/core'
import { ModalController } from '@ionic/angular'
import { WizardBaker } from '../components/install-wizard/prebaked-wizards'
import { OSWelcomePage } from '../modals/os-welcome/os-welcome.page'
import { ConfigService } from './config.service'
import { PatchDbService } from './patch-db/patch-db.service'
import { filter, take } from 'rxjs/operators'
import { isEmptyObject } from '../util/misc.util'
import { ApiService } from './api/embassy-api.service'
import { Subscription } from 'rxjs'
import { ServerConfigService } from './server-config.service'
@Injectable({
providedIn: 'root',
})
export class StartupAlertsService {
private checks: Check<any>[]
constructor(
private readonly config: ConfigService,
private readonly modalCtrl: ModalController,
private readonly api: ApiService,
private readonly wizardBaker: WizardBaker,
private readonly patch: PatchDbService,
private readonly serverConfig: ServerConfigService,
) {
const osWelcome: Check<boolean> = {
name: 'osWelcome',
shouldRun: () => this.shouldRunOsWelcome(),
display: () => this.displayOsWelcome(),
}
const shareStats: Check<boolean> = {
name: 'shareStats',
shouldRun: () => this.shouldRunShareStats(),
display: () => this.displayShareStats(),
}
this.checks = [osWelcome, shareStats]
}
// This takes our three checks and filters down to those that should run.
// Then, the reduce fires, quickly iterating through yielding a promise (previousDisplay) to the next element
// Each promise fires more or less concurrently, so each c.check(server) is run concurrently
// Then, since we await previousDisplay before c.display(res), each promise executing gets hung awaiting the display of the previous run
runChecks(): Subscription {
return this.patch
.watch$()
.pipe(
filter(data => !isEmptyObject(data)),
take(1),
)
.subscribe(async () => {
await this.checks
.filter(c => !this.config.skipStartupAlerts && c.shouldRun())
// returning true in the below block means to continue to next modal
// returning false means to skip all subsequent modals
.reduce(async (previousDisplay, c) => {
const displayRes = await previousDisplay
if (displayRes) return c.display()
}, Promise.resolve(true))
})
}
// ** should run **
private shouldRunOsWelcome(): boolean {
return this.patch.getData().ui['ack-welcome'] !== this.config.version
}
private shouldRunShareStats(): boolean {
return !this.patch.getData().ui['ack-share-stats']
}
private shouldRunOsUpdateCheck(): boolean {
return this.patch.getData().ui['auto-check-updates']
}
// ** display **
private async displayOsWelcome(): Promise<boolean> {
return new Promise(async resolve => {
const modal = await this.modalCtrl.create({
component: OSWelcomePage,
presentingElement: await this.modalCtrl.getTop(),
componentProps: {
version: this.config.version,
},
})
modal.onWillDismiss().then(() => {
this.api
.setDbValue({ pointer: '/ack-welcome', value: this.config.version })
.catch()
return resolve(true)
})
await modal.present()
})
}
private async displayShareStats(): Promise<boolean> {
return new Promise(async resolve => {
const alert = await this.serverConfig.presentAlert(
'share-stats',
this.patch.getData()['server-info']['share-stats'],
)
alert.onDidDismiss().then(() => {
this.api
.setDbValue({
pointer: '/ack-share-stats',
value: this.config.version,
})
.catch()
return resolve(true)
})
})
}
}
type Check<T> = {
// validates whether a check should run based on server properties
shouldRun: () => boolean
// display an alert based on the result of the check.
// return false if subsequent modals should not be displayed
display: () => Promise<boolean>
// for logging purposes
name: string
}

7
frontend/update-git-hash.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
cd "$(dirname "$0")"
TMP_FILE=$(mktemp)
jq ".gitHash = \"$(git rev-parse HEAD)\"" config.json > $TMP_FILE && mv $TMP_FILE config.json