server-websocket
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSteedos Server WebSocket | Steedos 服务端 WebSocket
Steedos 服务端 WebSocket
Overview | 概述
概述
Steedos Server uses Socket.IO via for real-time communication. The handles connections, authentication, room subscriptions, and event broadcasting.
@nestjs/websocketsAppGatewaySteedos 服务端使用 Socket.IO 进行实时通信。 处理连接、认证、房间订阅和事件广播。
AppGatewaySteedos 服务端通过 使用 Socket.IO 实现实时通信。 负责处理连接、认证、房间订阅和事件广播。
@nestjs/websocketsAppGatewayGateway Configuration | 网关配置
网关配置
typescript
@WebSocketGateway({ path: "/socket.io/", cors: true })
export class AppGateway implements OnGatewayConnection, OnGatewayDisconnect- Path:
/socket.io/ - CORS: Enabled for all origins
- Adapter: from
HybridAdapter@builder6/core
typescript
@WebSocketGateway({ path: "/socket.io/", cors: true })
export class AppGateway implements OnGatewayConnection, OnGatewayDisconnect- 路径:
/socket.io/ - 跨域(CORS):允许所有源访问
- 适配器:来自 的
@builder6/coreHybridAdapter
Connection Authentication | 连接认证
连接认证
On connect, the gateway authenticates via cookies from the handshake:
- Parse header from
cookiesocket.handshake.headers - Extract (tenant) and
X-Space-Id(auth token)X-Auth-Token - Validate via
AuthService.getUserByToken(token) - Create session:
{ user, anonymous, system, portal: { tenantId } }
Unauthenticated connections are marked as .
anonymous连接建立时,网关通过握手请求中的 Cookie 进行认证:
- 从 解析
socket.handshake.headers请求头cookie - 提取 (租户ID)和
X-Space-Id(认证令牌)X-Auth-Token - 通过 验证令牌有效性
AuthService.getUserByToken(token) - 创建会话:
{ user, anonymous, system, portal: { tenantId } }
未通过认证的连接会被标记为 (匿名)。
anonymousRoom System | 房间系统
房间系统
Rooms are scoped by tenant ID to ensure multi-tenant isolation:
Room format: {tenantId}-{roomPart}
Individual: {roomPart}-{userId}房间按租户ID划分范围,确保多租户隔离:
房间格式:{tenantId}-{roomPart}
个人房间:{roomPart}-{userId}Subscribe / Unsubscribe | 订阅 / 取消订阅
订阅 / 取消订阅
javascript
// Client-side
socket.emit("subscribe", {
roomParts: ["orders", "notifications"],
individual: false // shared room
});
socket.emit("subscribe", {
roomParts: "notification-change",
individual: true // per-user room: "notification-change-{userId}"
});
socket.emit("unsubscribe", {
roomParts: ["orders"]
});javascript
// 客户端代码
socket.emit("subscribe", {
roomParts: ["orders", "notifications"],
individual: false // 共享房间
});
socket.emit("subscribe", {
roomParts: "notification-change",
individual: true // 个人房间:"notification-change-{userId}"
});
socket.emit("unsubscribe", {
roomParts: ["orders"]
});Events | 事件
事件
Client → Server
客户端 → 服务端
| Event | Payload | Description |
|---|---|---|
| | Join rooms |
| | Leave rooms |
| date | System connection keep-alive |
| 事件 | 负载 | 描述 |
|---|---|---|
| | 加入房间 |
| | 离开房间 |
| 日期 | 系统连接保活 |
Server → Client
服务端 → 客户端
| Event | Payload | Description |
|---|---|---|
| — | Sent on successful connection |
| UTC date | Response to ping |
| | Metadata change (objects, fields, apps, etc.) |
| | User notification (per-user room) |
| | Record change event |
| 事件 | 负载 | 描述 |
|---|---|---|
| — | 连接成功时发送 |
| UTC日期 | 对ping的响应 |
| | 元数据变更(对象、字段、应用等) |
| | 用户通知(个人房间) |
| | 记录变更事件 |
Metadata Change Events | 元数据变更事件
元数据变更事件
Triggered when objects, fields, listviews, actions, or apps change:
javascript
// Emitted to ALL connected clients (global broadcast)
socket.on("s:metadata:change", ({ type, action, _id, name, objectName }) => {
// type: "apps", "objects", "object_listviews", "object_actions", "object_fields"
// action: "insert", "update", "delete"
// Refresh UI accordingly
});当对象、字段、列表视图、操作或应用发生变更时触发:
javascript
// 广播至所有连接的客户端(全局广播)
socket.on("s:metadata:change", ({ type, action, _id, name, objectName }) => {
// type: "apps", "objects", "object_listviews", "object_actions", "object_fields"
// action: "insert", "update", "delete"
// 据此刷新UI
});Record Change Events | 记录变更事件
记录变更事件
Scoped to specific rooms for fine-grained updates:
javascript
// Client subscribes to a specific record
socket.emit("subscribe", {
roomParts: `record:orders:change-${recordId}`,
individual: false
});
// Server emits when record changes
socket.on(`s:record:orders:change-${recordId}`, ({ _id }) => {
// Reload record
});Room name format:
{tenantId}-record:{objectApiName}:change-{recordId}针对特定房间进行细粒度更新:
javascript
// 客户端订阅特定记录
socket.emit("subscribe", {
roomParts: `record:orders:change-${recordId}`,
individual: false
});
// 记录变更时服务端发送事件
socket.on(`s:record:orders:change-${recordId}`, ({ _id }) => {
// 重新加载记录
});房间名称格式:
{tenantId}-record:{objectApiName}:change-{recordId}Notification Events | 通知事件
通知事件
Per-user notifications via individual rooms:
javascript
// Client subscribes
socket.emit("subscribe", {
roomParts: "notification-change",
individual: true // creates room: "notification-change-{userId}"
});
// Server pushes notification
socket.on("s:notification-change", ({ message }) => {
// Show notification
});通过个人房间发送用户专属通知:
javascript
// 客户端订阅
socket.emit("subscribe", {
roomParts: "notification-change",
individual: true // 创建房间:"notification-change-{userId}"
});
// 服务端推送通知
socket.on("s:notification-change", ({ message }) => {
// 显示通知
});Moleculer Integration | Moleculer 集成
Moleculer 集成
The WebSocket gateway is tightly integrated with Moleculer events:
| Moleculer Event | WebSocket Action |
|---|---|
| → |
| → |
| → Generic socket emit with optional room |
| → |
| Moleculer event emitted when client subscribes |
WebSocket网关与Moleculer事件紧密集成:
| Moleculer 事件 | WebSocket 操作 |
|---|---|
| → |
| → |
| → 带可选房间参数的通用socket事件发送 |
| → |
| 客户端订阅时触发的Moleculer事件 |
Broadcasting from Moleculer Services | 从 Moleculer 服务广播
从 Moleculer 服务广播
javascript
// Emit to specific room from a Moleculer service
broker.emit("$broadcast.socket.emit", {
data: {
eventName: "custom-event",
eventParams: { key: "value" },
room: "tenantId-room-name" // optional, omit for global
}
});
// Send notification to specific users
broker.emit("$broadcast.$notification.users", {
data: {
tenantId: "space_id",
users: ["user1", "user2"],
message: "You have a new task"
}
});javascript
// 从Moleculer服务向特定房间发送事件
broker.emit("$broadcast.socket.emit", {
data: {
eventName: "custom-event",
eventParams: { key: "value" },
room: "tenantId-room-name" // 可选参数,省略则全局广播
}
});
// 向特定用户发送通知
broker.emit("$broadcast.$notification.users", {
data: {
tenantId: "space_id",
users: ["user1", "user2"],
message: "您有新任务"
}
});