various bug, improve smtp

This commit is contained in:
Matt Hill
2026-03-05 18:28:34 -07:00
parent 8fdeeab5bb
commit 3901d38d65
15 changed files with 442 additions and 272 deletions

View File

@@ -1,41 +1,57 @@
import { SmtpValue } from '../../types'
import { GetSystemSmtp, Patterns } from '../../util'
import { InputSpec, InputSpecOf } from './builder/inputSpec'
import { InputSpec } from './builder/inputSpec'
import { Value } from './builder/value'
import { Variants } from './builder/variants'
const securityVariants = Variants.of({
tls: {
name: 'TLS',
spec: InputSpec.of({
port: Value.dynamicText(async () => ({
name: 'Port',
required: true,
default: '465',
disabled: 'Fixed for TLS',
})),
}),
},
starttls: {
name: 'STARTTLS',
spec: InputSpec.of({
port: Value.select({
name: 'Port',
default: '587',
values: { '25': '25', '587': '587', '2525': '2525' },
}),
}),
},
})
/**
* Creates an SMTP field spec with provider-specific defaults pre-filled.
*/
function smtpFields(
defaults: {
host?: string
port?: number
security?: 'starttls' | 'tls'
hostDisabled?: boolean
} = {},
): InputSpec<SmtpValue> {
return InputSpec.of<InputSpecOf<SmtpValue>>({
host: Value.text({
name: 'Host',
required: true,
default: defaults.host ?? null,
placeholder: 'smtp.example.com',
}),
port: Value.number({
name: 'Port',
required: true,
default: defaults.port ?? 587,
min: 1,
max: 65535,
integer: true,
}),
security: Value.select({
) {
const hostSpec = Value.text({
name: 'Host',
required: true,
default: defaults.host ?? null,
placeholder: 'smtp.example.com',
})
return InputSpec.of({
host: defaults.hostDisabled
? hostSpec.withDisabled('Fixed for this provider')
: hostSpec,
security: Value.union({
name: 'Connection Security',
default: defaults.security ?? 'starttls',
values: {
starttls: 'STARTTLS',
tls: 'TLS',
},
default: defaults.security ?? 'tls',
variants: securityVariants,
}),
from: Value.text({
name: 'From Address',
@@ -68,44 +84,47 @@ export const customSmtp = smtpFields()
* Each variant has SMTP fields pre-filled with the provider's recommended settings.
*/
export const smtpProviderVariants = Variants.of({
none: {
name: 'None',
spec: InputSpec.of({}),
},
gmail: {
name: 'Gmail',
spec: smtpFields({
host: 'smtp.gmail.com',
port: 587,
security: 'starttls',
security: 'tls',
hostDisabled: true,
}),
},
ses: {
name: 'Amazon SES',
spec: smtpFields({
host: 'email-smtp.us-east-1.amazonaws.com',
port: 587,
security: 'starttls',
security: 'tls',
}),
},
sendgrid: {
name: 'SendGrid',
spec: smtpFields({
host: 'smtp.sendgrid.net',
port: 587,
security: 'starttls',
security: 'tls',
hostDisabled: true,
}),
},
mailgun: {
name: 'Mailgun',
spec: smtpFields({
host: 'smtp.mailgun.org',
port: 587,
security: 'starttls',
security: 'tls',
hostDisabled: true,
}),
},
protonmail: {
name: 'Proton Mail',
spec: smtpFields({
host: 'smtp.protonmail.ch',
port: 587,
security: 'starttls',
security: 'tls',
hostDisabled: true,
}),
},
other: {
@@ -121,7 +140,7 @@ export const smtpProviderVariants = Variants.of({
export const systemSmtpSpec = InputSpec.of({
provider: Value.union({
name: 'Provider',
default: null as any,
default: 'none',
variants: smtpProviderVariants,
}),
})

View File

@@ -1,25 +1,25 @@
export * as inputSpecTypes from './actions/input/inputSpecTypes'
export {
CurrentDependenciesResult,
OptionalDependenciesOf as OptionalDependencies,
RequiredDependenciesOf as RequiredDependencies,
} from './dependencies/setupDependencies'
export * from './osBindings'
export { SDKManifest } from './types/ManifestTypes'
export { Effects }
import { InputSpec as InputSpecClass } from './actions/input/builder/inputSpec'
import {
DependencyRequirement,
NamedHealthCheckResult,
Manifest,
ServiceInterface,
ActionId,
} from './osBindings'
import { Affine, StringObject, ToKebab } from './util'
import { Action, Actions } from './actions/setupActions'
import { Effects } from './Effects'
import { ExtendedVersion, VersionRange } from './exver'
export { Effects }
export * from './osBindings'
export { SDKManifest } from './types/ManifestTypes'
export {
RequiredDependenciesOf as RequiredDependencies,
OptionalDependenciesOf as OptionalDependencies,
CurrentDependenciesResult,
} from './dependencies/setupDependencies'
import {
ActionId,
DependencyRequirement,
Manifest,
NamedHealthCheckResult,
ServiceInterface,
} from './osBindings'
import { StringObject, ToKebab } from './util'
/** An object that can be built into a terminable daemon process. */
export type DaemonBuildable = {

View File

@@ -324,6 +324,14 @@ function enabledAddresses(addr: DerivedAddressInfo): HostnameInfo[] {
})
}
/**
* Filters out localhost and IPv6 link-local hostnames from a list.
* Equivalent to the `nonLocal` filter on `Filled` addresses.
*/
export function filterNonLocal(hostnames: HostnameInfo[]): HostnameInfo[] {
return filterRec(hostnames, nonLocalFilter, false)
}
export const filledAddress = (
host: Host,
addressInfo: AddressInfo,

View File

@@ -8,6 +8,7 @@ export {
GetServiceInterface,
getServiceInterface,
filledAddress,
filterNonLocal,
} from './getServiceInterface'
export { getServiceInterfaces } from './getServiceInterfaces'
export { once } from './once'