LindqvistMartin/flare

GitHub: LindqvistMartin/flare

Flare 是一个基于 .NET 10 和 React 19 的自托管事件管理平台,提供从告警接入、实时协作到自动生成事后复盘的完整闭环,帮助团队摆脱昂贵的事件管理 SaaS 依赖。

Stars: 0 | Forks: 0

# Flare **自托管的事件管理。从告警到事后总结——无需 SaaS 账单。** [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![.NET](https://img.shields.io/badge/.NET-10-purple.svg)](https://dotnet.microsoft.com) [![React](https://img.shields.io/badge/React-19-61dafb.svg)](https://react.dev) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/c87f576ccf220216.svg)](https://github.com/LindqvistMartin/flare/actions) [![测试](https://img.shields.io/badge/tests-279%20passing-brightgreen.svg)](#) [![已部署](https://img.shields.io/badge/deployed-render.com-46E3B7.svg)](https://flare-ui.onrender.com) 🔗 **在线演示:** [flare-ui.onrender.com](https://flare-ui.onrender.com)  |  📊 **状态页面:** [flare-ui.onrender.com/#/p/demo](https://flare-ui.onrender.com/#/p/demo) ## 为什么选择 Flare FireHydrant 的定价为 44 美元/用户/月。Rootly 为 20+ 美元/用户/月,且采用了不透明的 分级定价。Incident.io 为 19-50 美元/用户/月,并将状态页面和 值班功能限制在更高等级的套餐中。PagerDuty 起价为 21 美元/用户/月。 **作为历来经济实惠的选择,Opsgenie 将于 2027 年 4 月关闭—— 约 80 万用户目前正在寻找替代品。** 目前还没有成熟的开源替代方案。Dispatch (Netflix) 需要 Keycloak、 PagerDuty 和 Slack 才能启动。其他项目的 star 数都没超过 2K。该领域 100% 被 SaaS 垄断。 Flare 填补了这一空白:完整的事件生命周期——webhook 接入、仅追加的 时间线、从事件流自动生成的复盘草稿、MTTR / MTTA 物化视图、通过 outbox 发送的 Slack 和 Teams 通知、公开的 状态页面——只需一条 `docker compose up` 命令。支持自托管,单一可部署单体架构, 后端使用 .NET 10,前端使用 React 19,一切存储交由 Postgres。 ## 截图 ![包含五个活跃事件、MTTR 30 天趋势和逾期行动项的仪表板](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/9a0949d305220222.png) ![包含分配的角色、时间线流和各服务手册侧边栏的事件详情](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/2473919083220231.png) ![从事件事件流自动生成并带有行动项的复盘草稿](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/f126943f84220237.png) ![带有“全部 / 待处理 / 进行中 / 已完成 / 已逾期”过滤器的行动项跟踪器](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/15a7be9846220242.png) ![包含 30 天事件计数和 Runbook 就绪指示器的服务目录](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/211fde5860220248.png) ![命令面板:搜索、导航、深色模式切换、最近事件](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/6619639a44220253.png) ![位于 /p/:slug 的公开状态页面——总体状态、各服务健康状况、活跃事件](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/fa08ec5b35220258.png) ## 架构 单一可部署单体架构。前端边缘采用 ASP.NET Core 10 Minimal API,EF Core 配合 Postgres 进行存储,`BackgroundService` + `Channel` 用于接入和 outbox 分发,OpenTelemetry 用于追踪和指标,Serilog 用于结构化 日志,位于 `/scalar` 的 Scalar UI 用于展示 OpenAPI 文档。 ``` ┌──────────────────────────────────────────────────┐ alerts ───▶ │ API: POST /api/v1/webhooks/ingest/{source} │ (Prometheus, │ POST /api/v1/incidents/{id}/postmortem/... │ Grafana, │ GET /api/v1/metrics/{mttr,mtta,dashboard} │ PulseWatch, │ GET /public/status/{slug} (cached 30s) │ generic) │ │ │ IAlertIngestionAdapter (4 implementations) │ │ Channel (bounded, backpressure) │ │ PostmortemDraftBuilder (inline, synchronous) │ └──────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────┐ │ IngestionWorker : BackgroundService │ │ one transaction: │ │ Incident + IncidentEvent + OutboxMessage │ └──────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────┐ │ Postgres │ │ incidents, incident_events (append-only), │ │ postmortems, action_items, outbox_messages, │ │ status_pages, mttr_by_service_30d, │ │ mtta_by_service_30d │ │ (materialized views, refreshed every 5 min) │ └──────────────────────────────────────────────────┘ │ │ ▼ ▼ ┌────────────────────────┐ ┌───────────────────────────┐ │ NotificationDispatcher │ │ MetricsAggregator │ │ outbox SKIP LOCKED │ │ REFRESH CONCURRENTLY 5m │ │ → status-page cache │ └───────────────────────────┘ │ invalidation │ └────────────────────────┘ ``` 事件在 Postgres 触发器级别是严格只追加的——参见 [ADR-001](docs/adr/001-append-only-incident-events.md)。复盘从事件流 物化而来,而不是由人工手动输入——参见 [ADR-002](docs/adr/002-postmortem-from-events.md)。通知通过 outbox + `SKIP LOCKED` 轮询 worker 进行分发,以尽力而为的方式向 SignalR 组 广播——参见 [ADR-003](docs/adr/003-outbox-notification-dispatch.md)。MTTR 和 MTTA 是根据标准的 `Incident.ResolvedAt` 和 `Incident.AcknowledgedAt` 时间戳,在滚动的 30 天窗口内按服务聚合的—— 领域状态机将它们与匹配的事件原子化写入,因此 matview SQL 保持快速且不受事件 payload 格式的影响。物化视图 策略及其扩展路径记录在 [ADR-004](docs/adr/004-mttr-materialized-views.md) 中。 ## 快速开始 后端: ``` cp src/Flare.Api/appsettings.Local.example.json src/Flare.Api/appsettings.Local.json # 编辑内部的 connection string(或导出 ConnectionStrings__Postgres), # 然后: dotnet run --project src/Flare.Api ``` `appsettings.Local.json` 已被 gitignored。示例文件是标准的 模板;在容器中首选使用环境变量。 客户端: ``` cd client npm install npm run dev ``` 然后打开 `http://localhost:5173/#/dashboard`。客户端期望 API 位于 `http://localhost:5000`(使用 `VITE_API_URL` 环境变量进行覆盖)。 开发服务器的 CORS 已经在 `Program.cs` 中配置好了。 ## 后端特性 - **Webhook 摄取** — Prometheus、Grafana、PulseWatch 和一个通用适配器 共享一个 `IAlertIngestionAdapter` 接口。入站请求排入一个 有界的 `Channel` 中;端点返回 202 Accepted,或当 通道已满时返回 503,以便发送方重试,而不是接受一个它无法 入队的任务。 - **仅追加时间线** — `IncidentEvents` 受 PostgreSQL `BEFORE UPDATE OR DELETE` 触发器保护;无论调用方、ORM 还是迁移框架,行变更都会抛出异常。 - **领域状态机** — `Triggered → Investigating → Identified → Monitoring → Resolved → Closed`,在聚合内部进行验证。无效的状态转换将 以 RFC 7807 Problem+JSON 422 响应的形式呈现。 - **自动生成的复盘草稿** — `PostmortemDraftBuilder` 按需直接从事件流 物化 Impact、Timeline 和 Root Cause。复盘一旦 `Published` 即不可变。 - **MTTR / MTTA 物化视图** — `mttr_by_service_30d` 和 `mtta_by_service_30d`,每五分钟由 `MetricsAggregator` 并发刷新。 - **Outbox 分发** — `NotificationDispatcher` 使用 `FOR UPDATE SKIP LOCKED` 进行轮询,将行标记为已处理,提交,*然后*向 SignalR 组和已配置的 Slack / Teams webhook 广播。批处理内的扇出 在基于每条消息的 DbContext 作用域上并发运行(上限为 10),这样一个慢速的 webhook 就不会阻塞同级的消息。数据库层面上保证至少一次传递,网络层面上保证最多一次——参见 ADR-003。 - **公开状态页面** — 只读的 `GET /public/status/{slug}` 为运营者策划的 服务列表返回各服务的当前状态和 30 天事件计数。响应在进程内缓存 30 秒;分发器 在 `IncidentCreated` 和 `IncidentStatusChanged` 提交后使受影响的页面失效,因此变化会在一个周期内显现,而不是 等待 TTL 到期。管理 CRUD 位于 `/api/v1/status-pages`;公开端点位于 `/api/v1` 之外,因此未来在管理界面上的身份验证门控 不会将客户拒之门外。管理 CRUD 目前 无需身份验证——身份验证将在下一个里程碑中引入。 缓存设计参见 [ADR-005](docs/adr/005-status-page-cache.md)。 - **Slack 和 Teams 频道** — 可通过 `INotificationChannel` 插拔。Webhook URL 在启动时通过 `ValidateOnStart` 与 HTTPS 主机白名单(`hooks.slack.com`、 `*.webhook.office.com`)进行校验——配置错误的 webhook 会在启动时报错,而不是默默地将 payload 发送出去。 HTTP、回环地址以及私有/链路本地目标将被拒绝(SSRF 防护)。 空的 URL = 静默跳过。 - **行动项提醒** — `ActionItemReminderService` 自上一次成功运行后 每 24 小时运行一次(计划任务作为 `ReminderHeartbeat` outbox 行持久化,因此它能在进程重启后存续)。失败的 周期会在重试前退避 15 分钟,以避免在 永久性错误时不断冲击数据库。 - **Outbox 保留期** — `OutboxJanitorService` 每 6 小时清理一次,删除 超过 30 天的已处理消息;为任何合规审计限制存储和历史 PII 窗口。 - **通知指标与追踪** — 位于 `Flare.Notifications` meter 上的 `flare_notification_channel_sends_total`、 `flare_outbox_messages_processed_total` 和 `flare_dispatcher_dropped_total` 计数器;`Flare.NotificationDispatcher` ActivitySource spans 补全了从 POST /incidents 到 通道 POST 的 Jaeger 追踪。 - **实时 UI 管道** — SignalR hub 位于 `/hubs/flare`,包含 `dashboard` 和 `incident:{id}` 组。 - **幂等的 POST 请求** — `Idempotency-Key` header 通过 内存缓存对写入请求进行去重,持续五分钟。 - **可观测性** — Serilog JSON 日志,基于 OTLP 的 OpenTelemetry 追踪和指标,`/metrics` 用于 Prometheus 抓取。 - **带有实时仪表板的 React 客户端** — `client/` 中的 Vite + React 19 前端。 仪表板订阅 `dashboard` SignalR 组;打开的 事件、MTTR 趋势和活跃事件表都会自动刷新而无需轮询。 - **事件详情页** — 完整的时间线流,带有每个事件的图标,状态 转换通过仅限允许项的下拉菜单进行(乐观更新,在 422 时回滚),Commander / Communicator / Responder 分配,带有 zod 校验的评论编辑器,以及从链接的服务中提取的 Markdown runbook 侧边栏。 页面订阅 `incident:{id}` SignalR 组,因此来自其他操作者的转换和 事件无需轮询即可呈现。 - **复盘查看器** — 从事件流生成草稿,Draft / Published 状态标签,带有内联创建功能的附加行动项面板。Impact 和 Root Cause 与物化的时间线一起展示,因此发布的复盘 可以在单个屏幕上进行端到端阅读。 - **行动项跟踪器** — `/action-items` 显示“全部 / 待处理 / 进行中 / 已完成 / 已逾期”选项卡,并带有内联状态切换。截止日期标签在逾期时 会变为警报色调。 - **服务与 Runbook 编辑器** — 服务目录包含各服务的 事件历史;Runbook 是一个带有防抖保存功能的 Markdown 编辑器。 - **命令面板** — 全局 Ctrl/Cmd + K 打开一个由 `cmdk` 驱动的面板,包含 导航操作和基于近期事件的搜索索引。 - **公开状态页面前端** — `/p/:slug` 针对缓存的 `/public/status/{slug}` 端点渲染面向客户的 布局(总体状态、各服务行、活跃事件)。不带凭证的独立 axios 实例 确保即使在引入管理员身份验证后,该路由也保持免验证状态。 ## 路线图 在 MVP(最小可行性产品)阶段刻意排除在外的事项: - **OIDC 身份验证** — `docker-compose` 中的 Keycloak 或 Authentik,`/api/v1/*` 上的 JWT bearer,`/public/*` 和 `/healthz` 保持开放。目前管理界面 无需身份验证;对于自托管演示而言,基于 token 的 访问已经足够,并且公开/管理端点的划分已经为该门控做好了准备。 - **值班调度引擎** — 轮换、覆盖、升级策略以及它们带来的 时区计算。这本身就是一个独立的产品;Flare 会通知 Slack 和 Teams,而寻呼功能则超出范围。 - **AI 辅助复盘总结** — 对事件流进行一次可选的 LLM 处理 ,以起草叙述性的 Root Cause 和 Contributing Factors。确定性的 `PostmortemDraftBuilder` 仍然是事实来源;AI 总结是作为附加功能存在的。 - **Opsgenie 导入** — 一个 CLI,用于读取 Opsgenie 导出文件并重新创建匹配的 服务和事件。Opsgenie 将于 2027 年 4 月关闭,因此迁移路径 是其用户最直接的入门方式。 - **Terraform provider** — 将服务、集成和状态页面作为声明式 资源,供将其他所有内容都作为代码管理的团队使用。
标签:On-Call, Postgres, React, Syscalls, 事件管理, 测试用例, 用户代理, 自托管, 运维监控