feat(M4): System-Rueckbau — Dashboard-Inhalte als MiniApps, Core = reiner Host (#362)
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
- system_miniapps.py: audit_activity (audit:read, settings max_items) + system_metrics (settings:read) als Core-Apps in Registry - base.py-Fix: native Manifest-MiniApps reichen component durch (M1-Luecke) - contacts-Manifest: contacts_stats (ContactsStatsWidget, contacts:read, show_companies/show_persons) - Seed-Fix: nur renderbare Apps (component) landen im Dashboard-Layout - Frontend: ContactsStatsWidget, AuditActivityWidget, SystemMetricsWidget; MiniAppHost-Registry +3 - Dashboard.tsx = reiner Host (26 Z.); Page-Tests auf Pure-Host umgeschrieben - Tests: M4 7/7 (TDD rot->gruen), Backend-Regression 46/46, Vitest 22/22, tsc clean, build OK
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"""Core-owned system MiniApps (Phase M4).
|
||||
|
||||
Host-level MiniApps that are not owned by a single plugin: audit activity
|
||||
feed and system metrics. They register in the universal registry with
|
||||
``plugin_name="system"`` at app startup and unregister with the registry
|
||||
reset (tests) — they never depend on plugin activation state.
|
||||
|
||||
Permissions follow the owning data source:
|
||||
- audit_activity -> audit:read (audit log route guard, CORE_PERMISSIONS)
|
||||
- system_metrics -> settings:read (Roadmap M4; the /system/dashboard
|
||||
endpoint itself stays require_admin — the widget degrades gracefully
|
||||
with a permission hint for non-admins)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from app.plugins.miniapp_registry import get_miniapp_registry
|
||||
|
||||
SYSTEM_PLUGIN_NAME = "system"
|
||||
|
||||
|
||||
def register_system_miniapps() -> None:
|
||||
"""Register the core system MiniApps in the universal registry."""
|
||||
registry = get_miniapp_registry()
|
||||
|
||||
registry.register(
|
||||
app_id="audit_activity",
|
||||
name="Aktivitäten",
|
||||
icon="History",
|
||||
description="Letzte Aktivitäten aus dem Audit-Log (Benutzer, Aktion, Zeitpunkt).",
|
||||
plugin_name=SYSTEM_PLUGIN_NAME,
|
||||
permission="audit:read",
|
||||
settings_schema={
|
||||
"fields": [
|
||||
{
|
||||
"name": "max_items",
|
||||
"label": "Max. Einträge",
|
||||
"type": "number",
|
||||
"default": 10,
|
||||
}
|
||||
]
|
||||
},
|
||||
col_span=2,
|
||||
row_span=1,
|
||||
hosts=["chat", "dashboard", "window"],
|
||||
component="@/components/dashboard/AuditActivityWidget",
|
||||
order=40,
|
||||
)
|
||||
|
||||
registry.register(
|
||||
app_id="system_metrics",
|
||||
name="System Status",
|
||||
icon="Server",
|
||||
description="Datenbank-, Redis-, Worker- und API-Metriken (Administration).",
|
||||
plugin_name=SYSTEM_PLUGIN_NAME,
|
||||
permission="settings:read",
|
||||
settings_schema={},
|
||||
col_span=2,
|
||||
row_span=1,
|
||||
hosts=["chat", "dashboard", "window"],
|
||||
component="@/components/dashboard/SystemMetricsWidget",
|
||||
order=50,
|
||||
)
|
||||
@@ -218,6 +218,11 @@ async def lifespan(app: FastAPI):
|
||||
registry.initialize(get_migration_engine(), app)
|
||||
registry.discover_builtins()
|
||||
|
||||
# Core system MiniApps (Phase M4): host-level, independent of plugin state
|
||||
from app.core.system_miniapps import register_system_miniapps
|
||||
|
||||
register_system_miniapps()
|
||||
|
||||
# Install discovered builtin plugins and activate only those marked active in DB
|
||||
from sqlalchemy import select as sa_select
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker
|
||||
|
||||
@@ -116,6 +116,7 @@ class BasePlugin(ABC):
|
||||
col_span=getattr(m, "col_span", 1),
|
||||
row_span=getattr(m, "row_span", 1),
|
||||
hosts=getattr(m, "hosts", None),
|
||||
component=getattr(m, "component", ""),
|
||||
order=getattr(m, "order", 100),
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from app.plugins.manifest import (
|
||||
FrontendDashboardWidget,
|
||||
FrontendMenuItem,
|
||||
FrontendPageRoute,
|
||||
MiniAppContribution,
|
||||
PluginManifest,
|
||||
PluginRouteDef,
|
||||
)
|
||||
@@ -60,6 +61,26 @@ class ContactsPlugin(BasePlugin):
|
||||
],
|
||||
events=[],
|
||||
migrations=[],
|
||||
miniapps=[
|
||||
MiniAppContribution(
|
||||
app_id="contacts_stats",
|
||||
name="Kontakt-Zähler",
|
||||
icon="Building2",
|
||||
description="Firmen- und Kontakt-Zähler (persönliche StatCards).",
|
||||
permission="contacts:read",
|
||||
settings_schema={
|
||||
"fields": [
|
||||
{"name": "show_companies", "label": "Firmen anzeigen", "type": "boolean", "default": True},
|
||||
{"name": "show_persons", "label": "Personen anzeigen", "type": "boolean", "default": True},
|
||||
]
|
||||
},
|
||||
col_span=2,
|
||||
row_span=1,
|
||||
hosts=["chat", "dashboard", "window"],
|
||||
component="@/components/dashboard/ContactsStatsWidget",
|
||||
order=5,
|
||||
),
|
||||
],
|
||||
dashboard_widgets=[
|
||||
FrontendDashboardWidget(
|
||||
id="recent_contacts",
|
||||
|
||||
@@ -110,7 +110,9 @@ async def _seed_default_dashboard(
|
||||
apps = [
|
||||
a
|
||||
for a in registry.list_apps(host="dashboard")
|
||||
if user_permits(current_user, a) and "dashboard" in (a.get("hosts") or [])
|
||||
if user_permits(current_user, a)
|
||||
and "dashboard" in (a.get("hosts") or [])
|
||||
and a.get("component") # renderable only (M4: chat apps stay off layouts)
|
||||
]
|
||||
apps.sort(key=lambda a: a.get("order", 100))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user