§01Что показывать клиенту
Заказчик хочет понимать три вещи: что именно ему продают, на какой стадии работа, и что ему нужно подписать. Всё остальное — шум.
- Клиентская смета — только утверждённая, с корректировками после их согласования.
- Прогресс работ — процент выполнения по объекту в целом и по каждой группе работ.
- Документы на подпись — клиентские акты КС-2, КС-3, дополнительные соглашения.
- История подписанных документов — то, что клиент уже одобрил и может скачать.
- Контактное лицо — менеджер на нашей стороне с именем, телефоном, почтой.
§02Что скрывать всегда
Клиент никогда не видит подрядной стороны учёта. Это не настройка, а инвариант системы.
- Подрядные сметы и их цены.
- Подрядные акты и справки.
- Любые документы, где side = CONTRACTOR.
- Маржу, расчёт маржи, плановую/фактическую разницу.
- Контрагентов-подрядчиков и их контакты.
- Внутренние комментарии менеджеров и ПТО.
- Чужие договоры и объекты другого клиента.
Утечка подрядной информации — баг критической приоритетности. В actrix такой случай привёл бы к немедленному откату последнего деплоя и разбору на уровне CTO. Не «доработка к следующему спринту», а «стоп-фикс».
§03Как реализована изоляция
Изоляция работает на трёх уровнях, и каждый последующий страхует предыдущий.
Уровень 1 — UI. В кабинете клиента просто нет элементов, отображающих подрядную сторону. Это первая линия обороны и самая слабая — UI всегда можно обойти.
Уровень 2 — middleware API. Все запросы из кабинета клиента проходят через guard, который проверяет роль (CLIENT_USER) и добавляет в запрос фильтры side=CLIENT и clientId=$currentClient. Любой запрос на ресурс с side=CONTRACTOR возвращает 403.
Уровень 3 — БД. На уровне Postgres настроены row-level security policies, которые отрабатывают, даже если в коде middleware появится баг. Это последний барьер, который не даёт случайному запросу вытащить чужие строки.
§04Инвариант
Сформулировано в документации архитектуры так: «Нет ни одного запроса из кабинета клиента, который мог бы вернуть строку, для которой нарушено условие side=CLIENT AND clientId=current_client». Этот инвариант проверяется автотестами на каждый коммит.
Если изоляция кабинетов — это «настройка», она однажды сломается. Если это инвариант — она ломаться не может, потому что её не на что переключать. — Из документации архитектуры actrix