mirror of
https://github.com/FAUSheppy/icinga-webhook-gateway
synced 2025-12-06 07:21:38 +01:00
feat: entry creation % details
This commit is contained in:
67
server.py
67
server.py
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import time
|
||||||
import flask
|
import flask
|
||||||
import json
|
import json
|
||||||
import argparse
|
import argparse
|
||||||
@@ -7,6 +8,12 @@ import os
|
|||||||
import datetime
|
import datetime
|
||||||
import pytimeparse.timeparse as timeparse
|
import pytimeparse.timeparse as timeparse
|
||||||
import sys
|
import sys
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
import flask_wtf
|
||||||
|
from flask_wtf import FlaskForm, CSRFProtect
|
||||||
|
from wtforms import StringField, SubmitField, BooleanField, DecimalField
|
||||||
|
from wtforms.validators import DataRequired, Length
|
||||||
|
|
||||||
from sqlalchemy import Column, Integer, String, Boolean, or_, and_
|
from sqlalchemy import Column, Integer, String, Boolean, or_, and_
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
@@ -17,6 +24,9 @@ from flask_sqlalchemy import SQLAlchemy
|
|||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
|
|
||||||
app = flask.Flask("Icinga Report In Gateway")
|
app = flask.Flask("Icinga Report In Gateway")
|
||||||
|
CSRFProtect(app)
|
||||||
|
|
||||||
|
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
|
||||||
app.config['JSON_CONFIG_FILE'] = 'services.json'
|
app.config['JSON_CONFIG_FILE'] = 'services.json'
|
||||||
app.config['JSON_CONFIG_DIR'] = 'config'
|
app.config['JSON_CONFIG_DIR'] = 'config'
|
||||||
@@ -40,6 +50,10 @@ class Status(db.Model):
|
|||||||
status = Column(String)
|
status = Column(String)
|
||||||
info_text = Column(String)
|
info_text = Column(String)
|
||||||
|
|
||||||
|
def human_date(self):
|
||||||
|
dt = datetime.datetime.fromtimestamp(self.timestamp)
|
||||||
|
return dt.strftime("%d-%m%-%y %H:%M")
|
||||||
|
|
||||||
def buildReponseDict(status, service=None):
|
def buildReponseDict(status, service=None):
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
@@ -56,6 +70,8 @@ def buildReponseDict(status, service=None):
|
|||||||
@app.route('/overview')
|
@app.route('/overview')
|
||||||
def overview():
|
def overview():
|
||||||
|
|
||||||
|
user = flask.request.headers.get("X-Preferred-Username")
|
||||||
|
|
||||||
# query all services #
|
# query all services #
|
||||||
services = db.session.query(Service).all()
|
services = db.session.query(Service).all()
|
||||||
|
|
||||||
@@ -83,7 +99,54 @@ def overview():
|
|||||||
status_unique_results.append(status)
|
status_unique_results.append(status)
|
||||||
|
|
||||||
return flask.render_template("overview.html", status_list=status_unique_results,
|
return flask.render_template("overview.html", status_list=status_unique_results,
|
||||||
datetime=datetime.datetime)
|
datetime=datetime.datetime, user=user)
|
||||||
|
|
||||||
|
class EntryForm(FlaskForm):
|
||||||
|
|
||||||
|
service = StringField("Service Name", validators=[DataRequired()])
|
||||||
|
timeout = DecimalField("Timeout in days", default=30)
|
||||||
|
|
||||||
|
def create_entry(form, user):
|
||||||
|
|
||||||
|
# TODO add entry to icinga
|
||||||
|
token = secrets.token_urlsafe(16)
|
||||||
|
print(form.timeout.data)
|
||||||
|
service = Service(service=form.service.data, timeout=int(form.timeout.data),
|
||||||
|
owner=user, token=token)
|
||||||
|
db.session.merge(service)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
@app.route("/service-details")
|
||||||
|
def service_details():
|
||||||
|
|
||||||
|
user = flask.request.headers.get("X-Preferred-Username")
|
||||||
|
service = flask.request.args.get("service")
|
||||||
|
|
||||||
|
# query service #
|
||||||
|
service = db.session.query(Service).filter(Service.service==service).first()
|
||||||
|
|
||||||
|
# validate #
|
||||||
|
if not service:
|
||||||
|
return ("{} not found".format("service"), 404)
|
||||||
|
if service.owner and service.owner != user:
|
||||||
|
return ("Services is not owned by {}".format(user))
|
||||||
|
|
||||||
|
status_list = db.session.query()
|
||||||
|
|
||||||
|
return flask.render_template("service_info.html", service=service, flask=flask,
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/entry-form", methods=["GET", "POST"])
|
||||||
|
def create_interface():
|
||||||
|
|
||||||
|
user = flask.request.headers.get("X-Preferred-Username")
|
||||||
|
|
||||||
|
form = EntryForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
create_entry(form, user)
|
||||||
|
return flask.redirect('/service-details?service={}'.format(form.service.data))
|
||||||
|
return flask.render_template('add_modify_service.html', form=form)
|
||||||
|
|
||||||
@app.route('/alive')
|
@app.route('/alive')
|
||||||
def alive():
|
def alive():
|
||||||
@@ -188,6 +251,8 @@ def create_app():
|
|||||||
db.create_all()
|
db.create_all()
|
||||||
config = {}
|
config = {}
|
||||||
|
|
||||||
|
app.config["SECRET_KEY"] = secrets.token_urlsafe(64)
|
||||||
|
|
||||||
if os.path.isfile(app.config["JSON_CONFIG_FILE"]):
|
if os.path.isfile(app.config["JSON_CONFIG_FILE"]):
|
||||||
with open(app.config["JSON_CONFIG_FILE"]) as f:
|
with open(app.config["JSON_CONFIG_FILE"]) as f:
|
||||||
config |= json.load(f)
|
config |= json.load(f)
|
||||||
|
|||||||
79
static/site.css
Normal file
79
static/site.css
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
body{
|
||||||
|
background: radial-gradient(ellipse at center, #47918a 0%, #0b3161 100%);
|
||||||
|
color: aliceblue !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar{
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(0,0,0,0);
|
||||||
|
display: block;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-el{
|
||||||
|
float: right;
|
||||||
|
display: inline-block;
|
||||||
|
background: rgba(0,0,0,0);
|
||||||
|
color: aliceblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-el:hover{
|
||||||
|
color: darkgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-tile{
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-status{
|
||||||
|
margin-top: 10px;
|
||||||
|
box-shadow: 0px 0px 4px 3px rgba(0,0,0,0.5);
|
||||||
|
background: linear-gradient(to top, #cfc6b054 0%, #cfcfcfc4 100%);
|
||||||
|
padding: 8px;
|
||||||
|
width: fit-content;
|
||||||
|
font-family: monospace;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-status p{
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-name{
|
||||||
|
margin: auto;
|
||||||
|
max-width: 1000px;
|
||||||
|
width: fit-content;
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-timeout{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-token{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.example{
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-indent{
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-indent-double{
|
||||||
|
padding-left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-table{
|
||||||
|
margin: auto;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
24
templates/add_modify_service.html
Normal file
24
templates/add_modify_service.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{% include "head.html" %}
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<button class="mt-4 mb-4 btn btn-secondary" onclick="window.location.href='/'">
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
{% if form.service.errors %}
|
||||||
|
<ul class="errors">
|
||||||
|
{% for error in form.service.errors %}
|
||||||
|
<li>{{ error }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
<form method="POST" action="/entry-form">
|
||||||
|
|
||||||
|
{{ form.csrf_token }}
|
||||||
|
{{ form.service.label }} {{ form.service(size=20) }} </br>
|
||||||
|
{{ form.timeout.label }} {{ form.timeout() }} </br>
|
||||||
|
|
||||||
|
<input type="submit" value="Go">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
24
templates/head.html
Normal file
24
templates/head.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/site.css">
|
||||||
|
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<!-- needed for @media-css mofiers -->
|
||||||
|
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||||
|
|
||||||
|
<!-- Font Awesome -->
|
||||||
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
|
||||||
|
|
||||||
|
<!-- Bootstrap core CSS -->
|
||||||
|
<link href="https://cdn.atlantishq.de/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Material Design Bootstrap -->
|
||||||
|
<link href="https://cdn.atlantishq.de/css/mdb.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="https://cdn.atlantishq.de/js/jquery.js"></script>
|
||||||
|
<script src="https://cdn.atlantishq.de/js/popper.js"></script>
|
||||||
|
<script src="https://cdn.atlantishq.de/js/bootstrap.js"></script>
|
||||||
|
<script src="https://cdn.atlantishq.de/js/mdb.min.js"></script>
|
||||||
|
<script src="https://cdn.atlantishq.de/js/addons/datatables.min.js" type="text/javascript">
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
12
templates/navbar.html
Normal file
12
templates/navbar.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<div class="navbar">
|
||||||
|
{% if user %}
|
||||||
|
<div style="float: left;" class="navbar-el">{{ user }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<a href="/overview" style="float: left;" class="navbar-el">Overview</a>
|
||||||
|
<a href="/entry-form" style="float: left;" class="navbar-el">Create Service</a>
|
||||||
|
{% if user %}
|
||||||
|
<a href="/oauth2/sign_out" class="navbar-el">Logout</a>
|
||||||
|
{% else %}
|
||||||
|
<div class="navbar-el placeholder"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
@@ -1,33 +1,12 @@
|
|||||||
<head>
|
{% include "head.html" %}
|
||||||
<link rel="stylesheet" type="text/css" href="/static/site.css">
|
|
||||||
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<!-- needed for @media-css mofiers -->
|
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
|
|
||||||
|
|
||||||
<!-- Bootstrap core CSS -->
|
|
||||||
<link href="https://cdn.atlantishq.de/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- Material Design Bootstrap -->
|
|
||||||
<link href="https://cdn.atlantishq.de/css/mdb.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="https://cdn.atlantishq.de/js/jquery.js"></script>
|
|
||||||
<script src="https://cdn.atlantishq.de/js/popper.js"></script>
|
|
||||||
<script src="https://cdn.atlantishq.de/js/bootstrap.js"></script>
|
|
||||||
<script src="https://cdn.atlantishq.de/js/mdb.min.js"></script>
|
|
||||||
<script src="https://cdn.atlantishq.de/js/addons/datatables.min.js" type="text/javascript">
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
|
{% include "navbar.html" %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for status in status_list %}
|
{% for status in status_list %}
|
||||||
<div class="col-md-5 m-3 p-2 border rounded"
|
<a href="/service-details?service={{ status.service}}"
|
||||||
|
class="col-md-5 m-3 p-2 border rounded overview-tile"
|
||||||
{% if status.status == "OK" %}
|
{% if status.status == "OK" %}
|
||||||
style="background-color: lightgreen;"
|
style="background-color: lightgreen;"
|
||||||
{% elif status.status == "WARNING" %}
|
{% elif status.status == "WARNING" %}
|
||||||
@@ -44,7 +23,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
{{ datetime.fromtimestamp(status.timestamp).strftime("%H:%M %d.%m.%y") }}
|
{{ datetime.fromtimestamp(status.timestamp).strftime("%H:%M %d.%m.%y") }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
61
templates/service_info.html
Normal file
61
templates/service_info.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{% include "head.html" %}
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
{% include "navbar.html" %}
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="service-name">Service: {{ service.service }}</h2>
|
||||||
|
<div class="service-timeout">Timeout: {{ service.timeout }}d</div>
|
||||||
|
<div class="service-token">Secret Token: {{ service.token }}</div>
|
||||||
|
|
||||||
|
<div class="last-status">
|
||||||
|
{% if status_list | length > 0 %}
|
||||||
|
<p>{{ status_list[0].status }} submitted on {{ status_list[0].timestamp }}</i>
|
||||||
|
{% else %}
|
||||||
|
<p style="color: darkred;">No status for this service submitted</i>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="my-4">Curl</h5>
|
||||||
|
<div class="ml-3 example">
|
||||||
|
curl -X POST \ <br>
|
||||||
|
<div class="example-indent">
|
||||||
|
-H "application/json" \ <br>
|
||||||
|
-d '{ "service_name" : "{{ service.service }}",
|
||||||
|
"token" : "{{ service.token }}", \<br>
|
||||||
|
"status" : "OK", "info" : "Free Text Information here" }' \<br>
|
||||||
|
{{ flask.request.url_root }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="my-4">Python</h5>
|
||||||
|
<div class="ml-3 example">
|
||||||
|
import requests
|
||||||
|
requests.post("{{ flask.request.url_root }}",<br>
|
||||||
|
<div class="example-indent-double">
|
||||||
|
json= { "service_name" : "{{ service.service }}", <br>
|
||||||
|
<div class="example-indent-double">
|
||||||
|
"token" : "{{ service.token }}", <br>
|
||||||
|
"status" : "OK", </br>
|
||||||
|
"info" : "additional information" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="mb-4 mt-5 status-table">
|
||||||
|
<thead>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Info</th>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for status in status_list %}
|
||||||
|
<td>status.human_date()<td>
|
||||||
|
<td>status.status</td>
|
||||||
|
<td>status.info</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user