mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
chore: Making sure that the values that we are returning are valid now with the new types
This commit is contained in:
@@ -147,11 +147,6 @@ export class HostSystemStartOs implements Effects {
|
||||
T.Effects["exposeForDependents"]
|
||||
>
|
||||
}
|
||||
exposeUi(...[options]: Parameters<T.Effects["exposeUi"]>) {
|
||||
return this.rpcRound("exposeUi", options) as ReturnType<
|
||||
T.Effects["exposeUi"]
|
||||
>
|
||||
}
|
||||
getConfigured(...[]: Parameters<T.Effects["getConfigured"]>) {
|
||||
return this.rpcRound("getConfigured", null) as ReturnType<
|
||||
T.Effects["getConfigured"]
|
||||
|
||||
@@ -25,15 +25,12 @@ export class MainLoop {
|
||||
wait: Promise<unknown>
|
||||
}>
|
||||
| undefined
|
||||
private propertiesEvent: NodeJS.Timeout | undefined
|
||||
constructor(
|
||||
readonly system: SystemForEmbassy,
|
||||
readonly effects: HostSystemStartOs,
|
||||
readonly runProperties: () => Promise<void>,
|
||||
) {
|
||||
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
|
||||
|
||||
@@ -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<PackagePropertiesV2>()
|
||||
const matchPackagePropertyObject: Parser<unknown, PackagePropertyObject> =
|
||||
@@ -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<void> {
|
||||
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<undefined> {
|
||||
private async properties(
|
||||
effects: HostSystemStartOs,
|
||||
): Promise<ReturnType<T.ExpectedExports.Properties>> {
|
||||
// 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<any, any>({
|
||||
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<any, any>({
|
||||
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<unknown> {
|
||||
// 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<void> {
|
||||
// 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<void> {
|
||||
// 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<T.ConfigRes> {
|
||||
// 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<T.SetResult> {
|
||||
// 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<T.MigrationRes> {
|
||||
// throw new Error("Migrations should never be ran in the sandbox mode")
|
||||
// }
|
||||
// private async roProperties(effects: HostSystemStartOs): Promise<unknown> {
|
||||
// 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<void> {
|
||||
// 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<T.ActionResult> {
|
||||
// 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<object> {
|
||||
// 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<void> {
|
||||
// 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<T.ConfigRes> {
|
||||
const startingSpec = structuredClone(value.spec)
|
||||
|
||||
@@ -35,6 +35,7 @@ export const jsonPath = some(
|
||||
"/backup/create",
|
||||
"/backup/restore",
|
||||
"/actions/metadata",
|
||||
"/properties",
|
||||
),
|
||||
string.refine(isNestedPath, "isNestedPath"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user