fix: auto-install discovered builtin plugins on startup — create DB records, run migrations, activate, register routes

- 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
This commit is contained in:
leocrm-bot
2026-07-02 13:32:48 +02:00
parent 5dc4d6d4c0
commit 07bf6ce445
+35 -11
View File
@@ -95,7 +95,7 @@ async def lifespan(app: FastAPI):
registry.initialize(get_engine(), app) registry.initialize(get_engine(), app)
registry.discover_builtins() 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 import select as sa_select
from sqlalchemy.ext.asyncio import async_sessionmaker from sqlalchemy.ext.asyncio import async_sessionmaker
from app.models.plugin import Plugin as PluginModel 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_session = async_sessionmaker(get_engine(), expire_on_commit=False)
async with async_session() as db: async with async_session() as db:
result = await db.execute( for name in registry._plugins:
sa_select(PluginModel).where(PluginModel.active == True) # noqa: E712 plugin = registry.get_plugin(name)
)
active_plugins = result.scalars().all()
for plugin_record in active_plugins:
plugin = registry.get_plugin(plugin_record.name)
if plugin is None: if plugin is None:
continue 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: try:
await plugin.on_activate(db, container, event_bus) await plugin.on_activate(db, container, event_bus)
# Register plugin routes into FastAPI app
for route_def in plugin.manifest.routes: for route_def in plugin.manifest.routes:
router_module = importlib.import_module(route_def.module) router_module = importlib.import_module(route_def.module)
router = getattr(router_module, route_def.router_attr) router = getattr(router_module, route_def.router_attr)
app.include_router(router) 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: 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() await db.commit()