mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
Merge branch 'integration/new-container-runtime' of github.com:Start9Labs/start-os into rebase/feat/domains
This commit is contained in:
@@ -147,11 +147,6 @@ export class HostSystemStartOs implements Effects {
|
|||||||
T.Effects["exposeForDependents"]
|
T.Effects["exposeForDependents"]
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
exposeUi(...[options]: Parameters<T.Effects["exposeUi"]>) {
|
|
||||||
return this.rpcRound("exposeUi", options) as ReturnType<
|
|
||||||
T.Effects["exposeUi"]
|
|
||||||
>
|
|
||||||
}
|
|
||||||
getConfigured(...[]: Parameters<T.Effects["getConfigured"]>) {
|
getConfigured(...[]: Parameters<T.Effects["getConfigured"]>) {
|
||||||
return this.rpcRound("getConfigured", null) as ReturnType<
|
return this.rpcRound("getConfigured", null) as ReturnType<
|
||||||
T.Effects["getConfigured"]
|
T.Effects["getConfigured"]
|
||||||
|
|||||||
@@ -25,15 +25,12 @@ export class MainLoop {
|
|||||||
wait: Promise<unknown>
|
wait: Promise<unknown>
|
||||||
}>
|
}>
|
||||||
| undefined
|
| undefined
|
||||||
private propertiesEvent: NodeJS.Timeout | undefined
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly system: SystemForEmbassy,
|
readonly system: SystemForEmbassy,
|
||||||
readonly effects: HostSystemStartOs,
|
readonly effects: HostSystemStartOs,
|
||||||
readonly runProperties: () => Promise<void>,
|
|
||||||
) {
|
) {
|
||||||
this.healthLoops = this.constructHealthLoops()
|
this.healthLoops = this.constructHealthLoops()
|
||||||
this.mainEvent = this.constructMainEvent()
|
this.mainEvent = this.constructMainEvent()
|
||||||
this.propertiesEvent = this.constructPropertiesEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async constructMainEvent() {
|
private async constructMainEvent() {
|
||||||
@@ -85,23 +82,14 @@ export class MainLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async clean(options?: { timeout?: number }) {
|
public async clean(options?: { timeout?: number }) {
|
||||||
const { mainEvent, healthLoops, propertiesEvent } = this
|
const { mainEvent, healthLoops } = this
|
||||||
const main = await mainEvent
|
const main = await mainEvent
|
||||||
delete this.mainEvent
|
delete this.mainEvent
|
||||||
delete this.healthLoops
|
delete this.healthLoops
|
||||||
delete this.propertiesEvent
|
|
||||||
if (mainEvent) await main?.daemon.term()
|
if (mainEvent) await main?.daemon.term()
|
||||||
clearInterval(propertiesEvent)
|
|
||||||
if (healthLoops) healthLoops.forEach((x) => clearInterval(x.interval))
|
if (healthLoops) healthLoops.forEach((x) => clearInterval(x.interval))
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructPropertiesEvent() {
|
|
||||||
const { runProperties } = this
|
|
||||||
return setInterval(() => {
|
|
||||||
runProperties()
|
|
||||||
}, EMBASSY_PROPERTIES_LOOP)
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructHealthLoops() {
|
private constructHealthLoops() {
|
||||||
const { manifest } = this.system
|
const { manifest } = this.system
|
||||||
const effects = this.effects
|
const effects = this.effects
|
||||||
|
|||||||
@@ -59,6 +59,33 @@ export type PackagePropertyObject = {
|
|||||||
type: "object"
|
type: "object"
|
||||||
description: string
|
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] =
|
const [matchPackageProperties, setMatchPackageProperties] =
|
||||||
deferred<PackagePropertiesV2>()
|
deferred<PackagePropertiesV2>()
|
||||||
const matchPackagePropertyObject: Parser<unknown, PackagePropertyObject> =
|
const matchPackagePropertyObject: Parser<unknown, PackagePropertyObject> =
|
||||||
@@ -92,44 +119,6 @@ const matchProperties = object({
|
|||||||
data: matchPackageProperties,
|
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 {
|
export class SystemForEmbassy implements System {
|
||||||
currentRunning: MainLoop | undefined
|
currentRunning: MainLoop | undefined
|
||||||
static async of(manifestLocation: string = MANIFEST_LOCATION) {
|
static async of(manifestLocation: string = MANIFEST_LOCATION) {
|
||||||
@@ -236,6 +225,8 @@ export class SystemForEmbassy implements System {
|
|||||||
return this.getConfig(effects)
|
return this.getConfig(effects)
|
||||||
case "/config/set":
|
case "/config/set":
|
||||||
return this.setConfig(effects, input)
|
return this.setConfig(effects, input)
|
||||||
|
case "/properties":
|
||||||
|
return this.properties(effects)
|
||||||
case "/actions/metadata":
|
case "/actions/metadata":
|
||||||
return todo()
|
return todo()
|
||||||
case "/init":
|
case "/init":
|
||||||
@@ -279,9 +270,7 @@ export class SystemForEmbassy implements System {
|
|||||||
private async mainStart(effects: HostSystemStartOs): Promise<void> {
|
private async mainStart(effects: HostSystemStartOs): Promise<void> {
|
||||||
if (!!this.currentRunning) return
|
if (!!this.currentRunning) return
|
||||||
|
|
||||||
this.currentRunning = new MainLoop(this, effects, () =>
|
this.currentRunning = new MainLoop(this, effects)
|
||||||
this.properties(effects),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
private async mainStop(
|
private async mainStop(
|
||||||
effects: HostSystemStartOs,
|
effects: HostSystemStartOs,
|
||||||
@@ -472,51 +461,44 @@ export class SystemForEmbassy implements System {
|
|||||||
}
|
}
|
||||||
return { configured: true }
|
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
|
// TODO BLU-J set the properties ever so often
|
||||||
const setConfigValue = this.manifest.properties
|
const setConfigValue = this.manifest.properties
|
||||||
if (!setConfigValue) return
|
if (!setConfigValue) throw new Error("There is no properties")
|
||||||
if (setConfigValue.type === "docker") {
|
if (setConfigValue.type === "docker") {
|
||||||
const container = await DockerProcedureContainer.of(
|
const container = await DockerProcedureContainer.of(
|
||||||
effects,
|
effects,
|
||||||
setConfigValue,
|
setConfigValue,
|
||||||
this.manifest.volumes,
|
this.manifest.volumes,
|
||||||
)
|
)
|
||||||
const properties = JSON.parse(
|
const properties = matchProperties.unsafeCast(
|
||||||
(
|
JSON.parse(
|
||||||
await container.exec([
|
(
|
||||||
setConfigValue.entrypoint,
|
await container.exec([
|
||||||
...setConfigValue.args,
|
setConfigValue.entrypoint,
|
||||||
])
|
...setConfigValue.args,
|
||||||
).stdout.toString(),
|
])
|
||||||
|
).stdout.toString(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if (!matchProperties.test(properties)) return
|
return asProperty(properties.data)
|
||||||
const exposeUis = propertiesToExportUi(properties.data)
|
|
||||||
await effects.store.set<any, any>({
|
|
||||||
path: "/properties",
|
|
||||||
value: exposeUis.values,
|
|
||||||
})
|
|
||||||
await effects.exposeUi(exposeUis.expose)
|
|
||||||
} else if (setConfigValue.type === "script") {
|
} else if (setConfigValue.type === "script") {
|
||||||
const moduleCode = this.moduleCode
|
const moduleCode = this.moduleCode
|
||||||
const method = moduleCode.properties
|
const method = moduleCode.properties
|
||||||
if (!method)
|
if (!method)
|
||||||
throw new Error("Expecting that the method properties exists")
|
throw new Error("Expecting that the method properties exists")
|
||||||
const properties = await method(
|
const properties = matchProperties.unsafeCast(
|
||||||
new PolyfillEffects(effects, this.manifest),
|
await method(new PolyfillEffects(effects, this.manifest)).then((x) => {
|
||||||
).then((x) => {
|
if ("result" in x) return x.result
|
||||||
if ("result" in x) return x.result
|
if ("error" in x) throw new Error("Error getting config: " + x.error)
|
||||||
if ("error" in x) throw new Error("Error getting config: " + x.error)
|
throw new Error("Error getting config: " + x["error-code"][1])
|
||||||
throw new Error("Error getting config: " + x["error-code"][1])
|
}),
|
||||||
})
|
)
|
||||||
if (!matchProperties.test(properties)) return
|
return asProperty(properties.data)
|
||||||
const exposeUis = propertiesToExportUi(properties.data)
|
|
||||||
await effects.store.set<any, any>({
|
|
||||||
path: "/properties",
|
|
||||||
value: exposeUis.values,
|
|
||||||
})
|
|
||||||
await effects.exposeUi(exposeUis.expose)
|
|
||||||
}
|
}
|
||||||
|
throw new Error(`Unknown type in the fetch properties: ${setConfigValue}`)
|
||||||
}
|
}
|
||||||
private async health(
|
private async health(
|
||||||
effects: HostSystemStartOs,
|
effects: HostSystemStartOs,
|
||||||
@@ -651,279 +633,6 @@ export class SystemForEmbassy implements System {
|
|||||||
throw new Error("Error getting config: " + x["error-code"][1])
|
throw new Error("Error getting config: " + x["error-code"][1])
|
||||||
})) as any
|
})) 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> {
|
async function removePointers(value: T.ConfigRes): Promise<T.ConfigRes> {
|
||||||
const startingSpec = structuredClone(value.spec)
|
const startingSpec = structuredClone(value.spec)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export const jsonPath = some(
|
|||||||
"/backup/create",
|
"/backup/create",
|
||||||
"/backup/restore",
|
"/backup/restore",
|
||||||
"/actions/metadata",
|
"/actions/metadata",
|
||||||
|
"/properties",
|
||||||
),
|
),
|
||||||
string.refine(isNestedPath, "isNestedPath"),
|
string.refine(isNestedPath, "isNestedPath"),
|
||||||
)
|
)
|
||||||
|
|||||||
29
core/Cargo.lock
generated
29
core/Cargo.lock
generated
@@ -2685,7 +2685,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"torut",
|
"torut",
|
||||||
"tracing",
|
"tracing",
|
||||||
"ts-rs",
|
"ts-rs 7.1.1",
|
||||||
"yasi",
|
"yasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4730,7 +4730,7 @@ dependencies = [
|
|||||||
"tracing-journald",
|
"tracing-journald",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"trust-dns-server",
|
"trust-dns-server",
|
||||||
"ts-rs",
|
"ts-rs 8.1.0",
|
||||||
"typed-builder",
|
"typed-builder",
|
||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
@@ -5441,7 +5441,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "fc2cae1fc5d05d47aa24b64f9a4f7cba24cdc9187a2084dd97ac57bef5eccae6"
|
checksum = "fc2cae1fc5d05d47aa24b64f9a4f7cba24cdc9187a2084dd97ac57bef5eccae6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror",
|
"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]]
|
[[package]]
|
||||||
@@ -5457,6 +5467,19 @@ dependencies = [
|
|||||||
"termcolor",
|
"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]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub enum ProcedureName {
|
|||||||
GetConfig,
|
GetConfig,
|
||||||
SetConfig,
|
SetConfig,
|
||||||
CreateBackup,
|
CreateBackup,
|
||||||
|
Properties,
|
||||||
RestoreBackup,
|
RestoreBackup,
|
||||||
ActionMetadata,
|
ActionMetadata,
|
||||||
RunAction(ActionId),
|
RunAction(ActionId),
|
||||||
@@ -29,6 +30,7 @@ impl ProcedureName {
|
|||||||
ProcedureName::SetConfig => "/config/set".to_string(),
|
ProcedureName::SetConfig => "/config/set".to_string(),
|
||||||
ProcedureName::GetConfig => "/config/get".to_string(),
|
ProcedureName::GetConfig => "/config/get".to_string(),
|
||||||
ProcedureName::CreateBackup => "/backup/create".to_string(),
|
ProcedureName::CreateBackup => "/backup/create".to_string(),
|
||||||
|
ProcedureName::Properties => "/properties".to_string(),
|
||||||
ProcedureName::RestoreBackup => "/backup/restore".to_string(),
|
ProcedureName::RestoreBackup => "/backup/restore".to_string(),
|
||||||
ProcedureName::ActionMetadata => "/actions/metadata".to_string(),
|
ProcedureName::ActionMetadata => "/actions/metadata".to_string(),
|
||||||
ProcedureName::RunAction(id) => format!("/actions/{}/run", id),
|
ProcedureName::RunAction(id) => format!("/actions/{}/run", id),
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ tracing-futures = "0.2.5"
|
|||||||
tracing-journald = "0.3.0"
|
tracing-journald = "0.3.0"
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||||
trust-dns-server = "0.23.1"
|
trust-dns-server = "0.23.1"
|
||||||
ts-rs = "7.1.1"
|
ts-rs = "8.1.0"
|
||||||
typed-builder = "0.18.0"
|
typed-builder = "0.18.0"
|
||||||
url = { version = "2.4.1", features = ["serde"] }
|
url = { version = "2.4.1", features = ["serde"] }
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
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, }
|
export type ActionMetadata = { name: string, description: string, warning: string | null, disabled: boolean, input: {[key: string]: any}, allowedStatuses: AllowedStatuses, group: string | null, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type AddSslOptions = { scheme: string | null, preferredExternalPort: number, addXForwardedHeaders: boolean | null, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { BindOptions } from "./BindOptions";
|
||||||
|
|
||||||
export interface AddressInfo { username: string | null, hostId: string, bindOptions: BindOptions, suffix: string, }
|
export type AddressInfo = { username: string | null, hostId: string, bindOptions: BindOptions, suffix: string, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
import type { AddSslOptions } from "./AddSslOptions";
|
import type { AddSslOptions } from "./AddSslOptions";
|
||||||
import type { BindOptionsSecure } from "./BindOptionsSecure";
|
import type { BindOptionsSecure } from "./BindOptionsSecure";
|
||||||
|
|
||||||
export interface BindOptions { scheme: string | null, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, }
|
export type BindOptions = { scheme: string | null, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type BindOptionsSecure = { ssl: boolean, };
|
||||||
@@ -3,4 +3,4 @@ import type { AddSslOptions } from "./AddSslOptions";
|
|||||||
import type { BindKind } from "./BindKind";
|
import type { BindKind } from "./BindKind";
|
||||||
import type { BindOptionsSecure } from "./BindOptionsSecure";
|
import type { BindOptionsSecure } from "./BindOptionsSecure";
|
||||||
|
|
||||||
export interface BindParams { kind: BindKind, id: string, internalPort: number, scheme: string, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, }
|
export type BindParams = { kind: BindKind, id: string, internalPort: number, scheme: string, preferredExternalPort: number, addSsl: AddSslOptions | null, secure: BindOptionsSecure | null, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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[], }
|
export type ChrootParams = { env: string | null, workdir: string | null, user: string | null, path: string, command: string, args: string[], };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type CreateOverlayedImageParams = { imageId: string, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type DestroyOverlayedImageParams = { guid: string, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type ExecuteAction = { serviceId: string | null, actionId: string, input: any, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { ActionMetadata } from "./ActionMetadata";
|
||||||
|
|
||||||
export interface ExportActionParams { id: string, metadata: ActionMetadata, }
|
export type ExportActionParams = { id: string; metadata: ActionMetadata };
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
import type { AddressInfo } from "./AddressInfo";
|
import type { AddressInfo } from "./AddressInfo";
|
||||||
import type { ServiceInterfaceType } from "./ServiceInterfaceType";
|
import type { ServiceInterfaceType } from "./ServiceInterfaceType";
|
||||||
|
|
||||||
export interface ExportServiceInterfaceParams { id: string, name: string, description: string, hasPrimary: boolean, disabled: boolean, masked: boolean, addressInfo: AddressInfo, type: ServiceInterfaceType, }
|
export type ExportServiceInterfaceParams = { id: string, name: string, description: string, hasPrimary: boolean, disabled: boolean, masked: boolean, addressInfo: AddressInfo, type: ServiceInterfaceType, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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[], }
|
export type ExposeForDependentsParams = { paths: string[], };
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
import type { Callback } from "./Callback";
|
import type { Callback } from "./Callback";
|
||||||
import type { GetHostInfoParamsKind } from "./GetHostInfoParamsKind";
|
import type { GetHostInfoParamsKind } from "./GetHostInfoParamsKind";
|
||||||
|
|
||||||
export interface GetHostInfoParams { kind: GetHostInfoParamsKind | null, serviceInterfaceId: string, packageId: string | null, callback: Callback, }
|
export type GetHostInfoParams = { kind: GetHostInfoParamsKind | null, serviceInterfaceId: string, packageId: string | null, callback: Callback, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { Callback } from "./Callback";
|
||||||
|
|
||||||
export interface GetPrimaryUrlParams { packageId: string | null, serviceInterfaceId: string, callback: Callback, }
|
export type GetPrimaryUrlParams = { packageId: string | null, serviceInterfaceId: string, callback: Callback, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { Callback } from "./Callback";
|
||||||
|
|
||||||
export interface GetServiceInterfaceParams { packageId: string | null, serviceInterfaceId: string, callback: Callback, }
|
export type GetServiceInterfaceParams = { packageId: string | null, serviceInterfaceId: string, callback: Callback, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type GetServicePortForwardParams = { packageId: string | null, internalPort: number, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { Algorithm } from "./Algorithm";
|
||||||
|
|
||||||
export interface GetSslCertificateParams { packageId: string | null, hostId: string, algorithm: Algorithm | null, }
|
export type GetSslCertificateParams = { packageId: string | null, hostId: string, algorithm: Algorithm | null, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { Algorithm } from "./Algorithm";
|
||||||
|
|
||||||
export interface GetSslKeyParams { packageId: string | null, hostId: string, algorithm: Algorithm | null, }
|
export type GetSslKeyParams = { packageId: string | null, hostId: string, algorithm: Algorithm | null, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type GetStoreParams = { packageId: string | null, path: string, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { Callback } from "./Callback";
|
||||||
|
|
||||||
export interface GetSystemSmtpParams { callback: Callback, }
|
export type GetSystemSmtpParams = { callback: Callback, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { Callback } from "./Callback";
|
||||||
|
|
||||||
export interface ListServiceInterfacesParams { packageId: string | null, callback: Callback, }
|
export type ListServiceInterfacesParams = { packageId: string | null, callback: Callback, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { MountTarget } from "./MountTarget";
|
||||||
|
|
||||||
export interface MountParams { location: string, target: MountTarget, }
|
export type MountParams = { location: string, target: MountTarget, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type MountTarget = { packageId: string, volumeId: string, subpath: string | null, readonly: boolean, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type ParamsMaybePackageId = { packageId: string | null, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type ParamsPackageId = { packageId: string, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type RemoveActionParams = { id: string, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type RemoveAddressParams = { id: string, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type ReverseProxyBind = { ip: string | null, port: number, ssl: boolean, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type ReverseProxyDestination = { ip: string | null, port: number, ssl: boolean, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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}, }
|
export type ReverseProxyHttp = { headers: null | {[key: string]: string}, };
|
||||||
@@ -3,4 +3,4 @@ import type { ReverseProxyBind } from "./ReverseProxyBind";
|
|||||||
import type { ReverseProxyDestination } from "./ReverseProxyDestination";
|
import type { ReverseProxyDestination } from "./ReverseProxyDestination";
|
||||||
import type { ReverseProxyHttp } from "./ReverseProxyHttp";
|
import type { ReverseProxyHttp } from "./ReverseProxyHttp";
|
||||||
|
|
||||||
export interface ReverseProxyParams { bind: ReverseProxyBind, dst: ReverseProxyDestination, http: ReverseProxyHttp, }
|
export type ReverseProxyParams = { bind: ReverseProxyBind, dst: ReverseProxyDestination, http: ReverseProxyHttp, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type SetConfigured = { configured: boolean, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { DependencyRequirement } from "./DependencyRequirement";
|
||||||
|
|
||||||
export interface SetDependenciesParams { dependencies: Array<DependencyRequirement>, }
|
export type SetDependenciesParams = { dependencies: Array<DependencyRequirement>, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { HealthCheckString } from "./HealthCheckString";
|
||||||
|
|
||||||
export interface SetHealth { name: string, status: HealthCheckString, message: string | null, }
|
export type SetHealth = { name: string, status: HealthCheckString, message: string | null, };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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";
|
import type { Status } from "./Status";
|
||||||
|
|
||||||
export interface SetMainStatus { status: Status, }
|
export type SetMainStatus = { status: Status, };
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, }
|
export type SetStoreParams = { value: any, path: string, };
|
||||||
@@ -93,7 +93,6 @@ pub fn auth() -> ParentHandler {
|
|||||||
from_fn_async(logout)
|
from_fn_async(logout)
|
||||||
.with_metadata("get-session", Value::Bool(true))
|
.with_metadata("get-session", Value::Bool(true))
|
||||||
.with_remote_cli::<CliContext>()
|
.with_remote_cli::<CliContext>()
|
||||||
// TODO @dr-bonez
|
|
||||||
.no_display(),
|
.no_display(),
|
||||||
)
|
)
|
||||||
.subcommand("session", session())
|
.subcommand("session", session())
|
||||||
|
|||||||
@@ -45,6 +45,15 @@ pub fn backup() -> ParentHandler {
|
|||||||
.subcommand("target", target::target())
|
.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::<CliContext>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct BackupMetadata {
|
struct BackupMetadata {
|
||||||
pub timestamp: DateTime<Utc>,
|
pub timestamp: DateTime<Utc>,
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ pub struct RestorePackageParams {
|
|||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO dr Why doesn't anything use this
|
|
||||||
// #[command(rename = "restore", display(display_none))]
|
// #[command(rename = "restore", display(display_none))]
|
||||||
#[instrument(skip(ctx, password))]
|
#[instrument(skip(ctx, password))]
|
||||||
pub async fn restore_packages_rpc(
|
pub async fn restore_packages_rpc(
|
||||||
@@ -71,7 +70,7 @@ pub async fn restore_packages_rpc(
|
|||||||
pub async fn recover_full_embassy(
|
pub async fn recover_full_embassy(
|
||||||
ctx: SetupContext,
|
ctx: SetupContext,
|
||||||
disk_guid: Arc<String>,
|
disk_guid: Arc<String>,
|
||||||
embassy_password: String,
|
start_os_password: String,
|
||||||
recovery_source: TmpMountGuard,
|
recovery_source: TmpMountGuard,
|
||||||
recovery_password: Option<String>,
|
recovery_password: Option<String>,
|
||||||
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
||||||
@@ -89,7 +88,7 @@ pub async fn recover_full_embassy(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
os_backup.account.password = argon2::hash_encoded(
|
os_backup.account.password = argon2::hash_encoded(
|
||||||
embassy_password.as_bytes(),
|
start_os_password.as_bytes(),
|
||||||
&rand::random::<[u8; 16]>()[..],
|
&rand::random::<[u8; 16]>()[..],
|
||||||
&argon2::Config::rfc9106_low_mem(),
|
&argon2::Config::rfc9106_low_mem(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ pub struct CifsBackupTarget {
|
|||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
username: String,
|
username: String,
|
||||||
mountable: bool,
|
mountable: bool,
|
||||||
embassy_os: Option<EmbassyOsRecoveryInfo>,
|
start_os: Option<EmbassyOsRecoveryInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cifs() -> ParentHandler {
|
pub fn cifs() -> ParentHandler {
|
||||||
@@ -93,7 +93,7 @@ pub async fn add(
|
|||||||
password,
|
password,
|
||||||
};
|
};
|
||||||
let guard = TmpMountGuard::mount(&cifs, ReadOnly).await?;
|
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?;
|
guard.unmount().await?;
|
||||||
let id = ctx
|
let id = ctx
|
||||||
.db
|
.db
|
||||||
@@ -116,7 +116,7 @@ pub async fn add(
|
|||||||
path: cifs.path,
|
path: cifs.path,
|
||||||
username: cifs.username,
|
username: cifs.username,
|
||||||
mountable: true,
|
mountable: true,
|
||||||
embassy_os,
|
start_os,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ pub async fn update(
|
|||||||
password,
|
password,
|
||||||
};
|
};
|
||||||
let guard = TmpMountGuard::mount(&cifs, ReadOnly).await?;
|
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?;
|
guard.unmount().await?;
|
||||||
ctx.db
|
ctx.db
|
||||||
.mutate(|db| {
|
.mutate(|db| {
|
||||||
@@ -180,7 +180,7 @@ pub async fn update(
|
|||||||
path: cifs.path,
|
path: cifs.path,
|
||||||
username: cifs.username,
|
username: cifs.username,
|
||||||
mountable: true,
|
mountable: true,
|
||||||
embassy_os,
|
start_os,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -224,11 +224,11 @@ pub async fn list(db: &DatabaseModel) -> Result<Vec<(u32, CifsBackupTarget)>, Er
|
|||||||
let mut cifs = Vec::new();
|
let mut cifs = Vec::new();
|
||||||
for (id, model) in db.as_private().as_cifs().as_entries()? {
|
for (id, model) in db.as_private().as_cifs().as_entries()? {
|
||||||
let mount_info = model.de()?;
|
let mount_info = model.de()?;
|
||||||
let embassy_os = async {
|
let start_os = async {
|
||||||
let guard = TmpMountGuard::mount(&mount_info, ReadOnly).await?;
|
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?;
|
guard.unmount().await?;
|
||||||
Ok::<_, Error>(embassy_os)
|
Ok::<_, Error>(start_os)
|
||||||
}
|
}
|
||||||
.await;
|
.await;
|
||||||
cifs.push((
|
cifs.push((
|
||||||
@@ -237,8 +237,8 @@ pub async fn list(db: &DatabaseModel) -> Result<Vec<(u32, CifsBackupTarget)>, Er
|
|||||||
hostname: mount_info.hostname,
|
hostname: mount_info.hostname,
|
||||||
path: mount_info.path,
|
path: mount_info.path,
|
||||||
username: mount_info.username,
|
username: mount_info.username,
|
||||||
mountable: embassy_os.is_ok(),
|
mountable: start_os.is_ok(),
|
||||||
embassy_os: embassy_os.ok().and_then(|a| a),
|
start_os: start_os.ok().and_then(|a| a),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,6 @@ pub struct SetParams {
|
|||||||
pub config: StdinDeserializable<Option<Config>>,
|
pub config: StdinDeserializable<Option<Config>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Dr Why isn't this used?
|
|
||||||
// #[command(
|
// #[command(
|
||||||
// subcommands(self(set_impl(async, context(RpcContext))), set_dry),
|
// subcommands(self(set_impl(async, context(RpcContext))), set_dry),
|
||||||
// display(display_none),
|
// display(display_none),
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ use super::setup::CURRENT_SECRET;
|
|||||||
use crate::account::AccountInfo;
|
use crate::account::AccountInfo;
|
||||||
use crate::context::config::ServerConfig;
|
use crate::context::config::ServerConfig;
|
||||||
use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation, WebSocketHandler};
|
use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation, WebSocketHandler};
|
||||||
use crate::db::model::package::CurrentDependents;
|
|
||||||
use crate::db::prelude::PatchDbExt;
|
use crate::db::prelude::PatchDbExt;
|
||||||
use crate::dependencies::compute_dependency_config_errs;
|
use crate::dependencies::compute_dependency_config_errs;
|
||||||
use crate::disk::OsPartitionInfo;
|
use crate::disk::OsPartitionInfo;
|
||||||
@@ -207,39 +206,6 @@ impl RpcContext {
|
|||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn cleanup_and_initialize(&self) -> Result<(), Error> {
|
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::<BTreeMap<_, _>>();
|
|
||||||
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?;
|
self.services.init(&self).await?;
|
||||||
tracing::info!("Initialized Package Managers");
|
tracing::info!("Initialized Package Managers");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use emver::VersionRange;
|
||||||
use imbl_value::InternedString;
|
use imbl_value::InternedString;
|
||||||
use models::{DataUrl, HealthCheckId, HostId, PackageId};
|
use models::{DataUrl, HealthCheckId, HostId, PackageId};
|
||||||
use patch_db::json_ptr::JsonPointer;
|
use patch_db::json_ptr::JsonPointer;
|
||||||
@@ -299,7 +300,6 @@ pub struct PackageDataEntry {
|
|||||||
pub icon: DataUrl<'static>,
|
pub icon: DataUrl<'static>,
|
||||||
pub last_backup: Option<DateTime<Utc>>,
|
pub last_backup: Option<DateTime<Utc>>,
|
||||||
pub dependency_info: BTreeMap<PackageId, StaticDependencyInfo>,
|
pub dependency_info: BTreeMap<PackageId, StaticDependencyInfo>,
|
||||||
pub current_dependents: CurrentDependents,
|
|
||||||
pub current_dependencies: CurrentDependencies,
|
pub current_dependencies: CurrentDependencies,
|
||||||
pub interface_addresses: InterfaceAddressMap,
|
pub interface_addresses: InterfaceAddressMap,
|
||||||
pub hosts: HostInfo,
|
pub hosts: HostInfo,
|
||||||
@@ -357,29 +357,6 @@ impl Default for ExposedUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
|
||||||
pub struct CurrentDependents(pub BTreeMap<PackageId, CurrentDependencyInfo>);
|
|
||||||
impl CurrentDependents {
|
|
||||||
pub fn map(
|
|
||||||
mut self,
|
|
||||||
transform: impl Fn(
|
|
||||||
BTreeMap<PackageId, CurrentDependencyInfo>,
|
|
||||||
) -> BTreeMap<PackageId, CurrentDependencyInfo>,
|
|
||||||
) -> Self {
|
|
||||||
self.0 = transform(self.0);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Map for CurrentDependents {
|
|
||||||
type Key = PackageId;
|
|
||||||
type Value = CurrentDependencyInfo;
|
|
||||||
fn key_str(key: &Self::Key) -> Result<impl AsRef<str>, Error> {
|
|
||||||
Ok(key)
|
|
||||||
}
|
|
||||||
fn key_string(key: &Self::Key) -> Result<InternedString, Error> {
|
|
||||||
Ok(key.clone().into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||||
pub struct CurrentDependencies(pub BTreeMap<PackageId, CurrentDependencyInfo>);
|
pub struct CurrentDependencies(pub BTreeMap<PackageId, CurrentDependencyInfo>);
|
||||||
impl CurrentDependencies {
|
impl CurrentDependencies {
|
||||||
@@ -416,10 +393,21 @@ pub struct StaticDependencyInfo {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[serde(tag = "kind")]
|
#[serde(tag = "kind")]
|
||||||
pub enum CurrentDependencyInfo {
|
pub enum CurrentDependencyInfo {
|
||||||
Exists,
|
#[serde(rename_all = "camelCase")]
|
||||||
|
Exists {
|
||||||
|
#[ts(type = "string")]
|
||||||
|
url: Url,
|
||||||
|
#[ts(type = "string")]
|
||||||
|
version_spec: VersionRange,
|
||||||
|
},
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
Running {
|
Running {
|
||||||
|
#[ts(type = "string")]
|
||||||
|
url: Url,
|
||||||
|
#[ts(type = "string")]
|
||||||
|
version_spec: VersionRange,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[ts(type = "string[]")]
|
||||||
health_checks: BTreeSet<HealthCheckId>,
|
health_checks: BTreeSet<HealthCheckId>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ pub struct DepInfo {
|
|||||||
pub version: VersionRange,
|
pub version: VersionRange,
|
||||||
pub requirement: DependencyRequirement,
|
pub requirement: DependencyRequirement,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
#[serde(default)]
|
|
||||||
pub config: Option<Value>, // TODO: remove
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Parser)]
|
#[derive(Deserialize, Serialize, Parser)]
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ fn display_disk_info(params: WithIoFormat<Empty>, args: Vec<DiskInfo>) {
|
|||||||
} else {
|
} else {
|
||||||
"N/A"
|
"N/A"
|
||||||
},
|
},
|
||||||
if let Some(eos) = part.embassy_os.as_ref() {
|
if let Some(eos) = part.start_os.as_ref() {
|
||||||
eos.version.as_str()
|
eos.version.as_str()
|
||||||
} else {
|
} else {
|
||||||
"N/A"
|
"N/A"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ pub struct PartitionInfo {
|
|||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
pub capacity: u64,
|
pub capacity: u64,
|
||||||
pub used: Option<u64>,
|
pub used: Option<u64>,
|
||||||
pub embassy_os: Option<EmbassyOsRecoveryInfo>,
|
pub start_os: Option<EmbassyOsRecoveryInfo>,
|
||||||
pub guid: Option<String>,
|
pub guid: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,7 +390,7 @@ async fn disk_info(disk: PathBuf) -> DiskInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn part_info(part: PathBuf) -> PartitionInfo {
|
async fn part_info(part: PathBuf) -> PartitionInfo {
|
||||||
let mut embassy_os = None;
|
let mut start_os = None;
|
||||||
let label = get_label(&part)
|
let label = get_label(&part)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| tracing::warn!("Could not get label of {}: {}", part.display(), e.source))
|
.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
|
None
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
embassy_os = Some(recovery_info)
|
start_os = Some(recovery_info)
|
||||||
}
|
}
|
||||||
if let Err(e) = mount_guard.unmount().await {
|
if let Err(e) = mount_guard.unmount().await {
|
||||||
tracing::error!("Error unmounting partition {}: {}", part.display(), e);
|
tracing::error!("Error unmounting partition {}: {}", part.display(), e);
|
||||||
@@ -430,7 +430,7 @@ async fn part_info(part: PathBuf) -> PartitionInfo {
|
|||||||
label,
|
label,
|
||||||
capacity,
|
capacity,
|
||||||
used,
|
used,
|
||||||
embassy_os,
|
start_os,
|
||||||
guid: None,
|
guid: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ pub fn package() -> ParentHandler {
|
|||||||
.with_remote_cli::<CliContext>(),
|
.with_remote_cli::<CliContext>(),
|
||||||
)
|
)
|
||||||
.subcommand("dependency", dependencies::dependency())
|
.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", from_fn_async(service::connect_rpc).no_cli())
|
||||||
.subcommand(
|
.subcommand(
|
||||||
"connect",
|
"connect",
|
||||||
|
|||||||
@@ -183,7 +183,6 @@ pub async fn root_ca_start_time() -> Result<SystemTime, Error> {
|
|||||||
const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1;
|
const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1;
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref EC_GROUP: EcGroup = EcGroup::from_curve_name(EC_CURVE_NAME).unwrap();
|
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<Private>, target: &Path) -> Result<(), Error> {
|
pub async fn export_key(key: &PKey<Private>, target: &Path) -> Result<(), Error> {
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ pub fn main_ui_server_router(ctx: RpcContext) -> Router {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.fallback(any(move |request: Request| async move {
|
.fallback(any(move |request: Request| async move {
|
||||||
main_embassy_ui(request, ctx)
|
main_start_os_ui(request, ctx)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(server_error)
|
.unwrap_or_else(server_error)
|
||||||
}))
|
}))
|
||||||
@@ -218,7 +218,7 @@ async fn if_authorized<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn main_embassy_ui(req: Request, ctx: RpcContext) -> Result<Response, Error> {
|
async fn main_start_os_ui(req: Request, ctx: RpcContext) -> Result<Response, Error> {
|
||||||
let (request_parts, _body) = req.into_parts();
|
let (request_parts, _body) = req.into_parts();
|
||||||
match (
|
match (
|
||||||
&request_parts.method,
|
&request_parts.method,
|
||||||
|
|||||||
@@ -768,7 +768,7 @@ async fn test() {
|
|||||||
let mut conn = torut::control::UnauthenticatedConn::new(
|
let mut conn = torut::control::UnauthenticatedConn::new(
|
||||||
TcpStream::connect(SocketAddr::from(([127, 0, 0, 1], 9051)))
|
TcpStream::connect(SocketAddr::from(([127, 0, 0, 1], 9051)))
|
||||||
.await
|
.await
|
||||||
.unwrap(), // TODO
|
.unwrap(),
|
||||||
);
|
);
|
||||||
let auth = conn
|
let auth = conn
|
||||||
.load_protocol_info()
|
.load_protocol_info()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use rpc_toolkit::{command, from_fn_async, AnyContext, HandlerExt, ParentHandler}
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::context::config::ServerConfig;
|
||||||
use crate::context::{CliContext, InstallContext};
|
use crate::context::{CliContext, InstallContext};
|
||||||
use crate::disk::mount::filesystem::bind::Bind;
|
use crate::disk::mount::filesystem::bind::Bind;
|
||||||
use crate::disk::mount::filesystem::block_dev::BlockDev;
|
use crate::disk::mount::filesystem::block_dev::BlockDev;
|
||||||
@@ -23,14 +24,6 @@ use crate::ARCH;
|
|||||||
mod gpt;
|
mod gpt;
|
||||||
mod mbr;
|
mod mbr;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct PostInstallConfig {
|
|
||||||
os_partitions: OsPartitionInfo,
|
|
||||||
ethernet_interface: String,
|
|
||||||
wifi_interface: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn install() -> ParentHandler {
|
pub fn install() -> ParentHandler {
|
||||||
ParentHandler::new()
|
ParentHandler::new()
|
||||||
.subcommand("disk", disk())
|
.subcommand("disk", disk())
|
||||||
@@ -263,10 +256,11 @@ pub async fn execute(
|
|||||||
|
|
||||||
tokio::fs::write(
|
tokio::fs::write(
|
||||||
rootfs.path().join("config/config.yaml"),
|
rootfs.path().join("config/config.yaml"),
|
||||||
IoFormat::Yaml.to_vec(&PostInstallConfig {
|
IoFormat::Yaml.to_vec(&ServerConfig {
|
||||||
os_partitions: part_info.clone(),
|
os_partitions: Some(part_info.clone()),
|
||||||
ethernet_interface: eth_iface,
|
ethernet_interface: Some(eth_iface),
|
||||||
wifi_interface: wifi_iface,
|
wifi_interface: wifi_iface,
|
||||||
|
..Default::default()
|
||||||
})?,
|
})?,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -299,7 +293,7 @@ pub async fn execute(
|
|||||||
.invoke(crate::ErrorKind::OpenSsh)
|
.invoke(crate::ErrorKind::OpenSsh)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let embassy_fs = MountGuard::mount(
|
let start_os_fs = MountGuard::mount(
|
||||||
&Bind::new(rootfs.path()),
|
&Bind::new(rootfs.path()),
|
||||||
current.join("media/embassy/embassyfs"),
|
current.join("media/embassy/embassyfs"),
|
||||||
MountType::ReadOnly,
|
MountType::ReadOnly,
|
||||||
@@ -348,7 +342,7 @@ pub async fn execute(
|
|||||||
}
|
}
|
||||||
sys.unmount(false).await?;
|
sys.unmount(false).await?;
|
||||||
proc.unmount(false).await?;
|
proc.unmount(false).await?;
|
||||||
embassy_fs.unmount(false).await?;
|
start_os_fs.unmount(false).await?;
|
||||||
if let Some(efi) = efi {
|
if let Some(efi) = efi {
|
||||||
efi.unmount(false).await?;
|
efi.unmount(false).await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::{borrow::Borrow, collections::BTreeMap};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use imbl_value::{json, InOMap, InternedString, Value};
|
use imbl_value::{json, InOMap, InternedString, Value};
|
||||||
@@ -15,48 +15,6 @@ pub fn display_properties(response: Value) {
|
|||||||
println!("{}", response);
|
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::<BTreeMap<String,_>>()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
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::<InOMap<InternedString, Value>>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Parser)]
|
#[derive(Deserialize, Serialize, Parser)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[command(rename_all = "kebab-case")]
|
#[command(rename_all = "kebab-case")]
|
||||||
@@ -68,19 +26,11 @@ pub async fn properties(
|
|||||||
ctx: RpcContext,
|
ctx: RpcContext,
|
||||||
PropertiesParam { id }: PropertiesParam,
|
PropertiesParam { id }: PropertiesParam,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
let peeked = ctx.db.peek().await;
|
match &*ctx.services.get(&id).await {
|
||||||
let data = peeked
|
Some(service) => service.properties().await,
|
||||||
.as_private()
|
None => Err(Error::new(
|
||||||
.as_package_stores()
|
eyre!("Could not find a service with id {id}"),
|
||||||
.as_idx(&id)
|
ErrorKind::NotFound,
|
||||||
.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))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ async fn add_image(
|
|||||||
Command::new("bash")
|
Command::new("bash")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(format!(
|
.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(),
|
container_id = container_id.trim(),
|
||||||
sqfs = sqfs_path.display()
|
sqfs = sqfs_path.display()
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use imbl_value::InOMap;
|
use imbl_value::InOMap;
|
||||||
pub use models::PackageId;
|
pub use models::PackageId;
|
||||||
|
use models::VolumeId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
@@ -11,7 +13,6 @@ use crate::prelude::*;
|
|||||||
use crate::s9pk::manifest::{Alerts, Description, HardwareRequirements};
|
use crate::s9pk::manifest::{Alerts, Description, HardwareRequirements};
|
||||||
use crate::util::Version;
|
use crate::util::Version;
|
||||||
use crate::version::{Current, VersionT};
|
use crate::version::{Current, VersionT};
|
||||||
use crate::volume::Volumes;
|
|
||||||
|
|
||||||
fn current_version() -> Version {
|
fn current_version() -> Version {
|
||||||
Current::new().semver().into()
|
Current::new().semver().into()
|
||||||
@@ -40,7 +41,7 @@ pub struct Manifest {
|
|||||||
pub donation_url: Option<Url>,
|
pub donation_url: Option<Url>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub alerts: Alerts,
|
pub alerts: Alerts,
|
||||||
pub volumes: Volumes,
|
pub volumes: BTreeMap<VolumeId, Value>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub dependencies: Dependencies,
|
pub dependencies: Dependencies,
|
||||||
pub config: Option<InOMap<String, Value>>,
|
pub config: Option<InOMap<String, Value>>,
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ use crate::s9pk::v1::reader::S9pkReader;
|
|||||||
use crate::s9pk::v2::S9pk;
|
use crate::s9pk::v2::S9pk;
|
||||||
use crate::util::io::TmpDir;
|
use crate::util::io::TmpDir;
|
||||||
use crate::util::Invoke;
|
use crate::util::Invoke;
|
||||||
use crate::volume::Volume;
|
|
||||||
use crate::ARCH;
|
use crate::ARCH;
|
||||||
|
|
||||||
pub const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x01];
|
pub const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x01];
|
||||||
@@ -254,7 +253,7 @@ impl S9pk<Section<MultiCursorFile>> {
|
|||||||
for (asset_id, _) in manifest
|
for (asset_id, _) in manifest
|
||||||
.volumes
|
.volumes
|
||||||
.iter()
|
.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 assets_path = asset_dir.join(&asset_id);
|
||||||
let sqfs_path = assets_path.with_extension("squashfs");
|
let sqfs_path = assets_path.with_extension("squashfs");
|
||||||
@@ -338,13 +337,13 @@ impl From<ManifestV1> for Manifest {
|
|||||||
assets: value
|
assets: value
|
||||||
.volumes
|
.volumes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, v)| matches!(v, &&Volume::Assets { .. }))
|
.filter(|(_, v)| v.get("type").and_then(|v| v.as_str()) == Some("assets"))
|
||||||
.map(|(id, _)| id.clone())
|
.map(|(id, _)| id.clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
volumes: value
|
volumes: value
|
||||||
.volumes
|
.volumes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, v)| matches!(v, &&Volume::Data { .. }))
|
.filter(|(_, v)| v.get("type").and_then(|v| v.as_str()) == Some("data"))
|
||||||
.map(|(id, _)| id.clone())
|
.map(|(id, _)| id.clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
alerts: value.alerts,
|
alerts: value.alerts,
|
||||||
|
|||||||
@@ -325,6 +325,17 @@ impl Service {
|
|||||||
.await
|
.await
|
||||||
.with_kind(ErrorKind::Action)
|
.with_kind(ErrorKind::Action)
|
||||||
}
|
}
|
||||||
|
pub async fn properties(&self) -> Result<Value, Error> {
|
||||||
|
let container = &self.seed.persistent_container;
|
||||||
|
container
|
||||||
|
.execute::<Value>(
|
||||||
|
ProcedureName::Properties,
|
||||||
|
Value::Null,
|
||||||
|
Some(Duration::from_secs(30)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_kind(ErrorKind::Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn shutdown(self) -> Result<(), Error> {
|
pub async fn shutdown(self) -> Result<(), Error> {
|
||||||
self.actor
|
self.actor
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::sync::{Arc, Weak};
|
|||||||
|
|
||||||
use clap::builder::ValueParserFactory;
|
use clap::builder::ValueParserFactory;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use emver::VersionRange;
|
||||||
use imbl::OrdMap;
|
use imbl::OrdMap;
|
||||||
use imbl_value::{json, InternedString};
|
use imbl_value::{json, InternedString};
|
||||||
use models::{ActionId, HealthCheckId, ImageId, PackageId, VolumeId};
|
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 serde::{Deserialize, Serialize};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::db::model::package::{
|
use crate::db::model::package::{
|
||||||
CurrentDependencies, CurrentDependencyInfo, ExposedUI, StoreExposedUI,
|
CurrentDependencies, CurrentDependencyInfo, ExposedUI, StoreExposedUI,
|
||||||
@@ -255,7 +257,7 @@ struct RemoveAddressParams {
|
|||||||
}
|
}
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "camelCase")]
|
||||||
enum AllowedStatuses {
|
enum AllowedStatuses {
|
||||||
OnlyRunning, // onlyRunning
|
OnlyRunning, // onlyRunning
|
||||||
OnlyStopped,
|
OnlyStopped,
|
||||||
@@ -1096,15 +1098,19 @@ enum DependencyRequirement {
|
|||||||
id: PackageId,
|
id: PackageId,
|
||||||
#[ts(type = "string[]")]
|
#[ts(type = "string[]")]
|
||||||
health_checks: BTreeSet<HealthCheckId>,
|
health_checks: BTreeSet<HealthCheckId>,
|
||||||
version_spec: String,
|
#[ts(type = "string")]
|
||||||
url: String,
|
version_spec: VersionRange,
|
||||||
|
#[ts(type = "string")]
|
||||||
|
url: Url,
|
||||||
},
|
},
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
Exists {
|
Exists {
|
||||||
#[ts(type = "string")]
|
#[ts(type = "string")]
|
||||||
id: PackageId,
|
id: PackageId,
|
||||||
version_spec: String,
|
#[ts(type = "string")]
|
||||||
url: String,
|
version_spec: VersionRange,
|
||||||
|
#[ts(type = "string")]
|
||||||
|
url: Url,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// filebrowser:exists,bitcoind:running:foo+bar+baz
|
// filebrowser:exists,bitcoind:running:foo+bar+baz
|
||||||
@@ -1114,8 +1120,8 @@ impl FromStr for DependencyRequirement {
|
|||||||
match s.split_once(':') {
|
match s.split_once(':') {
|
||||||
Some((id, "e")) | Some((id, "exists")) => Ok(Self::Exists {
|
Some((id, "e")) | Some((id, "exists")) => Ok(Self::Exists {
|
||||||
id: id.parse()?,
|
id: id.parse()?,
|
||||||
url: "".to_string(),
|
url: "".parse()?, // TODO
|
||||||
version_spec: "*".to_string(),
|
version_spec: "*".parse()?, // TODO
|
||||||
}),
|
}),
|
||||||
Some((id, rest)) => {
|
Some((id, rest)) => {
|
||||||
let health_checks = match rest.split_once(':') {
|
let health_checks = match rest.split_once(':') {
|
||||||
@@ -1138,15 +1144,15 @@ impl FromStr for DependencyRequirement {
|
|||||||
Ok(Self::Running {
|
Ok(Self::Running {
|
||||||
id: id.parse()?,
|
id: id.parse()?,
|
||||||
health_checks,
|
health_checks,
|
||||||
url: "".to_string(),
|
url: "".parse()?, // TODO
|
||||||
version_spec: "*".to_string(),
|
version_spec: "*".parse()?, // TODO
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => Ok(Self::Running {
|
None => Ok(Self::Running {
|
||||||
id: s.parse()?,
|
id: s.parse()?,
|
||||||
health_checks: BTreeSet::new(),
|
health_checks: BTreeSet::new(),
|
||||||
url: "".to_string(),
|
url: "".parse()?, // TODO
|
||||||
version_spec: "*".to_string(),
|
version_spec: "*".parse()?, // TODO
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1183,23 +1189,23 @@ async fn set_dependencies(
|
|||||||
id,
|
id,
|
||||||
url,
|
url,
|
||||||
version_spec,
|
version_spec,
|
||||||
} => (id, CurrentDependencyInfo::Exists),
|
} => (id, CurrentDependencyInfo::Exists { url, version_spec }),
|
||||||
DependencyRequirement::Running {
|
DependencyRequirement::Running {
|
||||||
id,
|
id,
|
||||||
health_checks,
|
health_checks,
|
||||||
url,
|
url,
|
||||||
version_spec,
|
version_spec,
|
||||||
} => (id, CurrentDependencyInfo::Running { health_checks }),
|
} => (
|
||||||
|
id,
|
||||||
|
CurrentDependencyInfo::Running {
|
||||||
|
url,
|
||||||
|
version_spec,
|
||||||
|
health_checks,
|
||||||
|
},
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.collect(),
|
.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()
|
db.as_public_mut()
|
||||||
.as_package_data_mut()
|
.as_package_data_mut()
|
||||||
.as_idx_mut(id)
|
.as_idx_mut(id)
|
||||||
|
|||||||
@@ -171,7 +171,6 @@ impl ServiceMap {
|
|||||||
icon,
|
icon,
|
||||||
last_backup: None,
|
last_backup: None,
|
||||||
dependency_info: Default::default(),
|
dependency_info: Default::default(),
|
||||||
current_dependents: Default::default(), // TODO: initialize
|
|
||||||
current_dependencies: Default::default(),
|
current_dependencies: Default::default(),
|
||||||
interface_addresses: Default::default(),
|
interface_addresses: Default::default(),
|
||||||
hosts: Default::default(),
|
hosts: Default::default(),
|
||||||
|
|||||||
@@ -240,9 +240,9 @@ pub async fn verify_cifs(
|
|||||||
ReadWrite,
|
ReadWrite,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let embassy_os = recovery_info(guard.path()).await?;
|
let start_os = recovery_info(guard.path()).await?;
|
||||||
guard.unmount().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)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
@@ -256,8 +256,8 @@ pub enum RecoverySource {
|
|||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ExecuteParams {
|
pub struct ExecuteParams {
|
||||||
embassy_logicalname: PathBuf,
|
start_os_logicalname: PathBuf,
|
||||||
embassy_password: EncryptedWire,
|
start_os_password: EncryptedWire,
|
||||||
recovery_source: Option<RecoverySource>,
|
recovery_source: Option<RecoverySource>,
|
||||||
recovery_password: Option<EncryptedWire>,
|
recovery_password: Option<EncryptedWire>,
|
||||||
}
|
}
|
||||||
@@ -266,13 +266,13 @@ pub struct ExecuteParams {
|
|||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
ctx: SetupContext,
|
ctx: SetupContext,
|
||||||
ExecuteParams {
|
ExecuteParams {
|
||||||
embassy_logicalname,
|
start_os_logicalname,
|
||||||
embassy_password,
|
start_os_password,
|
||||||
recovery_source,
|
recovery_source,
|
||||||
recovery_password,
|
recovery_password,
|
||||||
}: ExecuteParams,
|
}: ExecuteParams,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let embassy_password = match embassy_password.decrypt(&*ctx) {
|
let start_os_password = match start_os_password.decrypt(&*ctx) {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
@@ -311,8 +311,8 @@ pub async fn execute(
|
|||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
match execute_inner(
|
match execute_inner(
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
embassy_logicalname,
|
start_os_logicalname,
|
||||||
embassy_password,
|
start_os_password,
|
||||||
recovery_source,
|
recovery_source,
|
||||||
recovery_password,
|
recovery_password,
|
||||||
)
|
)
|
||||||
@@ -375,8 +375,8 @@ pub async fn exit(ctx: SetupContext) -> Result<(), Error> {
|
|||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn execute_inner(
|
pub async fn execute_inner(
|
||||||
ctx: SetupContext,
|
ctx: SetupContext,
|
||||||
embassy_logicalname: PathBuf,
|
start_os_logicalname: PathBuf,
|
||||||
embassy_password: String,
|
start_os_password: String,
|
||||||
recovery_source: Option<RecoverySource>,
|
recovery_source: Option<RecoverySource>,
|
||||||
recovery_password: Option<String>,
|
recovery_password: Option<String>,
|
||||||
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
||||||
@@ -387,7 +387,7 @@ pub async fn execute_inner(
|
|||||||
};
|
};
|
||||||
let guid = Arc::new(
|
let guid = Arc::new(
|
||||||
crate::disk::main::create(
|
crate::disk::main::create(
|
||||||
&[embassy_logicalname],
|
&[start_os_logicalname],
|
||||||
&pvscan().await?,
|
&pvscan().await?,
|
||||||
&ctx.datadir,
|
&ctx.datadir,
|
||||||
encryption_password,
|
encryption_password,
|
||||||
@@ -403,20 +403,20 @@ pub async fn execute_inner(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(RecoverySource::Backup { target }) = recovery_source {
|
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 {
|
} 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 {
|
} 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))
|
Ok((guid, hostname, tor_addr, root_ca))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fresh_setup(
|
async fn fresh_setup(
|
||||||
ctx: &SetupContext,
|
ctx: &SetupContext,
|
||||||
embassy_password: &str,
|
start_os_password: &str,
|
||||||
) -> Result<(Hostname, OnionAddressV3, X509), Error> {
|
) -> 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?;
|
let db = ctx.db().await?;
|
||||||
db.put(&ROOT, &Database::init(&account)?).await?;
|
db.put(&ROOT, &Database::init(&account)?).await?;
|
||||||
drop(db);
|
drop(db);
|
||||||
@@ -432,7 +432,7 @@ async fn fresh_setup(
|
|||||||
async fn recover(
|
async fn recover(
|
||||||
ctx: SetupContext,
|
ctx: SetupContext,
|
||||||
guid: Arc<String>,
|
guid: Arc<String>,
|
||||||
embassy_password: String,
|
start_os_password: String,
|
||||||
recovery_source: BackupTargetFS,
|
recovery_source: BackupTargetFS,
|
||||||
recovery_password: Option<String>,
|
recovery_password: Option<String>,
|
||||||
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
||||||
@@ -440,7 +440,7 @@ async fn recover(
|
|||||||
recover_full_embassy(
|
recover_full_embassy(
|
||||||
ctx,
|
ctx,
|
||||||
guid.clone(),
|
guid.clone(),
|
||||||
embassy_password,
|
start_os_password,
|
||||||
recovery_source,
|
recovery_source,
|
||||||
recovery_password,
|
recovery_password,
|
||||||
)
|
)
|
||||||
@@ -452,7 +452,7 @@ async fn migrate(
|
|||||||
ctx: SetupContext,
|
ctx: SetupContext,
|
||||||
guid: Arc<String>,
|
guid: Arc<String>,
|
||||||
old_guid: &str,
|
old_guid: &str,
|
||||||
embassy_password: String,
|
start_os_password: String,
|
||||||
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
) -> Result<(Arc<String>, Hostname, OnionAddressV3, X509), Error> {
|
||||||
*ctx.setup_status.write().await = Some(Ok(SetupStatus {
|
*ctx.setup_status.write().await = Some(Ok(SetupStatus {
|
||||||
bytes_transferred: 0,
|
bytes_transferred: 0,
|
||||||
@@ -537,7 +537,7 @@ async fn migrate(
|
|||||||
} => res,
|
} => 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?;
|
crate::disk::main::export(&old_guid, "/media/embassy/migrate").await?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub use helpers::script_dir;
|
pub use helpers::script_dir;
|
||||||
use imbl_value::InternedString;
|
|
||||||
pub use models::VolumeId;
|
pub use models::VolumeId;
|
||||||
use models::{HostId, PackageId};
|
use models::{HostId, PackageId};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tracing::instrument;
|
|
||||||
|
|
||||||
use crate::context::RpcContext;
|
|
||||||
use crate::net::PACKAGE_CERT_PATH;
|
use crate::net::PACKAGE_CERT_PATH;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::util::Version;
|
use crate::util::Version;
|
||||||
@@ -17,72 +11,6 @@ use crate::util::Version;
|
|||||||
pub const PKG_VOLUME_DIR: &str = "package-data/volumes";
|
pub const PKG_VOLUME_DIR: &str = "package-data/volumes";
|
||||||
pub const BACKUP_DIR: &str = "/media/embassy/backups";
|
pub const BACKUP_DIR: &str = "/media/embassy/backups";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
|
||||||
pub struct Volumes(BTreeMap<VolumeId, Volume>);
|
|
||||||
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<PathBuf> {
|
|
||||||
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<VolumeId, Volume>;
|
|
||||||
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<impl AsRef<str>, Error> {
|
|
||||||
Ok(key)
|
|
||||||
}
|
|
||||||
fn key_string(key: &Self::Key) -> Result<InternedString, Error> {
|
|
||||||
match key {
|
|
||||||
VolumeId::Custom(id) => Ok(id.clone().into()),
|
|
||||||
_ => Self::key_str(key).map(|s| InternedString::intern(s.as_ref())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data_dir<P: AsRef<Path>>(datadir: P, pkg_id: &PackageId, volume_id: &VolumeId) -> PathBuf {
|
pub fn data_dir<P: AsRef<Path>>(datadir: P, pkg_id: &PackageId, volume_id: &VolumeId) -> PathBuf {
|
||||||
datadir
|
datadir
|
||||||
.as_ref()
|
.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 {
|
pub fn cert_dir(pkg_id: &PackageId, host_id: &HostId) -> PathBuf {
|
||||||
Path::new(PACKAGE_CERT_PATH).join(pkg_id).join(host_id)
|
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<Path>,
|
|
||||||
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<Path>) -> Option<PathBuf> {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export type ActionMetadata = {
|
|||||||
warning: string | null
|
warning: string | null
|
||||||
input: InputSpec
|
input: InputSpec
|
||||||
disabled: boolean
|
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
|
* So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions
|
||||||
*/
|
*/
|
||||||
|
|||||||
30
web/package-lock.json
generated
30
web/package-lock.json
generated
@@ -8,17 +8,17 @@
|
|||||||
"name": "startos-ui",
|
"name": "startos-ui",
|
||||||
"version": "0.3.5.1",
|
"version": "0.3.5.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^17.0.6",
|
"@angular/animations": "^17.3.1",
|
||||||
"@angular/cdk": "^17.0.3",
|
"@angular/cdk": "^17.3.1",
|
||||||
"@angular/common": "^17.0.6",
|
"@angular/common": "^17.3.1",
|
||||||
"@angular/compiler": "^17.0.6",
|
"@angular/compiler": "^17.3.1",
|
||||||
"@angular/core": "^17.0.6",
|
"@angular/core": "^17.3.1",
|
||||||
"@angular/forms": "^17.0.6",
|
"@angular/forms": "^17.3.1",
|
||||||
"@angular/platform-browser": "^17.0.6",
|
"@angular/platform-browser": "^17.3.1",
|
||||||
"@angular/platform-browser-dynamic": "^17.0.6",
|
"@angular/platform-browser-dynamic": "^17.3.1",
|
||||||
"@angular/pwa": "^17.0.6",
|
"@angular/pwa": "^17.3.1",
|
||||||
"@angular/router": "^17.0.6",
|
"@angular/router": "^17.3.1",
|
||||||
"@angular/service-worker": "^17.0.6",
|
"@angular/service-worker": "^17.3.1",
|
||||||
"@ionic/angular": "^6.1.15",
|
"@ionic/angular": "^6.1.15",
|
||||||
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||||
"@start9labs/argon2": "^0.1.0",
|
"@start9labs/argon2": "^0.1.0",
|
||||||
@@ -62,10 +62,10 @@
|
|||||||
"zone.js": "^0.14.2"
|
"zone.js": "^0.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^17.0.6",
|
"@angular-devkit/build-angular": "^17.3.1",
|
||||||
"@angular/cli": "^17.0.6",
|
"@angular/cli": "^17.3.1",
|
||||||
"@angular/compiler-cli": "^17.0.6",
|
"@angular/compiler-cli": "^17.3.1",
|
||||||
"@angular/language-service": "^17.0.6",
|
"@angular/language-service": "^17.3.1",
|
||||||
"@ionic/cli": "^6.19.0",
|
"@ionic/cli": "^6.19.0",
|
||||||
"@types/dompurify": "^2.3.3",
|
"@types/dompurify": "^2.3.3",
|
||||||
"@types/estree": "^0.0.51",
|
"@types/estree": "^0.0.51",
|
||||||
|
|||||||
@@ -179,13 +179,13 @@ export class ServiceRoute {
|
|||||||
depErrors,
|
depErrors,
|
||||||
)
|
)
|
||||||
|
|
||||||
const depInfo = pkg.dependencyInfo[depId]
|
const { title, icon, versionSpec } = pkg.currentDependencies[depId]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: depId,
|
id: depId,
|
||||||
version: pkg.currentDependencies[depId].versionRange,
|
version: versionSpec,
|
||||||
title: depInfo?.title || depId,
|
title,
|
||||||
icon: depInfo?.icon || '',
|
icon,
|
||||||
errorText: errorText
|
errorText: errorText
|
||||||
? `${errorText}. ${pkgManifest.title} will not work as expected.`
|
? `${errorText}. ${pkgManifest.title} will not work as expected.`
|
||||||
: '',
|
: '',
|
||||||
@@ -252,7 +252,7 @@ export class ServiceRoute {
|
|||||||
return this.installDep(pkg, pkgManifest, depId)
|
return this.installDep(pkg, pkgManifest, depId)
|
||||||
case 'configure':
|
case 'configure':
|
||||||
return this.formDialog.open<PackageConfigData>(ServiceConfigModal, {
|
return this.formDialog.open<PackageConfigData>(ServiceConfigModal, {
|
||||||
label: `${pkg.dependencyInfo[depId].title} config`,
|
label: `${pkg.currentDependencies[depId].title} config`,
|
||||||
data: {
|
data: {
|
||||||
pkgId: depId,
|
pkgId: depId,
|
||||||
dependentInfo: pkgManifest,
|
dependentInfo: pkgManifest,
|
||||||
@@ -269,7 +269,7 @@ export class ServiceRoute {
|
|||||||
const dependentInfo: DependentInfo = {
|
const dependentInfo: DependentInfo = {
|
||||||
id: manifest.id,
|
id: manifest.id,
|
||||||
title: manifest.title,
|
title: manifest.title,
|
||||||
version: pkg.currentDependencies[depId].versionRange,
|
version: pkg.currentDependencies[depId].versionSpec,
|
||||||
}
|
}
|
||||||
const navigationExtras: NavigationExtras = {
|
const navigationExtras: NavigationExtras = {
|
||||||
state: { dependentInfo },
|
state: { dependentInfo },
|
||||||
|
|||||||
@@ -1493,7 +1493,6 @@ export module Mock {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentDependencies: {},
|
currentDependencies: {},
|
||||||
dependencyInfo: {},
|
|
||||||
marketplaceUrl: 'https://registry.start9.com/',
|
marketplaceUrl: 'https://registry.start9.com/',
|
||||||
developerKey: 'developer-key',
|
developerKey: 'developer-key',
|
||||||
outboundProxy: null,
|
outboundProxy: null,
|
||||||
@@ -1624,15 +1623,13 @@ export module Mock {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentDependencies: {
|
currentDependencies: {
|
||||||
bitcoind: {
|
|
||||||
versionRange: '>=26.0.0',
|
|
||||||
healthChecks: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencyInfo: {
|
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
title: Mock.MockManifestBitcoind.title,
|
title: Mock.MockManifestBitcoind.title,
|
||||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||||
|
kind: 'running',
|
||||||
|
registryUrl: '',
|
||||||
|
versionSpec: '>=26.0.0',
|
||||||
|
healthChecks: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
marketplaceUrl: 'https://registry.start9.com/',
|
marketplaceUrl: 'https://registry.start9.com/',
|
||||||
@@ -1871,23 +1868,21 @@ export module Mock {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentDependencies: {
|
currentDependencies: {
|
||||||
bitcoind: {
|
|
||||||
versionRange: '>=26.0.0',
|
|
||||||
healthChecks: [],
|
|
||||||
},
|
|
||||||
'btc-rpc-proxy': {
|
|
||||||
versionRange: '>2.0.0', // @TODO
|
|
||||||
healthChecks: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencyInfo: {
|
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
title: Mock.MockManifestBitcoind.title,
|
title: Mock.MockManifestBitcoind.title,
|
||||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||||
|
kind: 'running',
|
||||||
|
registryUrl: 'https://registry.start9.com',
|
||||||
|
versionSpec: '>=26.0.0',
|
||||||
|
healthChecks: [],
|
||||||
},
|
},
|
||||||
'btc-rpc-proxy': {
|
'btc-rpc-proxy': {
|
||||||
title: Mock.MockManifestBitcoinProxy.title,
|
title: Mock.MockManifestBitcoinProxy.title,
|
||||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
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/',
|
marketplaceUrl: 'https://registry.start9.com/',
|
||||||
|
|||||||
@@ -402,7 +402,6 @@ export const mockPatchData: DataModel = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentDependencies: {},
|
currentDependencies: {},
|
||||||
dependencyInfo: {},
|
|
||||||
marketplaceUrl: 'https://registry.start9.com/',
|
marketplaceUrl: 'https://registry.start9.com/',
|
||||||
developerKey: 'developer-key',
|
developerKey: 'developer-key',
|
||||||
outboundProxy: null,
|
outboundProxy: null,
|
||||||
@@ -639,23 +638,21 @@ export const mockPatchData: DataModel = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentDependencies: {
|
currentDependencies: {
|
||||||
bitcoind: {
|
|
||||||
versionRange: '>=26.0.0',
|
|
||||||
healthChecks: [],
|
|
||||||
},
|
|
||||||
'btc-rpc-proxy': {
|
|
||||||
versionRange: '>2.0.0',
|
|
||||||
healthChecks: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencyInfo: {
|
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
title: 'Bitcoin Core',
|
title: 'Bitcoin Core',
|
||||||
icon: 'assets/img/service-icons/bitcoind.svg',
|
icon: 'assets/img/service-icons/bitcoind.svg',
|
||||||
|
kind: 'running',
|
||||||
|
registryUrl: 'https://registry.start9.com',
|
||||||
|
versionSpec: '>=26.0.0',
|
||||||
|
healthChecks: [],
|
||||||
},
|
},
|
||||||
'btc-rpc-proxy': {
|
'btc-rpc-proxy': {
|
||||||
title: 'Bitcoin Proxy',
|
title: 'Bitcoin Proxy',
|
||||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
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/',
|
marketplaceUrl: 'https://registry.start9.com/',
|
||||||
|
|||||||
@@ -88,14 +88,14 @@ export class DepErrorService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const versionRange = pkg.currentDependencies[depId].versionRange
|
const versionSpec = pkg.currentDependencies[depId].versionSpec
|
||||||
const depManifest = dep.stateInfo.manifest
|
const depManifest = dep.stateInfo.manifest
|
||||||
|
|
||||||
// incorrect version
|
// incorrect version
|
||||||
if (!this.emver.satisfies(depManifest.version, versionRange)) {
|
if (!this.emver.satisfies(depManifest.version, versionSpec)) {
|
||||||
return {
|
return {
|
||||||
type: DependencyErrorType.IncorrectVersion,
|
type: DependencyErrorType.IncorrectVersion,
|
||||||
expected: versionRange,
|
expected: versionSpec,
|
||||||
received: depManifest.version,
|
received: depManifest.version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,13 +154,7 @@ export type PackageDataEntry<T extends StateInfo = StateInfo> = {
|
|||||||
status: Status
|
status: Status
|
||||||
actions: Record<string, T.ActionMetadata>
|
actions: Record<string, T.ActionMetadata>
|
||||||
lastBackup: string | null
|
lastBackup: string | null
|
||||||
currentDependencies: { [id: string]: CurrentDependencyInfo }
|
currentDependencies: Record<string, CurrentDependencyInfo>
|
||||||
dependencyInfo: {
|
|
||||||
[id: string]: {
|
|
||||||
title: string
|
|
||||||
icon: Url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serviceInterfaces: Record<string, T.ServiceInterfaceWithHostInfo>
|
serviceInterfaces: Record<string, T.ServiceInterfaceWithHostInfo>
|
||||||
marketplaceUrl: string | null
|
marketplaceUrl: string | null
|
||||||
developerKey: string
|
developerKey: string
|
||||||
@@ -197,7 +191,11 @@ export enum PackageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CurrentDependencyInfo {
|
export interface CurrentDependencyInfo {
|
||||||
versionRange: string
|
title: string
|
||||||
|
icon: string
|
||||||
|
kind: 'exists' | 'running'
|
||||||
|
registryUrl: string
|
||||||
|
versionSpec: string
|
||||||
healthChecks: string[] // array of health check IDs
|
healthChecks: string[] // array of health check IDs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ export function dryUpdate(
|
|||||||
pkg =>
|
pkg =>
|
||||||
Object.keys(pkg.currentDependencies || {}).some(
|
Object.keys(pkg.currentDependencies || {}).some(
|
||||||
pkgId => pkgId === id,
|
pkgId => pkgId === id,
|
||||||
) &&
|
) && !emver.satisfies(version, pkg.currentDependencies[id].versionSpec),
|
||||||
!emver.satisfies(version, pkg.currentDependencies[id].versionRange),
|
|
||||||
)
|
)
|
||||||
.map(pkg => getManifest(pkg).title)
|
.map(pkg => getManifest(pkg).title)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user