From 135a9dd01dc35a96f8cccbd316a4789df0330fc7 Mon Sep 17 00:00:00 2001 From: Sense T Date: Mon, 8 Aug 2022 05:51:01 +0000 Subject: [PATCH] init --- .gitignore | 4 ++ .python-version | 1 + .vscode/launch.json | 35 +++++++++++++++++ .vscode/settings.json | 3 ++ app/__init__.py | 3 ++ app/api.py | 4 ++ app/app.py | 24 ++++++++++++ config/config_example.yaml | 8 ++++ config/text_template_example.tpl | 22 +++++++++++ utils/__init__.py | 0 utils/error_handler.py | 11 ++++++ utils/json_encoder.py | 18 +++++++++ views/__init__.py | 1 + views/apis/__init__.py | 1 + views/apis/bark_service.py | 66 ++++++++++++++++++++++++++++++++ views/apis/health.py | 12 ++++++ 16 files changed, 213 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 app/__init__.py create mode 100644 app/api.py create mode 100644 app/app.py create mode 100644 config/config_example.yaml create mode 100644 config/text_template_example.tpl create mode 100644 utils/__init__.py create mode 100644 utils/error_handler.py create mode 100644 utils/json_encoder.py create mode 100644 views/__init__.py create mode 100644 views/apis/__init__.py create mode 100644 views/apis/bark_service.py create mode 100644 views/apis/health.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08ce83a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.venv +__pycache__ +config/config.yaml +config/text_template.tpl \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..bec3a35 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +system diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b0727c2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name":"Python: Flask Migration", + "type":"python", + "request":"launch", + "module":"flask", + "env":{ + "FLASK_APP":"app.migrate", + "FLASK_ENV":"development", + "FLASK_DEBUG":"0" + }, + "args":["db", "migrate"], + "jinja":false + }, + { + "name": "Python: Flask", + "type": "python", + "request": "launch", + "module": "flask", + "env": { + "FLASK_APP": "app", + "FLASK_ENV": "development", + "FLASK_DEBUG": "1" + }, + "args": [ + "run", + "--no-debugger", + "--no-reload" + ], + "jinja": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4fbe420 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.defaultInterpreterPath": "venv/bin/python3" +} \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..478ef1f --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,3 @@ +from app.app import app +from views import * +from views.apis import * \ No newline at end of file diff --git a/app/api.py b/app/api.py new file mode 100644 index 0000000..7a8f017 --- /dev/null +++ b/app/api.py @@ -0,0 +1,4 @@ +from .app import app +from flask_restful import Api + +api = Api(app) \ No newline at end of file diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..37db31d --- /dev/null +++ b/app/app.py @@ -0,0 +1,24 @@ +import yaml + +from flask import Flask +from utils.json_encoder import JSONEncoder +from os.path import dirname, abspath, join + +class Config(): + pass + +with open('config/config.yaml', 'r') as f: + config_yaml = yaml.safe_load(f) + +config = Config() +for k, v in config_yaml.items(): + setattr(config, k, v) + +app = Flask(__name__, + root_path=join(dirname(abspath(__file__)), '..')) +app.config.from_object(config) + +app.json_encoder = JSONEncoder +app.config['RESTFUL_JSON'] = { + 'cls': app.json_encoder +} \ No newline at end of file diff --git a/config/config_example.yaml b/config/config_example.yaml new file mode 100644 index 0000000..3afcaca --- /dev/null +++ b/config/config_example.yaml @@ -0,0 +1,8 @@ +bark_api: http://bark-server.default:8080/push +title: + firing: Homeserver 告警发生 + resolved: Homeserver 告警解除 + default: Homeserver +to: +- 'token' +template_file: text_template.tpl \ No newline at end of file diff --git a/config/text_template_example.tpl b/config/text_template_example.tpl new file mode 100644 index 0000000..62d2350 --- /dev/null +++ b/config/text_template_example.tpl @@ -0,0 +1,22 @@ +{%- for alert in alerts -%} + {%- if alert.status == 'firing' -%} +告警发生: + 告警时间:{{alert.startsAt}} + {% if alert.labels.instance -%}涉及实例:{{alert.labels.instance}}{%- endif %} + {% if alert.labels.device -%}涉及设备:{{alert.labels.device}}{%- endif %} + 告警名称:{{alert.labels.alertname}} + 严重性:{{alert.labels.severity}} + 详情:{{alert.annotations.summary}} + {% if alert.annotations.value -%}告警数据:{{alert.annotations.value}}{%- endif %} + {%- else -%} +告警恢复: + 告警时间:{{alert.startsAt}} + 恢复时间:{{alert.endsAt}} + {% if alert.labels.instance -%}涉及实例:{{alert.labels.instance}}{%- endif %} + {% if alert.labels.device -%}涉及设备:{{alert.labels.device}}{%- endif %} + 告警名称:{{alert.labels.alertname}} + 严重性:{{alert.labels.severity}} + 详情:{{alert.annotations.summary}} + {% if alert.annotations.value -%}告警数据:{{alert.annotations.value}}{%- endif %} + {%- endif -%} +{%- endfor -%} \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/error_handler.py b/utils/error_handler.py new file mode 100644 index 0000000..4ad897f --- /dev/null +++ b/utils/error_handler.py @@ -0,0 +1,11 @@ +import traceback +from app import app + + +def error_handler(e): + app.logger.warning(e, traceback.format_exc()) + return { + 'success': False, + 'message': 'Error: {0}'.format(str(e)), + 'data': None + }, 500 \ No newline at end of file diff --git a/utils/json_encoder.py b/utils/json_encoder.py new file mode 100644 index 0000000..7b7dee0 --- /dev/null +++ b/utils/json_encoder.py @@ -0,0 +1,18 @@ +from flask.json import JSONEncoder +import datetime +import decimal + + +class JSONEncoder(JSONEncoder): + def default(self, obj): + try: + if isinstance(obj, datetime.datetime): + return obj.strftime('%Y-%m-%d %H:%M:%S') + elif isinstance(obj, decimal.Decimal): + return float(obj) + iterable = iter(obj) + except TypeError: + pass + else: + return list(iterable) + return JSONEncoder.default(self, obj) \ No newline at end of file diff --git a/views/__init__.py b/views/__init__.py new file mode 100644 index 0000000..912e971 --- /dev/null +++ b/views/__init__.py @@ -0,0 +1 @@ +__all__ = ['apis'] \ No newline at end of file diff --git a/views/apis/__init__.py b/views/apis/__init__.py new file mode 100644 index 0000000..f1d8609 --- /dev/null +++ b/views/apis/__init__.py @@ -0,0 +1 @@ +__all__ = ['bark_service', 'health'] \ No newline at end of file diff --git a/views/apis/bark_service.py b/views/apis/bark_service.py new file mode 100644 index 0000000..f77e8e4 --- /dev/null +++ b/views/apis/bark_service.py @@ -0,0 +1,66 @@ +from flask import request +from flask_restful import Resource +from utils.error_handler import error_handler +from app.app import app +from app.api import api +from munch import Munch +import requests + +class BarkResource(Resource): + def post(self): + try: + data = Munch.fromDict(request.get_json(silent=True)) + app.logger.debug(data) + config = Munch.fromDict(app.config) + content = { + 'title': config.title.firing if data.status == 'firing' else config.title.resolved if data.status == 'resolved' else config.title.default, + 'category': 'category' + } + + # TODO: alertmanager message to bark content. + ''' + { + "version": "4", + "groupKey": , // key identifying the group of alerts (e.g. to deduplicate) + "truncatedAlerts": , // how many alerts have been truncated due to "max_alerts" + "status": "", + "receiver": , + "groupLabels": , + "commonLabels": , + "commonAnnotations": , + "externalURL": , // backlink to the Alertmanager. + "alerts": [ + { + "status": "", + "labels": , + "annotations": , + "startsAt": "", + "endsAt": "", + "generatorURL": , // identifies the entity that caused the alert + "fingerprint": // fingerprint to identify the alert + }, + ... + ] + } + ''' + content.body = app.jinja_environment.get_template(config.template_file).render(data) + app.logger.debug(content.body) + for to in config.to: + content['device_key'] = to + response = requests.post( + url = config.bark_api, + headers = { + "Content-Type": "application/json; charset=utf-8", + }, + data=content + ) + app.logger.info(response) + return { + 'success': True, + 'message': '', + 'data': {}, + } + except Exception as e: + return error_handler(e) + +api.add_resource(BarkResource, '/api/send') \ No newline at end of file diff --git a/views/apis/health.py b/views/apis/health.py new file mode 100644 index 0000000..4b13ef2 --- /dev/null +++ b/views/apis/health.py @@ -0,0 +1,12 @@ +from flask_restful import Resource +from app.api import api + +class HealthResource(Resource): + def get(self): + return { + 'success': True, + 'message': '', + 'data': {}, + } + +api.add_resource(HealthResource, '/health') \ No newline at end of file