rework smtp

This commit is contained in:
Matt Hill
2026-02-23 14:25:51 -07:00
parent 804560d43c
commit e9b9925c0e
14 changed files with 333 additions and 107 deletions

View File

@@ -5,42 +5,124 @@ import { Value } from './builder/value'
import { Variants } from './builder/variants'
/**
* Base SMTP settings, to be used by StartOS for system wide SMTP
* Creates an SMTP field spec with provider-specific defaults pre-filled.
*/
export const customSmtp: InputSpec<SmtpValue> = InputSpec.of<
InputSpecOf<SmtpValue>
>({
server: Value.text({
name: 'SMTP Server',
required: true,
default: null,
}),
port: Value.number({
name: 'Port',
required: true,
default: 587,
min: 1,
max: 65535,
integer: true,
}),
from: Value.text({
name: 'From Address',
required: true,
default: null,
placeholder: 'Example Name <test@example.com>',
inputmode: 'email',
patterns: [Patterns.emailWithName],
}),
login: Value.text({
name: 'Login',
required: true,
default: null,
}),
password: Value.text({
name: 'Password',
required: false,
default: null,
masked: true,
function smtpFields(
defaults: {
host?: string
port?: number
security?: 'starttls' | 'tls'
} = {},
): 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({
name: 'Connection Security',
default: defaults.security ?? 'starttls',
values: {
starttls: 'STARTTLS',
tls: 'TLS',
},
}),
from: Value.text({
name: 'From Address',
required: true,
default: null,
placeholder: 'Example Name <test@example.com>',
patterns: [Patterns.emailWithName],
}),
username: Value.text({
name: 'Username',
required: true,
default: null,
}),
password: Value.text({
name: 'Password',
required: false,
default: null,
masked: true,
}),
})
}
/**
* Base SMTP settings with no provider-specific defaults.
*/
export const customSmtp = smtpFields()
/**
* Provider presets for SMTP configuration.
* Each variant has SMTP fields pre-filled with the provider's recommended settings.
*/
export const smtpProviderVariants = Variants.of({
gmail: {
name: 'Gmail',
spec: smtpFields({
host: 'smtp.gmail.com',
port: 587,
security: 'starttls',
}),
},
ses: {
name: 'Amazon SES',
spec: smtpFields({
host: 'email-smtp.us-east-1.amazonaws.com',
port: 587,
security: 'starttls',
}),
},
sendgrid: {
name: 'SendGrid',
spec: smtpFields({
host: 'smtp.sendgrid.net',
port: 587,
security: 'starttls',
}),
},
mailgun: {
name: 'Mailgun',
spec: smtpFields({
host: 'smtp.mailgun.org',
port: 587,
security: 'starttls',
}),
},
protonmail: {
name: 'Proton Mail',
spec: smtpFields({
host: 'smtp.protonmail.ch',
port: 587,
security: 'starttls',
}),
},
other: {
name: 'Other',
spec: customSmtp,
},
})
/**
* System SMTP settings with provider presets.
* Wraps smtpProviderVariants in a union for use by the system email settings page.
*/
export const systemSmtpSpec = InputSpec.of({
provider: Value.union({
name: 'Provider',
default: null as any,
variants: smtpProviderVariants,
}),
})
@@ -55,19 +137,24 @@ const smtpVariants = Variants.of({
'A custom from address for this service. If not provided, the system from address will be used.',
required: false,
default: null,
placeholder: '<name>test@example.com',
inputmode: 'email',
patterns: [Patterns.email],
placeholder: 'Name <test@example.com>',
patterns: [Patterns.emailWithName],
}),
}),
},
custom: {
name: 'Custom Credentials',
spec: customSmtp,
spec: InputSpec.of({
provider: Value.union({
name: 'Provider',
default: null as any,
variants: smtpProviderVariants,
}),
}),
},
})
/**
* For service inputSpec. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings
* For service inputSpec. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings with provider presets
*/
export const smtpInputSpec = Value.dynamicUnion(async ({ effects }) => {
const smtp = await new GetSystemSmtp(effects).once()

View File

@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SmtpSecurity = 'starttls' | 'tls'

View File

@@ -1,9 +1,11 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SmtpSecurity } from './SmtpSecurity'
export type SmtpValue = {
server: string
host: string
port: number
from: string
login: string
username: string
password: string | null
security: SmtpSecurity
}

View File

@@ -1,10 +1,12 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SmtpSecurity } from './SmtpSecurity'
export type TestSmtpParams = {
server: string
host: string
port: number
from: string
to: string
login: string
username: string
password: string
security: SmtpSecurity
}

View File

@@ -269,6 +269,7 @@ export { SideloadResponse } from './SideloadResponse'
export { SignalStrength } from './SignalStrength'
export { SignAssetParams } from './SignAssetParams'
export { SignerInfo } from './SignerInfo'
export { SmtpSecurity } from './SmtpSecurity'
export { SmtpValue } from './SmtpValue'
export { SshAddParams } from './SshAddParams'
export { SshDeleteParams } from './SshDeleteParams'

View File

@@ -115,11 +115,12 @@ export type Daemon = {
export type HealthStatus = NamedHealthCheckResult['result']
/** SMTP mail server configuration values. */
export type SmtpValue = {
server: string
host: string
port: number
from: string
login: string
username: string
password: string | null | undefined
security: 'starttls' | 'tls'
}
/**

View File

@@ -9,7 +9,12 @@ import {
import { ServiceInterfaceType, Effects } from '../../base/lib/types'
import * as patterns from '../../base/lib/util/patterns'
import { Backups } from './backup/Backups'
import { smtpInputSpec } from '../../base/lib/actions/input/inputSpecConstants'
import {
smtpInputSpec,
systemSmtpSpec,
customSmtp,
smtpProviderVariants,
} from '../../base/lib/actions/input/inputSpecConstants'
import { Daemon, Daemons } from './mainFn/Daemons'
import { checkPortListening } from './health/checkFns/checkPortListening'
import { checkWebUrl, runHealthScript } from './health/checkFns'
@@ -468,7 +473,12 @@ export class StartSdk<Manifest extends T.SDKManifest> {
run: Run<{}>,
) => Action.withoutInput(id, metadata, run),
},
inputSpecConstants: { smtpInputSpec },
inputSpecConstants: {
smtpInputSpec,
systemSmtpSpec,
customSmtp,
smtpProviderVariants,
},
/**
* @description Use this function to create a service interface.
* @param effects

View File

@@ -1,12 +1,12 @@
{
"name": "@start9labs/start-sdk",
"version": "0.4.0-beta.51",
"version": "0.4.0-beta.52",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@start9labs/start-sdk",
"version": "0.4.0-beta.51",
"version": "0.4.0-beta.52",
"license": "MIT",
"dependencies": {
"@iarna/toml": "^3.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@start9labs/start-sdk",
"version": "0.4.0-beta.51",
"version": "0.4.0-beta.52",
"description": "Software development kit to facilitate packaging services for StartOS",
"main": "./package/lib/index.js",
"types": "./package/lib/index.d.ts",