Skip to content

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          
   UserMatrix    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:
  3. витягнути matrix_room_id / matrix_room_alias.
  4. Через internal виклик до matrix-gateway:
  5. отримати Matrix access token для цього user_id.
  6. Повернути фронтенду:
{
  "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. При завантаженні сторінки: ```tsx // /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]); ```

  1. Створення Matrix клієнта: tsx // Використовуємо 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 });

  2. Отримання історії: tsx const messages = await matrixClient.getMessages(roomId, { limit: 50 });

  3. Відправка повідомлень: tsx await matrixClient.sendMessage(roomId, { msgtype: 'm.text', body: text });

  4. Підписка на нові повідомлення: tsx // 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:
  2. слухати m.presence, m.typing → показувати "online/typing".

  3. Reactions & read receipts.

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

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


10. ACCEPTANCE CRITERIA

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