Compare commits
2 Commits
74ca9b247d
...
ee38e46c12
Author | SHA1 | Date |
---|---|---|
Ferdinand Thiessen | ee38e46c12 | |
Ferdinand Thiessen | d3530cc15f |
|
@ -1,4 +1,5 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
alembic_migrations = str(Path(__file__).resolve().parent / "migrations")
|
alembic_migrations_path = str(Path(__file__).resolve().parent / "migrations")
|
||||||
|
alembic_script_path = str(Path(__file__).resolve().parent)
|
||||||
|
|
|
@ -5,11 +5,10 @@ from flask_cors import CORS
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from flask.json import JSONEncoder, jsonify
|
from flask.json import JSONEncoder, jsonify
|
||||||
from importlib.metadata import entry_points
|
from importlib.metadata import entry_points
|
||||||
from sqlalchemy.exc import OperationalError
|
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
from flaschengeist.models import Plugin
|
from flaschengeist.controller import pluginController
|
||||||
from flaschengeist.utils.hook import Hook
|
from flaschengeist.utils.hook import Hook
|
||||||
from flaschengeist.config import configure_app
|
from flaschengeist.config import configure_app
|
||||||
|
|
||||||
|
@ -38,11 +37,9 @@ class CustomJSONEncoder(JSONEncoder):
|
||||||
@Hook("plugins.loaded")
|
@Hook("plugins.loaded")
|
||||||
def load_plugins(app: Flask):
|
def load_plugins(app: Flask):
|
||||||
app.config["FG_PLUGINS"] = {}
|
app.config["FG_PLUGINS"] = {}
|
||||||
|
|
||||||
enabled_plugins = Plugin.query.filter(Plugin.enabled == True).all()
|
|
||||||
all_plugins = entry_points(group="flaschengeist.plugins")
|
all_plugins = entry_points(group="flaschengeist.plugins")
|
||||||
|
|
||||||
for plugin in enabled_plugins:
|
for plugin in pluginController.get_enabled_plugins():
|
||||||
logger.debug(f"Searching for enabled plugin {plugin.name}")
|
logger.debug(f"Searching for enabled plugin {plugin.name}")
|
||||||
entry_point = all_plugins.select(name=plugin.name)
|
entry_point = all_plugins.select(name=plugin.name)
|
||||||
if not entry_point:
|
if not entry_point:
|
||||||
|
@ -52,7 +49,7 @@ def load_plugins(app: Flask):
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
loaded = entry_point.load()(entry_point)
|
loaded = entry_point[0].load()(entry_point[0])
|
||||||
if hasattr(plugin, "blueprint") and plugin.blueprint is not None:
|
if hasattr(plugin, "blueprint") and plugin.blueprint is not None:
|
||||||
app.register_blueprint(plugin.blueprint)
|
app.register_blueprint(plugin.blueprint)
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -3,7 +3,7 @@ from click.decorators import pass_context
|
||||||
from flask.cli import with_appcontext
|
from flask.cli import with_appcontext
|
||||||
from flask_migrate import upgrade
|
from flask_migrate import upgrade
|
||||||
|
|
||||||
from flaschengeist.alembic import alembic_migrations
|
from flaschengeist.alembic import alembic_migrations_path
|
||||||
from flaschengeist.cli.plugin_cmd import install_plugin_command
|
from flaschengeist.cli.plugin_cmd import install_plugin_command
|
||||||
from flaschengeist.utils.hook import Hook
|
from flaschengeist.utils.hook import Hook
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ from flaschengeist.utils.hook import Hook
|
||||||
@Hook("plugins.installed")
|
@Hook("plugins.installed")
|
||||||
def install(ctx):
|
def install(ctx):
|
||||||
# Install database
|
# Install database
|
||||||
upgrade(alembic_migrations, revision="heads")
|
upgrade(alembic_migrations_path, revision="heads")
|
||||||
|
|
||||||
# Install plugins
|
# Install plugins
|
||||||
install_plugin_command(ctx, [], True)
|
install_plugin_command(ctx, [], True)
|
||||||
|
|
|
@ -8,14 +8,35 @@ import sqlalchemy
|
||||||
from typing import Union
|
from typing import Union
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from werkzeug.exceptions import NotFound
|
from werkzeug.exceptions import NotFound
|
||||||
|
from sqlalchemy.exc import OperationalError
|
||||||
from importlib.metadata import entry_points
|
from importlib.metadata import entry_points
|
||||||
|
|
||||||
from .. import logger
|
from .. import logger
|
||||||
from ..database import db
|
from ..database import db
|
||||||
from ..utils import Hook
|
from ..utils.hook import Hook
|
||||||
from ..models import Plugin, PluginSetting, Notification
|
from ..models import Plugin, PluginSetting, Notification
|
||||||
|
|
||||||
|
|
||||||
|
def get_enabled_plugins():
|
||||||
|
try:
|
||||||
|
enabled_plugins = Plugin.query.filter(Plugin.enabled == True).all()
|
||||||
|
except OperationalError as e:
|
||||||
|
|
||||||
|
class PluginStub:
|
||||||
|
def __init__(self, name) -> None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
logger.error("Could not connect to database or database not initialized! No plugins enabled!")
|
||||||
|
logger.debug("Can not query enabled plugins", exc_info=True)
|
||||||
|
enabled_plugins = [
|
||||||
|
PluginStub("auth"),
|
||||||
|
PluginStub("roles"),
|
||||||
|
PluginStub("users"),
|
||||||
|
PluginStub("scheduler"),
|
||||||
|
]
|
||||||
|
return enabled_plugins
|
||||||
|
|
||||||
|
|
||||||
def get_setting(plugin_id: str, name: str, **kwargs):
|
def get_setting(plugin_id: str, name: str, **kwargs):
|
||||||
"""Get plugin setting from database
|
"""Get plugin setting from database
|
||||||
|
|
||||||
|
@ -29,9 +50,7 @@ def get_setting(plugin_id: str, name: str, **kwargs):
|
||||||
`KeyError` if no such setting exists in the database
|
`KeyError` if no such setting exists in the database
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
setting = (
|
setting = PluginSetting.query.filter(PluginSetting.plugin == plugin_id).filter(PluginSetting.name == name).one()
|
||||||
PluginSetting.query.filter(PluginSetting.plugin == plugin_id).filter(PluginSetting.name == name).one()
|
|
||||||
)
|
|
||||||
return setting.value
|
return setting.value
|
||||||
except sqlalchemy.orm.exc.NoResultFound:
|
except sqlalchemy.orm.exc.NoResultFound:
|
||||||
if "default" in kwargs:
|
if "default" in kwargs:
|
||||||
|
@ -49,9 +68,7 @@ def set_setting(plugin_id: str, name: str, value):
|
||||||
value: Value to be stored
|
value: Value to be stored
|
||||||
"""
|
"""
|
||||||
setting = (
|
setting = (
|
||||||
PluginSetting.query.filter(PluginSetting.plugin == plugin_id)
|
PluginSetting.query.filter(PluginSetting.plugin == plugin_id).filter(PluginSetting.name == name).one_or_none()
|
||||||
.filter(PluginSetting.name == name)
|
|
||||||
.one_or_none()
|
|
||||||
)
|
)
|
||||||
if setting is not None:
|
if setting is not None:
|
||||||
if value is None:
|
if value is None:
|
||||||
|
|
|
@ -4,7 +4,9 @@ from flask_sqlalchemy import SQLAlchemy
|
||||||
from importlib.metadata import EntryPoint, entry_points, distribution
|
from importlib.metadata import EntryPoint, entry_points, distribution
|
||||||
from sqlalchemy import MetaData
|
from sqlalchemy import MetaData
|
||||||
|
|
||||||
|
from flaschengeist.alembic import alembic_script_path
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
from flaschengeist.controller import pluginController
|
||||||
|
|
||||||
# https://alembic.sqlalchemy.org/en/latest/naming.html
|
# https://alembic.sqlalchemy.org/en/latest/naming.html
|
||||||
metadata = MetaData(
|
metadata = MetaData(
|
||||||
|
@ -31,25 +33,26 @@ def configure_alembic(config: Config):
|
||||||
uninstall can break the alembic version management.
|
uninstall can break the alembic version management.
|
||||||
"""
|
"""
|
||||||
# Set main script location
|
# Set main script location
|
||||||
config.set_main_option(
|
config.set_main_option("script_location", alembic_script_path)
|
||||||
"script_location", str(distribution("flaschengeist").locate_file("") / "flaschengeist" / "alembic")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Set Flaschengeist's migrations
|
# Set Flaschengeist's migrations
|
||||||
migrations = [config.get_main_option("script_location") + "/migrations"]
|
migrations = [config.get_main_option("script_location") + "/migrations"]
|
||||||
|
|
||||||
# Gather all migration paths
|
# Gather all migration paths
|
||||||
ep: EntryPoint
|
all_plugins = entry_points(group="flaschengeist.plugins")
|
||||||
for ep in entry_points(group="flaschengeist.plugins"):
|
for plugin in pluginController.get_enabled_plugins():
|
||||||
|
entry_point = all_plugins.select(name=plugin.name)
|
||||||
|
if not entry_point:
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
directory = ep.dist.locate_file("")
|
directory = entry_point.dist.locate_file("")
|
||||||
for loc in ep.module.split(".") + ["migrations"]:
|
for loc in entry_point.module.split(".") + ["migrations"]:
|
||||||
directory /= loc
|
directory /= loc
|
||||||
if directory.exists():
|
if directory.exists():
|
||||||
logger.debug(f"Adding migration version path {directory}")
|
logger.debug(f"Adding migration version path {directory}")
|
||||||
migrations.append(str(directory.resolve()))
|
migrations.append(str(directory.resolve()))
|
||||||
except:
|
except:
|
||||||
logger.warning(f"Could not load migrations of plugin {ep.name} for database migration.")
|
logger.warning(f"Could not load migrations of plugin {plugin.name} for database migration.")
|
||||||
logger.debug("Plugin loading failed", exc_info=True)
|
logger.debug("Plugin loading failed", exc_info=True)
|
||||||
|
|
||||||
# write back seperator (we changed it if neither seperator nor locations were specified)
|
# write back seperator (we changed it if neither seperator nor locations were specified)
|
||||||
|
|
|
@ -17,6 +17,7 @@ role_permission_association_table = db.Table(
|
||||||
db.Column("permission_id", Serial, db.ForeignKey("permission.id")),
|
db.Column("permission_id", Serial, db.ForeignKey("permission.id")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Permission(db.Model, ModelSerializeMixin):
|
class Permission(db.Model, ModelSerializeMixin):
|
||||||
__tablename__ = "permission"
|
__tablename__ = "permission"
|
||||||
name: str = db.Column(db.String(30), unique=True)
|
name: str = db.Column(db.String(30), unique=True)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from flask import Blueprint
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
from flaschengeist.config import config
|
||||||
from flaschengeist.plugins import BasePlugin
|
from flaschengeist.plugins import BasePlugin
|
||||||
from flaschengeist.utils.HTTP import no_content
|
from flaschengeist.utils.HTTP import no_content
|
||||||
|
|
||||||
|
@ -38,8 +39,8 @@ def scheduled(id: str, replace=False, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
class SchedulerPlugin(BasePlugin):
|
class SchedulerPlugin(BasePlugin):
|
||||||
def __init__(self, entry_point, config=None):
|
def __init__(self, entry_point):
|
||||||
super().__init__(entry_point, config)
|
super().__init__(entry_point)
|
||||||
self.blueprint = Blueprint(self.name, __name__)
|
self.blueprint = Blueprint(self.name, __name__)
|
||||||
|
|
||||||
def __view_func():
|
def __view_func():
|
||||||
|
@ -52,9 +53,9 @@ class SchedulerPlugin(BasePlugin):
|
||||||
except:
|
except:
|
||||||
logger.error("Error while executing scheduled tasks!", exc_info=True)
|
logger.error("Error while executing scheduled tasks!", exc_info=True)
|
||||||
|
|
||||||
cron = None if config is None else config.get("cron", "passive_web").lower()
|
cron = config.get("scheduler", {}).get("cron", "passive_web").lower()
|
||||||
|
|
||||||
if cron is None or cron == "passive_web":
|
if cron == "passive_web":
|
||||||
self.blueprint.teardown_app_request(__passiv_func)
|
self.blueprint.teardown_app_request(__passiv_func)
|
||||||
elif cron == "active_web":
|
elif cron == "active_web":
|
||||||
self.blueprint.add_url_rule("/cron", view_func=__view_func)
|
self.blueprint.add_url_rule("/cron", view_func=__view_func)
|
||||||
|
|
Loading…
Reference in New Issue