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
|
lazy_static.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
openssl.workspace = true
|
openssl.workspace = true
|
||||||
|
proxmox-http = { workspace = true, features = ["client-sync"], optional = true }
|
||||||
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"]}
|
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"]}
|
||||||
proxmox-section-config = { workspace = true }
|
proxmox-section-config = { workspace = true }
|
||||||
proxmox-sys = { workspace = true, optional = true }
|
proxmox-sys = { workspace = true, optional = true }
|
||||||
@ -20,5 +21,6 @@ serde = { workspace = true, features = ["derive"]}
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["sendmail"]
|
default = ["sendmail", "gotify"]
|
||||||
sendmail = ["dep:handlebars", "dep:proxmox-sys"]
|
sendmail = ["dep:handlebars", "dep:proxmox-sys"]
|
||||||
|
gotify = ["dep:proxmox-http"]
|
||||||
|
@ -24,6 +24,17 @@ fn config_init() -> SectionConfig {
|
|||||||
SENDMAIL_SCHEMA,
|
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
|
config
|
||||||
}
|
}
|
||||||
@ -31,6 +42,18 @@ fn config_init() -> SectionConfig {
|
|||||||
fn private_config_init() -> SectionConfig {
|
fn private_config_init() -> SectionConfig {
|
||||||
let mut config = SectionConfig::new(&BACKEND_NAME_SCHEMA);
|
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
|
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")]
|
#[cfg(feature = "sendmail")]
|
||||||
pub mod sendmail;
|
pub mod sendmail;
|
||||||
|
@ -201,7 +201,6 @@ impl Bus {
|
|||||||
let mut endpoints = HashMap::new();
|
let mut endpoints = HashMap::new();
|
||||||
|
|
||||||
// Instantiate endpoints
|
// Instantiate endpoints
|
||||||
|
|
||||||
#[cfg(feature = "sendmail")]
|
#[cfg(feature = "sendmail")]
|
||||||
{
|
{
|
||||||
use endpoints::sendmail::SENDMAIL_TYPENAME;
|
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 })
|
Ok(Bus { endpoints })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user