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:
+35
-11
@@ -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()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user