mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 02:11:56 +00:00
chore: Adding mainFn helpers
This commit is contained in:
4
lib/health/HealthReceipt.ts
Normal file
4
lib/health/HealthReceipt.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
declare const HealthProof: unique symbol;
|
||||
export type HealthReceipt = {
|
||||
[HealthProof]: never;
|
||||
};
|
||||
4
lib/health/ReadyProof.ts
Normal file
4
lib/health/ReadyProof.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export declare const ReadyProof: unique symbol;
|
||||
export type ReadyReceipt = {
|
||||
[ReadyProof]: never;
|
||||
};
|
||||
6
lib/health/checkFns/CheckResult.ts
Normal file
6
lib/health/checkFns/CheckResult.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { HealthStatus } from "../../types";
|
||||
|
||||
export type CheckResult = {
|
||||
status: HealthStatus;
|
||||
message?: string;
|
||||
};
|
||||
26
lib/health/checkFns/checkPortListening.ts
Normal file
26
lib/health/checkFns/checkPortListening.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Effects } from "../../types";
|
||||
export function containsAddress(x: string, port: number) {
|
||||
const readPorts = x
|
||||
.split("\n")
|
||||
.filter(Boolean)
|
||||
.splice(1)
|
||||
.map((x) => x.split(" ").filter(Boolean)[1]?.split(":")?.[1])
|
||||
.filter(Boolean)
|
||||
.map((x) => Number.parseInt(x, 16))
|
||||
.filter(Number.isFinite);
|
||||
return readPorts.indexOf(port) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to check if a port is listening on the system.
|
||||
* Used during the health check fn or the check main fn.
|
||||
*/
|
||||
export async function checkPortListening(effects: Effects, port: number) {
|
||||
const hasAddress =
|
||||
containsAddress(await effects.shell("cat /proc/net/tcp"), port) ||
|
||||
containsAddress(await effects.shell("cat /proc/net/udp"), port);
|
||||
if (hasAddress) {
|
||||
return;
|
||||
}
|
||||
throw new Error(`Port ${port} is not listening`);
|
||||
}
|
||||
31
lib/health/checkFns/checkWebUrl.ts
Normal file
31
lib/health/checkFns/checkWebUrl.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Effects } from "../../types";
|
||||
import { CheckResult } from "./CheckResult";
|
||||
import { timeoutPromise } from "./index";
|
||||
|
||||
/**
|
||||
* This is a helper function to check if a web url is reachable.
|
||||
* @param url
|
||||
* @param createSuccess
|
||||
* @returns
|
||||
*/
|
||||
export const checkWebUrl = async (
|
||||
url: string,
|
||||
effects: Effects,
|
||||
{
|
||||
timeout = 1000,
|
||||
successMessage = `Reached ${url}`,
|
||||
errorMessage = `Error while fetching URL: ${url}`,
|
||||
} = {}
|
||||
): Promise<CheckResult> => {
|
||||
return Promise.race([effects.fetch(url), timeoutPromise(timeout)])
|
||||
.then((x) => ({
|
||||
status: "passing" as const,
|
||||
message: successMessage,
|
||||
}))
|
||||
.catch((e) => {
|
||||
effects.warn(`Error while fetching URL: ${url}`);
|
||||
effects.error(JSON.stringify(e));
|
||||
effects.error(e.toString());
|
||||
return { status: "failing" as const, message: errorMessage };
|
||||
});
|
||||
};
|
||||
11
lib/health/checkFns/index.ts
Normal file
11
lib/health/checkFns/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { runHealthScript } from "./runHealthScript";
|
||||
export { checkPortListening } from "./checkPortListening";
|
||||
export { CheckResult } from "./CheckResult";
|
||||
export { checkWebUrl } from "./checkWebUrl";
|
||||
|
||||
export function timeoutPromise(ms: number, { message = "Timed out" } = {}) {
|
||||
return new Promise<never>((resolve, reject) =>
|
||||
setTimeout(() => reject(new Error(message)), ms)
|
||||
);
|
||||
}
|
||||
export { runHealthScript };
|
||||
35
lib/health/checkFns/runHealthScript.ts
Normal file
35
lib/health/checkFns/runHealthScript.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Effects } from "../../types";
|
||||
import { CheckResult } from "./CheckResult";
|
||||
import { timeoutPromise } from "./index";
|
||||
|
||||
/**
|
||||
* Running a health script, is used when we want to have a simple
|
||||
* script in bash or something like that. It should return something that is useful
|
||||
* in {result: string} else it is considered an error
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
export const runHealthScript = async (
|
||||
effects: Effects,
|
||||
runCommand: Parameters<Effects["runCommand"]>[0],
|
||||
{
|
||||
timeout = 30000,
|
||||
errorMessage = `Error while running command: ${runCommand}`,
|
||||
message = (res: string) =>
|
||||
`Have ran script ${runCommand} and the result: ${res}`,
|
||||
}
|
||||
): Promise<CheckResult> => {
|
||||
const res = await Promise.race([
|
||||
effects.runCommand(runCommand),
|
||||
timeoutPromise(timeout),
|
||||
]).catch((e) => {
|
||||
effects.warn(errorMessage);
|
||||
effects.warn(JSON.stringify(e));
|
||||
effects.warn(e.toString());
|
||||
throw { status: "failing", message: errorMessage } as CheckResult;
|
||||
});
|
||||
return {
|
||||
status: "passing",
|
||||
message: message(res),
|
||||
} as CheckResult;
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
import { Types } from "..";
|
||||
import { object, string } from "ts-matches";
|
||||
|
||||
export type HealthCheck = (
|
||||
effects: Types.Effects,
|
||||
dateMs: number
|
||||
) => Promise<HealthResult>;
|
||||
export type HealthResult =
|
||||
| { success: string }
|
||||
| { failure: string }
|
||||
| { disabled: null }
|
||||
| { starting: null }
|
||||
| { loading: string };
|
||||
const hasMessage = object({ message: string }).test;
|
||||
function safelyStringify(e: unknown) {
|
||||
if (hasMessage(e)) return e.message;
|
||||
if (string.test(e)) return e;
|
||||
try {
|
||||
return JSON.stringify(e);
|
||||
} catch (e) {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
async function timeoutHealth(
|
||||
effects: Types.Effects,
|
||||
timeMs: number
|
||||
): Promise<HealthResult> {
|
||||
await effects.sleep(timeMs);
|
||||
return { failure: "Timed out " };
|
||||
}
|
||||
|
||||
/**
|
||||
* Health runner is usually used during the main function, and will be running in a loop.
|
||||
* This health check then will be run every intervalS seconds.
|
||||
* The return needs a create()
|
||||
* then from there we need a start().
|
||||
* The stop function is used to stop the health check.
|
||||
*/
|
||||
|
||||
export default function healthRunner(name: string, fn: HealthCheck) {
|
||||
return {
|
||||
/**
|
||||
* All values in seconds. Defaults):
|
||||
*
|
||||
* interval: 60s
|
||||
*
|
||||
* timeout: 10s
|
||||
*
|
||||
* delay: 10s
|
||||
*/
|
||||
create(
|
||||
effects: Types.Effects,
|
||||
options = { interval: 60, timeout: 10, delay: 10 }
|
||||
) {
|
||||
let running: any;
|
||||
function startFn() {
|
||||
clearInterval(running);
|
||||
setTimeout(() => {
|
||||
running = setInterval(async () => {
|
||||
const result = await Promise.race([
|
||||
timeoutHealth(effects, options.timeout * 1000),
|
||||
fn(effects, 123),
|
||||
]).catch((e) => {
|
||||
return { failure: safelyStringify(e) };
|
||||
});
|
||||
(effects as any).setHealthStatus({
|
||||
name,
|
||||
result,
|
||||
});
|
||||
}, options.interval * 1000);
|
||||
}, options.delay * 1000);
|
||||
}
|
||||
|
||||
const self = {
|
||||
stop() {
|
||||
clearInterval(running);
|
||||
return self;
|
||||
},
|
||||
start() {
|
||||
startFn();
|
||||
return self;
|
||||
},
|
||||
};
|
||||
return self;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,2 +1,7 @@
|
||||
export * from "./util";
|
||||
export { default as healthRunner } from "./healthRunner";
|
||||
export * as checkFns from "./checkFns";
|
||||
export * as trigger from "./trigger";
|
||||
|
||||
export { TriggerInput } from "./trigger/TriggerInput";
|
||||
export { HealthReceipt } from "./HealthReceipt";
|
||||
export { readyCheck } from "./readyCheck";
|
||||
export { ReadyProof } from "./ReadyProof";
|
||||
|
||||
55
lib/health/readyCheck.ts
Normal file
55
lib/health/readyCheck.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { InterfaceReceipt } from "../mainFn/interfaceReceipt";
|
||||
import { Daemon, Effects } from "../types";
|
||||
import { CheckResult } from "./checkFns/CheckResult";
|
||||
import { ReadyReceipt } from "./ReadyProof";
|
||||
import { HealthReceipt } from "./HealthReceipt";
|
||||
import { Trigger } from "./trigger";
|
||||
import { TriggerInput } from "./trigger/TriggerInput";
|
||||
import { defaultTrigger } from "./trigger/defaultTrigger";
|
||||
|
||||
function readReciptOf<A extends { daemon: Daemon }>(a: A) {
|
||||
return a as A & ReadyReceipt;
|
||||
}
|
||||
export function readyCheck(o: {
|
||||
effects: Effects;
|
||||
started(onTerm: () => void): null;
|
||||
interfaceReceipt: InterfaceReceipt;
|
||||
healthReceipts: Iterable<HealthReceipt>;
|
||||
daemonReceipt: Daemon;
|
||||
name: string;
|
||||
trigger?: Trigger;
|
||||
fn(): Promise<CheckResult> | CheckResult;
|
||||
onFirstSuccess?: () => () => Promise<unknown> | unknown;
|
||||
}) {
|
||||
new Promise(async () => {
|
||||
const trigger = (o.trigger ?? defaultTrigger)();
|
||||
let currentValue: TriggerInput = {
|
||||
lastResult: null,
|
||||
hadSuccess: false,
|
||||
};
|
||||
for (
|
||||
let res = await trigger.next(currentValue);
|
||||
!res.done;
|
||||
res = await trigger.next(currentValue)
|
||||
) {
|
||||
try {
|
||||
const { status, message } = await o.fn();
|
||||
if (!currentValue.hadSuccess) {
|
||||
await o.started(o?.onFirstSuccess ?? (() => o.daemonReceipt.term()));
|
||||
}
|
||||
await o.effects.setHealth({
|
||||
name: o.name,
|
||||
status,
|
||||
message,
|
||||
});
|
||||
currentValue.hadSuccess = true;
|
||||
currentValue.lastResult = "success";
|
||||
} catch (_) {
|
||||
currentValue.lastResult = "failure";
|
||||
}
|
||||
}
|
||||
});
|
||||
return readReciptOf({
|
||||
daemon: o.daemonReceipt,
|
||||
});
|
||||
}
|
||||
4
lib/health/trigger/TriggerInput.ts
Normal file
4
lib/health/trigger/TriggerInput.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type TriggerInput = {
|
||||
lastResult: "success" | "failure" | null;
|
||||
hadSuccess: boolean;
|
||||
};
|
||||
28
lib/health/trigger/changeOnFirstSuccess.ts
Normal file
28
lib/health/trigger/changeOnFirstSuccess.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { TriggerInput } from "./TriggerInput";
|
||||
import { Trigger } from "./index";
|
||||
|
||||
export function changeOnFirstSuccess(o: {
|
||||
beforeFirstSuccess: Trigger;
|
||||
afterFirstSuccess: Trigger;
|
||||
}) {
|
||||
return async function* () {
|
||||
const beforeFirstSuccess = o.beforeFirstSuccess();
|
||||
let currentValue: TriggerInput = yield;
|
||||
beforeFirstSuccess.next(currentValue);
|
||||
for (
|
||||
let res = await beforeFirstSuccess.next(currentValue);
|
||||
currentValue?.lastResult !== "success" && !res.done;
|
||||
res = await beforeFirstSuccess.next(currentValue)
|
||||
) {
|
||||
currentValue = yield;
|
||||
}
|
||||
const afterFirstSuccess = o.afterFirstSuccess();
|
||||
for (
|
||||
let res = await afterFirstSuccess.next(currentValue);
|
||||
!res.done;
|
||||
res = await afterFirstSuccess.next(currentValue)
|
||||
) {
|
||||
currentValue = yield;
|
||||
}
|
||||
};
|
||||
}
|
||||
8
lib/health/trigger/cooldownTrigger.ts
Normal file
8
lib/health/trigger/cooldownTrigger.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export function cooldownTrigger(timeMs: number) {
|
||||
return async function* () {
|
||||
while (true) {
|
||||
await new Promise((resolve) => setTimeout(resolve, timeMs));
|
||||
yield;
|
||||
}
|
||||
};
|
||||
}
|
||||
7
lib/health/trigger/defaultTrigger.ts
Normal file
7
lib/health/trigger/defaultTrigger.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { cooldownTrigger } from "./cooldownTrigger";
|
||||
import { changeOnFirstSuccess } from "./changeOnFirstSuccess";
|
||||
|
||||
export const defaultTrigger = changeOnFirstSuccess({
|
||||
beforeFirstSuccess: cooldownTrigger(0),
|
||||
afterFirstSuccess: cooldownTrigger(30000),
|
||||
});
|
||||
5
lib/health/trigger/index.ts
Normal file
5
lib/health/trigger/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { TriggerInput } from "./TriggerInput";
|
||||
export { changeOnFirstSuccess } from "./changeOnFirstSuccess";
|
||||
export { cooldownTrigger } from "./cooldownTrigger";
|
||||
|
||||
export type Trigger = () => AsyncIterator<unknown, unknown, TriggerInput>;
|
||||
@@ -1,77 +0,0 @@
|
||||
import { Effects } from "../types";
|
||||
import { isKnownError } from "../util";
|
||||
import { HealthResult } from "./healthRunner";
|
||||
|
||||
/**
|
||||
* This is a helper function to check if a web url is reachable.
|
||||
* @param url
|
||||
* @param createSuccess
|
||||
* @returns
|
||||
*/
|
||||
export const checkWebUrl: (
|
||||
url: string,
|
||||
createSuccess?: null | ((response?: string | null) => string)
|
||||
) => (effects: Effects, duration: number) => Promise<HealthResult> = (
|
||||
url,
|
||||
createSuccess = null
|
||||
) => {
|
||||
return async (effects, duration) => {
|
||||
const errorValue = guardDurationAboveMinimum({
|
||||
duration,
|
||||
minimumTime: 5000,
|
||||
});
|
||||
if (!!errorValue) {
|
||||
return errorValue;
|
||||
}
|
||||
|
||||
return await effects
|
||||
.fetch(url)
|
||||
.then((x) => ({
|
||||
success: createSuccess?.(x.body) ?? `Successfully fetched URL: ${url}`,
|
||||
}))
|
||||
.catch((e) => {
|
||||
effects.warn(`Error while fetching URL: ${url}`);
|
||||
effects.error(JSON.stringify(e));
|
||||
effects.error(e.toString());
|
||||
return { failure: `Error while fetching URL: ${url}` };
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Running a health script, is used when we want to have a simple
|
||||
* script in bash or something like that. It should return something that is useful
|
||||
* in {result: string} else it is considered an error
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
export const runHealthScript =
|
||||
({
|
||||
command,
|
||||
args,
|
||||
message,
|
||||
}: {
|
||||
command: string;
|
||||
args: string[];
|
||||
message: ((result: unknown) => string) | null;
|
||||
}) =>
|
||||
async (effects: Effects, _duration: number): Promise<HealthResult> => {
|
||||
const res = await effects.runCommand({ command, args });
|
||||
return {
|
||||
success:
|
||||
message?.(res) ?? `Have ran script ${command} and the result: ${res}`,
|
||||
};
|
||||
};
|
||||
|
||||
// Ensure the starting duration is pass a minimum
|
||||
export const guardDurationAboveMinimum = (input: {
|
||||
duration: number;
|
||||
minimumTime: number;
|
||||
}): null | HealthResult =>
|
||||
input.duration <= input.minimumTime ? { starting: null } : null;
|
||||
|
||||
export const catchError = (effects: Effects) => (e: unknown) => {
|
||||
if (isKnownError(e)) return e;
|
||||
effects.error(`Health check failed: ${e}`);
|
||||
return { failure: "Error while running health check" };
|
||||
};
|
||||
19
lib/index.ts
19
lib/index.ts
@@ -1,12 +1,13 @@
|
||||
export * as matches from "ts-matches";
|
||||
export * as TOML from "@iarna/toml";
|
||||
export * as YAML from "yaml";
|
||||
export * as Types from "./types";
|
||||
export * as T from "./types";
|
||||
export * as healthUtil from "./health/util";
|
||||
export * as util from "./util";
|
||||
export * as configBuilder from "./config/builder";
|
||||
export * as backup from "./backup";
|
||||
export * as configTypes from "./config/configTypes";
|
||||
export * as config from "./config";
|
||||
export * as configBuilder from "./config/builder";
|
||||
export * as configTypes from "./config/configTypes";
|
||||
export * as health from "./health";
|
||||
export * as healthUtil from "./health/checkFns";
|
||||
export * as mainFn from "./mainFn";
|
||||
export * as matches from "ts-matches";
|
||||
export * as T from "./types";
|
||||
export * as TOML from "@iarna/toml";
|
||||
export * as Types from "./types";
|
||||
export * as util from "./util";
|
||||
export * as YAML from "yaml";
|
||||
|
||||
4
lib/mainFn/AddressReceipt.ts
Normal file
4
lib/mainFn/AddressReceipt.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
declare const AddressProof: unique symbol;
|
||||
export type AddressReceipt = {
|
||||
[AddressProof]: never;
|
||||
};
|
||||
11
lib/mainFn/LocalBinding.ts
Normal file
11
lib/mainFn/LocalBinding.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Origin } from "./Origin";
|
||||
|
||||
export class LocalBinding {
|
||||
constructor(readonly localAddress: string, readonly ipAddress: string) {}
|
||||
createOrigins(protocol: string) {
|
||||
return {
|
||||
local: new Origin(protocol, this.localAddress),
|
||||
ip: new Origin(protocol, this.ipAddress),
|
||||
};
|
||||
}
|
||||
}
|
||||
13
lib/mainFn/LocalPort.ts
Normal file
13
lib/mainFn/LocalPort.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Effects } from "../types";
|
||||
import { LocalBinding } from "./LocalBinding";
|
||||
|
||||
export class LocalPort {
|
||||
constructor(readonly id: string, readonly effects: Effects) {}
|
||||
async bindLan(internalPort: number) {
|
||||
const [localAddress, ipAddress] = await this.effects.bindLan({
|
||||
internalPort,
|
||||
name: this.id,
|
||||
});
|
||||
return new LocalBinding(localAddress, ipAddress);
|
||||
}
|
||||
}
|
||||
17
lib/mainFn/NetworkBuilder.ts
Normal file
17
lib/mainFn/NetworkBuilder.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Effects } from "../types";
|
||||
import { LocalPort } from "./LocalPort";
|
||||
import { TorHostname } from "./TorHostname";
|
||||
|
||||
export class NetworkBuilder {
|
||||
static of(effects: Effects) {
|
||||
return new NetworkBuilder(effects);
|
||||
}
|
||||
private constructor(private effects: Effects) {}
|
||||
|
||||
getTorHostName(id: string) {
|
||||
return new TorHostname(id, this.effects);
|
||||
}
|
||||
getPort(id: string) {
|
||||
return new LocalPort(id, this.effects);
|
||||
}
|
||||
}
|
||||
38
lib/mainFn/NetworkInterfaceBuilder.ts
Normal file
38
lib/mainFn/NetworkInterfaceBuilder.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Effects } from "../types";
|
||||
import { AddressReceipt } from "./AddressReceipt";
|
||||
import { Origin } from "./Origin";
|
||||
|
||||
export class NetworkInterfaceBuilder {
|
||||
constructor(
|
||||
readonly options: {
|
||||
effects: Effects;
|
||||
name: string;
|
||||
id: string;
|
||||
description: string;
|
||||
ui: boolean;
|
||||
basic?: null | { password: string; username: string };
|
||||
path?: string;
|
||||
search?: Record<string, string>;
|
||||
}
|
||||
) {}
|
||||
|
||||
async exportAddresses(addresses: Iterable<Origin>) {
|
||||
const { name, description, id, ui, path, search } = this.options;
|
||||
// prettier-ignore
|
||||
const urlAuth = !!(this.options?.basic) ? `${this.options.basic.username}:${this.options.basic.password}@` :
|
||||
'';
|
||||
for (const origin of addresses) {
|
||||
const address = `${origin.protocol}://${urlAuth}${origin.address}`;
|
||||
await this.options.effects.exportAddress({
|
||||
name,
|
||||
description,
|
||||
address,
|
||||
id,
|
||||
ui,
|
||||
path,
|
||||
search,
|
||||
});
|
||||
}
|
||||
return {} as AddressReceipt;
|
||||
}
|
||||
}
|
||||
3
lib/mainFn/Origin.ts
Normal file
3
lib/mainFn/Origin.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export class Origin {
|
||||
constructor(readonly protocol: string, readonly address: string) {}
|
||||
}
|
||||
7
lib/mainFn/RunningMainRet.ts
Normal file
7
lib/mainFn/RunningMainRet.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Daemon } from "../types";
|
||||
import { ReadyProof } from "../health/ReadyProof";
|
||||
|
||||
export type RunningMainRet = {
|
||||
[ReadyProof]: never;
|
||||
daemon: Daemon;
|
||||
};
|
||||
8
lib/mainFn/TorBinding.ts
Normal file
8
lib/mainFn/TorBinding.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Origin } from "./Origin";
|
||||
|
||||
export class TorBinding {
|
||||
constructor(readonly address: string) {}
|
||||
createOrigin(protocol: string) {
|
||||
return new Origin(protocol, this.address);
|
||||
}
|
||||
}
|
||||
14
lib/mainFn/TorHostname.ts
Normal file
14
lib/mainFn/TorHostname.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Effects } from "../types";
|
||||
import { TorBinding } from "./TorBinding";
|
||||
|
||||
export class TorHostname {
|
||||
constructor(readonly id: string, readonly effects: Effects) {}
|
||||
async bindTor(internalPort: number, externalPort: number) {
|
||||
const address = await this.effects.bindTor({
|
||||
internalPort,
|
||||
name: this.id,
|
||||
externalPort,
|
||||
});
|
||||
return new TorBinding(address);
|
||||
}
|
||||
}
|
||||
8
lib/mainFn/exportInterfaces.ts
Normal file
8
lib/mainFn/exportInterfaces.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { AddressReceipt } from "./AddressReceipt";
|
||||
import { InterfaceReceipt } from "./interfaceReceipt";
|
||||
|
||||
export const exportInterfaces = (
|
||||
_firstProof: AddressReceipt,
|
||||
..._rest: AddressReceipt[]
|
||||
) => ({} as InterfaceReceipt);
|
||||
export default exportInterfaces;
|
||||
22
lib/mainFn/index.ts
Normal file
22
lib/mainFn/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { RunningMainRet } from "./RunningMainRet";
|
||||
import { Effects, ExpectedExports } from "../types";
|
||||
export * as network from "./exportInterfaces";
|
||||
export { LocalBinding } from "./LocalBinding";
|
||||
export { LocalPort } from "./LocalPort";
|
||||
export { NetworkBuilder } from "./NetworkBuilder";
|
||||
export { NetworkInterfaceBuilder } from "./NetworkInterfaceBuilder";
|
||||
export { Origin } from "./Origin";
|
||||
export { TorBinding } from "./TorBinding";
|
||||
export { TorHostname } from "./TorHostname";
|
||||
|
||||
export const runningMain: (
|
||||
fn: (o: {
|
||||
effects: Effects;
|
||||
started(onTerm: () => void): null;
|
||||
}) => Promise<RunningMainRet>
|
||||
) => ExpectedExports.main = (fn) => {
|
||||
return async (options) => {
|
||||
const { daemon } = await fn(options);
|
||||
daemon.wait();
|
||||
};
|
||||
};
|
||||
4
lib/mainFn/interfaceReceipt.ts
Normal file
4
lib/mainFn/interfaceReceipt.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
declare const InterfaceProof: unique symbol;
|
||||
export type InterfaceReceipt = {
|
||||
[InterfaceProof]: never;
|
||||
};
|
||||
17
lib/test/health.readyCheck.test.ts
Normal file
17
lib/test/health.readyCheck.test.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { containsAddress } from "../health/checkFns/checkPortListening";
|
||||
|
||||
describe("Health ready check", () => {
|
||||
it("Should be able to parse an example information", () => {
|
||||
let input = `
|
||||
|
||||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
0: 00000000:1F90 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 21634478 1 0000000000000000 100 0 0 10 0
|
||||
1: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 21634477 1 0000000000000000 100 0 0 10 0
|
||||
2: 0B00007F:9671 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 21635458 1 0000000000000000 100 0 0 10 0
|
||||
3: 00000000:0D73 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 21634479 1 0000000000000000 100 0 0 10 0
|
||||
`;
|
||||
|
||||
expect(containsAddress(input, 80)).toBe(true);
|
||||
expect(containsAddress(input, 1234)).toBe(false);
|
||||
});
|
||||
});
|
||||
43
lib/types.ts
43
lib/types.ts
@@ -85,6 +85,19 @@ export type ConfigRes = {
|
||||
/** Shape that is describing the form in the ui */
|
||||
spec: InputSpec;
|
||||
};
|
||||
|
||||
declare const DaemonProof: unique symbol;
|
||||
export type DaemonReceipt = {
|
||||
[DaemonProof]: never;
|
||||
};
|
||||
export type Daemon = {
|
||||
wait(): Promise<string>;
|
||||
term(): Promise<void>;
|
||||
[DaemonProof]: never;
|
||||
};
|
||||
|
||||
export type HealthStatus = "passing" | "warning" | "failing" | "disabled";
|
||||
|
||||
/** Used to reach out from the pure js runtime */
|
||||
export type Effects = {
|
||||
/** Usable when not sandboxed */
|
||||
@@ -125,6 +138,10 @@ export type Effects = {
|
||||
args?: string[];
|
||||
timeoutMillis?: number;
|
||||
}): Promise<string>;
|
||||
runShellDaemon(command: string): {
|
||||
wait(): Promise<string>;
|
||||
term(): Promise<void>;
|
||||
};
|
||||
runDaemon(input: { command: string; args?: string[] }): {
|
||||
wait(): Promise<string>;
|
||||
term(): Promise<void>;
|
||||
@@ -154,11 +171,7 @@ export type Effects = {
|
||||
/** Check that a file exists or not */
|
||||
exists(input: { volumeId: string; path: string }): Promise<boolean>;
|
||||
/** Declaring that we are opening a interface on some protocal for local network */
|
||||
bindLocal(options: {
|
||||
internalPort: number;
|
||||
name: string;
|
||||
externalPort: number;
|
||||
}): Promise<string>;
|
||||
bindLan(options: { internalPort: number; name: string }): Promise<string[]>;
|
||||
/** Declaring that we are opening a interface on some protocal for tor network */
|
||||
bindTor(options: {
|
||||
internalPort: number;
|
||||
@@ -244,6 +257,20 @@ export type Effects = {
|
||||
* ui interface
|
||||
*/
|
||||
ui?: boolean;
|
||||
|
||||
/**
|
||||
* The id is that a path will create a link in the ui that can go to specific pages, like
|
||||
* admin, or settings, or something like that.
|
||||
* Default = ''
|
||||
*/
|
||||
path?: string;
|
||||
|
||||
/**
|
||||
* This is the query params in the url, and is a map of key value pairs
|
||||
* Default = {}
|
||||
* if empty then will not be added to the url
|
||||
*/
|
||||
search?: Record<string, string>;
|
||||
}): Promise<string>;
|
||||
|
||||
/**
|
||||
@@ -291,6 +318,12 @@ export type Effects = {
|
||||
* @returns PEM encoded ssl key (ecdsa)
|
||||
*/
|
||||
getSslKey: (packageId: string, algorithm?: "ecdsa" | "ed25519") => string;
|
||||
|
||||
setHealth(o: {
|
||||
name: string;
|
||||
status: HealthStatus;
|
||||
message?: string;
|
||||
}): Promise<void>;
|
||||
};
|
||||
|
||||
/* rsync options: https://linux.die.net/man/1/rsync
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as T from "../types";
|
||||
export { guardAll, typeFromProps } from "./propertiesMatcher";
|
||||
export { default as nullIfEmpty } from "./nullIfEmpty";
|
||||
export { FileHelper } from "./fileHelper";
|
||||
export { sh } from "./shell";
|
||||
|
||||
/** Used to check if the file exists before hand */
|
||||
export const exists = (
|
||||
|
||||
8
lib/util/shell.ts
Normal file
8
lib/util/shell.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Effects } from "../types";
|
||||
|
||||
export function sh(shellCommand: string) {
|
||||
return {
|
||||
command: "sh",
|
||||
args: ["-c", shellCommand],
|
||||
} as Partial<Parameters<Effects["runCommand"]>[0]>;
|
||||
}
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "start-sdk",
|
||||
"version": "0.4.0-lib0.charlie2",
|
||||
"version": "0.4.0-lib0.charlie3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "start-sdk",
|
||||
"version": "0.4.0-lib0.charlie2",
|
||||
"version": "0.4.0-lib0.charlie3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "start-sdk",
|
||||
"version": "0.4.0-lib0.charlie2",
|
||||
"version": "0.4.0-lib0.charlie3",
|
||||
"description": "For making the patterns that are wanted in making services for the startOS.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
||||
Reference in New Issue
Block a user