proxmox/proxmox-notify/src/endpoints/sendmail.rs
Lukas Wagner b421a7ca24 notify: replace filters and groups with matcher-based system
This shifts notification routing into the matcher-system. Every
notification has associated metadata (key-value fields, severity -
to be extended) that can be match with match directives in
notification matchers. Right now, there are 2 matching directives,
match-field and match-severity. The first one allows one to do a
regex match/exact match on a metadata field, the other one allows one
to match one or more severites.
Every matcher also allows 'target' directives, these decide which
target(s) will be notified if a matcher matches a notification.

Since routing now happens in matchers, the API for sending is
simplified, since we do not need to specify a target any more.

The API routes for filters and groups have been removed completely.
The parser for the configuration file will still accept filter/group
entries, but will delete them once the config is saved again. This is
needed to allow a smooth transition from the old system to the new
system, since the old system was already available on pvetest.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00

144 lines
4.1 KiB
Rust

use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use proxmox_schema::api_types::COMMENT_SCHEMA;
use proxmox_schema::{api, Updater};
use crate::context::context;
use crate::renderer::TemplateRenderer;
use crate::schema::{EMAIL_SCHEMA, ENTITY_NAME_SCHEMA, USER_SCHEMA};
use crate::{renderer, Content, Endpoint, Error, Notification};
pub(crate) const SENDMAIL_TYPENAME: &str = "sendmail";
#[api(
properties: {
name: {
schema: ENTITY_NAME_SCHEMA,
},
mailto: {
type: Array,
items: {
schema: EMAIL_SCHEMA,
},
optional: true,
},
"mailto-user": {
type: Array,
items: {
schema: USER_SCHEMA,
},
optional: true,
},
comment: {
optional: true,
schema: COMMENT_SCHEMA,
},
},
)]
#[derive(Debug, Serialize, Deserialize, Updater, Default)]
#[serde(rename_all = "kebab-case")]
/// Config for Sendmail notification endpoints
pub struct SendmailConfig {
/// Name of the endpoint
#[updater(skip)]
pub name: String,
/// Mail recipients
#[serde(skip_serializing_if = "Option::is_none")]
pub mailto: Option<Vec<String>>,
/// Mail recipients
#[serde(skip_serializing_if = "Option::is_none")]
pub mailto_user: Option<Vec<String>>,
/// `From` address for the mail
#[serde(skip_serializing_if = "Option::is_none")]
pub from_address: Option<String>,
/// Author of the mail
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
/// Comment
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
/// Deprecated.
#[serde(skip_serializing)]
#[updater(skip)]
pub filter: Option<String>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum DeleteableSendmailProperty {
FromAddress,
Author,
Comment,
Mailto,
MailtoUser,
}
/// A sendmail notification endpoint.
pub struct SendmailEndpoint {
pub config: SendmailConfig,
}
impl Endpoint for SendmailEndpoint {
fn send(&self, notification: &Notification) -> Result<(), Error> {
let mut recipients = HashSet::new();
if let Some(mailto_addrs) = self.config.mailto.as_ref() {
for addr in mailto_addrs {
recipients.insert(addr.clone());
}
}
if let Some(users) = self.config.mailto_user.as_ref() {
for user in users {
if let Some(addr) = context().lookup_email_for_user(user) {
recipients.insert(addr);
}
}
}
let recipients_str: Vec<&str> = recipients.iter().map(String::as_str).collect();
let mailfrom = self
.config
.from_address
.clone()
.unwrap_or_else(|| context().default_sendmail_from());
match &notification.content {
Content::Template {
title_template,
body_template,
data,
} => {
let subject =
renderer::render_template(TemplateRenderer::Plaintext, title_template, data)?;
let html_part =
renderer::render_template(TemplateRenderer::Html, body_template, data)?;
let text_part =
renderer::render_template(TemplateRenderer::Plaintext, body_template, data)?;
let author = self
.config
.author
.clone()
.unwrap_or_else(|| context().default_sendmail_author());
proxmox_sys::email::sendmail(
&recipients_str,
&subject,
Some(&text_part),
Some(&html_part),
Some(&mailfrom),
Some(&author),
)
.map_err(|err| Error::NotifyFailed(self.config.name.clone(), err.into()))
}
}
}
fn name(&self) -> &str {
&self.config.name
}
}