Files
onGuard24/onguard24/modules/registry.py
Alexandr 89b5983526 v1.5.0: IRM — инциденты, задачи, эскалации
- docs/IRM.md; Alembic 002: incidents, tasks, escalation_policies
- Модули incidents/tasks/escalations: API, UI, register_events(bus, pool)
- Авто-инцидент из alert.received; тесты test_irm_modules.py

Made-with: Cursor
2026-04-03 09:03:16 +03:00

108 lines
3.5 KiB
Python

"""Единая точка регистрации модулей: API, веб-UI и подписки на события.
Новый модуль: файл в `onguard24/modules/`, запись в `MODULE_MOUNTS` — см. docs/MODULES.md.
"""
from __future__ import annotations
import asyncpg
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from fastapi import APIRouter
from starlette.requests import Request
from onguard24.domain.events import EventBus
from onguard24.modules import (
contacts,
escalations,
incidents,
schedules,
statusboard,
tasks,
)
# async (Request) -> str — фрагмент HTML для главной страницы (опционально)
HomeFragment = Callable[[Request], Awaitable[str]]
RegisterEvents = Callable[[EventBus, asyncpg.Pool | None], None]
@dataclass(frozen=True)
class ModuleMount:
"""Один модуль: API под url_prefix, UI под /ui/modules/{slug}."""
router: APIRouter
url_prefix: str
register_events: RegisterEvents
slug: str
title: str
ui_router: APIRouter | None = None
render_home_fragment: HomeFragment | None = None
def _mounts() -> list[ModuleMount]:
return [
ModuleMount(
router=incidents.router,
url_prefix="/api/v1/modules/incidents",
register_events=incidents.register_events,
slug="incidents",
title="Инциденты",
ui_router=incidents.ui_router,
render_home_fragment=incidents.render_home_fragment,
),
ModuleMount(
router=tasks.router,
url_prefix="/api/v1/modules/tasks",
register_events=tasks.register_events,
slug="tasks",
title="Задачи",
ui_router=tasks.ui_router,
render_home_fragment=tasks.render_home_fragment,
),
ModuleMount(
router=escalations.router,
url_prefix="/api/v1/modules/escalations",
register_events=escalations.register_events,
slug="escalations",
title="Эскалации",
ui_router=escalations.ui_router,
render_home_fragment=escalations.render_home_fragment,
),
ModuleMount(
router=schedules.router,
url_prefix="/api/v1/modules/schedules",
register_events=schedules.register_events,
slug="schedules",
title="Календарь дежурств",
ui_router=schedules.ui_router,
render_home_fragment=schedules.render_home_fragment,
),
ModuleMount(
router=contacts.router,
url_prefix="/api/v1/modules/contacts",
register_events=contacts.register_events,
slug="contacts",
title="Контакты",
ui_router=contacts.ui_router,
render_home_fragment=contacts.render_home_fragment,
),
ModuleMount(
router=statusboard.router,
url_prefix="/api/v1/modules/statusboard",
register_events=statusboard.register_events,
slug="statusboard",
title="Светофор",
ui_router=statusboard.ui_router,
render_home_fragment=statusboard.render_home_fragment,
),
]
MODULE_MOUNTS: list[ModuleMount] = _mounts()
def register_module_events(bus: EventBus, pool: asyncpg.Pool | None = None) -> None:
for m in MODULE_MOUNTS:
m.register_events(bus, pool)