Compare commits
	
		
			3 Commits
		
	
	
		
			e9fbce0da7
			...
			2880076705
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 2880076705 | |
|  | fa503fe142 | |
|  | f1d6b6a2f2 | 
|  | @ -50,10 +50,16 @@ If not you need to create user and database manually do (or similar on Windows): | |||
|     ) | sudo mysql | ||||
| 
 | ||||
| Then you can install the database tables, this will update all tables from core + all enabled plugins. | ||||
| *Hint:* The same command can be later used to upgrade the database after plugins or core are updated. | ||||
| And also install all enabled plugins: | ||||
| 
 | ||||
|     $ flaschengeist install | ||||
| 
 | ||||
| *Hint:* To only install the database tables, or upgrade the database after plugins or core are updated later | ||||
| you can use this command: | ||||
| 
 | ||||
|     $ flaschengeist db upgrade heads | ||||
| 
 | ||||
| 
 | ||||
| ## Plugins | ||||
| To only upgrade one plugin (for example the `events` plugin): | ||||
| 
 | ||||
|  | @ -88,6 +94,7 @@ with the difference of the main logger will be forced to output to `stderr` and | |||
| of the CLI will override the logging level you have configured for the main logger. | ||||
| 
 | ||||
|     $ flaschengeist run | ||||
| 
 | ||||
| or with debug messages: | ||||
| 
 | ||||
|     $ flaschengeist run --debug | ||||
|  |  | |||
|  | @ -0,0 +1,4 @@ | |||
| from pathlib import Path | ||||
| 
 | ||||
| 
 | ||||
| alembic_migrations = str(Path(__file__).resolve().parent / "migrations") | ||||
|  | @ -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( | ||||
|  |  | |||
|  | @ -88,12 +88,14 @@ def main(*args, **kwargs): | |||
|     from .export_cmd import export | ||||
|     from .docs_cmd import docs | ||||
|     from .run_cmd import run | ||||
|     from .install_cmd import install | ||||
| 
 | ||||
|     # Override logging level | ||||
|     environ.setdefault("FG_LOGGING", logging.getLevelName(LOGGING_MAX)) | ||||
| 
 | ||||
|     cli.add_command(export) | ||||
|     cli.add_command(docs) | ||||
|     cli.add_command(install) | ||||
|     cli.add_command(plugin) | ||||
|     cli.add_command(run) | ||||
|     cli(*args, **kwargs) | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import click | ||||
| from importlib_metadata import entry_points | ||||
| 
 | ||||
| 
 | ||||
| @click.command() | ||||
|  | @ -8,14 +9,21 @@ import click | |||
| @click.option("--no-core", help="Skip models / types from flaschengeist core", is_flag=True) | ||||
| def export(namespace, output, no_core, plugin): | ||||
|     from flaschengeist import logger, models | ||||
|     from flaschengeist.app import get_plugins | ||||
|     from .InterfaceGenerator import InterfaceGenerator | ||||
| 
 | ||||
|     gen = InterfaceGenerator(namespace, output, logger) | ||||
|     if not no_core: | ||||
|         gen.run(models) | ||||
|     if plugin: | ||||
|         for plugin_class in get_plugins(): | ||||
|             if (len(plugin) == 0 or plugin_class.id in plugin) and plugin_class.models is not None: | ||||
|                 gen.run(plugin_class.models) | ||||
|         for entry_point in entry_points(group="flaschengeist.plugins"): | ||||
|             if len(plugin) == 0 or entry_point.name in plugin: | ||||
|                 try: | ||||
|                     plugin = entry_point.load() | ||||
|                     gen.run(plugin.models) | ||||
|                 except: | ||||
|                     logger.error( | ||||
|                         f"Plugin {entry_point.name} could not be loaded due to an error.", | ||||
|                         exc_info=True, | ||||
|                     ) | ||||
|                     continue | ||||
|     gen.write() | ||||
|  |  | |||
|  | @ -0,0 +1,20 @@ | |||
| import click | ||||
| from click.decorators import pass_context | ||||
| from flask.cli import with_appcontext | ||||
| from flask_migrate import upgrade | ||||
| 
 | ||||
| from flaschengeist.alembic import alembic_migrations | ||||
| from flaschengeist.cli.plugin_cmd import install_plugin_command | ||||
| from flaschengeist.utils.hook import Hook | ||||
| 
 | ||||
| 
 | ||||
| @click.command() | ||||
| @with_appcontext | ||||
| @pass_context | ||||
| @Hook("plugins.installed") | ||||
| def install(ctx): | ||||
|     # Install database | ||||
|     upgrade(alembic_migrations, revision="heads") | ||||
| 
 | ||||
|     # Install plugins | ||||
|     install_plugin_command(ctx, [], True) | ||||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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") | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,7 +13,6 @@ from flaschengeist.controller import sessionController, userController | |||
| 
 | ||||
| 
 | ||||
| class AuthRoutePlugin(Plugin): | ||||
|     id = "dev.flaschengeist.auth" | ||||
|     blueprint = Blueprint("auth", __name__) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -104,7 +104,7 @@ def frontend(userid, current_session): | |||
|         raise Forbidden | ||||
| 
 | ||||
|     if request.method == "POST": | ||||
|         if request.content_length > 1024**2: | ||||
|         if request.content_length > 1024 ** 2: | ||||
|             raise BadRequest | ||||
|         current_session.user_.set_attribute("frontend", request.get_json()) | ||||
|         return no_content() | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue