forked from hangshuo652/aurak
feat: implement QuestionBank CRUD with pagination and template query
- Add pagination support to findAll (page, limit query params) - Add findByTemplateId method to service - Add GET /by-template/:templateId endpoint to controller - Service already includes CRUD for QuestionBank and QuestionBankItem
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
# Feishu WebSocket Integration - Design Document
|
||||
|
||||
**Date**: 2026-03-17
|
||||
**Author**: Sisyphus AI Agent
|
||||
**Status**: Draft for Review
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Add WebSocket long-connection support for Feishu bot integration, enabling internal network deployment without requiring public domain or internet-facing endpoints. The system will support both existing webhook mode and new WebSocket mode, allowing users to choose their preferred connection method.
|
||||
|
||||
---
|
||||
|
||||
## 1. Architecture Overview
|
||||
|
||||
### Current Architecture (Webhook Mode)
|
||||
|
||||
```
|
||||
Feishu Cloud → Public Domain → NAT/Firewall → Internal Server
|
||||
└→ POST /api/feishu/webhook/:appId
|
||||
```
|
||||
|
||||
**Limitations:**
|
||||
- Requires public domain with SSL certificate
|
||||
- Requires NAT/firewall port forwarding or reverse proxy
|
||||
- Not suitable for pure internal network deployment
|
||||
|
||||
### New Architecture (WebSocket Mode)
|
||||
|
||||
```
|
||||
Feishu Cloud ←──────── WebSocket (wss://open.feishu.cn) ──────── Internal Server
|
||||
↑
|
||||
Feishu Cloud → Webhook (optional backup) → Internal Server
|
||||
```
|
||||
|
||||
**Advantages:**
|
||||
- No public domain required
|
||||
- No NAT/firewall configuration needed
|
||||
- Direct connection from internal network to Feishu cloud
|
||||
- Real-time message delivery (milliseconds vs minutes)
|
||||
- Connection复用,资源效率更高
|
||||
|
||||
### Architecture Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Feishu Open Platform │
|
||||
│ (WebSocket Event Subscription) │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────┴─────────────┐
|
||||
│ │
|
||||
┌─────▼──────┐ ┌──────▼─────┐
|
||||
│ Bot A │ │ Bot B │
|
||||
│ ws://.../A │ │ ws://.../B │
|
||||
└─────┬──────┘ └──────┬─────┘
|
||||
│ │
|
||||
┌─────▼──────────────────────────▼──────┐
|
||||
│ AuraK Server │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ FeishuModule │ │
|
||||
│ │ ┌────────────────────────────┐ │ │
|
||||
│ │ │ FeishuWsManager │ │ │
|
||||
│ │ │ - per-bot connections │ │ │
|
||||
│ │ │ - auto-reconnect │ │ │
|
||||
│ │ │ - message routing │ │ │
|
||||
│ │ └────────────────────────────┘ │ │
|
||||
│ │ ┌────────────────────────────┐ │ │
|
||||
│ │ │ FeishuService │ │ │
|
||||
│ │ │ - existing logic │ │ │
|
||||
│ │ │ - new ws connect/disconnect│ │ │
|
||||
│ │ └────────────────────────────┘ │ │
|
||||
│ │ ┌────────────────────────────┐ │ │
|
||||
│ │ │ FeishuController │ │ │
|
||||
│ │ │ - webhook endpoints │ │ │
|
||||
│ │ │ - ws management APIs │ │ │
|
||||
│ │ └────────────────────────────┘ │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Implementation Plan
|
||||
|
||||
### 2.1 New Components
|
||||
|
||||
#### 2.1.1 FeishuWsManager
|
||||
|
||||
**Purpose**: Manage WebSocket connections for each bot
|
||||
|
||||
**Responsibilities:**
|
||||
- Establish and maintain WebSocket connections
|
||||
- Handle connection lifecycle (connect, disconnect, reconnect)
|
||||
- Route incoming messages to appropriate bot handlers
|
||||
- Manage connection state per bot
|
||||
|
||||
**Location**: `server/src/feishu/feishu-ws.manager.ts`
|
||||
|
||||
**Key Methods:**
|
||||
```typescript
|
||||
class FeishuWsManager {
|
||||
// Start WebSocket connection for a bot
|
||||
async connect(bot: FeishuBot): Promise<void>
|
||||
|
||||
// Stop WebSocket connection for a bot
|
||||
async disconnect(botId: string): Promise<void>
|
||||
|
||||
// Get connection status
|
||||
getStatus(botId: string): ConnectionStatus
|
||||
|
||||
// Get all active connections
|
||||
getAllConnections(): Map<string, ConnectionStatus>
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.2 ConnectionStatus Type
|
||||
|
||||
```typescript
|
||||
enum ConnectionState {
|
||||
DISCONNECTED = 'disconnected',
|
||||
CONNECTING = 'connecting',
|
||||
CONNECTED = 'connected',
|
||||
ERROR = 'error'
|
||||
}
|
||||
|
||||
interface ConnectionStatus {
|
||||
botId: string
|
||||
state: ConnectionState
|
||||
connectedAt?: Date
|
||||
lastHeartbeat?: Date
|
||||
error?: string
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Modified Components
|
||||
|
||||
#### 2.2.1 FeishuService
|
||||
|
||||
**New Methods:**
|
||||
```typescript
|
||||
class FeishuService {
|
||||
// Start WebSocket connection for a bot
|
||||
async startWsConnection(botId: string): Promise<void>
|
||||
|
||||
// Stop WebSocket connection for a bot
|
||||
async stopWsConnection(botId: string): Promise<void>
|
||||
|
||||
// Get connection status
|
||||
async getWsStatus(botId: string): Promise<ConnectionStatus>
|
||||
|
||||
// List all connection statuses
|
||||
async getAllWsStatuses(): Promise<ConnectionStatus[]>
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.2 FeishuController
|
||||
|
||||
**New Endpoints:**
|
||||
```typescript
|
||||
// POST /feishu/bots/:id/ws/connect - Start WebSocket connection
|
||||
// POST /feishu/bots/:id/ws/disconnect - Stop WebSocket connection
|
||||
// GET /feishu/bots/:id/ws/status - Get connection status
|
||||
// GET /feishu/ws/status - Get all connection statuses
|
||||
```
|
||||
|
||||
**Modified Endpoints:**
|
||||
- Keep existing webhook endpoints unchanged
|
||||
- Add WebSocket status indicator in bot list response
|
||||
|
||||
#### 2.2.3 FeishuBot Entity
|
||||
|
||||
**New Fields:**
|
||||
```typescript
|
||||
@Entity('feishu_bots')
|
||||
export class FeishuBot {
|
||||
// ... existing fields ...
|
||||
|
||||
@Column({ default: false })
|
||||
useWebSocket: boolean
|
||||
|
||||
@Column({ nullable: true })
|
||||
wsConnectionState: string
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 Feishu SDK Integration
|
||||
|
||||
**Package**: `@larksuiteoapi/node-sdk`
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
cd server && yarn add @larksuiteoapi/node-sdk
|
||||
```
|
||||
|
||||
**Configuration:**
|
||||
```typescript
|
||||
import { EventDispatcher, Conf } from '@larksuiteoapi/node-sdk'
|
||||
|
||||
const client = new EventDispatcher({
|
||||
appId: bot.appId,
|
||||
appSecret: bot.appSecret,
|
||||
verificationToken: bot.verificationToken,
|
||||
}, {
|
||||
logger: console
|
||||
})
|
||||
```
|
||||
|
||||
### 2.4 Event Handling
|
||||
|
||||
**Flow for WebSocket Mode:**
|
||||
```
|
||||
Feishu Cloud ──WebSocket──> FeishuWsManager.on('message')
|
||||
│
|
||||
▼
|
||||
Parse event type
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
│ │ │
|
||||
im.message. im.message. other
|
||||
receive_v1 p2p_msg_received
|
||||
│ │
|
||||
▼ ▼
|
||||
FeishuService.processChatMessage()
|
||||
│
|
||||
▼
|
||||
Send response via
|
||||
FeishuService.sendTextMessage()
|
||||
```
|
||||
|
||||
### 2.5 Configuration Changes
|
||||
|
||||
**Feishu Open Platform:**
|
||||
|
||||
Users need to configure in Feishu developer console:
|
||||
1. Go to "Event Subscription" (事件与回调)
|
||||
2. Select "Use long connection to receive events" (使用长连接接收事件)
|
||||
3. Add event: `im.message.receive_v1`
|
||||
4. **Important**: Must start local WebSocket client first before saving
|
||||
|
||||
---
|
||||
|
||||
## 3. Data Flow
|
||||
|
||||
### WebSocket Message Flow
|
||||
|
||||
```
|
||||
1. User triggers connect API
|
||||
│
|
||||
▼
|
||||
2. FeishuController.connect(botId)
|
||||
│
|
||||
▼
|
||||
3. FeishuService.startWsConnection(botId)
|
||||
│
|
||||
▼
|
||||
4. FeishuWsManager.connect(bot)
|
||||
│
|
||||
▼
|
||||
5. SDK establishes WebSocket to open.feishu.cn
|
||||
│
|
||||
▼
|
||||
6. Connection established, events flow:
|
||||
│
|
||||
├─> on('message') ──> _processEvent() ──> _handleMessage()
|
||||
│ │
|
||||
│ ▼
|
||||
│ FeishuService.processChatMessage()
|
||||
│ │
|
||||
│ ▼
|
||||
│ FeishuService.sendTextMessage() (via SDK)
|
||||
│
|
||||
├─> on('error') ──> log error ──> trigger reconnect
|
||||
│
|
||||
└─> on('close') ──> trigger auto-reconnect
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Error Handling
|
||||
|
||||
### Connection Errors
|
||||
|
||||
| Error Type | Handling |
|
||||
|------------|----------|
|
||||
| Network timeout | Retry with exponential backoff (max 5 attempts) |
|
||||
| Invalid credentials | Mark bot as error state, notify user |
|
||||
| Token expired | Refresh token, reconnect |
|
||||
| Feishu server error | Wait 30s, retry |
|
||||
|
||||
### Auto-Reconnect Strategy
|
||||
|
||||
```
|
||||
Initial delay: 1 second
|
||||
Max delay: 60 seconds
|
||||
Backoff multiplier: 2x
|
||||
Max attempts: 5
|
||||
Reset on successful connection
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. API Design
|
||||
|
||||
### 5.1 Connect WebSocket
|
||||
|
||||
```
|
||||
POST /api/feishu/bots/:id/ws/connect
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"success": true,
|
||||
"botId": "bot_xxx",
|
||||
"status": "connecting"
|
||||
}
|
||||
|
||||
Response 400:
|
||||
{
|
||||
"success": false,
|
||||
"error": "Bot not found or disabled"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Disconnect WebSocket
|
||||
|
||||
```
|
||||
POST /api/feishu/bots/:id/ws/disconnect
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"success": true,
|
||||
"botId": "bot_xxx",
|
||||
"status": "disconnected"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Get Connection Status
|
||||
|
||||
```
|
||||
GET /api/feishu/bots/:id/ws/status
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"botId": "bot_xxx",
|
||||
"state": "connected",
|
||||
"connectedAt": "2026-03-17T10:00:00Z",
|
||||
"lastHeartbeat": "2026-03-17T10:05:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 Get All Statuses
|
||||
|
||||
```
|
||||
GET /api/feishu/ws/status
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"connections": [
|
||||
{
|
||||
"botId": "bot_xxx",
|
||||
"state": "connected",
|
||||
"connectedAt": "2026-03-17T10:00:00Z"
|
||||
},
|
||||
{
|
||||
"botId": "bot_yyy",
|
||||
"state": "disconnected"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Security Considerations
|
||||
|
||||
1. **Credential Storage**: App ID and App Secret stored encrypted in database
|
||||
2. **Connection Validation**: Verify bot belongs to authenticated user before connect
|
||||
3. **Rate Limiting**: Implement per-bot message rate limiting
|
||||
4. **Connection Limits**: Max 10 concurrent WebSocket connections per server instance
|
||||
|
||||
---
|
||||
|
||||
## 7. Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- FeishuWsManager connection lifecycle
|
||||
- Message routing logic
|
||||
- Error handling and reconnection
|
||||
|
||||
### Integration Tests
|
||||
- Full WebSocket connection flow
|
||||
- Message send/receive cycle
|
||||
- Reconnection after network failure
|
||||
|
||||
### Manual Testing
|
||||
- Local development without ngrok
|
||||
- Verify webhook still works alongside WebSocket
|
||||
|
||||
---
|
||||
|
||||
## 8. Backward Compatibility
|
||||
|
||||
- **Existing webhook endpoints**: Unchanged, continue to work
|
||||
- **Bot configuration**: Existing bots keep webhook mode by default
|
||||
- **Migration path**: Users can switch to WebSocket anytime via API
|
||||
- **Dual mode**: Both modes can run simultaneously for different bots
|
||||
|
||||
---
|
||||
|
||||
## 9. Migration Guide
|
||||
|
||||
### For Existing Users
|
||||
|
||||
1. Update AuraK to new version (with WebSocket support)
|
||||
2. Install Feishu SDK: `yarn add @larksuiteoapi/node-sdk`
|
||||
3. In Feishu Developer Console:
|
||||
- Start local WebSocket server
|
||||
- Change event subscription to "Use long connection"
|
||||
4. Call `POST /api/feishu/bots/:id/ws/connect` to activate
|
||||
|
||||
---
|
||||
|
||||
## 10. Limitations
|
||||
|
||||
1. **Outbound Network Required**: Server must be able to reach `open.feishu.cn` via WebSocket
|
||||
2. **Single Connection Per Bot**: Each bot needs its own WebSocket connection
|
||||
3. **Feishu SDK Required**: Must install official SDK, cannot use raw WebSocket
|
||||
4. **Private Feishu**: Does not support Feishu private deployment (自建飞书)
|
||||
|
||||
---
|
||||
|
||||
## 11. File Changes Summary
|
||||
|
||||
### New Files
|
||||
- `server/src/feishu/feishu-ws.manager.ts` - WebSocket connection manager
|
||||
- `server/src/feishu/dto/ws-status.dto.ts` - WebSocket status DTOs
|
||||
|
||||
### Modified Files
|
||||
- `server/src/feishu/feishu.service.ts` - Add WS methods
|
||||
- `server/src/feishu/feishu.controller.ts` - Add WS endpoints
|
||||
- `server/src/feishu/entities/feishu-bot.entity.ts` - Add WS fields
|
||||
- `server/src/feishu/feishu.module.ts` - Register new manager
|
||||
|
||||
### Dependencies
|
||||
- Add: `@larksuiteoapi/node-sdk`
|
||||
|
||||
---
|
||||
|
||||
## 12. Success Criteria
|
||||
|
||||
- [ ] Server can establish WebSocket connection to Feishu
|
||||
- [ ] Messages received via WebSocket are processed correctly
|
||||
- [ ] Responses sent back to Feishu via SDK
|
||||
- [ ] Auto-reconnect works after network interruption
|
||||
- [ ] Webhook mode continues to work unchanged
|
||||
- [ ] Both modes can coexist for different bots
|
||||
- [ ] Internal network deployment works without public domain
|
||||
Reference in New Issue
Block a user