Сессии и контексты в Metarhia

В Impress нет middleware и AsyncLocalStorage. Сессии и контексты сделаны через Proxy на data и явный инжект context в прикладной код.

Базовые идеи

  • Сессия — anonymous или authenticated. Создаётся всегда (даже для гостя).
  • Контекст — данные текущего запроса.
  • Несколько соединений на одну сессию — пользователь открыл много вкладок, сессия одна, RPC-каналов несколько.

Изоляция

«Нам нужно изолировать контексты друг от друга, изолировать системный код от прикладного, чтобы нельзя было из клиентского кода как-то обращаться к реквесту и респонсу.»

В Express/Nest часто пробивается до req, res, что нарушает абстракцию. В Metarhia:

  • В прикладной код доступен только context.
  • context.client — клиент (с методом emit для событий).
  • HTTP-внутрянка (request, response, headers) — недоступна.

Persistence сессий

Сессии могут сохраняться в БД:

  • Таблица SystemSession — token, data, IP, время создания.
  • Таблица SystemUser — пользователи.
  • Маленький модуль с методами register, restoreSession, validate.

Counter через context.data

// applications/myapp/api/incrementCounter.js
module.exports = async (args, context) => {
  context.data.counter = (context.data.counter || 0) + 1;
  return context.data.counter;
};

context.data — Proxy, который сессия отслеживает. При каждом изменении знает, что надо сохранить.

Proxy и инжект

«Сессия создала коллекшен data, обернула его в Proxy и инжектнула в прикладной код контекст. Поэтому каждый раз, когда мы что-то читаем или пишем в контекст, сессия может об этом знать.»

Mechanism:

  1. Impress принял запрос.
  2. Достал сессию по токену.
  3. Создал контекст для запроса.
  4. Обернул context.data в Proxy.
  5. Передал в прикладную функцию.
  6. После вызова — сохранил измененное в БД (если authenticated).

Без AsyncLocalStorage

Большинство Node-фреймворков используют ALS (AsyncLocalStorage) для контекста: «достань текущий контекст откуда угодно в стеке». Это сложно для отладки и имеет накладные расходы.

Metarhia: контекст явно прокидывается аргументом метода. context приходит как второй параметр в RPC-функцию.

Несколько соединений → одна сессия

Tab 1 ─→ WebSocket 1 ─┐
Tab 2 ─→ WebSocket 2 ─┼─→ Session (id=abc, user=roma)
Tab 3 ─→ WebSocket 3 ─┘

Все три канала разделяют одну сессию. Изменения context.data синкаются между ними через event bus.

Аутентификация

В Impress есть готовые методы:

  • registerUser({ login, password }) → токен.
  • signIn({ login, password }) → токен.
  • Auth-pluggable через адаптеры (если нужен OAuth/SAML).

🎓 Источники

См. также