fix: activate plugins on startup + register routes + seed admin script

- lifespan: load active plugins from DB, call on_activate, register routes
- Fixes: Calendar/Mail/DMS/Tags/Permissions routes returning 404
- scripts/seed_admin.py: creates default tenant + admin user
This commit is contained in:
leocrm-bot
2026-07-02 13:02:38 +02:00
parent f477efc366
commit 5dc4d6d4c0
2 changed files with 117 additions and 0 deletions
+36
View File
@@ -11,8 +11,12 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware
import importlib
import logging
import os import os
logger = logging.getLogger(__name__)
from app.config import get_settings from app.config import get_settings
from app.core.db import close_engine, get_engine from app.core.db import close_engine, get_engine
from app.core.middleware import CSRFMiddleware from app.core.middleware import CSRFMiddleware
@@ -91,6 +95,38 @@ 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
from sqlalchemy import select as sa_select
from sqlalchemy.ext.asyncio import async_sessionmaker
from app.models.plugin import Plugin as PluginModel
from app.core.event_bus import get_event_bus
event_bus = get_event_bus()
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)
if plugin is None:
continue
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}")
except Exception as exc:
logger.warning(f"Failed to activate plugin {plugin_record.name}: {exc}")
await db.commit()
yield yield
await close_engine() await close_engine()
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Seed a default tenant and admin user for LeoCRM.
Usage: python scripts/seed_admin.py
Creates:
- Tenant: "Default Org" (slug: default)
- Admin user: admin@leocrm.local / Admin123!
If tenant or user already exists, skips creation.
"""
import asyncio
import sys
import os
# Ensure app is importable
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.core.db import get_engine, close_engine
from app.core.auth import hash_password
from app.models.tenant import Tenant
from app.models.user import User, UserTenant
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import async_sessionmaker
async def seed():
engine = get_engine()
async_session = async_sessionmaker(engine, expire_on_commit=False)
async with async_session() as db: # type: AsyncSession
# Check if default tenant exists
result = await db.execute(select(Tenant).where(Tenant.slug == "default"))
tenant = result.scalar_one_or_none()
if tenant is None:
tenant = Tenant(name="Default Org", slug="default")
db.add(tenant)
await db.flush()
print(f"Created tenant: {tenant.name} (slug: {tenant.slug}, id: {tenant.id})")
else:
print(f"Tenant exists: {tenant.name} (id: {tenant.id})")
# Check if admin user exists
result = await db.execute(select(User).where(User.email == "admin@leocrm.local"))
user = result.scalar_one_or_none()
if user is None:
user = User(
tenant_id=tenant.id,
email="admin@leocrm.local",
name="Administrator",
password_hash=hash_password("Admin123!"),
role="admin",
is_active=True,
preferences={},
)
db.add(user)
await db.flush()
# Link user to tenant
ut = UserTenant(user_id=user.id, tenant_id=tenant.id, is_default=True)
db.add(ut)
await db.flush()
print(f"Created admin user: {user.email} (id: {user.id})")
else:
print(f"Admin user exists: {user.email} (id: {user.id})")
await db.commit()
print("\nSeed complete!")
print(f" Login URL: https://crm.media-on.de/login")
print(f" Email: admin@leocrm.local")
print(f" Password: Admin123!")
await close_engine()
if __name__ == "__main__":
asyncio.run(seed())