092_UI_core / docs /API_SPACES_ROOMS.md
anotherath's picture
update space and room
7aa8153

API Documentation — Spaces & Rooms

Tài liệu dành cho Frontend để tích hợp API Spaces và Rooms.


Base URL

/api

Authentication

Tất cả endpoints yêu cầu header:

Authorization: Bearer <access_token>
Content-Type: application/json

1. Spaces API (/spaces)

1.1 Lấy danh sách spaces của user

GET /spaces

Response:

{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Lớp Toán 12A",
      "description": "Không gian học tập lớp 12A",
      "icon_url": "https://...",
      "owner_id": "user-uuid",
      "is_private": true,
      "invite_code": "abc123",
      "created_at": "2026-05-01T10:00:00Z",
      "updated_at": "2026-05-01T10:00:00Z"
    }
  ]
}

1.2 Tạo space mới

POST /spaces

Request Body:

{
  "name": "Lớp Toán 12A",
  "description": "Không gian học tập",
  "icon": "https://...",
  "isPrivate": true
}
Field Type Required Constraints
name string Min 2, Max 100 chars
description string Max 500 chars
icon string (URL)
isPrivate boolean Default: false

Response:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Lớp Toán 12A",
    "description": "Không gian học tập",
    "icon_url": "https://...",
    "owner_id": "user-uuid",
    "is_private": true,
    "invite_code": "abc123",
    "created_at": "2026-05-01T10:00:00Z"
  }
}

1.3 Lấy chi tiết space

GET /spaces/:spaceId

Params:

  • spaceId (UUID) — ID của space

Response:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Lớp Toán 12A",
    "description": "Không gian học tập",
    "icon_url": "https://...",
    "owner_id": "user-uuid",
    "is_private": true,
    "invite_code": "abc123",
    "created_at": "2026-05-01T10:00:00Z"
  }
}

Lỗi:

  • 403 Forbidden — User không phải member của space private
  • 404 Not Found — Space không tồn tại

1.4 Cập nhật space

PATCH /spaces/:spaceId

Request Body:

{
  "name": "Lớp Toán 12A (Updated)",
  "description": "Mô tả mới",
  "icon": "https://...",
  "isPrivate": false
}

Lưu ý: Chỉ owner hoặc admin mới có quyền cập nhật. Tất cả fields optional.

Response:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Lớp Toán 12A (Updated)",
    ...
  }
}

1.5 Xóa space

DELETE /spaces/:spaceId

Lưu ý: Chỉ owner mới có quyền xóa. Trả về 204 No Content.


1.6 Tìm kiếm spaces công khai

GET /spaces/search?q=toán

Query Params:

  • q (string) — Từ khóa tìm kiếm (tối đa 100 ký tự)

Response:

{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Lớp Toán 12A",
      "description": "...",
      "is_private": false
    }
  ]
}

1.7 Lấy danh sách rooms trong space

GET /spaces/:spaceId/rooms

Response:

{
  "success": true,
  "data": [
    {
      "id": "room-uuid-1",
      "space_id": "space-uuid",
      "name": "Thông báo",
      "description": "Kênh thông báo chính",
      "type": "text",
      "is_private": false,
      "created_at": "2026-05-01T10:00:00Z"
    },
    {
      "id": "room-uuid-2",
      "space_id": "space-uuid",
      "name": "Tài liệu",
      "description": "Chia sẻ tài liệu",
      "type": "text",
      "is_private": false,
      "created_at": "2026-05-01T10:00:00Z"
    }
  ]
}

1.8 Thêm member vào space

POST /spaces/:spaceId/members

Request Body:

{
  "userId": "550e8400-e29b-41d4-a716-446655440000",
  "role": "member"
}
Field Type Required Values
userId UUID
role string "member" (default), "admin"

Lưu ý: Chỉ owner hoặc admin mới có quyền thêm member.

Response:

{
  "success": true,
  "data": {
    "id": "member-id",
    "space_id": "space-uuid",
    "user_id": "user-uuid",
    "role": "member",
    "joined_at": "2026-05-01T10:00:00Z"
  }
}

1.9 Tạo mã mờispace

POST /spaces/:spaceId/invite

Lưu ý: Chỉ owner hoặc admin mới có quyền tạo mã mờ.

Response:

{
  "success": true,
  "data": {
    "inviteCode": "abc123"
  }
}

1.10 Tham gia space bằng mã mờ

POST /spaces/join/:code

Params:

  • code (string) — Mã mờ

Response:

{
  "success": true,
  "data": {
    "id": "space-uuid",
    "name": "Lớp Toán 12A",
    ...
  }
}

1.11 Rờikhỏi space

POST /spaces/:spaceId/leave

Lưu ý: Owner không thể rờikhỏi space. Trả về 204 No Content.


2. Rooms API

2.1 Tạo room trong space

POST /spaces/:spaceId/rooms

Request Body:

