feat: Change the commands

This commit is contained in:
Blu-J
2023-06-08 14:54:14 -06:00
parent b06e46c181
commit 9156d89903
11 changed files with 270 additions and 140 deletions

View File

@@ -22,7 +22,7 @@ import {
} from "./types"
import * as patterns from "./util/patterns"
import { Utils } from "./util/utils"
import { DependencyConfig } from "./dependencyConfig/DependencyConfig"
import { DependencyConfig, Update } from "./dependencyConfig/DependencyConfig"
import { BackupSet, Backups } from "./backup/Backups"
import { smtpConfig } from "./config/configConstants"
import { Daemons } from "./mainFn/Daemons"
@@ -231,18 +231,20 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
localConfig,
remoteConfig,
dependencyConfig,
update,
}: {
localConfig: Config<LocalConfig, Store> | Config<LocalConfig, never>
remoteConfig: Config<RemoteConfig, any> | Config<RemoteConfig, never>
dependencyConfig: (options: {
effects: Effects
localConfig: LocalConfig
remoteConfig: RemoteConfig
utils: Utils<Store>
}) => Promise<void | DeepPartial<RemoteConfig>>
update?: Update<void | DeepPartial<RemoteConfig>, RemoteConfig>
}) {
return new DependencyConfig<Store, LocalConfig, RemoteConfig>(
dependencyConfig,
update,
)
},
},

View File

@@ -134,15 +134,15 @@ export class Backups<M extends SDKManifest> {
// path: item.dstPath,
// })
// }
await effects
.runRsync({
...item,
options: {
...this.options,
...item.options,
},
})
.wait()
// await effects
// .runRsync({
// ...item,
// options: {
// ...this.options,
// ...item.options,
// },
// })
// .wait()
}
return
}
@@ -158,18 +158,18 @@ export class Backups<M extends SDKManifest> {
// },
// )
// }
await effects
.runRsync({
options: {
...this.options,
...item.options,
},
srcVolume: item.dstVolume,
dstVolume: item.srcVolume,
srcPath: item.dstPath,
dstPath: item.srcPath,
})
.wait()
// await effects
// .runRsync({
// options: {
// ...this.options,
// ...item.options,
// },
// srcVolume: item.dstVolume,
// dstVolume: item.srcVolume,
// srcPath: item.dstPath,
// dstPath: item.srcPath,
// })
// .wait()
}
return
}

View File

