mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
switch all FE to camelCase (#2576)
* switch all fe to camelCase * switch to camelCase on backend --------- Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -63,15 +63,6 @@ const routes: Routes = [
|
||||
m => m.AppsRoutingModule,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'developer',
|
||||
canActivate: [AuthGuard],
|
||||
canActivateChild: [AuthGuard],
|
||||
loadChildren: () =>
|
||||
import('./pages/developer-routes/developer-routing.module').then(
|
||||
m => m.DeveloperRoutingModule,
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -29,13 +29,12 @@ export class AppComponent implements OnDestroy {
|
||||
this.authService.isVerified$,
|
||||
this.connection.connected$,
|
||||
this.patch
|
||||
.watch$('server-info', 'status-info')
|
||||
.pipe(startWith({ restarting: false, 'shutting-down': false })),
|
||||
.watch$('serverInfo', 'statusInfo')
|
||||
.pipe(startWith({ restarting: false, shuttingDown: false })),
|
||||
]).pipe(
|
||||
map(
|
||||
([verified, connected, status]) =>
|
||||
verified &&
|
||||
(!connected || status.restarting || status['shutting-down']),
|
||||
verified && (!connected || status.restarting || status.shuttingDown),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { DataModel } from '../../services/patch-db/data-model'
|
||||
})
|
||||
export class FooterComponent {
|
||||
readonly progress$ = this.patch
|
||||
.watch$('server-info', 'status-info', 'update-progress')
|
||||
.watch$('serverInfo', 'statusInfo', 'updateProgress')
|
||||
.pipe(map(a => a && { ...a }))
|
||||
|
||||
readonly animation = {
|
||||
|
||||
@@ -11,9 +11,7 @@ import {
|
||||
filter,
|
||||
first,
|
||||
map,
|
||||
merge,
|
||||
Observable,
|
||||
of,
|
||||
pairwise,
|
||||
startWith,
|
||||
switchMap,
|
||||
@@ -24,14 +22,7 @@ import { DataModel, PackageState } from 'src/app/services/patch-db/data-model'
|
||||
import { SplitPaneTracker } from 'src/app/services/split-pane.service'
|
||||
import { Emver, THEME } from '@start9labs/shared'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import {
|
||||
getManifest,
|
||||
isInstalled,
|
||||
isInstalling,
|
||||
isRestoring,
|
||||
isUpdating,
|
||||
} from 'src/app/util/get-package-data'
|
||||
import { getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
@Component({
|
||||
selector: 'app-menu',
|
||||
@@ -69,19 +60,19 @@ export class MenuComponent {
|
||||
]
|
||||
|
||||
readonly notificationCount$ = this.patch.watch$(
|
||||
'server-info',
|
||||
'unread-notification-count',
|
||||
'serverInfo',
|
||||
'unreadNotificationCount',
|
||||
)
|
||||
|
||||
readonly snekScore$ = this.patch.watch$('ui', 'gaming', 'snake', 'high-score')
|
||||
readonly snekScore$ = this.patch.watch$('ui', 'gaming', 'snake', 'highScore')
|
||||
|
||||
readonly showEOSUpdate$ = this.eosService.showUpdate$
|
||||
|
||||
private readonly local$ = this.connectionService.connected$.pipe(
|
||||
filter(Boolean),
|
||||
switchMap(() => this.patch.watch$('package-data').pipe(first())),
|
||||
switchMap(() => this.patch.watch$('packageData').pipe(first())),
|
||||
switchMap(outer =>
|
||||
this.patch.watch$('package-data').pipe(
|
||||
this.patch.watch$('packageData').pipe(
|
||||
pairwise(),
|
||||
filter(([prev, curr]) =>
|
||||
Object.values(prev).some(
|
||||
@@ -90,9 +81,9 @@ export class MenuComponent {
|
||||
PackageState.Installing,
|
||||
PackageState.Updating,
|
||||
PackageState.Restoring,
|
||||
].includes(p['state-info'].state) &&
|
||||
].includes(p.stateInfo.state) &&
|
||||
[PackageState.Installed, PackageState.Removing].includes(
|
||||
curr[getManifest(p).id]['state-info'].state,
|
||||
curr[getManifest(p).id].stateInfo.state,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -136,6 +127,5 @@ export class MenuComponent {
|
||||
private readonly splitPane: SplitPaneTracker,
|
||||
private readonly emver: Emver,
|
||||
private readonly connectionService: ConnectionService,
|
||||
private readonly config: ConfigService,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class BackupService {
|
||||
}
|
||||
|
||||
hasValidBackup(target: BackupTarget): boolean {
|
||||
const backup = target['embassy-os']
|
||||
const backup = target.startOs
|
||||
return !!backup && this.emver.compare(backup.version, '0.3.0') !== -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ const { enableWidgets } =
|
||||
})
|
||||
export class BadgeMenuComponent {
|
||||
readonly unreadCount$ = this.patch.watch$(
|
||||
'server-info',
|
||||
'unread-notification-count',
|
||||
'serverInfo',
|
||||
'unreadNotificationCount',
|
||||
)
|
||||
readonly sidebarOpen$ = this.splitPane.sidebarOpen$
|
||||
readonly widgetDrawer$ = this.clientStorageService.widgetDrawer$
|
||||
|
||||
@@ -22,8 +22,8 @@ export class ConnectionBarComponent {
|
||||
this.connectionService.networkConnected$,
|
||||
this.websocket$.pipe(startWith(false)),
|
||||
this.patch
|
||||
.watch$('server-info', 'status-info')
|
||||
.pipe(startWith({ restarting: false, 'shutting-down': false })),
|
||||
.watch$('serverInfo', 'statusInfo')
|
||||
.pipe(startWith({ restarting: false, shuttingDown: false })),
|
||||
]).pipe(
|
||||
map(([network, websocket, status]) => {
|
||||
if (!network)
|
||||
@@ -40,7 +40,7 @@ export class ConnectionBarComponent {
|
||||
icon: 'cloud-offline-outline',
|
||||
dots: true,
|
||||
}
|
||||
if (status['shutting-down'])
|
||||
if (status.shuttingDown)
|
||||
return {
|
||||
message: 'Shutting Down',
|
||||
color: 'dark',
|
||||
|
||||
@@ -78,7 +78,7 @@ export class LogsComponent {
|
||||
async ngOnInit() {
|
||||
from(this.followLogs({ limit: this.limit }))
|
||||
.pipe(
|
||||
switchMap(({ 'start-cursor': startCursor, guid }) => {
|
||||
switchMap(({ startCursor, guid }) => {
|
||||
this.startCursor = startCursor
|
||||
return this.connect$(guid)
|
||||
}),
|
||||
@@ -206,7 +206,7 @@ export class LogsComponent {
|
||||
}
|
||||
|
||||
private processRes(res: LogsRes) {
|
||||
const { entries, 'start-cursor': startCursor } = res
|
||||
const { entries, startCursor } = res
|
||||
|
||||
if (!entries.length) return
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class NotificationsToastService extends Observable<boolean> {
|
||||
private readonly stream$ = this.patch
|
||||
.watch$('server-info', 'unread-notification-count')
|
||||
.watch$('serverInfo', 'unreadNotificationCount')
|
||||
.pipe(
|
||||
pairwise(),
|
||||
map(([prev, cur]) => cur > prev),
|
||||
|
||||
@@ -8,7 +8,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class RefreshAlertService extends Observable<boolean> {
|
||||
private readonly stream$ = this.patch.watch$('server-info', 'version').pipe(
|
||||
private readonly stream$ = this.patch.watch$('serverInfo', 'version').pipe(
|
||||
map(version => !!this.emver.compare(this.config.version, version)),
|
||||
endWith(false),
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UpdateToastService extends Observable<boolean> {
|
||||
private readonly stream$ = this.patch
|
||||
.watch$('server-info', 'status-info', 'updated')
|
||||
.watch$('serverInfo', 'statusInfo', 'updated')
|
||||
.pipe(distinctUntilChanged(), filter(Boolean), endWith(false))
|
||||
|
||||
constructor(private readonly patch: PatchDB<DataModel>) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding" *ngIf="pkg['state-info'].manifest as manifest">
|
||||
<ion-content class="ion-padding" *ngIf="pkg.stateInfo.manifest as manifest">
|
||||
<!-- loading -->
|
||||
<text-spinner
|
||||
*ngIf="loading; else notLoading"
|
||||
@@ -82,7 +82,7 @@
|
||||
</ion-item>
|
||||
|
||||
<!-- no options -->
|
||||
<ion-item *ngIf="!hasOptions">
|
||||
<ion-item *ngIf="!hasConfig">
|
||||
<ion-label>
|
||||
<p>
|
||||
No config options for {{ manifest.title }} {{ manifest.version }}.
|
||||
@@ -111,11 +111,7 @@
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ng-container *ngIf="!loading && !loadingError">
|
||||
<ion-buttons
|
||||
*ngIf="configForm && hasOptions"
|
||||
slot="start"
|
||||
class="ion-padding-start"
|
||||
>
|
||||
<ion-buttons *ngIf="hasConfig" slot="start" class="ion-padding-start">
|
||||
<ion-button fill="clear" (click)="resetDefaults()">
|
||||
<ion-icon slot="start" name="refresh"></ion-icon>
|
||||
Reset Defaults
|
||||
@@ -123,7 +119,7 @@
|
||||
</ion-buttons>
|
||||
<ion-buttons slot="end" class="ion-padding-end">
|
||||
<ion-button
|
||||
*ngIf="configForm"
|
||||
*ngIf="hasConfig"
|
||||
fill="solid"
|
||||
color="primary"
|
||||
[disabled]="saving"
|
||||
@@ -134,7 +130,7 @@
|
||||
Save
|
||||
</ion-button>
|
||||
<ion-button
|
||||
*ngIf="!configForm"
|
||||
*ngIf="!hasConfig"
|
||||
fill="solid"
|
||||
color="dark"
|
||||
(click)="dismiss()"
|
||||
|
||||
@@ -59,8 +59,6 @@ export class AppConfigPage {
|
||||
saving = false
|
||||
loadingError: string | IonicSafeString = ''
|
||||
|
||||
hasOptions = false
|
||||
|
||||
constructor(
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly errToast: ErrorToastService,
|
||||
@@ -71,6 +69,10 @@ export class AppConfigPage {
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
get hasConfig() {
|
||||
return this.pkg.stateInfo.manifest.hasConfig
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
try {
|
||||
const pkg = await getPackage(this.patch, this.pkgId)
|
||||
@@ -78,7 +80,7 @@ export class AppConfigPage {
|
||||
|
||||
this.pkg = pkg
|
||||
|
||||
if (!this.pkg['state-info'].manifest['has-config']) return
|
||||
if (!this.hasConfig) return
|
||||
|
||||
let newConfig: object | undefined
|
||||
let patch: Operation[] | undefined
|
||||
@@ -86,12 +88,12 @@ export class AppConfigPage {
|
||||
if (this.dependentInfo) {
|
||||
this.loadingText = `Setting properties to accommodate ${this.dependentInfo.title}`
|
||||
const {
|
||||
'old-config': oc,
|
||||
'new-config': nc,
|
||||
oldConfig: oc,
|
||||
newConfig: nc,
|
||||
spec: s,
|
||||
} = await this.embassyApi.dryConfigureDependency({
|
||||
'dependency-id': this.pkgId,
|
||||
'dependent-id': this.dependentInfo.id,
|
||||
dependencyId: this.pkgId,
|
||||
dependentId: this.dependentInfo.id,
|
||||
})
|
||||
this.original = oc
|
||||
newConfig = nc
|
||||
@@ -111,10 +113,6 @@ export class AppConfigPage {
|
||||
newConfig || this.original,
|
||||
)
|
||||
|
||||
this.hasOptions = !!Object.values(this.configSpec).find(
|
||||
valSpec => valSpec.type !== 'pointer',
|
||||
)
|
||||
|
||||
if (patch) {
|
||||
this.diff = this.getDiff(patch)
|
||||
this.markDirty(patch)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ng-container
|
||||
*ngIf="packageData$ | toOptions : backupInfo['package-backups'] | async as options"
|
||||
*ngIf="packageData$ | toOptions : backupInfo.packageBackups | async as options"
|
||||
>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
|
||||
@@ -23,7 +23,7 @@ export class AppRecoverSelectPage {
|
||||
@Input() password!: string
|
||||
@Input() oldPassword?: string
|
||||
|
||||
readonly packageData$ = this.patch.watch$('package-data').pipe(take(1))
|
||||
readonly packageData$ = this.patch.watch$('packageData').pipe(take(1))
|
||||
|
||||
hasSelection = false
|
||||
error: string | IonicSafeString = ''
|
||||
@@ -53,8 +53,8 @@ export class AppRecoverSelectPage {
|
||||
try {
|
||||
await this.embassyApi.restorePackages({
|
||||
ids,
|
||||
'target-id': this.id,
|
||||
'old-password': this.oldPassword || null,
|
||||
targetId: this.id,
|
||||
oldPassword: this.oldPassword || null,
|
||||
password: this.password,
|
||||
})
|
||||
this.modalCtrl.dismiss(undefined, 'success')
|
||||
|
||||
@@ -34,7 +34,7 @@ export class ToOptionsPipe implements PipeTransform {
|
||||
id,
|
||||
installed: !!packageData[id],
|
||||
checked: false,
|
||||
'newer-eos': this.compare(packageBackups[id]['os-version']),
|
||||
'newer-eos': this.compare(packageBackups[id].osVersion),
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
b.title.toLowerCase() > a.title.toLowerCase() ? -1 : 1,
|
||||
|
||||
@@ -29,7 +29,7 @@ export class BackupSelectPage {
|
||||
|
||||
async ngOnInit() {
|
||||
this.pkgs = await firstValueFrom(
|
||||
this.patch.watch$('package-data').pipe(
|
||||
this.patch.watch$('packageData').pipe(
|
||||
map(pkgs => {
|
||||
return Object.values(pkgs)
|
||||
.map(pkg => {
|
||||
@@ -38,7 +38,7 @@ export class BackupSelectPage {
|
||||
id,
|
||||
title,
|
||||
icon: pkg.icon,
|
||||
disabled: pkg['state-info'].state !== PackageState.Installed,
|
||||
disabled: pkg.stateInfo.state !== PackageState.Installed,
|
||||
checked: false,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -200,7 +200,7 @@ export class MarketplaceSettingsPage {
|
||||
): Promise<void> {
|
||||
// Error on duplicates
|
||||
const hosts = await firstValueFrom(
|
||||
this.patch.watch$('ui', 'marketplace', 'known-hosts'),
|
||||
this.patch.watch$('ui', 'marketplace', 'knownHosts'),
|
||||
)
|
||||
const currentUrls = Object.keys(hosts).map(toUrl)
|
||||
if (currentUrls.includes(url)) throw new Error('marketplace already added')
|
||||
@@ -217,7 +217,7 @@ export class MarketplaceSettingsPage {
|
||||
loader.message = 'Saving...'
|
||||
|
||||
await this.api.setDbValue<{ name: string }>(
|
||||
['marketplace', 'known-hosts', url],
|
||||
['marketplace', 'knownHosts', url],
|
||||
{ name },
|
||||
)
|
||||
}
|
||||
@@ -229,7 +229,7 @@ export class MarketplaceSettingsPage {
|
||||
await loader.present()
|
||||
|
||||
const hosts = await firstValueFrom(
|
||||
this.patch.watch$('ui', 'marketplace', 'known-hosts'),
|
||||
this.patch.watch$('ui', 'marketplace', 'knownHosts'),
|
||||
)
|
||||
|
||||
const filtered: { [url: string]: UIStore } = Object.keys(hosts)
|
||||
@@ -244,7 +244,7 @@ export class MarketplaceSettingsPage {
|
||||
|
||||
try {
|
||||
await this.api.setDbValue<{ [url: string]: UIStore }>(
|
||||
['marketplace', 'known-hosts'],
|
||||
['marketplace', 'knownHosts'],
|
||||
filtered,
|
||||
)
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -22,7 +22,7 @@ export class OSUpdatePage {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
const releaseNotes = this.eosService.eos?.['release-notes']!
|
||||
const releaseNotes = this.eosService.eos?.releaseNotes!
|
||||
|
||||
this.versions = Object.keys(releaseNotes)
|
||||
.sort()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<ion-content class="ion-padding-top with-widgets">
|
||||
<ng-container *ngIf="pkg$ | async as pkg">
|
||||
<ion-item-group *ngIf="pkg['state-info'].state === 'installed'">
|
||||
<ion-item-group *ngIf="pkg.stateInfo.state === 'installed'">
|
||||
<!-- ** standard actions ** -->
|
||||
<ion-item-divider>Standard Actions</ion-item-divider>
|
||||
<app-actions-item
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<!-- ** specific actions ** -->
|
||||
<ion-item-divider *ngIf="!(pkg.actions | empty)">
|
||||
Actions for {{ pkg['state-info'].manifest.title }}
|
||||
Actions for {{ pkg.stateInfo.manifest.title }}
|
||||
</ion-item-divider>
|
||||
<app-actions-item
|
||||
*ngFor="let action of pkg.actions | keyvalue: asIsOrder"
|
||||
|
||||
@@ -29,7 +29,7 @@ import { ActionMetadata } from '@start9labs/start-sdk/cjs/sdk/lib/types'
|
||||
})
|
||||
export class AppActionsPage {
|
||||
readonly pkgId = getPkgId(this.route)
|
||||
readonly pkg$ = this.patch.watch$('package-data', this.pkgId)
|
||||
readonly pkg$ = this.patch.watch$('packageData', this.pkgId)
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
@@ -184,7 +184,7 @@ export class AppActionsPage {
|
||||
try {
|
||||
const res = await this.embassyApi.executePackageAction({
|
||||
id: this.pkgId,
|
||||
'action-id': actionId,
|
||||
actionId,
|
||||
input,
|
||||
})
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export class AppInterfacesPage {
|
||||
readonly pkgId = getPkgId(this.route)
|
||||
|
||||
readonly serviceInterfaces$ = this.patch
|
||||
.watch$('package-data', this.pkgId, 'service-interfaces')
|
||||
.watch$('packageData', this.pkgId, 'serviceInterfaces')
|
||||
.pipe(
|
||||
map(interfaces => {
|
||||
const sorted = Object.values(interfaces)
|
||||
|
||||
@@ -14,20 +14,20 @@
|
||||
<p>{{ manifest.version | displayEmver }}</p>
|
||||
<status
|
||||
[rendering]="pkg.primaryRendering"
|
||||
[installingInfo]="$any(pkg.entry['state-info'])['installing-info']"
|
||||
[installingInfo]="$any(pkg.entry.stateInfo).installingInfo"
|
||||
weight="bold"
|
||||
size="small"
|
||||
[sigtermTimeout]="sigtermTimeout"
|
||||
></status>
|
||||
</ion-label>
|
||||
<ion-button
|
||||
*ngIf="pkg.entry['service-interfaces'] | hasUi"
|
||||
*ngIf="pkg.entry.serviceInterfaces | hasUi"
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="primary"
|
||||
(click)="launchUi($event, pkg.entry['service-interfaces'])"
|
||||
(click)="launchUi($event, pkg.entry.serviceInterfaces)"
|
||||
[disabled]="
|
||||
!(pkg.entry['state-info'].state | isLaunchable: pkgMainStatus.status)
|
||||
!(pkg.entry.stateInfo.state | isLaunchable: pkgMainStatus.status)
|
||||
"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
|
||||
@@ -32,7 +32,7 @@ export class AppListPkgComponent {
|
||||
: null
|
||||
}
|
||||
|
||||
launchUi(e: Event, interfaces: PackageDataEntry['service-interfaces']): void {
|
||||
launchUi(e: Event, interfaces: PackageDataEntry['serviceInterfaces']): void {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.launcherService.launch(interfaces)
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getManifest } from 'src/app/util/get-package-data'
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppListPage {
|
||||
readonly pkgs$ = this.patch.watch$('package-data').pipe(
|
||||
readonly pkgs$ = this.patch.watch$('packageData').pipe(
|
||||
map(pkgs => Object.values(pkgs)),
|
||||
startWith([]),
|
||||
pairwise(),
|
||||
|
||||
@@ -17,7 +17,7 @@ export class PackageInfoPipe implements PipeTransform {
|
||||
|
||||
transform(pkgId: string): Observable<PkgInfo> {
|
||||
return combineLatest([
|
||||
this.patch.watch$('package-data', pkgId).pipe(filter(Boolean)),
|
||||
this.patch.watch$('packageData', pkgId).pipe(filter(Boolean)),
|
||||
this.depErrorService.getPkgDepErrors$(pkgId),
|
||||
]).pipe(map(([pkg, depErrors]) => getPackageInfo(pkg, depErrors)))
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export class AppPropertiesPage {
|
||||
unmasked: { [key: string]: boolean } = {}
|
||||
|
||||
stopped$ = this.patch
|
||||
.watch$('package-data', this.pkgId, 'status', 'main', 'status')
|
||||
.watch$('packageData', this.pkgId, 'status', 'main', 'status')
|
||||
.pipe(map(status => status === PackageMainStatus.Stopped))
|
||||
|
||||
@ViewChild(IonBackButtonDelegate, { static: false })
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<!-- ** installing, updating, restoring ** -->
|
||||
<ng-container *ngIf="showProgress(pkg); else installed">
|
||||
<app-show-progress
|
||||
*ngIf="pkg['state-info']['installing-info'] as installingInfo"
|
||||
*ngIf="pkg.stateInfo.installingInfo as installingInfo"
|
||||
[phases]="installingInfo.progress.phases"
|
||||
></app-show-progress>
|
||||
</ng-container>
|
||||
@@ -35,7 +35,7 @@
|
||||
<app-show-menu [buttons]="pkg | toButtons"></app-show-menu>
|
||||
<!-- ** additional ** -->
|
||||
<app-show-additional
|
||||
[manifest]="pkg['state-info'].manifest"
|
||||
[manifest]="pkg.stateInfo.manifest"
|
||||
></app-show-additional>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
@@ -47,7 +47,7 @@ export class AppShowPage {
|
||||
private readonly pkgId = getPkgId(this.route)
|
||||
|
||||
readonly pkgPlus$ = combineLatest([
|
||||
this.patch.watch$('package-data', this.pkgId),
|
||||
this.patch.watch$('packageData', this.pkgId),
|
||||
this.depErrorService.getPkgDepErrors$(this.pkgId),
|
||||
]).pipe(
|
||||
tap(([pkg, _]) => {
|
||||
@@ -85,7 +85,7 @@ export class AppShowPage {
|
||||
): DependencyInfo[] {
|
||||
const manifest = getManifest(pkg)
|
||||
|
||||
return Object.keys(pkg['current-dependencies'])
|
||||
return Object.keys(pkg.currentDependencies)
|
||||
.filter(id => !!manifest.dependencies[id])
|
||||
.map(id => this.getDepValues(pkg, manifest, id, depErrors))
|
||||
}
|
||||
@@ -103,11 +103,11 @@ export class AppShowPage {
|
||||
depErrors,
|
||||
)
|
||||
|
||||
const depInfo = pkg['dependency-info'][depId]
|
||||
const depInfo = pkg.dependencyInfo[depId]
|
||||
|
||||
return {
|
||||
id: depId,
|
||||
version: pkg['current-dependencies'][depId].versionRange, // @TODO do we want this version range?
|
||||
version: pkg.currentDependencies[depId].versionRange, // @TODO do we want this version range?
|
||||
title: depInfo?.title || depId,
|
||||
icon: depInfo?.icon || '',
|
||||
errorText: errorText
|
||||
@@ -184,7 +184,7 @@ export class AppShowPage {
|
||||
const dependentInfo: DependentInfo = {
|
||||
id: pkgManifest.id,
|
||||
title: pkgManifest.title,
|
||||
version: pkg['current-dependencies'][depId].versionRange,
|
||||
version: pkg.currentDependencies[depId].versionRange,
|
||||
}
|
||||
const navigationExtras: NavigationExtras = {
|
||||
state: { dependentInfo },
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
*ngIf="manifest['git-hash'] as gitHash; else noHash"
|
||||
*ngIf="manifest.gitHash as gitHash; else noHash"
|
||||
button
|
||||
detail="false"
|
||||
(click)="copy(gitHash)"
|
||||
@@ -37,15 +37,15 @@
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['marketing-site']"
|
||||
[disabled]="!manifest['marketing-site']"
|
||||
[href]="manifest.marketingSite"
|
||||
[disabled]="!manifest.marketingSite"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Marketing Site</h2>
|
||||
<p>{{ manifest['marketing-site'] || 'Not provided' }}</p>
|
||||
<p>{{ manifest.marketingSite || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
@@ -54,52 +54,52 @@
|
||||
<ion-col responsiveCol sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
[href]="manifest['upstream-repo']"
|
||||
[href]="manifest.upstreamRepo"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ manifest['upstream-repo'] }}</p>
|
||||
<p>{{ manifest.upstreamRepo }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['wrapper-repo']"
|
||||
[href]="manifest.wrapperRepo"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ manifest['wrapper-repo'] }}</p>
|
||||
<p>{{ manifest.wrapperRepo }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['support-site']"
|
||||
[disabled]="!manifest['support-site']"
|
||||
[href]="manifest.supportSite"
|
||||
[disabled]="!manifest.supportSite"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ manifest['support-site'] || 'Not provided' }}</p>
|
||||
<p>{{ manifest.supportSite || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['donation-url']"
|
||||
[disabled]="!manifest['donation-url']"
|
||||
[href]="manifest.donationUrl"
|
||||
[disabled]="!manifest.donationUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Donation Link</h2>
|
||||
<p>{{ manifest['donation-url'] || 'Not provided' }}</p>
|
||||
<p>{{ manifest.donationUrl || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<status
|
||||
size="x-large"
|
||||
weight="600"
|
||||
[installingInfo]="$any(pkg['state-info'])['installing-info']"
|
||||
[installingInfo]="$any(pkg.stateInfo).installingInfo"
|
||||
[rendering]="PR[status.primary]"
|
||||
[sigtermTimeout]="sigtermTimeout"
|
||||
></status>
|
||||
@@ -60,7 +60,7 @@
|
||||
class="action-button"
|
||||
color="primary"
|
||||
[disabled]="
|
||||
!(pkg['state-info'].state | isLaunchable: pkgStatus.main.status)
|
||||
!(pkg.stateInfo.state | isLaunchable: pkgStatus.main.status)
|
||||
"
|
||||
(click)="launchUi(interfaces)"
|
||||
>
|
||||
|
||||
@@ -55,8 +55,8 @@ export class AppShowStatusComponent {
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
get interfaces(): PackageDataEntry['service-interfaces'] {
|
||||
return this.pkg['service-interfaces']
|
||||
get interfaces(): PackageDataEntry['serviceInterfaces'] {
|
||||
return this.pkg.serviceInterfaces
|
||||
}
|
||||
|
||||
get pkgStatus(): Status {
|
||||
@@ -89,7 +89,7 @@ export class AppShowStatusComponent {
|
||||
: null
|
||||
}
|
||||
|
||||
launchUi(interfaces: PackageDataEntry['service-interfaces']): void {
|
||||
launchUi(interfaces: PackageDataEntry['serviceInterfaces']): void {
|
||||
this.launcherService.launch(interfaces)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
) {}
|
||||
|
||||
transform(pkg: PackageDataEntry<InstalledState>): Button[] {
|
||||
const manifest = pkg['state-info'].manifest
|
||||
const manifest = pkg.stateInfo.manifest
|
||||
|
||||
return [
|
||||
// instructions
|
||||
@@ -46,7 +46,7 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
description: `Understand how to use ${manifest.title}`,
|
||||
icon: 'list-outline',
|
||||
highlighted$: this.patch
|
||||
.watch$('ui', 'ack-instructions', manifest.id)
|
||||
.watch$('ui', 'ackInstructions', manifest.id)
|
||||
.pipe(map(seen => !seen)),
|
||||
},
|
||||
// config
|
||||
@@ -122,7 +122,7 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
private viewInMarketplaceButton(
|
||||
pkg: PackageDataEntry<InstalledState>,
|
||||
): Button {
|
||||
const url = pkg['marketplace-url']
|
||||
const url = pkg.marketplaceUrl
|
||||
const queryParams = url ? { url } : {}
|
||||
|
||||
let button: Button = {
|
||||
@@ -130,7 +130,7 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
icon: 'storefront-outline',
|
||||
action: () =>
|
||||
this.navCtrl.navigateForward(
|
||||
[`marketplace/${pkg['state-info'].manifest.id}`],
|
||||
[`marketplace/${pkg.stateInfo.manifest.id}`],
|
||||
{
|
||||
queryParams,
|
||||
},
|
||||
|
||||
@@ -19,16 +19,14 @@ export class ToHealthChecksPipe implements PipeTransform {
|
||||
transform(
|
||||
manifest: Manifest,
|
||||
): Observable<Record<string, HealthCheckResult | null> | null> {
|
||||
return this.patch
|
||||
.watch$('package-data', manifest.id, 'status', 'main')
|
||||
.pipe(
|
||||
map(main => {
|
||||
return main.status === PackageMainStatus.Running &&
|
||||
!isEmptyObject(main.health)
|
||||
? main.health
|
||||
: null
|
||||
}),
|
||||
startWith(null),
|
||||
)
|
||||
return this.patch.watch$('packageData', manifest.id, 'status', 'main').pipe(
|
||||
map(main => {
|
||||
return main.status === PackageMainStatus.Running &&
|
||||
!isEmptyObject(main.health)
|
||||
? main.health
|
||||
: null
|
||||
}),
|
||||
startWith(null),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DevConfigPage } from './dev-config.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DevConfigPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
BackupReportPageModule,
|
||||
FormsModule,
|
||||
MonacoEditorModule,
|
||||
],
|
||||
declarations: [DevConfigPage],
|
||||
})
|
||||
export class DevConfigPageModule {}
|
||||
@@ -1,28 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button
|
||||
[defaultHref]="'/developer/projects/' + projectId"
|
||||
></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>
|
||||
Config
|
||||
<ion-spinner
|
||||
*ngIf="saving"
|
||||
name="crescent"
|
||||
style="transform: scale(0.55); position: absolute"
|
||||
></ion-spinner>
|
||||
</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="preview()">Preview</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ngx-monaco-editor
|
||||
(keyup)="save()"
|
||||
[options]="editorOptions"
|
||||
[(ngModel)]="code"
|
||||
></ngx-monaco-editor>
|
||||
</ion-content>
|
||||
@@ -1,82 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { debounce, ErrorToastService } from '@start9labs/shared'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { filter, take } from 'rxjs/operators'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { getProjectId } from 'src/app/util/get-project-id'
|
||||
import { GenericFormPage } from '../../../modals/generic-form/generic-form.page'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'dev-config',
|
||||
templateUrl: 'dev-config.page.html',
|
||||
styleUrls: ['dev-config.page.scss'],
|
||||
})
|
||||
export class DevConfigPage {
|
||||
readonly projectId = getProjectId(this.route)
|
||||
editorOptions = { theme: 'vs-dark', language: 'yaml' }
|
||||
code: string = ''
|
||||
saving: boolean = false
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly api: ApiService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.patch
|
||||
.watch$('ui', 'dev', this.projectId, 'config')
|
||||
.pipe(filter(Boolean), take(1))
|
||||
.subscribe(config => {
|
||||
this.code = config
|
||||
})
|
||||
}
|
||||
|
||||
async preview() {
|
||||
let doc: any
|
||||
try {
|
||||
doc = yaml.load(this.code)
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
}
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
component: GenericFormPage,
|
||||
componentProps: {
|
||||
title: 'Config Sample',
|
||||
spec: JSON.parse(JSON.stringify(doc, null, 2)),
|
||||
buttons: [
|
||||
{
|
||||
text: 'OK',
|
||||
handler: () => {
|
||||
return
|
||||
},
|
||||
isSubmit: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
@debounce(1000)
|
||||
async save() {
|
||||
this.saving = true
|
||||
try {
|
||||
await this.api.setDbValue<string>(
|
||||
['dev', this.projectId, 'config'],
|
||||
this.code,
|
||||
)
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.saving = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DevInstructionsPage } from './dev-instructions.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DevInstructionsPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
BackupReportPageModule,
|
||||
FormsModule,
|
||||
MonacoEditorModule,
|
||||
],
|
||||
declarations: [DevInstructionsPage],
|
||||
})
|
||||
export class DevInstructionsPageModule {}
|
||||
@@ -1,28 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button
|
||||
[defaultHref]="'/developer/projects/' + projectId"
|
||||
></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>
|
||||
Instructions
|
||||
<ion-spinner
|
||||
*ngIf="saving"
|
||||
name="crescent"
|
||||
style="transform: scale(0.55); position: absolute"
|
||||
></ion-spinner>
|
||||
</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="preview()">Preview</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ngx-monaco-editor
|
||||
(keyup)="save()"
|
||||
[options]="editorOptions"
|
||||
[(ngModel)]="code"
|
||||
></ngx-monaco-editor>
|
||||
</ion-content>
|
||||
@@ -1,69 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { filter, take } from 'rxjs/operators'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import {
|
||||
debounce,
|
||||
ErrorToastService,
|
||||
MarkdownComponent,
|
||||
} from '@start9labs/shared'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { getProjectId } from 'src/app/util/get-project-id'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'dev-instructions',
|
||||
templateUrl: 'dev-instructions.page.html',
|
||||
styleUrls: ['dev-instructions.page.scss'],
|
||||
})
|
||||
export class DevInstructionsPage {
|
||||
readonly projectId = getProjectId(this.route)
|
||||
editorOptions = { theme: 'vs-dark', language: 'markdown' }
|
||||
code = ''
|
||||
saving = false
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly api: ApiService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.patch
|
||||
.watch$('ui', 'dev', this.projectId, 'instructions')
|
||||
.pipe(filter(Boolean), take(1))
|
||||
.subscribe(config => {
|
||||
this.code = config
|
||||
})
|
||||
}
|
||||
|
||||
async preview() {
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
title: 'Instructions Sample',
|
||||
content: this.code,
|
||||
},
|
||||
component: MarkdownComponent,
|
||||
})
|
||||
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
@debounce(1000)
|
||||
async save() {
|
||||
this.saving = true
|
||||
try {
|
||||
await this.api.setDbValue<string>(
|
||||
['dev', this.projectId, 'instructions'],
|
||||
this.code,
|
||||
)
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
this.saving = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DevManifestPage } from './dev-manifest.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DevManifestPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
BackupReportPageModule,
|
||||
FormsModule,
|
||||
MonacoEditorModule,
|
||||
],
|
||||
declarations: [DevManifestPage],
|
||||
})
|
||||
export class DevManifestPageModule {}
|
||||
@@ -1,17 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button
|
||||
[defaultHref]="'/developer/projects/' + projectId"
|
||||
></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Manifest</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ngx-monaco-editor
|
||||
[options]="editorOptions"
|
||||
[(ngModel)]="manifest"
|
||||
></ngx-monaco-editor>
|
||||
</ion-content>
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { take } from 'rxjs/operators'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { getProjectId } from 'src/app/util/get-project-id'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'dev-manifest',
|
||||
templateUrl: 'dev-manifest.page.html',
|
||||
styleUrls: ['dev-manifest.page.scss'],
|
||||
})
|
||||
export class DevManifestPage {
|
||||
readonly projectId = getProjectId(this.route)
|
||||
editorOptions = { theme: 'vs-dark', language: 'yaml', readOnly: true }
|
||||
manifest: string = ''
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.patch
|
||||
.watch$('ui', 'dev', this.projectId)
|
||||
.pipe(take(1))
|
||||
.subscribe(devData => {
|
||||
this.manifest = yaml.dump(devData['basic-info'])
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DeveloperListPage } from './developer-list.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DeveloperListPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
BackupReportPageModule,
|
||||
],
|
||||
declarations: [DeveloperListPage],
|
||||
})
|
||||
export class DeveloperListPageModule {}
|
||||
@@ -1,37 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Developer Tools</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<badge-menu-button></badge-menu-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-item-divider>Projects</ion-item-divider>
|
||||
|
||||
<ion-item button detail="false" (click)="openCreateProjectModal()">
|
||||
<ion-icon slot="start" name="add" color="dark"></ion-icon>
|
||||
<ion-label>
|
||||
<ion-text color="dark">Create project</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item
|
||||
button
|
||||
*ngFor="let entry of devData | keyvalue"
|
||||
[routerLink]="[entry.key]"
|
||||
>
|
||||
<p>{{ entry.value.name }}</p>
|
||||
<ion-button
|
||||
slot="end"
|
||||
fill="clear"
|
||||
(click)="presentAction(entry.key, $event)"
|
||||
>
|
||||
<ion-icon name="ellipsis-horizontal"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
@@ -1,266 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
import {
|
||||
ActionSheetButton,
|
||||
ActionSheetController,
|
||||
AlertController,
|
||||
LoadingController,
|
||||
ModalController,
|
||||
} from '@ionic/angular'
|
||||
import {
|
||||
GenericInputComponent,
|
||||
GenericInputOptions,
|
||||
} from 'src/app/modals/generic-input/generic-input.component'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { v4 } from 'uuid'
|
||||
import { DataModel, DevData } from 'src/app/services/patch-db/data-model'
|
||||
import { ErrorToastService } from '@start9labs/shared'
|
||||
import { TuiDestroyService } from '@taiga-ui/cdk'
|
||||
import { takeUntil } from 'rxjs/operators'
|
||||
|
||||
@Component({
|
||||
selector: 'developer-list',
|
||||
templateUrl: 'developer-list.page.html',
|
||||
styleUrls: ['developer-list.page.scss'],
|
||||
providers: [TuiDestroyService],
|
||||
})
|
||||
export class DeveloperListPage {
|
||||
devData: DevData = {}
|
||||
|
||||
constructor(
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly api: ApiService,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly destroy$: TuiDestroyService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly actionCtrl: ActionSheetController,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.patch
|
||||
.watch$('ui', 'dev')
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(dd => {
|
||||
this.devData = dd
|
||||
})
|
||||
}
|
||||
|
||||
async openCreateProjectModal() {
|
||||
const projNumber = Object.keys(this.devData).length + 1
|
||||
const options: GenericInputOptions = {
|
||||
title: 'Add new project',
|
||||
message: 'Create a new dev project.',
|
||||
label: 'New project',
|
||||
useMask: false,
|
||||
placeholder: `Project ${projNumber}`,
|
||||
nullable: true,
|
||||
initialValue: `Project ${projNumber}`,
|
||||
buttonText: 'Save',
|
||||
submitFn: (value: string) => this.createProject(value),
|
||||
}
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: { options },
|
||||
cssClass: 'alertlike-modal',
|
||||
presentingElement: await this.modalCtrl.getTop(),
|
||||
component: GenericInputComponent,
|
||||
})
|
||||
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
async presentAction(id: string, event: Event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
const buttons: ActionSheetButton[] = [
|
||||
{
|
||||
text: 'Edit Name',
|
||||
icon: 'pencil',
|
||||
handler: () => {
|
||||
this.openEditNameModal(id)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Delete',
|
||||
icon: 'trash',
|
||||
role: 'destructive',
|
||||
handler: () => {
|
||||
this.presentAlertDelete(id)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const action = await this.actionCtrl.create({
|
||||
header: this.devData[id].name,
|
||||
subHeader: 'Manage project',
|
||||
mode: 'ios',
|
||||
buttons,
|
||||
})
|
||||
|
||||
await action.present()
|
||||
}
|
||||
|
||||
async openEditNameModal(id: string) {
|
||||
const curName = this.devData[id].name
|
||||
const options: GenericInputOptions = {
|
||||
title: 'Edit Name',
|
||||
message: 'Edit the name of your project.',
|
||||
label: 'Name',
|
||||
useMask: false,
|
||||
placeholder: curName,
|
||||
nullable: true,
|
||||
initialValue: curName,
|
||||
buttonText: 'Save',
|
||||
submitFn: (value: string) => this.editName(id, value),
|
||||
}
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: { options },
|
||||
cssClass: 'alertlike-modal',
|
||||
presentingElement: await this.modalCtrl.getTop(),
|
||||
component: GenericInputComponent,
|
||||
})
|
||||
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
async createProject(name: string) {
|
||||
// fail silently if duplicate project name
|
||||
if (
|
||||
Object.values(this.devData)
|
||||
.map(v => v.name)
|
||||
.includes(name)
|
||||
)
|
||||
return
|
||||
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Creating Project...',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
const id = v4()
|
||||
const config = yaml
|
||||
.dump(SAMPLE_CONFIG)
|
||||
.replace(/warning:/g, '# Optional\n warning:')
|
||||
|
||||
const def = { name, config, instructions: SAMPLE_INSTUCTIONS }
|
||||
await this.api.setDbValue<{
|
||||
name: string
|
||||
config: string
|
||||
instructions: string
|
||||
}>(['dev', id], def)
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
async presentAlertDelete(id: string) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Caution',
|
||||
message: `Are you sure you want to delete this project?`,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Delete',
|
||||
handler: () => {
|
||||
this.delete(id)
|
||||
},
|
||||
cssClass: 'enter-click',
|
||||
},
|
||||
],
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
async editName(id: string, newName: string) {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Saving...',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.api.setDbValue<string>(['dev', id, 'name'], newName)
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Removing Project...',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
const devDataToSave: DevData = JSON.parse(JSON.stringify(this.devData))
|
||||
delete devDataToSave[id]
|
||||
await this.api.setDbValue<DevData>(['dev'], devDataToSave)
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SAMPLE_INSTUCTIONS = `# Create Instructions using Markdown! :)`
|
||||
|
||||
const SAMPLE_CONFIG: ConfigSpec = {
|
||||
'sample-string': {
|
||||
type: 'string',
|
||||
name: 'Example String Input',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
// optional
|
||||
description: 'Example description for required string input.',
|
||||
placeholder: 'Enter string value',
|
||||
pattern: '^[a-zA-Z0-9! _]+$',
|
||||
'pattern-description': 'Must be alphanumeric (may contain underscore).',
|
||||
},
|
||||
'sample-number': {
|
||||
type: 'number',
|
||||
name: 'Example Number Input',
|
||||
nullable: false,
|
||||
range: '[5,1000000]',
|
||||
integral: true,
|
||||
// optional
|
||||
warning: 'Example warning to display when changing this number value.',
|
||||
units: 'ms',
|
||||
description: 'Example description for optional number input.',
|
||||
placeholder: 'Enter number value',
|
||||
},
|
||||
'sample-boolean': {
|
||||
type: 'boolean',
|
||||
name: 'Example Boolean Toggle',
|
||||
// optional
|
||||
description: 'Example description for boolean toggle',
|
||||
default: true,
|
||||
},
|
||||
'sample-enum': {
|
||||
type: 'enum',
|
||||
name: 'Example Enum Select',
|
||||
values: ['red', 'blue', 'green'],
|
||||
'value-names': {
|
||||
red: 'Red',
|
||||
blue: 'Blue',
|
||||
green: 'Green',
|
||||
},
|
||||
// optional
|
||||
warning: 'Example warning to display when changing this enum value.',
|
||||
description: 'Example description for enum select',
|
||||
default: 'red',
|
||||
},
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DeveloperMenuPage } from './developer-menu.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
import { GenericFormPageModule } from 'src/app/modals/generic-form/generic-form.module'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DeveloperMenuPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
BackupReportPageModule,
|
||||
GenericFormPageModule,
|
||||
FormsModule,
|
||||
MonacoEditorModule,
|
||||
SharedPipesModule,
|
||||
],
|
||||
declarations: [DeveloperMenuPage],
|
||||
})
|
||||
export class DeveloperMenuPageModule {}
|
||||
@@ -1,51 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button defaultHref="/developer"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ (projectData$ | async)?.name || '' }}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button routerLink="manifest">View Manifest</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-item
|
||||
*ngIf="projectData$ | async as projectData"
|
||||
button
|
||||
(click)="openBasicInfoModal(projectData)"
|
||||
>
|
||||
<ion-icon slot="start" name="information-circle-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>Basic Info</h2>
|
||||
<p>Complete basic info for your package</p>
|
||||
</ion-label>
|
||||
<ion-icon
|
||||
slot="end"
|
||||
color="success"
|
||||
name="checkmark"
|
||||
*ngIf="!(projectData['basic-info'] | empty)"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
slot="end"
|
||||
color="warning"
|
||||
name="remove-outline"
|
||||
*ngIf="projectData['basic-info'] | empty"
|
||||
></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item button detail routerLink="instructions" routerDirection="forward">
|
||||
<ion-icon slot="start" name="list-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>Instructions Generator</h2>
|
||||
<p>Create instructions and see how they will appear to the end user</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button detail routerLink="config">
|
||||
<ion-icon slot="start" name="construct-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>Config Generator</h2>
|
||||
<p>Edit the config with YAML and see it in real time</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
@@ -1,68 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { LoadingController, ModalController } from '@ionic/angular'
|
||||
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
|
||||
import { BasicInfo, getBasicInfoSpec } from './form-info'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ErrorToastService } from '@start9labs/shared'
|
||||
import { getProjectId } from 'src/app/util/get-project-id'
|
||||
import { DataModel, DevProjectData } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'developer-menu',
|
||||
templateUrl: 'developer-menu.page.html',
|
||||
styleUrls: ['developer-menu.page.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DeveloperMenuPage {
|
||||
readonly projectId = getProjectId(this.route)
|
||||
readonly projectData$ = this.patch.watch$('ui', 'dev', this.projectId)
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly api: ApiService,
|
||||
private readonly errToast: ErrorToastService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
async openBasicInfoModal(data: DevProjectData) {
|
||||
const modal = await this.modalCtrl.create({
|
||||
component: GenericFormPage,
|
||||
componentProps: {
|
||||
title: 'Basic Info',
|
||||
spec: getBasicInfoSpec(data),
|
||||
buttons: [
|
||||
{
|
||||
text: 'Save',
|
||||
handler: (basicInfo: BasicInfo) => {
|
||||
this.saveBasicInfo(basicInfo)
|
||||
},
|
||||
isSubmit: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
async saveBasicInfo(basicInfo: BasicInfo) {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Saving...',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.api.setDbValue<BasicInfo>(
|
||||
['dev', this.projectId, 'basic-info'],
|
||||
basicInfo,
|
||||
)
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
import { DevProjectData } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
export type BasicInfo = {
|
||||
id: string
|
||||
title: string
|
||||
'service-version-number': string
|
||||
'release-notes': string
|
||||
license: string
|
||||
'wrapper-repo': string
|
||||
'upstream-repo'?: string
|
||||
'support-site'?: string
|
||||
'marketing-site'?: string
|
||||
description: {
|
||||
short: string
|
||||
long: string
|
||||
}
|
||||
}
|
||||
|
||||
export function getBasicInfoSpec(devData: DevProjectData): ConfigSpec {
|
||||
const basicInfo = devData['basic-info']
|
||||
return {
|
||||
id: {
|
||||
type: 'string',
|
||||
name: 'ID',
|
||||
description: 'The package identifier used by the OS',
|
||||
placeholder: 'e.g. bitcoind',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
pattern: '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$',
|
||||
'pattern-description': 'Must be kebab case',
|
||||
default: basicInfo?.id,
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
name: 'Service Name',
|
||||
description: 'A human readable service title',
|
||||
placeholder: 'e.g. Bitcoin Core',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
default: basicInfo ? basicInfo.title : devData.name,
|
||||
},
|
||||
'service-version-number': {
|
||||
type: 'string',
|
||||
name: 'Service Version',
|
||||
description:
|
||||
'Service version - accepts up to four digits, where the last confirms to revisions necessary for StartOS - see documentation: https://github.com/Start9Labs/emver-rs. This value will change with each release of the service',
|
||||
placeholder: 'e.g. 0.1.2.3',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
pattern: '^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$',
|
||||
'pattern-description': 'Must be valid Emver version',
|
||||
default: basicInfo?.['service-version-number'],
|
||||
},
|
||||
description: {
|
||||
type: 'object',
|
||||
name: 'Marketplace Descriptions',
|
||||
spec: {
|
||||
short: {
|
||||
type: 'string',
|
||||
name: 'Short Description',
|
||||
description:
|
||||
'This is the first description visible to the user in the marketplace',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
textarea: true,
|
||||
default: basicInfo?.description?.short,
|
||||
pattern: '^.{1,320}$',
|
||||
'pattern-description': 'Must be shorter than 320 characters',
|
||||
},
|
||||
long: {
|
||||
type: 'string',
|
||||
name: 'Long Description',
|
||||
description: `This description will display with additional details in the service's individual marketplace page`,
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
textarea: true,
|
||||
default: basicInfo?.description?.long,
|
||||
pattern: '^.{1,5000}$',
|
||||
'pattern-description': 'Must be shorter than 5000 characters',
|
||||
},
|
||||
},
|
||||
},
|
||||
'release-notes': {
|
||||
type: 'string',
|
||||
name: 'Release Notes',
|
||||
description:
|
||||
'Markdown supported release notes for this version of this service.',
|
||||
placeholder: 'e.g. Markdown _release notes_ for **Bitcoin Core**',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
textarea: true,
|
||||
default: basicInfo?.['release-notes'],
|
||||
},
|
||||
license: {
|
||||
type: 'enum',
|
||||
name: 'License',
|
||||
values: [
|
||||
'gnu-agpl-v3',
|
||||
'gnu-gpl-v3',
|
||||
'gnu-lgpl-v3',
|
||||
'mozilla-public-license-2.0',
|
||||
'apache-license-2.0',
|
||||
'mit',
|
||||
'boost-software-license-1.0',
|
||||
'the-unlicense',
|
||||
'custom',
|
||||
],
|
||||
'value-names': {
|
||||
'gnu-agpl-v3': 'GNU AGPLv3',
|
||||
'gnu-gpl-v3': 'GNU GPLv3',
|
||||
'gnu-lgpl-v3': 'GNU LGPLv3',
|
||||
'mozilla-public-license-2.0': 'Mozilla Public License 2.0',
|
||||
'apache-license-2.0': 'Apache License 2.0',
|
||||
mit: 'mit',
|
||||
'boost-software-license-1.0': 'Boost Software License 1.0',
|
||||
'the-unlicense': 'The Unlicense',
|
||||
custom: 'Custom',
|
||||
},
|
||||
description: 'Example description for enum select',
|
||||
default: 'mit',
|
||||
},
|
||||
'wrapper-repo': {
|
||||
type: 'string',
|
||||
name: 'Wrapper Repo',
|
||||
description:
|
||||
'The Start9 wrapper repository URL for the package. This repo contains the manifest file (this), any scripts necessary for configuration, backups, actions, or health checks',
|
||||
placeholder: 'e.g. www.github.com/example',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
default: basicInfo?.['wrapper-repo'],
|
||||
},
|
||||
'upstream-repo': {
|
||||
type: 'string',
|
||||
name: 'Upstream Repo',
|
||||
description: 'The original project repository URL',
|
||||
placeholder: 'e.g. www.github.com/example',
|
||||
nullable: true,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
default: basicInfo?.['upstream-repo'],
|
||||
},
|
||||
'support-site': {
|
||||
type: 'string',
|
||||
name: 'Support Site',
|
||||
description: 'URL to the support site / channel for the project',
|
||||
placeholder: 'e.g. start9.com/support',
|
||||
nullable: true,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
default: basicInfo?.['support-site'],
|
||||
},
|
||||
'marketing-site': {
|
||||
type: 'string',
|
||||
name: 'Marketing Site',
|
||||
description: 'URL to the marketing site / channel for the project',
|
||||
placeholder: 'e.g. start9.com',
|
||||
nullable: true,
|
||||
masked: false,
|
||||
copyable: false,
|
||||
default: basicInfo?.['marketing-site'],
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'projects',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'projects',
|
||||
loadChildren: () =>
|
||||
import('./developer-list/developer-list.module').then(
|
||||
m => m.DeveloperListPageModule,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId',
|
||||
loadChildren: () =>
|
||||
import('./developer-menu/developer-menu.module').then(
|
||||
m => m.DeveloperMenuPageModule,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId/config',
|
||||
loadChildren: () =>
|
||||
import('./dev-config/dev-config.module').then(m => m.DevConfigPageModule),
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId/instructions',
|
||||
loadChildren: () =>
|
||||
import('./dev-instructions/dev-instructions.module').then(
|
||||
m => m.DevInstructionsPageModule,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId/manifest',
|
||||
loadChildren: () =>
|
||||
import('./dev-manifest/dev-manifest.module').then(
|
||||
m => m.DevManifestPageModule,
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class DeveloperRoutingModule {}
|
||||
@@ -29,7 +29,7 @@ export class MarketplaceListPage {
|
||||
}),
|
||||
)
|
||||
|
||||
readonly localPkgs$ = this.patch.watch$('package-data')
|
||||
readonly localPkgs$ = this.patch.watch$('packageData')
|
||||
|
||||
readonly details$ = this.marketplaceService.getSelectedHost$().pipe(
|
||||
map(({ url, name }) => {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<ng-container *ngIf="localPkg; else install">
|
||||
<ng-container
|
||||
*ngIf="
|
||||
localPkg['state-info'].state === 'installed' &&
|
||||
localPkg.stateInfo.state === 'installed' &&
|
||||
(localPkg | toManifest) as manifest
|
||||
"
|
||||
>
|
||||
|
||||
@@ -66,7 +66,7 @@ export class MarketplaceShowControlsComponent {
|
||||
if (!this.localPkg) {
|
||||
this.alertInstall(url)
|
||||
} else {
|
||||
const originalUrl = this.localPkg['marketplace-url']
|
||||
const originalUrl = this.localPkg.marketplaceUrl
|
||||
|
||||
if (!sameUrl(url, originalUrl)) {
|
||||
const proceed = await this.presentAlertDifferentMarketplace(
|
||||
@@ -98,12 +98,11 @@ export class MarketplaceShowControlsComponent {
|
||||
this.patch.watch$('ui', 'marketplace'),
|
||||
)
|
||||
|
||||
const name: string = marketplaces['known-hosts'][url]?.name || url
|
||||
const name: string = marketplaces.knownHosts[url]?.name || url
|
||||
|
||||
let originalName: string | undefined
|
||||
if (originalUrl) {
|
||||
originalName =
|
||||
marketplaces['known-hosts'][originalUrl]?.name || originalUrl
|
||||
originalName = marketplaces.knownHosts[originalUrl]?.name || originalUrl
|
||||
}
|
||||
|
||||
return new Promise(async resolve => {
|
||||
|
||||
@@ -20,7 +20,7 @@ export class MarketplaceShowPage {
|
||||
readonly loadVersion$ = new BehaviorSubject<string>('*')
|
||||
|
||||
readonly localPkg$ = this.patch
|
||||
.watch$('package-data', this.pkgId)
|
||||
.watch$('packageData', this.pkgId)
|
||||
.pipe(filter(Boolean), shareReplay({ bufferSize: 1, refCount: true }))
|
||||
|
||||
readonly pkg$ = this.loadVersion$.pipe(
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
>
|
||||
<ion-text
|
||||
*ngIf="
|
||||
localPkg['state-info']['installing-info']!.progress.overall
|
||||
localPkg.stateInfo.installingInfo.progress.overall
|
||||
| installingProgressString as progress
|
||||
"
|
||||
color="primary"
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<ion-label>
|
||||
<h2>
|
||||
<b>
|
||||
<span *ngIf="not['package-id'] as pkgId">
|
||||
<span *ngIf="not.packageId as pkgId">
|
||||
{{ packageData[pkgId] ? (packageData[pkgId] |
|
||||
toManifest).title : pkgId }} -
|
||||
</span>
|
||||
@@ -104,7 +104,7 @@
|
||||
View Full Message
|
||||
</a>
|
||||
</p>
|
||||
<p>{{ not['created-at'] | date: 'medium' }}</p>
|
||||
<p>{{ not.createdAt | date: 'medium' }}</p>
|
||||
</ion-label>
|
||||
<ion-button
|
||||
*ngIf="not.code === 1"
|
||||
@@ -116,11 +116,11 @@
|
||||
View Report
|
||||
</ion-button>
|
||||
<ion-button
|
||||
*ngIf="not['package-id'] && packageData[not['package-id']]"
|
||||
*ngIf="not.packageId && packageData[not.packageId]"
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="dark"
|
||||
[routerLink]="['/services', not['package-id']]"
|
||||
[routerLink]="['/services', not.packageId]"
|
||||
>
|
||||
View Service
|
||||
</ion-button>
|
||||
|
||||
@@ -29,7 +29,7 @@ export class NotificationsPage {
|
||||
needInfinite = false
|
||||
fromToast = !!this.route.snapshot.queryParamMap.get('toast')
|
||||
readonly perPage = 40
|
||||
readonly packageData$ = this.patch.watch$('package-data').pipe(first())
|
||||
readonly packageData$ = this.patch.watch$('packageData').pipe(first())
|
||||
|
||||
constructor(
|
||||
private readonly embassyApi: ApiService,
|
||||
@@ -116,7 +116,7 @@ export class NotificationsPage {
|
||||
component: BackupReportPage,
|
||||
componentProps: {
|
||||
report: notification.data,
|
||||
timestamp: notification['created-at'],
|
||||
timestamp: notification.createdAt,
|
||||
},
|
||||
})
|
||||
await modal.present()
|
||||
|
||||
@@ -43,7 +43,7 @@ export class RestorePage {
|
||||
useMask: true,
|
||||
buttonText: 'Next',
|
||||
submitFn: async (password: string) => {
|
||||
const passwordHash = target.entry['embassy-os']?.['password-hash'] || ''
|
||||
const passwordHash = target.entry.startOs?.passwordHash || ''
|
||||
argon2.verify(passwordHash, password)
|
||||
await this.restoreFromBackup(target, password)
|
||||
},
|
||||
@@ -71,7 +71,7 @@ export class RestorePage {
|
||||
|
||||
try {
|
||||
const backupInfo = await this.embassyApi.getBackupInfo({
|
||||
'target-id': target.id,
|
||||
targetId: target.id,
|
||||
password,
|
||||
})
|
||||
this.presentModalSelect(target.id, backupInfo, password, oldPassword)
|
||||
|
||||
@@ -18,11 +18,11 @@ import { Observable } from 'rxjs'
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BackingUpComponent {
|
||||
readonly pkgs$ = this.patch.watch$('package-data').pipe(take(1))
|
||||
readonly pkgs$ = this.patch.watch$('packageData').pipe(take(1))
|
||||
readonly backupProgress$ = this.patch.watch$(
|
||||
'server-info',
|
||||
'status-info',
|
||||
'backup-progress',
|
||||
'serverInfo',
|
||||
'statusInfo',
|
||||
'backupProgress',
|
||||
)
|
||||
|
||||
constructor(private readonly patch: PatchDB<DataModel>) {}
|
||||
@@ -33,7 +33,7 @@ export class BackingUpComponent {
|
||||
})
|
||||
export class PkgMainStatusPipe implements PipeTransform {
|
||||
transform(pkgId: string): Observable<PackageMainStatus> {
|
||||
return this.patch.watch$('package-data', pkgId, 'status', 'main', 'status')
|
||||
return this.patch.watch$('packageData', pkgId, 'status', 'main', 'status')
|
||||
}
|
||||
|
||||
constructor(private readonly patch: PatchDB<DataModel>) {}
|
||||
|
||||
@@ -84,9 +84,7 @@ export class ServerBackupPage {
|
||||
buttonText: 'Create Backup',
|
||||
submitFn: async (password: string) => {
|
||||
// confirm password matches current master password
|
||||
const { 'password-hash': passwordHash } = await getServerInfo(
|
||||
this.patch,
|
||||
)
|
||||
const { passwordHash } = await getServerInfo(this.patch)
|
||||
argon2.verify(passwordHash, password)
|
||||
|
||||
// first time backup
|
||||
@@ -95,8 +93,7 @@ export class ServerBackupPage {
|
||||
// existing backup
|
||||
} else {
|
||||
try {
|
||||
const passwordHash =
|
||||
target.entry['embassy-os']?.['password-hash'] || ''
|
||||
const passwordHash = target.entry.startOs?.passwordHash || ''
|
||||
|
||||
argon2.verify(passwordHash, password)
|
||||
} catch {
|
||||
@@ -133,7 +130,7 @@ export class ServerBackupPage {
|
||||
useMask: true,
|
||||
buttonText: 'Create Backup',
|
||||
submitFn: async (oldPassword: string) => {
|
||||
const passwordHash = target.entry['embassy-os']?.['password-hash'] || ''
|
||||
const passwordHash = target.entry.startOs?.passwordHash || ''
|
||||
|
||||
argon2.verify(passwordHash, oldPassword)
|
||||
await this.createBackup(target, password, oldPassword)
|
||||
@@ -161,9 +158,9 @@ export class ServerBackupPage {
|
||||
|
||||
try {
|
||||
await this.embassyApi.createBackup({
|
||||
'target-id': target.id,
|
||||
'package-ids': this.serviceIds,
|
||||
'old-password': oldPassword || null,
|
||||
targetId: target.id,
|
||||
packageIds: this.serviceIds,
|
||||
oldPassword: oldPassword || null,
|
||||
password,
|
||||
})
|
||||
} finally {
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
<ion-item-divider>Memory</ion-item-divider>
|
||||
<ion-item>
|
||||
<ion-label>Percentage Used</ion-label>
|
||||
<ion-note slot="end">{{ memory['percentage-used'].value }} %</ion-note>
|
||||
<ion-note slot="end">{{ memory.percentageUsed }} %</ion-note>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Total</ion-label>
|
||||
@@ -94,15 +94,15 @@
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>zram Used</ion-label>
|
||||
<ion-note slot="end">{{ memory['zram-used'].value }} MiB</ion-note>
|
||||
<ion-note slot="end">{{ memory.zramUsed.value }} MiB</ion-note>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>zram Total</ion-label>
|
||||
<ion-note slot="end">{{ memory['zram-total'].value }} MiB</ion-note>
|
||||
<ion-note slot="end">{{ memory.zramTotal }} MiB</ion-note>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>zram Available</ion-label>
|
||||
<ion-note slot="end">{{ memory['zram-available'].value }} MiB</ion-note>
|
||||
<ion-note slot="end">{{ memory.available.value }} MiB</ion-note>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
@@ -110,18 +110,18 @@
|
||||
<ion-item-divider>CPU</ion-item-divider>
|
||||
<ion-item>
|
||||
<ion-label>Percentage Used</ion-label>
|
||||
<ion-note slot="end">{{ cpu['percentage-used'].value }} %</ion-note>
|
||||
<ion-note slot="end">{{ cpu.percentageUsed.value }} %</ion-note>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>User Space</ion-label>
|
||||
<ion-note slot="end">
|
||||
<ion-text>{{ cpu['user-space'].value }} %</ion-text>
|
||||
<ion-text>{{ cpu.userSpace.value }} %</ion-text>
|
||||
</ion-note>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Kernel Space</ion-label>
|
||||
<ion-note slot="end">
|
||||
<ion-text>{{ cpu['kernel-space'].value }} %</ion-text>
|
||||
<ion-text>{{ cpu.kernelSpace.value }} %</ion-text>
|
||||
</ion-note>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
@@ -138,7 +138,7 @@
|
||||
<ion-item-divider>Disk</ion-item-divider>
|
||||
<ion-item>
|
||||
<ion-label>Percentage Used</ion-label>
|
||||
<ion-note slot="end">{{ disk['percentage-used'].value }} %</ion-note>
|
||||
<ion-note slot="end">{{ disk.percentageUsed.value }} %</ion-note>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Capacity</ion-label>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<!-- loaded -->
|
||||
<ion-item-group *ngIf="server$ | async as server; else loading">
|
||||
<ion-item
|
||||
*ngIf="!server['ntp-synced']"
|
||||
*ngIf="!server.ntpSynced"
|
||||
color="warning"
|
||||
class="ion-margin-bottom"
|
||||
>
|
||||
@@ -62,15 +62,15 @@
|
||||
|
||||
<!-- "Create Backup" button only -->
|
||||
<p *ngIf="button.title === 'Create Backup'">
|
||||
<ng-container *ngIf="server['status-info'] as statusInfo">
|
||||
<ng-container *ngIf="server.statusInfo as statusInfo">
|
||||
<ion-text
|
||||
[color]="server['last-backup'] | backupColor"
|
||||
*ngIf="!statusInfo['backup-progress'] && !statusInfo['update-progress']"
|
||||
[color]="server.lastBackup | backupColor"
|
||||
*ngIf="!statusInfo.backupProgress && !statusInfo.updateProgress"
|
||||
>
|
||||
Last Backup: {{ server['last-backup'] ? (server['last-backup'] |
|
||||
date: 'medium') : 'never' }}
|
||||
Last Backup: {{ server.lastBackup ? (server.lastBackup | date:
|
||||
'medium') : 'never' }}
|
||||
</ion-text>
|
||||
<span *ngIf="!!statusInfo['backup-progress']" class="inline">
|
||||
<span *ngIf="!!statusInfo.backupProgress" class="inline">
|
||||
<ion-spinner
|
||||
color="success"
|
||||
style="height: 12px; width: 12px; margin-right: 6px"
|
||||
@@ -82,7 +82,7 @@
|
||||
<!-- "Software Update" button only -->
|
||||
<p *ngIf="button.title === 'Software Update'">
|
||||
<ion-text
|
||||
*ngIf="server['status-info'].updated; else notUpdated"
|
||||
*ngIf="server.statusInfo.updated; else notUpdated"
|
||||
class="inline"
|
||||
color="warning"
|
||||
>
|
||||
|
||||
@@ -37,7 +37,7 @@ export class ServerShowPage {
|
||||
manageClicks = 0
|
||||
powerClicks = 0
|
||||
|
||||
readonly server$ = this.patch.watch$('server-info')
|
||||
readonly server$ = this.patch.watch$('serverInfo')
|
||||
readonly showUpdate$ = this.eosService.showUpdate$
|
||||
readonly showDiskRepair$ = this.ClientStorageService.showDiskRepair$
|
||||
|
||||
@@ -141,7 +141,7 @@ export class ServerShowPage {
|
||||
}
|
||||
|
||||
// confirm current password is correct
|
||||
const { 'password-hash': passwordHash } = await getServerInfo(this.patch)
|
||||
const { passwordHash } = await getServerInfo(this.patch)
|
||||
try {
|
||||
argon2.verify(passwordHash, value.currPass)
|
||||
} catch (e) {
|
||||
@@ -160,8 +160,8 @@ export class ServerShowPage {
|
||||
|
||||
try {
|
||||
await this.embassyApi.resetPassword({
|
||||
'old-password': value.currPass,
|
||||
'new-password': value.newPass,
|
||||
oldPassword: value.currPass,
|
||||
newPassword: value.newPass,
|
||||
})
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: 'Password changed!',
|
||||
@@ -221,7 +221,7 @@ export class ServerShowPage {
|
||||
|
||||
try {
|
||||
await this.embassyApi.resetTor({
|
||||
'wipe-state': wipeState,
|
||||
wipeState: wipeState,
|
||||
reason: 'User triggered',
|
||||
})
|
||||
const toast = await this.toastCtrl.create({
|
||||
|
||||
@@ -30,17 +30,17 @@
|
||||
<ion-item>
|
||||
<ion-label class="break-all">
|
||||
<h2>Tor</h2>
|
||||
<p>{{ server['tor-address'] }}</p>
|
||||
<p>{{ server.torAddress }}</p>
|
||||
</ion-label>
|
||||
<div slot="end">
|
||||
<ion-button fill="clear" (click)="showQR(server['tor-address'])">
|
||||
<ion-button fill="clear" (click)="showQR(server.torAddress)">
|
||||
<ion-icon
|
||||
slot="icon-only"
|
||||
name="qr-code-outline"
|
||||
size="small"
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" (click)="copy(server['tor-address'])">
|
||||
<ion-button fill="clear" (click)="copy(server.torAddress)">
|
||||
<ion-icon
|
||||
slot="icon-only"
|
||||
name="copy-outline"
|
||||
@@ -52,13 +52,13 @@
|
||||
<ion-item>
|
||||
<ion-label class="break-all">
|
||||
<h2>LAN</h2>
|
||||
<p>{{ server['lan-address'] }}</p>
|
||||
<p>{{ server.lanAddress }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" (click)="copy(server['lan-address'])">
|
||||
<ion-button slot="end" fill="clear" (click)="copy(server.lanAddress)">
|
||||
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
<ng-container *ngFor="let iface of server['ip-info'] | keyvalue">
|
||||
<ng-container *ngFor="let iface of server.ipInfo| keyvalue">
|
||||
<ion-item *tuiLet="iface.value.ipv4 as ipv4">
|
||||
<ion-label>
|
||||
<h2>{{ iface.key }} (IPv4)</h2>
|
||||
@@ -92,13 +92,9 @@
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>CA fingerprint</h2>
|
||||
<p>{{ server['ca-fingerprint'] }}</p>
|
||||
<p>{{ server.caFingerprint }}</p>
|
||||
</ion-label>
|
||||
<ion-button
|
||||
slot="end"
|
||||
fill="clear"
|
||||
(click)="copy(server['ca-fingerprint'])"
|
||||
>
|
||||
<ion-button slot="end" fill="clear" (click)="copy(server.caFingerprint)">
|
||||
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
|
||||
@@ -13,7 +13,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ServerSpecsPage {
|
||||
readonly server$ = this.patch.watch$('server-info')
|
||||
readonly server$ = this.patch.watch$('serverInfo')
|
||||
|
||||
constructor(
|
||||
private readonly toastCtrl: ToastController,
|
||||
|
||||
@@ -52,10 +52,8 @@
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ getPlatformName(currentSession.metadata.platforms) }}</h1>
|
||||
<h2>
|
||||
Last Active: {{ currentSession['last-active'] | date : 'medium' }}
|
||||
</h2>
|
||||
<p>{{ currentSession['user-agent'] }}</p>
|
||||
<h2>Last Active: {{ currentSession.lastActive| date : 'medium' }}</h2>
|
||||
<p>{{ currentSession.userAgent }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -80,8 +78,8 @@
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ getPlatformName(session.metadata.platforms) }}</h1>
|
||||
<h2>Last Active: {{ session['last-active'] | date : 'medium' }}</h2>
|
||||
<p>{{ session['user-agent'] }}</p>
|
||||
<h2>Last Active: {{ session.lastActive | date : 'medium' }}</h2>
|
||||
<p>{{ session.userAgent }}</p>
|
||||
</ion-label>
|
||||
<ion-button
|
||||
slot="end"
|
||||
|
||||
@@ -35,8 +35,7 @@ export class SessionsPage {
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return (
|
||||
new Date(b['last-active']).valueOf() -
|
||||
new Date(a['last-active']).valueOf()
|
||||
new Date(b.lastActive).valueOf() - new Date(a.lastActive).valueOf()
|
||||
)
|
||||
})
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<ion-icon slot="start" name="key-outline" size="large"></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ ssh.hostname }}</h1>
|
||||
<h2>{{ ssh['created-at'] | date: 'medium' }}</h2>
|
||||
<h2>{{ ssh.createdAt| date: 'medium' }}</h2>
|
||||
<p>{{ ssh.alg }} {{ ssh.fingerprint }}</p>
|
||||
</ion-label>
|
||||
<ion-button
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item-divider>Available Networks</ion-item-divider>
|
||||
<ng-container *ngFor="let avWifi of wifi['available-wifi']">
|
||||
<ng-container *ngFor="let avWifi of wifi.availableWifi">
|
||||
<ion-item
|
||||
button
|
||||
detail="false"
|
||||
|
||||
@@ -39,8 +39,7 @@
|
||||
<h1 style="line-height: 1.3">{{ pkg.manifest.title }}</h1>
|
||||
<h2 class="inline">
|
||||
<span>
|
||||
{{ local['state-info'].manifest.version | displayEmver
|
||||
}}
|
||||
{{ local.stateInfo.manifest.version | displayEmver }}
|
||||
</span>
|
||||
|
||||
<ion-icon name="arrow-forward"></ion-icon>
|
||||
@@ -57,8 +56,8 @@
|
||||
</ion-label>
|
||||
<div slot="end" style="margin-left: 4px">
|
||||
<round-progress
|
||||
*ngIf="local['state-info'].state === 'updating' else notUpdating"
|
||||
[current]="(local['state-info']['installing-info'].progress.overall | installingProgress) || 0"
|
||||
*ngIf="local.stateInfo.state === 'updating' else notUpdating"
|
||||
[current]="(local.stateInfo.installingInfo.progress.overall | installingProgress) || 0"
|
||||
[max]="100"
|
||||
[radius]="13"
|
||||
[stroke]="3"
|
||||
@@ -86,9 +85,7 @@
|
||||
<div class="ion-padding" slot="content">
|
||||
<div class="notes">
|
||||
<h5>What's new</h5>
|
||||
<p
|
||||
[innerHTML]="pkg.manifest['release-notes'] | markdown"
|
||||
></p>
|
||||
<p [innerHTML]="pkg.manifest.releaseNotes| markdown"></p>
|
||||
</div>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
|
||||
@@ -42,7 +42,7 @@ export class UpdatesPage {
|
||||
readonly data$: Observable<UpdatesData> = combineLatest({
|
||||
hosts: this.marketplaceService.getKnownHosts$(true),
|
||||
marketplace: this.marketplaceService.getMarketplace$(),
|
||||
localPkgs: this.patch.watch$('package-data').pipe(
|
||||
localPkgs: this.patch.watch$('packageData').pipe(
|
||||
map(pkgs =>
|
||||
Object.values(pkgs).reduce((acc, curr) => {
|
||||
if (isInstalled(curr) || isUpdating(curr)) return { ...acc, curr }
|
||||
@@ -171,7 +171,7 @@ export class FilterUpdatesPipe implements PipeTransform {
|
||||
localPkg &&
|
||||
this.emver.compare(
|
||||
manifest.version,
|
||||
localPkg['state-info'].manifest.version,
|
||||
localPkg.stateInfo.manifest.version,
|
||||
) === 1
|
||||
)
|
||||
})
|
||||
|
||||
@@ -27,7 +27,7 @@ export class HealthComponent {
|
||||
] as const
|
||||
|
||||
readonly data$ = combineLatest([
|
||||
inject(PatchDB<DataModel>).watch$('package-data'),
|
||||
inject(PatchDB<DataModel>).watch$('packageData'),
|
||||
inject(DepErrorService).depErrors$,
|
||||
]).pipe(
|
||||
map(([data, depErrors]) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Manifest } from '@start9labs/marketplace'
|
||||
name: 'hasUi',
|
||||
})
|
||||
export class UiPipe implements PipeTransform {
|
||||
transform(interfaces: PackageDataEntry['service-interfaces']): boolean {
|
||||
transform(interfaces: PackageDataEntry['serviceInterfaces']): boolean {
|
||||
return interfaces ? hasUi(interfaces) : false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ export type ValueSpecOf<T extends ValueType> = T extends 'string'
|
||||
? ValueSpecList
|
||||
: T extends 'object'
|
||||
? ValueSpecObject
|
||||
: T extends 'pointer'
|
||||
? ValueSpecPointer
|
||||
: T extends 'union'
|
||||
? ValueSpecUnion
|
||||
: never
|
||||
@@ -60,16 +58,6 @@ export interface ValueSpecUnion {
|
||||
default: string
|
||||
}
|
||||
|
||||
export interface ValueSpecPointer extends WithStandalone {
|
||||
type: 'pointer'
|
||||
subtype: 'package' | 'system'
|
||||
'package-id': string
|
||||
target: 'lan-address' | 'tor-address' | 'config' | 'tor-key'
|
||||
interface: string // will only exist if target = tor-key || tor-address || lan-address
|
||||
selector?: string // will only exist if target = config
|
||||
multi?: boolean // will only exist if target = config
|
||||
}
|
||||
|
||||
export interface ValueSpecObject extends WithStandalone {
|
||||
type: 'object'
|
||||
spec: ConfigSpec
|
||||
|
||||
@@ -16,16 +16,16 @@ import { Log } from '@start9labs/shared'
|
||||
|
||||
export module Mock {
|
||||
export const ServerUpdated: ServerStatusInfo = {
|
||||
'backup-progress': null,
|
||||
'update-progress': null,
|
||||
backupProgress: null,
|
||||
updateProgress: null,
|
||||
updated: true,
|
||||
restarting: false,
|
||||
'shutting-down': false,
|
||||
shuttingDown: false,
|
||||
}
|
||||
export const MarketplaceEos: RR.GetMarketplaceEosRes = {
|
||||
version: '0.3.5.1',
|
||||
headline: 'Our biggest release ever.',
|
||||
'release-notes': {
|
||||
releaseNotes: {
|
||||
'0.3.5.1': 'Some **Markdown** release _notes_ for 0.3.5.1',
|
||||
'0.3.4.4': 'Some **Markdown** release _notes_ for 0.3.4.4',
|
||||
'0.3.4.3': 'Some **Markdown** release _notes_ for 0.3.4.3',
|
||||
@@ -51,19 +51,19 @@ export module Mock {
|
||||
id: 'bitcoind',
|
||||
title: 'Bitcoin Core',
|
||||
version: '0.21.0',
|
||||
'git-hash': 'abcdefgh',
|
||||
gitHash: 'abcdefgh',
|
||||
description: {
|
||||
short: 'A Bitcoin full node by Bitcoin Core.',
|
||||
long: 'Bitcoin is a decentralized consensus protocol and settlement network.',
|
||||
},
|
||||
replaces: ['banks', 'governments'],
|
||||
'release-notes': 'Taproot, Schnorr, and more.',
|
||||
releaseNotes: 'Taproot, Schnorr, and more.',
|
||||
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',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-wrapper',
|
||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||
supportSite: 'https://bitcoin.org',
|
||||
marketingSite: 'https://bitcoin.org',
|
||||
donationUrl: 'https://start9.com',
|
||||
alerts: {
|
||||
install: 'Bitcoin can take over a week to sync.',
|
||||
uninstall:
|
||||
@@ -72,9 +72,9 @@ export module Mock {
|
||||
start: 'Starting Bitcoin is good for your health.',
|
||||
stop: null,
|
||||
},
|
||||
'os-version': '0.2.12',
|
||||
osVersion: '0.2.12',
|
||||
dependencies: {},
|
||||
'has-config': true,
|
||||
hasConfig: true,
|
||||
}
|
||||
|
||||
export const MockManifestLnd: Manifest = {
|
||||
@@ -85,13 +85,13 @@ export module Mock {
|
||||
short: 'A bolt spec compliant client.',
|
||||
long: 'More info about LND. More info about LND. More info about LND.',
|
||||
},
|
||||
'release-notes': 'Dual funded channels!',
|
||||
releaseNotes: 'Dual funded channels!',
|
||||
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,
|
||||
wrapperRepo: 'https://github.com/start9labs/lnd-wrapper',
|
||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||
supportSite: 'https://lightning.engineering/',
|
||||
marketingSite: 'https://lightning.engineering/',
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: null,
|
||||
uninstall: null,
|
||||
@@ -100,7 +100,7 @@ export module Mock {
|
||||
start: 'Starting LND is good for your health.',
|
||||
stop: null,
|
||||
},
|
||||
'os-version': '0.2.12',
|
||||
osVersion: '0.2.12',
|
||||
dependencies: {
|
||||
bitcoind: {
|
||||
description: 'LND needs bitcoin to live.',
|
||||
@@ -112,25 +112,25 @@ export module Mock {
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
'has-config': true,
|
||||
hasConfig: true,
|
||||
}
|
||||
|
||||
export const MockManifestBitcoinProxy: Manifest = {
|
||||
id: 'btc-rpc-proxy',
|
||||
title: 'Bitcoin Proxy',
|
||||
version: '0.2.2',
|
||||
'git-hash': 'lmnopqrx',
|
||||
gitHash: 'lmnopqrx',
|
||||
description: {
|
||||
short: 'A super charger for your Bitcoin node.',
|
||||
long: 'More info about Bitcoin Proxy. More info about Bitcoin Proxy. More info about Bitcoin Proxy.',
|
||||
},
|
||||
'release-notes': 'Even better support for Bitcoin and wallets!',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
license: 'MIT',
|
||||
'wrapper-repo': 'https://github.com/start9labs/btc-rpc-proxy-wrapper',
|
||||
'upstream-repo': 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||
'support-site': '',
|
||||
'marketing-site': '',
|
||||
'donation-url': 'https://start9.com',
|
||||
wrapperRepo: 'https://github.com/start9labs/btc-rpc-proxy-wrapper',
|
||||
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||
supportSite: '',
|
||||
marketingSite: '',
|
||||
donationUrl: 'https://start9.com',
|
||||
alerts: {
|
||||
install: 'Testing install alert',
|
||||
uninstall: null,
|
||||
@@ -138,14 +138,14 @@ export module Mock {
|
||||
start: null,
|
||||
stop: null,
|
||||
},
|
||||
'os-version': '0.2.12',
|
||||
osVersion: '0.2.12',
|
||||
dependencies: {
|
||||
bitcoind: {
|
||||
description: 'Bitcoin Proxy requires a Bitcoin node.',
|
||||
optional: false,
|
||||
},
|
||||
},
|
||||
'has-config': false,
|
||||
hasConfig: false,
|
||||
}
|
||||
|
||||
export const BitcoinDep: DependencyMetadata = {
|
||||
@@ -178,8 +178,8 @@ export module Mock {
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
'dependency-metadata': {},
|
||||
'published-at': new Date().toISOString(),
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.20.0': {
|
||||
icon: BTC_ICON,
|
||||
@@ -191,8 +191,8 @@ export module Mock {
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
'dependency-metadata': {},
|
||||
'published-at': new Date().toISOString(),
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.21.0': {
|
||||
icon: BTC_ICON,
|
||||
@@ -201,13 +201,13 @@ export module Mock {
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.21.0',
|
||||
'release-notes':
|
||||
releaseNotes:
|
||||
'For a complete list of changes, please visit <a href="https://bitcoincore.org/en/releases/0.21.0/">https://bitcoincore.org/en/releases/0.21.0/</a><br /><ul><li>Taproot!</li><li>New RPCs</li><li>Experimental Descriptor Wallets</li></ul>',
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
'dependency-metadata': {},
|
||||
'published-at': new Date().toISOString(),
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
latest: {
|
||||
icon: BTC_ICON,
|
||||
@@ -215,13 +215,13 @@ export module Mock {
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
'release-notes':
|
||||
releaseNotes:
|
||||
'For a complete list of changes, please visit <a href="https://bitcoincore.org/en/releases/0.21.0/" target="_blank">https://bitcoincore.org/en/releases/0.21.0/</a><br />Or in [markdown](https://bitcoincore.org/en/releases/0.21.0/)<ul><li>Taproot!</li><li>New RPCs</li><li>Experimental Descriptor Wallets</li></ul>',
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
'dependency-metadata': {},
|
||||
'published-at': new Date().toISOString(),
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
lnd: {
|
||||
@@ -232,15 +232,15 @@ export module Mock {
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.0',
|
||||
'release-notes': 'release notes for LND 0.11.0',
|
||||
releaseNotes: 'release notes for LND 0.11.0',
|
||||
},
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
'dependency-metadata': {
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
},
|
||||
'published-at': new Date().toISOString(),
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.11.1': {
|
||||
icon: LND_ICON,
|
||||
@@ -249,15 +249,15 @@ export module Mock {
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.1',
|
||||
'release-notes': 'release notes for LND 0.11.1',
|
||||
releaseNotes: 'release notes for LND 0.11.1',
|
||||
},
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
'dependency-metadata': {
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
},
|
||||
'published-at': new Date().toISOString(),
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
latest: {
|
||||
icon: LND_ICON,
|
||||
@@ -266,11 +266,11 @@ export module Mock {
|
||||
manifest: Mock.MockManifestLnd,
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
'dependency-metadata': {
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
},
|
||||
'published-at': new Date(new Date().valueOf() + 10).toISOString(),
|
||||
publishedAt: new Date(new Date().valueOf() + 10).toISOString(),
|
||||
},
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
@@ -281,10 +281,10 @@ export module Mock {
|
||||
manifest: Mock.MockManifestBitcoinProxy,
|
||||
categories: ['bitcoin'],
|
||||
versions: ['0.2.2'],
|
||||
'dependency-metadata': {
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
},
|
||||
'published-at': new Date().toISOString(),
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -295,8 +295,8 @@ export module Mock {
|
||||
export const Notifications: ServerNotifications = [
|
||||
{
|
||||
id: 1,
|
||||
'package-id': null,
|
||||
'created-at': '2019-12-26T14:20:30.872Z',
|
||||
packageId: null,
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 1,
|
||||
level: NotificationLevel.Success,
|
||||
title: 'Backup Complete',
|
||||
@@ -315,8 +315,8 @@ export module Mock {
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
'package-id': null,
|
||||
'created-at': '2019-12-26T14:20:30.872Z',
|
||||
packageId: null,
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 2,
|
||||
level: NotificationLevel.Warning,
|
||||
title: 'SSH Key Added',
|
||||
@@ -325,8 +325,8 @@ export module Mock {
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
'package-id': null,
|
||||
'created-at': '2019-12-26T14:20:30.872Z',
|
||||
packageId: null,
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 3,
|
||||
level: NotificationLevel.Info,
|
||||
title: 'SSH Key Removed',
|
||||
@@ -335,8 +335,8 @@ export module Mock {
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
'package-id': 'bitcoind',
|
||||
'created-at': '2019-12-26T14:20:30.872Z',
|
||||
packageId: 'bitcoind',
|
||||
createdAt: '2019-12-26T14:20:30.872Z',
|
||||
code: 4,
|
||||
level: NotificationLevel.Error,
|
||||
title: 'Service Crashed',
|
||||
@@ -361,7 +361,7 @@ export module Mock {
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
'percentage-used': {
|
||||
percentageUsed: {
|
||||
value: '30.7',
|
||||
unit: '%',
|
||||
},
|
||||
@@ -377,29 +377,29 @@ export module Mock {
|
||||
value: '8784.97',
|
||||
unit: 'MiB',
|
||||
},
|
||||
'zram-total': {
|
||||
zramTotal: {
|
||||
value: '7992.00',
|
||||
unit: 'MiB',
|
||||
},
|
||||
'zram-available': {
|
||||
zramAvailable: {
|
||||
value: '7882.50',
|
||||
unit: 'MiB',
|
||||
},
|
||||
'zram-used': {
|
||||
zramUsed: {
|
||||
value: '109.50',
|
||||
unit: 'MiB',
|
||||
},
|
||||
},
|
||||
cpu: {
|
||||
'percentage-used': {
|
||||
percentageUsed: {
|
||||
value: '8.4',
|
||||
unit: '%',
|
||||
},
|
||||
'user-space': {
|
||||
userSpace: {
|
||||
value: '7.0',
|
||||
unit: '%',
|
||||
},
|
||||
'kernel-space': {
|
||||
kernelSpace: {
|
||||
value: '1.4',
|
||||
unit: '%',
|
||||
},
|
||||
@@ -425,7 +425,7 @@ export module Mock {
|
||||
value: '992.59',
|
||||
unit: 'GB',
|
||||
},
|
||||
'percentage-used': {
|
||||
percentageUsed: {
|
||||
value: '46.4',
|
||||
unit: '%',
|
||||
},
|
||||
@@ -487,15 +487,15 @@ export module Mock {
|
||||
current: 'b7b1a9cef4284f00af9e9dda6e676177',
|
||||
sessions: {
|
||||
'9513226517c54ddd8107d6d7b9d8aed7': {
|
||||
'last-active': '2021-07-14T20:49:17.774Z',
|
||||
'user-agent': 'AppleWebKit/{WebKit Rev} (KHTML, like Gecko)',
|
||||
lastActive: '2021-07-14T20:49:17.774Z',
|
||||
userAgent: 'AppleWebKit/{WebKit Rev} (KHTML, like Gecko)',
|
||||
metadata: {
|
||||
platforms: ['iphone', 'mobileweb', 'mobile', 'ios'],
|
||||
},
|
||||
},
|
||||
b7b1a9cef4284f00af9e9dda6e676177: {
|
||||
'last-active': '2021-06-14T20:49:17.774Z',
|
||||
'user-agent':
|
||||
lastActive: '2021-06-14T20:49:17.774Z',
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0',
|
||||
metadata: {
|
||||
platforms: ['desktop'],
|
||||
@@ -514,13 +514,13 @@ export module Mock {
|
||||
|
||||
export const SshKeys: RR.GetSSHKeysRes = [
|
||||
{
|
||||
'created-at': new Date().toISOString(),
|
||||
createdAt: new Date().toISOString(),
|
||||
alg: 'ed25519',
|
||||
hostname: 'Matt Key',
|
||||
fingerprint: '28:d2:7e:78:61:b4:bf:g2:de:24:15:96:4e:d4:15:53',
|
||||
},
|
||||
{
|
||||
'created-at': new Date().toISOString(),
|
||||
createdAt: new Date().toISOString(),
|
||||
alg: 'ed25519',
|
||||
hostname: 'Aiden Key',
|
||||
fingerprint: '12:f8:7e:78:61:b4:bf:e2:de:24:15:96:4e:d4:72:53',
|
||||
@@ -528,7 +528,7 @@ export module Mock {
|
||||
]
|
||||
|
||||
export const SshKey: RR.AddSSHKeyRes = {
|
||||
'created-at': new Date().toISOString(),
|
||||
createdAt: new Date().toISOString(),
|
||||
alg: 'ed25519',
|
||||
hostname: 'Lucy Key',
|
||||
fingerprint: '44:44:7e:78:61:b4:bf:g2:de:24:15:96:4e:d4:15:53',
|
||||
@@ -542,7 +542,7 @@ export module Mock {
|
||||
},
|
||||
connected: 'Goosers',
|
||||
country: 'US',
|
||||
'available-wifi': [
|
||||
availableWifi: [
|
||||
{
|
||||
ssid: 'Goosers a billion',
|
||||
strength: 40,
|
||||
@@ -568,13 +568,13 @@ export module Mock {
|
||||
path: '/Desktop/startos-backups',
|
||||
username: 'TestUser',
|
||||
mountable: false,
|
||||
'embassy-os': {
|
||||
startOs: {
|
||||
version: '0.3.0',
|
||||
full: true,
|
||||
'password-hash':
|
||||
passwordHash:
|
||||
// password is asdfasdf
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': '',
|
||||
wrappedKey: '',
|
||||
},
|
||||
},
|
||||
// 'ftcvewdnkemfksdm': {
|
||||
@@ -585,7 +585,7 @@ export module Mock {
|
||||
// used: 0,
|
||||
// model: 'Evo SATA 2.5',
|
||||
// vendor: 'Samsung',
|
||||
// 'embassy-os': null,
|
||||
// startOs: null,
|
||||
// },
|
||||
csgashbdjkasnd: {
|
||||
type: 'cifs',
|
||||
@@ -593,7 +593,7 @@ export module Mock {
|
||||
path: '/Desktop/startos-backups-2',
|
||||
username: 'TestUser',
|
||||
mountable: true,
|
||||
'embassy-os': null,
|
||||
startOs: null,
|
||||
},
|
||||
powjefhjbnwhdva: {
|
||||
type: 'disk',
|
||||
@@ -603,13 +603,13 @@ export module Mock {
|
||||
used: 100000000000,
|
||||
model: null,
|
||||
vendor: 'SSK',
|
||||
'embassy-os': {
|
||||
startOs: {
|
||||
version: '0.3.0',
|
||||
full: true,
|
||||
// password is asdfasdf
|
||||
'password-hash':
|
||||
passwordHash:
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'wrapped-key': '',
|
||||
wrappedKey: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -617,17 +617,17 @@ export module Mock {
|
||||
export const BackupInfo: RR.GetBackupInfoRes = {
|
||||
version: '0.3.0',
|
||||
timestamp: new Date().toISOString(),
|
||||
'package-backups': {
|
||||
packageBackups: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
version: '0.21.0',
|
||||
'os-version': '0.3.0',
|
||||
osVersion: '0.3.0',
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
version: '0.2.2',
|
||||
'os-version': '0.3.0',
|
||||
osVersion: '0.3.0',
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
@@ -1062,17 +1062,7 @@ export module Mock {
|
||||
warning: 'Careful changing this',
|
||||
},
|
||||
variants: {
|
||||
internal: {
|
||||
'lan-address': {
|
||||
name: 'LAN Address',
|
||||
type: 'pointer',
|
||||
subtype: 'package',
|
||||
target: 'lan-address',
|
||||
'package-id': 'bitcoind',
|
||||
description: 'the lan address',
|
||||
interface: 'asdf',
|
||||
},
|
||||
},
|
||||
internal: {},
|
||||
external: {
|
||||
'emergency-contact': {
|
||||
name: 'Emergency Contact',
|
||||
@@ -1385,12 +1375,12 @@ export module Mock {
|
||||
export const MockDependencyConfig = MockConfig
|
||||
|
||||
export const bitcoind: PackageDataEntry<InstalledState> = {
|
||||
'state-info': {
|
||||
stateInfo: {
|
||||
state: PackageState.Installed,
|
||||
manifest: MockManifestBitcoind,
|
||||
},
|
||||
icon: '/assets/img/service-icons/bitcoind.svg',
|
||||
'last-backup': null,
|
||||
lastBackup: null,
|
||||
status: {
|
||||
configured: true,
|
||||
main: {
|
||||
@@ -1398,10 +1388,10 @@ export module Mock {
|
||||
started: new Date().toISOString(),
|
||||
health: {},
|
||||
},
|
||||
'dependency-config-errors': {},
|
||||
dependencyConfigErrors: {},
|
||||
},
|
||||
actions: {}, // @TODO need mocks
|
||||
'service-interfaces': {
|
||||
serviceInterfaces: {
|
||||
ui: {
|
||||
id: 'ui',
|
||||
hasPrimary: false,
|
||||
@@ -1617,28 +1607,28 @@ export module Mock {
|
||||
},
|
||||
},
|
||||
},
|
||||
'current-dependencies': {},
|
||||
'dependency-info': {},
|
||||
'marketplace-url': 'https://registry.start9.com/',
|
||||
'developer-key': 'developer-key',
|
||||
currentDependencies: {},
|
||||
dependencyInfo: {},
|
||||
marketplaceUrl: 'https://registry.start9.com/',
|
||||
developerKey: 'developer-key',
|
||||
}
|
||||
|
||||
export const bitcoinProxy: PackageDataEntry<InstalledState> = {
|
||||
'state-info': {
|
||||
stateInfo: {
|
||||
state: PackageState.Installed,
|
||||
manifest: MockManifestBitcoinProxy,
|
||||
},
|
||||
icon: '/assets/img/service-icons/btc-rpc-proxy.png',
|
||||
'last-backup': null,
|
||||
lastBackup: null,
|
||||
status: {
|
||||
configured: false,
|
||||
main: {
|
||||
status: PackageMainStatus.Stopped,
|
||||
},
|
||||
'dependency-config-errors': {},
|
||||
dependencyConfigErrors: {},
|
||||
},
|
||||
actions: {},
|
||||
'service-interfaces': {
|
||||
serviceInterfaces: {
|
||||
ui: {
|
||||
id: 'ui',
|
||||
hasPrimary: false,
|
||||
@@ -1746,40 +1736,40 @@ export module Mock {
|
||||
},
|
||||
},
|
||||
},
|
||||
'current-dependencies': {
|
||||
currentDependencies: {
|
||||
bitcoind: {
|
||||
versionRange: '>=26.0.0',
|
||||
'health-checks': [],
|
||||
healthChecks: [],
|
||||
},
|
||||
},
|
||||
'dependency-info': {
|
||||
dependencyInfo: {
|
||||
bitcoind: {
|
||||
title: Mock.MockManifestBitcoind.title,
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
},
|
||||
},
|
||||
'marketplace-url': 'https://registry.start9.com/',
|
||||
'developer-key': 'developer-key',
|
||||
marketplaceUrl: 'https://registry.start9.com/',
|
||||
developerKey: 'developer-key',
|
||||
}
|
||||
|
||||
export const lnd: PackageDataEntry<InstalledState> = {
|
||||
'state-info': {
|
||||
stateInfo: {
|
||||
state: PackageState.Installed,
|
||||
manifest: MockManifestLnd,
|
||||
},
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
'last-backup': null,
|
||||
lastBackup: null,
|
||||
status: {
|
||||
configured: true,
|
||||
main: {
|
||||
status: PackageMainStatus.Stopped,
|
||||
},
|
||||
'dependency-config-errors': {
|
||||
dependencyConfigErrors: {
|
||||
'btc-rpc-proxy': 'Username not found',
|
||||
},
|
||||
},
|
||||
actions: {},
|
||||
'service-interfaces': {
|
||||
serviceInterfaces: {
|
||||
grpc: {
|
||||
id: 'grpc',
|
||||
hasPrimary: false,
|
||||
@@ -1991,17 +1981,17 @@ export module Mock {
|
||||
},
|
||||
},
|
||||
},
|
||||
'current-dependencies': {
|
||||
currentDependencies: {
|
||||
bitcoind: {
|
||||
versionRange: '>=26.0.0',
|
||||
'health-checks': [],
|
||||
healthChecks: [],
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
versionRange: '>2.0.0', // @TODO
|
||||
'health-checks': [],
|
||||
healthChecks: [],
|
||||
},
|
||||
},
|
||||
'dependency-info': {
|
||||
dependencyInfo: {
|
||||
bitcoind: {
|
||||
title: Mock.MockManifestBitcoind.title,
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
@@ -2011,8 +2001,8 @@ export module Mock {
|
||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
||||
},
|
||||
},
|
||||
'marketplace-url': 'https://registry.start9.com/',
|
||||
'developer-key': 'developer-key',
|
||||
marketplaceUrl: 'https://registry.start9.com/',
|
||||
developerKey: 'developer-key',
|
||||
}
|
||||
|
||||
export const LocalPkgs: { [key: string]: PackageDataEntry<InstalledState> } =
|
||||
|
||||
@@ -30,8 +30,8 @@ export module RR {
|
||||
export type LogoutRes = null
|
||||
|
||||
export type ResetPasswordReq = {
|
||||
'old-password': string
|
||||
'new-password': string
|
||||
oldPassword: string
|
||||
newPassword: string
|
||||
} // auth.reset-password
|
||||
export type ResetPasswordRes = null
|
||||
|
||||
@@ -51,14 +51,14 @@ export module RR {
|
||||
|
||||
export type FollowServerLogsReq = { limit?: number } // server.logs.follow & server.kernel-logs.follow
|
||||
export type FollowServerLogsRes = {
|
||||
'start-cursor': string
|
||||
startCursor: string
|
||||
guid: string
|
||||
}
|
||||
|
||||
export type GetServerMetricsReq = {} // server.metrics
|
||||
export type GetServerMetricsRes = Metrics
|
||||
|
||||
export type UpdateServerReq = { 'marketplace-url': string } // server.update
|
||||
export type UpdateServerReq = { marketplaceUrl: string } // server.update
|
||||
export type UpdateServerRes = 'updating' | 'no-updates'
|
||||
|
||||
export type RestartServerReq = {} // server.restart
|
||||
@@ -71,7 +71,7 @@ export module RR {
|
||||
export type SystemRebuildRes = null
|
||||
|
||||
export type ResetTorReq = {
|
||||
'wipe-state': boolean
|
||||
wipeState: boolean
|
||||
reason: string
|
||||
} // net.tor.reset
|
||||
export type ResetTorRes = null
|
||||
@@ -114,7 +114,7 @@ export module RR {
|
||||
connected: string | null
|
||||
country: string | null
|
||||
ethernet: boolean
|
||||
'available-wifi': AvailableWifi[]
|
||||
availableWifi: AvailableWifi[]
|
||||
}
|
||||
|
||||
export type AddWifiReq = {
|
||||
@@ -163,14 +163,14 @@ export module RR {
|
||||
export type RemoveBackupTargetReq = { id: string } // backup.target.cifs.remove
|
||||
export type RemoveBackupTargetRes = null
|
||||
|
||||
export type GetBackupInfoReq = { 'target-id': string; password: string } // backup.target.info
|
||||
export type GetBackupInfoReq = { targetId: string; password: string } // backup.target.info
|
||||
export type GetBackupInfoRes = BackupInfo
|
||||
|
||||
export type CreateBackupReq = {
|
||||
// backup.create
|
||||
'target-id': string
|
||||
'package-ids': string[]
|
||||
'old-password': string | null
|
||||
targetId: string
|
||||
packageIds: string[]
|
||||
oldPassword: string | null
|
||||
password: string
|
||||
}
|
||||
export type CreateBackupRes = null
|
||||
@@ -192,9 +192,9 @@ export module RR {
|
||||
|
||||
export type InstallPackageReq = {
|
||||
id: string
|
||||
'version-spec'?: string
|
||||
'version-priority'?: 'min' | 'max'
|
||||
'marketplace-url': string
|
||||
versionSpec?: string
|
||||
versionPriority?: 'min' | 'max'
|
||||
marketplaceUrl: string
|
||||
} // package.install
|
||||
export type InstallPackageRes = null
|
||||
|
||||
@@ -210,15 +210,15 @@ export module RR {
|
||||
export type RestorePackagesReq = {
|
||||
// package.backup.restore
|
||||
ids: string[]
|
||||
'target-id': string
|
||||
'old-password': string | null
|
||||
targetId: string
|
||||
oldPassword: string | null
|
||||
password: string
|
||||
}
|
||||
export type RestorePackagesRes = null
|
||||
|
||||
export type ExecutePackageActionReq = {
|
||||
id: string
|
||||
'action-id': string
|
||||
actionId: string
|
||||
input?: object
|
||||
} // package.action
|
||||
export type ExecutePackageActionRes = ActionResponse
|
||||
@@ -236,12 +236,12 @@ export module RR {
|
||||
export type UninstallPackageRes = null
|
||||
|
||||
export type DryConfigureDependencyReq = {
|
||||
'dependency-id': string
|
||||
'dependent-id': string
|
||||
dependencyId: string
|
||||
dependentId: string
|
||||
} // package.dependency.configure.dry
|
||||
export type DryConfigureDependencyRes = {
|
||||
'old-config': object
|
||||
'new-config': object
|
||||
oldConfig: object
|
||||
newConfig: object
|
||||
spec: ConfigSpec
|
||||
}
|
||||
|
||||
@@ -253,10 +253,10 @@ export module RR {
|
||||
|
||||
// marketplace
|
||||
|
||||
export type GetMarketplaceInfoReq = { 'server-id': string }
|
||||
export type GetMarketplaceInfoReq = { serverId: string }
|
||||
export type GetMarketplaceInfoRes = StoreInfo
|
||||
|
||||
export type GetMarketplaceEosReq = { 'server-id': string }
|
||||
export type GetMarketplaceEosReq = { serverId: string }
|
||||
export type GetMarketplaceEosRes = MarketplaceEOS
|
||||
|
||||
export type GetMarketplacePackagesReq = {
|
||||
@@ -265,7 +265,7 @@ export module RR {
|
||||
category?: string
|
||||
query?: string
|
||||
page?: number
|
||||
'per-page'?: number
|
||||
perPage?: number
|
||||
}
|
||||
export type GetMarketplacePackagesRes = MarketplacePkg[]
|
||||
|
||||
@@ -276,7 +276,7 @@ export module RR {
|
||||
export interface MarketplaceEOS {
|
||||
version: string
|
||||
headline: string
|
||||
'release-notes': { [version: string]: string }
|
||||
releaseNotes: { [version: string]: string }
|
||||
}
|
||||
|
||||
export interface Breakages {
|
||||
@@ -306,23 +306,23 @@ export interface Metrics {
|
||||
}
|
||||
memory: {
|
||||
total: MetricData
|
||||
'percentage-used': MetricData
|
||||
percentageUsed: MetricData
|
||||
used: MetricData
|
||||
available: MetricData
|
||||
'zram-total': MetricData
|
||||
'zram-used': MetricData
|
||||
'zram-available': MetricData
|
||||
zramTotal: MetricData
|
||||
zramUsed: MetricData
|
||||
zramAvailable: MetricData
|
||||
}
|
||||
cpu: {
|
||||
'percentage-used': MetricData
|
||||
percentageUsed: MetricData
|
||||
idle: MetricData
|
||||
'user-space': MetricData
|
||||
'kernel-space': MetricData
|
||||
userSpace: MetricData
|
||||
kernelSpace: MetricData
|
||||
wait: MetricData
|
||||
}
|
||||
disk: {
|
||||
capacity: MetricData
|
||||
'percentage-used': MetricData
|
||||
percentageUsed: MetricData
|
||||
used: MetricData
|
||||
available: MetricData
|
||||
}
|
||||
@@ -336,8 +336,8 @@ export interface Metric {
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
'last-active': string
|
||||
'user-agent': string
|
||||
lastActive: string
|
||||
userAgent: string
|
||||
metadata: SessionMetadata
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ export interface DiskBackupTarget {
|
||||
label: string | null
|
||||
capacity: number
|
||||
used: number | null
|
||||
'embassy-os': StartOSDiskInfo | null
|
||||
startOs: StartOSDiskInfo | null
|
||||
}
|
||||
|
||||
export interface CifsBackupTarget {
|
||||
@@ -381,7 +381,7 @@ export interface CifsBackupTarget {
|
||||
path: string
|
||||
username: string
|
||||
mountable: boolean
|
||||
'embassy-os': StartOSDiskInfo | null
|
||||
startOs: StartOSDiskInfo | null
|
||||
}
|
||||
|
||||
export type RecoverySource = DiskRecoverySource | CifsRecoverySource
|
||||
@@ -402,7 +402,7 @@ export interface CifsRecoverySource {
|
||||
export interface BackupInfo {
|
||||
version: string
|
||||
timestamp: string
|
||||
'package-backups': {
|
||||
packageBackups: {
|
||||
[id: string]: PackageBackupInfo
|
||||
}
|
||||
}
|
||||
@@ -410,7 +410,7 @@ export interface BackupInfo {
|
||||
export interface PackageBackupInfo {
|
||||
title: string
|
||||
version: string
|
||||
'os-version': string
|
||||
osVersion: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ export interface ServerSpecs {
|
||||
}
|
||||
|
||||
export interface SSHKey {
|
||||
'created-at': string
|
||||
createdAt: string
|
||||
alg: string
|
||||
hostname: string
|
||||
fingerprint: string
|
||||
@@ -429,8 +429,8 @@ export type ServerNotifications = ServerNotification<any>[]
|
||||
|
||||
export interface ServerNotification<T extends number> {
|
||||
id: number
|
||||
'package-id': string | null
|
||||
'created-at': string
|
||||
packageId: string | null
|
||||
createdAt: string
|
||||
code: T
|
||||
level: NotificationLevel
|
||||
title: string
|
||||
|
||||
@@ -158,7 +158,7 @@ export class LiveApiService extends ApiService {
|
||||
|
||||
async updateServer(url?: string): Promise<RR.UpdateServerRes> {
|
||||
const params = {
|
||||
'marketplace-url': url || this.config.marketplace.start9,
|
||||
marketplaceUrl: url || this.config.marketplace.start9,
|
||||
}
|
||||
return this.rpcRequest({ method: 'server.update', params })
|
||||
}
|
||||
@@ -205,7 +205,7 @@ export class LiveApiService extends ApiService {
|
||||
|
||||
async getEos(): Promise<RR.GetMarketplaceEosRes> {
|
||||
const { id } = await getServerInfo(this.patch)
|
||||
const qp: RR.GetMarketplaceEosReq = { 'server-id': id }
|
||||
const qp: RR.GetMarketplaceEosReq = { serverId: id }
|
||||
|
||||
return this.marketplaceProxy(
|
||||
'/eos/v0/latest',
|
||||
|
||||
@@ -216,8 +216,8 @@ export class MockApiService extends ApiService {
|
||||
|
||||
return {
|
||||
entries,
|
||||
'start-cursor': 'startCursor',
|
||||
'end-cursor': 'endCursor',
|
||||
startCursor: 'start-cursor',
|
||||
endCursor: 'end-cursor',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +229,8 @@ export class MockApiService extends ApiService {
|
||||
|
||||
return {
|
||||
entries,
|
||||
'start-cursor': 'startCursor',
|
||||
'end-cursor': 'endCursor',
|
||||
startCursor: 'start-cursor',
|
||||
endCursor: 'end-cursor',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,8 +240,8 @@ export class MockApiService extends ApiService {
|
||||
|
||||
return {
|
||||
entries,
|
||||
'start-cursor': 'startCursor',
|
||||
'end-cursor': 'endCursor',
|
||||
startCursor: 'startCursor',
|
||||
endCursor: 'end-cursor',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ export class MockApiService extends ApiService {
|
||||
): Promise<RR.FollowServerLogsRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'start-cursor': 'start-cursor',
|
||||
startCursor: 'start-cursor',
|
||||
guid: '7251d5be-645f-4362-a51b-3a85be92b31e',
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ export class MockApiService extends ApiService {
|
||||
): Promise<RR.FollowServerLogsRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'start-cursor': 'start-cursor',
|
||||
startCursor: 'start-cursor',
|
||||
guid: '7251d5be-645f-4362-a51b-3a85be92b31e',
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ export class MockApiService extends ApiService {
|
||||
): Promise<RR.FollowServerLogsRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'start-cursor': 'start-cursor',
|
||||
startCursor: 'start-cursor',
|
||||
guid: '7251d5be-645f-4362-a51b-3a85be92b31e',
|
||||
}
|
||||
}
|
||||
@@ -312,7 +312,7 @@ export class MockApiService extends ApiService {
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status-info/update-progress',
|
||||
path: '/serverInfo/statusInfo/updateProgress',
|
||||
value: initialProgress,
|
||||
},
|
||||
]
|
||||
@@ -329,7 +329,7 @@ export class MockApiService extends ApiService {
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status-info/restarting',
|
||||
path: '/serverInfo/statusInfo/restarting',
|
||||
value: true,
|
||||
},
|
||||
]
|
||||
@@ -339,7 +339,7 @@ export class MockApiService extends ApiService {
|
||||
const patch2 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status-info/restarting',
|
||||
path: '/serverInfo/statusInfo/restarting',
|
||||
value: false,
|
||||
},
|
||||
]
|
||||
@@ -357,7 +357,7 @@ export class MockApiService extends ApiService {
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status-info/shutting-down',
|
||||
path: '/serverInfo/statusInfo/shuttingDown',
|
||||
value: true,
|
||||
},
|
||||
]
|
||||
@@ -367,7 +367,7 @@ export class MockApiService extends ApiService {
|
||||
const patch2 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status-info/shutting-down',
|
||||
path: '/serverInfo/statusInfo/shuttingDown',
|
||||
value: false,
|
||||
},
|
||||
]
|
||||
@@ -439,7 +439,7 @@ export class MockApiService extends ApiService {
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/unread-notification-count',
|
||||
path: '/serverInfo/unreadNotificationCount',
|
||||
value: 0,
|
||||
},
|
||||
]
|
||||
@@ -529,7 +529,7 @@ export class MockApiService extends ApiService {
|
||||
path: path.replace(/\\/g, '/'),
|
||||
username,
|
||||
mountable: true,
|
||||
'embassy-os': null,
|
||||
startOs: null,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -565,13 +565,13 @@ export class MockApiService extends ApiService {
|
||||
|
||||
async createBackup(params: RR.CreateBackupReq): Promise<RR.CreateBackupRes> {
|
||||
await pauseFor(2000)
|
||||
const path = '/server-info/status-info/backup-progress'
|
||||
const ids = params['package-ids']
|
||||
const path = '/serverInfo/statusInfo/backupProgress'
|
||||
const ids = params.packageIds
|
||||
|
||||
setTimeout(async () => {
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i]
|
||||
const appPath = `/package-data/${id}/status/main/status`
|
||||
const appPath = `/packageData/${id}/status/main/status`
|
||||
const appPatch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
@@ -655,8 +655,8 @@ export class MockApiService extends ApiService {
|
||||
}
|
||||
return {
|
||||
entries,
|
||||
'start-cursor': 'startCursor',
|
||||
'end-cursor': 'endCursor',
|
||||
startCursor: 'startCursor',
|
||||
endCursor: 'end-cursor',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,7 +665,7 @@ export class MockApiService extends ApiService {
|
||||
): Promise<RR.FollowPackageLogsRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'start-cursor': 'start-cursor',
|
||||
startCursor: 'start-cursor',
|
||||
guid: '7251d5be-645f-4362-a51b-3a85be92b31e',
|
||||
}
|
||||
}
|
||||
@@ -679,17 +679,17 @@ export class MockApiService extends ApiService {
|
||||
this.updateProgress(params.id)
|
||||
}, 1000)
|
||||
|
||||
const manifest = Mock.LocalPkgs[params.id]['state-info'].manifest
|
||||
const manifest = Mock.LocalPkgs[params.id].stateInfo.manifest
|
||||
|
||||
const patch: Operation<
|
||||
PackageDataEntry<InstallingState | UpdatingState>
|
||||
>[] = [
|
||||
{
|
||||
op: PatchOp.ADD,
|
||||
path: `/package-data/${params.id}`,
|
||||
path: `/packageData/${params.id}`,
|
||||
value: {
|
||||
...Mock.LocalPkgs[params.id],
|
||||
'state-info': {
|
||||
stateInfo: {
|
||||
// if installing
|
||||
state: PackageState.Installing,
|
||||
|
||||
@@ -698,8 +698,8 @@ export class MockApiService extends ApiService {
|
||||
// manifest,
|
||||
|
||||
// both
|
||||
'installing-info': {
|
||||
'new-manifest': manifest,
|
||||
installingInfo: {
|
||||
newManifest: manifest,
|
||||
progress: PROGRESS,
|
||||
},
|
||||
},
|
||||
@@ -735,7 +735,7 @@ export class MockApiService extends ApiService {
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${params.id}/status/configured`,
|
||||
path: `/packageData/${params.id}/status/configured`,
|
||||
value: true,
|
||||
},
|
||||
]
|
||||
@@ -755,13 +755,13 @@ export class MockApiService extends ApiService {
|
||||
|
||||
return {
|
||||
op: PatchOp.ADD,
|
||||
path: `/package-data/${id}`,
|
||||
path: `/packageData/${id}`,
|
||||
value: {
|
||||
...Mock.LocalPkgs[id],
|
||||
'state-info': {
|
||||
stateInfo: {
|
||||
state: PackageState.Restoring,
|
||||
'installing-info': {
|
||||
'new-manifest': Mock.LocalPkgs[id]['state-info'].manifest!,
|
||||
installingInfo: {
|
||||
newManifest: Mock.LocalPkgs[id].stateInfo.manifest!,
|
||||
progress: PROGRESS,
|
||||
},
|
||||
},
|
||||
@@ -782,7 +782,7 @@ export class MockApiService extends ApiService {
|
||||
}
|
||||
|
||||
async startPackage(params: RR.StartPackageReq): Promise<RR.StartPackageRes> {
|
||||
const path = `/package-data/${params.id}/status/main`
|
||||
const path = `/packageData/${params.id}/status/main`
|
||||
|
||||
await pauseFor(2000)
|
||||
|
||||
@@ -865,7 +865,7 @@ export class MockApiService extends ApiService {
|
||||
): Promise<RR.RestartPackageRes> {
|
||||
// first enact stop
|
||||
await pauseFor(2000)
|
||||
const path = `/package-data/${params.id}/status/main`
|
||||
const path = `/packageData/${params.id}/status/main`
|
||||
|
||||
setTimeout(async () => {
|
||||
const patch2: Operation<any>[] = [
|
||||
@@ -941,7 +941,7 @@ export class MockApiService extends ApiService {
|
||||
|
||||
async stopPackage(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const path = `/package-data/${params.id}/status/main`
|
||||
const path = `/packageData/${params.id}/status/main`
|
||||
|
||||
setTimeout(() => {
|
||||
const patch2 = [
|
||||
@@ -981,7 +981,7 @@ export class MockApiService extends ApiService {
|
||||
const patch2: RemoveOperation[] = [
|
||||
{
|
||||
op: PatchOp.REMOVE,
|
||||
path: `/package-data/${params.id}`,
|
||||
path: `/packageData/${params.id}`,
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch2)
|
||||
@@ -990,7 +990,7 @@ export class MockApiService extends ApiService {
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${params.id}/state-info/state`,
|
||||
path: `/packageData/${params.id}/stateInfo/state`,
|
||||
value: PackageState.Removing,
|
||||
},
|
||||
]
|
||||
@@ -1005,8 +1005,8 @@ export class MockApiService extends ApiService {
|
||||
): Promise<RR.DryConfigureDependencyRes> {
|
||||
await pauseFor(2000)
|
||||
return {
|
||||
'old-config': Mock.MockConfig,
|
||||
'new-config': Mock.MockDependencyConfig,
|
||||
oldConfig: Mock.MockConfig,
|
||||
newConfig: Mock.MockDependencyConfig,
|
||||
spec: Mock.ConfigSpec,
|
||||
}
|
||||
}
|
||||
@@ -1028,7 +1028,7 @@ export class MockApiService extends ApiService {
|
||||
const patches: Operation<any>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${id}/state-info/installing-info/progress/phases/${i}/progress`,
|
||||
path: `/packageData/${id}/stateInfo/installingInfo/progress/phases/${i}/progress`,
|
||||
value: true,
|
||||
},
|
||||
]
|
||||
@@ -1041,7 +1041,7 @@ export class MockApiService extends ApiService {
|
||||
|
||||
patches.push({
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${id}/state-info/installing-info/progress/overall/done`,
|
||||
path: `/packageData/${id}/stateInfo/installingInfo/progress/overall/done`,
|
||||
value: progress.overall.done,
|
||||
})
|
||||
}
|
||||
@@ -1058,7 +1058,7 @@ export class MockApiService extends ApiService {
|
||||
const patches: Operation<any>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${id}/state-info/installing-info/progress/phases/${i}/progress/done`,
|
||||
path: `/packageData/${id}/stateInfo/installingInfo/progress/phases/${i}/progress/done`,
|
||||
value: phase.progress.done,
|
||||
},
|
||||
]
|
||||
@@ -1071,7 +1071,7 @@ export class MockApiService extends ApiService {
|
||||
|
||||
patches.push({
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${id}/state-info/installing-info/progress/overall/done`,
|
||||
path: `/packageData/${id}/stateInfo/installingInfo/progress/overall/done`,
|
||||
value: progress.overall.done,
|
||||
})
|
||||
}
|
||||
@@ -1083,7 +1083,7 @@ export class MockApiService extends ApiService {
|
||||
this.mockRevision([
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${id}/state-info/installing-info/progress/phases/${i}/progress`,
|
||||
path: `/packageData/${id}/stateInfo/installingInfo/progress/phases/${i}/progress`,
|
||||
value: true,
|
||||
},
|
||||
])
|
||||
@@ -1096,7 +1096,7 @@ export class MockApiService extends ApiService {
|
||||
this.mockRevision([
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${id}/state-info/installing-info/progress/overall`,
|
||||
path: `/packageData/${id}/stateInfo/installingInfo/progress/overall`,
|
||||
value: true,
|
||||
},
|
||||
])
|
||||
@@ -1105,10 +1105,10 @@ export class MockApiService extends ApiService {
|
||||
const patch2: Operation<StateInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/package-data/${id}/state-info`,
|
||||
path: `/packageData/${id}/stateInfo`,
|
||||
value: {
|
||||
state: PackageState.Installed,
|
||||
manifest: Mock.LocalPkgs[id]['state-info'].manifest,
|
||||
manifest: Mock.LocalPkgs[id].stateInfo.manifest,
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1122,7 +1122,7 @@ export class MockApiService extends ApiService {
|
||||
const patch0 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/server-info/status-info/update-progress/size`,
|
||||
path: `/serverInfo/statusInfo/updateProgress/size`,
|
||||
value: size,
|
||||
},
|
||||
]
|
||||
@@ -1134,7 +1134,7 @@ export class MockApiService extends ApiService {
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/server-info/status-info/update-progress/downloaded`,
|
||||
path: `/serverInfo/statusInfo/updateProgress/downloaded`,
|
||||
value: downloaded,
|
||||
},
|
||||
]
|
||||
@@ -1144,7 +1144,7 @@ export class MockApiService extends ApiService {
|
||||
const patch2 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: `/server-info/status-info/update-progress/downloaded`,
|
||||
path: `/serverInfo/statusInfo/updateProgress/downloaded`,
|
||||
value: size,
|
||||
},
|
||||
]
|
||||
@@ -1154,12 +1154,12 @@ export class MockApiService extends ApiService {
|
||||
const patch3: Operation<ServerStatus>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status',
|
||||
path: '/serverInfo/status',
|
||||
value: ServerStatus.Updated,
|
||||
},
|
||||
{
|
||||
op: PatchOp.REMOVE,
|
||||
path: '/server-info/status-info/update-progress',
|
||||
path: '/serverInfo/statusInfo/updateProgress',
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch3)
|
||||
@@ -1168,7 +1168,7 @@ export class MockApiService extends ApiService {
|
||||
const patch4 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status',
|
||||
path: '/serverInfo/status',
|
||||
value: ServerStatus.Running,
|
||||
},
|
||||
]
|
||||
@@ -1178,7 +1178,7 @@ export class MockApiService extends ApiService {
|
||||
const patch6 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: '/server-info/status-info',
|
||||
path: '/serverInfo/statusInfo',
|
||||
value: Mock.ServerUpdated,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -10,7 +10,7 @@ import { BUILT_IN_WIDGETS } from '../../pages/widgets/built-in/widgets'
|
||||
export const mockPatchData: DataModel = {
|
||||
ui: {
|
||||
name: `Matt's Server`,
|
||||
'ack-welcome': '1.0.0',
|
||||
ackWelcome: '1.0.0',
|
||||
theme: 'Dark',
|
||||
widgets: BUILT_IN_WIDGETS.filter(
|
||||
({ id }) =>
|
||||
@@ -20,8 +20,8 @@ export const mockPatchData: DataModel = {
|
||||
id === 'metrics',
|
||||
),
|
||||
marketplace: {
|
||||
'selected-url': 'https://registry.start9.com/',
|
||||
'known-hosts': {
|
||||
selectedUrl: 'https://registry.start9.com/',
|
||||
knownHosts: {
|
||||
'https://registry.start9.com/': {
|
||||
name: 'Start9 Registry',
|
||||
},
|
||||
@@ -31,21 +31,20 @@ export const mockPatchData: DataModel = {
|
||||
},
|
||||
},
|
||||
},
|
||||
dev: {},
|
||||
gaming: {
|
||||
snake: {
|
||||
'high-score': 0,
|
||||
highScore: 0,
|
||||
},
|
||||
},
|
||||
'ack-instructions': {},
|
||||
ackInstructions: {},
|
||||
},
|
||||
'server-info': {
|
||||
serverInfo: {
|
||||
id: 'abcdefgh',
|
||||
version: '0.3.5.1',
|
||||
'last-backup': new Date(new Date().valueOf() - 604800001).toISOString(),
|
||||
'lan-address': 'https://adjective-noun.local',
|
||||
'tor-address': 'https://myveryownspecialtoraddress.onion',
|
||||
'ip-info': {
|
||||
lastBackup: new Date(new Date().valueOf() - 604800001).toISOString(),
|
||||
lanAddress: 'https://adjective-noun.local',
|
||||
torAddress: 'https://myveryownspecialtoraddress.onion',
|
||||
ipInfo: {
|
||||
eth0: {
|
||||
ipv4: '10.0.0.1',
|
||||
ipv6: null,
|
||||
@@ -55,28 +54,28 @@ export const mockPatchData: DataModel = {
|
||||
ipv6: 'FE80:CD00:0000:0CDE:1257:0000:211E:729CD',
|
||||
},
|
||||
},
|
||||
'last-wifi-region': null,
|
||||
'unread-notification-count': 4,
|
||||
lastWifiRegion: null,
|
||||
unreadNotificationCount: 4,
|
||||
// password is asdfasdf
|
||||
'password-hash':
|
||||
passwordHash:
|
||||
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||
'eos-version-compat': '>=0.3.0 <=0.3.0.1',
|
||||
'status-info': {
|
||||
'backup-progress': null,
|
||||
eosVersionCompat: '>=0.3.0 <=0.3.0.1',
|
||||
statusInfo: {
|
||||
backupProgress: null,
|
||||
updated: false,
|
||||
'update-progress': null,
|
||||
updateProgress: null,
|
||||
restarting: false,
|
||||
'shutting-down': false,
|
||||
shuttingDown: false,
|
||||
},
|
||||
hostname: 'random-words',
|
||||
pubkey: 'npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m',
|
||||
'ca-fingerprint': 'SHA-256: 63 2B 11 99 44 40 17 DF 37 FC C3 DF 0F 3D 15',
|
||||
'ntp-synced': false,
|
||||
caFingerprint: 'SHA-256: 63 2B 11 99 44 40 17 DF 37 FC C3 DF 0F 3D 15',
|
||||
ntpSynced: false,
|
||||
platform: 'x86_64-nonfree',
|
||||
},
|
||||
'package-data': {
|
||||
packageData: {
|
||||
bitcoind: {
|
||||
'state-info': {
|
||||
stateInfo: {
|
||||
state: PackageState.Installed,
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
@@ -84,7 +83,7 @@ export const mockPatchData: DataModel = {
|
||||
},
|
||||
},
|
||||
icon: '/assets/img/service-icons/bitcoind.svg',
|
||||
'last-backup': null,
|
||||
lastBackup: null,
|
||||
status: {
|
||||
configured: true,
|
||||
main: {
|
||||
@@ -116,10 +115,10 @@ export const mockPatchData: DataModel = {
|
||||
},
|
||||
},
|
||||
},
|
||||
'dependency-config-errors': {},
|
||||
dependencyConfigErrors: {},
|
||||
},
|
||||
actions: {}, // @TODO
|
||||
'service-interfaces': {
|
||||
serviceInterfaces: {
|
||||
ui: {
|
||||
id: 'ui',
|
||||
hasPrimary: false,
|
||||
@@ -335,13 +334,13 @@ export const mockPatchData: DataModel = {
|
||||
},
|
||||
},
|
||||
},
|
||||
'current-dependencies': {},
|
||||
'dependency-info': {},
|
||||
'marketplace-url': 'https://registry.start9.com/',
|
||||
'developer-key': 'developer-key',
|
||||
currentDependencies: {},
|
||||
dependencyInfo: {},
|
||||
marketplaceUrl: 'https://registry.start9.com/',
|
||||
developerKey: 'developer-key',
|
||||
},
|
||||
lnd: {
|
||||
'state-info': {
|
||||
stateInfo: {
|
||||
state: PackageState.Installed,
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
@@ -349,18 +348,18 @@ export const mockPatchData: DataModel = {
|
||||
},
|
||||
},
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
'last-backup': null,
|
||||
lastBackup: null,
|
||||
status: {
|
||||
configured: true,
|
||||
main: {
|
||||
status: PackageMainStatus.Stopped,
|
||||
},
|
||||
'dependency-config-errors': {
|
||||
dependencyConfigErrors: {
|
||||
'btc-rpc-proxy': 'This is a config unsatisfied error',
|
||||
},
|
||||
},
|
||||
actions: {},
|
||||
'service-interfaces': {
|
||||
serviceInterfaces: {
|
||||
grpc: {
|
||||
id: 'grpc',
|
||||
hasPrimary: false,
|
||||
@@ -570,17 +569,17 @@ export const mockPatchData: DataModel = {
|
||||
},
|
||||
},
|
||||
},
|
||||
'current-dependencies': {
|
||||
currentDependencies: {
|
||||
bitcoind: {
|
||||
versionRange: '>=26.0.0',
|
||||
'health-checks': [],
|
||||
healthChecks: [],
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
versionRange: '>2.0.0',
|
||||
'health-checks': [],
|
||||
healthChecks: [],
|
||||
},
|
||||
},
|
||||
'dependency-info': {
|
||||
dependencyInfo: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
@@ -590,8 +589,8 @@ export const mockPatchData: DataModel = {
|
||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
||||
},
|
||||
},
|
||||
'marketplace-url': 'https://registry.start9.com/',
|
||||
'developer-key': 'developer-key',
|
||||
marketplaceUrl: 'https://registry.start9.com/',
|
||||
developerKey: 'developer-key',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -63,9 +63,7 @@ export class ConfigService {
|
||||
}
|
||||
|
||||
/** ${scheme}://${username}@${host}:${externalPort}${suffix} */
|
||||
launchableAddress(
|
||||
interfaces: PackageDataEntry['service-interfaces'],
|
||||
): string {
|
||||
launchableAddress(interfaces: PackageDataEntry['serviceInterfaces']): string {
|
||||
const ui = Object.values(interfaces).find(i => i.type === 'ui')
|
||||
|
||||
if (!ui) return ''
|
||||
@@ -128,7 +126,7 @@ export class ConfigService {
|
||||
}
|
||||
|
||||
export function hasUi(
|
||||
interfaces: PackageDataEntry['service-interfaces'],
|
||||
interfaces: PackageDataEntry['serviceInterfaces'],
|
||||
): boolean {
|
||||
return Object.values(interfaces).some(iface => iface.type === 'ui')
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export type PkgDependencyErrors = Record<string, DependencyError | null>
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DepErrorService {
|
||||
readonly depErrors$ = this.patch.watch$('package-data').pipe(
|
||||
readonly depErrors$ = this.patch.watch$('packageData').pipe(
|
||||
map(pkgs =>
|
||||
Object.keys(pkgs)
|
||||
.map(id => ({
|
||||
@@ -53,7 +53,7 @@ export class DepErrorService {
|
||||
}
|
||||
|
||||
private getDepErrors(
|
||||
pkgs: DataModel['package-data'],
|
||||
pkgs: DataModel['packageData'],
|
||||
pkgId: string,
|
||||
outerErrors: AllDependencyErrors,
|
||||
): PkgDependencyErrors {
|
||||
@@ -71,7 +71,7 @@ export class DepErrorService {
|
||||
}
|
||||
|
||||
private getDepError(
|
||||
pkgs: DataModel['package-data'],
|
||||
pkgs: DataModel['packageData'],
|
||||
pkg: PackageDataEntry<InstalledState>,
|
||||
depId: string,
|
||||
outerErrors: AllDependencyErrors,
|
||||
@@ -79,14 +79,14 @@ export class DepErrorService {
|
||||
const dep = pkgs[depId]
|
||||
|
||||
// not installed
|
||||
if (!dep || dep['state-info'].state !== PackageState.Installed) {
|
||||
if (!dep || dep.stateInfo.state !== PackageState.Installed) {
|
||||
return {
|
||||
type: DependencyErrorType.NotInstalled,
|
||||
}
|
||||
}
|
||||
|
||||
const versionRange = pkg['current-dependencies'][depId].versionRange
|
||||
const depManifest = dep['state-info'].manifest
|
||||
const versionRange = pkg.currentDependencies[depId].versionRange
|
||||
const depManifest = dep.stateInfo.manifest
|
||||
|
||||
// incorrect version
|
||||
if (!this.emver.satisfies(depManifest.version, versionRange)) {
|
||||
@@ -98,9 +98,7 @@ export class DepErrorService {
|
||||
}
|
||||
|
||||
// invalid config
|
||||
if (
|
||||
Object.values(pkg.status['dependency-config-errors']).some(err => !!err)
|
||||
) {
|
||||
if (Object.values(pkg.status.dependencyConfigErrors).some(err => !!err)) {
|
||||
return {
|
||||
type: DependencyErrorType.ConfigUnsatisfied,
|
||||
}
|
||||
@@ -120,7 +118,7 @@ export class DepErrorService {
|
||||
|
||||
// health check failure
|
||||
if (depStatus === PackageMainStatus.Running) {
|
||||
for (let id of pkg['current-dependencies'][depId]['health-checks']) {
|
||||
for (let id of pkg.currentDependencies[depId].healthChecks) {
|
||||
if (dep.status.main.health[id]?.result !== HealthResult.Success) {
|
||||
return {
|
||||
type: DependencyErrorType.HealthChecksFailed,
|
||||
@@ -144,14 +142,14 @@ export class DepErrorService {
|
||||
}
|
||||
}
|
||||
|
||||
function currentDeps(pkgs: DataModel['package-data'], id: string): string[] {
|
||||
return Object.keys(pkgs[id]?.['current-dependencies'] || {}).filter(
|
||||
function currentDeps(pkgs: DataModel['packageData'], id: string): string[] {
|
||||
return Object.keys(pkgs[id]?.currentDependencies || {}).filter(
|
||||
depId => depId !== id,
|
||||
)
|
||||
}
|
||||
|
||||
function dependencyDepth(
|
||||
pkgs: DataModel['package-data'],
|
||||
pkgs: DataModel['packageData'],
|
||||
id: string,
|
||||
depth = 0,
|
||||
): number {
|
||||
|
||||
@@ -15,13 +15,13 @@ export class EOSService {
|
||||
eos?: MarketplaceEOS
|
||||
updateAvailable$ = new BehaviorSubject<boolean>(false)
|
||||
|
||||
readonly updating$ = this.patch.watch$('server-info', 'status-info').pipe(
|
||||
map(status => !!status['update-progress'] || status.updated),
|
||||
readonly updating$ = this.patch.watch$('serverInfo', 'statusInfo').pipe(
|
||||
map(status => !!status.updateProgress || status.updated),
|
||||
distinctUntilChanged(),
|
||||
)
|
||||
|
||||
readonly backingUp$ = this.patch
|
||||
.watch$('server-info', 'status-info', 'backup-progress')
|
||||
.watch$('serverInfo', 'statusInfo', 'backupProgress')
|
||||
.pipe(
|
||||
map(obj => !!obj),
|
||||
distinctUntilChanged(),
|
||||
|
||||
@@ -89,7 +89,6 @@ export class FormService {
|
||||
UntypedFormGroup | UntypedFormArray | UntypedFormControl
|
||||
> = {}
|
||||
Object.entries(config).map(([key, spec]) => {
|
||||
if (spec.type === 'pointer') return
|
||||
group[key] = this.getFormEntry(spec, current ? current[key] : undefined)
|
||||
})
|
||||
return this.formBuilder.group(group, { validators })
|
||||
|
||||
@@ -39,7 +39,7 @@ import { ClientStorageService } from './client-storage.service'
|
||||
@Injectable()
|
||||
export class MarketplaceService implements AbstractMarketplaceService {
|
||||
private readonly knownHosts$: Observable<StoreIdentity[]> = this.patch
|
||||
.watch$('ui', 'marketplace', 'known-hosts')
|
||||
.watch$('ui', 'marketplace', 'knownHosts')
|
||||
.pipe(
|
||||
map(hosts => {
|
||||
const { start9, community } = this.config.marketplace
|
||||
@@ -73,8 +73,8 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
private readonly selectedHost$: Observable<StoreIdentity> = this.patch
|
||||
.watch$('ui', 'marketplace')
|
||||
.pipe(
|
||||
distinctUntilKeyChanged('selected-url'),
|
||||
map(({ 'selected-url': url, 'known-hosts': hosts }) =>
|
||||
distinctUntilKeyChanged('selectedUrl'),
|
||||
map(({ selectedUrl: url, knownHosts: hosts }) =>
|
||||
toStoreIdentity(url, hosts[url]),
|
||||
),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
@@ -171,9 +171,9 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
): Observable<MarketplacePkg> {
|
||||
return this.patch.watch$('ui', 'marketplace').pipe(
|
||||
switchMap(uiMarketplace => {
|
||||
const url = optionalUrl || uiMarketplace['selected-url']
|
||||
const url = optionalUrl || uiMarketplace.selectedUrl
|
||||
|
||||
if (version !== '*' || !uiMarketplace['known-hosts'][url]) {
|
||||
if (version !== '*' || !uiMarketplace.knownHosts[url]) {
|
||||
return this.fetchPackage$(id, version, url)
|
||||
}
|
||||
|
||||
@@ -206,18 +206,18 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
): Promise<void> {
|
||||
const params: RR.InstallPackageReq = {
|
||||
id,
|
||||
'version-spec': `=${version}`,
|
||||
'marketplace-url': url,
|
||||
versionSpec: `=${version}`,
|
||||
marketplaceUrl: url,
|
||||
}
|
||||
|
||||
await this.api.installPackage(params)
|
||||
}
|
||||
|
||||
fetchInfo$(url: string): Observable<StoreInfo> {
|
||||
return this.patch.watch$('server-info').pipe(
|
||||
return this.patch.watch$('serverInfo').pipe(
|
||||
take(1),
|
||||
switchMap(serverInfo => {
|
||||
const qp: RR.GetMarketplaceInfoReq = { 'server-id': serverInfo.id }
|
||||
const qp: RR.GetMarketplaceInfoReq = { serverId: serverInfo.id }
|
||||
return this.api.marketplaceProxy<RR.GetMarketplaceInfoRes>(
|
||||
'/package/v0/info',
|
||||
qp,
|
||||
@@ -276,7 +276,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
const qp: RR.GetMarketplacePackagesReq = {
|
||||
...params,
|
||||
page: 1,
|
||||
'per-page': 100,
|
||||
perPage: 100,
|
||||
}
|
||||
if (qp.ids) qp.ids = JSON.stringify(qp.ids)
|
||||
|
||||
@@ -306,7 +306,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
): Promise<void> {
|
||||
if (oldName !== newName) {
|
||||
this.api.setDbValue<string>(
|
||||
['marketplace', 'known-hosts', url, 'name'],
|
||||
['marketplace', 'knownHosts', url, 'name'],
|
||||
newName,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class PatchDataService extends Observable<DataModel> {
|
||||
// check for updates to eOS and services
|
||||
this.checkForUpdates()
|
||||
// show eos welcome message
|
||||
this.showEosWelcome(ui['ack-welcome'])
|
||||
this.showEosWelcome(ui.ackWelcome)
|
||||
}),
|
||||
share(),
|
||||
)
|
||||
@@ -60,7 +60,7 @@ export class PatchDataService extends Observable<DataModel> {
|
||||
})
|
||||
modal.onWillDismiss().then(() => {
|
||||
this.embassyApi
|
||||
.setDbValue<string>(['ack-welcome'], this.config.version)
|
||||
.setDbValue<string>(['ackWelcome'], this.config.version)
|
||||
.catch()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
import { Url } from '@start9labs/shared'
|
||||
import { Manifest } from '@start9labs/marketplace'
|
||||
import { BasicInfo } from 'src/app/pages/developer-routes/developer-menu/form-info'
|
||||
import { types } from '@start9labs/start-sdk'
|
||||
import { InputSpec } from '@start9labs/start-sdk/cjs/sdk/lib/config/configTypes'
|
||||
import { ActionMetadata } from '@start9labs/start-sdk/cjs/sdk/lib/types'
|
||||
type ServiceInterfaceWithHostInfo = types.ServiceInterfaceWithHostInfo
|
||||
|
||||
export interface DataModel {
|
||||
'server-info': ServerInfo
|
||||
'package-data': { [id: string]: PackageDataEntry }
|
||||
serverInfo: ServerInfo
|
||||
packageData: { [id: string]: PackageDataEntry }
|
||||
ui: UIData
|
||||
}
|
||||
|
||||
export interface UIData {
|
||||
name: string | null
|
||||
'ack-welcome': string // eOS emver
|
||||
ackWelcome: string // eOS emver
|
||||
marketplace: UIMarketplaceData
|
||||
dev: DevData
|
||||
gaming: {
|
||||
snake: {
|
||||
'high-score': number
|
||||
highScore: number
|
||||
}
|
||||
}
|
||||
'ack-instructions': Record<string, boolean>
|
||||
ackInstructions: Record<string, boolean>
|
||||
theme: string
|
||||
widgets: readonly Widget[]
|
||||
}
|
||||
@@ -41,8 +38,8 @@ export interface Widget {
|
||||
}
|
||||
|
||||
export interface UIMarketplaceData {
|
||||
'selected-url': string
|
||||
'known-hosts': {
|
||||
selectedUrl: string
|
||||
knownHosts: {
|
||||
'https://registry.start9.com/': UIStore
|
||||
'https://community-registry.start9.com/': UIStore
|
||||
[url: string]: UIStore
|
||||
@@ -53,33 +50,22 @@ export interface UIStore {
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface DevData {
|
||||
[id: string]: DevProjectData
|
||||
}
|
||||
|
||||
export interface DevProjectData {
|
||||
name: string
|
||||
instructions: string
|
||||
config: string
|
||||
'basic-info'?: BasicInfo
|
||||
}
|
||||
|
||||
export interface ServerInfo {
|
||||
id: string
|
||||
version: string
|
||||
'last-backup': string | null
|
||||
'lan-address': Url
|
||||
'tor-address': Url
|
||||
'ip-info': IpInfo
|
||||
'last-wifi-region': string | null
|
||||
'unread-notification-count': number
|
||||
'status-info': ServerStatusInfo
|
||||
'eos-version-compat': string
|
||||
'password-hash': string
|
||||
lastBackup: string | null
|
||||
lanAddress: Url
|
||||
torAddress: Url
|
||||
ipInfo: IpInfo
|
||||
lastWifiRegion: string | null
|
||||
unreadNotificationCount: number
|
||||
statusInfo: ServerStatusInfo
|
||||
eosVersionCompat: string
|
||||
passwordHash: string
|
||||
hostname: string
|
||||
pubkey: string
|
||||
'ca-fingerprint': string
|
||||
'ntp-synced': boolean
|
||||
caFingerprint: string
|
||||
ntpSynced: boolean
|
||||
platform: string
|
||||
}
|
||||
|
||||
@@ -91,15 +77,15 @@ export interface IpInfo {
|
||||
}
|
||||
|
||||
export interface ServerStatusInfo {
|
||||
'backup-progress': null | {
|
||||
backupProgress: null | {
|
||||
[packageId: string]: {
|
||||
complete: boolean
|
||||
}
|
||||
}
|
||||
updated: boolean
|
||||
'update-progress': { size: number | null; downloaded: number } | null
|
||||
updateProgress: { size: number | null; downloaded: number } | null
|
||||
restarting: boolean
|
||||
'shutting-down': boolean
|
||||
shuttingDown: boolean
|
||||
}
|
||||
|
||||
export enum ServerStatus {
|
||||
@@ -109,21 +95,21 @@ export enum ServerStatus {
|
||||
}
|
||||
|
||||
export type PackageDataEntry<T extends StateInfo = StateInfo> = {
|
||||
'state-info': T
|
||||
stateInfo: T
|
||||
icon: Url
|
||||
status: Status
|
||||
actions: Record<string, ActionMetadata>
|
||||
'last-backup': string | null
|
||||
'current-dependencies': { [id: string]: CurrentDependencyInfo }
|
||||
'dependency-info': {
|
||||
lastBackup: string | null
|
||||
currentDependencies: { [id: string]: CurrentDependencyInfo }
|
||||
dependencyInfo: {
|
||||
[id: string]: {
|
||||
title: string
|
||||
icon: Url
|
||||
}
|
||||
}
|
||||
'service-interfaces': Record<string, ServiceInterfaceWithHostInfo>
|
||||
'marketplace-url': string | null
|
||||
'developer-key': string
|
||||
serviceInterfaces: Record<string, ServiceInterfaceWithHostInfo>
|
||||
marketplaceUrl: string | null
|
||||
developerKey: string
|
||||
}
|
||||
|
||||
export type StateInfo = InstalledState | InstallingState | UpdatingState
|
||||
@@ -135,12 +121,12 @@ export type InstalledState = {
|
||||
|
||||
export type InstallingState = {
|
||||
state: PackageState.Installing | PackageState.Restoring
|
||||
'installing-info': InstallingInfo
|
||||
installingInfo: InstallingInfo
|
||||
}
|
||||
|
||||
export type UpdatingState = {
|
||||
state: PackageState.Updating
|
||||
'installing-info': InstallingInfo
|
||||
installingInfo: InstallingInfo
|
||||
manifest: Manifest
|
||||
}
|
||||
|
||||
@@ -154,13 +140,13 @@ export enum PackageState {
|
||||
|
||||
export interface CurrentDependencyInfo {
|
||||
versionRange: string
|
||||
'health-checks': string[] // array of health check IDs
|
||||
healthChecks: string[] // array of health check IDs
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
configured: boolean
|
||||
main: MainStatus
|
||||
'dependency-config-errors': { [id: string]: string | null }
|
||||
dependencyConfigErrors: { [id: string]: string | null }
|
||||
}
|
||||
|
||||
export type MainStatus =
|
||||
@@ -250,7 +236,7 @@ export interface HealthCheckResultFailure {
|
||||
|
||||
export type InstallingInfo = {
|
||||
progress: FullProgress
|
||||
'new-manifest': Manifest
|
||||
newManifest: Manifest
|
||||
}
|
||||
|
||||
export type FullProgress = {
|
||||
|
||||
@@ -22,12 +22,12 @@ export function renderPkgStatus(
|
||||
let dependency: DependencyStatus | null = null
|
||||
let health: HealthStatus | null = null
|
||||
|
||||
if (pkg['state-info'].state === PackageState.Installed) {
|
||||
if (pkg.stateInfo.state === PackageState.Installed) {
|
||||
primary = getPrimaryStatus(pkg.status)
|
||||
dependency = getDependencyStatus(depErrors)
|
||||
health = getHealthStatus(pkg.status)
|
||||
} else {
|
||||
primary = pkg['state-info'].state as string as PrimaryStatus
|
||||
primary = pkg.stateInfo.state as string as PrimaryStatus
|
||||
}
|
||||
|
||||
return { primary, dependency, health }
|
||||
|
||||
@@ -32,7 +32,7 @@ export class TimeService {
|
||||
|
||||
readonly now$ = combineLatest([
|
||||
this.time$,
|
||||
this.patch.watch$('server-info', 'ntp-synced'),
|
||||
this.patch.watch$('serverInfo', 'ntpSynced'),
|
||||
]).pipe(
|
||||
map(([time, synced]) => ({
|
||||
value: time.now,
|
||||
|
||||
@@ -12,7 +12,7 @@ export class UiLauncherService {
|
||||
private readonly config: ConfigService,
|
||||
) {}
|
||||
|
||||
launch(interfaces: PackageDataEntry['service-interfaces']): void {
|
||||
launch(interfaces: PackageDataEntry['serviceInterfaces']): void {
|
||||
this.windowRef.open(
|
||||
this.config.launchableAddress(interfaces),
|
||||
'_blank',
|
||||
|
||||
@@ -4,16 +4,16 @@ import { getManifest } from './get-package-data'
|
||||
|
||||
export function dryUpdate(
|
||||
{ id, version }: { id: string; version: string },
|
||||
pkgs: DataModel['package-data'],
|
||||
pkgs: DataModel['packageData'],
|
||||
emver: Emver,
|
||||
): string[] {
|
||||
return Object.values(pkgs)
|
||||
.filter(
|
||||
pkg =>
|
||||
Object.keys(pkg['current-dependencies'] || {}).some(
|
||||
Object.keys(pkg.currentDependencies || {}).some(
|
||||
pkgId => pkgId === id,
|
||||
) &&
|
||||
!emver.satisfies(version, pkg['current-dependencies'][id].versionRange),
|
||||
!emver.satisfies(version, pkg.currentDependencies[id].versionRange),
|
||||
)
|
||||
.map(pkg => getManifest(pkg).title)
|
||||
}
|
||||
|
||||
@@ -14,49 +14,47 @@ export async function getPackage(
|
||||
patch: PatchDB<DataModel>,
|
||||
id: string,
|
||||
): Promise<PackageDataEntry | undefined> {
|
||||
return firstValueFrom(patch.watch$('package-data', id))
|
||||
return firstValueFrom(patch.watch$('packageData', id))
|
||||
}
|
||||
|
||||
export async function getAllPackages(
|
||||
patch: PatchDB<DataModel>,
|
||||
): Promise<DataModel['package-data']> {
|
||||
return firstValueFrom(patch.watch$('package-data'))
|
||||
): Promise<DataModel['packageData']> {
|
||||
return firstValueFrom(patch.watch$('packageData'))
|
||||
}
|
||||
|
||||
export function getManifest(pkg: PackageDataEntry): Manifest {
|
||||
if (isInstalled(pkg) || isRemoving(pkg)) return pkg['state-info'].manifest
|
||||
if (isInstalled(pkg) || isRemoving(pkg)) return pkg.stateInfo.manifest
|
||||
|
||||
return (pkg['state-info'] as InstallingState)['installing-info'][
|
||||
'new-manifest'
|
||||
]
|
||||
return (pkg.stateInfo as InstallingState).installingInfo.newManifest
|
||||
}
|
||||
|
||||
export function isInstalled(
|
||||
pkg: PackageDataEntry,
|
||||
): pkg is PackageDataEntry<InstalledState> {
|
||||
return pkg['state-info'].state === PackageState.Installed
|
||||
return pkg.stateInfo.state === PackageState.Installed
|
||||
}
|
||||
|
||||
export function isRemoving(
|
||||
pkg: PackageDataEntry,
|
||||
): pkg is PackageDataEntry<InstalledState> {
|
||||
return pkg['state-info'].state === PackageState.Removing
|
||||
return pkg.stateInfo.state === PackageState.Removing
|
||||
}
|
||||
|
||||
export function isInstalling(
|
||||
pkg: PackageDataEntry,
|
||||
): pkg is PackageDataEntry<InstallingState> {
|
||||
return pkg['state-info'].state === PackageState.Installing
|
||||
return pkg.stateInfo.state === PackageState.Installing
|
||||
}
|
||||
|
||||
export function isRestoring(
|
||||
pkg: PackageDataEntry,
|
||||
): pkg is PackageDataEntry<InstallingState> {
|
||||
return pkg['state-info'].state === PackageState.Restoring
|
||||
return pkg.stateInfo.state === PackageState.Restoring
|
||||
}
|
||||
|
||||
export function isUpdating(
|
||||
pkg: PackageDataEntry,
|
||||
): pkg is PackageDataEntry<UpdatingState> {
|
||||
return pkg['state-info'].state === PackageState.Updating
|
||||
return pkg.stateInfo.state === PackageState.Updating
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import { firstValueFrom } from 'rxjs'
|
||||
export async function getServerInfo(
|
||||
patch: PatchDB<DataModel>,
|
||||
): Promise<ServerInfo> {
|
||||
return firstValueFrom(patch.watch$('server-info'))
|
||||
return firstValueFrom(patch.watch$('serverInfo'))
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ export function hasCurrentDeps(
|
||||
id: string,
|
||||
pkgs: Record<string, PackageDataEntry>,
|
||||
): boolean {
|
||||
return !!Object.values(pkgs).some(pkg => !!pkg['current-dependencies'][id])
|
||||
return !!Object.values(pkgs).some(pkg => !!pkg.currentDependencies[id])
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ import matches, {
|
||||
arrayOf,
|
||||
} from 'ts-matches'
|
||||
|
||||
type ValidVersion = 1 | 2
|
||||
|
||||
type PropertiesV1 = typeof matchPropertiesV1._TYPE
|
||||
type PackagePropertiesV1 = PropertiesV1[]
|
||||
type PackagePropertiesV2 = {
|
||||
|
||||
Reference in New Issue
Block a user