feat(alerts): алерты всей команде — детекторы, дедупликация, список (#15)
CI / ruff · mypy · pytest (pull_request) Successful in 4m49s
CI / ruff · mypy · pytest (pull_request) Successful in 4m49s
- детекторы по состоянию после каждого раунда снапшотов: краш, OOM, цикл рестартов, unhealthy, перегрузка ≥ 1 мин, мало места, Docker недоступен; пороги в config/alerts.yaml - AlertService: ключ kind+service, повтор в 10 мин — счётчик в исходном сообщении, «✅ восстановлено» ответом; разовые события (🛡) со счётчиком - рассылка всем активным (🛡 — только админам), темп ~25/с, RetryAfter, заблокировавший бота пропускается; alert_deliveries для правок и ответов - «🚨 Алерты»: активные + 7 дней по 10, карточка; кнопки «📜 Логи», «🩺 Статус» под алертом открывают новое сообщение, а не затирают алерт - 🛡 атаки через агента идут через общий сервис алертов - убраны заглушки «Раздел в разработке» — у всех разделов свои роутеры Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+31
-6
@@ -10,8 +10,13 @@ from aiogram.client.session.aiohttp import AiohttpSession
|
||||
from app import __version__
|
||||
from app.access.service import AccessService
|
||||
from app.agent.service import Agent
|
||||
from app.alerts.broadcast import TelegramNotifier
|
||||
from app.alerts.detectors import load_alerts_config
|
||||
from app.alerts.monitor import AlertMonitor
|
||||
from app.alerts.service import AlertService
|
||||
from app.bot.fsm import make_storage
|
||||
from app.bot.middlewares.access import AccessMiddleware
|
||||
from app.bot.routers import alerts as alerts_router
|
||||
from app.bot.routers import commit, common, logs, members, status
|
||||
from app.config import Settings
|
||||
from app.db.maintenance import cleanup_loop
|
||||
@@ -41,6 +46,7 @@ def build_dispatcher(
|
||||
docker: DockerClient | None = None,
|
||||
checker: Checker | None = None,
|
||||
agent: Agent | None = None,
|
||||
alerts: AlertService | None = None,
|
||||
) -> Dispatcher:
|
||||
dp = Dispatcher(storage=make_storage(sessions))
|
||||
gate = AccessMiddleware(access, texts)
|
||||
@@ -52,7 +58,8 @@ def build_dispatcher(
|
||||
commit.build_router(texts, repo, DraftService(sessions), Publisher(repo, sessions), commit_url_template)
|
||||
)
|
||||
dp.include_router(status.build_router(texts, docker, checker))
|
||||
dp.include_router(logs.build_router(texts, docker, LogJournal(sessions), agent, access))
|
||||
dp.include_router(logs.build_router(texts, docker, LogJournal(sessions), agent, alerts))
|
||||
dp.include_router(alerts_router.build_router(texts, alerts, docker))
|
||||
dp.include_router(common.build_router(texts, access))
|
||||
return dp
|
||||
|
||||
@@ -60,6 +67,7 @@ def build_dispatcher(
|
||||
async def run(settings: Settings) -> None:
|
||||
texts = load_texts(settings.runtime.config_dir)
|
||||
checks_cfg = load_checks(settings.runtime.config_dir)
|
||||
alerts_cfg = load_alerts_config(settings.runtime.config_dir)
|
||||
engine = make_engine(settings.database.url)
|
||||
await upgrade(engine)
|
||||
sessions = make_sessionmaker(engine)
|
||||
@@ -72,8 +80,25 @@ async def run(settings: Settings) -> None:
|
||||
# The mock provider answers `{}` to the guard — every question would be an «attack»: no real LLM, no agent
|
||||
llm = make_provider(settings.llm) if settings.llm.provider != "mock" else None
|
||||
agent = Agent.from_config(llm, sessions, settings.runtime.config_dir) if llm else None
|
||||
proxy = settings.telegram.proxy_url
|
||||
session = AiohttpSession(proxy=proxy.get_secret_value()) if proxy else None
|
||||
bot = Bot(settings.telegram.bot_token.get_secret_value(), session=session)
|
||||
alerts = AlertService(
|
||||
sessions,
|
||||
TelegramNotifier(bot, access, sessions, texts),
|
||||
alerts_cfg,
|
||||
log_source=(lambda cid, n: docker.logs(cid, tail=n)) if docker else None,
|
||||
)
|
||||
dp = build_dispatcher(
|
||||
texts, sessions, access, UploadsRepo(settings.git), settings.git.commit_url_template, docker, checker, agent
|
||||
texts,
|
||||
sessions,
|
||||
access,
|
||||
UploadsRepo(settings.git),
|
||||
settings.git.commit_url_template,
|
||||
docker,
|
||||
checker,
|
||||
agent,
|
||||
alerts,
|
||||
)
|
||||
rt = settings.runtime
|
||||
background = [
|
||||
@@ -81,10 +106,10 @@ async def run(settings: Settings) -> None:
|
||||
asyncio.create_task(cleanup_loop(sessions)),
|
||||
]
|
||||
if docker:
|
||||
background.append(asyncio.create_task(snapshot_loop(docker, store, settings.docker.snapshot_interval)))
|
||||
proxy = settings.telegram.proxy_url
|
||||
session = AiohttpSession(proxy=proxy.get_secret_value()) if proxy else None
|
||||
bot = Bot(settings.telegram.bot_token.get_secret_value(), session=session)
|
||||
monitor = AlertMonitor(alerts, store, alerts_cfg)
|
||||
background.append(
|
||||
asyncio.create_task(snapshot_loop(docker, store, settings.docker.snapshot_interval, monitor.on_round))
|
||||
)
|
||||
log.info(
|
||||
"tgit %s starting, %d bootstrap admin(s), telegram %s, docker %s, agent %s",
|
||||
__version__,
|
||||
|
||||
Reference in New Issue
Block a user