From 99bb55af73a01d57dd15b91c152bb37f692daa57 Mon Sep 17 00:00:00 2001 From: J H Date: Mon, 11 Mar 2024 15:37:04 -0600 Subject: [PATCH 1/2] Update some of the types --- .../Systems/SystemForEmbassy/index.ts | 73 +++++++++++++++++-- core/startos/src/db/model.rs | 12 +-- core/startos/src/properties.rs | 50 ++++++++++++- .../app-interfaces/app-interfaces.page.ts | 8 +- 4 files changed, 122 insertions(+), 21 deletions(-) diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index 8495a669b..fd8f81204 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -1,14 +1,11 @@ -import { types as T, util, EmVer } from "@start9labs/start-sdk" +import { types as T, util, EmVer, Utils } from "@start9labs/start-sdk" import * as fs from "fs/promises" import { PolyfillEffects } from "./polyfillEffects" import { Duration, duration } from "../../../Models/Duration" -import { ExecuteResult, System } from "../../../Interfaces/System" +import { System } from "../../../Interfaces/System" import { matchManifest, Manifest, Procedure } from "./matchManifest" -import { create } from "domain" import * as childProcess from "node:child_process" -import { Volume } from "../../../Models/Volume" -import { DockerProcedure } from "../../../Models/DockerProcedure" import { DockerProcedureContainer } from "./DockerProcedureContainer" import { promisify } from "node:util" import * as U from "./oldEmbassyTypes" @@ -28,9 +25,7 @@ import { } from "ts-matches" import { HostSystemStartOs } from "../../HostSystemStartOs" import { JsonPath, unNestPath } from "../../../Models/JsonPath" -import { HostSystem } from "../../../Interfaces/HostSystem" import { RpcResult, matchRpcResult } from "../../RpcListener" -import { ServiceInterface } from "../../../../../sdk/dist/cjs/lib/types" type Optional = A | undefined | null function todo(): never { @@ -42,6 +37,60 @@ const MANIFEST_LOCATION = "/usr/lib/startos/package/embassyManifest.json" const EMBASSY_JS_LOCATION = "/usr/lib/startos/package/embassy.js" const EMBASSY_POINTER_PATH_PREFIX = "/embassyConfig" +const matchPackagePropertyObject = object({ + value: any, + type: literal("object"), + description: string, +}) + +const matchPackagePropertyString = object( + { + type: literal("string"), + description: string, + value: string, + copyable: boolean, + qr: boolean, + masked: boolean, + }, + ["copyable", "description", "qr", "masked"], +) + +const matchProperties = object({ + version: literal(2), + data: any, +}) + +type ExportUi = { + value: string + title: string + description?: string | undefined + masked?: boolean | undefined + copyable?: boolean | undefined + qr?: boolean | undefined +} + +function propertiesToExportUi(properties: unknown): ExportUi[] { + if (!object.test(properties)) return [] + const paths: ExportUi[] = [] + for (const key in properties) { + const value: unknown = (properties as any)[key] + if (matchPackagePropertyObject.test(value)) { + paths.push(...propertiesToExportUi(value)) + continue + } + if (!matchPackagePropertyString.test(value)) continue + paths.push({ + value: value.value, + title: key, + description: value.description, + masked: value.masked, + copyable: value.copyable, + qr: value.qr, + }) + } + return paths +} + export class SystemForEmbassy implements System { currentRunning: MainLoop | undefined static async of(manifestLocation: string = MANIFEST_LOCATION) { @@ -399,7 +448,7 @@ export class SystemForEmbassy implements System { setConfigValue, this.manifest.volumes, ) - return JSON.parse( + const properties = JSON.parse( ( await container.exec([ setConfigValue.entrypoint, @@ -407,6 +456,14 @@ export class SystemForEmbassy implements System { ]) ).stdout.toString(), ) + const exposeUis = propertiesToExportUi(properties) + await effects.store.set({ + path: "/properties", + value: exposeUis.map((x) => x.value), + }) + await effects.exposeUi( + exposeUis.map((x, i) => ({ ...x, path: `/properties/${i}` }) as any), + ) } else if (setConfigValue.type === "script") { const moduleCode = this.moduleCode const method = moduleCode.properties diff --git a/core/startos/src/db/model.rs b/core/startos/src/db/model.rs index 19cfa37a1..92707060a 100644 --- a/core/startos/src/db/model.rs +++ b/core/startos/src/db/model.rs @@ -530,12 +530,12 @@ pub struct ExposedDependent { #[derive(Clone, Debug, Deserialize, Serialize, HasModel)] #[model = "Model"] pub struct ExposedUI { - path: Vec, - title: String, - description: Option, - masked: Option, - copyable: Option, - qr: Option, + pub path: JsonPointer, + pub title: String, + pub description: Option, + pub masked: Option, + pub copyable: Option, + pub qr: Option, } #[derive(Debug, Clone, Default, Deserialize, Serialize)] diff --git a/core/startos/src/properties.rs b/core/startos/src/properties.rs index 9c503c3f6..9d3a67054 100644 --- a/core/startos/src/properties.rs +++ b/core/startos/src/properties.rs @@ -1,16 +1,41 @@ use clap::Parser; +use imbl_value::{json, Value}; use models::PackageId; use rpc_toolkit::command; use serde::{Deserialize, Serialize}; -use serde_json::Value; -use crate::context::RpcContext; +use crate::prelude::*; use crate::Error; +use crate::{context::RpcContext, db::model::ExposedUI}; pub fn display_properties(response: Value) { println!("{}", response); } +trait IntoProperties { + fn into_properties(self, store: &Value) -> Value; +} +impl IntoProperties for Vec { + fn into_properties(self, store: &Value) -> Value { + let mut data = json!({}); + for ui in self { + let value = ui.path.get(store); + data[ui.title] = json!({ + "type": "string", + "description": ui.description, + "value": value.map(|x| x.to_string()).unwrap_or_default(), + "copyable": ui.copyable, + "qr": ui.qr, + "masked": ui.masked, + }); + } + json!({ + "version": 2, + "data": data + }) + } +} + #[derive(Deserialize, Serialize, Parser)] #[serde(rename_all = "kebab-case")] #[command(rename_all = "kebab-case")] @@ -22,5 +47,24 @@ pub async fn properties( ctx: RpcContext, PropertiesParam { id }: PropertiesParam, ) -> Result { - Ok(todo!()) + let peeked = ctx.db.peek().await; + let data = peeked + .as_public() + .as_package_data() + .as_idx(&id) + .or_not_found(&id)? + .as_installed() + .or_not_found(&id)? + .as_store() + .de()?; + Ok(peeked + .as_public() + .as_package_data() + .as_idx(&id) + .or_not_found(&id)? + .as_installed() + .or_not_found(&id)? + .as_store_exposed_ui() + .de()? + .into_properties(&data)) } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts index 3678474e6..84645fb9d 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts @@ -7,10 +7,10 @@ import { DataModel } from 'src/app/services/patch-db/data-model' import { PatchDB } from 'patch-db-client' import { QRComponent } from 'src/app/components/qr/qr.component' import { map } from 'rxjs' -import { - ServiceInterface, - ServiceInterfaceWithHostInfo, -} from '@start9labs/start-sdk/mjs/lib/types' +import { types as T } from '@start9labs/start-sdk' + +type ServiceInterface = T.ServiceInterface +type ServiceInterfaceWithHostInfo = T.ServiceInterfaceWithHostInfo type MappedInterface = ServiceInterface & { addresses: MappedAddress[] From dc3dc4a1f037f90293bf53f66fe8a44d23e3370a Mon Sep 17 00:00:00 2001 From: J H Date: Mon, 11 Mar 2024 15:47:22 -0600 Subject: [PATCH 2/2] chore: Update some of the system for the embassy properties to be filled with the correct properties. git push --- .../Systems/SystemForEmbassy/index.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index fd8f81204..a87facc56 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -227,13 +227,9 @@ export class SystemForEmbassy implements System { effects: HostSystemStartOs, previousVersion: Optional, ): Promise { - console.log("here1") if (previousVersion) await this.migration(effects, previousVersion) - console.log("here2") await this.properties(effects) - console.log("here3") await effects.setMainStatus({ status: "stopped" }) - console.log("here4") } private async uninit( effects: HostSystemStartOs, @@ -456,7 +452,8 @@ export class SystemForEmbassy implements System { ]) ).stdout.toString(), ) - const exposeUis = propertiesToExportUi(properties) + if (!matchProperties.test(properties)) return + const exposeUis = propertiesToExportUi(properties.data) await effects.store.set({ path: "/properties", value: exposeUis.map((x) => x.value), @@ -469,11 +466,22 @@ export class SystemForEmbassy implements System { const method = moduleCode.properties if (!method) throw new Error("Expecting that the method properties exists") - await method(new PolyfillEffects(effects, this.manifest)).then((x) => { + const properties = await method( + new PolyfillEffects(effects, this.manifest), + ).then((x) => { if ("result" in x) return x.result if ("error" in x) throw new Error("Error getting config: " + x.error) throw new Error("Error getting config: " + x["error-code"][1]) }) + if (!matchProperties.test(properties)) return + const exposeUis = propertiesToExportUi(properties.data) + await effects.store.set({ + path: "/properties", + value: exposeUis.map((x) => x.value), + }) + await effects.exposeUi( + exposeUis.map((x, i) => ({ ...x, path: `/properties/${i}` }) as any), + ) } } private async health(