@@ -7,55 +7,39 @@ import { Utils, utils } from "../util/utils"
import { deepEqual } from "../util/deepEqual"
import { deepMerge } from "../util/deepMerge"
export type Update<QueryResults, RemoteConfig> = (options: {
remoteConfig: RemoteConfig
queryResults: QueryResults
}) => Promise<RemoteConfig>
export class DependencyConfig<
Store,
Input extends Record<string, any>,
RemoteConfig extends Record<string, any>,
> {
static defaultUpdate = async (options: {
queryResults: unknown
remoteConfig: unknown
}): Promise<unknown> => {
return deepMerge({}, options.remoteConfig, options.queryResults || {})
}
constructor(
readonly dependencyConfig: (options: {
effects: Effects
localConfig: Input
remoteConfig: RemoteConfig
utils: Utils<Store>
}) => Promise<void | DeepPartial<RemoteConfig>>,
readonly update: Update<
void | DeepPartial<RemoteConfig>,
RemoteConfig
> = DependencyConfig.defaultUpdate as any,
) {}
async check(
options: Parameters<DependencyConfigType["check"]>[0],
): ReturnType<DependencyConfigType["check"]> {
const origConfig = JSON.parse(JSON.stringify(options.localConfig))
const newOptions = {
...options,
utils: utils<Store>(options.effects),
async query(options: { effects: Effects; localConfig: unknown }) {
return this.dependencyConfig({
localConfig: options.localConfig as Input,
remoteConfig: options.remoteConfig as RemoteConfig,
}
if (
!deepEqual(
origConfig,
deepMerge(
{},
options.localConfig,
await this.dependencyConfig(newOptions),
),
)
)
throw new Error(`Check failed`)
}
async autoConfigure(
options: Parameters<DependencyConfigType["autoConfigure"]>[0],
): ReturnType<DependencyConfigType["autoConfigure"]> {
const newOptions = {
...options,
effects: options.effects,
utils: utils<Store>(options.effects),
localConfig: options.localConfig as Input,
remoteConfig: options.remoteConfig as any,
}
return deepMerge(
{},
options.remoteConfig,
await this.dependencyConfig(newOptions),
)
})
}
}

View File

@@ -1,4 +1,5 @@
import { Effects } from "../../types"
import { createUtils } from "../../util"
import { CheckResult } from "./CheckResult"
export function containsAddress(x: string, port: number) {
const readPorts = x
@@ -26,11 +27,15 @@ export async function checkPortListening(
timeout?: number
},
): Promise<CheckResult> {
const utils = createUtils(effects)
return Promise.race<CheckResult>([
Promise.resolve().then(async () => {
const hasAddress =
containsAddress(await effects.runCommand(`cat /proc/net/tcp`), port) ||
containsAddress(await effects.runCommand("cat /proc/net/udp"), port)
containsAddress(
await utils.runCommand(`cat /proc/net/tcp`, {}),
port,
) ||
containsAddress(await utils.runCommand("cat /proc/net/udp", {}), port)
if (hasAddress) {
return { status: "passing", message: options.successMessage }
}

View File

@@ -1,6 +1,7 @@
import { Effects } from "../../types"
import { CheckResult } from "./CheckResult"
import { timeoutPromise } from "./index"
import fetch from "node-fetch"
/**
* This is a helper function to check if a web url is reachable.
@@ -17,7 +18,7 @@ export const checkWebUrl = async (
errorMessage = `Error while fetching URL: ${url}`,
} = {},
): Promise<CheckResult> => {
return Promise.race([effects.fetch(url), timeoutPromise(timeout)])
return Promise.race([fetch(url), timeoutPromise(timeout)])
.then((x) => ({
status: "passing" as const,
message: successMessage,

View File

@@ -1,4 +1,5 @@
import { CommandType, Effects } from "../../types"
import { createUtils } from "../../util"
import { CheckResult } from "./CheckResult"
import { timeoutPromise } from "./index"
@@ -9,9 +10,9 @@ import { timeoutPromise } from "./index"
* @param param0
* @returns
*/
export const runHealthScript = async <A extends string>(
export const runHealthScript = async (
effects: Effects,
runCommand: CommandType<A>,
runCommand: string,
{
timeout = 30000,
errorMessage = `Error while running command: ${runCommand}`,
@@ -19,8 +20,9 @@ export const runHealthScript = async <A extends string>(
`Have ran script ${runCommand} and the result: ${res}`,
} = {},
): Promise<CheckResult> => {
const utils = createUtils(effects)
const res = await Promise.race([
effects.runCommand(runCommand, { timeoutMillis: timeout }),
utils.runCommand(runCommand, { timeout }),
timeoutPromise(timeout),
]).catch((e) => {
console.warn(errorMessage)

View File

@@ -4,9 +4,10 @@ import { Trigger } from "../trigger"
import { TriggerInput } from "../trigger/TriggerInput"
import { defaultTrigger } from "../trigger/defaultTrigger"
import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types"
import { createUtils } from "../util"
type Daemon<Ids extends string, Command extends string, Id extends string> = {
id: "" extends Id ? never : Id
command: ValidIfNoStupidEscape<Command> | [string, ...string[]]
command: string
env?: Record<string, string>
ready: {
display: string | null
@@ -95,8 +96,9 @@ export class Daemons<Ids extends string> {
)
daemonsStarted[daemon.id] = requiredPromise.then(async () => {
const { command } = daemon
const utils = createUtils(effects)
const child = effects.runDaemon(command, { env: daemon.env })
const child = utils.runDaemon(command, { env: daemon.env })
let currentInput: TriggerInput = {}
const getCurrentInput = () => currentInput
const trigger = (daemon.ready.trigger ?? defaultTrigger)(

View File

@@ -97,16 +97,11 @@ export type VersionString = string
* this is used to make sure that other dependencies have the values that this service could use.
*/
export type DependencyConfig = {
/** Checks are called to make sure that our dependency is in the correct shape. If a known error is returned we know that the dependency needs modification */
check(options: {
effects: Effects
localConfig: unknown
remoteConfig: unknown
}): Promise<void>
/** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */
autoConfigure(options: {
effects: Effects
localConfig: unknown
/** During autoconfigure, we have access to effects and local data. We are going to figure out all the data that we need and send it to update. For the sdk it is the desired delta */
query(options: { effects: Effects; localConfig: unknown }): Promise<unknown>
/** This is the second part. Given the query results off the previous function, we will determine what to change the remote config to. In our sdk normall we are going to use the previous as a deep merge. */
update(options: {
queryResults: unknown
remoteConfig: unknown
}): Promise<unknown>
}

View File

@@ -6,6 +6,7 @@ import {
checkWebUrl,
} from "../health/checkFns"
import {
DaemonReturned,
Effects,
EnsureStorePath,
ExtractStore,
@@ -34,6 +35,8 @@ import {
getNetworkInterfaces,
} from "./getNetworkInterfaces"
import { exec } from "node:child_process"
export type Utils<Store, WrapperOverWrite = { const: never }> = {
checkPortListening(
port: number,
@@ -91,6 +94,14 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
}
nullIfEmpty: typeof nullIfEmpty
readFile: <A>(fileHelper: FileHelper<A>) => ReturnType<FileHelper<A>["read"]>
runDaemon: (
command: string,
options: { env?: Record<string, string> },
) => Promise<DaemonReturned>
runCommand: (
command: string,
options: { env?: Record<string, string>; timeout?: number },
) => Promise<string>
store: {
get: <Path extends string>(
packageId: string,
@@ -111,7 +122,8 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
}
export const utils = <Store = never, WrapperOverWrite = { const: never }>(
effects: Effects,
): Utils<Store, WrapperOverWrite> => ({
): Utils<Store, WrapperOverWrite> => {
return {
createInterface: (options: {
name: string
id: string
@@ -165,6 +177,46 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
value: ExtractStore<Store, Path>,
) => effects.store.set<Store, Path>({ value, path: path as any }),
},
runDaemon: async (
command: string,
options: { env?: Record<string, string> },
): Promise<DaemonReturned> => {
let childProcess: null | {
kill(signal?: number | string): void
} = null
const answer = new Promise<string>(
(resolve, reject) =>
(childProcess = exec(command, options, (error, stdout, stderr) => {
if (error) return reject(error.toString())
if (stderr) return reject(stderr.toString())
return resolve(stdout.toString())
})),
)
return {
wait() {
return answer
},
async term() {
childProcess?.kill()
},
}
},
runCommand: async (
command: string,
options: { env?: Record<string, string>; timeout?: number },
): Promise<string> => {
const answer = new Promise<string>((resolve, reject) =>
exec(command, options, (error, stdout, stderr) => {
if (error) return reject(error.toString())
if (stderr) return reject(stderr.toString())
return resolve(stdout.toString())
}),
)
return answer
},
checkPortListening: checkPortListening.bind(null, effects),
checkWebUrl: checkWebUrl.bind(null, effects),
@@ -177,5 +229,6 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
>(
value: In,
) => mountDependencies(effects, value),
})
}
}
function noop(): void {}

85
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "MIT",
"dependencies": {
"@iarna/toml": "^2.2.5",
"node-fetch": "^3.3.1",
"ts-matches": "^5.4.1",
"yaml": "^2.2.2"
},
@@ -2205,6 +2206,14 @@
"node": ">= 8"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -2471,6 +2480,28 @@
"bser": "2.1.1"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -2496,6 +2527,17 @@
"node": ">=8"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3719,6 +3761,41 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz",
"integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -5107,6 +5184,14 @@
"makeerror": "1.0.12"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"engines": {
"node": ">= 8"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@@ -21,6 +21,7 @@
"homepage": "https://github.com/Start9Labs/start-sdk#readme",
"dependencies": {
"@iarna/toml": "^2.2.5",
"node-fetch": "^3.3.1",
"ts-matches": "^5.4.1",
"yaml": "^2.2.2"
},