| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
use serde::Deserialize; |
| 8 |
use sqlx::PgPool; |
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
#[derive(Debug, Clone, Copy, Deserialize)] |
| 21 |
#[serde(rename_all = "snake_case")] |
| 22 |
pub enum AlertKind { |
| 23 |
Health, |
| 24 |
Tls, |
| 25 |
Dns, |
| 26 |
Whois, |
| 27 |
Latency, |
| 28 |
Cors, |
| 29 |
Backup, |
| 30 |
Peer, |
| 31 |
Route, |
| 32 |
Scan, |
| 33 |
Monitoring, |
| 34 |
} |
| 35 |
|
| 36 |
impl AlertKind { |
| 37 |
|
| 38 |
pub fn as_str(self) -> &'static str { |
| 39 |
match self { |
| 40 |
Self::Health => "health", |
| 41 |
Self::Tls => "tls", |
| 42 |
Self::Dns => "dns", |
| 43 |
Self::Whois => "whois", |
| 44 |
Self::Latency => "latency", |
| 45 |
Self::Cors => "cors", |
| 46 |
Self::Backup => "backup", |
| 47 |
Self::Peer => "peer", |
| 48 |
Self::Route => "route", |
| 49 |
Self::Scan => "scan", |
| 50 |
Self::Monitoring => "monitoring", |
| 51 |
} |
| 52 |
} |
| 53 |
} |
| 54 |
|
| 55 |
|
| 56 |
#[derive(Debug, Clone, Copy, Deserialize)] |
| 57 |
#[serde(rename_all = "snake_case")] |
| 58 |
pub enum AlertSeverity { |
| 59 |
Critical, |
| 60 |
Warning, |
| 61 |
Info, |
| 62 |
} |
| 63 |
|
| 64 |
impl AlertSeverity { |
| 65 |
|
| 66 |
pub fn as_str(self) -> &'static str { |
| 67 |
match self { |
| 68 |
Self::Critical => "critical", |
| 69 |
Self::Warning => "warning", |
| 70 |
Self::Info => "info", |
| 71 |
} |
| 72 |
} |
| 73 |
} |
| 74 |
|
| 75 |
|
| 76 |
pub struct NewAlert<'a> { |
| 77 |
pub source: &'a str, |
| 78 |
pub kind: AlertKind, |
| 79 |
pub severity: AlertSeverity, |
| 80 |
pub title: &'a str, |
| 81 |
pub body: &'a str, |
| 82 |
pub dedup_key: Option<&'a str>, |
| 83 |
pub details: Option<&'a serde_json::Value>, |
| 84 |
} |
| 85 |
|
| 86 |
|
| 87 |
|
| 88 |
|
| 89 |
#[tracing::instrument(skip_all, fields(source = alert.source, kind = alert.kind.as_str(), severity = alert.severity.as_str()))] |
| 90 |
pub async fn insert_alert(pool: &PgPool, alert: &NewAlert<'_>) -> Result<i64, sqlx::Error> { |
| 91 |
let id: i64 = sqlx::query_scalar( |
| 92 |
"INSERT INTO admin_alerts (source, kind, severity, title, body, dedup_key, details) |
| 93 |
VALUES ($1, $2, $3, $4, $5, $6, $7) |
| 94 |
RETURNING id", |
| 95 |
) |
| 96 |
.bind(alert.source) |
| 97 |
.bind(alert.kind.as_str()) |
| 98 |
.bind(alert.severity.as_str()) |
| 99 |
.bind(alert.title) |
| 100 |
.bind(alert.body) |
| 101 |
.bind(alert.dedup_key) |
| 102 |
.bind(alert.details) |
| 103 |
.fetch_one(pool) |
| 104 |
.await?; |
| 105 |
Ok(id) |
| 106 |
} |
| 107 |
|
| 108 |
|
| 109 |
#[tracing::instrument(skip_all)] |
| 110 |
pub async fn mark_emailed(pool: &PgPool, id: i64) -> Result<(), sqlx::Error> { |
| 111 |
sqlx::query("UPDATE admin_alerts SET emailed = true WHERE id = $1") |
| 112 |
.bind(id) |
| 113 |
.execute(pool) |
| 114 |
.await?; |
| 115 |
Ok(()) |
| 116 |
} |
| 117 |
|