Files
microdao-daarion/docs/matrix/MATRIX_CHAT_CLIENT_SPEC.md
2025-11-26 13:15:01 -08:00

14 KiB
Raw Permalink Blame History

MATRIX CHAT CLIENT — DAARION.city

Version: 1.0.0

0. PURPOSE

Зробити так, щоб сторінка /city/[slug] у DAARION UI була повноцінним Matrix-чатом:

  • використовує реальні Matrix rooms (matrix_room_id, matrix_room_alias),
  • працює від імені поточного користувача DAARION,
  • показує історію, нові повідомлення, статус підключення,
  • використовує існуючий Chat Layout (UI), але замість тимчасового WebSocket — Matrix.

Це базовий крок для подальшого:

  • Presence / Typing / Read receipts,
  • агентів як ботів,
  • 2D/2.5D City Map з live-активністю.

1. ARCHITECTURE OVERVIEW

┌─────────────────────────────────────────────────────────────────┐
│                      DAARION Frontend                           │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │               /city/[slug] Page                           │  │
│  │  ┌─────────────────┐  ┌────────────────────────────────┐  │  │
│  │  │   Room Info     │  │      Matrix Chat Client        │  │  │
│  │  │   (from API)    │  │  - Connect to Synapse          │  │  │
│  │  └─────────────────┘  │  - Send/receive messages       │  │  │
│  │                       │  - Show history                 │  │  │
│  │                       └────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                         Backend                                  │
│  ┌─────────────┐  ┌──────────────────┐  ┌───────────────────┐   │
│  │ auth-service│  │   city-service   │  │  matrix-gateway   │   │
│  │   (7020)    │  │     (7001)       │  │     (7025)        │   │
│  │             │  │                  │  │                   │   │
│  │ JWT tokens  │  │ /chat/bootstrap  │  │ /user/token       │   │
│  │ User→Matrix │  │ matrix_room_id   │  │ Create rooms      │   │
│  └─────────────┘  └──────────────────┘  └───────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Matrix Synapse (8018)                        │
│  - Rooms: !xxx:daarion.space                                    │
│  - Users: @daarion_xxx:daarion.space                            │
│  - Messages, history, sync                                      │
└─────────────────────────────────────────────────────────────────┘

Компоненти:

  • auth-service (7020)

    • знає user_id, email, Matrix user mapping.
  • matrix-gateway (7025)

    • вміє створювати кімнати (вже реалізовано),
    • буде видавати Matrix access tokens для користувачів.
  • city-service (7001)

    • надає matrix_room_id / matrix_room_alias,
    • новий endpoint /chat/bootstrap.
  • web (Next.js UI)

    • сторінка /city/[slug],
    • компонент ChatRoom,
    • Matrix chat client.

2. AUTH MODEL

Допущення (MVP):

  • Користувач уже залогінений у DAARION (JWT).
  • Для кожного user_id вже існує Matrix-акаунт (авто-provisioning реалізовано раніше).
  • Потрібен bootstrap endpoint, який:
    • по JWT → знаходить Matrix user,
    • видає Matrix access token,
    • повертає matrix_room_id для кімнати.

Matrix User Mapping

DAARION user_id Matrix user_id
87838688-d7c4-436c-... @daarion_87838688:daarion.space

3. BACKEND: CHAT BOOTSTRAP API

3.1. Endpoint: GET /api/city/chat/bootstrap

Розташування: city-service (логічно відповідає за City+Matrix інтеграцію)

Вхід:

  • HTTP заголовок Authorization: Bearer <access_token> (DAARION JWT)
  • query param: room_slug, наприклад energy

Логіка:

  1. Валідувати JWT → отримати user_id.
  2. Знайти city_room по slug:
    • витягнути matrix_room_id / matrix_room_alias.
  3. Через internal виклик до matrix-gateway:
    • отримати Matrix access token для цього user_id.
  4. Повернути фронтенду:
{
  "matrix_hs_url": "https://app.daarion.space",
  "matrix_user_id": "@daarion_87838688:daarion.space",
  "matrix_access_token": "syt_...",
  "matrix_room_id": "!gykdLyazhkcSZGHmbG:daarion.space",
  "matrix_room_alias": "#city_energy:daarion.space",
  "room": {
    "id": "room_city_energy",
    "slug": "energy",
    "name": "Energy"
  }
}

3.2. Matrix Gateway: User Token Endpoint

Endpoint: POST /internal/matrix/users/token

Request:

{
  "user_id": "87838688-d7c4-436c-9466-4ab0947d7730"
}

Response:

{
  "matrix_user_id": "@daarion_87838688:daarion.space",
  "access_token": "syt_...",
  "device_id": "DEVICE_ID"
}

Логіка:

  1. Побудувати Matrix username: daarion_{user_id[:8]}
  2. Спробувати логін з відомим паролем
  3. Якщо користувач не існує — створити через admin API
  4. Повернути access token

