mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Feature/fe new registry (#2647)
* bugfixes * update fe types * implement new registry types in marketplace and ui * fix marketplace types to have default params * add alt implementation toggle * merge cleanup * more cleanup and notes * fix build * cleanup sync with next/minor * add exver JS parser * parse ValidExVer to string * update types to interface * add VersionRange and comparative functions * Parse ExtendedVersion from string * add conjunction, disjunction, and inversion logic * consider flavor in satisfiedBy fn * consider prerelease for ordering * add compare fn for sorting * rename fns for consistency * refactoring * update compare fn to return null if flavors don't match * begin simplifying dependencies * under construction * wip * add dependency metadata to CurrentDependencyInfo * ditch inheritance for recursive VersionRange constructor. Recursive 'satisfiedBy' fn wip * preprocess manifest * misc fixes * use sdk version as osVersion in manifest * chore: Change the type to just validate and not generate all solutions. * add publishedAt * fix pegjs exports * integrate exver into sdk * misc fixes * complete satisfiedBy fn * refactor - use greaterThanOrEqual and lessThanOrEqual fns * fix tests * update dependency details * update types * remove interim types * rename alt implementation to flavor * cleanup os update * format exver.ts * add s9pk parsing endpoints * fix build * update to exver * exver and bug fixes * update static endpoints + cleanup * cleanup * update static proxy verification * make mocks more robust; fix dep icon fallback; cleanup * refactor alert versions and update fixtures * registry bugfixes * misc fixes * cleanup unused * convert patchdb ui seed to camelCase * update otherVersions type * change otherVersions: null to 'none' * refactor and complete feature * improve static endpoints * fix install params * mask systemd-networkd-wait-online * fix static file fetching * include non-matching versions in otherVersions * convert release notes to modal and clean up displayExver * alert for no other versions * Fix ack-instructions casing * fix indeterminate loader on service install --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me> Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Co-authored-by: J H <dragondef@gmail.com> Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
@@ -20,7 +20,7 @@ import { AbstractMarketplaceService } from '@start9labs/marketplace'
|
||||
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { SplitPaneTracker } from 'src/app/services/split-pane.service'
|
||||
import { Emver, THEME } from '@start9labs/shared'
|
||||
import { Exver, THEME } from '@start9labs/shared'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import { getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
@@ -99,10 +99,10 @@ export class MenuComponent {
|
||||
]).pipe(
|
||||
map(([marketplace, local]) =>
|
||||
Object.entries(marketplace).reduce((list, [_, store]) => {
|
||||
store?.packages.forEach(({ manifest: { id, version } }) => {
|
||||
store?.packages.forEach(({ id, version }) => {
|
||||
if (
|
||||
local[id] &&
|
||||
this.emver.compare(
|
||||
this.exver.compareExver(
|
||||
version,
|
||||
getManifest(local[id]).version || '',
|
||||
) === 1
|
||||
@@ -125,7 +125,7 @@ export class MenuComponent {
|
||||
@Inject(AbstractMarketplaceService)
|
||||
private readonly marketplaceService: MarketplaceService,
|
||||
private readonly splitPane: SplitPaneTracker,
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
private readonly connection$: ConnectionService,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
DiskBackupTarget,
|
||||
} from 'src/app/services/api/api.types'
|
||||
import { MappedBackupTarget } from 'src/app/types/mapped-backup-target'
|
||||
import { getErrorMessage, Emver } from '@start9labs/shared'
|
||||
import { Exver, getErrorMessage } from '@start9labs/shared'
|
||||
import { Version } from '@start9labs/start-sdk'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -20,7 +21,7 @@ export class BackupService {
|
||||
|
||||
constructor(
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
async getBackupTargets(): Promise<void> {
|
||||
@@ -57,14 +58,15 @@ export class BackupService {
|
||||
|
||||
hasAnyBackup(target: BackupTarget): boolean {
|
||||
return Object.values(target.startOs).some(
|
||||
s => this.emver.compare(s.version, '0.3.6') !== -1,
|
||||
s => this.exver.compareOsVersion(s.version, '0.3.6') !== 'less',
|
||||
)
|
||||
}
|
||||
|
||||
hasThisBackup(target: BackupTarget, id: string): boolean {
|
||||
return (
|
||||
target.startOs[id] &&
|
||||
this.emver.compare(target.startOs[id].version, '0.3.6') !== -1
|
||||
this.exver.compareOsVersion(target.startOs[id].version, '0.3.6') !==
|
||||
'less'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
[open]="!!open.get(item)"
|
||||
(openChange)="open.set(item, $event)"
|
||||
>
|
||||
{{ item.value | mustache: $any(spec.spec).displayAs }}
|
||||
{{ item.value | mustache : $any(spec.spec).displayAs }}
|
||||
<ng-container *ngTemplateOutlet="remove"></ng-container>
|
||||
</form-object>
|
||||
<ng-template #control>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
[disabled]="!!spec.disabled"
|
||||
[readOnly]="readOnly"
|
||||
[pseudoInvalid]="invalid"
|
||||
[min]="spec.min ? (spec.min | tuiMapper: getLimit)[0] : min"
|
||||
[max]="spec.max ? (spec.max | tuiMapper: getLimit)[0] : max"
|
||||
[min]="spec.min ? (spec.min | tuiMapper : getLimit)[0] : min"
|
||||
[max]="spec.max ? (spec.max | tuiMapper : getLimit)[0] : max"
|
||||
[(ngModel)]="value"
|
||||
(focusedChange)="onFocus($event)"
|
||||
>
|
||||
@@ -32,8 +32,8 @@
|
||||
[disabled]="!!spec.disabled"
|
||||
[readOnly]="readOnly"
|
||||
[pseudoInvalid]="invalid"
|
||||
[min]="spec.min ? (spec.min | tuiMapper: getLimit) : min"
|
||||
[max]="spec.max ? (spec.max | tuiMapper: getLimit) : max"
|
||||
[min]="spec.min ? (spec.min | tuiMapper : getLimit) : min"
|
||||
[max]="spec.max ? (spec.max | tuiMapper : getLimit) : max"
|
||||
[(ngModel)]="value"
|
||||
(focusedChange)="onFocus($event)"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { endWith, Observable } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { ConfigService } from '../../../services/config.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
@@ -9,13 +9,16 @@ 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$('serverInfo', 'version').pipe(
|
||||
map(version => !!this.emver.compare(this.config.version, version)),
|
||||
map(
|
||||
version =>
|
||||
this.exver.compareOsVersion(this.config.version, version) !== 'equal',
|
||||
),
|
||||
endWith(false),
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
private readonly config: ConfigService,
|
||||
) {
|
||||
super(subscriber => this.stream$.subscribe(subscriber))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
import { PackageBackupInfo } from 'src/app/services/api/api.types'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
@@ -19,7 +19,7 @@ export interface AppRecoverOption extends PackageBackupInfo {
|
||||
export class ToOptionsPipe implements PipeTransform {
|
||||
constructor(
|
||||
private readonly config: ConfigService,
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
transform(
|
||||
@@ -44,7 +44,9 @@ export class ToOptionsPipe implements PipeTransform {
|
||||
}
|
||||
|
||||
private compare(version: string): boolean {
|
||||
// checks to see if backup was made on a newer version of eOS
|
||||
return this.emver.compare(version, this.config.version) === 1
|
||||
// checks to see if backup was made on a newer version of startOS
|
||||
return (
|
||||
this.exver.compareOsVersion(version, this.config.version) === 'greater'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ export class AppActionsPage {
|
||||
try {
|
||||
await this.embassyApi.uninstallPackage({ id: this.pkgId })
|
||||
this.embassyApi
|
||||
.setDbValue<boolean>(['ack-instructions', this.pkgId], false)
|
||||
.setDbValue<boolean>(['ackInstructions', this.pkgId], false)
|
||||
.catch(e => console.error('Failed to mark instructions as unseen', e))
|
||||
this.navCtrl.navigateRoot('/services')
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
<h2 ticker>{{ manifest.title }}</h2>
|
||||
<p>{{ manifest.version | displayEmver }}</p>
|
||||
<p>{{ manifest.version }}</p>
|
||||
<status
|
||||
[rendering]="pkg.primaryRendering"
|
||||
[installingInfo]="$any(pkg.entry.stateInfo).installingInfo"
|
||||
@@ -27,7 +27,7 @@
|
||||
color="primary"
|
||||
(click)="launchUi($event, pkg.entry.serviceInterfaces)"
|
||||
[disabled]="
|
||||
!(pkg.entry.stateInfo.state | isLaunchable: pkgMainStatus.status)
|
||||
!(pkg.entry.stateInfo.state | isLaunchable : pkgMainStatus.status)
|
||||
"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppListPage } from './app-list.page'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
ResponsiveColModule,
|
||||
TextSpinnerComponentModule,
|
||||
TickerModule,
|
||||
@@ -29,7 +29,7 @@ const routes: Routes = [
|
||||
imports: [
|
||||
CommonModule,
|
||||
StatusComponentModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
LaunchablePipeModule,
|
||||
UiPipeModule,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppShowPage } from './app-show.page'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
ResponsiveColModule,
|
||||
SharedPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
@@ -49,7 +49,7 @@ const routes: Routes = [
|
||||
InstallingProgressPipeModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
LaunchablePipeModule,
|
||||
UiPipeModule,
|
||||
ResponsiveColModule,
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
<!-- ** installing, updating, restoring ** -->
|
||||
<ng-container *ngIf="showProgress(pkg); else installed">
|
||||
<app-show-progress
|
||||
*ngIf="pkg.stateInfo.installingInfo as installingInfo"
|
||||
[phases]="installingInfo.progress.phases"
|
||||
[phases]="pkg.stateInfo.installingInfo.progress.phases"
|
||||
></app-show-progress>
|
||||
</ng-container>
|
||||
|
||||
@@ -34,9 +33,7 @@
|
||||
<!-- ** menu ** -->
|
||||
<app-show-menu [buttons]="pkg | toButtons"></app-show-menu>
|
||||
<!-- ** additional ** -->
|
||||
<app-show-additional
|
||||
[manifest]="pkg.stateInfo.manifest"
|
||||
></app-show-additional>
|
||||
<app-show-additional [pkg]="pkg"></app-show-additional>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
</ng-template>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { NavController } from '@ionic/angular'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
AllPackageData,
|
||||
DataModel,
|
||||
InstallingState,
|
||||
PackageDataEntry,
|
||||
@@ -47,17 +48,19 @@ export class AppShowPage {
|
||||
private readonly pkgId = getPkgId(this.route)
|
||||
|
||||
readonly pkgPlus$ = combineLatest([
|
||||
this.patch.watch$('packageData', this.pkgId),
|
||||
this.patch.watch$('packageData'),
|
||||
this.depErrorService.getPkgDepErrors$(this.pkgId),
|
||||
]).pipe(
|
||||
tap(([pkg, _]) => {
|
||||
tap(([allPkgs, _]) => {
|
||||
const pkg = allPkgs[this.pkgId]
|
||||
// if package disappears, navigate to list page
|
||||
if (!pkg) this.navCtrl.navigateRoot('/services')
|
||||
}),
|
||||
map(([pkg, depErrors]) => {
|
||||
map(([allPkgs, depErrors]) => {
|
||||
const pkg = allPkgs[this.pkgId]
|
||||
return {
|
||||
pkg,
|
||||
dependencies: this.getDepInfo(pkg, depErrors),
|
||||
dependencies: this.getDepInfo(pkg, allPkgs, depErrors),
|
||||
status: renderPkgStatus(pkg, depErrors),
|
||||
}
|
||||
}),
|
||||
@@ -81,17 +84,45 @@ export class AppShowPage {
|
||||
|
||||
private getDepInfo(
|
||||
pkg: PackageDataEntry,
|
||||
allPkgs: AllPackageData,
|
||||
depErrors: PkgDependencyErrors,
|
||||
): DependencyInfo[] {
|
||||
const manifest = getManifest(pkg)
|
||||
|
||||
return Object.keys(pkg.currentDependencies)
|
||||
.filter(id => !!manifest.dependencies[id])
|
||||
.map(id => this.getDepValues(pkg, manifest, id, depErrors))
|
||||
.map(id => this.getDepValues(pkg, allPkgs, manifest, id, depErrors))
|
||||
}
|
||||
|
||||
private getDepDetails(
|
||||
pkg: PackageDataEntry,
|
||||
allPkgs: AllPackageData,
|
||||
depId: string,
|
||||
) {
|
||||
const { title, icon, versionRange } = pkg.currentDependencies[depId]
|
||||
|
||||
if (
|
||||
allPkgs[depId] &&
|
||||
(allPkgs[depId].stateInfo.state === 'installed' ||
|
||||
allPkgs[depId].stateInfo.state === 'updating')
|
||||
) {
|
||||
return {
|
||||
title: allPkgs[depId].stateInfo.manifest!.title,
|
||||
icon: allPkgs[depId].icon,
|
||||
versionRange,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
title: title ? title : depId,
|
||||
icon: icon ? icon : 'assets/img/service-icons/fallback.png',
|
||||
versionRange,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getDepValues(
|
||||
pkg: PackageDataEntry,
|
||||
allPkgs: AllPackageData,
|
||||
manifest: T.Manifest,
|
||||
depId: string,
|
||||
depErrors: PkgDependencyErrors,
|
||||
@@ -103,11 +134,15 @@ export class AppShowPage {
|
||||
depErrors,
|
||||
)
|
||||
|
||||
const { title, icon, versionSpec } = pkg.currentDependencies[depId]
|
||||
const { title, icon, versionRange } = this.getDepDetails(
|
||||
pkg,
|
||||
allPkgs,
|
||||
depId,
|
||||
)
|
||||
|
||||
return {
|
||||
id: depId,
|
||||
version: versionSpec,
|
||||
version: versionRange,
|
||||
title,
|
||||
icon,
|
||||
errorText: errorText
|
||||
@@ -190,7 +225,7 @@ export class AppShowPage {
|
||||
const dependentInfo: DependentInfo = {
|
||||
id: pkgManifest.id,
|
||||
title: pkgManifest.title,
|
||||
version: pkg.currentDependencies[depId].versionSpec,
|
||||
version: pkg.currentDependencies[depId].versionRange,
|
||||
}
|
||||
const navigationExtras: NavigationExtras = {
|
||||
state: { dependentInfo },
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Version</h2>
|
||||
<p>{{ manifest.version | displayEmver }}</p>
|
||||
<p>{{ pkg.stateInfo.manifest.version }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
*ngIf="manifest.gitHash as gitHash; else noHash"
|
||||
*ngIf="pkg.stateInfo.manifest.gitHash as gitHash; else noHash"
|
||||
button
|
||||
detail="false"
|
||||
(click)="copy(gitHash)"
|
||||
@@ -32,20 +32,20 @@
|
||||
<ion-item button detail="false" (click)="presentModalLicense()">
|
||||
<ion-label>
|
||||
<h2>License</h2>
|
||||
<p>{{ manifest.license }}</p>
|
||||
<p>{{ pkg.stateInfo.manifest.license }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest.marketingSite"
|
||||
[disabled]="!manifest.marketingSite"
|
||||
[href]="pkg.stateInfo.manifest.marketingSite"
|
||||
[disabled]="!pkg.stateInfo.manifest.marketingSite"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Marketing Site</h2>
|
||||
<p>{{ manifest.marketingSite || 'Not provided' }}</p>
|
||||
<p>{{ pkg.stateInfo.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.upstreamRepo"
|
||||
[href]="pkg.stateInfo.manifest.upstreamRepo"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ manifest.upstreamRepo }}</p>
|
||||
<p>{{ pkg.stateInfo.manifest.upstreamRepo }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest.wrapperRepo"
|
||||
[href]="pkg.stateInfo.manifest.wrapperRepo"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ manifest.wrapperRepo }}</p>
|
||||
<p>{{ pkg.stateInfo.manifest.wrapperRepo }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest.supportSite"
|
||||
[disabled]="!manifest.supportSite"
|
||||
[href]="pkg.stateInfo.manifest.supportSite"
|
||||
[disabled]="!pkg.stateInfo.manifest.supportSite"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ manifest.supportSite || 'Not provided' }}</p>
|
||||
<p>{{ pkg.stateInfo.manifest.supportSite || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest.donationUrl"
|
||||
[disabled]="!manifest.donationUrl"
|
||||
[href]="pkg.stateInfo.manifest.donationUrl"
|
||||
[disabled]="!pkg.stateInfo.manifest.donationUrl"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Donation Link</h2>
|
||||
<p>{{ manifest.donationUrl || 'Not provided' }}</p>
|
||||
<p>{{ pkg.stateInfo.manifest.donationUrl || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { ModalController, ToastController } from '@ionic/angular'
|
||||
import { copyToClipboard, MarkdownComponent } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { from } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import {
|
||||
InstalledState,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-additional',
|
||||
@@ -12,7 +15,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
})
|
||||
export class AppShowAdditionalComponent {
|
||||
@Input()
|
||||
manifest!: T.Manifest
|
||||
pkg!: PackageDataEntry<InstalledState>
|
||||
|
||||
constructor(
|
||||
private readonly modalCtrl: ModalController,
|
||||
@@ -35,16 +38,12 @@ export class AppShowAdditionalComponent {
|
||||
}
|
||||
|
||||
async presentModalLicense() {
|
||||
const { id, version } = this.manifest
|
||||
const { id } = this.pkg.stateInfo.manifest
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
title: 'License',
|
||||
content: from(
|
||||
this.api.getStatic(
|
||||
`/public/package-data/${id}/${version}/LICENSE.md`,
|
||||
),
|
||||
),
|
||||
content: from(this.api.getStaticInstalled(id, 'LICENSE.md')),
|
||||
},
|
||||
component: MarkdownComponent,
|
||||
})
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
></ion-icon>
|
||||
{{ dep.title }}
|
||||
</h2>
|
||||
<p>{{ dep.version | displayEmver }}</p>
|
||||
<p>{{ dep.version }}</p>
|
||||
<p>
|
||||
<ion-text [color]="dep.errorText ? 'warning' : 'success'">
|
||||
{{ dep.errorText || 'satisfied' }}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<h1 class="montserrat" [class.less-large]="manifest.title.length > 20">
|
||||
{{ manifest.title }}
|
||||
</h1>
|
||||
<h2>{{ manifest.version | displayEmver }}</h2>
|
||||
<h2>{{ manifest.version }}</h2>
|
||||
</ion-label>
|
||||
</div>
|
||||
</ion-toolbar>
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
</p>
|
||||
<ion-progress-bar
|
||||
[type]="
|
||||
phase.progress === true ||
|
||||
(phase.progress !== false && phase.progress!== null && phase.progress.total)
|
||||
? 'determinate'
|
||||
: 'indeterminate'
|
||||
phase.progress === false ||
|
||||
(phase.progress !== true &&
|
||||
phase.progress !== null &&
|
||||
!phase.progress.total)
|
||||
? 'indeterminate'
|
||||
: 'determinate'
|
||||
"
|
||||
[color]="phase.progress === true ? 'success' : 'secondary'"
|
||||
[value]="phase.progress | installingProgress"
|
||||
|
||||
@@ -9,5 +9,5 @@ import { T } from '@start9labs/start-sdk'
|
||||
})
|
||||
export class AppShowProgressComponent {
|
||||
@Input()
|
||||
phases!: T.FullProgress['phases']
|
||||
phases!: T.NamedProgress[]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { from, map, Observable } from 'rxjs'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||
import { ConfigModal, PackageConfigData } from 'src/app/modals/config.component'
|
||||
|
||||
@@ -32,6 +31,7 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly apiService: ApiService,
|
||||
private readonly api: ApiService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly formDialog: FormDialogService,
|
||||
) {}
|
||||
@@ -42,7 +42,7 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
return [
|
||||
// instructions
|
||||
{
|
||||
action: () => this.presentModalInstructions(manifest),
|
||||
action: () => this.presentModalInstructions(pkg),
|
||||
title: 'Instructions',
|
||||
description: `Understand how to use ${manifest.title}`,
|
||||
icon: 'list-outline',
|
||||
@@ -103,17 +103,20 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
]
|
||||
}
|
||||
|
||||
private async presentModalInstructions(manifest: T.Manifest) {
|
||||
private async presentModalInstructions(
|
||||
pkg: PackageDataEntry<InstalledState>,
|
||||
) {
|
||||
this.apiService
|
||||
.setDbValue<boolean>(['ack-instructions', manifest.id], true)
|
||||
.setDbValue<boolean>(['ackInstructions', pkg.stateInfo.manifest.id], true)
|
||||
.catch(e => console.error('Failed to mark instructions as seen', e))
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
title: 'Instructions',
|
||||
content: from(
|
||||
this.apiService.getStatic(
|
||||
`/public/package-data/${manifest.id}/${manifest.version}/INSTRUCTIONS.md`,
|
||||
this.api.getStaticInstalled(
|
||||
pkg.stateInfo.manifest.id,
|
||||
'instructions.md',
|
||||
),
|
||||
),
|
||||
},
|
||||
|
||||
@@ -97,7 +97,4 @@
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<a
|
||||
id="install-cert"
|
||||
href="/eos/local.crt"
|
||||
></a>
|
||||
<a id="install-cert" href="/static/local-root-ca.crt"></a>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
ResponsiveColModule,
|
||||
} from '@start9labs/shared'
|
||||
import {
|
||||
@@ -34,7 +34,7 @@ const routes: Routes = [
|
||||
FormsModule,
|
||||
RouterModule.forChild(routes),
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
FilterPackagesPipeModule,
|
||||
MarketplaceStatusModule,
|
||||
BadgeMenuComponentModule,
|
||||
|
||||
@@ -62,9 +62,10 @@
|
||||
>
|
||||
<marketplace-item [pkg]="pkg">
|
||||
<marketplace-status
|
||||
*ngIf="localPkgs[pkg.id] as localPkg"
|
||||
class="status"
|
||||
[version]="pkg.manifest.version"
|
||||
[localPkg]="localPkgs[pkg.manifest.id]"
|
||||
[version]="pkg.version"
|
||||
[localPkg]="localPkg"
|
||||
></marketplace-status>
|
||||
</marketplace-item>
|
||||
</ion-col>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { AbstractMarketplaceService } from '@start9labs/marketplace'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { map } from 'rxjs'
|
||||
@@ -20,12 +21,21 @@ export class MarketplaceListPage {
|
||||
|
||||
readonly store$ = this.marketplaceService.getSelectedStore$().pipe(
|
||||
map(({ info, packages }) => {
|
||||
const categories = new Set<string>()
|
||||
if (info.categories.includes('featured')) categories.add('featured')
|
||||
info.categories.forEach(c => categories.add(c))
|
||||
categories.add('all')
|
||||
const categories = new Map<string, T.Category>()
|
||||
if (info.categories['featured'])
|
||||
categories.set('featured', info.categories['featured'])
|
||||
Object.keys(info.categories).forEach(c =>
|
||||
categories.set(c, info.categories[c]),
|
||||
)
|
||||
categories.set('all', {
|
||||
name: 'All',
|
||||
description: {
|
||||
short: 'All registry packages',
|
||||
long: 'An unfiltered list of all packages available on this registry.',
|
||||
},
|
||||
})
|
||||
|
||||
return { categories: Array.from(categories), packages }
|
||||
return { categories, packages }
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -17,13 +17,6 @@ const routes: Routes = [
|
||||
m => m.MarketplaceShowPageModule,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ':pkgId/notes',
|
||||
loadChildren: () =>
|
||||
import('./release-notes/release-notes.module').then(
|
||||
m => m.ReleaseNotesPageModule,
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<div class="action-buttons">
|
||||
<ion-button
|
||||
*ngIf="localPkg"
|
||||
*ngIf="localPkg && localPkg.stateInfo.state !== 'removing'"
|
||||
expand="block"
|
||||
color="primary"
|
||||
[routerLink]="['/services', pkg.manifest.id]"
|
||||
[routerLink]="['/services', pkg.id]"
|
||||
>
|
||||
View Installed
|
||||
{{
|
||||
localPkg.stateInfo.state === 'installed'
|
||||
? 'View Installed'
|
||||
: 'View Installing'
|
||||
}}
|
||||
</ion-button>
|
||||
<ng-container *ngIf="localPkg; else install">
|
||||
<ng-container
|
||||
@@ -15,7 +19,7 @@
|
||||
"
|
||||
>
|
||||
<ion-button
|
||||
*ngIf="(manifest.version | compareEmver: pkg.manifest.version) === -1"
|
||||
*ngIf="(manifest.version | compareExver : pkg.version) === -1"
|
||||
expand="block"
|
||||
color="success"
|
||||
(click)="tryInstall()"
|
||||
@@ -23,7 +27,7 @@
|
||||
Update
|
||||
</ion-button>
|
||||
<ion-button
|
||||
*ngIf="(manifest.version | compareEmver: pkg.manifest.version) === 1"
|
||||
*ngIf="(manifest.version | compareExver : pkg.version) === 1"
|
||||
expand="block"
|
||||
color="warning"
|
||||
(click)="tryInstall()"
|
||||
@@ -32,7 +36,7 @@
|
||||
</ion-button>
|
||||
<ng-container *ngIf="showDevTools$ | async">
|
||||
<ion-button
|
||||
*ngIf="(manifest.version | compareEmver: pkg.manifest.version) === 0"
|
||||
*ngIf="(manifest.version | compareExver : pkg.version) === 0"
|
||||
expand="block"
|
||||
color="success"
|
||||
(click)="tryInstall()"
|
||||
@@ -44,8 +48,12 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-template #install>
|
||||
<ion-button expand="block" color="success" (click)="tryInstall()">
|
||||
Install
|
||||
<ion-button
|
||||
expand="block"
|
||||
[color]="localFlavor ? 'warning' : 'success'"
|
||||
(click)="tryInstall()"
|
||||
>
|
||||
{{ localFlavor ? 'Switch' : 'Install' }}
|
||||
</ion-button>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
import {
|
||||
Emver,
|
||||
Exver,
|
||||
ErrorService,
|
||||
isEmptyObject,
|
||||
LoadingService,
|
||||
@@ -44,6 +44,9 @@ export class MarketplaceShowControlsComponent {
|
||||
@Input()
|
||||
localPkg!: PackageDataEntry | null
|
||||
|
||||
@Input()
|
||||
localFlavor!: boolean
|
||||
|
||||
readonly showDevTools$ = this.ClientStorageService.showDevTools$
|
||||
|
||||
constructor(
|
||||
@@ -52,7 +55,7 @@ export class MarketplaceShowControlsComponent {
|
||||
@Inject(AbstractMarketplaceService)
|
||||
private readonly marketplaceService: MarketplaceService,
|
||||
private readonly loader: LoadingService,
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
private readonly errorService: ErrorService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
@@ -79,7 +82,7 @@ export class MarketplaceShowControlsComponent {
|
||||
const localManifest = getManifest(this.localPkg)
|
||||
|
||||
if (
|
||||
this.emver.compare(localManifest.version, this.pkg.manifest.version) !==
|
||||
this.exver.compareExver(localManifest.version, this.pkg.version) !==
|
||||
0 &&
|
||||
hasCurrentDeps(localManifest.id, await getAllPackages(this.patch))
|
||||
) {
|
||||
@@ -136,9 +139,9 @@ export class MarketplaceShowControlsComponent {
|
||||
|
||||
private async dryInstall(url: string) {
|
||||
const breakages = dryUpdate(
|
||||
this.pkg.manifest,
|
||||
this.pkg,
|
||||
await getAllPackages(this.patch),
|
||||
this.emver,
|
||||
this.exver,
|
||||
)
|
||||
|
||||
if (isEmptyObject(breakages)) {
|
||||
@@ -152,7 +155,7 @@ export class MarketplaceShowControlsComponent {
|
||||
}
|
||||
|
||||
private async alertInstall(url: string) {
|
||||
const installAlert = this.pkg.manifest.alerts.install
|
||||
const installAlert = this.pkg.alerts.install
|
||||
|
||||
if (!installAlert) return this.install(url)
|
||||
|
||||
@@ -179,7 +182,7 @@ export class MarketplaceShowControlsComponent {
|
||||
private async install(url: string) {
|
||||
const loader = this.loader.open('Beginning Install...').subscribe()
|
||||
|
||||
const { id, version } = this.pkg.manifest
|
||||
const { id, version } = this.pkg
|
||||
|
||||
try {
|
||||
await this.marketplaceService.installPackage(id, version, url)
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
<br />
|
||||
<br />
|
||||
<span
|
||||
*ngIf="version | satisfiesEmver: dependentInfo.version"
|
||||
*ngIf="version | satisfiesExver : dependentInfo.version"
|
||||
class="text"
|
||||
>
|
||||
{{ title }} version {{ version | displayEmver }} is compatible.
|
||||
{{ title }} version {{ version }} is compatible.
|
||||
</span>
|
||||
<span
|
||||
*ngIf="!(version | satisfiesEmver: dependentInfo.version)"
|
||||
*ngIf="!(version | satisfiesExver : dependentInfo.version)"
|
||||
class="text text_error"
|
||||
>
|
||||
{{ title }} version {{ version | displayEmver }} is NOT compatible.
|
||||
{{ title }} version {{ version }} is NOT compatible.
|
||||
</span>
|
||||
</ion-text>
|
||||
</p>
|
||||
|
||||
@@ -24,10 +24,10 @@ export class MarketplaceShowDependentComponent {
|
||||
constructor(@Inject(DOCUMENT) private readonly document: Document) {}
|
||||
|
||||
get title(): string {
|
||||
return this.pkg.manifest.title
|
||||
return this.pkg.title
|
||||
}
|
||||
|
||||
get version(): string {
|
||||
return this.pkg.manifest.version
|
||||
return this.pkg.version
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
MarkdownPipeModule,
|
||||
SharedPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
AdditionalModule,
|
||||
DependenciesModule,
|
||||
PackageModule,
|
||||
FlavorsModule,
|
||||
} from '@start9labs/marketplace'
|
||||
import { MarketplaceStatusModule } from '../marketplace-status/marketplace-status.module'
|
||||
import { MarketplaceShowPage } from './marketplace-show.page'
|
||||
@@ -35,13 +36,14 @@ const routes: Routes = [
|
||||
RouterModule.forChild(routes),
|
||||
TextSpinnerComponentModule,
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
MarkdownPipeModule,
|
||||
MarketplaceStatusModule,
|
||||
PackageModule,
|
||||
AboutModule,
|
||||
DependenciesModule,
|
||||
AdditionalModule,
|
||||
FlavorsModule,
|
||||
UiPipeModule,
|
||||
],
|
||||
declarations: [
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
<marketplace-show-header></marketplace-show-header>
|
||||
|
||||
<ion-content class="ion-padding with-widgets">
|
||||
<ng-container *ngIf="pkg$ | async as pkg else loading">
|
||||
<ng-container *ngIf="pkg$ | async as pkg; else loading">
|
||||
<ng-container *ngIf="pkg | empty; else show">
|
||||
<div
|
||||
*ngIf="loadVersion$ | async as version"
|
||||
class="ion-text-center"
|
||||
style="padding-top: 64px"
|
||||
>
|
||||
<div class="ion-text-center" style="padding-top: 64px">
|
||||
<ion-icon
|
||||
name="close-circle-outline"
|
||||
style="font-size: 48px"
|
||||
></ion-icon>
|
||||
<h2>
|
||||
{{ pkgId }} @{{ version === '*' ? 'latest' : version }} not found in
|
||||
this registry
|
||||
</h2>
|
||||
<h2>{{ pkgId }} not found in this registry</h2>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@@ -25,21 +18,26 @@
|
||||
[url]="url"
|
||||
[pkg]="pkg"
|
||||
[localPkg]="localPkg$ | async"
|
||||
[localFlavor]="!!(localFlavor$ | async)"
|
||||
></marketplace-show-controls>
|
||||
|
||||
<marketplace-show-dependent [pkg]="pkg"></marketplace-show-dependent>
|
||||
|
||||
<ion-item-group>
|
||||
<marketplace-about [pkg]="pkg"></marketplace-about>
|
||||
<marketplace-flavors
|
||||
*ngIf="flavors$ | async as flavors"
|
||||
[pkgs]="flavors"
|
||||
></marketplace-flavors>
|
||||
<marketplace-dependencies
|
||||
*ngIf="!(pkg.manifest.dependencies | empty)"
|
||||
*ngIf="!(pkg.dependencyMetadata | empty)"
|
||||
[pkg]="pkg"
|
||||
></marketplace-dependencies>
|
||||
</ion-item-group>
|
||||
|
||||
<marketplace-additional
|
||||
[pkg]="pkg"
|
||||
(version)="loadVersion$.next($event)"
|
||||
(version)="updateVersion($event)"
|
||||
></marketplace-additional>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { getPkgId } from '@start9labs/shared'
|
||||
import { AbstractMarketplaceService } from '@start9labs/marketplace'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { Exver, getPkgId } from '@start9labs/shared'
|
||||
import {
|
||||
AbstractMarketplaceService,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { filter, shareReplay, switchMap } from 'rxjs/operators'
|
||||
import { combineLatest, Observable } from 'rxjs'
|
||||
import { filter, map, shareReplay, startWith, switchMap } from 'rxjs/operators'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { getManifest } from 'src/app/util/get-package-data'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-show',
|
||||
@@ -17,21 +21,61 @@ export class MarketplaceShowPage {
|
||||
readonly pkgId = getPkgId(this.route)
|
||||
readonly url = this.route.snapshot.queryParamMap.get('url') || undefined
|
||||
|
||||
readonly loadVersion$ = new BehaviorSubject<string>('*')
|
||||
readonly localPkg$ = combineLatest([
|
||||
this.patch.watch$('packageData', this.pkgId).pipe(filter(Boolean)),
|
||||
this.route.queryParamMap,
|
||||
]).pipe(
|
||||
map(([pkg, paramMap]) =>
|
||||
this.exver.getFlavor(getManifest(pkg).version) === paramMap.get('flavor')
|
||||
? pkg
|
||||
: null,
|
||||
),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
)
|
||||
|
||||
readonly localPkg$ = this.patch
|
||||
.watch$('packageData', this.pkgId)
|
||||
.pipe(filter(Boolean), shareReplay({ bufferSize: 1, refCount: true }))
|
||||
readonly localFlavor$ = this.localPkg$.pipe(
|
||||
map(pkg => !pkg),
|
||||
startWith(false),
|
||||
)
|
||||
|
||||
readonly pkg$ = this.loadVersion$.pipe(
|
||||
switchMap(version =>
|
||||
this.marketplaceService.getPackage$(this.pkgId, version, this.url),
|
||||
readonly pkg$: Observable<MarketplacePkg> = this.route.queryParamMap.pipe(
|
||||
switchMap(paramMap =>
|
||||
this.marketplaceService.getPackage$(
|
||||
this.pkgId,
|
||||
paramMap.get('version'),
|
||||
paramMap.get('flavor'),
|
||||
this.url,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
readonly flavors$ = this.route.queryParamMap.pipe(
|
||||
switchMap(paramMap =>
|
||||
this.marketplaceService
|
||||
.getSelectedStore$()
|
||||
.pipe(
|
||||
map(s =>
|
||||
s.packages.filter(
|
||||
p => p.id === this.pkgId && p.flavor !== paramMap.get('flavor'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly router: Router,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly marketplaceService: AbstractMarketplaceService,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
updateVersion(version: string) {
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
queryParams: { version },
|
||||
queryParamsHandling: 'merge',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<ng-container *ngIf="localPkg">
|
||||
<ng-container *ngIf="localPkg && sameFlavor">
|
||||
<div *ngIf="isInstalled(localPkg)">
|
||||
<ion-text
|
||||
*ngIf="(version | compareEmver: localVersion) !== 1"
|
||||
*ngIf="(version | compareExver : localVersion) !== 1"
|
||||
color="primary"
|
||||
>
|
||||
Installed
|
||||
</ion-text>
|
||||
<ion-text
|
||||
*ngIf="(version | compareEmver: localVersion) === 1"
|
||||
*ngIf="(version | compareExver : localVersion) === 1"
|
||||
color="success"
|
||||
>
|
||||
Update Available
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
isRestoring,
|
||||
getManifest,
|
||||
} from 'src/app/util/get-package-data'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-status',
|
||||
@@ -16,8 +17,7 @@ import {
|
||||
})
|
||||
export class MarketplaceStatusComponent {
|
||||
@Input() version!: string
|
||||
|
||||
@Input() localPkg?: PackageDataEntry
|
||||
@Input() localPkg!: PackageDataEntry
|
||||
|
||||
isInstalled = isInstalled
|
||||
isInstalling = isInstalling
|
||||
@@ -26,6 +26,15 @@ export class MarketplaceStatusComponent {
|
||||
isRestoring = isRestoring
|
||||
|
||||
get localVersion(): string {
|
||||
return this.localPkg ? getManifest(this.localPkg).version : ''
|
||||
return getManifest(this.localPkg).version
|
||||
}
|
||||
|
||||
get sameFlavor(): boolean {
|
||||
return (
|
||||
this.exver.getFlavor(this.version) ===
|
||||
this.exver.getFlavor(this.localVersion)
|
||||
)
|
||||
}
|
||||
|
||||
constructor(private readonly exver: Exver) {}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { EmverPipesModule } from '@start9labs/shared'
|
||||
import { ExverPipesModule } from '@start9labs/shared'
|
||||
import { InstallingProgressPipeModule } from '../../../pipes/install-progress/install-progress.module'
|
||||
import { MarketplaceStatusComponent } from './marketplace-status.component'
|
||||
|
||||
@@ -9,7 +9,7 @@ import { MarketplaceStatusComponent } from './marketplace-status.component'
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
InstallingProgressPipeModule,
|
||||
],
|
||||
declarations: [MarketplaceStatusComponent],
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
</ion-item-group>
|
||||
|
||||
<!-- hidden element for downloading cert -->
|
||||
<a id="install-cert" href="/eos/local.crt"></a>
|
||||
<a id="install-cert" href="/static/local-root-ca.crt"></a>
|
||||
</ion-content>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { ServerSpecsPage } from './server-specs.page'
|
||||
import { EmverPipesModule } from '@start9labs/shared'
|
||||
import { ExverPipesModule } from '@start9labs/shared'
|
||||
import { TuiLetModule } from '@taiga-ui/cdk'
|
||||
import { QRComponentModule } from 'src/app/components/qr/qr.component.module'
|
||||
|
||||
@@ -20,7 +20,7 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
QRComponentModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
TuiLetModule,
|
||||
],
|
||||
declarations: [ServerSpecsPage],
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Version</h2>
|
||||
<p>{{ server.version | displayEmver }}</p>
|
||||
<p>{{ server.version }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { SideloadPage } from './sideload.page'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { EmverPipesModule, SharedPipesModule } from '@start9labs/shared'
|
||||
import { ExverPipesModule, SharedPipesModule } from '@start9labs/shared'
|
||||
import { DragNDropDirective } from './dnd.directive'
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -19,7 +19,7 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
],
|
||||
declarations: [SideloadPage, DragNDropDirective],
|
||||
})
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
[src]="toUpload.icon | trustUrl"
|
||||
/>
|
||||
<h2>{{ toUpload.manifest.title }}</h2>
|
||||
<p>{{ toUpload.manifest.version | displayEmver }}</p>
|
||||
<p>{{ toUpload.manifest.version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { RouterModule, Routes } from '@angular/router'
|
||||
import { FilterUpdatesPipe, UpdatesPage } from './updates.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
MarkdownPipeModule,
|
||||
SharedPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
@@ -34,7 +34,7 @@ const routes: Routes = [
|
||||
RoundProgressModule,
|
||||
InstallingProgressPipeModule,
|
||||
StoreIconComponentModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
],
|
||||
})
|
||||
export class UpdatesPageModule {}
|
||||
|
||||
@@ -30,27 +30,21 @@
|
||||
>
|
||||
<ion-accordion-group multiple="true" class="ion-padding-start">
|
||||
<div *ngFor="let pkg of updates" class="item-container">
|
||||
<ion-accordion *ngIf="data.localPkgs[pkg.manifest.id] as local">
|
||||
<ion-accordion *ngIf="data.localPkgs[pkg.id] as local">
|
||||
<ion-item lines="none" slot="header">
|
||||
<ion-avatar slot="start" style="width: 50px; height: 50px">
|
||||
<img [src]="pkg.icon | trustUrl" />
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<h1 style="line-height: 1.3">{{ pkg.manifest.title }}</h1>
|
||||
<h1 style="line-height: 1.3">{{ pkg.title }}</h1>
|
||||
<h2 class="inline">
|
||||
<span>
|
||||
{{ local.stateInfo.manifest.version | displayEmver }}
|
||||
</span>
|
||||
<span>{{ local.stateInfo.manifest.version }}</span>
|
||||
|
||||
<ion-icon name="arrow-forward"></ion-icon>
|
||||
|
||||
<ion-text color="success">
|
||||
{{ pkg.manifest.version | displayEmver }}
|
||||
</ion-text>
|
||||
<ion-text color="success">{{ pkg.version }}</ion-text>
|
||||
</h2>
|
||||
<p
|
||||
*ngIf="marketplaceService.updateErrors[pkg.manifest.id] as error"
|
||||
>
|
||||
<p *ngIf="marketplaceService.updateErrors[pkg.id] as error">
|
||||
<ion-text color="danger">{{ error }}</ion-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
@@ -69,17 +63,17 @@
|
||||
</ng-container>
|
||||
<ng-template #notUpdating>
|
||||
<ion-spinner
|
||||
*ngIf="marketplaceService.updateQueue[pkg.manifest.id] else updateBtn"
|
||||
*ngIf="marketplaceService.updateQueue[pkg.id] else updateBtn"
|
||||
color="dark"
|
||||
></ion-spinner>
|
||||
<ng-template #updateBtn>
|
||||
<ion-button
|
||||
(click)="tryUpdate(pkg.manifest, host.url, $event)"
|
||||
[color]="marketplaceService.updateErrors[pkg.manifest.id] ? 'danger' : 'tertiary'"
|
||||
(click)="tryUpdate(pkg, host.url, $event)"
|
||||
[color]="marketplaceService.updateErrors[pkg.id] ? 'danger' : 'tertiary'"
|
||||
strong
|
||||
>
|
||||
{{ marketplaceService.updateErrors[pkg.manifest.id] ?
|
||||
'Retry' : 'Update' }}
|
||||
{{ marketplaceService.updateErrors[pkg.id] ? 'Retry' :
|
||||
'Update' }}
|
||||
</ion-button>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
@@ -88,12 +82,12 @@
|
||||
<div class="ion-padding" slot="content">
|
||||
<div class="notes">
|
||||
<h5>What's new</h5>
|
||||
<p [innerHTML]="pkg.manifest.releaseNotes| markdown"></p>
|
||||
<p [innerHTML]="pkg.releaseNotes| markdown"></p>
|
||||
</div>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
strong
|
||||
(click)="viewInMarketplace($event, host.url, pkg.manifest.id)"
|
||||
(click)="viewInMarketplace($event, host.url, pkg.id)"
|
||||
>
|
||||
View listing
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
MarketplacePkg,
|
||||
StoreIdentity,
|
||||
} from '@start9labs/marketplace'
|
||||
import { Emver, isEmptyObject } from '@start9labs/shared'
|
||||
import { Exver, isEmptyObject } from '@start9labs/shared'
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { combineLatest, map, Observable } from 'rxjs'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
isUpdating,
|
||||
} from 'src/app/util/get-package-data'
|
||||
import { dryUpdate } from 'src/app/util/dry-update'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
|
||||
interface UpdatesData {
|
||||
hosts: StoreIdentity[]
|
||||
@@ -59,7 +58,7 @@ export class UpdatesPage {
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
viewInMarketplace(event: Event, url: string, id: string) {
|
||||
@@ -70,29 +69,29 @@ export class UpdatesPage {
|
||||
})
|
||||
}
|
||||
|
||||
async tryUpdate(manifest: T.Manifest, url: string, e: Event): Promise<void> {
|
||||
async tryUpdate(pkg: MarketplacePkg, url: string, e: Event): Promise<void> {
|
||||
e.stopPropagation()
|
||||
|
||||
const { id, version } = manifest
|
||||
const { id, version } = pkg
|
||||
|
||||
delete this.marketplaceService.updateErrors[id]
|
||||
this.marketplaceService.updateQueue[id] = true
|
||||
|
||||
// manifest.id OK because same as local id for update
|
||||
if (hasCurrentDeps(manifest.id, await getAllPackages(this.patch))) {
|
||||
this.dryInstall(manifest, url)
|
||||
// id OK because same as local id for update
|
||||
if (hasCurrentDeps(id, await getAllPackages(this.patch))) {
|
||||
this.dryInstall(pkg, url)
|
||||
} else {
|
||||
this.install(id, version, url)
|
||||
}
|
||||
}
|
||||
|
||||
private async dryInstall(manifest: T.Manifest, url: string) {
|
||||
const { id, version, title } = manifest
|
||||
private async dryInstall(pkg: MarketplacePkg, url: string) {
|
||||
const { id, version, title } = pkg
|
||||
|
||||
const breakages = dryUpdate(
|
||||
manifest,
|
||||
pkg,
|
||||
await getAllPackages(this.patch),
|
||||
this.emver,
|
||||
this.exver,
|
||||
)
|
||||
|
||||
if (isEmptyObject(breakages)) {
|
||||
@@ -159,18 +158,19 @@ export class UpdatesPage {
|
||||
name: 'filterUpdates',
|
||||
})
|
||||
export class FilterUpdatesPipe implements PipeTransform {
|
||||
constructor(private readonly emver: Emver) {}
|
||||
constructor(private readonly exver: Exver) {}
|
||||
|
||||
transform(
|
||||
pkgs: MarketplacePkg[],
|
||||
local: Record<string, PackageDataEntry<InstalledState | UpdatingState>>,
|
||||
): MarketplacePkg[] {
|
||||
return pkgs.filter(({ manifest }) => {
|
||||
const localPkg = local[manifest.id]
|
||||
return pkgs.filter(({ id, version, flavor }) => {
|
||||
const localPkg = local[id]
|
||||
return (
|
||||
localPkg &&
|
||||
this.emver.compare(
|
||||
manifest.version,
|
||||
this.exver.getFlavor(localPkg.stateInfo.manifest.version) === flavor &&
|
||||
this.exver.compareExver(
|
||||
version,
|
||||
localPkg.stateInfo.manifest.version,
|
||||
) === 1
|
||||
)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,11 +3,26 @@ import {
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { Metric, NotificationLevel, RR, ServerNotifications } from './api.types'
|
||||
import { BTC_ICON, LND_ICON, PROXY_ICON } from './api-icons'
|
||||
import { DependencyMetadata, MarketplacePkg } from '@start9labs/marketplace'
|
||||
import { BTC_ICON, LND_ICON, PROXY_ICON, REGISTRY_ICON } from './api-icons'
|
||||
import { Log } from '@start9labs/shared'
|
||||
import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec'
|
||||
import { T, CB } from '@start9labs/start-sdk'
|
||||
import { GetPackagesRes } from '@start9labs/marketplace'
|
||||
|
||||
const mockBlake3Commitment: T.Blake3Commitment = {
|
||||
hash: 'fakehash',
|
||||
size: 0,
|
||||
}
|
||||
|
||||
const mockMerkleArchiveCommitment: T.MerkleArchiveCommitment = {
|
||||
rootSighash: 'fakehash',
|
||||
rootMaxsize: 0,
|
||||
}
|
||||
|
||||
const mockDescription = {
|
||||
short: 'Lorem ipsum dolor sit amet',
|
||||
long: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
}
|
||||
|
||||
export module Mock {
|
||||
export const ServerUpdated: T.ServerStatus = {
|
||||
@@ -37,17 +52,42 @@ export module Mock {
|
||||
},
|
||||
}
|
||||
|
||||
export const ReleaseNotes: RR.GetReleaseNotesRes = {
|
||||
'0.19.2':
|
||||
'Contrary to popular belief, Lorem Ipsum is not simply random text.',
|
||||
'0.19.1': 'release notes for Bitcoin 0.19.1',
|
||||
'0.19.0': 'release notes for Bitcoin 0.19.0',
|
||||
export const RegistryInfo: T.RegistryInfo = {
|
||||
name: 'Start9 Registry',
|
||||
icon: REGISTRY_ICON,
|
||||
categories: {
|
||||
bitcoin: {
|
||||
name: 'Bitcoin',
|
||||
description: mockDescription,
|
||||
},
|
||||
featured: {
|
||||
name: 'Featured',
|
||||
description: mockDescription,
|
||||
},
|
||||
lightning: {
|
||||
name: 'Lightning',
|
||||
description: mockDescription,
|
||||
},
|
||||
communications: {
|
||||
name: 'Communications',
|
||||
description: mockDescription,
|
||||
},
|
||||
data: {
|
||||
name: 'Data',
|
||||
description: mockDescription,
|
||||
},
|
||||
ai: {
|
||||
name: 'AI',
|
||||
description: mockDescription,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const MockManifestBitcoind: T.Manifest = {
|
||||
id: 'bitcoind',
|
||||
title: 'Bitcoin Core',
|
||||
version: '0.21.0',
|
||||
version: '0.21.0:0',
|
||||
satisfies: [],
|
||||
gitHash: 'abcdefgh',
|
||||
description: {
|
||||
short: 'A Bitcoin full node by Bitcoin Core.',
|
||||
@@ -90,7 +130,8 @@ export module Mock {
|
||||
export const MockManifestLnd: T.Manifest = {
|
||||
id: 'lnd',
|
||||
title: 'Lightning Network Daemon',
|
||||
version: '0.11.1',
|
||||
version: '0.11.1:0',
|
||||
satisfies: [],
|
||||
gitHash: 'abcdefgh',
|
||||
description: {
|
||||
short: 'A bolt spec compliant client.',
|
||||
@@ -116,11 +157,13 @@ export module Mock {
|
||||
bitcoind: {
|
||||
description: 'LND needs bitcoin to live.',
|
||||
optional: true,
|
||||
s9pk: '',
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
description:
|
||||
'As long as Bitcoin is pruned, LND needs Bitcoin Proxy to fetch block over the P2P network.',
|
||||
optional: true,
|
||||
s9pk: '',
|
||||
},
|
||||
},
|
||||
hasConfig: true,
|
||||
@@ -143,7 +186,8 @@ export module Mock {
|
||||
export const MockManifestBitcoinProxy: T.Manifest = {
|
||||
id: 'btc-rpc-proxy',
|
||||
title: 'Bitcoin Proxy',
|
||||
version: '0.2.2',
|
||||
version: '0.2.2:0',
|
||||
satisfies: [],
|
||||
gitHash: 'lmnopqrx',
|
||||
description: {
|
||||
short: 'A super charger for your Bitcoin node.',
|
||||
@@ -168,6 +212,7 @@ export module Mock {
|
||||
bitcoind: {
|
||||
description: 'Bitcoin Proxy requires a Bitcoin node.',
|
||||
optional: false,
|
||||
s9pk: '',
|
||||
},
|
||||
},
|
||||
hasConfig: false,
|
||||
@@ -187,149 +232,509 @@ export module Mock {
|
||||
},
|
||||
}
|
||||
|
||||
export const BitcoinDep: DependencyMetadata = {
|
||||
export const BitcoinDep: T.DependencyMetadata = {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
optional: false,
|
||||
hidden: true,
|
||||
description: 'Needed to run',
|
||||
}
|
||||
|
||||
export const ProxyDep: DependencyMetadata = {
|
||||
export const ProxyDep: T.DependencyMetadata = {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: PROXY_ICON,
|
||||
optional: true,
|
||||
hidden: false,
|
||||
description: 'Needed to run',
|
||||
}
|
||||
|
||||
export const MarketplacePkgs: {
|
||||
[id: string]: {
|
||||
[version: string]: MarketplacePkg
|
||||
}
|
||||
export const OtherPackageVersions: {
|
||||
[id: T.PackageId]: GetPackagesRes
|
||||
} = {
|
||||
bitcoind: {
|
||||
'0.19.0': {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.19.0',
|
||||
'=26.1.0:0.1.0': {
|
||||
best: {
|
||||
'26.1.0:0.1.0': {
|
||||
title: 'Bitcoin Core',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||
supportSite: 'https://bitcoin.org',
|
||||
marketingSite: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoind-startos/releases/download/v26.1.0/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
'#knots:26.1.20240325:0': {
|
||||
title: 'Bitcoin Knots',
|
||||
description: {
|
||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||
},
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||
supportSite: 'https://bitcoinknots.org',
|
||||
marketingSite: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoinknots-startos/releases/download/v26.1.20240513/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'featured'],
|
||||
otherVersions: {
|
||||
'27.0.0:1.0.0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
'#knots:27.1.0:0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.20.0': {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.20.0',
|
||||
'=#knots:26.1.20240325:0': {
|
||||
best: {
|
||||
'26.1.0:0.1.0': {
|
||||
title: 'Bitcoin Core',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||
supportSite: 'https://bitcoin.org',
|
||||
marketingSite: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoind-startos/releases/download/v26.1.0/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
'#knots:26.1.20240325:0': {
|
||||
title: 'Bitcoin Knots',
|
||||
description: {
|
||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||
},
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||
supportSite: 'https://bitcoinknots.org',
|
||||
marketingSite: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoinknots-startos/releases/download/v26.1.20240513/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.21.0': {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.21.0',
|
||||
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', 'featured'],
|
||||
otherVersions: {
|
||||
'27.0.0:1.0.0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
'#knots:27.1.0:0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'cryptocurrency'],
|
||||
versions: ['0.19.0', '0.20.0', '0.21.0'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
latest: {
|
||||
icon: BTC_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
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'],
|
||||
dependencyMetadata: {},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
lnd: {
|
||||
'0.11.0': {
|
||||
icon: LND_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.0',
|
||||
releaseNotes: 'release notes for LND 0.11.0',
|
||||
'=0.17.5:0': {
|
||||
best: {
|
||||
'0.17.5:0': {
|
||||
title: 'LND',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||
supportSite: 'https://lightning.engineering/slack.html',
|
||||
marketingSite: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release to 0.17.5',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
description: 'Used for RPC requests',
|
||||
optional: false,
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: PROXY_ICON,
|
||||
description: 'Used for authorized proxying of RPC requests',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/lnd-startos/releases/download/v0.17.5/lnd.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
categories: ['lightning', 'featured'],
|
||||
otherVersions: {
|
||||
'0.18.0:0.0.1': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
'0.17.4-beta:1.0-alpha': {
|
||||
releaseNotes: 'Upstream release to 0.17.4',
|
||||
},
|
||||
},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
'0.11.1': {
|
||||
icon: LND_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.1',
|
||||
releaseNotes: 'release notes for LND 0.11.1',
|
||||
'=0.17.4-beta:1.0-alpha': {
|
||||
best: {
|
||||
'0.17.4-beta:1.0-alpha': {
|
||||
title: 'LND',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||
supportSite: 'https://lightning.engineering/slack.html',
|
||||
marketingSite: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release to 0.17.4',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
description: 'Used for RPC requests',
|
||||
optional: false,
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: PROXY_ICON,
|
||||
description: 'Used for authorized proxying of RPC requests',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/lnd-startos/releases/download/v0.17.4/lnd.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
categories: ['lightning', 'featured'],
|
||||
otherVersions: {
|
||||
'0.18.0:0.0.1': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
'0.17.5:0': {
|
||||
releaseNotes: 'Upstream release to 0.17.5',
|
||||
},
|
||||
},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
latest: {
|
||||
icon: LND_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: Mock.MockManifestLnd,
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'btc-rpc-proxy': ProxyDep,
|
||||
},
|
||||
publishedAt: new Date(new Date().valueOf() + 10).toISOString(),
|
||||
},
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
latest: {
|
||||
icon: PROXY_ICON,
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: Mock.MockManifestBitcoinProxy,
|
||||
categories: ['bitcoin'],
|
||||
versions: ['0.2.2'],
|
||||
dependencyMetadata: {
|
||||
bitcoind: BitcoinDep,
|
||||
'=0.3.2.6:0': {
|
||||
best: {
|
||||
'0.3.2.6:0': {
|
||||
title: 'Bitcoin Proxy',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
||||
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||
supportSite: 'https://github.com/Kixunil/btc-rpc-proxy/issues',
|
||||
marketingSite: '',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: PROXY_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/btc-rpc-proxy-startos/releases/download/v0.3.2.7.1/btc-rpc-proxy.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin'],
|
||||
otherVersions: {
|
||||
'0.3.2.7:0': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
},
|
||||
publishedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const MarketplacePkgsList: RR.GetMarketplacePackagesRes =
|
||||
Object.values(Mock.MarketplacePkgs).map(service => service['latest'])
|
||||
export const RegistryPackages: GetPackagesRes = {
|
||||
bitcoind: {
|
||||
best: {
|
||||
'27.0.0:1.0.0': {
|
||||
title: 'Bitcoin Core',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoind-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoin/bitcoin',
|
||||
supportSite: 'https://bitcoin.org',
|
||||
marketingSite: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoind-startos/releases/download/v27.0.0/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
'#knots:27.1.0:0': {
|
||||
title: 'Bitcoin Knots',
|
||||
description: {
|
||||
short: 'An alternate fully verifying implementation of Bitcoin',
|
||||
long: 'Bitcoin Knots is a combined Bitcoin node and wallet. Not only is it easy to use, but it also ensures bitcoins you receive are both real bitcoins and really yours.',
|
||||
},
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/bitcoinknots-startos',
|
||||
upstreamRepo: 'https://github.com/bitcoinknots/bitcoin',
|
||||
supportSite: 'https://bitcoinknots.org',
|
||||
marketingSite: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/bitcoinknots-startos/releases/download/v26.1.20240513/bitcoind.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin', 'featured'],
|
||||
otherVersions: {
|
||||
'26.1.0:0.1.0': {
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
},
|
||||
'#knots:26.1.20240325:0': {
|
||||
releaseNotes: 'Even better Knots support for Bitcoin and wallets!',
|
||||
},
|
||||
},
|
||||
},
|
||||
lnd: {
|
||||
best: {
|
||||
'0.18.0:0.0.1': {
|
||||
title: 'LND',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/start9labs/lnd-startos',
|
||||
upstreamRepo: 'https://github.com/lightningnetwork/lnd',
|
||||
supportSite: 'https://lightning.engineering/slack.html',
|
||||
marketingSite: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
icon: BTC_ICON,
|
||||
description: 'Used for RPC requests',
|
||||
optional: false,
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: null,
|
||||
description: 'Used for authorized RPC requests',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/lnd-startos/releases/download/v0.18.0.1/lnd.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['lightning', 'featured'],
|
||||
otherVersions: {
|
||||
'0.17.5:0': {
|
||||
releaseNotes: 'Upstream release to 0.17.5',
|
||||
},
|
||||
'0.17.4-beta:1.0-alpha': {
|
||||
releaseNotes: 'Upstream release to 0.17.4',
|
||||
},
|
||||
},
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
best: {
|
||||
'0.3.2.7:0': {
|
||||
title: 'Bitcoin Proxy',
|
||||
description: mockDescription,
|
||||
hardwareRequirements: { arch: null, device: {}, ram: null },
|
||||
license: 'mit',
|
||||
wrapperRepo: 'https://github.com/Start9Labs/btc-rpc-proxy-wrappers',
|
||||
upstreamRepo: 'https://github.com/Kixunil/btc-rpc-proxy',
|
||||
supportSite: 'https://github.com/Kixunil/btc-rpc-proxy/issues',
|
||||
marketingSite: '',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
gitHash: 'fakehash',
|
||||
icon: PROXY_ICON,
|
||||
sourceVersion: null,
|
||||
dependencyMetadata: {},
|
||||
donationUrl: null,
|
||||
alerts: {
|
||||
install: 'test',
|
||||
uninstall: 'test',
|
||||
start: 'test',
|
||||
stop: 'test',
|
||||
restore: 'test',
|
||||
},
|
||||
s9pk: {
|
||||
url: 'https://github.com/Start9Labs/btc-rpc-proxy-startos/releases/download/v0.3.2.7/btc-rpc-proxy.s9pk',
|
||||
commitment: mockMerkleArchiveCommitment,
|
||||
signatures: {},
|
||||
publishedAt: Date.now().toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
categories: ['bitcoin'],
|
||||
otherVersions: {
|
||||
'0.3.2.6:0': {
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const Notifications: ServerNotifications = [
|
||||
{
|
||||
@@ -658,13 +1063,13 @@ export module Mock {
|
||||
packageBackups: {
|
||||
bitcoind: {
|
||||
title: 'Bitcoin Core',
|
||||
version: '0.21.0',
|
||||
version: '0.21.0:0',
|
||||
osVersion: '0.3.6',
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
'btc-rpc-proxy': {
|
||||
title: 'Bitcoin Proxy',
|
||||
version: '0.2.2',
|
||||
version: '0.2.2:0',
|
||||
osVersion: '0.3.6',
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
@@ -1489,8 +1894,7 @@ export module Mock {
|
||||
title: Mock.MockManifestBitcoind.title,
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
kind: 'running',
|
||||
registryUrl: '',
|
||||
versionSpec: '>=26.0.0',
|
||||
versionRange: '>=26.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: true,
|
||||
},
|
||||
@@ -1576,8 +1980,7 @@ export module Mock {
|
||||
title: Mock.MockManifestBitcoind.title,
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
kind: 'running',
|
||||
registryUrl: 'https://registry.start9.com',
|
||||
versionSpec: '>=26.0.0',
|
||||
versionRange: '>=26.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: true,
|
||||
},
|
||||
@@ -1585,8 +1988,7 @@ export module Mock {
|
||||
title: Mock.MockManifestBitcoinProxy.title,
|
||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
||||
kind: 'exists',
|
||||
registryUrl: 'https://community-registry.start9.com',
|
||||
versionSpec: '>2.0.0',
|
||||
versionRange: '>2.0.0',
|
||||
configSatisfied: false,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Dump } from 'patch-db-client'
|
||||
import { MarketplacePkg, StoreInfo } from '@start9labs/marketplace'
|
||||
import { PackagePropertiesVersioned } from 'src/app/util/properties.util'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared'
|
||||
@@ -223,12 +222,7 @@ export module RR {
|
||||
export type GetPackageMetricsReq = { id: string } // package.metrics
|
||||
export type GetPackageMetricsRes = Metric
|
||||
|
||||
export type InstallPackageReq = {
|
||||
id: string
|
||||
versionSpec?: string
|
||||
versionPriority?: 'min' | 'max'
|
||||
registry: string
|
||||
} // package.install
|
||||
export type InstallPackageReq = T.InstallParams
|
||||
export type InstallPackageRes = null
|
||||
|
||||
export type GetPackageConfigReq = { id: string } // package.config.get
|
||||
@@ -287,26 +281,13 @@ export module RR {
|
||||
progress: string // guid
|
||||
}
|
||||
|
||||
// marketplace
|
||||
// registry
|
||||
|
||||
export type GetMarketplaceInfoReq = { serverId: string }
|
||||
export type GetMarketplaceInfoRes = StoreInfo
|
||||
/** these are returned in ASCENDING order. the newest available version will be the LAST in the object */
|
||||
export type GetRegistryOsUpdateRes = { [version: string]: T.OsVersionInfo }
|
||||
|
||||
export type CheckOSUpdateReq = { serverId: string }
|
||||
export type CheckOSUpdateRes = OSUpdate
|
||||
|
||||
export type GetMarketplacePackagesReq = {
|
||||
ids?: { id: string; version: string }[]
|
||||
// iff !ids
|
||||
category?: string
|
||||
query?: string
|
||||
page?: number
|
||||
perPage?: number
|
||||
}
|
||||
export type GetMarketplacePackagesRes = MarketplacePkg[]
|
||||
|
||||
export type GetReleaseNotesReq = { id: string }
|
||||
export type GetReleaseNotesRes = { [version: string]: string }
|
||||
}
|
||||
|
||||
export interface OSUpdate {
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
import { Observable } from 'rxjs'
|
||||
import { RR } from './api.types'
|
||||
import { RPCOptions } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import {
|
||||
GetPackageRes,
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
|
||||
export abstract class ApiService {
|
||||
// http
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
abstract getStatic(url: string): Promise<string>
|
||||
|
||||
// for sideloading packages
|
||||
abstract uploadPackage(guid: string, body: Blob): Promise<string>
|
||||
|
||||
abstract uploadFile(body: Blob): Promise<string>
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
abstract getStaticProxy(
|
||||
pkg: MarketplacePkg,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string>
|
||||
|
||||
abstract getStaticInstalled(
|
||||
id: T.PackageId,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string>
|
||||
|
||||
// websocket
|
||||
|
||||
abstract openWebsocket$<T>(
|
||||
@@ -120,14 +135,23 @@ export abstract class ApiService {
|
||||
|
||||
// marketplace URLs
|
||||
|
||||
abstract marketplaceProxy<T>(
|
||||
path: string,
|
||||
params: Record<string, unknown>,
|
||||
url: string,
|
||||
abstract registryRequest<T>(
|
||||
registryUrl: string,
|
||||
options: RPCOptions,
|
||||
): Promise<T>
|
||||
|
||||
abstract checkOSUpdate(qp: RR.CheckOSUpdateReq): Promise<RR.CheckOSUpdateRes>
|
||||
|
||||
abstract getRegistryInfo(registryUrl: string): Promise<T.RegistryInfo>
|
||||
|
||||
abstract getRegistryPackage(
|
||||
url: string,
|
||||
id: string,
|
||||
versionRange: string | null,
|
||||
): Promise<GetPackageRes>
|
||||
|
||||
abstract getRegistryPackages(registryUrl: string): Promise<GetPackagesRes>
|
||||
|
||||
// notification
|
||||
|
||||
abstract getNotifications(
|
||||
|
||||
@@ -18,6 +18,15 @@ import { AuthService } from '../auth.service'
|
||||
import { DOCUMENT } from '@angular/common'
|
||||
import { DataModel } from '../patch-db/data-model'
|
||||
import { Dump, pathFromArray } from 'patch-db-client'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import {
|
||||
GetPackageReq,
|
||||
GetPackageRes,
|
||||
GetPackagesReq,
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
import { blake3 } from '@noble/hashes/blake3'
|
||||
|
||||
@Injectable()
|
||||
export class LiveApiService extends ApiService {
|
||||
@@ -29,17 +38,7 @@ export class LiveApiService extends ApiService {
|
||||
@Inject(PATCH_CACHE) private readonly cache$: Observable<Dump<DataModel>>,
|
||||
) {
|
||||
super()
|
||||
; (window as any).rpcClient = this
|
||||
}
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
|
||||
async getStatic(url: string): Promise<string> {
|
||||
return this.httpRequest({
|
||||
method: Method.GET,
|
||||
url,
|
||||
responseType: 'text',
|
||||
})
|
||||
;(window as any).rpcClient = this
|
||||
}
|
||||
|
||||
// for sideloading packages
|
||||
@@ -62,6 +61,36 @@ export class LiveApiService extends ApiService {
|
||||
})
|
||||
}
|
||||
|
||||
// for getting static files: ex. instructions, licenses
|
||||
|
||||
async getStaticProxy(
|
||||
pkg: MarketplacePkg,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
const encodedUrl = encodeURIComponent(pkg.s9pk.url)
|
||||
|
||||
return this.httpRequest({
|
||||
method: Method.GET,
|
||||
url: `/s9pk/proxy/${encodedUrl}/${path}`,
|
||||
params: {
|
||||
rootSighash: pkg.s9pk.commitment.rootSighash,
|
||||
rootMaxsize: pkg.s9pk.commitment.rootMaxsize,
|
||||
},
|
||||
responseType: 'text',
|
||||
})
|
||||
}
|
||||
|
||||
async getStaticInstalled(
|
||||
id: T.PackageId,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
return this.httpRequest({
|
||||
method: Method.GET,
|
||||
url: `/s9pk/installed/${id}.s9pk/${path}`,
|
||||
responseType: 'text',
|
||||
})
|
||||
}
|
||||
|
||||
// websocket
|
||||
|
||||
openWebsocket$<T>(
|
||||
@@ -257,24 +286,63 @@ export class LiveApiService extends ApiService {
|
||||
|
||||
// marketplace URLs
|
||||
|
||||
async marketplaceProxy<T>(
|
||||
path: string,
|
||||
qp: Record<string, string>,
|
||||
baseUrl: string,
|
||||
async registryRequest<T>(
|
||||
registryUrl: string,
|
||||
options: RPCOptions,
|
||||
): Promise<T> {
|
||||
const fullUrl = `${baseUrl}${path}?${new URLSearchParams(qp).toString()}`
|
||||
return this.rpcRequest({
|
||||
method: 'marketplace.get',
|
||||
params: { url: fullUrl },
|
||||
...options,
|
||||
method: `registry.${options.method}`,
|
||||
params: { registry: registryUrl, ...options.params },
|
||||
})
|
||||
}
|
||||
|
||||
async checkOSUpdate(qp: RR.CheckOSUpdateReq): Promise<RR.CheckOSUpdateRes> {
|
||||
return this.marketplaceProxy(
|
||||
'/eos/v0/latest',
|
||||
qp,
|
||||
this.config.marketplace.start9,
|
||||
)
|
||||
const { serverId } = qp
|
||||
|
||||
return this.registryRequest(this.config.marketplace.start9, {
|
||||
method: 'os.version.get',
|
||||
params: { serverId },
|
||||
})
|
||||
}
|
||||
|
||||
async getRegistryInfo(registryUrl: string): Promise<T.RegistryInfo> {
|
||||
return this.registryRequest(registryUrl, {
|
||||
method: 'info',
|
||||
params: {},
|
||||
})
|
||||
}
|
||||
|
||||
async getRegistryPackage(
|
||||
registryUrl: string,
|
||||
id: string,
|
||||
versionRange: string | null,
|
||||
): Promise<GetPackageRes> {
|
||||
const params: GetPackageReq = {
|
||||
id,
|
||||
version: versionRange,
|
||||
sourceVersion: null,
|
||||
otherVersions: 'short',
|
||||
}
|
||||
|
||||
return this.registryRequest<GetPackageRes>(registryUrl, {
|
||||
method: 'package.get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
async getRegistryPackages(registryUrl: string): Promise<GetPackagesRes> {
|
||||
const params: GetPackagesReq = {
|
||||
id: null,
|
||||
version: null,
|
||||
sourceVersion: null,
|
||||
otherVersions: 'short',
|
||||
}
|
||||
|
||||
return this.registryRequest<GetPackagesRes>(registryUrl, {
|
||||
method: 'package.get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
// notification
|
||||
@@ -504,6 +572,29 @@ export class LiveApiService extends ApiService {
|
||||
|
||||
private async httpRequest<T>(opts: HttpOptions): Promise<T> {
|
||||
const res = await this.http.httpRequest<T>(opts)
|
||||
if (res.headers.get('Repr-Digest')) {
|
||||
// verify
|
||||
const digest = res.headers.get('Repr-Digest')!
|
||||
let data: Uint8Array
|
||||
if (opts.responseType === 'arrayBuffer') {
|
||||
data = Buffer.from(res.body as ArrayBuffer)
|
||||
} else if (opts.responseType === 'text') {
|
||||
data = Buffer.from(res.body as string)
|
||||
} else if ((opts.responseType as string) === 'blob') {
|
||||
data = Buffer.from(await (res.body as Blob).arrayBuffer())
|
||||
} else {
|
||||
console.warn(
|
||||
`could not verify Repr-Digest for responseType ${
|
||||
opts.responseType || 'json'
|
||||
}`,
|
||||
)
|
||||
return res.body
|
||||
}
|
||||
const computedDigest = Buffer.from(blake3(data)).toString('base64')
|
||||
if (`blake3=:${computedDigest}:` === digest) return res.body
|
||||
console.debug(computedDigest, digest)
|
||||
throw new Error('File digest mismatch.')
|
||||
}
|
||||
return res.body
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Log, RPCErrorDetails, pauseFor } from '@start9labs/shared'
|
||||
import { Log, RPCErrorDetails, RPCOptions, pauseFor } from '@start9labs/shared'
|
||||
import { ApiService } from './embassy-api.service'
|
||||
import {
|
||||
Operation,
|
||||
@@ -30,8 +30,12 @@ import {
|
||||
} from 'rxjs'
|
||||
import { mockPatchData } from './mock-patch'
|
||||
import { AuthService } from '../auth.service'
|
||||
import { StoreInfo } from '@start9labs/marketplace'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import {
|
||||
GetPackageRes,
|
||||
GetPackagesRes,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
|
||||
const PROGRESS: T.FullProgress = {
|
||||
overall: {
|
||||
@@ -48,10 +52,7 @@ const PROGRESS: T.FullProgress = {
|
||||
},
|
||||
{
|
||||
name: 'Validating',
|
||||
progress: {
|
||||
done: 0,
|
||||
total: 40,
|
||||
},
|
||||
progress: null,
|
||||
},
|
||||
{
|
||||
name: 'Installing',
|
||||
@@ -80,14 +81,25 @@ export class MockApiService extends ApiService {
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
async getStatic(url: string): Promise<string> {
|
||||
async uploadPackage(guid: string, body: Blob): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
return 'success'
|
||||
}
|
||||
|
||||
async getStaticProxy(
|
||||
pkg: MarketplacePkg,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
return markdown
|
||||
}
|
||||
|
||||
async uploadPackage(guid: string, body: Blob): Promise<string> {
|
||||
async getStaticInstalled(
|
||||
id: T.PackageId,
|
||||
path: 'LICENSE.md' | 'instructions.md',
|
||||
): Promise<string> {
|
||||
await pauseFor(2000)
|
||||
return 'success'
|
||||
return markdown
|
||||
}
|
||||
|
||||
// websocket
|
||||
@@ -136,7 +148,7 @@ export class MockApiService extends ApiService {
|
||||
|
||||
this.stateIndex++
|
||||
|
||||
return this.stateIndex === 1 ? 'initializing' : 'running'
|
||||
return this.stateIndex === 1 ? 'running' : 'running'
|
||||
}
|
||||
|
||||
// db
|
||||
@@ -448,34 +460,13 @@ export class MockApiService extends ApiService {
|
||||
|
||||
// marketplace URLs
|
||||
|
||||
async marketplaceProxy(
|
||||
path: string,
|
||||
params: Record<string, string>,
|
||||
url: string,
|
||||
async registryRequest(
|
||||
registryUrl: string,
|
||||
options: RPCOptions,
|
||||
): Promise<any> {
|
||||
await pauseFor(2000)
|
||||
|
||||
if (path === '/package/v0/info') {
|
||||
const info: StoreInfo = {
|
||||
name: 'Start9 Registry',
|
||||
categories: [
|
||||
'bitcoin',
|
||||
'lightning',
|
||||
'data',
|
||||
'featured',
|
||||
'messaging',
|
||||
'social',
|
||||
'alt coin',
|
||||
],
|
||||
}
|
||||
return info
|
||||
} else if (path === '/package/v0/index') {
|
||||
return Mock.MarketplacePkgsList
|
||||
} else if (path.startsWith('/package/v0/release-notes')) {
|
||||
return Mock.ReleaseNotes
|
||||
} else if (path.includes('instructions') || path.includes('license')) {
|
||||
return markdown
|
||||
}
|
||||
return Error('do not call directly')
|
||||
}
|
||||
|
||||
async checkOSUpdate(qp: RR.CheckOSUpdateReq): Promise<RR.CheckOSUpdateRes> {
|
||||
@@ -483,6 +474,29 @@ export class MockApiService extends ApiService {
|
||||
return Mock.MarketplaceEos
|
||||
}
|
||||
|
||||
async getRegistryInfo(registryUrl: string): Promise<T.RegistryInfo> {
|
||||
await pauseFor(2000)
|
||||
return Mock.RegistryInfo
|
||||
}
|
||||
|
||||
async getRegistryPackage(
|
||||
url: string,
|
||||
id: string,
|
||||
versionRange: string,
|
||||
): Promise<GetPackageRes> {
|
||||
await pauseFor(2000)
|
||||
if (!versionRange) {
|
||||
return Mock.RegistryPackages[id]
|
||||
} else {
|
||||
return Mock.OtherPackageVersions[id][versionRange]
|
||||
}
|
||||
}
|
||||
|
||||
async getRegistryPackages(registryUrl: string): Promise<GetPackagesRes> {
|
||||
await pauseFor(2000)
|
||||
return Mock.RegistryPackages
|
||||
}
|
||||
|
||||
// notification
|
||||
|
||||
async getNotifications(
|
||||
@@ -742,11 +756,11 @@ export class MockApiService extends ApiService {
|
||||
...Mock.LocalPkgs[params.id],
|
||||
stateInfo: {
|
||||
// if installing
|
||||
// state: PackageState.Installing,
|
||||
state: 'installing',
|
||||
|
||||
// if updating
|
||||
state: 'updating',
|
||||
manifest: mockPatchData.packageData[params.id].stateInfo.manifest!,
|
||||
// state: 'updating',
|
||||
// manifest: mockPatchData.packageData[params.id].stateInfo.manifest!,
|
||||
|
||||
// both
|
||||
installingInfo: {
|
||||
@@ -1129,11 +1143,7 @@ export class MockApiService extends ApiService {
|
||||
const progress = JSON.parse(JSON.stringify(PROGRESS))
|
||||
|
||||
for (let [i, phase] of progress.phases.entries()) {
|
||||
if (
|
||||
!phase.progress ||
|
||||
typeof phase.progress !== 'object' ||
|
||||
!phase.progress.total
|
||||
) {
|
||||
if (!phase.progress || phase.progress === true || !phase.progress.total) {
|
||||
await pauseFor(2000)
|
||||
|
||||
const patches: Operation<any>[] = [
|
||||
|
||||
@@ -88,7 +88,7 @@ export const mockPatchData: DataModel = {
|
||||
state: 'installed',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.20.0',
|
||||
version: '0.20.0:0',
|
||||
},
|
||||
},
|
||||
icon: '/assets/img/service-icons/bitcoind.svg',
|
||||
@@ -295,7 +295,7 @@ export const mockPatchData: DataModel = {
|
||||
state: 'installed',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.0',
|
||||
version: '0.11.0:0.0.1',
|
||||
},
|
||||
},
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
@@ -368,8 +368,7 @@ export const mockPatchData: DataModel = {
|
||||
title: 'Bitcoin Core',
|
||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||
kind: 'running',
|
||||
registryUrl: 'https://registry.start9.com',
|
||||
versionSpec: '>=26.0.0',
|
||||
versionRange: '>=26.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: true,
|
||||
},
|
||||
@@ -377,8 +376,7 @@ export const mockPatchData: DataModel = {
|
||||
title: 'Bitcoin Proxy',
|
||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
||||
kind: 'running',
|
||||
registryUrl: 'https://community-registry.start9.com',
|
||||
versionSpec: '>2.0.0',
|
||||
versionRange: '>2.0.0',
|
||||
healthChecks: [],
|
||||
configSatisfied: false,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
import { distinctUntilChanged, map, shareReplay } from 'rxjs/operators'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
@@ -39,7 +39,7 @@ export class DepErrorService {
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
@@ -87,11 +87,17 @@ export class DepErrorService {
|
||||
const depManifest = dep.stateInfo.manifest
|
||||
|
||||
// incorrect version
|
||||
if (!this.emver.satisfies(depManifest.version, currentDep.versionSpec)) {
|
||||
return {
|
||||
type: 'incorrectVersion',
|
||||
expected: currentDep.versionSpec,
|
||||
received: depManifest.version,
|
||||
if (!this.exver.satisfies(depManifest.version, currentDep.versionRange)) {
|
||||
if (
|
||||
depManifest.satisfies.some(
|
||||
v => !this.exver.satisfies(v, currentDep.versionRange),
|
||||
)
|
||||
) {
|
||||
return {
|
||||
type: 'incorrectVersion',
|
||||
expected: currentDep.versionRange,
|
||||
received: depManifest.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { BehaviorSubject, combineLatest } from 'rxjs'
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators'
|
||||
import { OSUpdate } from 'src/app/services/api/api.types'
|
||||
@@ -7,6 +6,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { getServerInfo } from 'src/app/util/get-server-info'
|
||||
import { DataModel } from './patch-db/data-model'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -47,15 +47,15 @@ export class EOSService {
|
||||
|
||||
constructor(
|
||||
private readonly api: ApiService,
|
||||
private readonly emver: Emver,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
async loadEos(): Promise<void> {
|
||||
const { version, id } = await getServerInfo(this.patch)
|
||||
this.osUpdate = await this.api.checkOSUpdate({ serverId: id })
|
||||
const updateAvailable =
|
||||
this.emver.compare(this.osUpdate.version, version) === 1
|
||||
this.exver.compareOsVersion(this.osUpdate.version, version) === 'greater'
|
||||
this.updateAvailable$.next(updateAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Inject, Injectable } from '@angular/core'
|
||||
import {
|
||||
MarketplacePkg,
|
||||
AbstractMarketplaceService,
|
||||
StoreData,
|
||||
Marketplace,
|
||||
StoreInfo,
|
||||
StoreIdentity,
|
||||
MarketplacePkg,
|
||||
GetPackageRes,
|
||||
} from '@start9labs/marketplace'
|
||||
import {
|
||||
BehaviorSubject,
|
||||
@@ -37,8 +37,9 @@ import {
|
||||
tap,
|
||||
} from 'rxjs/operators'
|
||||
import { ConfigService } from './config.service'
|
||||
import { sameUrl } from '@start9labs/shared'
|
||||
import { Exver, sameUrl } from '@start9labs/shared'
|
||||
import { ClientStorageService } from './client-storage.service'
|
||||
import { ExtendedVersion, T } from '@start9labs/start-sdk'
|
||||
|
||||
@Injectable()
|
||||
export class MarketplaceService implements AbstractMarketplaceService {
|
||||
@@ -93,11 +94,9 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
mergeMap(({ url, name }) =>
|
||||
this.fetchStore$(url).pipe(
|
||||
tap(data => {
|
||||
if (data?.info) this.updateStoreName(url, name, data.info.name)
|
||||
}),
|
||||
map<StoreData | null, [string, StoreData | null]>(data => {
|
||||
return [url, data]
|
||||
if (data?.info.name) this.updateStoreName(url, name, data.info.name)
|
||||
}),
|
||||
map<StoreData | null, [string, StoreData | null]>(data => [url, data]),
|
||||
startWith<[string, StoreData | null]>([url, null]),
|
||||
),
|
||||
),
|
||||
@@ -148,6 +147,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly config: ConfigService,
|
||||
private readonly clientStorageService: ClientStorageService,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
|
||||
getKnownHosts$(filtered = false): Observable<StoreIdentity[]> {
|
||||
@@ -170,28 +170,29 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
|
||||
getPackage$(
|
||||
id: string,
|
||||
version: string,
|
||||
optionalUrl?: string,
|
||||
version: string | null,
|
||||
flavor: string | null,
|
||||
registryUrl?: string,
|
||||
): Observable<MarketplacePkg> {
|
||||
return this.patch.watch$('ui', 'marketplace').pipe(
|
||||
switchMap(uiMarketplace => {
|
||||
const url = optionalUrl || uiMarketplace.selectedUrl
|
||||
return this.selectedHost$.pipe(
|
||||
switchMap(selected =>
|
||||
this.marketplace$.pipe(
|
||||
switchMap(m => {
|
||||
const url = registryUrl || selected.url
|
||||
|
||||
if (version !== '*' || !uiMarketplace.knownHosts[url]) {
|
||||
return this.fetchPackage$(id, version, url)
|
||||
}
|
||||
const pkg = m[url]?.packages.find(
|
||||
p =>
|
||||
p.id === id &&
|
||||
p.flavor === flavor &&
|
||||
(!version || this.exver.compareExver(p.version, version) === 0),
|
||||
)
|
||||
|
||||
return this.marketplace$.pipe(
|
||||
map(m => m[url]),
|
||||
filter(Boolean),
|
||||
take(1),
|
||||
map(
|
||||
store =>
|
||||
store.packages.find(p => p.manifest.id === id) ||
|
||||
({} as MarketplacePkg),
|
||||
),
|
||||
)
|
||||
}),
|
||||
return !!pkg
|
||||
? of(pkg)
|
||||
: this.fetchPackage$(url, id, version, flavor)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -210,56 +211,22 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
): Promise<void> {
|
||||
const params: RR.InstallPackageReq = {
|
||||
id,
|
||||
versionSpec: `=${version}`,
|
||||
version,
|
||||
registry: url,
|
||||
}
|
||||
|
||||
await this.api.installPackage(params)
|
||||
}
|
||||
|
||||
fetchInfo$(url: string): Observable<StoreInfo> {
|
||||
return this.patch.watch$('serverInfo').pipe(
|
||||
take(1),
|
||||
switchMap(serverInfo => {
|
||||
const qp: RR.GetMarketplaceInfoReq = { serverId: serverInfo.id }
|
||||
return this.api.marketplaceProxy<RR.GetMarketplaceInfoRes>(
|
||||
'/package/v0/info',
|
||||
qp,
|
||||
url,
|
||||
)
|
||||
}),
|
||||
)
|
||||
fetchInfo$(url: string): Observable<T.RegistryInfo> {
|
||||
return from(this.api.getRegistryInfo(url))
|
||||
}
|
||||
|
||||
fetchReleaseNotes$(
|
||||
id: string,
|
||||
url?: string,
|
||||
): Observable<Record<string, string>> {
|
||||
return this.selectedHost$.pipe(
|
||||
switchMap(m => {
|
||||
return from(
|
||||
this.api.marketplaceProxy<Record<string, string>>(
|
||||
`/package/v0/release-notes/${id}`,
|
||||
{},
|
||||
url || m.url,
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fetchStatic$(id: string, type: string, url?: string): Observable<string> {
|
||||
return this.selectedHost$.pipe(
|
||||
switchMap(m => {
|
||||
return from(
|
||||
this.api.marketplaceProxy<string>(
|
||||
`/package/v0/${type}/${id}`,
|
||||
{},
|
||||
url || m.url,
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
fetchStatic$(
|
||||
pkg: MarketplacePkg,
|
||||
type: 'LICENSE.md' | 'instructions.md',
|
||||
): Observable<string> {
|
||||
return from(this.api.getStaticProxy(pkg, type))
|
||||
}
|
||||
|
||||
private fetchStore$(url: string): Observable<StoreData | null> {
|
||||
@@ -273,33 +240,57 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
)
|
||||
}
|
||||
|
||||
private fetchPackages$(
|
||||
url: string,
|
||||
params: Omit<RR.GetMarketplacePackagesReq, 'page' | 'per-page'> = {},
|
||||
): Observable<MarketplacePkg[]> {
|
||||
const qp: RR.GetMarketplacePackagesReq = {
|
||||
...params,
|
||||
page: 1,
|
||||
perPage: 100,
|
||||
}
|
||||
if (qp.ids) qp.ids = JSON.stringify(qp.ids)
|
||||
|
||||
return from(
|
||||
this.api.marketplaceProxy<RR.GetMarketplacePackagesRes>(
|
||||
'/package/v0/index',
|
||||
qp,
|
||||
url,
|
||||
),
|
||||
private fetchPackages$(url: string): Observable<MarketplacePkg[]> {
|
||||
return from(this.api.getRegistryPackages(url)).pipe(
|
||||
map(packages => {
|
||||
return Object.entries(packages).flatMap(([id, pkgInfo]) =>
|
||||
Object.keys(pkgInfo.best).map(version =>
|
||||
this.convertToMarketplacePkg(
|
||||
id,
|
||||
version,
|
||||
this.exver.getFlavor(version),
|
||||
pkgInfo,
|
||||
),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private fetchPackage$(
|
||||
convertToMarketplacePkg(
|
||||
id: string,
|
||||
version: string,
|
||||
version: string | null,
|
||||
flavor: string | null,
|
||||
pkgInfo: GetPackageRes,
|
||||
): MarketplacePkg {
|
||||
version =
|
||||
version ||
|
||||
Object.keys(pkgInfo.best).find(v => this.exver.getFlavor(v) === flavor) ||
|
||||
null
|
||||
|
||||
return !version || !pkgInfo.best[version]
|
||||
? ({} as MarketplacePkg)
|
||||
: {
|
||||
id,
|
||||
version,
|
||||
flavor,
|
||||
...pkgInfo,
|
||||
...pkgInfo.best[version],
|
||||
}
|
||||
}
|
||||
|
||||
private fetchPackage$(
|
||||
url: string,
|
||||
id: string,
|
||||
version: string | null,
|
||||
flavor: string | null,
|
||||
): Observable<MarketplacePkg> {
|
||||
return this.fetchPackages$(url, { ids: [{ id, version }] }).pipe(
|
||||
map(pkgs => pkgs[0] || {}),
|
||||
return from(
|
||||
this.api.getRegistryPackage(url, id, version ? `=${version}` : null),
|
||||
).pipe(
|
||||
map(pkgInfo =>
|
||||
this.convertToMarketplacePkg(id, version, flavor, pkgInfo),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ export type PackageDataEntry<T extends StateInfo = StateInfo> =
|
||||
stateInfo: T
|
||||
}
|
||||
|
||||
export type AllPackageData = NonNullable<
|
||||
T.AllPackageData & Record<string, PackageDataEntry<StateInfo>>
|
||||
>
|
||||
|
||||
export type StateInfo = InstalledState | InstallingState | UpdatingState
|
||||
|
||||
export type InstalledState = {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
import { DataModel } from '../services/patch-db/data-model'
|
||||
import { getManifest } from './get-package-data'
|
||||
|
||||
export function dryUpdate(
|
||||
{ id, version }: { id: string; version: string },
|
||||
pkgs: DataModel['packageData'],
|
||||
emver: Emver,
|
||||
exver: Exver,
|
||||
): string[] {
|
||||
return Object.values(pkgs)
|
||||
.filter(
|
||||
pkg =>
|
||||
Object.keys(pkg.currentDependencies || {}).some(
|
||||
pkgId => pkgId === id,
|
||||
) && !emver.satisfies(version, pkg.currentDependencies[id].versionSpec),
|
||||
) &&
|
||||
!exver.satisfies(version, pkg.currentDependencies[id].versionRange),
|
||||
)
|
||||
.map(pkg => getManifest(pkg).title)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user