Compare commits

..

No commits in common. "348adefb7c5375f403501cae60ac2005d9629a86" and "653c1c584cfd69f651355b83c2c3b8d3273e316f" have entirely different histories.

8 changed files with 38 additions and 126 deletions

View File

@ -36,21 +36,18 @@ class CustomJSONEncoder(JSONEncoder):
def __load_plugins(app):
logger.debug("Search for plugins")
logger.info("Search for plugins")
app.config["FG_PLUGINS"] = {}
for entry_point in pkg_resources.iter_entry_points("flaschengeist.plugins"):
logger.debug(f"Found plugin: >{entry_point.name}<")
if entry_point.name == config["FLASCHENGEIST"]["auth"] or (
entry_point.name in config and config[entry_point.name].get("enabled", False)
):
logger.debug(f"Load plugin {entry_point.name}")
logger.debug("Found plugin: >{}<".format(entry_point.name))
plugin = None
if entry_point.name in config and config[entry_point.name].get("enabled", False):
try:
logger.info(f"Load plugin {entry_point.name}")
plugin = entry_point.load()
if not hasattr(plugin, "name"):
setattr(plugin, "name", entry_point.name)
plugin = plugin(config.get(entry_point.name, {}))
plugin = plugin(config[entry_point.name])
if hasattr(plugin, "blueprint") and plugin.blueprint is not None:
app.register_blueprint(plugin.blueprint)
except:
@ -58,17 +55,16 @@ def __load_plugins(app):
f"Plugin {entry_point.name} was enabled, but could not be loaded due to an error.",
exc_info=True,
)
continue
if isinstance(plugin, AuthPlugin):
if entry_point.name != config["FLASCHENGEIST"]["auth"]:
logger.debug(f"Unload not configured AuthPlugin {entry_point.name}")
del plugin
continue
else:
logger.info(f"Using authentication plugin: {entry_point.name}")
if isinstance(plugin, AuthPlugin):
logger.debug(f"Found authentication plugin: {entry_point.name}")
if entry_point.name == config["FLASCHENGEIST"]["auth"]:
app.config["FG_AUTH_BACKEND"] = plugin
else:
logger.info(f"Using plugin: {entry_point.name}")
del plugin
continue
if plugin:
app.config["FG_PLUGINS"][entry_point.name] = plugin
if "FG_AUTH_BACKEND" not in app.config:
logger.error("No authentication plugin configured or authentication plugin not found")

View File

@ -77,7 +77,6 @@ def configure_app(app, test_config=None):
"auth": {"enabled": True},
"roles": {"enabled": True},
"users": {"enabled": True},
"scheduler": {"enabled": True},
},
)

View File

@ -11,11 +11,7 @@ root = "/api"
# Set secret key
secret_key = "V3ryS3cr3t"
# Domain used by frontend
[scheduler]
# Possible values are: "passive_web" (default), "active_web" and "system"
# See documentation
# cron = "passive_web"
#domain = "flaschengeist.local"
[LOGGING]
# You can override all settings from the logging.toml here
@ -48,8 +44,12 @@ allowed_mimetypes = [
"image/webp"
]
[auth_plain]
enabled = true
[auth_ldap]
# Full documentation https://flaschengeist.dev/Flaschengeist/flaschengeist/wiki/plugins_auth_ldap
enabled = false
# host = "localhost"
# port = 389
# base_dn = "dc=example,dc=com"

View File

@ -96,9 +96,6 @@ class Plugin:
.one_or_none()
)
if setting is not None:
if value is None:
db.session.delete(setting)
else:
setting.value = value
else:
db.session.add(_PluginSetting(plugin=self.name, name=name, value=value))

View File

@ -1,83 +0,0 @@
import pkg_resources
from datetime import datetime, timedelta
from flask import Blueprint
from flaschengeist import logger
from flaschengeist.utils.HTTP import no_content
from . import Plugin
class __Task:
def __init__(self, function, **kwags):
self.function = function
self.interval = timedelta(**kwags)
_scheduled_tasks: dict[__Task] = dict()
def scheduled(id: str, replace=False, **kwargs):
"""
kwargs: days, hours, minutes
"""
def real_decorator(function):
if id not in _scheduled_tasks or replace:
logger.info(f"Registered task: {id}")
_scheduled_tasks[id] = __Task(function, **kwargs)
else:
logger.debug(f"Skipping already registered task: {id}")
return function
return real_decorator
class SchedulerPlugin(Plugin):
id = "dev.flaschengeist.scheduler"
name = "scheduler"
blueprint = Blueprint(name, __name__)
def __init__(self, config=None):
"""Constructor called by create_app
Args:
config: Dict configuration containing the plugin section
"""
def __view_func():
self.run_tasks()
return no_content()
def __passiv_func(v):
try:
self.run_tasks()
except:
logger.error("Error while executing scheduled tasks!", exc_info=True)
self.version = pkg_resources.get_distribution(self.__module__.split(".")[0]).version
cron = None if config is None else config.get("cron", "passive_web").lower()
if cron is None or cron == "passive_web":
self.blueprint.teardown_app_request(__passiv_func)
elif cron == "active_web":
self.blueprint.add_url_rule("/cron", view_func=__view_func)
def run_tasks(self):
changed = False
now = datetime.now()
status = self.get_setting("status", default=dict())
for id, task in _scheduled_tasks.items():
last_run = status.setdefault(id, now)
if last_run + task.interval <= now:
logger.debug(
f"Run task {id}, was scheduled for {last_run + task.interval}, next iteration: {now + task.interval}"
)
task.function()
changed = True
if changed:
# Remove not registered tasks
for id in status.keys():
if id not in _scheduled_tasks.keys():
del status[id]
self.set_setting("status", status)

View File

@ -0,0 +1,17 @@
from flask import current_app
from flaschengeist.utils.HTTP import no_content
_scheduled = set()
def scheduled(func):
_scheduled.add(func)
return func
@current_app.route("/cron")
def __run_scheduled():
for function in _scheduled:
function()
return no_content()

View File

@ -39,19 +39,6 @@ Configuration is done within the a `flaschengeist.toml`file, you can copy the on
Uncomment and change at least all the database parameters!
#### CRON
Some functionality used by some plugins rely on regular updates,
but as flaschengeists works as an WSGI app it can not controll when it gets called.
So you have to configure one of the following options to call flaschengeists CRON tasks:
1. Passive Web-CRON: Every time an users calls flaschengeist a task is scheduled (**NOT RECOMMENDED**)
- Pros: No external configuration needed
- Cons: Slower user experience, no guaranteed execution time of tasks
2. Active Web-CRON: You configure a webworker to call `<flaschengeist>/cron`
- Pros: Guaranteed execution interval, no impact on user experience (at least if you do not limit wsgi worker threads)
- Cons: Uses one of the webserver threads while executing
### Database installation
The user needs to have full permissions to the database.
If not you need to create user and database manually do (or similar on Windows):

View File

@ -68,7 +68,6 @@ setup(
"balance = flaschengeist.plugins.balance:BalancePlugin",
"mail = flaschengeist.plugins.message_mail:MailMessagePlugin",
"pricelist = flaschengeist.plugins.pricelist:PriceListPlugin",
"scheduler = flaschengeist.plugins.scheduler:SchedulerPlugin",
],
},
cmdclass={