oggit/api_server/api_server.py

140 lines
3.6 KiB
Python

#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, "/usr/share/opengnsys-modules/python3/dist-packages")
import importlib
import logging
import uuid
import argparse
from flask import Flask, request
from flask_executor import Executor
from flask_restx import Api
from werkzeug.exceptions import HTTPException
from systemd.journal import JournalHandler
parser = argparse.ArgumentParser(
prog="api_server.py",
description="OpenGnsys Repository API Server",
)
debug_enabled = False
listen_host = '0.0.0.0'
parser.add_argument('--debug', action='store_true', help="Enable debug output")
parser.add_argument('--listen', metavar="HOST", help="Listen address")
parser.add_argument("-v", "--verbose", action="store_true", help = "Verbose console output")
args = parser.parse_args()
log = logging.getLogger('api_server')
log.addHandler(JournalHandler())
if args.verbose:
log.addHandler(logging.StreamHandler(stream=sys.stderr))
log.setLevel(logging.DEBUG)
else:
log.setLevel(logging.INFO)
if args.listen:
listen_host = args.listen
if args.debug:
debug_enabled = True
api_base_dir = os.path.dirname(os.path.realpath(__file__))
blueprints_dir = os.path.join(api_base_dir, 'blueprints')
installer_dir = os.path.join(api_base_dir, '../installer')
sys.path.insert(0, installer_dir)
# Create an instance of the Flask class
app = Flask(__name__)
api = Api(app,
version='0.50',
title = "OpenGnsys Git API",
description = "API for managing disk images stored in Git",
doc = "/swagger/")
executor = Executor(app)
log.info("Loading blueprints from %s", blueprints_dir)
sys.path.insert(0, blueprints_dir)
for filename in os.listdir(blueprints_dir):
if filename.endswith('.py'):
log.info("Loading %s/%s", blueprints_dir, filename)
module_name = filename.replace(".py", "")
log.info("Importing %s", module_name)
importlib.invalidate_caches()
module = importlib.import_module(module_name)
log.debug("Returned: %s", module)
app.register_blueprint(module.blueprint)
@app.errorhandler(HTTPException)
def handle_exception(e):
"""Return JSON for HTTP errors.
We create and log an error UUID for each error, and use journald's additional fields for easier searching.
"""
# start with the correct headers and status code from the error
response = e.get_response()
errid = uuid.uuid4().hex
if debug_enabled:
response = {
"errcode": e.code,
"errname": e.name,
"description": e.description,
}
else:
response = {
"errcode" : 500,
"errname" : "Internal error",
"description": f"Please see the log for error {errid}",
"error_id" : errid
}
log.error("Error ID %s: code %i, name %s, description %s", errid, e.code, e.name, e.description, extra = { "error_id" : errid, "errcode" : e.code, "errname" : e.name, "description" : e.description })
return response, 500
@app.after_request
def after_request(response):
log.info("Request from %s: %s %s %s %s", request.remote_addr, request.method, request.scheme, request.full_path, response.status,
extra = {"remote_addr" : request.remote_addr, "method" : request.method, "scheme" : request.scheme, "full_path" : request.full_path, "status" : response.status})
if debug_enabled:
log.debug("Response: %s", response.data, extra = {"response" : response.data})
return response
# Run the Flask app
if __name__ == '__main__':
print(f"Map: {app.url_map}")
app.run(debug=debug_enabled, host=listen_host)