Files
mailks/error.rs
T
zhenyi c4824ef261 feat(k8s): add Kubernetes Helm chart for emailks service
- Create Helm chart structure with Chart.yaml and values.yaml
- Add deployment template with container configuration and environment variables
- Implement service template for gRPC port exposure
- Add service account template with security configuration
- Include horizontal pod autoscaler template for scaling capabilities
- Add helper templates for naming and label management
- Configure SMTP settings as configurable parameters in values.yaml
- Set up resource limits and requests for container performance
- Implement liveness and readiness probes for health checks
- Add support for existing secrets and custom configurations
2026-06-07 22:59:06 +08:00

148 lines
4.5 KiB
Rust

use std::fmt;
use crate::ENV_PREFIX;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigError {
MissingEnv { name: &'static str },
InvalidEnv { name: &'static str, reason: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QueueError {
Closed,
Full,
IdExhausted,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EmailError {
MissingSender,
MissingRecipients,
RequestSenderDisabled,
IncompleteCredentials,
InvalidAddress {
field: &'static str,
value: String,
reason: String,
},
InvalidContentType {
filename: String,
value: String,
reason: String,
},
InvalidHeader {
name: String,
reason: String,
},
ForbiddenHeader {
name: String,
},
UnsupportedAttachmentUrl {
filename: String,
url: String,
},
BuildTransport(String),
BuildMessage(String),
Send(String),
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingEnv { name } => {
write!(f, "missing environment variable {ENV_PREFIX}{name}")
}
Self::InvalidEnv { name, reason } => {
write!(
f,
"invalid environment variable {ENV_PREFIX}{name}: {reason}"
)
}
}
}
}
impl fmt::Display for QueueError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Closed => f.write_str("email queue is closed"),
Self::Full => f.write_str("email queue is full"),
Self::IdExhausted => f.write_str("email queue id space is exhausted"),
}
}
}
impl fmt::Display for EmailError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingSender => {
f.write_str("missing sender: set request.from or APP_SMTP_FROM_EMAIL")
}
Self::MissingRecipients => {
f.write_str("missing recipients: request.to must not be empty")
}
Self::RequestSenderDisabled => {
f.write_str("request.from is disabled: use APP_SMTP_FROM_EMAIL or enable APP_SMTP_ALLOW_REQUEST_FROM")
}
Self::IncompleteCredentials => {
f.write_str("APP_SMTP_USERNAME and APP_SMTP_PASSWORD must be set together")
}
Self::InvalidAddress {
field,
value,
reason,
} => write!(f, "invalid email address in {field} ({value}): {reason}"),
Self::InvalidContentType {
filename,
value,
reason,
} => write!(
f,
"invalid content type for attachment {filename} ({value}): {reason}"
),
Self::InvalidHeader { name, reason } => {
write!(f, "invalid custom header {name}: {reason}")
}
Self::ForbiddenHeader { name } => {
write!(f, "custom header {name} is managed by the mail builder")
}
Self::UnsupportedAttachmentUrl { filename, url } => write!(
f,
"attachment {filename} uses url {url}, but URL attachment fetching is not supported"
),
Self::BuildTransport(reason) => write!(f, "failed to build SMTP transport: {reason}"),
Self::BuildMessage(reason) => write!(f, "failed to build email message: {reason}"),
Self::Send(reason) => write!(f, "failed to send email: {reason}"),
}
}
}
impl std::error::Error for ConfigError {}
impl std::error::Error for QueueError {}
impl std::error::Error for EmailError {}
impl EmailError {
/// 返回 true 表示该错误不可重试,应直接销毁任务
pub fn is_terminal(&self) -> bool {
matches!(
self,
Self::MissingSender
| Self::MissingRecipients
| Self::RequestSenderDisabled
| Self::IncompleteCredentials
| Self::InvalidAddress { .. }
| Self::InvalidContentType { .. }
| Self::InvalidHeader { .. }
| Self::ForbiddenHeader { .. }
| Self::UnsupportedAttachmentUrl { .. }
| Self::BuildTransport(_)
| Self::BuildMessage(_)
)
}
pub fn is_retryable(&self) -> bool {
!self.is_terminal()
}
}