{
  "name": "Thảo luận",
  "description": "Phòng thảo luận bài tập",
  "type": "text",
  "isPrivate": false
}
Field Type Required Values Default
name string Min 2, Max 100 chars
description string Max 500 chars
type string "text", "voice" "text"
isPrivate boolean false

Lưu ý: Chỉ space member mới có quyền tạo room.

Response:

{
  "success": true,
  "data": {
    "id": "room-uuid",
    "space_id": "space-uuid",
    "name": "Thảo luận",
    "description": "Phòng thảo luận bài tập",
    "type": "text",
    "is_private": false,
    "created_at": "2026-05-01T10:00:00Z"
  }
}

2.2 Lấy chi tiết room

GET /rooms/:roomId

Response:

{
  "success": true,
  "data": {
    "id": "room-uuid",
    "space_id": "space-uuid",
    "name": "Thảo luận",
    "description": "Phòng thảo luận bài tập",
    "type": "text",
    "is_private": false,
    "created_at": "2026-05-01T10:00:00Z"
  }
}

Lỗi:

  • 403 Forbidden — User không phải member của room

2.3 Cập nhật room

PATCH /rooms/:roomId

Request Body:

{
  "name": "Thảo luận (Updated)",
  "description": "Mô tả mới",
  "type": "text",
  "isPrivate": true
}

Lưu ý: Chỉ room creator hoặc space admin mới có quyền cập nhật. Tất cả fields optional.

Response:

{
  "success": true,
  "data": {
    "id": "room-uuid",
    "name": "Thảo luận (Updated)",
    ...
  }
}

2.4 Xóa room

DELETE /rooms/:roomId

Lưu ý: Chỉ room creator hoặc space admin mới có quyền xóa.

Response:

{
  "success": true,
  "message": "Room deleted successfully"
}

2.5 Lấy danh sách rooms trong space

GET /spaces/:spaceId/rooms

Cùng chức năng với 1.7, trả về danh sách rooms trong space.


2.6 Lấy danh sách members trong room

GET /rooms/:roomId/members

Response:

{
  "success": true,
  "data": [
    "user-id-1",
    "user-id-2",
    "user-id-3"
  ]
}

2.7 Thêm member vào room

POST /rooms/:roomId/members

Request Body:

{
  "userId": "550e8400-e29b-41d4-a716-446655440000"
}
Field Type Required
userId UUID

Lưu ý:

  • User phải là member của space trước
  • Chỉ room creator hoặc space admin mới có quyền thêm

Response:

{
  "success": true,
  "message": "Member added successfully"
}

2.8 Xóa member khỏi room

DELETE /rooms/:roomId/members/:userId

Lưu ý: Chỉ room creator hoặc space admin mới có quyền xóa.

Response:

{
  "success": true,
  "message": "Member removed successfully"
}

2.9 Lấy thống kê room

GET /rooms/:roomId/stats

Response:

{
  "success": true,
  "data": {
    "memberCount": 25,
    "messageCount": 1200,
    "lastActivity": "2026-05-04T10:00:00Z"
  }
}

3. Members API (/spaces/:spaceId/members)

3.1 Lấy danh sách members trong space

GET /spaces/:spaceId/members

Response:

{
  "success": true,
  "data": [
    {
      "id": "user-id-1",
      "email": "alice@example.com",
      "username": "alice",
      "displayName": "Alice",
      "avatar": "https://...",
      "status": "online",
      "role": "owner",
      "joinedAt": "2026-05-01T10:00:00Z"
    },
    {
      "id": "user-id-2",
      "email": "bob@example.com",
      "username": "bob",
      "displayName": "Bob",
      "avatar": "https://...",
      "status": "offline",
      "role": "member",
      "joinedAt": "2026-05-02T10:00:00Z"
    }
  ]
}

3.2 Tìm kiếm members trong space

GET /spaces/:spaceId/members/search?q=alice

Query Params:

  • q (string) — Từ khóa tìm kiếm username/display_name/email

Response:

{
  "success": true,
  "data": [
    {
      "id": "user-id-1",
      "email": "alice@example.com",
      "username": "alice",
      "displayName": "Alice",
      "avatar": "https://...",
      "status": "online",
      "role": "owner",
      "joinedAt": "2026-05-01T10:00:00Z"
    }
  ]
}

3.3 Lấy role của member

GET /spaces/:spaceId/members/:userId/role

Response:

{
  "success": true,
  "data": {
    "role": "admin"
  }
}

3.4 Cập nhật role member

PATCH /spaces/:spaceId/members/:userId/role

Request Body:

{
  "role": "admin"
}
Field Type Required Values
role string "member", "admin"

Lưu ý:

  • Chỉ owner mới có quyền promote lên admin
  • Không thể thay đổi role của owner

Response:

{
  "success": true,
  "data": {
    "id": "member-id",
    "space_id": "space-uuid",
    "user_id": "user-uuid",
    "role": "admin",
    "joined_at": "2026-05-01T10:00:00Z"
  }
}

