oneshotEFA/DisasterRelief-api

GitHub: oneshotEFA/DisasterRelief-api

基于微服务架构的众包灾难响应平台,实现从市民上报事件、自动三阶段验证到最近救援人员智能调度的完整异步容错流水线。

Stars: 1 | Forks: 0

## 公共 Web 应用程序 供公众报告事件、请求帮助并与平台互动。 仓库: [DisasterRelief Public Web App](https://github.com/kirubel4/DisasterRelief-public-web-app?utm_source=chatgpt.com) ### 响应者移动应用程序 供响应人员和紧急救援人员使用的移动应用程序,用于管理事件并协调灾难响应活动。 仓库: [DisasterRelief Responder App](https://github.com/oneshotEFA/DisasterRelief-App?utm_source=chatgpt.com)

Nest Logo

A progressive Node.js framework for building efficient and scalable server-side applications.

NPM Version Package License NPM Downloads CircleCI Discord Backers on Open Collective Sponsors on Open Collective Donate us Support us Follow us on Twitter

Disaster Response Platform — Backend 一个采用微服务架构构建的分布式、众包灾难响应系统。任何人都可以在几秒钟内报告事件。系统对其进行验证,将其发布到实时动态中,并自动派遣最近的注册响应者——所有这些都通过一个异步、容错的管道完成。 ## 架构概述 ``` Public User / Responder App │ ▼ API Gateway :3000 ← single entry point, rate limiting, auth guard, proxy │ ├──▶ Auth Service :3001 ← Better Auth, responder registration & login ├──▶ Incident Service :3002 ← CRUD, voting, SSE live feed ├──▶ Dispatch Service :3003 ← PostGIS radius query, responder dispatch └──▶ Notification Service :3005 ← Firebase FCM push notifications Background Workers: Validation Worker :3004 ← Kafka consumer, 3-stage pipeline Infrastructure: PostgreSQL + PostGIS ← primary data store Apache Kafka ← inter-service async messaging Redis ← caching, vote counters Cloudinary ← media storage ``` ## 技术栈 | 层级 | 技术 | | ------------------ | ------------------------ | | Framework | NestJS (monorepo) | | Language | TypeScript | | Database | PostgreSQL 16 + PostGIS | | ORM | Prisma | | Auth | Better Auth | | Messaging | Apache Kafka (KafkaJS) | | Cache | Redis (ioredis) | | Media | Cloudinary | | Push Notifications | Firebase Cloud Messaging | | Containerization | Docker + Docker Compose | | Orchestration | Kubernetes | ## 服务 ### API Gateway `:3000` 所有客户端请求的单一入口。职责: - 速率限制(20 请求/秒,200 请求/分钟) - 通过 Better Auth 对受保护路由进行会话验证 - 对下游服务的反向代理——零业务逻辑 - 通过 Redis 中间件进行响应缓存 ### Auth Service `:3001` 处理所有身份验证和响应者身份。职责: - 通过 Better Auth 进行邮箱 + 密码登录 - 响应者注册(状态以 `PENDING` 开始) - 会话管理(7天滚动会话) - 个人资料管理,FCM token 注册 - 审批门槛——响应者在管理员于数据库中设置 `emailVerified = true` 之前无法登录 ### Incident Service `:3002` 拥有完整的事件生命周期。职责: - 接受事件提交(多部分——文本 + 照片) - 将媒体上传到 Cloudinary - 向 Kafka 生成 `incident.submitted` - 消费 `incident.validated` → 将状态更新为 `ACTIVE` - 通过 SSE (Server-Sent Events) 提供实时动态 - 使用 Redis 计数器处理社区投票 ### Validation Worker `:3004` 无状态后台工作者——支持水平扩展。职责: - 从 Kafka 消费 `incident.submitted` - 运行 3 个顺序检查: 1. **重复检查**——PostGIS 半径查询(200米,30分钟时间窗口) 2. **地理合理性**——验证坐标是否在城市边界框内 3. **内容检查**——屏蔽词/短语过滤 - 生成 `incident.validated` 或 `incident.rejected_pipeline` ### Dispatch Service `:3003` 查找并通知最近的可用响应者。职责: - 从 Kafka 消费 `incident.active` - PostGIS 半径查询,以在5公里范围内查找最近的已批准、可用的响应者 - 创建派发记录并设置5分钟 ACK 超时 - 拒绝或超时 → 使用下一个最近的响应者重试 - 耗尽时 → 生成 `dispatch.exhausted` ### Notification Service `:3005` 向响应者设备发送推送通知。职责: - 从 Kafka 消费 `dispatch.created` - 向所有响应者设备 token 发送 FCM 多播推送 - 自动清理无效/过期的 FCM token - 将通知记录到 `responder_notifications` 表中 ## Kafka 主题 | 主题 | 生产者 | 消费者 | 目的 | | ---------------------------- | ----------------- | -------------------- | ---------------------------- | | `incident.submitted` | Incident Service | Validation Worker | 新事件进入管道 | | `incident.validated` | Validation Worker | Incident Service | 所有检查通过 | | `incident.rejected_pipeline` | Validation Worker | Incident Service | 检查未通过 | | `incident.active` | Incident Service | Dispatch Service | 事件已上线 | | `incident.flagged_community` | Incident Service | Notification Service | 达到踩阈值 | | `dispatch.created` | Dispatch Service | Notification Service | 响应者已派发 | | `dispatch.acknowledged` | Dispatch Service | Incident Service | 响应者正在途中 | | `dispatch.rejected` | Dispatch Service | Dispatch Service | 尝试下一个响应者 | | `dispatch.timed_out` | Dispatch Service | Dispatch Service | ACK 窗口已过期 | | `dispatch.resolved` | Dispatch Service | Incident Service | 事件已解决 | | `dispatch.exhausted` | Dispatch Service | Notification Service | 未找到响应者 | ## 入门指南 ### 前置条件 - Node.js 22+ - Docker Desktop - npm ### 1. 克隆并安装 ``` git clone https://github.com/youruser/disaster-relief-api.git cd disaster-relief-api npm install ``` ### 2. 环境设置 ``` cp .env.example .env ``` 在 `.env` 中填写所需的值: ``` # Database DATABASE_URL=postgresql://postgres:postgres@localhost:5432/disaster_platform # Redis REDIS_URL=redis://localhost:6379 # Kafka KAFKA_BROKERS=localhost:9092 # Better Auth BETTER_AUTH_SECRET=your_random_32_char_secret BETTER_AUTH_BASE_URL=http://localhost:3001 BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:3000 # Cloudinary CLOUDINARY_CLOUD_NAME=your_cloud_name CLOUDINARY_API_KEY=your_api_key CLOUDINARY_API_SECRET=your_api_secret # Firebase FCM FCM_PROJECT_ID=your_project_id FCM_CLIENT_EMAIL=your_service_account_email FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n" # 城市 bounding box(geo 验证) CITY_BBOX_MIN_LAT=44.32 CITY_BBOX_MAX_LAT=44.54 CITY_BBOX_MIN_LNG=25.92 CITY_BBOX_MAX_LNG=26.30 # Service URL(本地开发) AUTH_SERVICE_URL=http://localhost:3001 INCIDENT_SERVICE_URL=http://localhost:3002 DISPATCH_SERVICE_URL=http://localhost:3003 NOTIFICATION_SERVICE_URL=http://localhost:3005 ``` ### 3. 启动基础设施 ``` docker-compose up postgres redis kafka zookeeper kafka-ui -d ``` ### 4. 运行数据库迁移 ``` npm run prisma:migrate npm run prisma:generate ``` ### 5. 应用 PostGIS 列 ``` psql postgresql://postgres:postgres@localhost:5432/disaster_platform ``` ``` ALTER TABLE incidents ADD COLUMN IF NOT EXISTS location geography(Point, 4326); ALTER TABLE responder_profiles ADD COLUMN IF NOT EXISTS base_location geography(Point, 4326); CREATE INDEX IF NOT EXISTS idx_incidents_location ON incidents USING GIST(location); CREATE INDEX IF NOT EXISTS idx_responder_profiles_base_location ON responder_profiles USING GIST(base_location); \q ``` ### 6. 启动所有服务 打开 6 个单独的终端: ``` npm run start:dev # API Gateway :3000 npm run start:dev:auth # Auth Service :3001 npm run start:dev:incident # Incident Service :3002 npm run start:dev:dispatch # Dispatch Service :3003 npm run start:dev:validation # Validation Worker :3004 npm run start:dev:notification # Notification Service :3005 ``` ## 使用 Docker Compose 运行(所有服务) ``` # 构建并启动所有内容 docker-compose up --build -d # 检查所有服务是否正在运行 docker-compose ps # 查看特定服务的日志 docker logs disater-relief-api-incident-service-1 --follow # 扩展 Validation Worker docker-compose up --scale validation-worker=3 -d # 停止所有内容 docker-compose down ``` ## API 参考 Base URL: `http://localhost:3000/v1` ### 公开端点(无需身份验证) | 方法 | 路由 | 描述 | | ------ | ----------------------- | --------------------------------- | | `POST` | `/incidents` | 提交新事件 (multipart) | | `GET` | `/incidents` | 列出带有过滤器的事件 | | `GET` | `/incidents/:id` | 获取单个事件 | | `GET` | `/incidents/stream` | SSE 实时动态 | | `GET` | `/incidents/:id/stream` | SSE 单个事件更新 | | `POST` | `/incidents/:id/vote` | 对事件进行投票 | | `POST` | `/responders/register` | 注册为响应者 | | `POST` | `/auth/sign-in/email` | 响应者登录 | | `POST` | `/auth/sign-out` | 登出 | ### 受保护端点(需要会话) | 方法 | 路由 | 描述 | | ------- | ----------------------------- | ---------------------- | | `GET` | `/responders/me` | 获取自己的个人资料 | | `PATCH` | `/responders/me` | 更新个人资料 | | `PATCH` | `/responders/me/availability` | 切换可用性 | | `PATCH` | `/responders/me/location` | 更新大本营 | | `PATCH` | `/responders/me/fcm-token` | 注册设备 token | | `POST` | `/dispatches/:id/ack` | 确认“正在路上” | | `POST` | `/dispatches/:id/reject` | 无法响应 | | `POST` | `/dispatches/:id/resolve` | 标记事件已解决 | | `GET` | `/notifications` | 列出通知 | ### 提交事件示例 ``` curl -X POST http://localhost:3000/v1/incidents \ -H "Content-Type: application/json" \ -d '{ "type": "FIRE", "title": "Building fire on Str. Eminescu", "latitude": 44.43, "longitude": 26.10 }' ``` 响应: ``` { "data": { "id": "uuid", "status": "PENDING_VALIDATION", "message": "Your report is under review." }, "error": null } ``` ## 响应者审批 响应者通过 `POST /responders/register` 注册,并以 `status: PENDING` 开始。超级管理员在数据库中手动批准他们: ``` UPDATE responder_profiles rp SET status = 'APPROVED' FROM users u WHERE rp."userId" = u.id AND u.email = 'responder@example.com'; UPDATE users SET "emailVerified" = true WHERE email = 'responder@example.com'; ``` ## 分布式系统属性 | 要求 | 实现 | | ------------------------ | --------------------------------------------------------------------------------- | | 多节点 | 6 个独立的 NestJS 服务,每个都在自己的容器中 | | 节点间通信 | 所有异步事件使用 Kafka 主题,同步代理使用 REST | | 容错 | `restart: unless-stopped` + Kafka 消息保留 + 幂等消费者 | | 可扩展性 | `docker-compose up --scale validation-worker=N`——Kafka 自动分配 | | 数据一致性 | 强一致性(ACK 的 DB 事务) + 最终一致性(Redis → PostgreSQL 投票同步) | ## 文件夹结构 ``` apps/ ├── api-gateway/ # Reverse proxy + rate limiting + auth guard ├── auth-service/ # Better Auth + responder registration ├── incident-service/ # Incident CRUD + SSE + voting ├── validation-worker/ # Kafka consumer + 3-stage validation pipeline ├── dispatch-service/ # PostGIS dispatch + ACK flow └── notification-service/ # FCM push notifications libs/ ├── prisma/ # Prisma client + schema ├── kafka/ # KafkaJS producer/consumer + event types ├── better-auth/ # Better Auth instance + session guard ├── cloudinary/ # Media upload/delete helpers ├── fcm/ # Firebase Admin + multicast send ├── redis/ # Redis client + cache helpers ├── dto/ # Shared DTOs + validation └── types/ # Shared TypeScript types k8s/ # Kubernetes manifests docker-compose.yml # Local development + demo ``` ## Kafka UI 在运行 Docker Compose 时可通过 `http://localhost:8080` 访问。实时显示所有主题、消息、消费者组和延迟——有助于在开发和演示期间监控管道。 ## 健康检查 每个服务都会暴露: ``` GET /v1/health → { status: "ok", uptime: number } GET /v1/health/ready → checks DB + Kafka connection → 200 or 503 ```
标签:CISA项目, GNU通用公共许可证, IP 地址批量处理, NestJS, Node.js, 事件上报, 众包系统, 公共安全, 分布式系统, 响应大小分析, 子域名突变, 实时推送, 实时数据流, 实时通信, 容错管道, 库, 应急响应, 应急救援, 异步处理, 微服务架构, 搜索引擎查询, 救援系统, 测试用例, 灾难响应, 物联网, 移动应用, 自动化攻击, 自动调度, 请求拦截, 软件成分分析