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