14 KiB
Project Context for GitHub Copilot (v1.3.2)
This document provides technical context, architectural decisions, and code conventions for the Alert Message Center project. It is intended to help AI assistants understand the codebase.
1. Project Overview
Alert Message Center (formerly Alert Manager) is a centralized alert dispatching system.
- Goal: Decouple alert sources from alert recipients.
- Mechanism:
- Topics: Alerts are sent to a Topic. Users subscribe to Topics to receive messages.
- Personal Inbox: Users can send alerts directly to themselves via a private webhook URL, bypassing Topic creation and approval.
- Group Chat: Alerts can be dispatched to Feishu Group Chats where the App Bot is a member.
- Dispatch: The system sends messages via Feishu (Lark) Private Messages or Group Messages.
- Runtime: Bun (JavaScript/TypeScript runtime).
2. Tech Stack
- Monorepo: Simple directory structure (
apps/server,apps/web). - Backend:
- Runtime: Bun.
- Framework: Hono (Web Standard based).
- Database: PostgreSQL.
- ORM: Drizzle ORM.
- Authentication: Feishu OAuth2 (Session-based with cookies).
- External API: Feishu Open Platform (Server-side API via
@larksuiteoapi/node-sdk).
- Frontend:
- Build Tool: Vite.
- Framework: React.
- Styling: Tailwind CSS.
- Icons: Lucide React.
- Client:
hono/client(RPC-style type-safe client).
3. Data Model (Schema)
The database schema is defined in apps/server/src/db/schema.ts.
Entities
-
Topic (
topics)id: UUID (Primary Key)name: Display name (e.g., "Payment Service Errors").slug: URL-safe identifier (e.g.,payment-errors). Used in webhook URLs.description: Optional text.status:pending,approved, orrejected.createdBy: Foreign Key ->users.id.approvedBy: Foreign Key ->users.id.
-
User (
users)id: UUID (Primary Key).name: Display name.feishuUserId: The Feishuopen_id. Critical for sending messages.email: Contact info.isAdmin: Boolean flag for administrative privileges (create topics, view all users).isTrusted: Boolean flag for trusted users (topics are auto-approved).
-
Subscription (
subscriptions)topicId: Foreign Key ->topics.id.userId: Foreign Key ->users.id.- Relationship: Many-to-Many between Topics and Users.
-
Topic Group Chat (
topic_group_chats)id: UUID (Primary Key).topicId: Foreign Key ->topics.id.chatId: The Feishuchat_id.name: Group name (snapshot).status:pending,approved, orrejected.createdBy: Foreign Key ->users.id.- Relationship: Many-to-Many between Topics and Feishu Groups.
-
Known Group Chat (
known_group_chats)chatId: Feishuchat_id(Primary Key).name: Group name.lastActiveAt: Timestamp of last event from this group.- Purpose: Caches groups the bot has been added to, facilitating easy selection in the UI.
-
Alert Task (
alert_tasks)id: UUID (Primary Key).topicSlug: The slug of the target topic (orNULLfor DM).senderId: Foreign Key ->users.id(who triggered the webhook).status:pending,processing,completed, orfailed.recipientCount: Total recipients (subscribers + groups).successCount: Number of successful deliveries.payload: Snapshot of the incoming webhook body (JSONB).error: Last error message if failed.- Purpose: Tracks the lifecycle of a single alert ingestion events.
-
Alert Log (
alert_logs)id: UUID (Primary Key).taskId: Foreign Key ->alert_tasks.id.userId: Target user open_id (snapshot).status:sentorfailed.- Purpose: Granular tracking for each individual delivery within a task.
4. Key Workflows
Authentication
- Strategy: Feishu OAuth2.
- Flow:
- Frontend calls
/api/auth/login-urlto get Feishu auth URL. - User redirects to Feishu, approves, redirects back to
/api/auth/callback. - Server exchanges code for token, gets user info, creates/updates user in DB.
- Server sets
sessioncookie (httpOnly).
- Frontend calls
- Context:
AuthContext.tsxmanages user state on frontend.
Personal Inbox (Direct Messaging)
- Strategy: Direct delivery to a specific user.
- Mechanism:
- Each user has a
personalToken(8-character hex string). - Sending to
POST /api/webhook/:token/dmroutes messages directly to the user associated with the token. - No Topic or Subscription is required.
- Each user has a
Alert Ingestion & Dispatch
File: apps/server/src/webhook.ts
- Ingest:
- Topic-based:
POST /api/webhook/:token/topic/:slug - Direct (Inbox):
POST /api/webhook/:token/dm
- Topic-based:
- Lookup:
- For Topic-based: Find
Topicbyslugand fetch allsubscriptions. - For Direct: Identify the user via
token.
- For Topic-based: Find
- Dispatch:
-
Call
FeishuClient.sendMessagefor each recipient. -
Payload: Supports
textandinteractive(Feishu Card) message types. -
Call
FeishuClient.sendMessagefor each recipient. -
Payload: Supports
textandinteractive(Feishu Card) message types.
-
Feishu Group Chat Integration
- Strategy: App Bot in Group.
- Discovery:
- The system listens for
im.chat.member.bot.added_v1events (via Webhook or WebSocket). - When the bot is added to a group, the group details are cached in
known_group_chats.
- The system listens for
- Bot Removal:
- The system listens for
im.chat.member.bot.deleted_v1events. - When the bot is removed, the cached group is deleted from
known_group_chats. - Auto-Unbind: All bindings in
topic_group_chatsfor thatchat_idare automatically deleted to ensure data consistency.
- The system listens for
- Binding: Users/Admins bind a Topic to a known Feishu Group in the UI.
- Search: The binding UI supports real-time, server-side debounced search by group name.
- Security: Only the Topic Creator or an Admin can bind/unbind groups to a Topic.
- Approval:
- Normal users: Binding status is
pendingupon creation. Admins receive notification. - Admins/Trusted Users: Binding status is
approvedimmediately.
- Normal users: Binding status is
- Dispatch: Alerts for the topic are sent to all bound
chat_ids in addition to individual subscribers.
Long Connection (WebSocket)
- Problem: Intranet deployments cannot receive public Webhook callbacks from Feishu.
- Solution: Use Feishu Open Platform's WebSocket mode.
- Configuration: Set
FEISHU_USE_WS=truein.env. - Implementation: Uses
@larksuiteoapi/node-sdkto establish a persistent connection and receive events likeim.chat.member.bot.added_v1. - Users can subscribe/unsubscribe themselves to any topic.
- Admins can manage subscriptions for other users globally in
AdminView. - Topic Deletion: Centralized in the Admin Dashboard (All Topics Tab) to avoid accidental deletion from the main topic list.
- Button logic on frontend toggles between "Subscribe" and "Unsubscribe".
- Topic Approval:
- Normal users: Topic status is
pendingupon creation. Admins receive an interactive Feishu notification. - Admins/Trusted Users: Topic status is
approvedimmediately. - Admin notification logic is located in
apps/server/src/lib/admin-notifier.ts.
- Normal users: Topic status is
- Trusted User System:
- Users with
isTrusted=true(set by Admin) orisAdmin=truehave their requests (Topics/Bindings) automatically approved.
- Users with
5. API Endpoints
Auth
GET /api/auth/login-urlGET /api/auth/callbackGET /api/auth/mePOST /api/auth/logout
Management
GET /api/topics: List all approved topics.GET /api/topics/my-requests: List user's own topic requests.GET /api/topics/requests: List pending topic requests (Admin only).GET /api/topics/all: List all topics regardless of status (Admin only).POST /api/topics: Create a topic (Admin creates approved, User creates pending).POST /api/topics/:id/approve: Approve a topic request (Admin only).POST /api/topics/:id/reject: Reject a topic request (Admin only).DELETE /api/topics/:id: Delete a topic (Admin only).POST /api/topics/:id/subscribe/:userId: Subscribe.DELETE /api/topics/:id/subscribe/:userId: Unsubscribe.GET /api/users: List users (Admin only).GET /api/users: List users (Admin only).
Feishu Group Management
GET /api/groups: List known groups (cached from bot events). Supportsqfor search andlimitparameters.GET /api/topics/:id/groups: List group bindings for a topic.POST /api/topics/:id/groups: Bind a group to a topic.DELETE /api/topics/:id/groups/:bindingId: Unbind a group.
Feishu Event
POST /api/feishu/event: Endpoint for receiving Feishu events (Webhook mode).- Note: This endpoint uses manual challenge handling (
lark.generateChallenge) andeventDispatcher.invokeinstead of the SDK'sadaptDefaultto maintain compatibility with Hono's non-standard Node.js response object. - Signature Verification Hack: To preserve Feishu's signature verification, the internal
invokecall usesObject.create({ headers })to inject HTTP headers on the prototype of the payload object. This ensures headers are accessible to the SDK's internal verification logic but are excluded fromJSON.stringify, which is critical for matching the SHA256 content checksum.
- Note: This endpoint uses manual challenge handling (
Webhook
POST /api/webhook/:token/topic/:slug: Trigger an alert for a topic.POST /api/webhook/:token/dm: Trigger a direct alert to the user's private inbox.
6. Future Roadmap (Planned)
- Message Preview: Preview Feishu card JSON in the UI.
- History/Logs: Tracking for sent alerts (Alert Tasks/Logs).
- Retry Mechanism: Handle Feishu API failures.
- Deployment: Dockerfile and CI/CD.
7. Development Conventions
- Imports: Use relative imports.
- Styling: Use Tailwind utility classes directly in JSX.
- Async/Await: Prefer
async/awaitover.then(). - Strict Type Safety &
anyProhibition:Important
The usage of
anyis strictly prohibited. This has been a recurring issue and must be avoided at all costs.- Explicit Interfaces: Always define clear interfaces or types for API responses, webhook payloads, and complex objects.
- Type Inference: Leverage TypeScript's type inference. If a variable is initialized later, provide an explicit type during declaration (e.g.,
let whereClause: SQL | undefined;) instead of leaving it implicit. - Hono RPC: Utilize the type-safe client (
client.api...) to ensure end-to-end type safety between backend and frontend. - No Type Casting: Avoid
as anyor<any>casts. Use type guards (if,switch,instanceof) or Zod schema validation to narrow types safely. - AI Responsibility: AI assistants MUST ensure every new or modified piece of code passes strict TypeScript and Biome checks. If a type is unknown, research the schema rather than defaulting to
any.
- Vite Env Access: When accessing Vite environment variables via
import.meta.env(or castingimport.meta as any), always use optional chaining (e.g.,meta.env?.VITE_...). This prevents crashes if the environment is not initialized or if the code runs in a non-browser context during pre-rendering/testing. - Frontend Resilience:
- Always check
res.okbefore attempting to parse or use API responses. - Use
Array.isArray()to verify that data expected to be a list actually is one, especially when mapping over it in JSX. This prevents "white page" crashes when the backend returns error objects instead of arrays. - Provide fallback empty states (e.g.,
setTopics([])) incatchblocks or failed response branches.
- Always check
- Logging:
- Framework:
pino. - Structured Log: Use JSON format for easy parsing and aggregation.
- Contextual Data: Pass objects as the first argument to
loggermethods (e.g.,logger.error({ err, chatId }, 'message')) for indexed search. - Dev Mode: Uses
pino-prettyfor human-friendly output during development.
- Framework:
- Environment Isolation:
- Each workspace (
apps/server,apps/web) uses its own.envfile via Bun's--env-file .envflag. - Development proxy target for the frontend is configurable via
VITE_API_URL(default:http://localhost:3000).
- Each workspace (
- Critical Environment Variables:
FEISHU_ENCRYPT_KEY: Essential for thelark.generateChallengeand event signature verification.FEISHU_VERIFICATION_TOKEN: Used byEventDispatcherfor event authentication.FEISHU_USE_WS: Set totrueto enable WebSocket mode (bypassesfeishu-event.ts).ADMIN_EMAILS: Comma-separated list of emails that automatically receiveisAdmin=trueupon first login.
- CI/CD:
- GitHub Actions automates building a multi-stage Docker image and pushing it to GitHub Container Registry (GHCR).
- Image path:
ghcr.io/${USER}/alert-message-center. - Deployment Architecture: A single container runs the Bun server, which serves API requests and static frontend assets (via
hono/bun'sserveStatic). - Database Initialization: The Docker entrypoint automatically runs
bun run db:migrate:deploybefore starting the server to ensure the schema is up-to-date in new environments. - Token Migration: The
db:migrate:deployscript (defined insrc/db/migrate.ts) also handles legacy user token shortening to maintain consistency with the 8-character token logic introduced in v1.2.6. - Drizzle Meta: The
apps/server/drizzle/metadirectory MUST NOT be ignored by git (it was previously explicitly excluded in.gitignorebut has been restored). This directory contains_journal.json, which is essential fordrizzle-kitand the runtime migrator to verify migration integrity. Without it, migrations will fail in clean environments like Docker containers.
8. Core Documents
- README.md: Main project documentation (English version).
- README.zh-CN.md: Simplified Chinese version of the documentation.
- CHANGELOG.md: Record of version changes.
- todo.md: Task tracking.