server-websocket

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Steedos Server WebSocket | Steedos 服务端 WebSocket

Steedos 服务端 WebSocket

Overview | 概述

概述

Steedos Server uses Socket.IO via
@nestjs/websockets
for real-time communication. The
AppGateway
handles connections, authentication, room subscriptions, and event broadcasting.
Steedos 服务端使用 Socket.IO 进行实时通信。
AppGateway
处理连接、认证、房间订阅和事件广播。
Steedos 服务端通过
@nestjs/websockets
使用 Socket.IO 实现实时通信。
AppGateway
负责处理连接、认证、房间订阅和事件广播。

Gateway Configuration | 网关配置

网关配置

typescript
@WebSocketGateway({ path: "/socket.io/", cors: true })
export class AppGateway implements OnGatewayConnection, OnGatewayDisconnect
  • Path:
    /socket.io/
  • CORS: Enabled for all origins
  • Adapter:
    HybridAdapter
    from
    @builder6/core
typescript
@WebSocketGateway({ path: "/socket.io/", cors: true })
export class AppGateway implements OnGatewayConnection, OnGatewayDisconnect
  • 路径
    /socket.io/
  • 跨域(CORS):允许所有源访问
  • 适配器:来自
    @builder6/core
    HybridAdapter

Connection Authentication | 连接认证

连接认证

On connect, the gateway authenticates via cookies from the handshake:
  1. Parse
    cookie
    header from
    socket.handshake.headers
  2. Extract
    X-Space-Id
    (tenant) and
    X-Auth-Token
    (auth token)
  3. Validate via
    AuthService.getUserByToken(token)
  4. Create session:
    { user, anonymous, system, portal: { tenantId } }
Unauthenticated connections are marked as
anonymous
.
连接建立时,网关通过握手请求中的 Cookie 进行认证:
  1. socket.handshake.headers
    解析
    cookie
    请求头
  2. 提取
    X-Space-Id
    (租户ID)和
    X-Auth-Token
    (认证令牌)
  3. 通过
    AuthService.getUserByToken(token)
    验证令牌有效性
  4. 创建会话:
    { user, anonymous, system, portal: { tenantId } }
未通过认证的连接会被标记为
anonymous
(匿名)。

Room 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

客户端 → 服务端

EventPayloadDescription
subscribe
{ roomParts, individual }
Join rooms
unsubscribe
{ roomParts, individual }
Leave rooms
ping
dateSystem connection keep-alive
事件负载描述
subscribe
{ roomParts, individual }
加入房间
unsubscribe
{ roomParts, individual }
离开房间
ping
日期系统连接保活

Server → Client

服务端 → 客户端

EventPayloadDescription
connection-init
Sent on successful connection
pong
UTC dateResponse to ping
s:metadata:change
{ type, action, _id, name, objectName }
Metadata change (objects, fields, apps, etc.)
s:notification-change
{ message }
User notification (per-user room)
s:record:{objectApiName}:change-{id}
{ _id }
Record change event
事件负载描述
connection-init
连接成功时发送
pong
UTC日期对ping的响应
s:metadata:change
{ type, action, _id, name, objectName }
元数据变更(对象、字段、应用等)
s:notification-change
{ message }
用户通知(个人房间)
s:record:{objectApiName}:change-{id}
{ _id }
记录变更事件

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 EventWebSocket Action
$metadata.*
s:metadata:change
(global broadcast)
$broadcast.$notification.users
s:notification-change
(per-user rooms)
$broadcast.socket.emit
→ Generic socket emit with optional room
@objectRecordEvent.*.*
s:record:{obj}:change-{id}
(record rooms)
$socket.subscribe.*
Moleculer event emitted when client subscribes
WebSocket网关与Moleculer事件紧密集成:
Moleculer 事件WebSocket 操作
$metadata.*
s:metadata:change
(全局广播)
$broadcast.$notification.users
s:notification-change
(个人房间)
$broadcast.socket.emit
→ 带可选房间参数的通用socket事件发送
@objectRecordEvent.*.*
s:record:{obj}:change-{id}
(记录房间)
$socket.subscribe.*
客户端订阅时触发的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: "您有新任务"
  }
});