mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
* TODO: images * wip * pack s9pk images * include path in packsource error * debug info * add cmd as context to invoke * filehelper bugfix * fix file helper * fix exposeForDependents * misc fixes * force image removal * fix filtering * fix deadlock * fix api * chore: Up the version of the package.json * always allow concurrency within same call stack * Update core/startos/src/s9pk/merkle_archive/expected.rs Co-authored-by: Jade <2364004+Blu-J@users.noreply.github.com> --------- Co-authored-by: J H <dragondef@gmail.com> Co-authored-by: Jade <2364004+Blu-J@users.noreply.github.com>
194 lines
6.2 KiB
TypeScript
194 lines
6.2 KiB
TypeScript
import { ExecuteResult, System } from "../../Interfaces/System"
|
|
import { unNestPath } from "../../Models/JsonPath"
|
|
import matches, { any, number, object, string, tuple } from "ts-matches"
|
|
import { HostSystemStartOs } from "../HostSystemStartOs"
|
|
import { Effects } from "../../Models/Effects"
|
|
import { RpcResult, matchRpcResult } from "../RpcListener"
|
|
import { duration } from "../../Models/Duration"
|
|
import { T } from "@start9labs/start-sdk"
|
|
import { MainEffects } from "@start9labs/start-sdk/cjs/lib/StartSdk"
|
|
export const STARTOS_JS_LOCATION = "/usr/lib/startos/package/index.js"
|
|
export class SystemForStartOs implements System {
|
|
private onTerm: (() => Promise<void>) | undefined
|
|
static of() {
|
|
return new SystemForStartOs(require(STARTOS_JS_LOCATION))
|
|
}
|
|
constructor(readonly abi: T.ABI) {}
|
|
async execute(
|
|
effects: HostSystemStartOs,
|
|
options: {
|
|
id: string
|
|
procedure:
|
|
| "/init"
|
|
| "/uninit"
|
|
| "/main/start"
|
|
| "/main/stop"
|
|
| "/config/set"
|
|
| "/config/get"
|
|
| "/backup/create"
|
|
| "/backup/restore"
|
|
| "/actions/metadata"
|
|
| `/actions/${string}/get`
|
|
| `/actions/${string}/run`
|
|
| `/dependencies/${string}/query`
|
|
| `/dependencies/${string}/update`
|
|
input: unknown
|
|
timeout?: number | undefined
|
|
},
|
|
): Promise<RpcResult> {
|
|
effects = Object.create(effects)
|
|
effects.procedureId = options.id
|
|
return this._execute(effects, options)
|
|
.then((x) =>
|
|
matches(x)
|
|
.when(
|
|
object({
|
|
result: any,
|
|
}),
|
|
(x) => x,
|
|
)
|
|
.when(
|
|
object({
|
|
error: string,
|
|
}),
|
|
(x) => ({
|
|
error: {
|
|
code: 0,
|
|
message: x.error,
|
|
},
|
|
}),
|
|
)
|
|
.when(
|
|
object({
|
|
"error-code": tuple(number, string),
|
|
}),
|
|
({ "error-code": [code, message] }) => ({
|
|
error: {
|
|
code,
|
|
message,
|
|
},
|
|
}),
|
|
)
|
|
.defaultTo({ result: x }),
|
|
)
|
|
.catch((error: unknown) => {
|
|
if (error instanceof Error)
|
|
return {
|
|
error: {
|
|
code: 0,
|
|
message: error.name,
|
|
data: {
|
|
details: error.message,
|
|
debug: `${error?.cause ?? "[noCause]"}:${error?.stack ?? "[noStack]"}`,
|
|
},
|
|
},
|
|
}
|
|
if (matchRpcResult.test(error)) return error
|
|
return {
|
|
error: {
|
|
code: 0,
|
|
message: String(error),
|
|
},
|
|
}
|
|
})
|
|
}
|
|
async _execute(
|
|
effects: Effects,
|
|
options: {
|
|
procedure:
|
|
| "/init"
|
|
| "/uninit"
|
|
| "/main/start"
|
|
| "/main/stop"
|
|
| "/config/set"
|
|
| "/config/get"
|
|
| "/backup/create"
|
|
| "/backup/restore"
|
|
| "/actions/metadata"
|
|
| `/actions/${string}/get`
|
|
| `/actions/${string}/run`
|
|
| `/dependencies/${string}/query`
|
|
| `/dependencies/${string}/update`
|
|
input: unknown
|
|
timeout?: number | undefined
|
|
},
|
|
): Promise<unknown> {
|
|
switch (options.procedure) {
|
|
case "/init": {
|
|
const previousVersion =
|
|
string.optional().unsafeCast(options.input) || null
|
|
return this.abi.init({ effects, previousVersion })
|
|
}
|
|
case "/uninit": {
|
|
const nextVersion = string.optional().unsafeCast(options.input) || null
|
|
return this.abi.uninit({ effects, nextVersion })
|
|
}
|
|
case "/main/start": {
|
|
const started = async (onTerm: () => Promise<void>) => {
|
|
await effects.setMainStatus({ status: "running" })
|
|
if (this.onTerm) await this.onTerm()
|
|
this.onTerm = onTerm
|
|
}
|
|
const daemons = await (
|
|
await this.abi.main({
|
|
effects: { ...effects, _type: "main" },
|
|
started,
|
|
})
|
|
).build()
|
|
this.onTerm = daemons.term
|
|
}
|
|
case "/main/stop": {
|
|
await effects.setMainStatus({ status: "stopped" })
|
|
if (this.onTerm) await this.onTerm()
|
|
delete this.onTerm
|
|
return duration(30, "s")
|
|
}
|
|
case "/config/set": {
|
|
const input = options.input as any // TODO
|
|
return this.abi.setConfig({ effects, input })
|
|
}
|
|
case "/config/get": {
|
|
return this.abi.getConfig({ effects })
|
|
}
|
|
case "/backup/create":
|
|
case "/backup/restore":
|
|
throw new Error("this should be called with the init/unit")
|
|
case "/actions/metadata": {
|
|
return this.abi.actionsMetadata({ effects })
|
|
}
|
|
default:
|
|
const procedures = unNestPath(options.procedure)
|
|
const id = procedures[2]
|
|
switch (true) {
|
|
case procedures[1] === "actions" && procedures[3] === "get": {
|
|
const action = (await this.abi.actions({ effects }))[id]
|
|
if (!action) throw new Error(`Action ${id} not found`)
|
|
return action.getConfig({ effects })
|
|
}
|
|
case procedures[1] === "actions" && procedures[3] === "run": {
|
|
const action = (await this.abi.actions({ effects }))[id]
|
|
if (!action) throw new Error(`Action ${id} not found`)
|
|
return action.run({ effects, input: options.input as any }) // TODO
|
|
}
|
|
case procedures[1] === "dependencies" && procedures[3] === "query": {
|
|
const dependencyConfig = this.abi.dependencyConfig[id]
|
|
if (!dependencyConfig)
|
|
throw new Error(`dependencyConfig ${id} not found`)
|
|
const localConfig = options.input
|
|
return dependencyConfig.query({ effects })
|
|
}
|
|
case procedures[1] === "dependencies" && procedures[3] === "update": {
|
|
const dependencyConfig = this.abi.dependencyConfig[id]
|
|
if (!dependencyConfig)
|
|
throw new Error(`dependencyConfig ${id} not found`)
|
|
return dependencyConfig.update(options.input as any) // TODO
|
|
}
|
|
}
|
|
}
|
|
throw new Error(`Method ${options.procedure} not implemented.`)
|
|
}
|
|
async exit(effects: Effects): Promise<void> {
|
|
return void null
|
|
}
|
|
}
|