3.3. Security

  • Endpoint вимагає валідний DAARION JWT.
  • matrix_access_token — короткоживучий (30 хв) або session-based.
  • Internal endpoints (/internal/*) доступні тільки з Docker network.

4. FRONTEND: MATRIX CHAT CLIENT

4.1. Поточний Chat Layout

Вже існує:

  • сторінка /city/[slug],
  • компонент ChatRoom:
    • messages[],
    • onSend(message),
    • індикатор підключення.

Зараз він працює через свій WebSocket/stub.

4.2. Нова схема

  1. При завантаженні сторінки:

    // /city/[slug]/page.tsx
    const [bootstrap, setBootstrap] = useState(null);
    const [status, setStatus] = useState<'loading' | 'connecting' | 'online' | 'error'>('loading');
    
    useEffect(() => {
      async function init() {
        // 1. Отримати bootstrap дані
        const res = await fetch(`/api/city/chat/bootstrap?room_slug=${slug}`, {
          headers: { Authorization: `Bearer ${token}` }
        });
        const data = await res.json();
        setBootstrap(data);
    
        // 2. Ініціалізувати Matrix client
        setStatus('connecting');
      }
      init();
    }, [slug]);
    
  2. Створення Matrix клієнта:

    // Використовуємо REST API напряму (без matrix-js-sdk для простоти MVP)
    const matrixClient = new MatrixRestClient({
      baseUrl: bootstrap.matrix_hs_url,
      accessToken: bootstrap.matrix_access_token,
      userId: bootstrap.matrix_user_id,
      roomId: bootstrap.matrix_room_id
    });
    
  3. Отримання історії:

    const messages = await matrixClient.getMessages(roomId, { limit: 50 });
    
  4. Відправка повідомлень:

    await matrixClient.sendMessage(roomId, {
      msgtype: 'm.text',
      body: text
    });
    
  5. Підписка на нові повідомлення:

    // Long-polling або sync
    matrixClient.onMessage((event) => {
      setMessages(prev => [...prev, mapMatrixEvent(event)]);
    });
    

4.3. Matrix Event → Chat Message Mapping

function mapMatrixEvent(event: MatrixEvent): ChatMessage {
  return {
    id: event.event_id,
    senderId: event.sender,
    senderName: event.sender.split(':')[0].replace('@daarion_', 'User '),
    text: event.content.body,
    timestamp: new Date(event.origin_server_ts),
    isUser: event.sender === bootstrap.matrix_user_id,
  };
}

5. MATRIX REST CLIENT (Lightweight)

Замість важкого matrix-js-sdk, створимо легкий REST клієнт:

// lib/matrix-client.ts

export class MatrixRestClient {
  private baseUrl: string;
  private accessToken: string;
  private userId: string;
  
  constructor(config: MatrixClientConfig) {
    this.baseUrl = config.baseUrl;
    this.accessToken = config.accessToken;
    this.userId = config.userId;
  }
  
  // Get room messages
  async getMessages(roomId: string, options?: { limit?: number; from?: string }) {
    const params = new URLSearchParams({
      dir: 'b',
      limit: String(options?.limit || 50)
    });
    if (options?.from) params.set('from', options.from);
    
    const res = await fetch(
      `${this.baseUrl}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/messages?${params}`,
      { headers: this.authHeaders() }
    );
    return res.json();
  }
  
  // Send text message
  async sendMessage(roomId: string, body: string) {
    const txnId = `m${Date.now()}`;
    const res = await fetch(
      `${this.baseUrl}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${txnId}`,
      {
        method: 'PUT',
        headers: this.authHeaders(),
        body: JSON.stringify({
          msgtype: 'm.text',
          body: body
        })
      }
    );
    return res.json();
  }
  
  // Join room
  async joinRoom(roomId: string) {
    const res = await fetch(
      `${this.baseUrl}/_matrix/client/v3/join/${encodeURIComponent(roomId)}`,
      {
        method: 'POST',
        headers: this.authHeaders()
      }
    );
    return res.json();
  }
  
  // Sync (for real-time updates)
  async sync(since?: string) {
    const params = new URLSearchParams({ timeout: '30000' });
    if (since) params.set('since', since);
    
    const res = await fetch(
      `${this.baseUrl}/_matrix/client/v3/sync?${params}`,
      { headers: this.authHeaders() }
    );
    return res.json();
  }
  
  private authHeaders() {
    return {
      'Authorization': `Bearer ${this.accessToken}`,
      'Content-Type': 'application/json'
    };
  }
}

6. UI / UX REQUIREMENTS

6.1. Стан підключення

Status UI
loading Skeleton loader
connecting "Підключення до Matrix…" + spinner
online Зелений індикатор "Онлайн"
error Червоний індикатор + "Помилка підключення" + кнопка "Повторити"

6.2. Відображення історії

  • При завантаженні показувати останні 50 повідомлень
  • Infinite scroll для старіших повідомлень
  • Показувати дату-роздільник між днями

6.3. Надсилання повідомлень

  • Enter — відправити
  • Shift+Enter — новий рядок
  • Показувати "sending..." стан
  • При помилці — показати "Не вдалося відправити" + retry

7. LIMITATIONS / MVP

Поки що:

  • Тільки текстові повідомлення (m.text)
  • Без файлів/зображень
  • Без threads/reactions
  • Без read receipts
  • Без typing indicators

Це все буде додано у наступних фазах.


8. API SUMMARY

City Service (7001)

Method Endpoint Description
GET /api/city/chat/bootstrap?room_slug=X Bootstrap Matrix chat

Matrix Gateway (7025)

Method Endpoint Description
POST /internal/matrix/users/token Get/create user token

9. ROADMAP AFTER THIS

Після Matrix Chat Client:

  1. Presence & Typing:

    • слухати m.presence, m.typing → показувати "online/typing".
  2. Reactions & read receipts.

  3. Attachments (фото/файли).

  4. City Map інтеграція (активність кімнат → візуалізація).


10. ACCEPTANCE CRITERIA

  • /api/city/chat/bootstrap повертає Matrix credentials для авторизованого користувача
  • Frontend підключається до Matrix і показує історію повідомлень
  • Користувач може надсилати повідомлення через DAARION UI
  • Повідомлення з'являються в Element Web і навпаки
  • Обробляються стани: loading, connecting, online, error