3.5 Lấy activity của member

GET /spaces/:spaceId/members/:userId/activity

Response:

{
  "success": true,
  "data": {
    "lastActive": "2026-05-04T10:00:00Z",
    "messageCount": 150,
    "reactionCount": 45
  }
}

3.6 Xóa member khỏi space

DELETE /spaces/:spaceId/members/:userId

Lưu ý:

  • Chỉ owner hoặc admin mới có quyền xóa
  • Admin không thể xóa owner
  • Owner không thể bị xóa
  • Member có thể tự xóa chính mình (leave)

Response:

{
  "success": true,
  "message": "Member removed successfully"
}

4. Error Responses

Format lỗi chuẩn

{
  "success": false,
  "message": "Error description",
  "error": "ERROR_CODE"
}

Các mã lỗi phổ biến

Status Code Mô tả
400 BAD_REQUEST Request không hợp lệ (validation error)
401 UNAUTHORIZED Token hết hạn hoặc không hợp lệ
403 FORBIDDEN Không có quyền thực hiện
404 NOT_FOUND Resource không tồn tại
409 CONFLICT Conflict (đã tồn tại, v.v.)
429 RATE_LIMIT Quá nhiều requests

Ví dụ lỗi 403

{
  "statusCode": 403,
  "message": "You are not a member of this space",
  "error": "Forbidden"
}

Ví dụ lỗi 400 (Validation)

{
  "statusCode": 400,
  "message": ["name must be longer than or equal to 2 characters"],
  "error": "Bad Request"
}

5. DTO Reference

CreateSpaceDto

{
  name: string;           // required, min 2, max 100 chars
  description?: string;   // optional, max 500 chars
  icon?: string;          // optional, URL
  isPrivate?: boolean;    // optional, default: false
}

UpdateSpaceDto

{
  name?: string;          // optional, min 2, max 100 chars
  description?: string;   // optional, max 500 chars
  icon?: string;          // optional, URL
  isPrivate?: boolean;    // optional
}

CreateRoomDto

{
  name: string;           // required, min 2, max 100 chars
  description?: string;   // optional, max 500 chars
  type?: string;          // optional, default: "text" ("text" | "voice")
  isPrivate?: boolean;    // optional, default: false
}

UpdateRoomDto

{
  name?: string;
  description?: string;
  type?: string;          // "text" | "voice"
  isPrivate?: boolean;
}

AddMemberDto

{
  userId: string;         // required, UUID
  role?: string;          // optional, default: "member" ("member" | "admin")
}

AddRoomMemberDto

{
  userId: string;         // required, UUID
}

UpdateRoleDto

{
  role: string;           // required, "member" | "admin"
}

6. Flow gợi ý cho Frontend

Tạo space mới

async function createSpace(name: string, description?: string) {
  const response = await fetch('/api/spaces', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ name, description, isPrivate: false }),
  });
  return response.json();
}

Lấy danh sách spaces và rooms

async function loadSpacesAndRooms() {
  // 1. Lấy danh sách spaces
  const spacesRes = await fetch('/api/spaces', {
    headers: { 'Authorization': `Bearer ${token}` },
  });
  const { data: spaces } = await spacesRes.json();

  // 2. Với mỗi space, lấy rooms
  for (const space of spaces) {
    const roomsRes = await fetch(`/api/spaces/${space.id}/rooms`, {
      headers: { 'Authorization': `Bearer ${token}` },
    });
    const { data: rooms } = await roomsRes.json();
    space.rooms = rooms;
  }

  return spaces;
}

Tham gia room qua WebSocket

import { io } from 'socket.io-client';

const socket = io('wss://api.example.com/chat', {
  auth: { token: accessToken },
});

// Join room
socket.emit('joinRoom', { roomId: 'room-uuid' });

// Listen events
socket.on('joinedRoom', (data) => {
  console.log('Joined room:', data);
});

socket.on('newMessage', (message) => {
  console.log('New message:', message);
});

Thêm member vào space

async function addMember(spaceId: string, userId: string, role: string = 'member') {
  const response = await fetch(`/api/spaces/${spaceId}/members`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ userId, role }),
  });
  return response.json();
}

7. Permissions Summary

Action Who can do it
Create space Any authenticated user
Update space Owner, Admin
Delete space Owner only
Add member Owner, Admin
Remove member Owner, Admin (except owner)
Update member role Owner only
Create room Space member
Update room Room creator, Space admin
Delete room Room creator, Space admin
Add room member Room creator, Space admin
Remove room member Room creator, Space admin
Generate invite code Owner, Admin
Join by invite code Any authenticated user
Leave space Member (not owner)

8. Cache Notes

Endpoint Cache
GET /spaces/:spaceId/rooms private, max-age=30
GET /rooms/:roomId/members Redis cache 5 phút
GET /spaces/:spaceId/members Redis cache 5 phút

Frontend nên implement stale-while-revalidate cho danh sách spaces/rooms.