Compare commits
No commits in common. "348adefb7c5375f403501cae60ac2005d9629a86" and "653c1c584cfd69f651355b83c2c3b8d3273e316f" have entirely different histories.
348adefb7c
...
653c1c584c
|
@ -36,21 +36,18 @@ class CustomJSONEncoder(JSONEncoder):
|
||||||
|
|
||||||
|
|
||||||
def __load_plugins(app):
|
def __load_plugins(app):
|
||||||
logger.debug("Search for plugins")
|
logger.info("Search for plugins")
|
||||||
|
|
||||||
app.config["FG_PLUGINS"] = {}
|
app.config["FG_PLUGINS"] = {}
|
||||||
for entry_point in pkg_resources.iter_entry_points("flaschengeist.plugins"):
|
for entry_point in pkg_resources.iter_entry_points("flaschengeist.plugins"):
|
||||||
logger.debug(f"Found plugin: >{entry_point.name}<")
|
logger.debug("Found plugin: >{}<".format(entry_point.name))
|
||||||
|
plugin = None
|
||||||
if entry_point.name == config["FLASCHENGEIST"]["auth"] or (
|
if entry_point.name in config and config[entry_point.name].get("enabled", False):
|
||||||
entry_point.name in config and config[entry_point.name].get("enabled", False)
|
|
||||||
):
|
|
||||||
logger.debug(f"Load plugin {entry_point.name}")
|
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"Load plugin {entry_point.name}")
|
||||||
plugin = entry_point.load()
|
plugin = entry_point.load()
|
||||||
if not hasattr(plugin, "name"):
|
if not hasattr(plugin, "name"):
|
||||||
setattr(plugin, "name", entry_point.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:
|
if hasattr(plugin, "blueprint") and plugin.blueprint is not None:
|
||||||
app.register_blueprint(plugin.blueprint)
|
app.register_blueprint(plugin.blueprint)
|
||||||
except:
|
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.",
|
f"Plugin {entry_point.name} was enabled, but could not be loaded due to an error.",
|
||||||
exc_info=True,
|
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
|
del plugin
|
||||||
continue
|
continue
|
||||||
else:
|
if isinstance(plugin, AuthPlugin):
|
||||||
logger.info(f"Using authentication plugin: {entry_point.name}")
|
logger.debug(f"Found authentication plugin: {entry_point.name}")
|
||||||
|
if entry_point.name == config["FLASCHENGEIST"]["auth"]:
|
||||||
app.config["FG_AUTH_BACKEND"] = plugin
|
app.config["FG_AUTH_BACKEND"] = plugin
|
||||||
else:
|
else:
|
||||||
logger.info(f"Using plugin: {entry_point.name}")
|
del plugin
|
||||||
|
continue
|
||||||
|
if plugin:
|
||||||
app.config["FG_PLUGINS"][entry_point.name] = plugin
|
app.config["FG_PLUGINS"][entry_point.name] = plugin
|
||||||
if "FG_AUTH_BACKEND" not in app.config:
|
if "FG_AUTH_BACKEND" not in app.config:
|
||||||
logger.error("No authentication plugin configured or authentication plugin not found")
|
logger.error("No authentication plugin configured or authentication plugin not found")
|
||||||
|
|
|
@ -77,7 +77,6 @@ def configure_app(app, test_config=None):
|
||||||
"auth": {"enabled": True},
|
"auth": {"enabled": True},
|
||||||
"roles": {"enabled": True},
|
"roles": {"enabled": True},
|
||||||
"users": {"enabled": True},
|
"users": {"enabled": True},
|
||||||
"scheduler": {"enabled": True},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,7 @@ root = "/api"
|
||||||
# Set secret key
|
# Set secret key
|
||||||
secret_key = "V3ryS3cr3t"
|
secret_key = "V3ryS3cr3t"
|
||||||
# Domain used by frontend
|
# Domain used by frontend
|
||||||
|
#domain = "flaschengeist.local"
|
||||||
[scheduler]
|
|
||||||
# Possible values are: "passive_web" (default), "active_web" and "system"
|
|
||||||
# See documentation
|
|
||||||
# cron = "passive_web"
|
|
||||||
|
|
||||||
[LOGGING]
|
[LOGGING]
|
||||||
# You can override all settings from the logging.toml here
|
# You can override all settings from the logging.toml here
|
||||||
|
@ -48,8 +44,12 @@ allowed_mimetypes = [
|
||||||
"image/webp"
|
"image/webp"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[auth_plain]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
[auth_ldap]
|
[auth_ldap]
|
||||||
# Full documentation https://flaschengeist.dev/Flaschengeist/flaschengeist/wiki/plugins_auth_ldap
|
# Full documentation https://flaschengeist.dev/Flaschengeist/flaschengeist/wiki/plugins_auth_ldap
|
||||||
|
enabled = false
|
||||||
# host = "localhost"
|
# host = "localhost"
|
||||||
# port = 389
|
# port = 389
|
||||||
# base_dn = "dc=example,dc=com"
|
# base_dn = "dc=example,dc=com"
|
||||||
|
|
|
@ -96,9 +96,6 @@ class Plugin:
|
||||||
.one_or_none()
|
.one_or_none()
|
||||||
)
|
)
|
||||||
if setting is not None:
|
if setting is not None:
|
||||||
if value is None:
|
|
||||||
db.session.delete(setting)
|
|
||||||
else:
|
|
||||||
setting.value = value
|
setting.value = value
|
||||||
else:
|
else:
|
||||||
db.session.add(_PluginSetting(plugin=self.name, name=name, value=value))
|
db.session.add(_PluginSetting(plugin=self.name, name=name, value=value))
|
||||||
|
|
|
@ -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)
|
|
|
@ -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()
|
13
readme.md
13
readme.md
|
@ -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!
|
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
|
### Database installation
|
||||||
The user needs to have full permissions to the database.
|
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):
|
If not you need to create user and database manually do (or similar on Windows):
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -68,7 +68,6 @@ setup(
|
||||||
"balance = flaschengeist.plugins.balance:BalancePlugin",
|
"balance = flaschengeist.plugins.balance:BalancePlugin",
|
||||||
"mail = flaschengeist.plugins.message_mail:MailMessagePlugin",
|
"mail = flaschengeist.plugins.message_mail:MailMessagePlugin",
|
||||||
"pricelist = flaschengeist.plugins.pricelist:PriceListPlugin",
|
"pricelist = flaschengeist.plugins.pricelist:PriceListPlugin",
|
||||||
"scheduler = flaschengeist.plugins.scheduler:SchedulerPlugin",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
cmdclass={
|
cmdclass={
|
||||||
|
|
Loading…
Reference in New Issue