Bugfix/ssl proxy to ssl (#2956)

* fix registry rm command

* fix bind with addSsl on ssl proto

* fix bind with addSsl on ssl proto

* Add pre-release version migrations

* fix os build

* add mime to package deps

* update lockfile

* more ssl fixes

* add waitFor

* improve restart lockup

* beta.26

* fix dependency health check logic

* handle missing health check

* fix port forwards

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
Dominion5254
2025-06-04 19:41:21 -06:00
committed by GitHub
parent 02413a4fac
commit ab6ca8e16a
40 changed files with 1240 additions and 816 deletions

View File

@@ -81,9 +81,13 @@ export async function checkDependencies<
) {
throw new Error(`Unknown HealthCheckId ${healthCheckId}`)
}
const errors = Object.entries(dep.result.healthChecks)
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
.filter(([_, res]) => res.result !== "success")
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) =>
@@ -153,15 +157,20 @@ export async function checkDependencies<
) {
throw new Error(`Unknown HealthCheckId ${healthCheckId}`)
}
const errors = Object.entries(dep.result.healthChecks)
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
.filter(([_, res]) => res.result !== "success")
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(
([_, e]) =>
`Health Check ${e.name} of ${dep.result.title || packageId} failed with status ${e.result}${e.message ? `: ${e.message}` : ""}`,
.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("; "),
)

View File

@@ -134,19 +134,26 @@ export class MultiHost {
const preferredExternalPort =
options.preferredExternalPort ||
knownProtocols[options.protocol].defaultPort
const sslProto = this.getSslProto(options, protoInfo)
const addSsl =
sslProto && "alpn" in protoInfo
const sslProto = this.getSslProto(options)
const addSsl = sslProto
? {
// addXForwardedHeaders: null,
preferredExternalPort: knownProtocols[sslProto].defaultPort,
scheme: sslProto,
alpn: "alpn" in protoInfo ? protoInfo.alpn : null,
...("addSsl" in options ? options.addSsl : null),
}
: options.addSsl
? {
// addXForwardedHeaders: null,
preferredExternalPort: knownProtocols[sslProto].defaultPort,
preferredExternalPort: 443,
scheme: sslProto,
alpn: protoInfo.alpn,
alpn: null,
...("addSsl" in options ? options.addSsl : null),
}
: null
const secure: Security | null = !protoInfo.secure ? null : { ssl: false }
const secure: Security | null = protoInfo.secure ?? null
await this.options.effects.bind({
id: this.options.id,
@@ -159,12 +166,12 @@ export class MultiHost {
return new Origin(this, internalPort, options.protocol, sslProto)
}
private getSslProto(
options: BindOptionsByKnownProtocol,
protoInfo: KnownProtocols[keyof KnownProtocols],
) {
private getSslProto(options: BindOptionsByKnownProtocol) {
const proto = options.protocol
const protoInfo = knownProtocols[proto]
if (inObject("noAddSsl", options) && options.noAddSsl) return null
if ("withSsl" in protoInfo && protoInfo.withSsl) return protoInfo.withSsl
if (protoInfo.secure?.ssl) return proto
return null
}
}

View File

@@ -71,4 +71,30 @@ export class GetSystemSmtp {
),
)
}
/**
* Watches the system SMTP credentials. Returns when the predicate is true
*/
async waitFor(pred: (value: T.SmtpValue | null) => boolean) {
const resolveCell = { resolve: () => {} }
this.effects.onLeaveContext(() => {
resolveCell.resolve()
})
while (this.effects.isInContext) {
let callback: () => void = () => {}
const waitForNext = new Promise<void>((resolve) => {
callback = resolve
resolveCell.resolve = resolve
})
const res = await this.effects.getSystemSmtp({
callback: () => callback(),
})
if (pred(res)) {
resolveCell.resolve()
return res
}
await waitForNext
}
return null
}
}

View File

@@ -366,6 +366,36 @@ export class GetServiceInterface {
),
)
}
/**
* Watches the requested service interface. Returns when the predicate is true
*/
async waitFor(pred: (value: ServiceInterfaceFilled | null) => boolean) {
const { id, packageId } = this.opts
const resolveCell = { resolve: () => {} }
this.effects.onLeaveContext(() => {
resolveCell.resolve()
})
while (this.effects.isInContext) {
let callback: () => void = () => {}
const waitForNext = new Promise<void>((resolve) => {
callback = resolve
resolveCell.resolve = resolve
})
const res = await makeInterfaceFilled({
effects: this.effects,
id,
packageId,
callback,
})
if (pred(res)) {
resolveCell.resolve()
return res
}
await waitForNext
}
return null
}
}
export function getServiceInterface(
effects: Effects,

View File

@@ -130,6 +130,35 @@ export class GetServiceInterfaces {
),
)
}
/**
* Watches the service interfaces for the package. Returns when the predicate is true
*/
async waitFor(pred: (value: ServiceInterfaceFilled[] | null) => boolean) {
const { packageId } = this.opts
const resolveCell = { resolve: () => {} }
this.effects.onLeaveContext(() => {
resolveCell.resolve()
})
while (this.effects.isInContext) {
let callback: () => void = () => {}
const waitForNext = new Promise<void>((resolve) => {
callback = resolve
resolveCell.resolve = resolve
})
const res = await makeManyInterfaceFilled({
effects: this.effects,
packageId,
callback,
})
if (pred(res)) {
resolveCell.resolve()
return res
}
await waitForNext
}
return null
}
}
export function getServiceInterfaces(
effects: Effects,