diff --git a/container-runtime/src/Adapters/HostSystemStartOs.ts b/container-runtime/src/Adapters/HostSystemStartOs.ts index f173e4e62..5e52224fa 100644 --- a/container-runtime/src/Adapters/HostSystemStartOs.ts +++ b/container-runtime/src/Adapters/HostSystemStartOs.ts @@ -147,11 +147,6 @@ export class HostSystemStartOs implements Effects { T.Effects["exposeForDependents"] > } - exposeUi(...[options]: Parameters) { - return this.rpcRound("exposeUi", options) as ReturnType< - T.Effects["exposeUi"] - > - } getConfigured(...[]: Parameters) { return this.rpcRound("getConfigured", null) as ReturnType< T.Effects["getConfigured"] diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index e5e532246..69e3e6615 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -25,15 +25,12 @@ export class MainLoop { wait: Promise }> | undefined - private propertiesEvent: NodeJS.Timeout | undefined constructor( readonly system: SystemForEmbassy, readonly effects: HostSystemStartOs, - readonly runProperties: () => Promise, ) { this.healthLoops = this.constructHealthLoops() this.mainEvent = this.constructMainEvent() - this.propertiesEvent = this.constructPropertiesEvent() } private async constructMainEvent() { @@ -85,23 +82,14 @@ export class MainLoop { } public async clean(options?: { timeout?: number }) { - const { mainEvent, healthLoops, propertiesEvent } = this + const { mainEvent, healthLoops } = this const main = await mainEvent delete this.mainEvent delete this.healthLoops - delete this.propertiesEvent if (mainEvent) await main?.daemon.term() - clearInterval(propertiesEvent) if (healthLoops) healthLoops.forEach((x) => clearInterval(x.interval)) } - private constructPropertiesEvent() { - const { runProperties } = this - return setInterval(() => { - runProperties() - }, EMBASSY_PROPERTIES_LOOP) - } - private constructHealthLoops() { const { manifest } = this.system const effects = this.effects diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index a96ee5549..7ff875d91 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -59,6 +59,33 @@ export type PackagePropertyObject = { type: "object" description: string } + +const asProperty_ = ( + x: PackagePropertyString | PackagePropertyObject, +): T.PropertiesValue => { + if (x.type === "object") { + return { + ...x, + value: Object.fromEntries( + Object.entries(x.value).map(([key, value]) => [ + key, + asProperty_(value), + ]), + ), + } + } + return { + masked: false, + description: null, + qr: null, + copyable: null, + ...x, + } +} +const asProperty = (x: PackagePropertiesV2): T.PropertiesReturn => + Object.fromEntries( + Object.entries(x).map(([key, value]) => [key, asProperty_(value)]), + ) const [matchPackageProperties, setMatchPackageProperties] = deferred() const matchPackagePropertyObject: Parser = @@ -92,44 +119,6 @@ const matchProperties = object({ data: matchPackageProperties, }) -type ExportUi = { - values: { [key: string]: any } - expose: { [key: string]: T.ExposeUiPathsAll } -} - -function propertiesToExportUi( - properties: PackagePropertiesV2, - previousPath = "", -): ExportUi { - const exportUi: ExportUi = { - values: {}, - expose: {}, - } - for (const [key, value] of Object.entries(properties)) { - const path = `${previousPath}/${key}` - if (value.type === "object") { - const { values, expose } = propertiesToExportUi(value.value, path) - exportUi.values[key] = values - exportUi.expose[key] = { - type: "object", - value: expose, - description: value.description, - } - continue - } - exportUi.values[key] = value.value - exportUi.expose[key] = { - type: "string", - path, - description: value.description ?? null, - masked: value.masked ?? false, - copyable: value.copyable ?? null, - qr: value.qr ?? null, - } - } - return exportUi -} - export class SystemForEmbassy implements System { currentRunning: MainLoop | undefined static async of(manifestLocation: string = MANIFEST_LOCATION) { @@ -236,6 +225,8 @@ export class SystemForEmbassy implements System { return this.getConfig(effects) case "/config/set": return this.setConfig(effects, input) + case "/properties": + return this.properties(effects) case "/actions/metadata": return todo() case "/init": @@ -279,9 +270,7 @@ export class SystemForEmbassy implements System { private async mainStart(effects: HostSystemStartOs): Promise { if (!!this.currentRunning) return - this.currentRunning = new MainLoop(this, effects, () => - this.properties(effects), - ) + this.currentRunning = new MainLoop(this, effects) } private async mainStop( effects: HostSystemStartOs, @@ -472,51 +461,44 @@ export class SystemForEmbassy implements System { } return { configured: true } } - private async properties(effects: HostSystemStartOs): Promise { + private async properties( + effects: HostSystemStartOs, + ): Promise> { // TODO BLU-J set the properties ever so often const setConfigValue = this.manifest.properties - if (!setConfigValue) return + if (!setConfigValue) throw new Error("There is no properties") if (setConfigValue.type === "docker") { const container = await DockerProcedureContainer.of( effects, setConfigValue, this.manifest.volumes, ) - const properties = JSON.parse( - ( - await container.exec([ - setConfigValue.entrypoint, - ...setConfigValue.args, - ]) - ).stdout.toString(), + const properties = matchProperties.unsafeCast( + JSON.parse( + ( + await container.exec([ + setConfigValue.entrypoint, + ...setConfigValue.args, + ]) + ).stdout.toString(), + ), ) - if (!matchProperties.test(properties)) return - const exposeUis = propertiesToExportUi(properties.data) - await effects.store.set({ - path: "/properties", - value: exposeUis.values, - }) - await effects.exposeUi(exposeUis.expose) + return asProperty(properties.data) } else if (setConfigValue.type === "script") { const moduleCode = this.moduleCode const method = moduleCode.properties if (!method) throw new Error("Expecting that the method properties exists") - 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.values, - }) - await effects.exposeUi(exposeUis.expose) + const properties = matchProperties.unsafeCast( + 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]) + }), + ) + return asProperty(properties.data) } + throw new Error(`Unknown type in the fetch properties: ${setConfigValue}`) } private async health( effects: HostSystemStartOs, @@ -651,279 +633,6 @@ export class SystemForEmbassy implements System { throw new Error("Error getting config: " + x["error-code"][1]) })) as any } - // private async sandbox( - // effects: HostSystemStartOs, - // options: { - // procedure: - // | "/createBackup" - // | "/restoreBackup" - // | "/getConfig" - // | "/setConfig" - // | "migration" - // | "/properties" - // | `/action/${string}` - // | `/dependencies/${string}/check` - // | `/dependencies/${string}/autoConfigure` - // input: unknown - // timeout?: number | undefined - // }, - // ): Promise { - // const input = options.input - // switch (options.procedure) { - // case "/createBackup": - // return this.roCreateBackup(effects) - // case "/restoreBackup": - // return this.roRestoreBackup(effects) - // case "/getConfig": - // return this.roGetConfig(effects) - // case "/setConfig": - // return this.roSetConfig(effects, input) - // case "migration": - // return this.roMigration(effects, input) - // case "/properties": - // return this.roProperties(effects) - // default: - // const procedure = options.procedure.split("/") - // switch (true) { - // case options.procedure.startsWith("/action/"): - // return this.roAction(effects, procedure[2], input) - // case options.procedure.startsWith("/dependencies/") && - // procedure[3] === "check": - // return this.roDependenciesCheck(effects, procedure[2], input) - - // case options.procedure.startsWith("/dependencies/") && - // procedure[3] === "autoConfigure": - // return this.roDependenciesAutoconfig(effects, procedure[2], input) - // } - // } - // } - - // private async roCreateBackup(effects: HostSystemStartOs): Promise { - // const backup = this.manifest.backup.create - // if (backup.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf(backup) - // await container.exec([backup.entrypoint, ...backup.args]) - // } else { - // const moduleCode = await this.moduleCode - // await moduleCode.createBackup?.(new PolyfillEffects(effects)) - // } - // } - // private async roRestoreBackup(effects: HostSystemStartOs): Promise { - // const restoreBackup = this.manifest.backup.restore - // if (restoreBackup.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf(restoreBackup) - // await container.exec([restoreBackup.entrypoint, ...restoreBackup.args]) - // } else { - // const moduleCode = await this.moduleCode - // await moduleCode.restoreBackup?.(new PolyfillEffects(effects)) - // } - // } - // private async roGetConfig(effects: HostSystemStartOs): Promise { - // const config = this.manifest.config?.get - // if (!config) return { spec: {} } - // if (config.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf(config) - // return JSON.parse( - // (await container.exec([config.entrypoint, ...config.args])).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.getConfig - // if (!method) throw new Error("Expecting that the method getConfig exists") - // return (await method(new PolyfillEffects(effects)).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]) - // })) as any - // } - // } - // private async roSetConfig( - // effects: HostSystemStartOs, - // newConfig: unknown, - // ): Promise { - // const setConfigValue = this.manifest.config?.set - // if (!setConfigValue) return { signal: "SIGTERM", "depends-on": {} } - // if (setConfigValue.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // setConfigValue, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // setConfigValue.entrypoint, - // ...setConfigValue.args, - // JSON.stringify(newConfig), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.setConfig - // if (!method) throw new Error("Expecting that the method setConfig exists") - // return await method( - // new PolyfillEffects(effects), - // newConfig as U.Config, - // ).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]) - // }) - // } - // } - // private async roMigration( - // effects: HostSystemStartOs, - // fromVersion: unknown, - // ): Promise { - // throw new Error("Migrations should never be ran in the sandbox mode") - // } - // private async roProperties(effects: HostSystemStartOs): Promise { - // const setConfigValue = this.manifest.properties - // if (!setConfigValue) return {} - // if (setConfigValue.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // setConfigValue, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // setConfigValue.entrypoint, - // ...setConfigValue.args, - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.properties - // if (!method) - // throw new Error("Expecting that the method properties exists") - // return await method(new PolyfillEffects(effects)).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]) - // }) - // } - // } - // private async roHealth( - // effects: HostSystemStartOs, - // healthId: string, - // timeSinceStarted: unknown, - // ): Promise { - // const healthProcedure = this.manifest["health-checks"][healthId] - // if (!healthProcedure) return - // if (healthProcedure.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // healthProcedure, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // healthProcedure.entrypoint, - // ...healthProcedure.args, - // JSON.stringify(timeSinceStarted), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.health?.[healthId] - // if (!method) throw new Error("Expecting that the method health exists") - // await method(new PolyfillEffects(effects), Number(timeSinceStarted)).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]) - // }, - // ) - // } - // } - // private async roAction( - // effects: HostSystemStartOs, - // actionId: string, - // formData: unknown, - // ): Promise { - // const actionProcedure = this.manifest.actions?.[actionId]?.implementation - // if (!actionProcedure) return { message: "Action not found", value: null } - // if (actionProcedure.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // actionProcedure, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // actionProcedure.entrypoint, - // ...actionProcedure.args, - // JSON.stringify(formData), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.action?.[actionId] - // if (!method) throw new Error("Expecting that the method action exists") - // return (await method(new PolyfillEffects(effects), formData as any).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]) - // }, - // )) as any - // } - // } - // private async roDependenciesCheck( - // effects: HostSystemStartOs, - // id: string, - // oldConfig: unknown, - // ): Promise { - // const actionProcedure = this.manifest.dependencies?.[id]?.config?.check - // if (!actionProcedure) return { message: "Action not found", value: null } - // if (actionProcedure.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // actionProcedure, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // actionProcedure.entrypoint, - // ...actionProcedure.args, - // JSON.stringify(oldConfig), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.dependencies?.[id]?.check - // if (!method) - // throw new Error( - // `Expecting that the method dependency check ${id} exists`, - // ) - // return (await method(new PolyfillEffects(effects), oldConfig as any).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]) - // }, - // )) as any - // } - // } - // private async roDependenciesAutoconfig( - // effects: HostSystemStartOs, - // id: string, - // oldConfig: unknown, - // ): Promise { - // const moduleCode = await this.moduleCode - // const method = moduleCode.dependencies?.[id]?.autoConfigure - // if (!method) - // throw new Error( - // `Expecting that the method dependency autoConfigure ${id} exists`, - // ) - // return (await method(new PolyfillEffects(effects), oldConfig as any).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]) - // }, - // )) as any - // } } async function removePointers(value: T.ConfigRes): Promise { const startingSpec = structuredClone(value.spec) diff --git a/container-runtime/src/Models/JsonPath.ts b/container-runtime/src/Models/JsonPath.ts index 627eb3be2..773331b19 100644 --- a/container-runtime/src/Models/JsonPath.ts +++ b/container-runtime/src/Models/JsonPath.ts @@ -35,6 +35,7 @@ export const jsonPath = some( "/backup/create", "/backup/restore", "/actions/metadata", + "/properties", ), string.refine(isNestedPath, "isNestedPath"), ) diff --git a/core/Cargo.lock b/core/Cargo.lock index 2d800eda1..24a641819 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -2685,7 +2685,7 @@ dependencies = [ "tokio", "torut", "tracing", - "ts-rs", + "ts-rs 7.1.1", "yasi", ] @@ -4730,7 +4730,7 @@ dependencies = [ "tracing-journald", "tracing-subscriber", "trust-dns-server", - "ts-rs", + "ts-rs 8.1.0", "typed-builder", "url", "urlencoding", @@ -5441,7 +5441,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2cae1fc5d05d47aa24b64f9a4f7cba24cdc9187a2084dd97ac57bef5eccae6" dependencies = [ "thiserror", - "ts-rs-macros", + "ts-rs-macros 7.1.1", +] + +[[package]] +name = "ts-rs" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d3fa4606cdab1e9b668cc65ce2545941d01f52bc27536a195c66c55b91cb84" +dependencies = [ + "thiserror", + "ts-rs-macros 8.1.0", ] [[package]] @@ -5457,6 +5467,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "ts-rs-macros" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86ae36cbb2d58b86677ad413054feeb0712e382e822131cf9a4a1e580c419b5" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.52", + "termcolor", +] + [[package]] name = "tungstenite" version = "0.21.0" diff --git a/core/models/src/procedure_name.rs b/core/models/src/procedure_name.rs index bf69b06b8..c42068be3 100644 --- a/core/models/src/procedure_name.rs +++ b/core/models/src/procedure_name.rs @@ -9,6 +9,7 @@ pub enum ProcedureName { GetConfig, SetConfig, CreateBackup, + Properties, RestoreBackup, ActionMetadata, RunAction(ActionId), @@ -29,6 +30,7 @@ impl ProcedureName { ProcedureName::SetConfig => "/config/set".to_string(), ProcedureName::GetConfig => "/config/get".to_string(), ProcedureName::CreateBackup => "/backup/create".to_string(), + ProcedureName::Properties => "/properties".to_string(), ProcedureName::RestoreBackup => "/backup/restore".to_string(), ProcedureName::ActionMetadata => "/actions/metadata".to_string(), ProcedureName::RunAction(id) => format!("/actions/{}/run", id), diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index 5d48179f9..301602e2d 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -174,7 +174,7 @@ tracing-futures = "0.2.5" tracing-journald = "0.3.0" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } trust-dns-server = "0.23.1" -ts-rs = "7.1.1" +ts-rs = "8.1.0" typed-builder = "0.18.0" url = { version = "2.4.1", features = ["serde"] } urlencoding = "2.1.3" diff --git a/core/startos/bindings/ActionMetadata.ts b/core/startos/bindings/ActionMetadata.ts index e2e0a8fb8..91af02b7b 100644 --- a/core/startos/bindings/ActionMetadata.ts +++ b/core/startos/bindings/ActionMetadata.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"; -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 +export type 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/AddSslOptions.ts b/core/startos/bindings/AddSslOptions.ts index 304bcb515..f0f74fe03 100644 --- a/core/startos/bindings/AddSslOptions.ts +++ b/core/startos/bindings/AddSslOptions.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 interface AddSslOptions { scheme: string | null, preferredExternalPort: number, addXForwardedHeaders: boolean | null, } \ No newline at end of file +export type AddSslOptions = { scheme: string | null, preferredExternalPort: number, addXForwardedHeaders: boolean | null, }; \ No newline at end of file diff --git a/core/startos/bindings/AddressInfo.ts b/core/startos/bindings/AddressInfo.ts index 9d4c8ee6e..b253ab823 100644 --- a/core/startos/bindings/AddressInfo.ts +++ b/core/startos/bindings/AddressInfo.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 { BindOptions } from "./BindOptions"; -export interface AddressInfo { username: string | null, hostId: string, bindOptions: BindOptions, suffix: string, } \ No newline at end of file +export type AddressInfo = { username: string | null, hostId: string, bindOptions: BindOptions, suffix: string, }; \ No newline at end of file diff --git a/core/startos/bindings/AllowedStatuses.ts b/core/startos/bindings/AllowedStatuses.ts index 02f9584d1..a04ee2bac 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"; +export type AllowedStatuses = "onlyRunning" | "onlyStopped" | "any"; diff --git a/core/startos/bindings/BindOptions.ts b/core/startos/bindings/BindOptions.ts index 029be6a16..997f2bd8b 100644 --- a/core/startos/bindings/BindOptions.ts +++ b/core/startos/bindings/BindOptions.ts @@ -2,4 +2,4 @@ import type { AddSslOptions } from "./AddSslOptions"; import type { BindOptionsSecure } from "./BindOptionsSecure"; -export interface BindOptions { scheme: string | null, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, } \ No newline at end of file +export type BindOptions = { scheme: string | null, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, }; \ No newline at end of file diff --git a/core/startos/bindings/BindOptionsSecure.ts b/core/startos/bindings/BindOptionsSecure.ts index 00bfd7dd2..c7b1b69af 100644 --- a/core/startos/bindings/BindOptionsSecure.ts +++ b/core/startos/bindings/BindOptionsSecure.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 interface BindOptionsSecure { ssl: boolean, } \ No newline at end of file +export type BindOptionsSecure = { ssl: boolean, }; \ No newline at end of file diff --git a/core/startos/bindings/BindParams.ts b/core/startos/bindings/BindParams.ts index f48f848b8..319666538 100644 --- a/core/startos/bindings/BindParams.ts +++ b/core/startos/bindings/BindParams.ts @@ -3,4 +3,4 @@ import type { AddSslOptions } from "./AddSslOptions"; import type { BindKind } from "./BindKind"; import type { BindOptionsSecure } from "./BindOptionsSecure"; -export interface BindParams { kind: BindKind, id: string, internalPort: number, scheme: string, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, } \ No newline at end of file +export type BindParams = { kind: BindKind, id: string, internalPort: number, scheme: string, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, }; \ No newline at end of file diff --git a/core/startos/bindings/ChrootParams.ts b/core/startos/bindings/ChrootParams.ts index afbfb83b8..d8d1b1b46 100644 --- a/core/startos/bindings/ChrootParams.ts +++ b/core/startos/bindings/ChrootParams.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 interface ChrootParams { env: string | null, workdir: string | null, user: string | null, path: string, command: string, args: string[], } \ No newline at end of file +export type ChrootParams = { env: string | null, workdir: string | null, user: string | null, path: string, command: string, args: string[], }; \ No newline at end of file diff --git a/core/startos/bindings/CreateOverlayedImageParams.ts b/core/startos/bindings/CreateOverlayedImageParams.ts index 34924cb1a..65e7487b6 100644 --- a/core/startos/bindings/CreateOverlayedImageParams.ts +++ b/core/startos/bindings/CreateOverlayedImageParams.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 interface CreateOverlayedImageParams { imageId: string, } \ No newline at end of file +export type CreateOverlayedImageParams = { imageId: string, }; \ No newline at end of file diff --git a/core/startos/bindings/DestroyOverlayedImageParams.ts b/core/startos/bindings/DestroyOverlayedImageParams.ts index b875e45bd..34d4085a1 100644 --- a/core/startos/bindings/DestroyOverlayedImageParams.ts +++ b/core/startos/bindings/DestroyOverlayedImageParams.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 interface DestroyOverlayedImageParams { guid: string, } \ No newline at end of file +export type DestroyOverlayedImageParams = { guid: string, }; \ No newline at end of file diff --git a/core/startos/bindings/ExecuteAction.ts b/core/startos/bindings/ExecuteAction.ts index d31ae9279..52636bdbc 100644 --- a/core/startos/bindings/ExecuteAction.ts +++ b/core/startos/bindings/ExecuteAction.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 interface ExecuteAction { serviceId: string | null, actionId: string, input: any, } \ No newline at end of file +export type ExecuteAction = { serviceId: string | null, actionId: string, input: any, }; \ No newline at end of file diff --git a/core/startos/bindings/ExportActionParams.ts b/core/startos/bindings/ExportActionParams.ts index ed58f67c9..4961c4a11 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 { ActionMetadata } from "./ActionMetadata"; -export interface ExportActionParams { id: string, metadata: ActionMetadata, } +export type ExportActionParams = { id: string; metadata: ActionMetadata }; diff --git a/core/startos/bindings/ExportServiceInterfaceParams.ts b/core/startos/bindings/ExportServiceInterfaceParams.ts index d30cce9db..ad7885507 100644 --- a/core/startos/bindings/ExportServiceInterfaceParams.ts +++ b/core/startos/bindings/ExportServiceInterfaceParams.ts @@ -2,4 +2,4 @@ import type { AddressInfo } from "./AddressInfo"; import type { ServiceInterfaceType } from "./ServiceInterfaceType"; -export interface ExportServiceInterfaceParams { id: string, name: string, description: string, hasPrimary: boolean, disabled: boolean, masked: boolean, addressInfo: AddressInfo, type: ServiceInterfaceType, } \ No newline at end of file +export type ExportServiceInterfaceParams = { id: string, name: string, description: string, hasPrimary: boolean, disabled: boolean, masked: boolean, addressInfo: AddressInfo, type: ServiceInterfaceType, }; \ No newline at end of file diff --git a/core/startos/bindings/ExposeForDependentsParams.ts b/core/startos/bindings/ExposeForDependentsParams.ts index 4a1d627b6..9626f4dcf 100644 --- a/core/startos/bindings/ExposeForDependentsParams.ts +++ b/core/startos/bindings/ExposeForDependentsParams.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 interface ExposeForDependentsParams { paths: string[], } \ No newline at end of file +export type ExposeForDependentsParams = { paths: string[], }; \ No newline at end of file diff --git a/core/startos/bindings/GetHostInfoParams.ts b/core/startos/bindings/GetHostInfoParams.ts index 4041bff45..69abeeb13 100644 --- a/core/startos/bindings/GetHostInfoParams.ts +++ b/core/startos/bindings/GetHostInfoParams.ts @@ -2,4 +2,4 @@ import type { Callback } from "./Callback"; import type { GetHostInfoParamsKind } from "./GetHostInfoParamsKind"; -export interface GetHostInfoParams { kind: GetHostInfoParamsKind | null, serviceInterfaceId: string, packageId: string | null, callback: Callback, } \ No newline at end of file +export type GetHostInfoParams = { kind: GetHostInfoParamsKind | null, serviceInterfaceId: string, packageId: string | null, callback: Callback, }; \ No newline at end of file diff --git a/core/startos/bindings/GetPrimaryUrlParams.ts b/core/startos/bindings/GetPrimaryUrlParams.ts index 40fe7f154..6296d4342 100644 --- a/core/startos/bindings/GetPrimaryUrlParams.ts +++ b/core/startos/bindings/GetPrimaryUrlParams.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 { Callback } from "./Callback"; -export interface GetPrimaryUrlParams { packageId: string | null, serviceInterfaceId: string, callback: Callback, } \ No newline at end of file +export type GetPrimaryUrlParams = { packageId: string | null, serviceInterfaceId: string, callback: Callback, }; \ No newline at end of file diff --git a/core/startos/bindings/GetServiceInterfaceParams.ts b/core/startos/bindings/GetServiceInterfaceParams.ts index fb0f96791..1eb2b6f28 100644 --- a/core/startos/bindings/GetServiceInterfaceParams.ts +++ b/core/startos/bindings/GetServiceInterfaceParams.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 { Callback } from "./Callback"; -export interface GetServiceInterfaceParams { packageId: string | null, serviceInterfaceId: string, callback: Callback, } \ No newline at end of file +export type GetServiceInterfaceParams = { packageId: string | null, serviceInterfaceId: string, callback: Callback, }; \ No newline at end of file diff --git a/core/startos/bindings/GetServicePortForwardParams.ts b/core/startos/bindings/GetServicePortForwardParams.ts index 270fcb709..dd4eaf211 100644 --- a/core/startos/bindings/GetServicePortForwardParams.ts +++ b/core/startos/bindings/GetServicePortForwardParams.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 interface GetServicePortForwardParams { packageId: string | null, internalPort: number, } \ No newline at end of file +export type GetServicePortForwardParams = { packageId: string | null, internalPort: number, }; \ No newline at end of file diff --git a/core/startos/bindings/GetSslCertificateParams.ts b/core/startos/bindings/GetSslCertificateParams.ts index 5be8333a5..a4a2447cd 100644 --- a/core/startos/bindings/GetSslCertificateParams.ts +++ b/core/startos/bindings/GetSslCertificateParams.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 { Algorithm } from "./Algorithm"; -export interface GetSslCertificateParams { packageId: string | null, hostId: string, algorithm: Algorithm | null, } \ No newline at end of file +export type GetSslCertificateParams = { packageId: string | null, hostId: string, algorithm: Algorithm | null, }; \ No newline at end of file diff --git a/core/startos/bindings/GetSslKeyParams.ts b/core/startos/bindings/GetSslKeyParams.ts index ca7e0e404..eb36cd2f5 100644 --- a/core/startos/bindings/GetSslKeyParams.ts +++ b/core/startos/bindings/GetSslKeyParams.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 { Algorithm } from "./Algorithm"; -export interface GetSslKeyParams { packageId: string | null, hostId: string, algorithm: Algorithm | null, } \ No newline at end of file +export type GetSslKeyParams = { packageId: string | null, hostId: string, algorithm: Algorithm | null, }; \ No newline at end of file diff --git a/core/startos/bindings/GetStoreParams.ts b/core/startos/bindings/GetStoreParams.ts index 65703be7a..6b965e75f 100644 --- a/core/startos/bindings/GetStoreParams.ts +++ b/core/startos/bindings/GetStoreParams.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 interface GetStoreParams { packageId: string | null, path: string, } \ No newline at end of file +export type GetStoreParams = { packageId: string | null, path: string, }; \ No newline at end of file diff --git a/core/startos/bindings/GetSystemSmtpParams.ts b/core/startos/bindings/GetSystemSmtpParams.ts index 617fd7fa0..25d30529f 100644 --- a/core/startos/bindings/GetSystemSmtpParams.ts +++ b/core/startos/bindings/GetSystemSmtpParams.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 { Callback } from "./Callback"; -export interface GetSystemSmtpParams { callback: Callback, } \ No newline at end of file +export type GetSystemSmtpParams = { callback: Callback, }; \ No newline at end of file diff --git a/core/startos/bindings/ListServiceInterfacesParams.ts b/core/startos/bindings/ListServiceInterfacesParams.ts index 33db3f129..cd889b149 100644 --- a/core/startos/bindings/ListServiceInterfacesParams.ts +++ b/core/startos/bindings/ListServiceInterfacesParams.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 { Callback } from "./Callback"; -export interface ListServiceInterfacesParams { packageId: string | null, callback: Callback, } \ No newline at end of file +export type ListServiceInterfacesParams = { packageId: string | null, callback: Callback, }; \ No newline at end of file diff --git a/core/startos/bindings/MountParams.ts b/core/startos/bindings/MountParams.ts index 26e8373c7..4f8615a25 100644 --- a/core/startos/bindings/MountParams.ts +++ b/core/startos/bindings/MountParams.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 { MountTarget } from "./MountTarget"; -export interface MountParams { location: string, target: MountTarget, } \ No newline at end of file +export type MountParams = { location: string, target: MountTarget, }; \ No newline at end of file diff --git a/core/startos/bindings/MountTarget.ts b/core/startos/bindings/MountTarget.ts index 6cbee7aa6..6a28ec3ac 100644 --- a/core/startos/bindings/MountTarget.ts +++ b/core/startos/bindings/MountTarget.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 interface MountTarget { packageId: string, volumeId: string, subpath: string | null, readonly: boolean, } \ No newline at end of file +export type MountTarget = { packageId: string, volumeId: string, subpath: string | null, readonly: boolean, }; \ No newline at end of file diff --git a/core/startos/bindings/ParamsMaybePackageId.ts b/core/startos/bindings/ParamsMaybePackageId.ts index 80bdd9a55..867eb2a51 100644 --- a/core/startos/bindings/ParamsMaybePackageId.ts +++ b/core/startos/bindings/ParamsMaybePackageId.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 interface ParamsMaybePackageId { packageId: string | null, } \ No newline at end of file +export type ParamsMaybePackageId = { packageId: string | null, }; \ No newline at end of file diff --git a/core/startos/bindings/ParamsPackageId.ts b/core/startos/bindings/ParamsPackageId.ts index 7631cfb11..aa3caec92 100644 --- a/core/startos/bindings/ParamsPackageId.ts +++ b/core/startos/bindings/ParamsPackageId.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 interface ParamsPackageId { packageId: string, } \ No newline at end of file +export type ParamsPackageId = { packageId: string, }; \ No newline at end of file diff --git a/core/startos/bindings/RemoveActionParams.ts b/core/startos/bindings/RemoveActionParams.ts index 3e0c3c48d..ae2faccbe 100644 --- a/core/startos/bindings/RemoveActionParams.ts +++ b/core/startos/bindings/RemoveActionParams.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 interface RemoveActionParams { id: string, } \ No newline at end of file +export type RemoveActionParams = { id: string, }; \ No newline at end of file diff --git a/core/startos/bindings/RemoveAddressParams.ts b/core/startos/bindings/RemoveAddressParams.ts index 1bc483f89..2b9881ce1 100644 --- a/core/startos/bindings/RemoveAddressParams.ts +++ b/core/startos/bindings/RemoveAddressParams.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 interface RemoveAddressParams { id: string, } \ No newline at end of file +export type RemoveAddressParams = { id: string, }; \ No newline at end of file diff --git a/core/startos/bindings/ReverseProxyBind.ts b/core/startos/bindings/ReverseProxyBind.ts index 3f9e0dc25..6c2d5a586 100644 --- a/core/startos/bindings/ReverseProxyBind.ts +++ b/core/startos/bindings/ReverseProxyBind.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 interface ReverseProxyBind { ip: string | null, port: number, ssl: boolean, } \ No newline at end of file +export type ReverseProxyBind = { ip: string | null, port: number, ssl: boolean, }; \ No newline at end of file diff --git a/core/startos/bindings/ReverseProxyDestination.ts b/core/startos/bindings/ReverseProxyDestination.ts index a6003e189..8786f1008 100644 --- a/core/startos/bindings/ReverseProxyDestination.ts +++ b/core/startos/bindings/ReverseProxyDestination.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 interface ReverseProxyDestination { ip: string | null, port: number, ssl: boolean, } \ No newline at end of file +export type ReverseProxyDestination = { ip: string | null, port: number, ssl: boolean, }; \ No newline at end of file diff --git a/core/startos/bindings/ReverseProxyHttp.ts b/core/startos/bindings/ReverseProxyHttp.ts index 5c1d0aa1b..f7b652e6a 100644 --- a/core/startos/bindings/ReverseProxyHttp.ts +++ b/core/startos/bindings/ReverseProxyHttp.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 interface ReverseProxyHttp { headers: null | {[key: string]: string}, } \ No newline at end of file +export type ReverseProxyHttp = { headers: null | {[key: string]: string}, }; \ No newline at end of file diff --git a/core/startos/bindings/ReverseProxyParams.ts b/core/startos/bindings/ReverseProxyParams.ts index bde3034e9..bc6d3016c 100644 --- a/core/startos/bindings/ReverseProxyParams.ts +++ b/core/startos/bindings/ReverseProxyParams.ts @@ -3,4 +3,4 @@ import type { ReverseProxyBind } from "./ReverseProxyBind"; import type { ReverseProxyDestination } from "./ReverseProxyDestination"; import type { ReverseProxyHttp } from "./ReverseProxyHttp"; -export interface ReverseProxyParams { bind: ReverseProxyBind, dst: ReverseProxyDestination, http: ReverseProxyHttp, } \ No newline at end of file +export type ReverseProxyParams = { bind: ReverseProxyBind, dst: ReverseProxyDestination, http: ReverseProxyHttp, }; \ No newline at end of file diff --git a/core/startos/bindings/SetConfigured.ts b/core/startos/bindings/SetConfigured.ts index 05e1c7f83..8f191b562 100644 --- a/core/startos/bindings/SetConfigured.ts +++ b/core/startos/bindings/SetConfigured.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 interface SetConfigured { configured: boolean, } \ No newline at end of file +export type SetConfigured = { configured: boolean, }; \ No newline at end of file diff --git a/core/startos/bindings/SetDependenciesParams.ts b/core/startos/bindings/SetDependenciesParams.ts index 7741cddde..b313b0718 100644 --- a/core/startos/bindings/SetDependenciesParams.ts +++ b/core/startos/bindings/SetDependenciesParams.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 { DependencyRequirement } from "./DependencyRequirement"; -export interface SetDependenciesParams { dependencies: Array, } \ No newline at end of file +export type SetDependenciesParams = { dependencies: Array, }; \ No newline at end of file diff --git a/core/startos/bindings/SetHealth.ts b/core/startos/bindings/SetHealth.ts index 3635ec9c5..321b5c97b 100644 --- a/core/startos/bindings/SetHealth.ts +++ b/core/startos/bindings/SetHealth.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 { HealthCheckString } from "./HealthCheckString"; -export interface SetHealth { name: string, status: HealthCheckString, message: string | null, } \ No newline at end of file +export type SetHealth = { name: string, status: HealthCheckString, message: string | null, }; \ No newline at end of file diff --git a/core/startos/bindings/SetMainStatus.ts b/core/startos/bindings/SetMainStatus.ts index 86e7d9b2f..0476ff4ce 100644 --- a/core/startos/bindings/SetMainStatus.ts +++ b/core/startos/bindings/SetMainStatus.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 { Status } from "./Status"; -export interface SetMainStatus { status: Status, } \ No newline at end of file +export type SetMainStatus = { status: Status, }; \ No newline at end of file diff --git a/core/startos/bindings/SetStoreParams.ts b/core/startos/bindings/SetStoreParams.ts index 9be56724e..e3496a171 100644 --- a/core/startos/bindings/SetStoreParams.ts +++ b/core/startos/bindings/SetStoreParams.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 interface SetStoreParams { value: any, path: string, } \ No newline at end of file +export type SetStoreParams = { value: any, path: string, }; \ No newline at end of file diff --git a/core/startos/src/auth.rs b/core/startos/src/auth.rs index 4d3c20bd1..7adce72bc 100644 --- a/core/startos/src/auth.rs +++ b/core/startos/src/auth.rs @@ -93,7 +93,6 @@ pub fn auth() -> ParentHandler { from_fn_async(logout) .with_metadata("get-session", Value::Bool(true)) .with_remote_cli::() - // TODO @dr-bonez .no_display(), ) .subcommand("session", session()) diff --git a/core/startos/src/backup/mod.rs b/core/startos/src/backup/mod.rs index de2dfbf7d..c963118c4 100644 --- a/core/startos/src/backup/mod.rs +++ b/core/startos/src/backup/mod.rs @@ -45,6 +45,15 @@ pub fn backup() -> ParentHandler { .subcommand("target", target::target()) } +pub fn package_backup() -> ParentHandler { + ParentHandler::new().subcommand( + "restore", + from_fn_async(restore::restore_packages_rpc) + .no_display() + .with_remote_cli::(), + ) +} + #[derive(Deserialize, Serialize)] struct BackupMetadata { pub timestamp: DateTime, diff --git a/core/startos/src/backup/restore.rs b/core/startos/src/backup/restore.rs index 6001c335a..70695b42e 100644 --- a/core/startos/src/backup/restore.rs +++ b/core/startos/src/backup/restore.rs @@ -33,7 +33,6 @@ pub struct RestorePackageParams { pub password: String, } -// TODO dr Why doesn't anything use this // #[command(rename = "restore", display(display_none))] #[instrument(skip(ctx, password))] pub async fn restore_packages_rpc( @@ -71,7 +70,7 @@ pub async fn restore_packages_rpc( pub async fn recover_full_embassy( ctx: SetupContext, disk_guid: Arc, - embassy_password: String, + start_os_password: String, recovery_source: TmpMountGuard, recovery_password: Option, ) -> Result<(Arc, Hostname, OnionAddressV3, X509), Error> { @@ -89,7 +88,7 @@ pub async fn recover_full_embassy( )?; os_backup.account.password = argon2::hash_encoded( - embassy_password.as_bytes(), + start_os_password.as_bytes(), &rand::random::<[u8; 16]>()[..], &argon2::Config::rfc9106_low_mem(), ) diff --git a/core/startos/src/backup/target/cifs.rs b/core/startos/src/backup/target/cifs.rs index d4b65b7fe..a3bbb9759 100644 --- a/core/startos/src/backup/target/cifs.rs +++ b/core/startos/src/backup/target/cifs.rs @@ -42,7 +42,7 @@ pub struct CifsBackupTarget { path: PathBuf, username: String, mountable: bool, - embassy_os: Option, + start_os: Option, } pub fn cifs() -> ParentHandler { @@ -93,7 +93,7 @@ pub async fn add( password, }; let guard = TmpMountGuard::mount(&cifs, ReadOnly).await?; - let embassy_os = recovery_info(guard.path()).await?; + let start_os = recovery_info(guard.path()).await?; guard.unmount().await?; let id = ctx .db @@ -116,7 +116,7 @@ pub async fn add( path: cifs.path, username: cifs.username, mountable: true, - embassy_os, + start_os, }), }) } @@ -157,7 +157,7 @@ pub async fn update( password, }; let guard = TmpMountGuard::mount(&cifs, ReadOnly).await?; - let embassy_os = recovery_info(guard.path()).await?; + let start_os = recovery_info(guard.path()).await?; guard.unmount().await?; ctx.db .mutate(|db| { @@ -180,7 +180,7 @@ pub async fn update( path: cifs.path, username: cifs.username, mountable: true, - embassy_os, + start_os, }), }) } @@ -224,11 +224,11 @@ pub async fn list(db: &DatabaseModel) -> Result, Er let mut cifs = Vec::new(); for (id, model) in db.as_private().as_cifs().as_entries()? { let mount_info = model.de()?; - let embassy_os = async { + let start_os = async { let guard = TmpMountGuard::mount(&mount_info, ReadOnly).await?; - let embassy_os = recovery_info(guard.path()).await?; + let start_os = recovery_info(guard.path()).await?; guard.unmount().await?; - Ok::<_, Error>(embassy_os) + Ok::<_, Error>(start_os) } .await; cifs.push(( @@ -237,8 +237,8 @@ pub async fn list(db: &DatabaseModel) -> Result, Er hostname: mount_info.hostname, path: mount_info.path, username: mount_info.username, - mountable: embassy_os.is_ok(), - embassy_os: embassy_os.ok().and_then(|a| a), + mountable: start_os.is_ok(), + start_os: start_os.ok().and_then(|a| a), }, )); } diff --git a/core/startos/src/config/mod.rs b/core/startos/src/config/mod.rs index bcd44193b..18f67b05a 100644 --- a/core/startos/src/config/mod.rs +++ b/core/startos/src/config/mod.rs @@ -165,7 +165,6 @@ pub struct SetParams { pub config: StdinDeserializable>, } -// TODO Dr Why isn't this used? // #[command( // subcommands(self(set_impl(async, context(RpcContext))), set_dry), // display(display_none), diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index f987ffbf4..6450eb561 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -19,7 +19,6 @@ use super::setup::CURRENT_SECRET; use crate::account::AccountInfo; use crate::context::config::ServerConfig; use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation, WebSocketHandler}; -use crate::db::model::package::CurrentDependents; use crate::db::prelude::PatchDbExt; use crate::dependencies::compute_dependency_config_errs; use crate::disk::OsPartitionInfo; @@ -207,39 +206,6 @@ impl RpcContext { #[instrument(skip(self))] pub async fn cleanup_and_initialize(&self) -> Result<(), Error> { - self.db - .mutate(|f| { - let mut current_dependents = f - .as_public_mut() - .as_package_data() - .keys()? - .into_iter() - .map(|k| (k.clone(), BTreeMap::new())) - .collect::>(); - for (package_id, package) in - f.as_public_mut().as_package_data_mut().as_entries_mut()? - { - for (k, v) in package.clone().into_current_dependencies().into_entries()? { - let mut entry: BTreeMap<_, _> = - current_dependents.remove(&k).unwrap_or_default(); - entry.insert(package_id.clone(), v.de()?); - current_dependents.insert(k, entry); - } - } - for (package_id, current_dependents) in current_dependents { - if let Some(deps) = f - .as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package_id) - .map(|i| i.as_current_dependents_mut()) - { - deps.ser(&CurrentDependents(current_dependents))?; - } - } - Ok(()) - }) - .await?; - self.services.init(&self).await?; tracing::info!("Initialized Package Managers"); diff --git a/core/startos/src/db/model/package.rs b/core/startos/src/db/model/package.rs index d9e702a0d..05d6e73b7 100644 --- a/core/startos/src/db/model/package.rs +++ b/core/startos/src/db/model/package.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use chrono::{DateTime, Utc}; +use emver::VersionRange; use imbl_value::InternedString; use models::{DataUrl, HealthCheckId, HostId, PackageId}; use patch_db::json_ptr::JsonPointer; @@ -299,7 +300,6 @@ pub struct PackageDataEntry { pub icon: DataUrl<'static>, pub last_backup: Option>, pub dependency_info: BTreeMap, - pub current_dependents: CurrentDependents, pub current_dependencies: CurrentDependencies, pub interface_addresses: InterfaceAddressMap, pub hosts: HostInfo, @@ -357,29 +357,6 @@ impl Default for ExposedUI { } } -#[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct CurrentDependents(pub BTreeMap); -impl CurrentDependents { - pub fn map( - mut self, - transform: impl Fn( - BTreeMap, - ) -> BTreeMap, - ) -> Self { - self.0 = transform(self.0); - self - } -} -impl Map for CurrentDependents { - type Key = PackageId; - type Value = CurrentDependencyInfo; - fn key_str(key: &Self::Key) -> Result, Error> { - Ok(key) - } - fn key_string(key: &Self::Key) -> Result { - Ok(key.clone().into()) - } -} #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct CurrentDependencies(pub BTreeMap); impl CurrentDependencies { @@ -416,10 +393,21 @@ pub struct StaticDependencyInfo { #[serde(rename_all = "camelCase")] #[serde(tag = "kind")] pub enum CurrentDependencyInfo { - Exists, + #[serde(rename_all = "camelCase")] + Exists { + #[ts(type = "string")] + url: Url, + #[ts(type = "string")] + version_spec: VersionRange, + }, #[serde(rename_all = "camelCase")] Running { + #[ts(type = "string")] + url: Url, + #[ts(type = "string")] + version_spec: VersionRange, #[serde(default)] + #[ts(type = "string[]")] health_checks: BTreeSet, }, } diff --git a/core/startos/src/dependencies.rs b/core/startos/src/dependencies.rs index 168713f61..ca40bf432 100644 --- a/core/startos/src/dependencies.rs +++ b/core/startos/src/dependencies.rs @@ -56,8 +56,6 @@ pub struct DepInfo { pub version: VersionRange, pub requirement: DependencyRequirement, pub description: Option, - #[serde(default)] - pub config: Option, // TODO: remove } #[derive(Deserialize, Serialize, Parser)] diff --git a/core/startos/src/disk/mod.rs b/core/startos/src/disk/mod.rs index be2d07bce..f74c944a4 100644 --- a/core/startos/src/disk/mod.rs +++ b/core/startos/src/disk/mod.rs @@ -101,7 +101,7 @@ fn display_disk_info(params: WithIoFormat, args: Vec) { } else { "N/A" }, - if let Some(eos) = part.embassy_os.as_ref() { + if let Some(eos) = part.start_os.as_ref() { eos.version.as_str() } else { "N/A" diff --git a/core/startos/src/disk/util.rs b/core/startos/src/disk/util.rs index be0b16a6b..b0bc00a5d 100644 --- a/core/startos/src/disk/util.rs +++ b/core/startos/src/disk/util.rs @@ -49,7 +49,7 @@ pub struct PartitionInfo { pub label: Option, pub capacity: u64, pub used: Option, - pub embassy_os: Option, + pub start_os: Option, pub guid: Option, } @@ -390,7 +390,7 @@ async fn disk_info(disk: PathBuf) -> DiskInfo { } async fn part_info(part: PathBuf) -> PartitionInfo { - let mut embassy_os = None; + let mut start_os = None; let label = get_label(&part) .await .map_err(|e| tracing::warn!("Could not get label of {}: {}", part.display(), e.source)) @@ -417,7 +417,7 @@ async fn part_info(part: PathBuf) -> PartitionInfo { None } } { - embassy_os = Some(recovery_info) + start_os = Some(recovery_info) } if let Err(e) = mount_guard.unmount().await { tracing::error!("Error unmounting partition {}: {}", part.display(), e); @@ -430,7 +430,7 @@ async fn part_info(part: PathBuf) -> PartitionInfo { label, capacity, used, - embassy_os, + start_os, guid: None, } } diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index d19f9c61a..0b3336783 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -236,7 +236,7 @@ pub fn package() -> ParentHandler { .with_remote_cli::(), ) .subcommand("dependency", dependencies::dependency()) - .subcommand("package-backup", backup::backup()) + .subcommand("backup", backup::package_backup()) .subcommand("connect", from_fn_async(service::connect_rpc).no_cli()) .subcommand( "connect", diff --git a/core/startos/src/net/ssl.rs b/core/startos/src/net/ssl.rs index 0756114c5..245881c55 100644 --- a/core/startos/src/net/ssl.rs +++ b/core/startos/src/net/ssl.rs @@ -183,7 +183,6 @@ pub async fn root_ca_start_time() -> Result { const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1; lazy_static::lazy_static! { static ref EC_GROUP: EcGroup = EcGroup::from_curve_name(EC_CURVE_NAME).unwrap(); - static ref SSL_MUTEX: Mutex<()> = Mutex::new(()); // TODO: make thread safe } pub async fn export_key(key: &PKey, target: &Path) -> Result<(), Error> { diff --git a/core/startos/src/net/static_server.rs b/core/startos/src/net/static_server.rs index fec881795..e8207e3bf 100644 --- a/core/startos/src/net/static_server.rs +++ b/core/startos/src/net/static_server.rs @@ -168,7 +168,7 @@ pub fn main_ui_server_router(ctx: RpcContext) -> Router { }), ) .fallback(any(move |request: Request| async move { - main_embassy_ui(request, ctx) + main_start_os_ui(request, ctx) .await .unwrap_or_else(server_error) })) @@ -218,7 +218,7 @@ async fn if_authorized< } } -async fn main_embassy_ui(req: Request, ctx: RpcContext) -> Result { +async fn main_start_os_ui(req: Request, ctx: RpcContext) -> Result { let (request_parts, _body) = req.into_parts(); match ( &request_parts.method, diff --git a/core/startos/src/net/tor.rs b/core/startos/src/net/tor.rs index 72b05e2aa..c87f506dc 100644 --- a/core/startos/src/net/tor.rs +++ b/core/startos/src/net/tor.rs @@ -768,7 +768,7 @@ async fn test() { let mut conn = torut::control::UnauthenticatedConn::new( TcpStream::connect(SocketAddr::from(([127, 0, 0, 1], 9051))) .await - .unwrap(), // TODO + .unwrap(), ); let auth = conn .load_protocol_info() diff --git a/core/startos/src/os_install/mod.rs b/core/startos/src/os_install/mod.rs index 55c333fba..6d55741f4 100644 --- a/core/startos/src/os_install/mod.rs +++ b/core/startos/src/os_install/mod.rs @@ -7,6 +7,7 @@ use rpc_toolkit::{command, from_fn_async, AnyContext, HandlerExt, ParentHandler} use serde::{Deserialize, Serialize}; use tokio::process::Command; +use crate::context::config::ServerConfig; use crate::context::{CliContext, InstallContext}; use crate::disk::mount::filesystem::bind::Bind; use crate::disk::mount::filesystem::block_dev::BlockDev; @@ -23,14 +24,6 @@ use crate::ARCH; mod gpt; mod mbr; -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PostInstallConfig { - os_partitions: OsPartitionInfo, - ethernet_interface: String, - wifi_interface: Option, -} - pub fn install() -> ParentHandler { ParentHandler::new() .subcommand("disk", disk()) @@ -263,10 +256,11 @@ pub async fn execute( tokio::fs::write( rootfs.path().join("config/config.yaml"), - IoFormat::Yaml.to_vec(&PostInstallConfig { - os_partitions: part_info.clone(), - ethernet_interface: eth_iface, + IoFormat::Yaml.to_vec(&ServerConfig { + os_partitions: Some(part_info.clone()), + ethernet_interface: Some(eth_iface), wifi_interface: wifi_iface, + ..Default::default() })?, ) .await?; @@ -299,7 +293,7 @@ pub async fn execute( .invoke(crate::ErrorKind::OpenSsh) .await?; - let embassy_fs = MountGuard::mount( + let start_os_fs = MountGuard::mount( &Bind::new(rootfs.path()), current.join("media/embassy/embassyfs"), MountType::ReadOnly, @@ -348,7 +342,7 @@ pub async fn execute( } sys.unmount(false).await?; proc.unmount(false).await?; - embassy_fs.unmount(false).await?; + start_os_fs.unmount(false).await?; if let Some(efi) = efi { efi.unmount(false).await?; } diff --git a/core/startos/src/properties.rs b/core/startos/src/properties.rs index cfb0dd66b..5482bdb58 100644 --- a/core/startos/src/properties.rs +++ b/core/startos/src/properties.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::{borrow::Borrow, collections::BTreeMap}; use clap::Parser; use imbl_value::{json, InOMap, InternedString, Value}; @@ -15,48 +15,6 @@ pub fn display_properties(response: Value) { println!("{}", response); } -trait IntoProperties { - fn into_properties(self, store: &Value) -> Value; -} -impl IntoProperties for ExposedUI { - fn into_properties(self, store: &Value) -> Value { - match self { - ExposedUI::Object { value, description } => { - json!({ - "type": "object", - "description": description, - "value": value.into_iter().map(|(k, v)| (k, v.into_properties(store))).collect::>() - }) - } - ExposedUI::String { - path, - description, - masked, - copyable, - qr, - } => json!({ - "type": "string", - "description": description, - "value": path.get(store).cloned().unwrap_or_default(), - "copyable": copyable, - "qr": qr, - "masked": masked - }), - } - } -} - -impl IntoProperties for StoreExposedUI { - fn into_properties(self, store: &Value) -> Value { - Value::Object( - self.0 - .into_iter() - .map(|(k, v)| (k, v.into_properties(store))) - .collect::>(), - ) - } -} - #[derive(Deserialize, Serialize, Parser)] #[serde(rename_all = "camelCase")] #[command(rename_all = "kebab-case")] @@ -68,19 +26,11 @@ pub async fn properties( ctx: RpcContext, PropertiesParam { id }: PropertiesParam, ) -> Result { - let peeked = ctx.db.peek().await; - let data = peeked - .as_private() - .as_package_stores() - .as_idx(&id) - .map(|x| x.de()) - .unwrap_or_else(|| Ok(json!({})))?; - Ok(peeked - .as_public() - .as_package_data() - .as_idx(&id) - .or_not_found(&id)? - .as_store_exposed_ui() - .de()? - .into_properties(&data)) + match &*ctx.services.get(&id).await { + Some(service) => service.properties().await, + None => Err(Error::new( + eyre!("Could not find a service with id {id}"), + ErrorKind::NotFound, + )), + } } diff --git a/core/startos/src/s9pk/rpc.rs b/core/startos/src/s9pk/rpc.rs index e11faa2ff..3a98da02b 100644 --- a/core/startos/src/s9pk/rpc.rs +++ b/core/startos/src/s9pk/rpc.rs @@ -132,7 +132,7 @@ async fn add_image( Command::new("bash") .arg("-c") .arg(format!( - "{CONTAINER_TOOL} export {container_id} | mksquashfs - {sqfs} -tar -force-uid 100000 -force-gid 100000", // TODO: real uid mapping + "{CONTAINER_TOOL} export {container_id} | mksquashfs - {sqfs} -tar", container_id = container_id.trim(), sqfs = sqfs_path.display() )) diff --git a/core/startos/src/s9pk/v1/manifest.rs b/core/startos/src/s9pk/v1/manifest.rs index 381a446fe..813ad11a6 100644 --- a/core/startos/src/s9pk/v1/manifest.rs +++ b/core/startos/src/s9pk/v1/manifest.rs @@ -1,7 +1,9 @@ +use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use imbl_value::InOMap; pub use models::PackageId; +use models::VolumeId; use serde::{Deserialize, Serialize}; use url::Url; @@ -11,7 +13,6 @@ use crate::prelude::*; use crate::s9pk::manifest::{Alerts, Description, HardwareRequirements}; use crate::util::Version; use crate::version::{Current, VersionT}; -use crate::volume::Volumes; fn current_version() -> Version { Current::new().semver().into() @@ -40,7 +41,7 @@ pub struct Manifest { pub donation_url: Option, #[serde(default)] pub alerts: Alerts, - pub volumes: Volumes, + pub volumes: BTreeMap, #[serde(default)] pub dependencies: Dependencies, pub config: Option>, diff --git a/core/startos/src/s9pk/v2/compat.rs b/core/startos/src/s9pk/v2/compat.rs index e2ef8bdbb..423491e7a 100644 --- a/core/startos/src/s9pk/v2/compat.rs +++ b/core/startos/src/s9pk/v2/compat.rs @@ -19,7 +19,6 @@ use crate::s9pk::v1::reader::S9pkReader; use crate::s9pk::v2::S9pk; use crate::util::io::TmpDir; use crate::util::Invoke; -use crate::volume::Volume; use crate::ARCH; pub const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x01]; @@ -254,7 +253,7 @@ impl S9pk> { for (asset_id, _) in manifest .volumes .iter() - .filter(|(_, v)| matches!(v, Volume::Assets { .. })) + .filter(|(_, v)| v.get("type").and_then(|v| v.as_str()) == Some("assets")) { let assets_path = asset_dir.join(&asset_id); let sqfs_path = assets_path.with_extension("squashfs"); @@ -338,13 +337,13 @@ impl From for Manifest { assets: value .volumes .iter() - .filter(|(_, v)| matches!(v, &&Volume::Assets { .. })) + .filter(|(_, v)| v.get("type").and_then(|v| v.as_str()) == Some("assets")) .map(|(id, _)| id.clone()) .collect(), volumes: value .volumes .iter() - .filter(|(_, v)| matches!(v, &&Volume::Data { .. })) + .filter(|(_, v)| v.get("type").and_then(|v| v.as_str()) == Some("data")) .map(|(id, _)| id.clone()) .collect(), alerts: value.alerts, diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index fb521ca34..2feb4ca34 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -325,6 +325,17 @@ impl Service { .await .with_kind(ErrorKind::Action) } + pub async fn properties(&self) -> Result { + let container = &self.seed.persistent_container; + container + .execute::( + ProcedureName::Properties, + Value::Null, + Some(Duration::from_secs(30)), + ) + .await + .with_kind(ErrorKind::Unknown) + } pub async fn shutdown(self) -> Result<(), Error> { self.actor diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index c3a49ac64..b3eaf365c 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -8,6 +8,7 @@ use std::sync::{Arc, Weak}; use clap::builder::ValueParserFactory; use clap::Parser; +use emver::VersionRange; use imbl::OrdMap; use imbl_value::{json, InternedString}; use models::{ActionId, HealthCheckId, ImageId, PackageId, VolumeId}; @@ -16,6 +17,7 @@ use rpc_toolkit::{from_fn, from_fn_async, AnyContext, Context, Empty, HandlerExt use serde::{Deserialize, Serialize}; use tokio::process::Command; use ts_rs::TS; +use url::Url; use crate::db::model::package::{ CurrentDependencies, CurrentDependencyInfo, ExposedUI, StoreExposedUI, @@ -255,7 +257,7 @@ struct RemoveAddressParams { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all = "camelCase")] enum AllowedStatuses { OnlyRunning, // onlyRunning OnlyStopped, @@ -1096,15 +1098,19 @@ enum DependencyRequirement { id: PackageId, #[ts(type = "string[]")] health_checks: BTreeSet, - version_spec: String, - url: String, + #[ts(type = "string")] + version_spec: VersionRange, + #[ts(type = "string")] + url: Url, }, #[serde(rename_all = "camelCase")] Exists { #[ts(type = "string")] id: PackageId, - version_spec: String, - url: String, + #[ts(type = "string")] + version_spec: VersionRange, + #[ts(type = "string")] + url: Url, }, } // filebrowser:exists,bitcoind:running:foo+bar+baz @@ -1114,8 +1120,8 @@ impl FromStr for DependencyRequirement { match s.split_once(':') { Some((id, "e")) | Some((id, "exists")) => Ok(Self::Exists { id: id.parse()?, - url: "".to_string(), - version_spec: "*".to_string(), + url: "".parse()?, // TODO + version_spec: "*".parse()?, // TODO }), Some((id, rest)) => { let health_checks = match rest.split_once(':') { @@ -1138,15 +1144,15 @@ impl FromStr for DependencyRequirement { Ok(Self::Running { id: id.parse()?, health_checks, - url: "".to_string(), - version_spec: "*".to_string(), + url: "".parse()?, // TODO + version_spec: "*".parse()?, // TODO }) } None => Ok(Self::Running { id: s.parse()?, health_checks: BTreeSet::new(), - url: "".to_string(), - version_spec: "*".to_string(), + url: "".parse()?, // TODO + version_spec: "*".parse()?, // TODO }), } } @@ -1183,23 +1189,23 @@ async fn set_dependencies( id, url, version_spec, - } => (id, CurrentDependencyInfo::Exists), + } => (id, CurrentDependencyInfo::Exists { url, version_spec }), DependencyRequirement::Running { id, health_checks, url, version_spec, - } => (id, CurrentDependencyInfo::Running { health_checks }), + } => ( + id, + CurrentDependencyInfo::Running { + url, + version_spec, + health_checks, + }, + ), }) .collect(), ); - for (dep, entry) in db.as_public_mut().as_package_data_mut().as_entries_mut()? { - if let Some(info) = dependencies.0.get(&dep) { - entry.as_current_dependents_mut().insert(id, info)?; - } else { - entry.as_current_dependents_mut().remove(id)?; - } - } db.as_public_mut() .as_package_data_mut() .as_idx_mut(id) diff --git a/core/startos/src/service/service_map.rs b/core/startos/src/service/service_map.rs index 23e1bb540..1a9d2342c 100644 --- a/core/startos/src/service/service_map.rs +++ b/core/startos/src/service/service_map.rs @@ -171,7 +171,6 @@ impl ServiceMap { icon, last_backup: None, dependency_info: Default::default(), - current_dependents: Default::default(), // TODO: initialize current_dependencies: Default::default(), interface_addresses: Default::default(), hosts: Default::default(), diff --git a/core/startos/src/setup.rs b/core/startos/src/setup.rs index 2e5c7b3ad..ad76066e8 100644 --- a/core/startos/src/setup.rs +++ b/core/startos/src/setup.rs @@ -240,9 +240,9 @@ pub async fn verify_cifs( ReadWrite, ) .await?; - let embassy_os = recovery_info(guard.path()).await?; + let start_os = recovery_info(guard.path()).await?; guard.unmount().await?; - embassy_os.ok_or_else(|| Error::new(eyre!("No Backup Found"), crate::ErrorKind::NotFound)) + start_os.ok_or_else(|| Error::new(eyre!("No Backup Found"), crate::ErrorKind::NotFound)) } #[derive(Debug, Deserialize, Serialize)] @@ -256,8 +256,8 @@ pub enum RecoverySource { #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ExecuteParams { - embassy_logicalname: PathBuf, - embassy_password: EncryptedWire, + start_os_logicalname: PathBuf, + start_os_password: EncryptedWire, recovery_source: Option, recovery_password: Option, } @@ -266,13 +266,13 @@ pub struct ExecuteParams { pub async fn execute( ctx: SetupContext, ExecuteParams { - embassy_logicalname, - embassy_password, + start_os_logicalname, + start_os_password, recovery_source, recovery_password, }: ExecuteParams, ) -> Result<(), Error> { - let embassy_password = match embassy_password.decrypt(&*ctx) { + let start_os_password = match start_os_password.decrypt(&*ctx) { Some(a) => a, None => { return Err(Error::new( @@ -311,8 +311,8 @@ pub async fn execute( let ctx = ctx.clone(); match execute_inner( ctx.clone(), - embassy_logicalname, - embassy_password, + start_os_logicalname, + start_os_password, recovery_source, recovery_password, ) @@ -375,8 +375,8 @@ pub async fn exit(ctx: SetupContext) -> Result<(), Error> { #[instrument(skip_all)] pub async fn execute_inner( ctx: SetupContext, - embassy_logicalname: PathBuf, - embassy_password: String, + start_os_logicalname: PathBuf, + start_os_password: String, recovery_source: Option, recovery_password: Option, ) -> Result<(Arc, Hostname, OnionAddressV3, X509), Error> { @@ -387,7 +387,7 @@ pub async fn execute_inner( }; let guid = Arc::new( crate::disk::main::create( - &[embassy_logicalname], + &[start_os_logicalname], &pvscan().await?, &ctx.datadir, encryption_password, @@ -403,20 +403,20 @@ pub async fn execute_inner( .await?; if let Some(RecoverySource::Backup { target }) = recovery_source { - recover(ctx, guid, embassy_password, target, recovery_password).await + recover(ctx, guid, start_os_password, target, recovery_password).await } else if let Some(RecoverySource::Migrate { guid: old_guid }) = recovery_source { - migrate(ctx, guid, &old_guid, embassy_password).await + migrate(ctx, guid, &old_guid, start_os_password).await } else { - let (hostname, tor_addr, root_ca) = fresh_setup(&ctx, &embassy_password).await?; + let (hostname, tor_addr, root_ca) = fresh_setup(&ctx, &start_os_password).await?; Ok((guid, hostname, tor_addr, root_ca)) } } async fn fresh_setup( ctx: &SetupContext, - embassy_password: &str, + start_os_password: &str, ) -> Result<(Hostname, OnionAddressV3, X509), Error> { - let account = AccountInfo::new(embassy_password, root_ca_start_time().await?)?; + let account = AccountInfo::new(start_os_password, root_ca_start_time().await?)?; let db = ctx.db().await?; db.put(&ROOT, &Database::init(&account)?).await?; drop(db); @@ -432,7 +432,7 @@ async fn fresh_setup( async fn recover( ctx: SetupContext, guid: Arc, - embassy_password: String, + start_os_password: String, recovery_source: BackupTargetFS, recovery_password: Option, ) -> Result<(Arc, Hostname, OnionAddressV3, X509), Error> { @@ -440,7 +440,7 @@ async fn recover( recover_full_embassy( ctx, guid.clone(), - embassy_password, + start_os_password, recovery_source, recovery_password, ) @@ -452,7 +452,7 @@ async fn migrate( ctx: SetupContext, guid: Arc, old_guid: &str, - embassy_password: String, + start_os_password: String, ) -> Result<(Arc, Hostname, OnionAddressV3, X509), Error> { *ctx.setup_status.write().await = Some(Ok(SetupStatus { bytes_transferred: 0, @@ -537,7 +537,7 @@ async fn migrate( } => res, } - let (hostname, tor_addr, root_ca) = setup_init(&ctx, Some(embassy_password)).await?; + let (hostname, tor_addr, root_ca) = setup_init(&ctx, Some(start_os_password)).await?; crate::disk::main::export(&old_guid, "/media/embassy/migrate").await?; diff --git a/core/startos/src/volume.rs b/core/startos/src/volume.rs index a631e68e6..593422a67 100644 --- a/core/startos/src/volume.rs +++ b/core/startos/src/volume.rs @@ -1,15 +1,9 @@ -use std::collections::BTreeMap; -use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; pub use helpers::script_dir; -use imbl_value::InternedString; pub use models::VolumeId; use models::{HostId, PackageId}; -use serde::{Deserialize, Serialize}; -use tracing::instrument; -use crate::context::RpcContext; use crate::net::PACKAGE_CERT_PATH; use crate::prelude::*; use crate::util::Version; @@ -17,72 +11,6 @@ use crate::util::Version; pub const PKG_VOLUME_DIR: &str = "package-data/volumes"; pub const BACKUP_DIR: &str = "/media/embassy/backups"; -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Volumes(BTreeMap); -impl Volumes { - #[instrument(skip_all)] - pub async fn install( - &self, - ctx: &RpcContext, - pkg_id: &PackageId, - version: &Version, - ) -> Result<(), Error> { - for (volume_id, volume) in &self.0 { - volume - .install(&ctx.datadir, pkg_id, version, volume_id) - .await?; // TODO: concurrent? - } - Ok(()) - } - pub fn get_path_for( - &self, - path: &PathBuf, - pkg_id: &PackageId, - version: &Version, - volume_id: &VolumeId, - ) -> Option { - self.0 - .get(volume_id) - .map(|volume| volume.path_for(path, pkg_id, version, volume_id)) - } - pub fn to_readonly(&self) -> Self { - Volumes( - self.0 - .iter() - .map(|(id, volume)| { - let mut volume = volume.clone(); - volume.set_readonly(); - (id.clone(), volume) - }) - .collect(), - ) - } -} -impl Deref for Volumes { - type Target = BTreeMap; - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl DerefMut for Volumes { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} -impl Map for Volumes { - type Key = VolumeId; - type Value = Volume; - fn key_str(key: &Self::Key) -> Result, Error> { - Ok(key) - } - fn key_string(key: &Self::Key) -> Result { - match key { - VolumeId::Custom(id) => Ok(id.clone().into()), - _ => Self::key_str(key).map(|s| InternedString::intern(s.as_ref())), - } - } -} - pub fn data_dir>(datadir: P, pkg_id: &PackageId, volume_id: &VolumeId) -> PathBuf { datadir .as_ref() @@ -108,114 +36,3 @@ pub fn backup_dir(pkg_id: &PackageId) -> PathBuf { pub fn cert_dir(pkg_id: &PackageId, host_id: &HostId) -> PathBuf { Path::new(PACKAGE_CERT_PATH).join(pkg_id).join(host_id) } - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -#[serde(rename_all = "camelCase")] -pub enum Volume { - #[serde(rename_all = "camelCase")] - Data { - #[serde(skip)] - readonly: bool, - }, - #[serde(rename_all = "camelCase")] - Assets {}, - #[serde(rename_all = "camelCase")] - Pointer { - package_id: PackageId, - volume_id: VolumeId, - path: PathBuf, - readonly: bool, - }, - #[serde(rename_all = "camelCase")] - Certificate { interface_id: HostId }, - #[serde(rename_all = "camelCase")] - Backup { readonly: bool }, -} -impl Volume { - pub async fn install( - &self, - path: &PathBuf, - pkg_id: &PackageId, - version: &Version, - volume_id: &VolumeId, - ) -> Result<(), Error> { - match self { - Volume::Data { .. } => { - tokio::fs::create_dir_all(self.path_for(path, pkg_id, version, volume_id)).await?; - } - _ => (), - } - Ok(()) - } - pub fn path_for( - &self, - data_dir_path: impl AsRef, - pkg_id: &PackageId, - version: &Version, - volume_id: &VolumeId, - ) -> PathBuf { - match self { - Volume::Data { .. } => data_dir(&data_dir_path, pkg_id, volume_id), - Volume::Assets {} => asset_dir(&data_dir_path, pkg_id, version).join(volume_id), - Volume::Pointer { - package_id, - volume_id, - path, - .. - } => data_dir(&data_dir_path, package_id, volume_id).join(if path.is_absolute() { - path.strip_prefix("/").unwrap() - } else { - path.as_ref() - }), - Volume::Certificate { interface_id } => cert_dir(pkg_id, &interface_id), - Volume::Backup { .. } => backup_dir(pkg_id), - } - } - - pub fn pointer_path(&self, data_dir_path: impl AsRef) -> Option { - if let Volume::Pointer { - path, - package_id, - volume_id, - .. - } = self - { - Some( - data_dir(data_dir_path.as_ref(), package_id, volume_id).join( - if path.is_absolute() { - path.strip_prefix("/").unwrap() - } else { - path.as_ref() - }, - ), - ) - } else { - None - } - } - - pub fn set_readonly(&mut self) { - match self { - Volume::Data { readonly } => { - *readonly = true; - } - Volume::Pointer { readonly, .. } => { - *readonly = true; - } - Volume::Backup { readonly } => { - *readonly = true; - } - _ => (), - } - } - pub fn readonly(&self) -> bool { - match self { - Volume::Data { readonly } => *readonly, - Volume::Assets {} => true, - Volume::Pointer { readonly, .. } => *readonly, - Volume::Certificate { .. } => true, - Volume::Backup { readonly } => *readonly, - } - } -} diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts index ce3143ab6..fa77d2c02 100644 --- a/sdk/lib/types.ts +++ b/sdk/lib/types.ts @@ -172,7 +172,7 @@ export type ActionMetadata = { warning: string | null input: InputSpec disabled: boolean - allowedStatuses: "only-running" | "only-stopped" | "any" + allowedStatuses: "onlyRunning" | "onlyStopped" | "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/package-lock.json b/web/package-lock.json index 54e432e7c..72e79e219 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,17 +8,17 @@ "name": "startos-ui", "version": "0.3.5.1", "dependencies": { - "@angular/animations": "^17.0.6", - "@angular/cdk": "^17.0.3", - "@angular/common": "^17.0.6", - "@angular/compiler": "^17.0.6", - "@angular/core": "^17.0.6", - "@angular/forms": "^17.0.6", - "@angular/platform-browser": "^17.0.6", - "@angular/platform-browser-dynamic": "^17.0.6", - "@angular/pwa": "^17.0.6", - "@angular/router": "^17.0.6", - "@angular/service-worker": "^17.0.6", + "@angular/animations": "^17.3.1", + "@angular/cdk": "^17.3.1", + "@angular/common": "^17.3.1", + "@angular/compiler": "^17.3.1", + "@angular/core": "^17.3.1", + "@angular/forms": "^17.3.1", + "@angular/platform-browser": "^17.3.1", + "@angular/platform-browser-dynamic": "^17.3.1", + "@angular/pwa": "^17.3.1", + "@angular/router": "^17.3.1", + "@angular/service-worker": "^17.3.1", "@ionic/angular": "^6.1.15", "@materia-ui/ngx-monaco-editor": "^6.0.0", "@start9labs/argon2": "^0.1.0", @@ -62,10 +62,10 @@ "zone.js": "^0.14.2" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.0.6", - "@angular/cli": "^17.0.6", - "@angular/compiler-cli": "^17.0.6", - "@angular/language-service": "^17.0.6", + "@angular-devkit/build-angular": "^17.3.1", + "@angular/cli": "^17.3.1", + "@angular/compiler-cli": "^17.3.1", + "@angular/language-service": "^17.3.1", "@ionic/cli": "^6.19.0", "@types/dompurify": "^2.3.3", "@types/estree": "^0.0.51", diff --git a/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts b/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts index a35184593..c4954c420 100644 --- a/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts +++ b/web/projects/ui/src/app/apps/portal/routes/service/routes/service.component.ts @@ -179,13 +179,13 @@ export class ServiceRoute { depErrors, ) - const depInfo = pkg.dependencyInfo[depId] + const { title, icon, versionSpec } = pkg.currentDependencies[depId] return { id: depId, - version: pkg.currentDependencies[depId].versionRange, - title: depInfo?.title || depId, - icon: depInfo?.icon || '', + version: versionSpec, + title, + icon, errorText: errorText ? `${errorText}. ${pkgManifest.title} will not work as expected.` : '', @@ -252,7 +252,7 @@ export class ServiceRoute { return this.installDep(pkg, pkgManifest, depId) case 'configure': return this.formDialog.open(ServiceConfigModal, { - label: `${pkg.dependencyInfo[depId].title} config`, + label: `${pkg.currentDependencies[depId].title} config`, data: { pkgId: depId, dependentInfo: pkgManifest, @@ -269,7 +269,7 @@ export class ServiceRoute { const dependentInfo: DependentInfo = { id: manifest.id, title: manifest.title, - version: pkg.currentDependencies[depId].versionRange, + version: pkg.currentDependencies[depId].versionSpec, } const navigationExtras: NavigationExtras = { state: { dependentInfo }, 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 91ae0618e..35c860be6 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -1493,7 +1493,6 @@ export module Mock { }, }, currentDependencies: {}, - dependencyInfo: {}, marketplaceUrl: 'https://registry.start9.com/', developerKey: 'developer-key', outboundProxy: null, @@ -1624,15 +1623,13 @@ export module Mock { }, }, currentDependencies: { - bitcoind: { - versionRange: '>=26.0.0', - healthChecks: [], - }, - }, - dependencyInfo: { bitcoind: { title: Mock.MockManifestBitcoind.title, icon: 'assets/img/service-icons/bitcoind.svg', + kind: 'running', + registryUrl: '', + versionSpec: '>=26.0.0', + healthChecks: [], }, }, marketplaceUrl: 'https://registry.start9.com/', @@ -1871,23 +1868,21 @@ export module Mock { }, }, currentDependencies: { - bitcoind: { - versionRange: '>=26.0.0', - healthChecks: [], - }, - 'btc-rpc-proxy': { - versionRange: '>2.0.0', // @TODO - healthChecks: [], - }, - }, - dependencyInfo: { bitcoind: { title: Mock.MockManifestBitcoind.title, icon: 'assets/img/service-icons/bitcoind.svg', + kind: 'running', + registryUrl: 'https://registry.start9.com', + versionSpec: '>=26.0.0', + healthChecks: [], }, 'btc-rpc-proxy': { 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', // @TODO + healthChecks: [], }, }, marketplaceUrl: 'https://registry.start9.com/', 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 6b0edbfaa..bb9c84d85 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -402,7 +402,6 @@ export const mockPatchData: DataModel = { }, }, currentDependencies: {}, - dependencyInfo: {}, marketplaceUrl: 'https://registry.start9.com/', developerKey: 'developer-key', outboundProxy: null, @@ -639,23 +638,21 @@ export const mockPatchData: DataModel = { }, }, currentDependencies: { - bitcoind: { - versionRange: '>=26.0.0', - healthChecks: [], - }, - 'btc-rpc-proxy': { - versionRange: '>2.0.0', - healthChecks: [], - }, - }, - dependencyInfo: { bitcoind: { title: 'Bitcoin Core', icon: 'assets/img/service-icons/bitcoind.svg', + kind: 'running', + registryUrl: 'https://registry.start9.com', + versionSpec: '>=26.0.0', + healthChecks: [], }, 'btc-rpc-proxy': { title: 'Bitcoin Proxy', icon: 'assets/img/service-icons/btc-rpc-proxy.png', + kind: 'running', + registryUrl: 'https://community-registry.start9.com', + versionSpec: '>2.0.0', + healthChecks: [], }, }, marketplaceUrl: 'https://registry.start9.com/', 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 90a168587..c48726c85 100644 --- a/web/projects/ui/src/app/services/dep-error.service.ts +++ b/web/projects/ui/src/app/services/dep-error.service.ts @@ -88,14 +88,14 @@ export class DepErrorService { } } - const versionRange = pkg.currentDependencies[depId].versionRange + const versionSpec = pkg.currentDependencies[depId].versionSpec const depManifest = dep.stateInfo.manifest // incorrect version - if (!this.emver.satisfies(depManifest.version, versionRange)) { + if (!this.emver.satisfies(depManifest.version, versionSpec)) { return { type: DependencyErrorType.IncorrectVersion, - expected: versionRange, + expected: versionSpec, 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 11aad4498..9012e9142 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 @@ -154,13 +154,7 @@ export type PackageDataEntry = { status: Status actions: Record lastBackup: string | null - currentDependencies: { [id: string]: CurrentDependencyInfo } - dependencyInfo: { - [id: string]: { - title: string - icon: Url - } - } + currentDependencies: Record serviceInterfaces: Record marketplaceUrl: string | null developerKey: string @@ -197,7 +191,11 @@ export enum PackageState { } export interface CurrentDependencyInfo { - versionRange: string + title: string + icon: string + kind: 'exists' | 'running' + registryUrl: string + versionSpec: string healthChecks: string[] // array of health check IDs } diff --git a/web/projects/ui/src/app/util/dry-update.ts b/web/projects/ui/src/app/util/dry-update.ts index 472a48389..2d4d1aa10 100644 --- a/web/projects/ui/src/app/util/dry-update.ts +++ b/web/projects/ui/src/app/util/dry-update.ts @@ -12,8 +12,7 @@ export function dryUpdate( pkg => Object.keys(pkg.currentDependencies || {}).some( pkgId => pkgId === id, - ) && - !emver.satisfies(version, pkg.currentDependencies[id].versionRange), + ) && !emver.satisfies(version, pkg.currentDependencies[id].versionSpec), ) .map(pkg => getManifest(pkg).title) }