]> git.proxmox.com Git - proxmox-perl-rs.git/blob - common/src/notify.rs
notify: support 'origin' paramter
[proxmox-perl-rs.git] / common / src / notify.rs
1 #[perlmod::package(name = "Proxmox::RS::Notify")]
2 mod export {
3 use std::collections::HashMap;
4 use std::sync::Mutex;
5
6 use anyhow::{bail, Error};
7 use serde_json::Value as JSONValue;
8
9 use perlmod::Value;
10 use proxmox_http_error::HttpError;
11 use proxmox_notify::endpoints::gotify::{
12 DeleteableGotifyProperty, GotifyConfig, GotifyConfigUpdater, GotifyPrivateConfig,
13 GotifyPrivateConfigUpdater,
14 };
15 use proxmox_notify::endpoints::sendmail::{
16 DeleteableSendmailProperty, SendmailConfig, SendmailConfigUpdater,
17 };
18 use proxmox_notify::endpoints::smtp::{
19 DeleteableSmtpProperty, SmtpConfig, SmtpConfigUpdater, SmtpMode, SmtpPrivateConfig,
20 SmtpPrivateConfigUpdater,
21 };
22 use proxmox_notify::matcher::{
23 CalendarMatcher, DeleteableMatcherProperty, FieldMatcher, MatchModeOperator, MatcherConfig,
24 MatcherConfigUpdater, SeverityMatcher,
25 };
26 use proxmox_notify::{api, Config, Notification, Severity};
27
28 pub struct NotificationConfig {
29 config: Mutex<Config>,
30 }
31
32 perlmod::declare_magic!(Box<NotificationConfig> : &NotificationConfig as "Proxmox::RS::Notify");
33
34 /// Support `dclone` so this can be put into the `ccache` of `PVE::Cluster`.
35 #[export(name = "STORABLE_freeze", raw_return)]
36 fn storable_freeze(
37 #[try_from_ref] this: &NotificationConfig,
38 cloning: bool,
39 ) -> Result<Value, Error> {
40 if !cloning {
41 bail!("freezing Notification config not supported!");
42 }
43
44 let mut cloned = Box::new(NotificationConfig {
45 config: Mutex::new(this.config.lock().unwrap().clone()),
46 });
47 let value = Value::new_pointer::<NotificationConfig>(&mut *cloned);
48 let _perl = Box::leak(cloned);
49 Ok(value)
50 }
51
52 /// Instead of `thaw` we implement `attach` for `dclone`.
53 #[export(name = "STORABLE_attach", raw_return)]
54 fn storable_attach(
55 #[raw] class: Value,
56 cloning: bool,
57 #[raw] serialized: Value,
58 ) -> Result<Value, Error> {
59 if !cloning {
60 bail!("STORABLE_attach called with cloning=false");
61 }
62 let data = unsafe { Box::from_raw(serialized.pv_raw::<NotificationConfig>()?) };
63 Ok(perlmod::instantiate_magic!(&class, MAGIC => data))
64 }
65
66 #[export(raw_return)]
67 fn parse_config(
68 #[raw] class: Value,
69 raw_config: &[u8],
70 raw_private_config: &[u8],
71 ) -> Result<Value, Error> {
72 let raw_config = std::str::from_utf8(raw_config)?;
73 let raw_private_config = std::str::from_utf8(raw_private_config)?;
74
75 Ok(perlmod::instantiate_magic!(&class, MAGIC => Box::new(
76 NotificationConfig {
77 config: Mutex::new(Config::new(raw_config, raw_private_config)?)
78 }
79 )))
80 }
81
82 #[export]
83 fn write_config(#[try_from_ref] this: &NotificationConfig) -> Result<(String, String), Error> {
84 Ok(this.config.lock().unwrap().write()?)
85 }
86
87 #[export]
88 fn digest(#[try_from_ref] this: &NotificationConfig) -> String {
89 let config = this.config.lock().unwrap();
90 hex::encode(config.digest())
91 }
92
93 #[export(serialize_error)]
94 fn send(
95 #[try_from_ref] this: &NotificationConfig,
96 severity: Severity,
97 title: String,
98 body: String,
99 template_data: Option<JSONValue>,
100 fields: Option<HashMap<String, String>>,
101 ) -> Result<(), HttpError> {
102 let config = this.config.lock().unwrap();
103 let notification = Notification::new_templated(
104 severity,
105 title,
106 body,
107 template_data.unwrap_or_default(),
108 fields.unwrap_or_default(),
109 );
110
111 api::common::send(&config, &notification)
112 }
113
114 #[export(serialize_error)]
115 fn test_target(
116 #[try_from_ref] this: &NotificationConfig,
117 target: &str,
118 ) -> Result<(), HttpError> {
119 let config = this.config.lock().unwrap();
120 api::common::test_target(&config, target)
121 }
122
123 #[export(serialize_error)]
124 fn get_sendmail_endpoints(
125 #[try_from_ref] this: &NotificationConfig,
126 ) -> Result<Vec<SendmailConfig>, HttpError> {
127 let config = this.config.lock().unwrap();
128 api::sendmail::get_endpoints(&config)
129 }
130
131 #[export(serialize_error)]
132 fn get_sendmail_endpoint(
133 #[try_from_ref] this: &NotificationConfig,
134 id: &str,
135 ) -> Result<SendmailConfig, HttpError> {
136 let config = this.config.lock().unwrap();
137 api::sendmail::get_endpoint(&config, id)
138 }
139
140 #[export(serialize_error)]
141 #[allow(clippy::too_many_arguments)]
142 fn add_sendmail_endpoint(
143 #[try_from_ref] this: &NotificationConfig,
144 name: String,
145 mailto: Option<Vec<String>>,
146 mailto_user: Option<Vec<String>>,
147 from_address: Option<String>,
148 author: Option<String>,
149 comment: Option<String>,
150 disable: Option<bool>,
151 ) -> Result<(), HttpError> {
152 let mut config = this.config.lock().unwrap();
153
154 api::sendmail::add_endpoint(
155 &mut config,
156 &SendmailConfig {
157 name,
158 mailto,
159 mailto_user,
160 from_address,
161 author,
162 comment,
163 disable,
164 filter: None,
165 origin: None,
166 },
167 )
168 }
169
170 #[export(serialize_error)]
171 #[allow(clippy::too_many_arguments)]
172 fn update_sendmail_endpoint(
173 #[try_from_ref] this: &NotificationConfig,
174 name: &str,
175 mailto: Option<Vec<String>>,
176 mailto_user: Option<Vec<String>>,
177 from_address: Option<String>,
178 author: Option<String>,
179 comment: Option<String>,
180 disable: Option<bool>,
181 delete: Option<Vec<DeleteableSendmailProperty>>,
182 digest: Option<&str>,
183 ) -> Result<(), HttpError> {
184 let mut config = this.config.lock().unwrap();
185 let digest = decode_digest(digest)?;
186
187 api::sendmail::update_endpoint(
188 &mut config,
189 name,
190 &SendmailConfigUpdater {
191 mailto,
192 mailto_user,
193 from_address,
194 author,
195 comment,
196 disable,
197 },
198 delete.as_deref(),
199 digest.as_deref(),
200 )
201 }
202
203 #[export(serialize_error)]
204 fn delete_sendmail_endpoint(
205 #[try_from_ref] this: &NotificationConfig,
206 name: &str,
207 ) -> Result<(), HttpError> {
208 let mut config = this.config.lock().unwrap();
209 api::sendmail::delete_endpoint(&mut config, name)
210 }
211
212 #[export(serialize_error)]
213 fn get_gotify_endpoints(
214 #[try_from_ref] this: &NotificationConfig,
215 ) -> Result<Vec<GotifyConfig>, HttpError> {
216 let config = this.config.lock().unwrap();
217 api::gotify::get_endpoints(&config)
218 }
219
220 #[export(serialize_error)]
221 fn get_gotify_endpoint(
222 #[try_from_ref] this: &NotificationConfig,
223 id: &str,
224 ) -> Result<GotifyConfig, HttpError> {
225 let config = this.config.lock().unwrap();
226 api::gotify::get_endpoint(&config, id)
227 }
228
229 #[export(serialize_error)]
230 fn add_gotify_endpoint(
231 #[try_from_ref] this: &NotificationConfig,
232 name: String,
233 server: String,
234 token: String,
235 comment: Option<String>,
236 disable: Option<bool>,
237 ) -> Result<(), HttpError> {
238 let mut config = this.config.lock().unwrap();
239 api::gotify::add_endpoint(
240 &mut config,
241 &GotifyConfig {
242 name: name.clone(),
243 server,
244 comment,
245 disable,
246 filter: None,
247 origin: None,
248 },
249 &GotifyPrivateConfig { name, token },
250 )
251 }
252
253 #[export(serialize_error)]
254 #[allow(clippy::too_many_arguments)]
255 fn update_gotify_endpoint(
256 #[try_from_ref] this: &NotificationConfig,
257 name: &str,
258 server: Option<String>,
259 token: Option<String>,
260 comment: Option<String>,
261 disable: Option<bool>,
262 delete: Option<Vec<DeleteableGotifyProperty>>,
263 digest: Option<&str>,
264 ) -> Result<(), HttpError> {
265 let mut config = this.config.lock().unwrap();
266 let digest = decode_digest(digest)?;
267
268 api::gotify::update_endpoint(
269 &mut config,
270 name,
271 &GotifyConfigUpdater {
272 server,
273 comment,
274 disable,
275 },
276 &GotifyPrivateConfigUpdater { token },
277 delete.as_deref(),
278 digest.as_deref(),
279 )
280 }
281
282 #[export(serialize_error)]
283 fn delete_gotify_endpoint(
284 #[try_from_ref] this: &NotificationConfig,
285 name: &str,
286 ) -> Result<(), HttpError> {
287 let mut config = this.config.lock().unwrap();
288 api::gotify::delete_gotify_endpoint(&mut config, name)
289 }
290
291 #[export(serialize_error)]
292 fn get_smtp_endpoints(
293 #[try_from_ref] this: &NotificationConfig,
294 ) -> Result<Vec<SmtpConfig>, HttpError> {
295 let config = this.config.lock().unwrap();
296 api::smtp::get_endpoints(&config)
297 }
298
299 #[export(serialize_error)]
300 fn get_smtp_endpoint(
301 #[try_from_ref] this: &NotificationConfig,
302 id: &str,
303 ) -> Result<SmtpConfig, HttpError> {
304 let config = this.config.lock().unwrap();
305 api::smtp::get_endpoint(&config, id)
306 }
307
308 #[export(serialize_error)]
309 #[allow(clippy::too_many_arguments)]
310 fn add_smtp_endpoint(
311 #[try_from_ref] this: &NotificationConfig,
312 name: String,
313 server: String,
314 port: Option<u16>,
315 mode: Option<SmtpMode>,
316 username: Option<String>,
317 password: Option<String>,
318 mailto: Option<Vec<String>>,
319 mailto_user: Option<Vec<String>>,
320 from_address: String,
321 author: Option<String>,
322 comment: Option<String>,
323 disable: Option<bool>,
324 ) -> Result<(), HttpError> {
325 let mut config = this.config.lock().unwrap();
326 api::smtp::add_endpoint(
327 &mut config,
328 &SmtpConfig {
329 name: name.clone(),
330 server,
331 port,
332 mode,
333 username,
334 mailto,
335 mailto_user,
336 from_address,
337 author,
338 comment,
339 disable,
340 origin: None,
341 },
342 &SmtpPrivateConfig { name, password },
343 )
344 }
345
346 #[export(serialize_error)]
347 #[allow(clippy::too_many_arguments)]
348 fn update_smtp_endpoint(
349 #[try_from_ref] this: &NotificationConfig,
350 name: &str,
351 server: Option<String>,
352 port: Option<u16>,
353 mode: Option<SmtpMode>,
354 username: Option<String>,
355 password: Option<String>,
356 mailto: Option<Vec<String>>,
357 mailto_user: Option<Vec<String>>,
358 from_address: Option<String>,
359 author: Option<String>,
360 comment: Option<String>,
361 disable: Option<bool>,
362 delete: Option<Vec<DeleteableSmtpProperty>>,
363 digest: Option<&str>,
364 ) -> Result<(), HttpError> {
365 let mut config = this.config.lock().unwrap();
366 let digest = decode_digest(digest)?;
367
368 api::smtp::update_endpoint(
369 &mut config,
370 name,
371 &SmtpConfigUpdater {
372 server,
373 port,
374 mode,
375 username,
376 mailto,
377 mailto_user,
378 from_address,
379 author,
380 comment,
381 disable,
382 },
383 &SmtpPrivateConfigUpdater { password },
384 delete.as_deref(),
385 digest.as_deref(),
386 )
387 }
388
389 #[export(serialize_error)]
390 fn delete_smtp_endpoint(
391 #[try_from_ref] this: &NotificationConfig,
392 name: &str,
393 ) -> Result<(), HttpError> {
394 let mut config = this.config.lock().unwrap();
395 api::smtp::delete_endpoint(&mut config, name)
396 }
397
398 #[export(serialize_error)]
399 fn get_matchers(
400 #[try_from_ref] this: &NotificationConfig,
401 ) -> Result<Vec<MatcherConfig>, HttpError> {
402 let config = this.config.lock().unwrap();
403 api::matcher::get_matchers(&config)
404 }
405
406 #[export(serialize_error)]
407 fn get_matcher(
408 #[try_from_ref] this: &NotificationConfig,
409 id: &str,
410 ) -> Result<MatcherConfig, HttpError> {
411 let config = this.config.lock().unwrap();
412 api::matcher::get_matcher(&config, id)
413 }
414
415 #[export(serialize_error)]
416 #[allow(clippy::too_many_arguments)]
417 fn add_matcher(
418 #[try_from_ref] this: &NotificationConfig,
419 name: String,
420 target: Option<Vec<String>>,
421 match_severity: Option<Vec<SeverityMatcher>>,
422 match_field: Option<Vec<FieldMatcher>>,
423 match_calendar: Option<Vec<CalendarMatcher>>,
424 mode: Option<MatchModeOperator>,
425 invert_match: Option<bool>,
426 comment: Option<String>,
427 disable: Option<bool>,
428 ) -> Result<(), HttpError> {
429 let mut config = this.config.lock().unwrap();
430 api::matcher::add_matcher(
431 &mut config,
432 &MatcherConfig {
433 name,
434 match_severity,
435 match_field,
436 match_calendar,
437 target,
438 mode,
439 invert_match,
440 comment,
441 disable,
442 origin: None,
443 },
444 )
445 }
446
447 #[export(serialize_error)]
448 #[allow(clippy::too_many_arguments)]
449 fn update_matcher(
450 #[try_from_ref] this: &NotificationConfig,
451 name: &str,
452 target: Option<Vec<String>>,
453 match_severity: Option<Vec<SeverityMatcher>>,
454 match_field: Option<Vec<FieldMatcher>>,
455 match_calendar: Option<Vec<CalendarMatcher>>,
456 mode: Option<MatchModeOperator>,
457 invert_match: Option<bool>,
458 comment: Option<String>,
459 disable: Option<bool>,
460 delete: Option<Vec<DeleteableMatcherProperty>>,
461 digest: Option<&str>,
462 ) -> Result<(), HttpError> {
463 let mut config = this.config.lock().unwrap();
464 let digest = decode_digest(digest)?;
465
466 api::matcher::update_matcher(
467 &mut config,
468 name,
469 &MatcherConfigUpdater {
470 match_severity,
471 match_field,
472 match_calendar,
473 target,
474 mode,
475 invert_match,
476 comment,
477 disable,
478 },
479 delete.as_deref(),
480 digest.as_deref(),
481 )
482 }
483
484 #[export(serialize_error)]
485 fn delete_matcher(
486 #[try_from_ref] this: &NotificationConfig,
487 name: &str,
488 ) -> Result<(), HttpError> {
489 let mut config = this.config.lock().unwrap();
490 api::matcher::delete_matcher(&mut config, name)
491 }
492
493 #[export]
494 fn get_referenced_entities(
495 #[try_from_ref] this: &NotificationConfig,
496 name: &str,
497 ) -> Result<Vec<String>, HttpError> {
498 let config = this.config.lock().unwrap();
499 api::common::get_referenced_entities(&config, name)
500 }
501
502 fn decode_digest(digest: Option<&str>) -> Result<Option<Vec<u8>>, HttpError> {
503 digest
504 .map(hex::decode)
505 .transpose()
506 .map_err(|e| api::http_err!(BAD_REQUEST, "invalid digest: {e}"))
507 }
508 }