diff --git a/flaschengeist/alembic/migrations/255b93b6beed_flaschengeist_initial.py b/flaschengeist/alembic/migrations/255b93b6beed_flaschengeist_initial.py index b7deac6..b18a71e 100644 --- a/flaschengeist/alembic/migrations/255b93b6beed_flaschengeist_initial.py +++ b/flaschengeist/alembic/migrations/255b93b6beed_flaschengeist_initial.py @@ -18,14 +18,21 @@ depends_on = None def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "plugin_setting", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("plugin", sa.String(length=127), nullable=True), + sa.Column("name", sa.String(length=127), nullable=False), + sa.Column("value", sa.PickleType(protocol=4), nullable=True), + sa.PrimaryKeyConstraint("id", name=op.f("pk_plugin_setting")), + ) op.create_table( "image", sa.Column("id", flaschengeist.models.Serial(), nullable=False), - sa.Column("filename_", sa.String(length=127), nullable=False), - sa.Column("mimetype_", sa.String(length=30), nullable=False), - sa.Column("thumbnail_", sa.String(length=127), nullable=True), - sa.Column("path_", sa.String(length=127), nullable=True), + sa.Column("filename", sa.String(length=255), nullable=False), + sa.Column("mimetype", sa.String(length=127), nullable=False), + sa.Column("thumbnail", sa.String(length=255), nullable=True), + sa.Column("path", sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint("id", name=op.f("pk_image")), ) op.create_table( diff --git a/flaschengeist/cli/plugin_cmd.py b/flaschengeist/cli/plugin_cmd.py index bb0dd98..c2b5274 100644 --- a/flaschengeist/cli/plugin_cmd.py +++ b/flaschengeist/cli/plugin_cmd.py @@ -4,7 +4,9 @@ from flask import current_app from flask.cli import with_appcontext from importlib_metadata import EntryPoint, entry_points +from flaschengeist.database import db from flaschengeist.config import config +from flaschengeist.models.user import Permission @click.group() @@ -12,6 +14,35 @@ def plugin(): pass +def install_plugin_command(ctx, plugin, all): + """Install one or more plugins""" + if not all and len(plugin) == 0: + ctx.fail("At least one plugin must be specified, or use `--all` flag.") + + if all: + plugins = current_app.config["FG_PLUGINS"] + else: + try: + plugins = {plugin_name: current_app.config["FG_PLUGINS"][plugin_name] for plugin_name in plugin} + except KeyError as e: + ctx.fail(f"Invalid plugin name, could not find >{e.args[0]}<") + + for name, plugin in plugins.items(): + click.echo(f"Installing {name}{'.'*(20-len(name))}", nl=False) + # Install permissions + if plugin.permissions: + cur_perm = set(x.name for x in Permission.query.filter(Permission.name.in_(plugin.permissions)).all()) + all_perm = set(plugin.permissions) + + add = all_perm - cur_perm + if add: + db.session.bulk_save_objects([Permission(name=x) for x in all_perm]) + db.session.commit() + # Custom installation steps + plugin.install() + click.secho(" ok", fg="green") + + @plugin.command() @click.argument("plugin", nargs=-1, type=str) @click.option("--all", help="Install all enabled plugins", is_flag=True) @@ -19,20 +50,7 @@ def plugin(): @pass_context def install(ctx, plugin, all): """Install one or more plugins""" - if not all and len(plugin) == 0: - ctx.fail("At least one plugin must be specified, or use `--all` flag.") - if all: - plugins = current_app.config["FG_PLUGINS"].values() - else: - try: - plugins = [current_app.config["FG_PLUGINS"][p] for p in plugin] - except KeyError as e: - ctx.fail(f"Invalid plugin ID, could not find >{e.args[0]}<") - for p in plugins: - name = p.id.split(".")[-1] - click.echo(f"Installing {name}{'.'*(20-len(name))}", nl=False) - p.install() - click.secho(" ok", fg="green") + return install_plugin_command(ctx, plugin, all) @plugin.command() @@ -44,14 +62,16 @@ def uninstall(ctx: click.Context, plugin): if len(plugin) == 0: ctx.fail("At least one plugin must be specified") + try: - plugins = [current_app.config["FG_PLUGINS"][p] for p in plugin] + plugins = {plugin_name: current_app.config["FG_PLUGINS"][plugin_name] for plugin_name in plugin} except KeyError as e: ctx.fail(f"Invalid plugin ID, could not find >{e.args[0]}<") + if ( click.prompt( "You are going to uninstall:\n\n" - f"\t{', '.join([p.id.split('.')[-1] for p in plugins])}\n\n" + f"\t{', '.join([plugin_name for plugin_name in plugins.keys()])}\n\n" "Are you sure?", default="n", show_choices=True, @@ -60,10 +80,9 @@ def uninstall(ctx: click.Context, plugin): != "y" ): ctx.exit() - for p in plugins: - name = p.id.split(".")[-1] + for name, plugin in plugins.items(): click.echo(f"Uninstalling {name}{'.'*(20-len(name))}", nl=False) - p.uninstall() + plugin.uninstall() click.secho(" ok", fg="green") @@ -78,7 +97,7 @@ def ls(enabled, no_header): return p.version plugins = entry_points(group="flaschengeist.plugins") - enabled_plugins = [key for key, value in config.items() if "enabled" in value] + [config["FLASCHENGEIST"]["auth"]] + enabled_plugins = [key for key, value in config.items() if "enabled" in value and value["enabled"]] + [config["FLASCHENGEIST"]["auth"]] loaded_plugins = current_app.config["FG_PLUGINS"].keys() if not no_header: diff --git a/flaschengeist/models/image.py b/flaschengeist/models/image.py index 4c963e7..9a97ea8 100644 --- a/flaschengeist/models/image.py +++ b/flaschengeist/models/image.py @@ -9,11 +9,11 @@ from ..database import db class Image(db.Model, ModelSerializeMixin): __tablename__ = "image" - id: int = db.Column("id", Serial, primary_key=True) - filename_: str = db.Column(db.String(127), nullable=False) - mimetype_: str = db.Column(db.String(30), nullable=False) - thumbnail_: str = db.Column(db.String(127)) - path_: str = db.Column(db.String(127)) + id: int = db.Column(Serial, primary_key=True) + filename_: str = db.Column("filename", db.String(255), nullable=False) + mimetype_: str = db.Column("mimetype", db.String(127), nullable=False) + thumbnail_: str = db.Column("thumbnail", db.String(255)) + path_: str = db.Column("path", db.String(255)) def open(self): return open(self.path_, "rb") diff --git a/flaschengeist/models/setting.py b/flaschengeist/models/setting.py index 277f36c..b090c3e 100644 --- a/flaschengeist/models/setting.py +++ b/flaschengeist/models/setting.py @@ -8,6 +8,6 @@ from ..database import db class _PluginSetting(db.Model): __tablename__ = "plugin_setting" id = db.Column("id", Serial, primary_key=True) - plugin: str = db.Column(db.String(30)) - name: str = db.Column(db.String(30), nullable=False) + plugin: str = db.Column(db.String(127)) + name: str = db.Column(db.String(127), nullable=False) value: Any = db.Column(db.PickleType(protocol=4)) diff --git a/flaschengeist/plugins/__init__.py b/flaschengeist/plugins/__init__.py index 369e117..13936e5 100644 --- a/flaschengeist/plugins/__init__.py +++ b/flaschengeist/plugins/__init__.py @@ -126,7 +126,8 @@ class Plugin: def install(self): """Installation routine - Is always called with Flask application context + Is always called with Flask application context, + it is called after the plugin permissions are installed. """ pass diff --git a/flaschengeist/plugins/auth/__init__.py b/flaschengeist/plugins/auth/__init__.py index 66d77be..afcc854 100644 --- a/flaschengeist/plugins/auth/__init__.py +++ b/flaschengeist/plugins/auth/__init__.py @@ -13,7 +13,6 @@ from flaschengeist.controller import sessionController, userController class AuthRoutePlugin(Plugin): - id = "dev.flaschengeist.auth" blueprint = Blueprint("auth", __name__) diff --git a/flaschengeist/plugins/auth_plain/__init__.py b/flaschengeist/plugins/auth_plain/__init__.py index e73b98c..44c27f7 100644 --- a/flaschengeist/plugins/auth_plain/__init__.py +++ b/flaschengeist/plugins/auth_plain/__init__.py @@ -14,12 +14,10 @@ from flaschengeist import logger class AuthPlain(AuthPlugin): - id = "auth_plain" - def install(self): plugins_installed(self.post_install) - def post_install(self, **kwargs): + def post_install(self, *args, **kwargs): if User.query.filter(User.deleted == False).count() == 0: logger.info("Installing admin user") role = Role.query.filter(Role.name == "Superuser").first()