From 405b3be496eced77764ef97d79dac5fabe57ca50 Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Wed, 20 Mar 2024 21:26:18 -0600 Subject: [PATCH 1/4] complete get_container_ip effect handler --- core/startos/src/net/net_controller.rs | 4 ++++ .../src/service/service_effect_handler.rs | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index 5fa2a30ec..c9338cdff 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -385,6 +385,10 @@ impl NetService { )) } } + + pub fn get_ip(&self) -> Ipv4Addr { + self.ip.to_owned() + } } impl Drop for NetService { diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index 65217ecd5..c103d5994 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -1,3 +1,5 @@ +use std::any::Any; +use std::borrow::Borrow; use std::collections::BTreeSet; use std::ffi::OsString; use std::net::Ipv4Addr; @@ -23,7 +25,8 @@ use crate::db::model::package::{ use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::loop_dev::LoopDev; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; -use crate::prelude::*; +use crate::net::net; +use crate::{net, prelude::*}; use crate::s9pk::rpc::SKIP_ENV; use crate::service::cli::ContainerCliContext; use crate::service::ServiceActorSeed; @@ -337,7 +340,18 @@ async fn get_system_smtp( todo!() } async fn get_container_ip(context: EffectContext, _: Empty) -> Result { - todo!() + match context.0.upgrade() { + Some(c) => { + let net_service = c.persistent_container.net_service.lock().await; + Ok(net_service.get_ip()) + }, + None => { + Err(Error::new( + eyre!("Upgrade on Weak resulted in a None variant"), + crate::ErrorKind::NotFound + )) + } + } } async fn get_service_port_forward( context: EffectContext, From ab836c6922cdd1f12e7c2e533bd44ffa0e6ab32b Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Wed, 20 Mar 2024 21:50:21 -0600 Subject: [PATCH 2/4] remove unneeded imports --- core/startos/src/service/service_effect_handler.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index c103d5994..81ea40eaa 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -1,5 +1,3 @@ -use std::any::Any; -use std::borrow::Borrow; use std::collections::BTreeSet; use std::ffi::OsString; use std::net::Ipv4Addr; @@ -25,8 +23,7 @@ use crate::db::model::package::{ use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::loop_dev::LoopDev; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; -use crate::net::net; -use crate::{net, prelude::*}; +use crate::prelude::*; use crate::s9pk::rpc::SKIP_ENV; use crate::service::cli::ContainerCliContext; use crate::service::ServiceActorSeed; From 66b0108c51e4f65fd2e3501995dad52f6af62cd8 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 21 Mar 2024 17:21:37 -0600 Subject: [PATCH 3/4] revamp manifest types --- sdk/lib/StartSdk.ts | 19 +- sdk/lib/actions/createAction.ts | 19 +- sdk/lib/actions/setupActions.ts | 4 +- sdk/lib/types.ts | 5 +- .../src/pages/list/item/item.component.html | 2 +- .../src/pages/list/item/item.module.ts | 9 +- .../dependencies/dependencies.component.html | 10 +- .../dependencies/dependencies.component.ts | 3 +- .../pages/show/package/package.component.html | 2 +- .../src/pages/show/package/package.module.ts | 2 - .../marketplace/src/pipes/mime-type.pipe.ts | 32 -- web/projects/marketplace/src/public-api.ts | 1 - web/projects/marketplace/src/types.ts | 33 +- .../app/modals/app-config/app-config.page.ts | 4 +- .../app-actions/app-actions.page.html | 10 +- .../app-actions/app-actions.page.ts | 23 +- .../apps-routes/app-show/app-show.module.ts | 8 +- .../apps-routes/app-show/app-show.page.html | 4 +- .../apps-routes/app-show/app-show.page.ts | 20 +- .../app-show-additional.component.ts | 2 +- .../app-show-health-checks.component.html | 164 ++++--- .../app-show-health-checks.component.ts | 11 +- .../app-show-status.component.ts | 16 +- .../app-show/pipes/to-buttons.pipe.ts | 3 +- .../app-show/pipes/to-health-checks.pipe.ts | 19 +- .../marketplace-show-controls.component.html | 13 +- .../marketplace-show-controls.component.ts | 12 +- .../marketplace-show.module.ts | 2 + .../server-routes/sideload/sideload.page.ts | 6 +- .../src/app/pages/updates/updates.module.ts | 3 - .../src/app/pages/updates/updates.page.html | 4 +- .../ui/src/app/pages/updates/updates.page.ts | 14 +- web/projects/ui/src/app/pipes/ui/ui.pipe.ts | 3 +- .../ui/src/app/services/api/api.fixures.ts | 419 +----------------- .../ui/src/app/services/api/api.types.ts | 3 +- .../services/api/embassy-mock-api.service.ts | 10 +- .../ui/src/app/services/api/mock-patch.ts | 22 +- .../ui/src/app/services/dep-error.service.ts | 11 +- .../src/app/services/patch-db/data-model.ts | 132 +----- .../services/pkg-status-rendering.service.ts | 20 +- web/projects/ui/src/app/util/dry-update.ts | 2 +- .../ui/src/app/util/get-package-data.ts | 2 +- web/projects/ui/src/app/util/has-deps.ts | 10 +- 43 files changed, 279 insertions(+), 834 deletions(-) delete mode 100644 web/projects/marketplace/src/pipes/mime-type.pipe.ts diff --git a/sdk/lib/StartSdk.ts b/sdk/lib/StartSdk.ts index 77c791f55..59a8715a6 100644 --- a/sdk/lib/StartSdk.ts +++ b/sdk/lib/StartSdk.ts @@ -23,7 +23,6 @@ import { PackageId, EnsureStorePath, ExtractStore, - DaemonReturned, ValidIfNoStupidEscape, } from "./types" import * as patterns from "./util/patterns" @@ -205,7 +204,8 @@ export class StartSdk { | Config, Type extends Record = ExtractConfigType, >( - metaData: Omit & { + id: string, + metadata: Omit & { input: Config | Config }, fn: (options: { @@ -213,8 +213,13 @@ export class StartSdk { input: Type }) => Promise, ) => { - const { input, ...rest } = metaData - return createAction(rest, fn, input) + const { input, ...rest } = metadata + return createAction( + id, + rest, + fn, + input, + ) }, getSystemSmtp: (effects: E) => removeConstType()(new GetSystemSmtp(effects)), @@ -236,7 +241,8 @@ export class StartSdk { | Config, Type extends Record = ExtractConfigType, >( - metaData: (options: { + id: string, + metadata: (options: { effects: Effects }) => MaybePromise>, fn: (options: { @@ -246,7 +252,8 @@ export class StartSdk { input: Config | Config, ) => { return createAction( - metaData, + id, + metadata, fn, input, ) diff --git a/sdk/lib/actions/createAction.ts b/sdk/lib/actions/createAction.ts index dc52a658a..2fe4dfa74 100644 --- a/sdk/lib/actions/createAction.ts +++ b/sdk/lib/actions/createAction.ts @@ -15,7 +15,8 @@ export class CreatedAction< Type extends Record = ExtractConfigType, > { private constructor( - public readonly myMetaData: MaybeFn< + public readonly id: string, + public readonly myMetadata: MaybeFn< Manifest, Store, Omit @@ -37,12 +38,14 @@ export class CreatedAction< | Config, Type extends Record = ExtractConfigType, >( - metaData: MaybeFn>, + id: string, + metadata: MaybeFn>, fn: (options: { effects: Effects; input: Type }) => Promise, inputConfig: Config | Config, ) { return new CreatedAction( - metaData, + id, + metadata, fn, inputConfig as Config, ) @@ -62,15 +65,15 @@ export class CreatedAction< }) } - async metaData(options: { effects: Effects }) { - if (this.myMetaData instanceof Function) - return await this.myMetaData(options) - return this.myMetaData + async metadata(options: { effects: Effects }) { + if (this.myMetadata instanceof Function) + return await this.myMetadata(options) + return this.myMetadata } async ActionMetadata(options: { effects: Effects }): Promise { return { - ...(await this.metaData(options)), + ...(await this.metadata(options)), input: await this.input.build(options), } } diff --git a/sdk/lib/actions/setupActions.ts b/sdk/lib/actions/setupActions.ts index 035f8dafa..9dd9937b4 100644 --- a/sdk/lib/actions/setupActions.ts +++ b/sdk/lib/actions/setupActions.ts @@ -1,6 +1,5 @@ import { SDKManifest } from "../manifest/ManifestTypes" import { Effects, ExpectedExports } from "../types" -import { once } from "../util/once" import { CreatedAction } from "./createAction" export function setupActions( @@ -9,8 +8,7 @@ export function setupActions( const myActions = async (options: { effects: Effects }) => { const actions: Record> = {} for (const action of createdActions) { - const actionMetadata = await action.metaData(options) - actions[actionMetadata.id] = action + actions[action.id] = action } return actions } diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts index c6ab16e64..5796a9fcc 100644 --- a/sdk/lib/types.ts +++ b/sdk/lib/types.ts @@ -163,9 +163,10 @@ export type DaemonReturned = { export type ActionMetadata = { name: string description: string - id: string + warning: string | null input: InputSpec - allowedStatuses: "only-running" | "only-stopped" | "any" | "disabled" + disabled: boolean + allowedStatuses: "only-running" | "only-stopped" | "any" /** * So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions */ diff --git a/web/projects/marketplace/src/pages/list/item/item.component.html b/web/projects/marketplace/src/pages/list/item/item.component.html index 0220a0a0c..9a88d0869 100644 --- a/web/projects/marketplace/src/pages/list/item/item.component.html +++ b/web/projects/marketplace/src/pages/list/item/item.component.html @@ -1,6 +1,6 @@ - +

diff --git a/web/projects/marketplace/src/pages/list/item/item.module.ts b/web/projects/marketplace/src/pages/list/item/item.module.ts index dd157174e..3f943ac2d 100644 --- a/web/projects/marketplace/src/pages/list/item/item.module.ts +++ b/web/projects/marketplace/src/pages/list/item/item.module.ts @@ -4,17 +4,10 @@ import { IonicModule } from '@ionic/angular' import { RouterModule } from '@angular/router' import { SharedPipesModule } from '@start9labs/shared' import { ItemComponent } from './item.component' -import { MimeTypePipeModule } from '../../../pipes/mime-type.pipe' @NgModule({ declarations: [ItemComponent], exports: [ItemComponent], - imports: [ - CommonModule, - IonicModule, - RouterModule, - SharedPipesModule, - MimeTypePipeModule, - ], + imports: [CommonModule, IonicModule, RouterModule, SharedPipesModule], }) export class ItemModule {} diff --git a/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.html b/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.html index 87b902603..e5711b432 100644 --- a/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.html +++ b/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.html @@ -18,15 +18,11 @@

{{ pkg['dependency-metadata'][dep.key].title }} - - (required) - (required by default) + (optional) + (optional) - +

-

- {{ dep.value.version | displayEmver }} -

{{ dep.value.description }}

diff --git a/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.ts b/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.ts index a6ecb103f..0626f4817 100644 --- a/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.ts +++ b/web/projects/marketplace/src/pages/show/dependencies/dependencies.component.ts @@ -11,7 +11,6 @@ export class DependenciesComponent { pkg!: MarketplacePkg getImg(key: string): string { - // @TODO fix when registry api is updated to include mimetype in icon url - return 'data:image/png;base64,' + this.pkg['dependency-metadata'][key].icon + return this.pkg['dependency-metadata'][key].icon } } diff --git a/web/projects/marketplace/src/pages/show/package/package.component.html b/web/projects/marketplace/src/pages/show/package/package.component.html index ca853030e..1e0ad89eb 100644 --- a/web/projects/marketplace/src/pages/show/package/package.component.html +++ b/web/projects/marketplace/src/pages/show/package/package.component.html @@ -1,5 +1,5 @@
- +

{{ pkg.manifest.title }}

{{ pkg.manifest.version | displayEmver }}

diff --git a/web/projects/marketplace/src/pages/show/package/package.module.ts b/web/projects/marketplace/src/pages/show/package/package.module.ts index 665e146b5..502ee82bb 100644 --- a/web/projects/marketplace/src/pages/show/package/package.module.ts +++ b/web/projects/marketplace/src/pages/show/package/package.module.ts @@ -8,7 +8,6 @@ import { } from '@start9labs/shared' import { PackageComponent } from './package.component' -import { MimeTypePipeModule } from '../../../pipes/mime-type.pipe' @NgModule({ declarations: [PackageComponent], @@ -19,7 +18,6 @@ import { MimeTypePipeModule } from '../../../pipes/mime-type.pipe' SharedPipesModule, EmverPipesModule, TickerModule, - MimeTypePipeModule, ], }) export class PackageModule {} diff --git a/web/projects/marketplace/src/pipes/mime-type.pipe.ts b/web/projects/marketplace/src/pipes/mime-type.pipe.ts deleted file mode 100644 index a0dc14a00..000000000 --- a/web/projects/marketplace/src/pipes/mime-type.pipe.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NgModule, Pipe, PipeTransform } from '@angular/core' -import { MarketplacePkg } from '../types' - -@Pipe({ - name: 'mimeType', -}) -export class MimeTypePipe implements PipeTransform { - transform(pkg: MarketplacePkg): string { - if (pkg.manifest.assets.icon) { - switch (pkg.manifest.assets.icon.split('.').pop()) { - case 'png': - return `data:image/png;base64,${pkg.icon}` - case 'jpeg': - case 'jpg': - return `data:image/jpeg;base64,${pkg.icon}` - case 'gif': - return `data:image/gif;base64,${pkg.icon}` - case 'svg': - return `data:image/svg+xml;base64,${pkg.icon}` - default: - return `data:image/png;base64,${pkg.icon}` - } - } - return `data:image/png;base64,${pkg.icon}` - } -} - -@NgModule({ - declarations: [MimeTypePipe], - exports: [MimeTypePipe], -}) -export class MimeTypePipeModule {} diff --git a/web/projects/marketplace/src/public-api.ts b/web/projects/marketplace/src/public-api.ts index fef451a3e..b9e8c4686 100644 --- a/web/projects/marketplace/src/public-api.ts +++ b/web/projects/marketplace/src/public-api.ts @@ -22,7 +22,6 @@ export * from './pages/show/package/package.component' export * from './pages/show/package/package.module' export * from './pipes/filter-packages.pipe' -export * from './pipes/mime-type.pipe' export * from './services/marketplace.service' diff --git a/web/projects/marketplace/src/types.ts b/web/projects/marketplace/src/types.ts index 8187d3bc0..7007ee806 100644 --- a/web/projects/marketplace/src/types.ts +++ b/web/projects/marketplace/src/types.ts @@ -19,11 +19,13 @@ export interface StoreInfo { categories: string[] } +export type StoreIdentityWithData = StoreData & StoreIdentity + export interface MarketplacePkg { icon: Url license: Url instructions: Url - manifest: MarketplaceManifest + manifest: Manifest categories: string[] versions: string[] 'dependency-metadata': { @@ -35,10 +37,11 @@ export interface MarketplacePkg { export interface DependencyMetadata { title: string icon: Url + optional: boolean hidden: boolean } -export interface MarketplaceManifest { +export interface Manifest { id: string title: string version: string @@ -47,12 +50,9 @@ export interface MarketplaceManifest { short: string long: string } - assets: { - icon: string // ie. icon.png - } replaces?: string[] 'release-notes': string - license: string // type of license + license: string // name of license 'wrapper-repo': Url 'upstream-repo': Url 'support-site': Url @@ -65,23 +65,12 @@ export interface MarketplaceManifest { start: string | null stop: string | null } - dependencies: Record> + dependencies: Record + 'os-version': string + 'has-config': boolean } -export interface Dependency { - version: string - requirement: - | { - type: 'opt-in' - how: string - } - | { - type: 'opt-out' - how: string - } - | { - type: 'required' - } +export interface Dependency { description: string | null - config: T + optional: boolean } diff --git a/web/projects/ui/src/app/modals/app-config/app-config.page.ts b/web/projects/ui/src/app/modals/app-config/app-config.page.ts index c8f2186f0..770ade4a2 100644 --- a/web/projects/ui/src/app/modals/app-config/app-config.page.ts +++ b/web/projects/ui/src/app/modals/app-config/app-config.page.ts @@ -78,7 +78,7 @@ export class AppConfigPage { this.pkg = pkg - if (!this.pkg['state-info'].manifest.config) return + if (!this.pkg['state-info'].manifest['has-config']) return let newConfig: object | undefined let patch: Operation[] | undefined @@ -152,7 +152,7 @@ export class AppConfigPage { this.saving = true - if (hasCurrentDeps(this.pkg)) { + if (hasCurrentDeps(this.pkgId, await getAllPackages(this.patch))) { this.dryConfigure() } else { this.configure() diff --git a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html index c611f4a71..bb5374856 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html @@ -9,9 +9,7 @@ - + Standard Actions - - Actions for {{ manifest.title }} + + Actions for {{ pkg['state-info'].manifest.title }} this.fixDep(manifest, 'install', depId) + fixAction = () => this.fixDep(pkg, manifest, 'install', depId) } else if (depError.type === DependencyErrorType.IncorrectVersion) { errorText = 'Incorrect version' fixText = 'Update' - fixAction = () => this.fixDep(manifest, 'update', depId) + fixAction = () => this.fixDep(pkg, manifest, 'update', depId) } else if (depError.type === DependencyErrorType.ConfigUnsatisfied) { errorText = 'Config not satisfied' fixText = 'Auto config' - fixAction = () => this.fixDep(manifest, 'configure', depId) + fixAction = () => this.fixDep(pkg, manifest, 'configure', depId) } else if (depError.type === DependencyErrorType.NotRunning) { errorText = 'Not running' fixText = 'Start' @@ -160,6 +162,7 @@ export class AppShowPage { } private async fixDep( + pkg: PackageDataEntry, pkgManifest: Manifest, action: 'install' | 'update' | 'configure', id: string, @@ -167,22 +170,21 @@ export class AppShowPage { switch (action) { case 'install': case 'update': - return this.installDep(pkgManifest, id) + return this.installDep(pkg, pkgManifest, id) case 'configure': return this.configureDep(pkgManifest, id) } } private async installDep( + pkg: PackageDataEntry, pkgManifest: Manifest, depId: string, ): Promise { - const version = pkgManifest.dependencies[depId].version - const dependentInfo: DependentInfo = { id: pkgManifest.id, title: pkgManifest.title, - version, + version: pkg['current-dependencies'][depId].versionRange, } const navigationExtras: NavigationExtras = { state: { dependentInfo }, diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-additional/app-show-additional.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-additional/app-show-additional.component.ts index 3487093b5..26281bb80 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-additional/app-show-additional.component.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-additional/app-show-additional.component.ts @@ -3,7 +3,7 @@ import { ModalController, ToastController } from '@ionic/angular' import { copyToClipboard, MarkdownComponent } from '@start9labs/shared' import { from } from 'rxjs' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { Manifest } from 'src/app/services/patch-db/data-model' +import { Manifest } from '@start9labs/marketplace' @Component({ selector: 'app-show-additional', diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html index 9ea8d29ac..3668adc7b 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html @@ -1,91 +1,81 @@ - - - Health Checks - - - - - - - - - - -

- {{ manifest['health-checks'][health.key].name }} -

- -

- {{ result | titlecase }} - ... - - {{ $any(health.value).error }} - - - {{ $any(health.value).message }} - - - : - {{ manifest['health-checks'][health.key]['success-message'] }} - -

-
-
-
- - - - -

- {{ manifest['health-checks'][health.key].name }} -

-

Awaiting result...

-
-
-
-
- - - - - - + + Health Checks + + + + + + + + + - - +

+ {{ check.value.name }} +

+ +

+ {{ result | titlecase }} + ... + + {{ check.value.message }} + +

+
-
-
+
+ + + + +

+ {{ check.value.name }} +

+

Awaiting result...

+
+
+
+ + + + + + + + + + + +
diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.ts index a6cbc4ab7..5ecdf9a0f 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.ts @@ -1,6 +1,11 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ConnectionService } from 'src/app/services/connection.service' -import { HealthResult, Manifest } from 'src/app/services/patch-db/data-model' +import { + HealthCheckResult, + HealthResult, + MainStatus, +} from 'src/app/services/patch-db/data-model' +import { Manifest } from '@start9labs/marketplace' @Component({ selector: 'app-show-health-checks', @@ -10,9 +15,7 @@ import { HealthResult, Manifest } from 'src/app/services/patch-db/data-model' }) export class AppShowHealthChecksComponent { @Input() - manifest!: Manifest - - HealthResult = HealthResult + healthChecks!: Record readonly connected$ = this.connectionService.connected$ diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts index 9bca7f2ac..6f26d2eb6 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts @@ -6,10 +6,9 @@ import { PrimaryStatus, } from 'src/app/services/pkg-status-rendering.service' import { - Manifest, + DataModel, PackageDataEntry, PackageMainStatus, - PackageState, Status, } from 'src/app/services/patch-db/data-model' import { ErrorToastService } from '@start9labs/shared' @@ -18,7 +17,13 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' import { ModalService } from 'src/app/services/modal.service' import { hasCurrentDeps } from 'src/app/util/has-deps' import { ConnectionService } from 'src/app/services/connection.service' -import { isInstalled, getManifest } from 'src/app/util/get-package-data' +import { + isInstalled, + getManifest, + getAllPackages, +} from 'src/app/util/get-package-data' +import { Manifest } from '@start9labs/marketplace' +import { PatchDB } from 'patch-db-client' @Component({ selector: 'app-show-status', @@ -47,6 +52,7 @@ export class AppShowStatusComponent { private readonly launcherService: UiLauncherService, private readonly modalService: ModalService, private readonly connectionService: ConnectionService, + private readonly patch: PatchDB, ) {} get interfaces(): PackageDataEntry['service-interfaces'] { @@ -116,7 +122,7 @@ export class AppShowStatusComponent { const { title, alerts } = this.manifest let message = alerts.stop || '' - if (hasCurrentDeps(this.pkg)) { + if (hasCurrentDeps(this.manifest.id, await getAllPackages(this.patch))) { const depMessage = `Services that depend on ${title} will no longer work properly and may crash` message = message ? `${message}.\n\n${depMessage}` : depMessage } @@ -148,7 +154,7 @@ export class AppShowStatusComponent { } async tryRestart(): Promise { - if (hasCurrentDeps(this.pkg)) { + if (hasCurrentDeps(this.manifest.id, await getAllPackages(this.patch))) { const alert = await this.alertCtrl.create({ header: 'Warning', message: `Services that depend on ${this.manifest.title} may temporarily experiences issues`, diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts index a25914367..2eda6f296 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts @@ -5,14 +5,13 @@ import { MarkdownComponent } from '@start9labs/shared' import { DataModel, InstalledState, - Manifest, PackageDataEntry, } from 'src/app/services/patch-db/data-model' import { ModalService } from 'src/app/services/modal.service' import { ApiService } from 'src/app/services/api/embassy-api.service' import { from, map, Observable } from 'rxjs' import { PatchDB } from 'patch-db-client' -import { getManifest } from 'src/app/util/get-package-data' +import { Manifest } from '@start9labs/marketplace' export interface Button { title: string diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts index 066411eb0..e099cb356 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts @@ -2,13 +2,13 @@ import { Pipe, PipeTransform } from '@angular/core' import { DataModel, HealthCheckResult, - Manifest, PackageMainStatus, } from 'src/app/services/patch-db/data-model' import { isEmptyObject } from '@start9labs/shared' import { map, startWith } from 'rxjs/operators' import { PatchDB } from 'patch-db-client' import { Observable } from 'rxjs' +import { Manifest } from '@start9labs/marketplace' @Pipe({ name: 'toHealthChecks', @@ -18,26 +18,17 @@ export class ToHealthChecksPipe implements PipeTransform { transform( manifest: Manifest, - ): Observable> | null { - const healthChecks = Object.keys(manifest['health-checks']).reduce( - (obj, key) => ({ ...obj, [key]: null }), - {}, - ) - - const healthChecks$ = this.patch + ): Observable | null> { + return this.patch .watch$('package-data', manifest.id, 'status', 'main') .pipe( map(main => { - // Question: is this ok or do we have to use Object.keys - // to maintain order and the keys initially present in pkg? return main.status === PackageMainStatus.Running && !isEmptyObject(main.health) ? main.health - : healthChecks + : null }), - startWith(healthChecks), + startWith(null), ) - - return isEmptyObject(healthChecks) ? null : healthChecks$ } } diff --git a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html index 8d347ffa2..a1edbb593 100644 --- a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html +++ b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.html @@ -8,9 +8,14 @@ View Installed - + , ) {} - get localVersion(): string { - return this.localPkg ? getManifest(this.localPkg).version : '' - } - async tryInstall() { const currentMarketplace = await firstValueFrom( this.marketplaceService.getSelectedHost$(), @@ -80,10 +76,12 @@ export class MarketplaceShowControlsComponent { if (!proceed) return } + const localManifest = getManifest(this.localPkg) + if ( - this.emver.compare(this.localVersion, this.pkg.manifest.version) !== + this.emver.compare(localManifest.version, this.pkg.manifest.version) !== 0 && - hasCurrentDeps(this.localPkg) + hasCurrentDeps(localManifest.id, await getAllPackages(this.patch)) ) { this.dryInstall(url) } else { diff --git a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.module.ts b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.module.ts index 3cef27e61..058cbb158 100644 --- a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.module.ts +++ b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show.module.ts @@ -19,6 +19,7 @@ import { MarketplaceShowPage } from './marketplace-show.page' import { MarketplaceShowHeaderComponent } from './marketplace-show-header/marketplace-show-header.component' import { MarketplaceShowDependentComponent } from './marketplace-show-dependent/marketplace-show-dependent.component' import { MarketplaceShowControlsComponent } from './marketplace-show-controls/marketplace-show-controls.component' +import { UiPipeModule } from 'src/app/pipes/ui/ui.module' const routes: Routes = [ { @@ -41,6 +42,7 @@ const routes: Routes = [ AboutModule, DependenciesModule, AdditionalModule, + UiPipeModule, ], declarations: [ MarketplaceShowPage, diff --git a/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts b/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts index baaf5a9ec..b834d084c 100644 --- a/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core' import { isPlatform, LoadingController, NavController } from '@ionic/angular' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { Manifest } from 'src/app/services/patch-db/data-model' import { ConfigService } from 'src/app/services/config.service' import cbor from 'cbor' import { ErrorToastService } from '@start9labs/shared' +import { Manifest } from '@start9labs/marketplace' interface Positions { [key: string]: [bigint, bigint] // [position, length] @@ -133,9 +133,7 @@ export class SideloadPage { } async getIcon(positions: Positions, file: Blob) { - const contentType = `image/${this.toUpload.manifest?.assets.icon - .split('.') - .pop()}` + const contentType = '' // @TODO const data = file.slice( Number(positions['icon'][0]), Number(positions['icon'][0]) + Number(positions['icon'][1]), diff --git a/web/projects/ui/src/app/pages/updates/updates.module.ts b/web/projects/ui/src/app/pages/updates/updates.module.ts index 62a6d2076..7b6c6594c 100644 --- a/web/projects/ui/src/app/pages/updates/updates.module.ts +++ b/web/projects/ui/src/app/pages/updates/updates.module.ts @@ -5,7 +5,6 @@ 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 { - EmverDisplayPipe, EmverPipesModule, MarkdownPipeModule, SharedPipesModule, @@ -14,7 +13,6 @@ import { SkeletonListComponentModule } from 'src/app/components/skeleton-list/sk import { RoundProgressModule } from 'angular-svg-round-progressbar' import { InstallingProgressPipeModule } from 'src/app/pipes/install-progress/install-progress.module' import { StoreIconComponentModule } from 'src/app/components/store-icon/store-icon.component.module' -import { MimeTypePipeModule } from '@start9labs/marketplace' const routes: Routes = [ { @@ -37,7 +35,6 @@ const routes: Routes = [ InstallingProgressPipeModule, StoreIconComponentModule, EmverPipesModule, - MimeTypePipeModule, ], }) export class UpdatesPageModule {} diff --git a/web/projects/ui/src/app/pages/updates/updates.page.html b/web/projects/ui/src/app/pages/updates/updates.page.html index 4ee0d53f4..c31294920 100644 --- a/web/projects/ui/src/app/pages/updates/updates.page.html +++ b/web/projects/ui/src/app/pages/updates/updates.page.html @@ -33,7 +33,7 @@ - +

{{ pkg.manifest.title }}

@@ -72,7 +72,7 @@ > diff --git a/web/projects/ui/src/app/pages/updates/updates.page.ts b/web/projects/ui/src/app/pages/updates/updates.page.ts index e09dc300d..20feabd0b 100644 --- a/web/projects/ui/src/app/pages/updates/updates.page.ts +++ b/web/projects/ui/src/app/pages/updates/updates.page.ts @@ -10,7 +10,7 @@ import { MarketplaceService } from 'src/app/services/marketplace.service' import { AbstractMarketplaceService, Marketplace, - MarketplaceManifest, + Manifest, MarketplacePkg, StoreIdentity, } from '@start9labs/marketplace' @@ -70,12 +70,7 @@ export class UpdatesPage { }) } - async tryUpdate( - manifest: MarketplaceManifest, - url: string, - local: PackageDataEntry, - e: Event, - ): Promise { + async tryUpdate(manifest: Manifest, url: string, e: Event): Promise { e.stopPropagation() const { id, version } = manifest @@ -83,14 +78,15 @@ export class UpdatesPage { delete this.marketplaceService.updateErrors[id] this.marketplaceService.updateQueue[id] = true - if (hasCurrentDeps(local)) { + // manifest.id OK because same as local id for update + if (hasCurrentDeps(manifest.id, await getAllPackages(this.patch))) { this.dryInstall(manifest, url) } else { this.install(id, version, url) } } - private async dryInstall(manifest: MarketplaceManifest, url: string) { + private async dryInstall(manifest: Manifest, url: string) { const { id, version, title } = manifest const breakages = dryUpdate( diff --git a/web/projects/ui/src/app/pipes/ui/ui.pipe.ts b/web/projects/ui/src/app/pipes/ui/ui.pipe.ts index 2a81e4afd..0c9bab5df 100644 --- a/web/projects/ui/src/app/pipes/ui/ui.pipe.ts +++ b/web/projects/ui/src/app/pipes/ui/ui.pipe.ts @@ -1,7 +1,8 @@ import { Pipe, PipeTransform } from '@angular/core' -import { Manifest, PackageDataEntry } from '../../services/patch-db/data-model' +import { PackageDataEntry } from '../../services/patch-db/data-model' import { hasUi } from '../../services/config.service' import { getManifest } from 'src/app/util/get-package-data' +import { Manifest } from '@start9labs/marketplace' @Pipe({ name: 'hasUi', diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index 22e6a3b32..8f6a907bc 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -1,16 +1,17 @@ import { - DockerIoFormat, InstalledState, - Manifest, PackageDataEntry, PackageMainStatus, PackageState, ServerStatusInfo, } 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 { + DependencyMetadata, + Manifest, + MarketplacePkg, +} from '@start9labs/marketplace' import { Log } from '@start9labs/shared' export module Mock { @@ -57,14 +58,6 @@ export module Mock { }, replaces: ['banks', 'governments'], 'release-notes': 'Taproot, Schnorr, and more.', - assets: { - icon: 'icon.png', - license: 'LICENSE.md', - instructions: 'INSTRUCTIONS.md', - docker_images: 'image.tar', - assets: './assets', - scripts: './scripts', - }, license: 'MIT', 'wrapper-repo': 'https://github.com/start9labs/bitcoind-wrapper', 'upstream-repo': 'https://github.com/bitcoin/bitcoin', @@ -79,231 +72,9 @@ export module Mock { start: 'Starting Bitcoin is good for your health.', stop: null, }, - 'health-checks': {}, - config: { - get: null, - set: null, - }, - volumes: {}, - 'min-os-version': '0.2.12', - backup: { - create: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - restore: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - }, - migrations: null, - actions: { - resync: { - name: 'Resync Blockchain', - description: 'Use this to resync the Bitcoin blockchain from genesis', - warning: 'This will take a couple of days.', - 'allowed-statuses': [ - PackageMainStatus.Running, - PackageMainStatus.Stopped, - ], - implementation: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - 'input-spec': { - reason: { - type: 'string', - name: 'Re-sync Reason', - description: 'Your reason for re-syncing. Why are you doing this?', - nullable: false, - masked: false, - copyable: false, - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'Must contain only letters.', - }, - name: { - type: 'string', - name: 'Your Name', - description: 'Tell the class your name.', - nullable: true, - masked: false, - copyable: false, - warning: 'You may loose all your money by providing your name.', - }, - notifications: { - name: 'Notification Preferences', - type: 'list', - subtype: 'enum', - description: 'how you want to be notified', - range: '[1,3]', - default: ['email'], - spec: { - 'value-names': { - email: 'Email', - text: 'Text', - call: 'Call', - push: 'Push', - webhook: 'Webhook', - }, - values: ['email', 'text', 'call', 'push', 'webhook'], - }, - }, - 'days-ago': { - type: 'number', - name: 'Days Ago', - description: 'Number of days to re-sync.', - nullable: false, - default: 100, - range: '[0, 9999]', - integral: true, - }, - 'top-speed': { - type: 'number', - name: 'Top Speed', - description: 'The fastest you can possibly run.', - nullable: false, - range: '[-1000, 1000]', - integral: false, - units: 'm/s', - }, - testnet: { - name: 'Testnet', - type: 'boolean', - description: - '
  • determines whether your node is running on testnet or mainnet
', - warning: 'Chain will have to resync!', - default: false, - }, - randomEnum: { - name: 'Random Enum', - type: 'enum', - 'value-names': { - null: 'Null', - good: 'Good', - bad: 'Bad', - ugly: 'Ugly', - }, - default: 'null', - description: 'This is not even real.', - warning: 'Be careful changing this!', - values: ['null', 'good', 'bad', 'ugly'], - }, - 'emergency-contact': { - name: 'Emergency Contact', - type: 'object', - description: 'The person to contact in case of emergency.', - spec: { - name: { - type: 'string', - name: 'Name', - nullable: false, - masked: false, - copyable: false, - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'Must contain only letters.', - }, - email: { - type: 'string', - name: 'Email', - nullable: false, - masked: false, - copyable: true, - }, - }, - }, - ips: { - name: 'Whitelist IPs', - type: 'list', - subtype: 'string', - description: - 'external ip addresses that are authorized to access your Bitcoin node', - warning: - 'Any IP you allow here will have RPC access to your Bitcoin node.', - range: '[1,10]', - default: ['192.168.1.1'], - spec: { - pattern: '^[0-9]{1,3}([,.][0-9]{1,3})?$', - 'pattern-description': 'Must be a valid IP address', - masked: false, - copyable: false, - }, - }, - bitcoinNode: { - type: 'union', - default: 'internal', - tag: { - id: 'type', - 'variant-names': { - internal: 'Internal', - external: 'External', - }, - name: 'Bitcoin Node Settings', - description: 'The node settings', - warning: 'Careful changing this', - }, - variants: { - internal: { - 'lan-address': { - name: 'LAN Address', - type: 'pointer', - subtype: 'package', - target: 'lan-address', - description: 'the lan address', - interface: 'tor-address', - 'package-id': '12341234', - }, - 'friendly-name': { - name: 'Friendly Name', - type: 'string', - description: 'the lan address', - nullable: true, - masked: false, - copyable: false, - }, - }, - external: { - 'public-domain': { - name: 'Public Domain', - type: 'string', - description: 'the public address of the node', - nullable: false, - default: 'bitcoinnode.com', - pattern: '.*', - 'pattern-description': 'anything', - masked: false, - copyable: true, - }, - }, - }, - }, - }, - }, - }, + 'os-version': '0.2.12', dependencies: {}, + 'has-config': true, } export const MockManifestLnd: Manifest = { @@ -315,14 +86,6 @@ export module Mock { long: 'More info about LND. More info about LND. More info about LND.', }, 'release-notes': 'Dual funded channels!', - assets: { - icon: 'icon.png', - license: 'LICENSE.md', - instructions: 'INSTRUCTIONS.md', - docker_images: 'image.tar', - assets: './assets', - scripts: './scripts', - }, license: 'MIT', 'wrapper-repo': 'https://github.com/start9labs/lnd-wrapper', 'upstream-repo': 'https://github.com/lightningnetwork/lnd', @@ -337,82 +100,19 @@ export module Mock { start: 'Starting LND is good for your health.', stop: null, }, - 'health-checks': {}, - config: { - get: null, - set: null, - }, - volumes: {}, - 'min-os-version': '0.2.12', - backup: { - create: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - restore: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - }, - migrations: null, - actions: { - resync: { - name: 'Resync Network Graph', - description: 'Your node will resync its network graph.', - warning: 'This will take a couple hours.', - 'allowed-statuses': [PackageMainStatus.Running], - implementation: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - 'input-spec': null, - }, - }, + 'os-version': '0.2.12', dependencies: { bitcoind: { - version: '=0.21.0', description: 'LND needs bitcoin to live.', - requirement: { - type: 'opt-out', - how: 'You can use an external node from your server if you prefer.', - }, - config: null, + optional: true, }, 'btc-rpc-proxy': { - version: '>=0.2.2', description: 'As long as Bitcoin is pruned, LND needs Bitcoin Proxy to fetch block over the P2P network.', - requirement: { - type: 'opt-in', - how: `To use Proxy's user management system, go to LND config and select Bitcoin Proxy under Bitcoin config.`, - }, - config: null, + optional: true, }, }, + 'has-config': true, } export const MockManifestBitcoinProxy: Manifest = { @@ -425,14 +125,6 @@ export module Mock { long: 'More info about Bitcoin Proxy. More info about Bitcoin Proxy. More info about Bitcoin Proxy.', }, 'release-notes': 'Even better support for Bitcoin and wallets!', - assets: { - icon: 'icon.png', - license: 'LICENSE.md', - instructions: 'INSTRUCTIONS.md', - docker_images: 'image.tar', - assets: './assets', - scripts: './scripts', - }, license: 'MIT', 'wrapper-repo': 'https://github.com/start9labs/btc-rpc-proxy-wrapper', 'upstream-repo': 'https://github.com/Kixunil/btc-rpc-proxy', @@ -446,84 +138,27 @@ export module Mock { start: null, stop: null, }, - 'health-checks': {}, - config: { get: {} as any, set: {} as any }, - volumes: {}, - 'min-os-version': '0.2.12', - backup: { - create: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - restore: { - type: 'docker', - image: '', - system: true, - entrypoint: '', - args: [''], - mounts: {}, - 'io-format': DockerIoFormat.Yaml, - inject: false, - 'shm-size': '', - 'sigterm-timeout': null, - }, - }, - migrations: null, - actions: {}, + 'os-version': '0.2.12', dependencies: { bitcoind: { - version: '>=0.20.0', description: 'Bitcoin Proxy requires a Bitcoin node.', - requirement: { - type: 'required', - }, - config: { - check: { - type: 'docker', - image: 'alpine', - system: true, - entrypoint: 'true', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Cbor, - inject: false, - 'shm-size': '10m', - 'sigterm-timeout': null, - }, - 'auto-configure': { - type: 'docker', - image: 'alpine', - system: true, - entrypoint: 'cat', - args: [], - mounts: {}, - 'io-format': DockerIoFormat.Cbor, - inject: false, - 'shm-size': '10m', - 'sigterm-timeout': null, - }, - }, + optional: false, }, }, + 'has-config': false, } export const BitcoinDep: DependencyMetadata = { title: 'Bitcoin Core', icon: BTC_ICON, + optional: false, hidden: true, } export const ProxyDep: DependencyMetadata = { title: 'Bitcoin Proxy', icon: PROXY_ICON, + optional: true, hidden: false, } @@ -1765,6 +1400,7 @@ export module Mock { }, 'dependency-config-errors': {}, }, + actions: {}, // @TODO need mocks 'service-interfaces': { ui: { id: 'ui', @@ -1981,12 +1617,6 @@ export module Mock { }, }, }, - 'current-dependents': { - lnd: { - pointers: [], - 'health-checks': [], - }, - }, 'current-dependencies': {}, 'dependency-info': {}, 'marketplace-url': 'https://registry.start9.com/', @@ -2007,6 +1637,7 @@ export module Mock { }, 'dependency-config-errors': {}, }, + actions: {}, 'service-interfaces': { ui: { id: 'ui', @@ -2115,15 +1746,9 @@ export module Mock { }, }, }, - 'current-dependents': { - lnd: { - pointers: [], - 'health-checks': [], - }, - }, 'current-dependencies': { bitcoind: { - pointers: [], + versionRange: '>=26.0.0', 'health-checks': [], }, }, @@ -2153,6 +1778,7 @@ export module Mock { 'btc-rpc-proxy': 'Username not found', }, }, + actions: {}, 'service-interfaces': { grpc: { id: 'grpc', @@ -2365,14 +1991,13 @@ export module Mock { }, }, }, - 'current-dependents': {}, 'current-dependencies': { bitcoind: { - pointers: [], + versionRange: '>=26.0.0', 'health-checks': [], }, 'btc-rpc-proxy': { - pointers: [], + versionRange: '>2.0.0', // @TODO 'health-checks': [], }, }, diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index c46cee61d..7dc83e2e6 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -1,11 +1,10 @@ import { Dump, Revision } from 'patch-db-client' -import { MarketplacePkg, StoreInfo } from '@start9labs/marketplace' +import { Manifest, MarketplacePkg, StoreInfo } from '@start9labs/marketplace' import { PackagePropertiesVersioned } from 'src/app/util/properties.util' import { ConfigSpec } from 'src/app/pkg-config/config-types' import { DataModel, HealthCheckResult, - Manifest, } from 'src/app/services/patch-db/data-model' import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared' diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 87c243628..deabffdd2 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -571,7 +571,7 @@ export class MockApiService extends ApiService { setTimeout(async () => { for (let i = 0; i < ids.length; i++) { const id = ids[i] - const appPath = `/package-data/${id}/installed/status/main/status` + const appPath = `/package-data/${id}/status/main/status` const appPatch = [ { op: PatchOp.REPLACE, @@ -735,7 +735,7 @@ export class MockApiService extends ApiService { const patch = [ { op: PatchOp.REPLACE, - path: `/package-data/${params.id}/installed/status/configured`, + path: `/package-data/${params.id}/status/configured`, value: true, }, ] @@ -782,7 +782,7 @@ export class MockApiService extends ApiService { } async startPackage(params: RR.StartPackageReq): Promise { - const path = `/package-data/${params.id}/installed/status/main` + const path = `/package-data/${params.id}/status/main` await pauseFor(2000) @@ -865,7 +865,7 @@ export class MockApiService extends ApiService { ): Promise { // first enact stop await pauseFor(2000) - const path = `/package-data/${params.id}/installed/status/main` + const path = `/package-data/${params.id}/status/main` setTimeout(async () => { const patch2: Operation[] = [ @@ -941,7 +941,7 @@ export class MockApiService extends ApiService { async stopPackage(params: RR.StopPackageReq): Promise { await pauseFor(2000) - const path = `/package-data/${params.id}/installed/status/main` + const path = `/package-data/${params.id}/status/main` setTimeout(() => { const patch2 = [ diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index 5fd3326a9..db9f5c86b 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -1,6 +1,5 @@ import { DataModel, - DockerIoFormat, HealthResult, PackageMainStatus, PackageState, @@ -93,26 +92,33 @@ export const mockPatchData: DataModel = { started: '2021-06-14T20:49:17.774Z', health: { 'ephemeral-health-check': { + name: 'Ephemeral Health Check', result: HealthResult.Starting, }, 'chain-state': { + name: 'Chain State', result: HealthResult.Loading, message: 'Bitcoin is syncing from genesis', }, 'p2p-interface': { + name: 'P2P', result: HealthResult.Success, + message: 'Health check successful', }, 'rpc-interface': { + name: 'RPC', result: HealthResult.Failure, - error: 'RPC interface unreachable.', + message: 'RPC interface unreachable.', }, 'unnecessary-health-check': { + name: 'Unnecessary Health Check', result: HealthResult.Disabled, }, }, }, 'dependency-config-errors': {}, }, + actions: {}, // @TODO 'service-interfaces': { ui: { id: 'ui', @@ -329,12 +335,6 @@ export const mockPatchData: DataModel = { }, }, }, - 'current-dependents': { - lnd: { - pointers: [], - 'health-checks': [], - }, - }, 'current-dependencies': {}, 'dependency-info': {}, 'marketplace-url': 'https://registry.start9.com/', @@ -359,6 +359,7 @@ export const mockPatchData: DataModel = { 'btc-rpc-proxy': 'This is a config unsatisfied error', }, }, + actions: {}, 'service-interfaces': { grpc: { id: 'grpc', @@ -569,14 +570,13 @@ export const mockPatchData: DataModel = { }, }, }, - 'current-dependents': {}, 'current-dependencies': { bitcoind: { - pointers: [], + versionRange: '>=26.0.0', 'health-checks': [], }, 'btc-rpc-proxy': { - pointers: [], + versionRange: '>2.0.0', 'health-checks': [], }, }, diff --git a/web/projects/ui/src/app/services/dep-error.service.ts b/web/projects/ui/src/app/services/dep-error.service.ts index 8d0d93ead..c73beabda 100644 --- a/web/projects/ui/src/app/services/dep-error.service.ts +++ b/web/projects/ui/src/app/services/dep-error.service.ts @@ -85,19 +85,14 @@ export class DepErrorService { } } - const pkgManifest = pkg['state-info'].manifest + const versionRange = pkg['current-dependencies'][depId].versionRange const depManifest = dep['state-info'].manifest // incorrect version - if ( - !this.emver.satisfies( - depManifest.version, - pkgManifest.dependencies[depId].version, - ) - ) { + if (!this.emver.satisfies(depManifest.version, versionRange)) { return { type: DependencyErrorType.IncorrectVersion, - expected: pkgManifest.dependencies[depId].version, + expected: versionRange, received: depManifest.version, } } diff --git a/web/projects/ui/src/app/services/patch-db/data-model.ts b/web/projects/ui/src/app/services/patch-db/data-model.ts index 363fc8b89..9f90e798f 100644 --- a/web/projects/ui/src/app/services/patch-db/data-model.ts +++ b/web/projects/ui/src/app/services/patch-db/data-model.ts @@ -1,8 +1,9 @@ -import { ConfigSpec } from 'src/app/pkg-config/config-types' import { Url } from '@start9labs/shared' -import { MarketplaceManifest } from '@start9labs/marketplace' +import { Manifest } from '@start9labs/marketplace' import { BasicInfo } from 'src/app/pages/developer-routes/developer-menu/form-info' import { types } from '@start9labs/start-sdk' +import { InputSpec } from '@start9labs/start-sdk/cjs/sdk/lib/config/configTypes' +import { ActionMetadata } from '@start9labs/start-sdk/cjs/sdk/lib/types' type ServiceInterfaceWithHostInfo = types.ServiceInterfaceWithHostInfo export interface DataModel { @@ -111,8 +112,8 @@ export type PackageDataEntry = { 'state-info': T icon: Url status: Status + actions: Record 'last-backup': string | null - 'current-dependents': { [id: string]: CurrentDependencyInfo } 'current-dependencies': { [id: string]: CurrentDependencyInfo } 'dependency-info': { [id: string]: { @@ -152,125 +153,10 @@ export enum PackageState { } export interface CurrentDependencyInfo { - pointers: any[] + versionRange: string 'health-checks': string[] // array of health check IDs } -export interface Manifest extends MarketplaceManifest { - assets: { - license: string // filename - instructions: string // filename - icon: string // filename - docker_images: string // filename - assets: string // path to assets folder - scripts: string // path to scripts folder - } - 'health-checks': Record< - string, - ActionImpl & { name: string; 'success-message': string | null } - > - config: ConfigActions | null - volumes: Record - 'min-os-version': string - backup: BackupActions - migrations: Migrations | null - actions: Record -} - -export interface DependencyConfig { - check: ActionImpl - 'auto-configure': ActionImpl -} - -export interface ActionImpl { - type: 'docker' - image: string - system: boolean - entrypoint: string - args: string[] - mounts: { [id: string]: string } - 'io-format': DockerIoFormat | null - inject: boolean - 'shm-size': string - 'sigterm-timeout': string | null -} - -export enum DockerIoFormat { - Json = 'json', - Yaml = 'yaml', - Cbor = 'cbor', - Toml = 'toml', -} - -export interface ConfigActions { - get: ActionImpl | null - set: ActionImpl | null -} - -export type Volume = VolumeData - -export interface VolumeData { - type: VolumeType.Data - readonly: boolean -} - -export interface VolumeAssets { - type: VolumeType.Assets -} - -export interface VolumePointer { - type: VolumeType.Pointer - 'package-id': string - 'volume-id': string - path: string - readonly: boolean -} - -export interface VolumeCertificate { - type: VolumeType.Certificate - 'interface-id': string -} - -export interface VolumeBackup { - type: VolumeType.Backup - readonly: boolean -} - -export enum VolumeType { - Data = 'data', - Assets = 'assets', - Pointer = 'pointer', - Certificate = 'certificate', - Backup = 'backup', -} - -export interface TorConfig { - 'port-mapping': { [port: number]: number } -} - -export type LanConfig = { - [port: number]: { ssl: boolean; mapping: number } -} - -export interface BackupActions { - create: ActionImpl - restore: ActionImpl -} - -export interface Migrations { - from: { [versionRange: string]: ActionImpl } - to: { [versionRange: string]: ActionImpl } -} - -export interface Action { - name: string - description: string - warning: string | null - implementation: ActionImpl - 'allowed-statuses': (PackageMainStatus.Stopped | PackageMainStatus.Running)[] - 'input-spec': ConfigSpec | null -} - export interface Status { configured: boolean main: MainStatus @@ -302,7 +188,7 @@ export interface MainStatusStarting { export interface MainStatusRunning { status: PackageMainStatus.Running started: string // UTC date string - health: { [id: string]: HealthCheckResult } + health: Record } export interface MainStatusBackingUp { @@ -323,12 +209,13 @@ export enum PackageMainStatus { Restarting = 'restarting', } -export type HealthCheckResult = +export type HealthCheckResult = { name: string } & ( | HealthCheckResultStarting | HealthCheckResultLoading | HealthCheckResultDisabled | HealthCheckResultSuccess | HealthCheckResultFailure +) export enum HealthResult { Starting = 'starting', @@ -348,6 +235,7 @@ export interface HealthCheckResultDisabled { export interface HealthCheckResultSuccess { result: HealthResult.Success + message: string } export interface HealthCheckResultLoading { @@ -357,7 +245,7 @@ export interface HealthCheckResultLoading { export interface HealthCheckResultFailure { result: HealthResult.Failure - error: string + message: string } export type InstallingInfo = { diff --git a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts index a041b5e7c..cda895e89 100644 --- a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts +++ b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts @@ -25,10 +25,7 @@ export function renderPkgStatus( if (pkg['state-info'].state === PackageState.Installed) { primary = getPrimaryStatus(pkg.status) dependency = getDependencyStatus(depErrors) - health = getHealthStatus( - pkg.status, - !isEmptyObject(pkg['state-info'].manifest['health-checks']), - ) + health = getHealthStatus(pkg.status) } else { primary = pkg['state-info'].state as string as PrimaryStatus } @@ -52,29 +49,26 @@ function getDependencyStatus(depErrors: PkgDependencyErrors): DependencyStatus { : DependencyStatus.Satisfied } -function getHealthStatus( - status: Status, - hasHealthChecks: boolean, -): HealthStatus | null { +function getHealthStatus(status: Status): HealthStatus | null { if (status.main.status !== PackageMainStatus.Running || !status.main.health) { return null } const values = Object.values(status.main.health) - if (values.some(h => h.result === 'failure')) { - return HealthStatus.Failure + if (values.some(h => !h.result)) { + return HealthStatus.Waiting } - if (!values.length && hasHealthChecks) { - return HealthStatus.Waiting + if (values.some(h => h.result === 'failure')) { + return HealthStatus.Failure } if (values.some(h => h.result === 'loading')) { return HealthStatus.Loading } - if (values.some(h => !h.result || h.result === 'starting')) { + if (values.some(h => h.result === 'starting')) { return HealthStatus.Starting } diff --git a/web/projects/ui/src/app/util/dry-update.ts b/web/projects/ui/src/app/util/dry-update.ts index 2a37c9160..36d220327 100644 --- a/web/projects/ui/src/app/util/dry-update.ts +++ b/web/projects/ui/src/app/util/dry-update.ts @@ -13,7 +13,7 @@ export function dryUpdate( Object.keys(pkg['current-dependencies'] || {}).some( pkgId => pkgId === id, ) && - !emver.satisfies(version, getManifest(pkg).dependencies[id].version), + !emver.satisfies(version, pkg['current-dependencies'][id].versionRange), ) .map(pkg => getManifest(pkg).title) } diff --git a/web/projects/ui/src/app/util/get-package-data.ts b/web/projects/ui/src/app/util/get-package-data.ts index 472a549d5..79834bbe7 100644 --- a/web/projects/ui/src/app/util/get-package-data.ts +++ b/web/projects/ui/src/app/util/get-package-data.ts @@ -3,12 +3,12 @@ import { DataModel, InstalledState, InstallingState, - Manifest, PackageDataEntry, PackageState, UpdatingState, } from 'src/app/services/patch-db/data-model' import { firstValueFrom } from 'rxjs' +import { Manifest } from '@start9labs/marketplace' export async function getPackage( patch: PatchDB, diff --git a/web/projects/ui/src/app/util/has-deps.ts b/web/projects/ui/src/app/util/has-deps.ts index 3699ef916..b5c8c1f92 100644 --- a/web/projects/ui/src/app/util/has-deps.ts +++ b/web/projects/ui/src/app/util/has-deps.ts @@ -1,8 +1,8 @@ import { PackageDataEntry } from '../services/patch-db/data-model' -import { getManifest } from './get-package-data' -export function hasCurrentDeps(pkg: PackageDataEntry): boolean { - return !!Object.keys(pkg['current-dependents']).filter( - depId => depId !== getManifest(pkg).id, - ).length +export function hasCurrentDeps( + id: string, + pkgs: Record, +): boolean { + return !!Object.values(pkgs).some(pkg => !!pkg['current-dependencies'][id]) } From a35baca5807eccfdb12dab3a3d017543fd3702ba Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 21 Mar 2024 17:53:34 -0600 Subject: [PATCH 4/4] update rust types to match sdk changes --- core/startos/bindings/ActionMetadata.ts | 4 ++++ core/startos/bindings/AllowedStatuses.ts | 2 +- core/startos/bindings/ExportActionParams.ts | 4 ++-- core/startos/src/service/service_effect_handler.rs | 12 ++++++++++-- sdk/lib/types.ts | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 core/startos/bindings/ActionMetadata.ts diff --git a/core/startos/bindings/ActionMetadata.ts b/core/startos/bindings/ActionMetadata.ts new file mode 100644 index 000000000..e2e0a8fb8 --- /dev/null +++ b/core/startos/bindings/ActionMetadata.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AllowedStatuses } from "./AllowedStatuses"; + +export interface ActionMetadata { name: string, description: string, warning: string | null, disabled: boolean, input: {[key: string]: any}, allowedStatuses: AllowedStatuses, group: string | null, } \ No newline at end of file diff --git a/core/startos/bindings/AllowedStatuses.ts b/core/startos/bindings/AllowedStatuses.ts index 87d122f70..e81a1639f 100644 --- a/core/startos/bindings/AllowedStatuses.ts +++ b/core/startos/bindings/AllowedStatuses.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type AllowedStatuses = "only-running" | "only-stopped" | "any" | "disabled"; \ No newline at end of file +export type AllowedStatuses = "only-running" | "only-stopped" | "any"; \ No newline at end of file diff --git a/core/startos/bindings/ExportActionParams.ts b/core/startos/bindings/ExportActionParams.ts index 413a63183..e96194745 100644 --- a/core/startos/bindings/ExportActionParams.ts +++ b/core/startos/bindings/ExportActionParams.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { AllowedStatuses } from "./AllowedStatuses"; +import type { ActionMetadata } from "./ActionMetadata"; -export interface ExportActionParams { name: string, description: string, id: string, input: {[key: string]: any}, allowedStatuses: AllowedStatuses, group: string | null, } \ No newline at end of file +export interface ExportActionParams { id: string, metadata: ActionMetadata, } \ No newline at end of file diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index 81ea40eaa..cdeb98e56 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -260,16 +260,24 @@ enum AllowedStatuses { OnlyRunning, OnlyStopped, Any, - Disabled, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] struct ExportActionParams { + #[ts(type = "string")] + id: ActionId, + metadata: ActionMetadata +} +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +struct ActionMetadata { name: String, description: String, - id: String, + warning: Option, + disabled: bool, #[ts(type = "{[key: string]: any}")] input: Value, allowed_statuses: AllowedStatuses, diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts index 5796a9fcc..ee0574164 100644 --- a/sdk/lib/types.ts +++ b/sdk/lib/types.ts @@ -444,7 +444,7 @@ export type Effects = { * * @param options */ - exportAction(options: ActionMetadata): Promise + exportAction(options: { id: string; metadata: ActionMetadata }): Promise /** * Remove an action that was exported. Used problably during main or during setConfig. */