mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
* add support for inbound proxies * backend changes * fix file type * proxy -> tunnel, implement backend apis * wip start-tunneld * add domains and gateways, remove routers, fix docs links * dont show hidden actions * show and test dns * edit instead of chnage acme and change gateway * refactor: domains page * refactor: gateways page * domains and acme refactor * certificate authorities * refactor public/private gateways * fix fe types * domains mostly finished * refactor: add file control to form service * add ip util to sdk * domains api + migration * start service interface page, WIP * different options for clearnet domains * refactor: styles for interfaces page * minor * better placeholder for no addresses * start sorting addresses * best address logic * comments * fix unnecessary export * MVP of service interface page * domains preferred * fix: address comments * only translations left * wip: start-tunnel & fix build * forms for adding domain, rework things based on new ideas * fix: dns testing * public domain, max width, descriptions for dns * nix StartOS domains, implement public and private domains at interface scope * restart tor instead of reset * better icon for restart tor * dns * fix sort functions for public and private domains * with todos * update types * clean up tech debt, bump dependencies * revert to ts-rs v9 * fix all types * fix dns form * add missing translations * it builds * fix: comments (#3009) * fix: comments * undo default --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * fix: refactor legacy components (#3010) * fix: comments * fix: refactor legacy components * remove default again --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * more translations * wip * fix deadlock * coukd work * simple renaming * placeholder for empty service interfaces table * honor hidden form values * remove logs * reason instead of description * fix dns * misc fixes * implement toggling gateways for service interface * fix showing dns records * move status column in service list * remove unnecessary truthy check * refactor: refactor forms components and remove legacy Taiga UI package (#3012) * handle wh file uploads * wip: debugging tor * socks5 proxy working * refactor: fix multiple comments (#3013) * refactor: fix multiple comments * styling changes, add documentation to sidebar * translations for dns page * refactor: subtle colors * rearrange service page --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * fix file_stream and remove non-terminating test * clean up logs * support for sccache * fix gha sccache * more marketplace translations * install wizard clarity * stub hostnameInfo in migration * fix address info after setup, fix styling on SI page, new 040 release notes * remove tor logs from os * misc fixes * reset tor still not functioning... * update ts * minor styling and wording * chore: some fixes (#3015) * fix gateway renames * different handling for public domains * styling fixes * whole navbar should not be clickable on service show page * timeout getState request * remove links from changelog * misc fixes from pairing * use custom name for gateway in more places * fix dns parsing * closes #3003 * closes #2999 * chore: some fixes (#3017) * small copy change * revert hardcoded error for testing * dont require port forward if gateway is public * use old wan ip when not available * fix .const hanging on undefined * fix test * fix doc test * fix renames * update deps * allow specifying dependency metadata directly * temporarily make dependencies not cliackable in marketplace listings * fix socks bind * fix test --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: waterplea <alexander@inkin.ru>
233 lines
7.7 KiB
TypeScript
233 lines
7.7 KiB
TypeScript
import { ExtendedVersion, VersionRange } from "../exver"
|
|
import {
|
|
PackageId,
|
|
HealthCheckId,
|
|
DependencyRequirement,
|
|
CheckDependenciesResult,
|
|
} from "../types"
|
|
import { Effects } from "../Effects"
|
|
|
|
export type CheckDependencies<DependencyId extends PackageId = PackageId> = {
|
|
infoFor: (packageId: DependencyId) => {
|
|
requirement: DependencyRequirement
|
|
result: CheckDependenciesResult
|
|
}
|
|
|
|
installedSatisfied: (packageId: DependencyId) => boolean
|
|
installedVersionSatisfied: (packageId: DependencyId) => boolean
|
|
runningSatisfied: (packageId: DependencyId) => boolean
|
|
tasksSatisfied: (packageId: DependencyId) => boolean
|
|
healthCheckSatisfied: (
|
|
packageId: DependencyId,
|
|
healthCheckId: HealthCheckId,
|
|
) => boolean
|
|
satisfied: () => boolean
|
|
|
|
throwIfInstalledNotSatisfied: (packageId: DependencyId) => null
|
|
throwIfInstalledVersionNotSatisfied: (packageId: DependencyId) => null
|
|
throwIfRunningNotSatisfied: (packageId: DependencyId) => null
|
|
throwIfTasksNotSatisfied: (packageId: DependencyId) => null
|
|
throwIfHealthNotSatisfied: (
|
|
packageId: DependencyId,
|
|
healthCheckId?: HealthCheckId,
|
|
) => null
|
|
throwIfNotSatisfied: (packageId?: DependencyId) => null
|
|
}
|
|
export async function checkDependencies<
|
|
DependencyId extends PackageId = PackageId,
|
|
>(
|
|
effects: Effects,
|
|
packageIds?: DependencyId[],
|
|
): Promise<CheckDependencies<DependencyId>> {
|
|
let [dependencies, results] = await Promise.all([
|
|
effects.getDependencies(),
|
|
effects.checkDependencies({
|
|
packageIds,
|
|
}),
|
|
])
|
|
if (packageIds) {
|
|
dependencies = dependencies.filter((d) =>
|
|
(packageIds as PackageId[]).includes(d.id),
|
|
)
|
|
}
|
|
|
|
const infoFor = (packageId: DependencyId) => {
|
|
const dependencyRequirement = dependencies.find((d) => d.id === packageId)
|
|
const dependencyResult = results.find((d) => d.packageId === packageId)
|
|
if (!dependencyRequirement || !dependencyResult) {
|
|
throw new Error(`Unknown DependencyId ${packageId}`)
|
|
}
|
|
return { requirement: dependencyRequirement, result: dependencyResult }
|
|
}
|
|
|
|
const installedSatisfied = (packageId: DependencyId) =>
|
|
!!infoFor(packageId).result.installedVersion
|
|
const installedVersionSatisfied = (packageId: DependencyId) => {
|
|
const dep = infoFor(packageId)
|
|
return (
|
|
!!dep.result.installedVersion &&
|
|
ExtendedVersion.parse(dep.result.installedVersion).satisfies(
|
|
VersionRange.parse(dep.requirement.versionRange),
|
|
)
|
|
)
|
|
}
|
|
const runningSatisfied = (packageId: DependencyId) => {
|
|
const dep = infoFor(packageId)
|
|
return dep.requirement.kind !== "running" || dep.result.isRunning
|
|
}
|
|
const tasksSatisfied = (packageId: DependencyId) =>
|
|
Object.entries(infoFor(packageId).result.tasks).filter(
|
|
([_, t]) => t?.active && t.task.severity === "critical",
|
|
).length === 0
|
|
const healthCheckSatisfied = (
|
|
packageId: DependencyId,
|
|
healthCheckId?: HealthCheckId,
|
|
) => {
|
|
const dep = infoFor(packageId)
|
|
if (
|
|
healthCheckId &&
|
|
(dep.requirement.kind !== "running" ||
|
|
!dep.requirement.healthChecks.includes(healthCheckId))
|
|
) {
|
|
throw new Error(`Unknown HealthCheckId ${healthCheckId}`)
|
|
}
|
|
const errors =
|
|
dep.requirement.kind === "running"
|
|
? dep.requirement.healthChecks
|
|
.map((id) => [id, dep.result.healthChecks[id] ?? null] as const)
|
|
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
|
|
.filter(([_, res]) => res?.result !== "success")
|
|
: []
|
|
return errors.length === 0
|
|
}
|
|
const pkgSatisfied = (packageId: DependencyId) =>
|
|
installedSatisfied(packageId) &&
|
|
installedVersionSatisfied(packageId) &&
|
|
runningSatisfied(packageId) &&
|
|
tasksSatisfied(packageId) &&
|
|
healthCheckSatisfied(packageId)
|
|
const satisfied = (packageId?: DependencyId) =>
|
|
packageId
|
|
? pkgSatisfied(packageId)
|
|
: dependencies.every((d) => pkgSatisfied(d.id as DependencyId))
|
|
|
|
const throwIfInstalledNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = infoFor(packageId)
|
|
if (!dep.result.installedVersion) {
|
|
throw new Error(`${dep.result.title || packageId} is not installed`)
|
|
}
|
|
return null
|
|
}
|
|
const throwIfInstalledVersionNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = infoFor(packageId)
|
|
if (!dep.result.installedVersion) {
|
|
throw new Error(`${dep.result.title || packageId} is not installed`)
|
|
}
|
|
if (
|
|
![dep.result.installedVersion, ...dep.result.satisfies].find((v) =>
|
|
ExtendedVersion.parse(v).satisfies(
|
|
VersionRange.parse(dep.requirement.versionRange),
|
|
),
|
|
)
|
|
) {
|
|
throw new Error(
|
|
`Installed version ${dep.result.installedVersion} of ${dep.result.title || packageId} does not match expected version range ${dep.requirement.versionRange}`,
|
|
)
|
|
}
|
|
return null
|
|
}
|
|
const throwIfRunningNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = infoFor(packageId)
|
|
if (dep.requirement.kind === "running" && !dep.result.isRunning) {
|
|
throw new Error(`${dep.result.title || packageId} is not running`)
|
|
}
|
|
return null
|
|
}
|
|
const throwIfTasksNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = infoFor(packageId)
|
|
const reqs = Object.entries(dep.result.tasks)
|
|
.filter(([_, t]) => t?.active && t.task.severity === "critical")
|
|
.map(([id, _]) => id)
|
|
if (reqs.length) {
|
|
throw new Error(
|
|
`The following action requests have not been fulfilled: ${reqs.join(", ")}`,
|
|
)
|
|
}
|
|
return null
|
|
}
|
|
const throwIfHealthNotSatisfied = (
|
|
packageId: DependencyId,
|
|
healthCheckId?: HealthCheckId,
|
|
) => {
|
|
const dep = infoFor(packageId)
|
|
if (
|
|
healthCheckId &&
|
|
(dep.requirement.kind !== "running" ||
|
|
!dep.requirement.healthChecks.includes(healthCheckId))
|
|
) {
|
|
throw new Error(`Unknown HealthCheckId ${healthCheckId}`)
|
|
}
|
|
const errors =
|
|
dep.requirement.kind === "running"
|
|
? dep.requirement.healthChecks
|
|
.map((id) => [id, dep.result.healthChecks[id] ?? null] as const)
|
|
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
|
|
.filter(([_, res]) => res?.result !== "success")
|
|
: []
|
|
if (errors.length) {
|
|
throw new Error(
|
|
errors
|
|
.map(([id, e]) =>
|
|
e
|
|
? `Health Check ${e.name} of ${dep.result.title || packageId} failed with status ${e.result}${e.message ? `: ${e.message}` : ""}`
|
|
: `Health Check ${id} of ${dep.result.title} does not exist`,
|
|
)
|
|
.join("; "),
|
|
)
|
|
}
|
|
return null
|
|
}
|
|
const throwIfPkgNotSatisfied = (packageId: DependencyId) => {
|
|
throwIfInstalledNotSatisfied(packageId)
|
|
throwIfInstalledVersionNotSatisfied(packageId)
|
|
throwIfRunningNotSatisfied(packageId)
|
|
throwIfTasksNotSatisfied(packageId)
|
|
throwIfHealthNotSatisfied(packageId)
|
|
return null
|
|
}
|
|
const throwIfNotSatisfied = (packageId?: DependencyId) =>
|
|
packageId
|
|
? throwIfPkgNotSatisfied(packageId)
|
|
: (() => {
|
|
const err = dependencies.flatMap((d) => {
|
|
try {
|
|
throwIfPkgNotSatisfied(d.id as DependencyId)
|
|
} catch (e) {
|
|
if (e instanceof Error) return [e.message]
|
|
throw e
|
|
}
|
|
return []
|
|
})
|
|
if (err.length) {
|
|
throw new Error(err.join("; "))
|
|
}
|
|
return null
|
|
})()
|
|
|
|
return {
|
|
infoFor,
|
|
installedSatisfied,
|
|
installedVersionSatisfied,
|
|
runningSatisfied,
|
|
tasksSatisfied,
|
|
healthCheckSatisfied,
|
|
satisfied,
|
|
throwIfInstalledNotSatisfied,
|
|
throwIfInstalledVersionNotSatisfied,
|
|
throwIfRunningNotSatisfied,
|
|
throwIfTasksNotSatisfied,
|
|
throwIfHealthNotSatisfied,
|
|
throwIfNotSatisfied,
|
|
}
|
|
}
|