2021-03-24 16:09:21 +00:00
|
|
|
import enum
|
2023-04-09 18:57:15 +00:00
|
|
|
import json
|
2022-02-13 22:01:49 +00:00
|
|
|
|
2021-12-19 21:11:57 +00:00
|
|
|
from flask import Flask
|
2020-09-07 14:07:35 +00:00
|
|
|
from flask_cors import CORS
|
2020-11-15 14:49:18 +00:00
|
|
|
from datetime import datetime, date
|
2023-04-09 18:57:15 +00:00
|
|
|
from flask.json import jsonify
|
|
|
|
from json import JSONEncoder
|
2023-05-03 04:29:55 +00:00
|
|
|
from flask.json.provider import JSONProvider
|
2022-08-25 13:14:11 +00:00
|
|
|
from sqlalchemy.exc import OperationalError
|
2020-09-07 14:07:35 +00:00
|
|
|
from werkzeug.exceptions import HTTPException
|
|
|
|
|
2022-02-13 22:01:49 +00:00
|
|
|
from flaschengeist import logger
|
2022-08-18 20:59:19 +00:00
|
|
|
from flaschengeist.controller import pluginController
|
2021-12-17 13:27:27 +00:00
|
|
|
from flaschengeist.utils.hook import Hook
|
2022-08-18 17:58:02 +00:00
|
|
|
from flaschengeist.config import configure_app
|
2023-04-09 18:57:15 +00:00
|
|
|
|
|
|
|
from flaschengeist.database import db
|
2020-09-07 14:07:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CustomJSONEncoder(JSONEncoder):
|
|
|
|
def default(self, o):
|
|
|
|
try:
|
2021-03-24 17:37:53 +00:00
|
|
|
# Check if custom model
|
2020-09-07 14:07:35 +00:00
|
|
|
return o.serialize()
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2020-11-15 14:49:18 +00:00
|
|
|
if isinstance(o, datetime) or isinstance(o, date):
|
2020-09-07 14:07:35 +00:00
|
|
|
return o.isoformat()
|
2021-03-24 16:09:21 +00:00
|
|
|
if isinstance(o, enum.Enum):
|
|
|
|
return o.value
|
2020-09-07 14:07:35 +00:00
|
|
|
try:
|
2021-03-24 17:37:53 +00:00
|
|
|
# Check if iterable
|
2020-09-07 14:07:35 +00:00
|
|
|
iterable = iter(o)
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
return list(iterable)
|
|
|
|
return JSONEncoder.default(self, o)
|
|
|
|
|
2023-04-09 18:57:15 +00:00
|
|
|
|
2023-05-03 04:29:55 +00:00
|
|
|
class CustomJSONProvider(JSONProvider):
|
2023-04-09 18:57:15 +00:00
|
|
|
ensure_ascii: bool = True
|
|
|
|
sort_keys: bool = True
|
|
|
|
|
|
|
|
def dumps(self, obj, **kwargs):
|
|
|
|
kwargs.setdefault("ensure_ascii", self.ensure_ascii)
|
|
|
|
kwargs.setdefault("sort_keys", self.sort_keys)
|
|
|
|
return json.dumps(obj, **kwargs, cls=CustomJSONEncoder)
|
|
|
|
|
|
|
|
def loads(self, s: str | bytes, **kwargs):
|
|
|
|
return json.loads(s, **kwargs)
|
|
|
|
|
2020-09-07 14:07:35 +00:00
|
|
|
|
2021-12-17 13:27:27 +00:00
|
|
|
@Hook("plugins.loaded")
|
2021-12-19 21:11:57 +00:00
|
|
|
def load_plugins(app: Flask):
|
2020-09-07 14:07:35 +00:00
|
|
|
app.config["FG_PLUGINS"] = {}
|
2021-12-06 22:44:07 +00:00
|
|
|
|
2022-08-18 20:59:19 +00:00
|
|
|
for plugin in pluginController.get_enabled_plugins():
|
2022-08-18 17:58:02 +00:00
|
|
|
logger.debug(f"Searching for enabled plugin {plugin.name}")
|
|
|
|
try:
|
2022-08-25 15:05:04 +00:00
|
|
|
# Load class
|
2022-08-25 13:14:11 +00:00
|
|
|
cls = plugin.entry_point.load()
|
2023-05-03 04:29:55 +00:00
|
|
|
# plugin = cls.query.get(plugin.id) if plugin.id is not None else plugin
|
|
|
|
# plugin = db.session.query(cls).get(plugin.id) if plugin.id is not None else plugin
|
2023-04-09 18:57:15 +00:00
|
|
|
plugin = db.session.get(cls, plugin.id) if plugin.id is not None else plugin
|
2022-08-25 15:05:04 +00:00
|
|
|
# Custom loading tasks
|
|
|
|
plugin.load()
|
|
|
|
# Register blueprint
|
|
|
|
if hasattr(plugin, "blueprint") and plugin.blueprint is not None:
|
|
|
|
app.register_blueprint(plugin.blueprint)
|
2022-08-18 17:58:02 +00:00
|
|
|
except:
|
|
|
|
logger.error(
|
|
|
|
f"Plugin {plugin.name} was enabled, but could not be loaded due to an error.",
|
|
|
|
exc_info=True,
|
|
|
|
)
|
|
|
|
continue
|
|
|
|
logger.info(f"Loaded plugin: {plugin.name}")
|
2022-08-25 15:05:04 +00:00
|
|
|
app.config["FG_PLUGINS"][plugin.name] = plugin
|
2020-10-15 12:44:58 +00:00
|
|
|
|
2023-05-03 04:29:55 +00:00
|
|
|
|
2021-12-19 21:11:57 +00:00
|
|
|
def create_app(test_config=None, cli=False):
|
2022-02-21 20:02:45 +00:00
|
|
|
app = Flask("flaschengeist")
|
2023-04-09 18:57:15 +00:00
|
|
|
app.json_provider_class = CustomJSONProvider
|
|
|
|
app.json = CustomJSONProvider(app)
|
2020-09-07 14:07:35 +00:00
|
|
|
CORS(app)
|
|
|
|
|
|
|
|
with app.app_context():
|
2021-12-19 21:11:57 +00:00
|
|
|
from flaschengeist.database import db, migrate
|
2020-10-15 20:10:50 +00:00
|
|
|
|
2022-02-13 22:01:49 +00:00
|
|
|
configure_app(app, test_config)
|
2020-09-07 14:07:35 +00:00
|
|
|
db.init_app(app)
|
2021-12-19 21:11:57 +00:00
|
|
|
migrate.init_app(app, db, compare_type=True)
|
|
|
|
load_plugins(app)
|
2020-09-07 14:07:35 +00:00
|
|
|
|
|
|
|
@app.route("/", methods=["GET"])
|
|
|
|
def __get_state():
|
|
|
|
from . import __version__ as version
|
2020-10-15 20:10:50 +00:00
|
|
|
|
2022-08-25 13:14:11 +00:00
|
|
|
return jsonify({"plugins": pluginController.get_loaded_plugins(), "version": version})
|
2020-09-07 14:07:35 +00:00
|
|
|
|
|
|
|
@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
|
2020-11-15 17:53:46 +00:00
|
|
|
if isinstance(e, OperationalError):
|
|
|
|
logger.error(e, exc_info=True)
|
|
|
|
return {"error": "Database unavailable"}, 504
|
2020-09-07 14:07:35 +00:00
|
|
|
logger.error(str(e), exc_info=True)
|
|
|
|
return jsonify({"error": "Internal server error occurred"}), 500
|
|
|
|
|
|
|
|
return app
|