From 07bf6ce4455a666b6bdc05ca9ab1f4e9859df018 Mon Sep 17 00:00:00 2001 From: leocrm-bot Date: Thu, 2 Jul 2026 13:32:48 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20auto-install=20discovered=20builtin=20pl?= =?UTF-8?q?ugins=20on=20startup=20=E2=80=94=20create=20DB=20records,=20run?= =?UTF-8?q?=20migrations,=20activate,=20register=20routes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Lifespan creates PluginModel records for discovered plugins if missing - Runs plugin migrations automatically - Activates plugins and registers routes in FastAPI - Fixes: Calendar/Mail/DMS/Tags/Permissions 404 after restart --- app/main.py | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/app/main.py b/app/main.py index e05d0b9..59408a3 100644 --- a/app/main.py +++ b/app/main.py @@ -95,7 +95,7 @@ async def lifespan(app: FastAPI): registry.initialize(get_engine(), app) registry.discover_builtins() - # Activate plugins that are marked active in the database + # Auto-install and activate all discovered builtin plugins from sqlalchemy import select as sa_select from sqlalchemy.ext.asyncio import async_sessionmaker from app.models.plugin import Plugin as PluginModel @@ -105,25 +105,49 @@ async def lifespan(app: FastAPI): async_session = async_sessionmaker(get_engine(), expire_on_commit=False) async with async_session() as db: - result = await db.execute( - sa_select(PluginModel).where(PluginModel.active == True) # noqa: E712 - ) - active_plugins = result.scalars().all() - - for plugin_record in active_plugins: - plugin = registry.get_plugin(plugin_record.name) + for name in registry._plugins: + plugin = registry.get_plugin(name) if plugin is None: continue + + # Check if plugin already in DB + result = await db.execute( + sa_select(PluginModel).where(PluginModel.name == name) + ) + plugin_record = result.scalar_one_or_none() + + if plugin_record is None: + # Create DB record for this builtin plugin + plugin_record = PluginModel( + name=name, + version=plugin.manifest.version, + status="installed", + active=True, + ) + db.add(plugin_record) + await db.flush() + logger.info(f"Created plugin record: {name}") + + # Run migrations if not yet applied + if plugin.manifest.migrations: + try: + await registry.migration_runner.run_all_migrations( + db, name, plugin.manifest.migrations + ) + except Exception as exc: + logger.warning(f"Migration for {name}: {exc}") + + # Activate plugin and register routes try: await plugin.on_activate(db, container, event_bus) - # Register plugin routes into FastAPI app for route_def in plugin.manifest.routes: router_module = importlib.import_module(route_def.module) router = getattr(router_module, route_def.router_attr) app.include_router(router) - logger.info(f"Activated plugin: {plugin_record.name}") + plugin_record.active = True + logger.info(f"Activated plugin: {name} ({len(plugin.manifest.routes)} routes)") except Exception as exc: - logger.warning(f"Failed to activate plugin {plugin_record.name}: {exc}") + logger.warning(f"Failed to activate plugin {name}: {exc}") await db.commit()