catalingrigoriev285/issueflow

GitHub: catalingrigoriev285/issueflow

一个自托管、框架无关的错误追踪器和轻量级 SIEM,通过统一 HTTP API 收集异常并提供分组、回归检测、仪表板和审计追踪功能。

Stars: 0 | Forks: 0

# IssueFlow **一个自托管、框架无关的错误追踪器和轻量级 SIEM。** 通过 HTTP API 从*任何*语言或技术栈中收集异常,然后对其进行分流、**记录**和**解决** —— 这是一个更精简的 GlitchTip/Sentry,具备 issue 分组、回归检测、仪表板和审计追踪功能。将你自己的 **Markdown 报告**附加到任何事件中,IssueFlow 会将其原样渲染。内置提供一流的 Laravel 支持。 - **`backend/`** — Express.js + TypeScript REST API。负责管理 MongoDB、JWT 认证、ingestion pipeline、issue 分组/去重、RBAC 以及所有业务逻辑。 - **`frontend/`** — Next.js (App Router) + Tailwind UI。仅通过 HTTP 与后端通信。 - **`laravel-package/`** — 用于你的 Laravel 应用的即拷即用型上报器。 ``` Any app ──POST /api/v1/ingest──▶ Express backend ──▶ MongoDB (Laravel / Node / Python / …) ▲ Next.js UI ────────┘ (httpOnly JWT cookie) ``` **两种 payload 结构,一个 endpoint。** ingestion API 会自动检测你发送的内容:**Sentry 风格的 envelope**(`exception.values[]`,即 Laravel 包发出的格式)*或*扁平的**通用/自定义错误**(`type`, `message`, `file`, `line`, `stacktrace`, `request`, `user`, …)。无论哪种方式,IssueFlow 都会渲染 Markdown 报告 —— 或者显示你随其发送的预渲染 `markdown` 内容。 ## 核心亮点 - **多项目** — 每个 Laravel 应用/环境都有自己专属的 ingestion API key。 - **安全认证** — 存储于 httpOnly + SameSite cookie 中的 JWT(由 Express 签发,在 Next.js edge 端验证)、CSRF 双重提交、RBAC(`admin` / `analyst` / `viewer`)、防止 NoSQL 注入的查询、经过 HMAC 哈希处理的 API key、速率限制。 - **SIEM 风格分流** — 确定性的 issue 分组、去重、**回归检测**、状态标记(未解决 / 已解决 / 已忽略)、文档笔记,以及只追加的活动/审计日志。 - **仪表板** — 随时间变化的事件量、严重程度分布、热门 issue。 ## 前置条件 - **Node.js 22+**(在 26 上也可运行) - **MongoDB 6+** 在本地运行(或使用下文提到的 Docker) ## 快速开始(本地,不使用 Docker) **1. 启动 MongoDB**(任何运行在 `27017` 端口的本地实例)。使用项目本地数据目录的示例: ``` mkdir -p .mongodb-data mongod --dbpath ./.mongodb-data --port 27017 --bind_ip 127.0.0.1 ``` **2. 配置环境变量** — 示例文件已经包含了可用的开发默认值。确保 `JWT_SECRET` 在两个服务中**完全一致**: ``` cp backend/.env.example backend/.env cp frontend/.env.example frontend/.env.local # 在 backend/.env 和 frontend/.env.local 中设置相同的 JWT_SECRET ``` **3. 后端** — 安装、种子数据填充、运行: ``` cd backend npm install npm run seed # creates the super-admin + a demo project and PRINTS an ingestion key (copy it!) npm run dev # http://localhost:4000 ``` **4. 前端** — 安装并运行: ``` cd frontend npm install npm run dev # http://localhost:3000 ``` **5. 打开** http://localhost:3000 并使用 `ADMIN_EMAIL` / `ADMIN_PASSWORD` 登录 (默认值:`admin@issueflow.local` / `ChangeMe_Str0ng!Pass`)。 ## 快速开始(Docker) ``` cp backend/.env.example backend/.env cp frontend/.env.example frontend/.env # 在两者中都设置相同的 JWT_SECRET,并在 backend/.env 中设置 MONGODB_URI=mongodb://mongo:27017/issueflow docker compose up --build docker compose exec backend npm run seed # prints the demo ingestion key ``` UI 运行在 http://localhost:3000,API 运行在 http://localhost:4000。 ## 冒烟测试 ingestion API 使用 `npm run seed` 打印出的 key: ``` curl -X POST http://localhost:4000/api/v1/ingest \ -H "Content-Type: application/json" \ -H "X-IssueFlow-Key: iflow_xxxxxxxxxxxx_yyyyyyyy..." \ -d '{"level":"error","transaction":"App\\Http\\Controllers\\OrderController@store", "exception":{"values":[{"type":"RuntimeException","value":"Undefined array key id", "stacktrace":{"frames":[{"filename":"app/Http/Controllers/OrderController.php", "function":"store","lineno":42,"in_app":true}]}}]}}' ``` → `202 { "issueId": "...", "fingerprint": "...", "status": "unresolved", "isNew": true }`。 再次发送 → 发生次数将会增加,并且不会创建新的 issue。在 UI 中刷新 **Issues** 即可看到它。将其标记为已解决,再发送另一个事件 → 它会翻转为 **regressed**(回归)状态。 ## 通用 / 自定义错误(任何语言) 你不需要 Sentry envelope。任何服务都可以将**扁平错误** POST 到同一个 endpoint —— 发送你已有的内容,IssueFlow 会填补剩余部分。除 payload 必须携带错误(`type`/`message`/`stacktrace`)*或* `message` 之外,所有字段都是可选的。 ``` curl -X POST http://localhost:4000/api/v1/ingest \ -H "Content-Type: application/json" \ -H "X-IssueFlow-Key: iflow_xxxxxxxxxxxx_yyyyyyyy..." \ -d '{ "service": "checkout-api", "environment": "production", "level": "critical", "type": "App\\Exceptions\\PaymentFailed", "message": "Card declined", "code": 402, "file": "app/Services/Billing.php", "line": 88, "previous": "PDOException: server has gone away", "stacktrace": "#0 app/Services/Billing.php(88): charge()\n#1 app/Http/OrderController.php(31): store()", "request": { "method": "POST", "url": "https://shop.test/pay", "ip": "9.9.9.9", "user_agent": "curl/8" }, "user": { "Id": 42, "Name": "Ada", "Email": "ada@shop.test" }, "tags": { "region": "eu-west" } }' ``` **接受的字段**(括号内为宽松的别名): | 字段 | 备注 | |---|---| | `type` (`exception`, `error.type`) | 异常/错误类 → issue 标题和分组 | | `message` (`error`, `error.message`) | 人类可读的信息 | | `code`, `file`, `line`, `previous` | 位置与起因 | | `stacktrace` (`trace`) | 原始**字符串** *或* `{ file, line, function }` 帧数组(从最旧到最新) | | `level` (`severity`), `environment`, `release` (`version`) | `level` 映射到 `fatal/error/warning/info/debug` | | `service` (`app`), `server_name`, `transaction` (`culprit`) | 来源身份 | | `request` | `{ method, url, ip, referer, user_agent, query_string, headers }` | | `user` | 任何 `label → value` 映射(渲染为表格) | | `tags` | 对象 `{k:v}` *或* 数组 `[{key,value}]` | | `context` / `extra`, `contexts`, `fingerprint` | 直接透传;`fingerprint` 会覆盖分组逻辑 | | `markdown` | **预渲染的** GitHub 风格 Markdown 报告 —— 原样显示 | ### 自带 Markdown 如果你已经格式化了丰富的错误报告(例如,你发布到 Telegram 或 TheHive 的那些报告),将该字符串作为 `markdown` 发送,IssueFlow 会将其**逐字**渲染在 issue 的 **Report**(报告)面板中 —— 表格、代码块、粗体、链接等全部保留。你发送什么布局,就得到什么布局;IssueFlow 不会强加自己的模板。没有携带 `markdown` 字段的事件仅是没有 Report 面板而已 —— 结构化的 **Overview**(概览,包含 stack trace、request、user、tags)依然会显示所有内容。 ``` curl -X POST http://localhost:4000/api/v1/ingest \ -H "X-IssueFlow-Key: iflow_..." -H "Content-Type: application/json" \ -d '{ "type": "RuntimeException", "message": "boom", "markdown": "### 🚨 RuntimeException\n\n| Field | Value |\n|---|---|\n| Env | `production` |" }' ``` ## 连接真实的 Laravel 应用 请参阅 [`laravel-package/README.md`](laravel-package/README.md) —— 复制两个文件,设置两个环境变量,注册一个 `reportable()` 钩子。 ## 项目结构 ``` backend/ Express + TS API (models, ingestion, auth, routes, seed, tests) frontend/ Next.js App Router UI (design system, shell, dashboard, issues, projects, admin) laravel-package/ Copy-paste Laravel reporter shared/contracts/ Canonical DTO types synced into both services docker-compose.yml + .override.yml ``` ## 角色 | 角色 | 权限 | |---|---| | **viewer** | 读取 issue、事件、仪表板 | | **analyst** | + 分流:解决 / 忽略 / 分配 / 评论 / 批量操作 | | **admin** | + 管理项目、ingestion key 和用户 | 初始化的超级管理员受到保护 —— 其他管理员无法降级或禁用它。 ## 测试 ``` cd backend && npm test # Vitest + Supertest (grouping, jwt, api-keys, ingest, auth) — 26 tests ``` ## 安全说明 - JWT 使用 `jose` 签名(HS256 共享密钥用于开发环境)。**投入生产前:** 切换到 EdDSA/RS256(Express 持有私钥,前端仅获取公开验证密钥),设置 `COOKIE_SECURE=true`,在反向代理后将两个服务部署在同一父域名下,将 rate-limit store 迁移至 Redis,并设置强大的非默认 `ADMIN_PASSWORD` / `JWT_SECRET` / `INGEST_KEY_PEPPER`。 - Ingestion key 以 HMAC-SHA256(secret, pepper) 的形式存储;明文仅显示一次。 - 所有用户提供的查询输入在接触 MongoDB 之前都会被验证为基本类型(无操作符注入)。 - 原始事件带有 TTL (`retentionDays`);issue 聚合数据和审计日志将无限期保留。
标签:Express.js, Laravel, MongoDB, 自动化攻击, 请求拦截, 运维监控, 错误追踪