Initial import

This commit is contained in:
Lucas Gabriel Vuotto 2023-12-31 15:14:19 +00:00
commit b1552cb7a8
2 changed files with 214 additions and 0 deletions

10
damntfy.conf.example Normal file
View 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
View 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&ndash;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>