Skip to content

REST API & Cấu hình

1. Controller

Mã nguồn: src/controllers/websocket-clients/websocket-client.controller.tsKế thừa: BaseControllerBase Path: /socket/websocket/clients (định nghĩa trong RestPaths.WEBSOCKET_CLIENTS) DI Dependencies: SignalEventService

typescript
// packages/signal/src/controllers/websocket-clients/websocket-client.controller.ts
@controller({ path: RestPaths.WEBSOCKET_CLIENTS })
export class WebSocketClientController extends BaseController {
  constructor(
    @inject({
      key: BindingKeys.build({
        namespace: BindingNamespaces.SERVICE,
        key: SignalEventService.name,
      }),
    })
    private readonly _signalEventService: SignalEventService,
  ) {
    super({ scope: WebSocketClientController.name });
  }
}

2. REST Endpoints

Tiền tố đường dẫn đầy đủ: /v1/api/socket/websocket/clients

Phương thứcĐường dẫnXác thựcRoute KeyMô tả
GET/statusKhôngGET_STATUSTrạng thái sẵn sàng của server và số client đang kết nối
GET/JWT, BasicGET_CLIENTSLiệt kê tất cả client đang kết nối
GET/{clientId}JWT, BasicGET_CLIENT_BY_IDLấy chi tiết client cụ thể
POST/broadcastJWT, BasicBROADCASTPhát tin nhắn đến tất cả client
POST/rooms/{roomName}/sendJWT, BasicSEND_TO_ROOMGửi tin nhắn đến một phòng
POST/{clientId}/sendJWT, BasicSEND_TO_CLIENTGửi tin nhắn đến một client cụ thể
POST/{clientId}/disconnectJWT, BasicDISCONNECT_CLIENTBuộc ngắt kết nối client

3. Định nghĩa Route (OpenAPI)

Mã nguồn: src/controllers/websocket-clients/definitions.ts

Tất cả route được định nghĩa sử dụng IAuthenticateRouteConfig với Zod schemas cho xác thực request và tài liệu OpenAPI.

Schemas dùng chung

typescript
// packages/signal/src/controllers/websocket-clients/definitions.ts
const ClientIdParam = z.object({
  clientId: z.string().openapi({
    param: { name: 'clientId', in: 'path', description: 'WebSocket client ID' },
  }),
});

const RoomNameParam = z.object({
  roomName: z.string().openapi({
    param: { name: 'roomName', in: 'path', description: 'WebSocket room name' },
  }),
});

const MessageBody = z.object({
  topic: z.string(),
  data: z.any(),
});

GET /status

Không yêu cầu xác thực.

Response:

json
{ "isReady": true, "clientCount": 5 }

Response schema: z.object({ isReady: z.boolean(), clientCount: z.number() })

Logic controller:

typescript
const isReady = this._signalEventService.isReady();
const clientCount = isReady ? this._signalEventService.getClientCount() : 0;
return context.json({ isReady, clientCount }, HTTP.ResultCodes.RS_2.Ok);

GET / (Danh sách Client)

Xác thực: JWT hoặc Basic.

Response:

json
{
  "clients": [
    {
      "id": "abc123",
      "state": "authenticated",
      "encrypted": true,
      "rooms": ["ws-default"],
      "connectedAt": 1704067200000,
      "lastActivity": 1704067260000,
      "metadata": {}
    }
  ]
}

Response schema: z.object({ clients: z.array(WebSocketClientInfoSchema) })

GET /

Xác thực: JWT hoặc Basic. Trả về 404 nếu không tìm thấy client.

Response schema: WebSocketClientInfoSchema

Logic controller:

typescript
const client = this._signalEventService.getClientInfo({ clientId });
if (!client) {
  return context.json({ error: 'Client not found' }, HTTP.ResultCodes.RS_4.NotFound);
}
return context.json(client, HTTP.ResultCodes.RS_2.Ok);

POST /broadcast

Xác thực: JWT hoặc Basic.

Request body:

json
{ "topic": "order.updated", "data": { "orderId": "123" } }

Request schema: MessageBodyz.object({ topic: z.string(), data: z.any() })

Response: { "success": true }

POST /rooms/{roomName}/send

Xác thực: JWT hoặc Basic.

Path params: roomName (string) Request body: Giống broadcast (MessageBody) Response: { "success": true }

POST /{clientId}/send

Xác thực: JWT hoặc Basic.

Path params: clientId (string) Request body: Giống broadcast (MessageBody) Response: { "success": true }

POST /{clientId}/disconnect

Xác thực: JWT hoặc Basic. Trả về 404 nếu không tìm thấy client.

Response:

json
// Thành công
{ "success": true }

// Không tìm thấy
{ "error": "Client not found" }

Logic controller:

typescript
const success = this._signalEventService.disconnectClient({ clientId });
if (!success) {
  return context.json({ error: 'Client not found' }, HTTP.ResultCodes.RS_4.NotFound);
}
return context.json({ success: true }, HTTP.ResultCodes.RS_2.Ok);

4. Chính sách bảo mật

Quy tắcThực thi
Mã hóa bắt buộcrequireEncryption: true — client phải hoàn thành ECDH handshake trong quá trình xác thực
Không public key = từ chốiXác thực thiếu trường clientPublicKey sẽ bị từ chối handshake
Yêu cầu JWTChỉ hỗ trợ xác thực Bearer token cho WebSocket qua JWTTokenService
Xác thực RESTREST endpoints chấp nhận cả JWT (Authorization: Bearer <token>) và Basic (Authorization: Basic <base64>)
Khóa theo clientMỗi client nhận khóa AES-256-GCM dẫn xuất từ ECDH tạm thời riêng
Forward secrecyCặp khóa ECDH mới cho mỗi kết nối; khóa phiên không bao giờ tái sử dụng
Dọn dẹp khóaKhóa AES bị xóa khỏi bộ nhớ server khi client ngắt kết nối
Mã hóa gửi đioutboundTransformer tự động mã hóa tất cả tin nhắn gửi đi (trừ connectederror)
Xác thực đầu vàomessageHandler xác thực cấu trúc { iv, ct } trước khi giải mã

5. Biên dịch Binary độc lập

Script compile tạo binary Linux x64 độc lập cho triển khai trong Docker container debian:12-slim:

bash
#!/bin/sh
bun build \
  --compile \
  --minify-whitespace \
  --minify-syntax \
  --sourcemap \
  --target=bun-linux-x64 \
  ./dist/index.js \
  --outfile ./dist/signal

Kể từ @venizia/ignis 0.0.7-7 đã loại bỏ các dependency tùy chọn nặng (socket.io, bullmq, v.v.) khỏi barrel export, script biên dịch đơn giản không cần workaround stub hay flag --external.

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.