forked from Proxmox/proxmox
notify: add gotify endpoint
Add an endpoint for Gotify [1], showing the how easy it is to add new endpoint implementations. [1] https://gotify.net/ Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
This commit is contained in:
parent
21c5c9a0c7
commit
990fc8efd2
@ -12,6 +12,7 @@ handlebars = { workspace = true, optional = true }
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
openssl.workspace = true
|
||||
proxmox-http = { workspace = true, features = ["client-sync"], optional = true }
|
||||
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"]}
|
||||
proxmox-section-config = { workspace = true }
|
||||
proxmox-sys = { workspace = true, optional = true }
|
||||
@ -20,5 +21,6 @@ serde = { workspace = true, features = ["derive"]}
|
||||
serde_json.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["sendmail"]
|
||||
default = ["sendmail", "gotify"]
|
||||
sendmail = ["dep:handlebars", "dep:proxmox-sys"]
|
||||
gotify = ["dep:proxmox-http"]
|
||||
|
@ -24,6 +24,17 @@ fn config_init() -> SectionConfig {
|
||||
SENDMAIL_SCHEMA,
|
||||
));
|
||||
}
|
||||
#[cfg(feature = "gotify")]
|
||||
{
|
||||
use crate::endpoints::gotify::{GotifyConfig, GOTIFY_TYPENAME};
|
||||
|
||||
const GOTIFY_SCHEMA: &ObjectSchema = GotifyConfig::API_SCHEMA.unwrap_object_schema();
|
||||
config.register_plugin(SectionConfigPlugin::new(
|
||||
GOTIFY_TYPENAME.to_string(),
|
||||
Some(String::from("name")),
|
||||
GOTIFY_SCHEMA,
|
||||
));
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
@ -31,6 +42,18 @@ fn config_init() -> SectionConfig {
|
||||
fn private_config_init() -> SectionConfig {
|
||||
let mut config = SectionConfig::new(&BACKEND_NAME_SCHEMA);
|
||||
|
||||
#[cfg(feature = "gotify")]
|
||||
{
|
||||
use crate::endpoints::gotify::{GotifyPrivateConfig, GOTIFY_TYPENAME};
|
||||
|
||||
const GOTIFY_SCHEMA: &ObjectSchema = GotifyPrivateConfig::API_SCHEMA.unwrap_object_schema();
|
||||
config.register_plugin(SectionConfigPlugin::new(
|
||||
GOTIFY_TYPENAME.to_string(),
|
||||
Some(String::from("name")),
|
||||
GOTIFY_SCHEMA,
|
||||
));
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
|
117
proxmox-notify/src/endpoints/gotify.rs
Normal file
117
proxmox-notify/src/endpoints/gotify.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::schema::ENTITY_NAME_SCHEMA;
|
||||
use crate::{Endpoint, Error, Notification, Severity};
|
||||
|
||||
use proxmox_schema::api_types::COMMENT_SCHEMA;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use proxmox_http::client::sync::Client;
|
||||
use proxmox_http::{HttpClient, HttpOptions};
|
||||
use proxmox_schema::{api, Updater};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GotifyMessageBody<'a> {
|
||||
title: &'a str,
|
||||
message: &'a str,
|
||||
priority: u32,
|
||||
}
|
||||
|
||||
fn severity_to_priority(level: Severity) -> u32 {
|
||||
match level {
|
||||
Severity::Info => 1,
|
||||
Severity::Notice => 3,
|
||||
Severity::Warning => 5,
|
||||
Severity::Error => 9,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const GOTIFY_TYPENAME: &str = "gotify";
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
name: {
|
||||
schema: ENTITY_NAME_SCHEMA,
|
||||
},
|
||||
comment: {
|
||||
optional: true,
|
||||
schema: COMMENT_SCHEMA,
|
||||
},
|
||||
}
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, Updater, Default)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Config for Gotify notification endpoints
|
||||
pub struct GotifyConfig {
|
||||
/// Name of the endpoint
|
||||
#[updater(skip)]
|
||||
pub name: String,
|
||||
/// Gotify Server URL
|
||||
pub server: String,
|
||||
/// Comment
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
#[api()]
|
||||
#[derive(Serialize, Deserialize, Clone, Updater)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Private configuration for Gotify notification endpoints.
|
||||
/// This config will be saved to a separate configuration file with stricter
|
||||
/// permissions (root:root 0600)
|
||||
pub struct GotifyPrivateConfig {
|
||||
/// Name of the endpoint
|
||||
#[updater(skip)]
|
||||
pub name: String,
|
||||
/// Authentication token
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
/// A Gotify notification endpoint.
|
||||
pub struct GotifyEndpoint {
|
||||
pub config: GotifyConfig,
|
||||
pub private_config: GotifyPrivateConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum DeleteableGotifyProperty {
|
||||
Comment,
|
||||
}
|
||||
|
||||
impl Endpoint for GotifyEndpoint {
|
||||
fn send(&self, notification: &Notification) -> Result<(), Error> {
|
||||
// TODO: What about proxy configuration?
|
||||
let client = Client::new(HttpOptions::default());
|
||||
|
||||
let uri = format!("{}/message", self.config.server);
|
||||
|
||||
let body = GotifyMessageBody {
|
||||
title: ¬ification.title,
|
||||
message: ¬ification.body,
|
||||
priority: severity_to_priority(notification.severity),
|
||||
};
|
||||
|
||||
let body = serde_json::to_vec(&body)
|
||||
.map_err(|err| Error::NotifyFailed(self.name().to_string(), err.into()))?;
|
||||
let extra_headers = HashMap::from([(
|
||||
"Authorization".into(),
|
||||
format!("Bearer {}", self.private_config.token),
|
||||
)]);
|
||||
|
||||
client
|
||||
.post(
|
||||
&uri,
|
||||
Some(body.as_slice()),
|
||||
Some("application/json"),
|
||||
Some(&extra_headers),
|
||||
)
|
||||
.map_err(|err| Error::NotifyFailed(self.name().to_string(), err.into()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.config.name
|
||||
}
|
||||
}
|
@ -1,2 +1,4 @@
|
||||
#[cfg(feature = "gotify")]
|
||||
pub mod gotify;
|
||||
#[cfg(feature = "sendmail")]
|
||||
pub mod sendmail;
|
||||
|
@ -201,7 +201,6 @@ impl Bus {
|
||||
let mut endpoints = HashMap::new();
|
||||
|
||||
// Instantiate endpoints
|
||||
|
||||
#[cfg(feature = "sendmail")]
|
||||
{
|
||||
use endpoints::sendmail::SENDMAIL_TYPENAME;
|
||||
@ -218,6 +217,23 @@ impl Bus {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "gotify")]
|
||||
{
|
||||
use endpoints::gotify::GOTIFY_TYPENAME;
|
||||
use endpoints::gotify::{GotifyConfig, GotifyEndpoint, GotifyPrivateConfig};
|
||||
endpoints.extend(
|
||||
parse_endpoints_with_private_config!(
|
||||
config,
|
||||
GotifyConfig,
|
||||
GotifyPrivateConfig,
|
||||
GotifyEndpoint,
|
||||
GOTIFY_TYPENAME
|
||||
)?
|
||||
.into_iter()
|
||||
.map(|e| (e.name().into(), e)),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Bus { endpoints })
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user