From 9cf62f03fafdf78b213cad094c5a36bc7277ccb3 Mon Sep 17 00:00:00 2001 From: J H Date: Wed, 27 Mar 2024 15:28:54 -0600 Subject: [PATCH] Add some extra export action --- .../Systems/SystemForEmbassy/index.ts | 30 ++++++++ core/startos/src/db/model/package.rs | 25 ++++++- .../src/service/service_effect_handler.rs | 71 ++++++++++++------- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index fb99795cc..ade166eff 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -29,6 +29,7 @@ import { import { HostSystemStartOs } from "../../HostSystemStartOs" import { JsonPath, unNestPath } from "../../../Models/JsonPath" import { RpcResult, matchRpcResult } from "../../RpcListener" +import { InputSpec } from "@start9labs/start-sdk/cjs/sdk/lib/config/configTypes" type Optional = A | undefined | null function todo(): never { @@ -259,6 +260,35 @@ export class SystemForEmbassy implements System { ): Promise { if (previousVersion) await this.migration(effects, previousVersion) await effects.setMainStatus({ status: "stopped" }) + await this.exportActions(effects) + } + async exportActions(effects: HostSystemStartOs) { + const manifest = this.manifest + if (!manifest.actions) return + for (const [actionId, action] of Object.entries(manifest.actions)) { + const hasRunning = !!action["allowed-statuses"].find( + (x) => x === "running", + ) + const hasStopped = !!action["allowed-statuses"].find( + (x) => x === "stopped", + ) + // prettier-ignore + const allowedStatuses = hasRunning && hasStopped ? "any": + hasRunning ? "onlyRunning" : + "onlyStopped" + await effects.exportAction({ + id: actionId, + metadata: { + name: action.name, + description: action.description, + warning: action.warning || null, + input: action["input-spec"] as InputSpec, + disabled: false, + allowedStatuses, + group: null, + }, + }) + } } private async uninit( effects: HostSystemStartOs, diff --git a/core/startos/src/db/model/package.rs b/core/startos/src/db/model/package.rs index 641b0b56d..0c82bf80c 100644 --- a/core/startos/src/db/model/package.rs +++ b/core/startos/src/db/model/package.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, BTreeSet}; use chrono::{DateTime, Utc}; use emver::VersionRange; use imbl_value::InternedString; -use models::{DataUrl, HealthCheckId, HostId, PackageId}; +use models::{ActionId, DataUrl, HealthCheckId, HostId, PackageId}; use patch_db::json_ptr::JsonPointer; use patch_db::HasModel; use reqwest::Url; @@ -293,6 +293,28 @@ pub struct InstallingInfo { pub new_manifest: Manifest, pub progress: FullProgress, } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub enum AllowedStatuses { + OnlyRunning, // onlyRunning + OnlyStopped, + Any, +} + +#[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS)] +#[serde(rename_all = "camelCase")] +#[model = "Model"] +pub struct ActionMetadata { + name: String, + description: String, + warning: Option, + #[ts(type = "any")] + input: Value, + disabled: bool, + allowedStatuses: AllowedStatuses, + group: Option, +} #[derive(Debug, Deserialize, Serialize, HasModel, TS)] #[serde(rename_all = "camelCase")] @@ -312,6 +334,7 @@ pub struct PackageDataEntry { pub hosts: HostInfo, #[ts(type = "string[]")] pub store_exposed_dependents: Vec, + pub exposed_actions: BTreeMap, } impl AsRef for PackageDataEntry { fn as_ref(&self) -> &PackageDataEntry { diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index ed82138b1..7bd1c0ab8 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -20,7 +20,7 @@ use ts_rs::TS; use url::Url; use crate::db::model::package::{ - CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, + ActionMetadata, CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, }; use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::loop_dev::LoopDev; @@ -254,14 +254,6 @@ struct ListServiceInterfacesParams { struct RemoveAddressParams { id: String, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -enum AllowedStatuses { - OnlyRunning, // onlyRunning - OnlyStopped, - Any, -} #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] @@ -274,21 +266,9 @@ struct ExportActionParams { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] -struct ActionMetadata { - name: String, - description: String, - warning: Option, - disabled: bool, - #[ts(type = "{[key: string]: any}")] - input: Value, - allowed_statuses: AllowedStatuses, - group: Option, -} -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] struct RemoveActionParams { - id: String, + #[ts(type = "string")] + id: ActionId, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] @@ -387,11 +367,48 @@ async fn list_service_interfaces( async fn remove_address(context: EffectContext, data: RemoveAddressParams) -> Result { todo!() } -async fn export_action(context: EffectContext, data: ExportActionParams) -> Result { - todo!() +async fn export_action(context: EffectContext, data: ExportActionParams) -> Result<(), Error> { + let context = context.deref()?; + let package_id = context.id.clone(); + context + .ctx + .db + .mutate(|db| { + let model = db + .as_public_mut() + .as_package_data_mut() + .as_idx_mut(&package_id) + .or_not_found(&package_id)? + .as_exposed_actions_mut(); + let mut value = model.de()?; + value + .insert(data.id, data.metadata) + .map(|_| ()) + .unwrap_or_default(); + model.ser(&value) + }) + .await?; + Ok(()) } -async fn remove_action(context: EffectContext, data: RemoveActionParams) -> Result { - todo!() +async fn remove_action(context: EffectContext, data: RemoveActionParams) -> Result<(), Error> { + let context = context.deref()?; + let package_id = context.id.clone(); + context + .ctx + .db + .mutate(|db| { + let model = db + .as_public_mut() + .as_package_data_mut() + .as_idx_mut(&package_id) + .or_not_found(&package_id)? + .as_exposed_actions_mut(); + let mut value = model.de()?; + value.remove(&data.id).map(|_| ()).unwrap_or_default(); + model.ser(&value) + }) + .await?; + Ok(()) } async fn reverse_proxy(context: EffectContext, data: ReverseProxyParams) -> Result { todo!()