flaschengeist/flaschengeist/app.py

100 lines
3.5 KiB
Python

import enum
from flask import Flask
from flask_cors import CORS
from datetime import datetime, date
from flask.json import JSONEncoder, jsonify
from sqlalchemy.exc import OperationalError
from werkzeug.exceptions import HTTPException
from flaschengeist import logger
from flaschengeist.utils.hook import Hook
from flaschengeist.plugins import AuthPlugin, Plugin
from flaschengeist.utils.plugin import get_plugins
from flaschengeist.controller import roleController
from flaschengeist.config import config, configure_app
class CustomJSONEncoder(JSONEncoder):
def default(self, o):
try:
# Check if custom model
return o.serialize()
except AttributeError:
pass
if isinstance(o, datetime) or isinstance(o, date):
return o.isoformat()
if isinstance(o, enum.Enum):
return o.value
try:
# Check if iterable
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
return JSONEncoder.default(self, o)
@Hook("plugins.loaded")
def load_plugins(app: Flask):
def load_plugin(cls: type[Plugin]):
logger.debug(f"Load plugin {cls.id}")
# Initialize plugin with config section
plugin = cls(config.get(plugin_class.id, config.get(plugin_class.id.split(".")[-1], {})))
# Register blueprint if provided
if plugin.blueprint is not None:
app.register_blueprint(plugin.blueprint)
# Save plugin application context
app.config.setdefault("FG_PLUGINS", {})[plugin.id] = plugin
return plugin
for plugin_class in get_plugins():
names = [plugin_class.id, plugin_class.id.split(".")[-1]]
if config["FLASCHENGEIST"]["auth"] in names:
# Load authentification plugin
app.config["FG_AUTH_BACKEND"] = load_plugin(plugin_class)
logger.info(f"Using authentication plugin: {plugin_class.id}")
elif any([i in config and config[i].get("enabled", False) for i in names]):
# Load all other enabled plugins
load_plugin(plugin_class)
logger.info(f"Using plugin: {plugin_class.id}")
else:
logger.debug(f"Skip disabled plugin {plugin_class.id}")
if "FG_AUTH_BACKEND" not in app.config:
logger.fatal("No authentication plugin configured or authentication plugin not found")
raise RuntimeError("No authentication plugin configured or authentication plugin not found")
def create_app(test_config=None, cli=False):
app = Flask("flaschengeist")
app.json_encoder = CustomJSONEncoder
CORS(app)
with app.app_context():
from flaschengeist.database import db, migrate
configure_app(app, test_config)
db.init_app(app)
migrate.init_app(app, db, compare_type=True)
load_plugins(app)
@app.route("/", methods=["GET"])
def __get_state():
from . import __version__ as version
return jsonify({"plugins": app.config["FG_PLUGINS"], "version": version})
@app.errorhandler(Exception)
def handle_exception(e):
if isinstance(e, HTTPException):
logger.debug(e.description, exc_info=True)
return jsonify({"error": e.description}), e.code
if isinstance(e, OperationalError):
logger.error(e, exc_info=True)
return {"error": "Database unavailable"}, 504
logger.error(str(e), exc_info=True)
return jsonify({"error": "Internal server error occurred"}), 500
return app