Initial import
This commit is contained in:
commit
b1552cb7a8
10
damntfy.conf.example
Normal file
10
damntfy.conf.example
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
ntfy_default_topic => "alerts",
|
||||
ntfy_token => "tk_s3kr1tt0k3n",
|
||||
ntfy_url => "https://ntfy.example.com",
|
||||
|
||||
app_secrets => [
|
||||
# Generate with "openssl rand -base64 33".
|
||||
"t0ps3kr1t",
|
||||
],
|
||||
}
|
204
damntfy.pl
Executable file
204
damntfy.pl
Executable file
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env perl
|
||||
use Mojolicious::Lite -signatures;
|
||||
|
||||
use Mojo::Headers;
|
||||
use Mojo::URL;
|
||||
use Mojo::Util qw(b64_encode);
|
||||
|
||||
my $config = plugin "Config" => {
|
||||
default => {
|
||||
max_request_size => 1024 * 1024,
|
||||
ntfy_default_priority => 3,
|
||||
ntfy_default_topic => "am",
|
||||
ntfy_url => "https://ntfy.sh",
|
||||
app_secrets => [app->moniker],
|
||||
},
|
||||
};
|
||||
|
||||
app->max_request_size($config->{max_request_size});
|
||||
app->exception_format("txt");
|
||||
app->secrets($config->{app_secrets});
|
||||
|
||||
|
||||
sub am_validjson ($json) {
|
||||
return $json->{version} eq "4" &&
|
||||
defined($json->{status}) &&
|
||||
ref $json->{alerts} eq "ARRAY" &&
|
||||
$json->{alerts}->@* > 0;
|
||||
}
|
||||
|
||||
sub am_labelpairs ($m) {
|
||||
return map {"$_=$m->{$_}"} grep {$_ ne "alertname"} sort keys $m->%*;
|
||||
}
|
||||
|
||||
sub ntfy_post ($ua, $json) {
|
||||
state $url = Mojo::URL->new($config->{ntfy_url});
|
||||
state $headers = do {
|
||||
my $hs = Mojo::Headers->new();
|
||||
$hs->accept("application/json");
|
||||
$hs->content_type("application/json");
|
||||
|
||||
if (defined($config->{ntfy_token})) {
|
||||
$hs->authorization("Bearer $config->{ntfy_token}");
|
||||
} elsif (defined($config->{ntfy_user}) &&
|
||||
defined($config->{ntfy_pass})) {
|
||||
my $ui = "$config->{ntfy_user}:$config->{ntfy_pass}";
|
||||
$hs->authorization("Basic " . b64_encode($ui));
|
||||
}
|
||||
|
||||
$hs->to_hash;
|
||||
};
|
||||
|
||||
return $ua->post($url, $headers, json => $json)->result;
|
||||
}
|
||||
|
||||
|
||||
get "/" => sub ($c) {
|
||||
$c->render(template => "index");
|
||||
};
|
||||
|
||||
post "/webhook" => sub ($c) {
|
||||
my $query_params = $c->req->query_params;
|
||||
my $req_json = $c->req->json;
|
||||
|
||||
return $c->reply->exception("Invalid input")->rendered(400) if
|
||||
! am_validjson($req_json);
|
||||
|
||||
my $priority = int($query_params->param("priority") //
|
||||
$config->{ntfy_default_priority});
|
||||
my $topic = $query_params->param("topic") //
|
||||
$config->{ntfy_default_topic};
|
||||
|
||||
my $title = uc($req_json->{status});
|
||||
$title .= " - $req_json->{groupLabels}->{alertname}" if
|
||||
defined $req_json->{groupLabels}->{alertname};
|
||||
|
||||
my @tags_common = am_labelpairs $req_json->{commonLabels};
|
||||
my @tags_group = am_labelpairs $req_json->{groupLabels};
|
||||
|
||||
$c->stash(
|
||||
"am.groupKey" => $req_json->{groupKey},
|
||||
"am.truncatedAlerts" => $req_json->{truncatedAlerts},
|
||||
"am.status" => $req_json->{status},
|
||||
"am.receiver" => $req_json->{receiver},
|
||||
"am.groupLabels" => $req_json->{groupLabels},
|
||||
"am.commonLabels" => $req_json->{commonLabels},
|
||||
"am.commonAnnotations" => $req_json->{commonAnnotations},
|
||||
"am.externalURL" => $req_json->{externalURL},
|
||||
"am.alerts" => $req_json->{alerts},
|
||||
"am.alerts.Firing" => [grep {$_->{status} eq "firing"}
|
||||
$req_json->{alerts}->@*],
|
||||
"am.alerts.Resolved" => [grep {$_->{status} eq "resolved"}
|
||||
$req_json->{alerts}->@*],
|
||||
);
|
||||
|
||||
my $json = {
|
||||
message => $c->render_to_string(
|
||||
template => "message",
|
||||
format => "txt",
|
||||
),
|
||||
priority => $priority,
|
||||
tags => [@tags_group, @tags_common],
|
||||
title => $title,
|
||||
topic => $topic,
|
||||
};
|
||||
|
||||
my $ua = $c->ua->request_timeout(10);
|
||||
my $res = ntfy_post($ua, $json);
|
||||
|
||||
return $c->reply->exception("Upstream error")->rendered($res->code) if
|
||||
!$res->is_success;
|
||||
|
||||
$c->render(json => $res->json);
|
||||
};
|
||||
|
||||
app->start;
|
||||
__DATA__
|
||||
|
||||
@@ message.txt.ep
|
||||
% my $alerts_firing = stash "am.alerts.Firing";
|
||||
% my $alerts_resolved = stash "am.alerts.Resolved";
|
||||
% my $common_annotations = stash "am.commonAnnotations";
|
||||
% if (defined $common_annotations->{summary}) {
|
||||
%= $common_annotations->{summary}
|
||||
% }
|
||||
% if ($alerts_firing->@* > 0) {
|
||||
|
||||
FIRING:<%== scalar $alerts_firing->@* %>
|
||||
% for my $alert ($alerts_firing->@*) {
|
||||
%== $alert->{annotations}->{description};
|
||||
% }
|
||||
% }
|
||||
% if ($alerts_resolved->@* > 0) {
|
||||
|
||||
RESOLVED:<%== scalar $alerts_resolved->@* %>
|
||||
% for my $alert ($alerts_resolved->@*) {
|
||||
- <%== $alert->{labels}->{instance} %>
|
||||
% }
|
||||
% }
|
||||
|
||||
@@ index.html.ep
|
||||
% layout "main";
|
||||
% title "damntfy";
|
||||
<h1>damntfy</h1>
|
||||
<p>Alertmanager–ntfy integrator</a>
|
||||
<dl>
|
||||
<dt>POST <a href="/webhook">/webhook</a></dt>
|
||||
<dd>Receives an Alertmanager webhook payload and sends a ntfy payload.</dd>
|
||||
</dl>
|
||||
|
||||
@@ layouts/main.html.ep
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title><%= title %></title>
|
||||
<style type="text/css">
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
dd,
|
||||
dl,
|
||||
dt,
|
||||
h1,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0 auto;
|
||||
min-height: 100vh;
|
||||
max-width: 60rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
dl,
|
||||
h1,
|
||||
p {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 3rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
%= content
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user