О платформе SuraqHub Введение

SuraqHub — это корпоративная отказоустойчивая платформа для проектирования интерактивных электронных форм, проведения опросов, сбора структурированных данных, их валидации, антивирусной проверки и передачи во внешние аналитические хранилища в режиме реального времени.

⚡ Ключевые возможности
  • Визуальный конструктор с поддержкой более 30 типов компонентов, адаптивных сеток и формул (полное руководство).
  • Антивирусная защита: каждый загружаемый файл изолируется и проверяется через ClamAV до попадания в хранилище.
  • Асинхронная архитектура: экспорт сотен тысяч анкет в Excel (XLSX) выполняется в фоне через RabbitMQ.
  • Интеграции корпоративного уровня: синхронный GraphQL API по M2M-ключам, моментальные вебхуки с криптографической подписью HMAC-SHA256 и pull-канал аналитики для Power BI через выделенную схему БД.
  • Соответствие стандартам: поддержка светлой/темной тем, accessibility WCAG 2.2 AA и строгие политики безопасности Content Security Policy (CSP).

Концепции и Базовые Сущности Архитектура

В основе архитектуры SuraqHub лежит четкое разделение между структурой вопросов (схемой) и точкой доступа к анкете (публикацией):

📝 Шаблон формы (Form Schema)

Это JSON-структура, которая описывает вопросы, валидацию, типы полей, выпадающие списки и условную логику. Шаблоны неизменяемы: при каждом сохранении создается новая ревизия в истории версий, что гарантирует целостность уже собранных ответов.

🌐 Публикация (Publication)

Это конфигурация веб-страницы анкеты. Содержит Slug (человекочитаемый адрес, например /f/feedback), статус доступности (Активна / Неактивна), брендинг (Header, Footer), тему оформления и привязку к конкретной версии шаблона формы.

💡 Преимущество разделения:

Вы можете заранее конструировать и тестировать новые версии формы, а затем в один клик переключить публикацию на новую версию без простоя пользователей или изменения адреса ссылки.

Жизненный цикл создания формы Workflow

Стандартный рабочий процесс от задумки до получения готовых отчетов состоит из 5 шагов:

  1. Создание публикации (Экран «Основное»): задайте название анкеты и ее будущий веб-адрес (Slug).
  2. Конструирование вопросов (Экран «Конструктор»): соберите структуру полей, настройте обязательность, подсказки и логику показа.
  3. Брендинг и локализация (Экраны «Дизайн» и «Переводы»): настройте шапку компании, подвал и добавьте перевод на казахский/английский языки.
  4. Предпросмотр и проверка (Экран «Предпросмотр»): заполните тестовую анкету, убедитесь в корректности расчетов и валидации.
  5. Активация и сбор ответов: включите тумблер «Статус: Активно» и анализируйте результаты на экране «Отчетность» либо через Webhooks/API.

Раздел «Основное» (Управление публикациями) Экран

Центральный экран для мониторинга всех опубликованных анкет и управления ревизиями.

Элементы управления

  • Кнопка «Новая публикация»: Очищает форму создания и переводит менеджер в режим регистрации нового адреса.
  • Поиск (Живой фильтр): Мгновенно фильтрует список публикаций по названию или слагу.
  • Поле «Заголовок публикации»: Название, отображаемое пользователям в заголовке вкладки браузера и в шапке анкеты.
  • Поле «URL (Адрес)» / Slug: Уникальная строка латиницей (например, anketa-2026). Итоговая ссылка на форму: https://domain/f/anketa-2026.
  • Переключатель «Статус: Активно»: Если выключен, пользователям при попытке заполнения отобразится экран завершения приема ответов (Inactive Screen).
  • История ревизий и Откат (Rollback): В нижней части карточки отображается таймлайн всех сохраненных версий с датой и автором. Клик по ревизии мгновенно загружает историческую версию формы в конструктор.

Административный список публикаций

Для администраторов доступен сквозной список «Публикации» со всеми анкетами платформы, включая созданные другими пользователями:

  • Поиск: по названию публикации, слагу или названию формы.
  • Фильтры: статус (активные/неактивные), тип доступа (публичные, приватные, по ролям) и владелец.
  • Действия: переход к редактированию любой публикации, активация/деактивация; в таблице видны количество ответов и дата завершения приёма.
  • Создатели без роли администратора видят в разделе «Основное» только свои публикации.

Раздел «Конструктор форм» Экран

Визуальный drag-and-drop редактор цифровых форм и интерактивных анкет.

Структура рабочей среды

  • Левая палитра компонентов: Содержит базовые поля (Текст, Число, Чекбокс, Файлы) и разметку (Панели, Колонки). Перетащите их мышью в правую рабочую зону. Полный справочник всех компонентов — в руководстве конструктора.
  • Рабочий холст: Отображает интерактивный макет будущей формы. При наведении на любой компонент доступна панель быстрых действий:
    • Настройки: Открывает диалог с параметрами валидации, API-ключа и подсказок.
    • Копировать: Клонирует поле со всеми его вложенными правилами.
    • Вставить ниже: Вставляет ранее скопированный компонент.
    • JSON: Прямое редактирование схемы поля (доступно в Продвинутом режиме).
    • Переместить: Ручное перемещение компонента в другую позицию на холсте.
    • Удалить: Удаляет компонент из схемы.
⚙️ Продвинутый режим (Advanced Mode)

Включается в разделе «Настройки». Позволяет опытным разработчикам настраивать кастомные формулы вычислений, JSONLogic, интеграцию с PDF и редактировать JSON схемы компонентов напрямую. Подробное руководство.

Раздел «Дизайн публикации» Экран

Управление внешним видом веб-страниц, отображаемых респондентам.

Возможности кастомизации

  • Выбор готовых шаблонов (Ассетов): Быстрое подключение корпоративной шапки, подвала или экрана неактивности из централизованного справочника ассетов.
  • Редактор кода (Ace Editor): Встроенный редактор с подсветкой синтаксиса HTML/CSS для написания собственного оформления.
  • Панель живого предпросмотра (Preview Box): Рендерит верстку в реальном времени параллельно с вводом кода.
  • Стилизация темы: Поддержка светлой темы (Corporate Light) и высококонтрастной темной темы (Dark Enterprise).

Встраивание формы на другой сайт

Публикацию можно встроить как фрейм на внешний сайт — например, на корпоративный портал, чтобы респонденты заполняли анкету, не покидая привычный сайт.

  1. На вкладке «Дизайн» включите переключатель «Разрешить встраивание формы на внешние сайты» в карточке «Встраивание (Embed)».
  2. Укажите разрешённые домены — по одному в строке в формате https://www.ktz.kz. Форму можно встроить только на сайт, чей домен есть в этом списке (защита от встраивания на произвольные ресурсы через CSP frame-ancestors), до 10 доменов.
  3. Нажмите «Сохранить дизайн» — настройка вступает в силу через несколько секунд.
  4. Скопируйте готовый код для встраивания кнопкой «Копировать код» и передайте его владельцу сайта. Код содержит фрейм и скрипт автоматической подгонки высоты.

Со стороны сайта-хоста больше ничего не нужно: разработчик просто вставляет полученный код на страницу — ключи, токены и регистрация не требуются. Собственный фрейм по адресу формы тоже сработает, но без скрипта из сниппета высоту придётся задать фиксированной. Важно соблюдать порядок: домен сайта должен оказаться в списке до публикации кода на их стороне, иначе браузер заблокирует фрейм.

Ограничения:

  • Формы с ролевым доступом тоже встраиваются: внутри фрейма появится кнопка «Войти», вход откроется в отдельном окне (брендированная страница единого входа), после входа форма загрузится автоматически — без перезагрузки страницы сайта-хоста.
  • Формы с обязательной подписью (NCALayer) во фрейме могут не получить доступ к программе подписи из-за браузерных ограничений (Private Network Access). В этом случае форма сама предложит «Открыть в новой вкладке» — в отдельной вкладке подпись работает. Проверяйте на целевом сайте.
  • Если флаг включён, но список доменов пуст, встраивание запрещено — форма по-прежнему защищена от помещения во фрейм.

Модульный брендинг & White-label (КТЖ Forms) Платформа

Архитектура брендирования платформы под корпоративного заказчика по модели Co-branding / White-label («Powered by SuraqHub»).

Концепция «КТЖ Forms by SuraqHub»

Для корпоративного внедрения в АО «НК «ҚТЖ» платформа развернута в ко-брендинговом исполнении: для сотрудников и подразделений система позиционируется как доверенный внутренний сервис «КТЖ Forms» с указанием платформенного ядра «by SuraqHub».

  • Без форков и дублирования кода: единая кодовая база SuraqHub обслуживает как стандартные установки, так и брендированные инстансы заказчиков.
  • Конфигурация на лету: параметры брендинга управляются через Kubernetes ConfigMap (suraqhub-frontend-env) и файл конфигурации env.js.
  • Zero Downtime & Мгновенное обновление: файл конфигурации защищён заголовками no-cache и монтируется как том, изменения вступают в силу без пересборки Docker-образа.

Где применяется брендинг

Элемент интерфейса Отображение для КТЖ Параметр в конфигурации
Вкладка браузера (Title) КТЖ Forms by SuraqHub | Корпоративная система форм PAGE_TITLE / APP_NAME
Фавикон во вкладке Фирменная иконка КТЖ в тёмном контейнере (ktz-icon.svg) FAVICON_URL
Заставка загрузки (Preloader) Логотип КТЖ, заголовок КТЖ Forms и бейдж powered by SuraqHub LOADER_BADGE, POWERED_BY
Шапка панели (Header) Векторная эмблема КТЖ + название КТЖ Forms + бейдж powered by SuraqHub LOGO_URL, APP_NAME
Экран входа (Login Gate) Логотип КТЖ, заголовок КТЖ Forms, плашка powered by SuraqHub и подсказка учетной записи LOGIN_HINTS (ru/kk/en)
Окно проверки сессии Логотип КТЖ, заголовок КТЖ Forms, кнопка возврата APP_NAME
Корпоративная палитра Основной синий акцент КТЖ (#0066b2) в CSS-токенах PRIMARY_COLOR

Пример конфигурации в env.js

window.APP_CONFIG = {
    AUTH_SERVER_URL: 'https://auth.ktz.dostyq.app',
    AUTH_REALM: 'SuraqHub',
    AUTH_CLIENT_ID: 'suraqhub-api',

    BRANDING: {
        APP_NAME: "КТЖ Forms",
        POWERED_BY: "powered by SuraqHub",
        PAGE_TITLE: "КТЖ Forms powered by SuraqHub | Корпоративная система форм",
        LOADER_BADGE: null, // или "АО «НК «ҚТЖ»" при необходимости дополнительного префикса
        LOGIN_HINTS: {
            ru: "Корпоративная учётная запись ҚТЖ",
            kk: "ҚТЖ корпоративтік тіркелгісі",
            en: "KTZ Corporate Account"
        },
        LOGO_URL: "/static/ktz-emblem.svg",
        FAVICON_URL: "/static/ktz-icon.svg",
        PRIMARY_COLOR: "#0066b2",
        COPYRIGHT: "© 2026 АО «НК «Қазақстан темір жолы» • На платформе SuraqHub"
    }
};

Раздел «Переводы» (Локализация) Экран

Обеспечивает мультиязычность анкет для русскоязычной, казахскоязычной и международной аудитории.

Как работает локализация

  1. При переходе на вкладку система автоматически парсит текущую схему формы и находит все текстовые строки: метки полей, подсказки (placeholder), описания и тексты ошибок.
  2. В интерактивной таблице выводятся столбцы для языков: Русский (RU), Казахский (KK) и Английский (EN).
  3. Экспорт в CSV: Вы можете выгрузить таблицу в файл для передачи профессиональным переводчикам.
  4. При заполнении анкеты респондент выбирает удобный язык в переключателе, и все вопросы формы мгновенно переводятся без перезагрузки страницы.

Раздел «Справочники (НСИ)» Экран

Централизованное ведение нормативно-справочной информации: списки подразделений, регионов, категорий и других значений, которые используются в выпадающих списках форм.

Реестр и редактор справочников

  • Реестр справочников: таблица всех справочников со статусом, прод-версией и датой обновления. Новый справочник создается кнопкой «Создать справочник».
  • Встроенный редактор: табличное редактирование записей — добавление строк, живой поиск по таблице, редактирование двойным кликом по ячейке.
  • Импорт и экспорт: массовая загрузка значений из Excel/CSV, выгрузка текущего черновика в Excel, вставка строк из буфера обмена (Ctrl+C в Excel → клик по таблице → Ctrl+V).
  • Черновик и публикация: правки сохраняются в черновик кнопкой «Сохранить черновик»; «Опубликовать» делает черновик прод-версией, из которой формы читают значения.
  • Внешние справочники: в строке справочника с иконкой облака отображается статус последней синхронизации, а кнопка-молния запускает синхронизацию вручную.

Внешние справочники (автосинхронизация)

Справочник может получать значения из внешнего источника автоматически: платформа сама забирает данные (server-to-server), приводит их к контракту справочника и публикует новую прод-версию — только если данные действительно изменились. Типовой сценарий — транспортные справочники внешнего онтологического API (станции, категории поездов), ведение которых выполняется внешней системой.

  • Настройка: кнопка-облако в строке справочника открывает карточку источника — URL ресурса, пароль/креды (Basic), трансформер (маппинг полей), переменная окружения (альтернатива паролю), интервал обновления в минутах, вкл/выкл.
  • Ручная синхронизация: кнопка-молния в строке реестра (или в карточке источника) запускает внеочередное обновление сразу, не дожидаясь интервала.
  • Журнал обновлений: в карточке источника виден журнал последних попыток — время, статус, HTTP-код, сколько записей получено и изменено, какая ревизия опубликована.
  • Ревизии и консистентность: каждое изменение источника фиксируется как новая неизменяемая ревизия справочника с метаданными версии внешнего набора данных. Формы продолжают читать значения даже при недоступности внешнего источника — используется последняя опубликованная версия. Ручное редактирование черновика внешнего справочника заблокировано: следующий синк его перезапишет.
  • Откат: к любой прошлой ревизии можно мгновенно вернуться через историю публикаций — так же, как для обычных справочников.

Автозаполнение полей по пользователю (префилл)

Поля формы могут заполняться автоматически из профиля пользователя, который её открыл: ФИО — из учётной записи (Keycloak), объекты (участки, станции) — из персональных назначений, которые ведутся во вкладке «Назначения».

  • Как работает: у поля формы включается свойство prefill — при открытии формы значение подставляется из профиля (ФИО при этом блокируется от правки), а варианты выпадающих списков фильтруются до назначенных пользователю объектов.
  • Гарантия на сервере: при отправке анкеты сервер проверяет данные независимо от браузера — ФИО всегда перезаписывается значением из учётной записи, а выбор объектов сверяется с назначениями. Подменить значение через DevTools невозможно.
  • Если назначений нет: список объектов остаётся полным (режим fallback), а выбор пользователя помечается как ручной. Такие значения попадают во вкладку «Назначения» как кандидаты — администратор одобряет их, и со следующего рапорта поля заполняются автоматически.
  • Вкладка «Назначения»: выдача объектов вручную (поиск пользователя по email), список активных назначений и кандидатов, одобрение и удаление. Кандидаты создаются автоматически из сабмитов, где пользователь выбрал объект вручную.
🔐 Безопасность внешних источников:

Пароль указывается отдельно для каждого справочника и хранится в защищенной таблице (как у вебхуков); интерфейс никогда не показывает его повторно и позволяет только заменить или удалить. Альтернатива — переменная окружения сервиса: в карточке указывается лишь имя переменной, а её значение не попадает в базу и интерфейс.

💡 Подключение справочника к форме:

В выпадающем списке конструктора укажите тип источника данных «URL» и адрес словаря /api/v1/dictionaries/{код}. При обновлении справочника форма подхватит новые значения автоматически — новая ревизия формы не требуется. Пошаговый рецепт — в разделе Практические сценарии руководства конструктора.

🔐 Доступ:

Вкладка доступна ролям admin и dictionary_manager — см. карточку «Роли и права доступа» в разделе Безопасность.

Раздел «Предпросмотр» Экран

Интерактивная среда для верификации готовой анкеты перед публикацией.

Экран рендерит точную копию боевого интерфейса респондента со всеми подключенными стилями, шрифтами, шапкой и подвалом. Позволяет протестировать отправку формы, обязательные поля, маски телефонов, прикрепление файлов и логику ветвления вопросов.

Раздел «Отчетность и Аналитика» Экран

Инструменты для визуализации данных и выгрузки собранных ответов.

Возможности аналитики

  • Счетчики в реальном времени: Общее количество отправленных анкет (сабмишнов) и дата последнего ответа.
  • График «Динамика ответов»: Временная шкала заполнения анкеты по дням.
  • Географическая карта ответов: Интерактивная карта Казахстана с кластеризацией точек отправки анкет (для форм, собирающих геолокацию).
  • Детализация по полям: Выберите один или несколько вопросов из выпадающего списка — система автоматически построит круговые и столбчатые диаграммы распределения вариантов ответов.
  • Асинхронный экспорт в Excel (XLSX): При нажатии кнопки «Скачать Excel» задача передается воркерам RabbitMQ. Готовый отчет скачивается мгновенно, не блокируя работу интерфейса.

Раздел «Настройки системы» Экран

Параметры безопасности, антивируса и обслуживания платформы.

Системные параметры

  • Максимальный размер загрузки: Ограничение размера файлов (в МБ), разрешенных к прикреплению в формах.
  • Часовой пояс платформы: Определяет корректный расчет таймстемпов в аналитике и выгрузках Excel.
  • Языки переводов форм: Управляет языками, доступными на вкладке «Переводы» и в опубликованных формах. Русский язык остаётся основным; казахский, кыргызский, английский и узбекский можно включать и выключать.
  • Антивирусный сканер (ClamAV): Включение проверки файлов на вирусы и адрес сервиса.
  • Справочник ролей Keycloak: Сопоставление системных ролей платформы (Admin, Creator, Viewer) с токенами Keycloak.
  • Пакет диагностики: Формирует единый архив с логами микросервисов, состоянием очередей RabbitMQ и очередью ошибок DLQ для быстрой технической поддержки.

Панель «Состояние системы»

В разделе «Обслуживание» показываются живые параметры инфраструктуры. Панель обновляется кнопкой «Обновить» и при каждом открытии вкладки. Как читать показатели:

ПараметрНорма и тревожные признаки
Рабочие очереди (form_submissions, templates.v1, publications.modular, reports.tasks, files.quarantine)Глубина 0 — норма (рост на несколько сообщений на секунды допустим при пиковой нагрузке). Консьюмеров должно быть ≥ 1. Тревога: глубина растёт и не опускается либо консьюмеров 0 — обработчик упал или недоступна БД.
Retry-очереди (submissions_retry_queue и аналогичные)Глубина 0 в спокойном состоянии; единицы сообщений — норма (повторы с задержкой). Консьюмеров 0 — норма: это парковка с таймером, их читает сам брокер по таймауту.
Очереди ошибок (DLQ) (dead_letter_queue, webhooks.failed, reports.failed)Норма — строго 0. Любое значение больше нуля — сигнал разбираться: DLQ не разгружается сама. Строки подсвечиваются красным.
RabbitMQ / База данных / RedisЗелёная отметка — компонент доступен. Красный крест — сервис недоступен; при наведении показывается причина.
Анкет за час / за 24 часаДолжно соответствовать ожидаемому трафику. Ноль при активном трафике — критичный признак: ответы заполняющих не сохраняются.
Записей идемпотентностиМонотонный рост — норма (защита от дублей). Десятки тысяч записей без чистки замедляют обработку — вопрос к администратору БД.
Активных webhook-подписокСколько подписок реально получают события. Ноль — события никуда не отправляются (проверьте вкладку «Интеграции»). Подписка с постоянно падающей доставкой копит ошибки в webhooks.failed.
💡 Короткое правило

Рабочие очереди: 0 — хорошо, консьюмеры — обязательны. Retry: ноль и консьюмеров ноль — нормально. DLQ: только ноль. Счётчик анкет стоит на нуле, а люди заполняют формы, — срочно смотреть логи savetodb.

API-ключи (M2M Integration & GraphQL) Интеграции

Синхронный доступ внешних BI/ETL систем (PowerBI, Tableau, корпоративные CRM) к аналитике и выгрузкам без интерактивного входа через Keycloak.

Безопасность API-токенов

  • Префикс whsec_: Токен генерируется криптографически стойким генератором и выводится только один раз при создании.
  • Хэширование SHA-256: В БД платформы сохраняется только хэш токена. Даже в случае компрометации базы исходный ключ восстановить невозможно.
  • Ограничение видимости (Scopes): Ключ можно сделать глобальным либо ограничить доступом только к выбранным публикациям.

Пример GraphQL-запроса через cURL

curl -X POST https://suraqhub.domain.com/graphql \
  -H "Content-Type: application/json" \
  -H "X-API-Key: whsec_your_secret_api_key_here" \
  -d '{
    "query": "query GetPubStats($id: UUID!) { publicationStats(publicationId: $id) { totalSubmissions lastSubmissionDate } }",
    "variables": { "id": "16a5ff0e-f1ff-4b8a-9887-c5f907912ea1" }
  }'

Вебхуки в реальном времени (Webhooks) Real-Time

Асинхронная отправка HTTP POST уведомлений на ваши серверы при наступлении событий в SuraqHub.

Типы событий:

  • submission.created — пользователь заполнил и успешно отправил форму.
  • form.saved — изменена или сохранена структура вопросов формы.
  • publication.saved — изменена или активирована публикация.

Спецификация JSON Payload:

{
  "event": "submission.created",
  "event_id": "9f1c2d3e-4b5a-6c7d-8e9f-0a1b2c3d4e5f",
  "timestamp": "2026-09-02T15:30:00.000Z",
  "publication_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "form_id": "f5e4d3c2-b1a0-9876-5432-10fedcba9876",
  "submission_id": "77aa88bb-99cc-00dd-11ee-22ff33aa44bb",
  "data": {
    "fullName": "Алексей Смирнов",
    "phoneNumber": "+77015554433",
    "score": 5,
    "comment": "Отличная скорость работы!"
  }
}

Проверка подписи HMAC-SHA256 (Заголовок X-SuraqHub-Signature)

Каждый запрос подписывается вашим секретом подписки. Сервер-приемник обязан проверить подпись перед обработкой:

Пример на Python (FastAPI / Flask):

import hmac, hashlib
from fastapi import FastAPI, Request, HTTPException, Header

app = FastAPI()
WEBHOOK_SECRET = "whsec_your_secret_key"

@app.post("/webhook")
async def receive_webhook(request: Request, x_suraqhub_signature: str = Header(None)):
    raw_body = await request.body()
    expected = hmac.new(WEBHOOK_SECRET.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()

    if not x_suraqhub_signature or not hmac.compare_digest(expected, x_suraqhub_signature):
        raise HTTPException(status_code=403, detail="Invalid HMAC signature")

    payload = await request.json()
    print(f"✅ Успешно получено событие {payload['event']}")
    return {"status": "ok"}

Гарантии надежности (SRE Retry Policy):

  • Manual ACK: Сообщение подтверждается в RabbitMQ только после получения от вашего сервера HTTP-кода 200 OK.
  • Экспоненциальный Retry: При сетевой ошибке или коде 5xx платформа повторяет отправку до 5 раз с увеличивающимся интервалом.
  • Dead Letter Queue (DLQ): При фатальных ошибках (404, 403) сообщение безопасно сохраняется в очереди брака suraqhub.webhooks.failed для ручного анализа.

Аналитика для BI (схема analytics) Pull / BI

Прямое подключение Power BI (и любого инструмента с PostgreSQL-коннектором) к выделенной read-only схеме базы данных для построения дэшбордов и отчетов по накопленным анкетам.

Чем этот канал отличается от вебхуков и API-ключей:

  • Вебхуки — мгновенные push-уведомления для систем-автоматизаций (реакция на событие за секунды).
  • API-ключи — синхронные запросы к GraphQL API для внешних сервисов (детали конкретной анкеты, бизнес-логика).
  • Схема analytics — история для человека: дэшборды, воронки, сводные отчеты в Power BI с обновлением по расписанию.

Модель данных:

  • vw_submissions — шапки анкет: когда, какая публикация, язык, наличие подписи. Без содержимого ответов.
  • vw_submission_answers — ответы в «длинном» формате (одна строка = один ответ). Универсален для любых форм: в Power BI разворачивается в широкую таблицу сводной таблицей (Pivot).
  • vw_submission_geo — координаты устройств (широта, долгота, точность) для форм, собирающих геолокацию.
  • vw_publications — справочник публикаций: названия, политики отправки, подписи и геолокации, периоды активности.

Безопасность и приватность:

  • Подключение выполняется под выделенной read-only ролью powerbi_reader, которой доступна только схема analytics. Служебные таблицы платформы (API-ключи, подписки, аудит) роли недоступны.
  • Персональные данные не отдаются: ИИН, криптографические подписи и сырые метаданные в вью не включены.
  • Запись невозможна по определению — роль имеет только право чтения.

Подключение:

В Power BI Desktop: Получить данные → PostgreSQL, укажите адрес сервера и базы данных suraqhub, авторизация — под учетной записью powerbi_reader (выдается администратором платформы). Для опубликованных дэшбордов добавьте тот же источник в локальный Data Gateway.

Рекомендация: используйте режим импорта (Import) и настраивайте обновление по расписанию (например, ежечасно или ночью) — тяжелые дэшборды не должны опрашивать базу чаще необходимого.

Для развертываний с высокой нагрузкой платформа предусматривает read-only реплику БД: администратор может направить аналитические отчеты и BI-подключения на копию базы (DATABASE_RO_URL), оставив основную базу только для приема анкет. Подробности — в документе suraqhub-docs/analytics_powerbi.md.

Скачивание файлов и медиа (FileConductor API) File API

Безопасное программное и пользовательское скачивание вложений (фотографий, видеозаписей, сканов), прикрепленных респондентами в формах.

1. Как файлы передаются во внешние системы

При получении вебхука submission.created или выгрузке отчетов в Excel/CSV, вложения представлены массивом объектов внутри submission_data:

{
  "event": "submission.created",
  "submission_id": "123e4567-e89b-12d3-a456-426614174000",
  "submission_data": {
    "media_field": [
      {
        "file_id": "c0378e90-f99a-4c28-bb8d-d602ff59187a",
        "originalName": "photo_bridge.jpg",
        "size": 2450123,
        "type": "image/jpeg",
        "url": "/api/files/download/c0378e90-f99a-4c28-bb8d-d602ff59187a"
      }
    ]
  }
}

Каждый файл идентифицируется постоянным UUID (file_id) и содержит относительный URL для загрузки.

2. Способы аутентификации

Эндпоинт /api/files/download/{file_id} защищен и поддерживает следующие способы авторизации:

  • M2M API-ключ (рекомендуется для скриптов/ETL): передается в заголовке X-API-Key: whsec_... или параметром URL ?api_key=whsec_.... Ключи создаются во вкладке «Интеграции» менеджера.
  • Keycloak Bearer JWT (для внутренних сервисов): передается в заголовке Authorization: Bearer <token> или параметром ?token=<token>.

3. Режимы скачивания

А. Прямой редирект (HTTP 302) — для браузеров и утилит

При передаче параметра ?redirect=true (или переходе из браузера) шлюз FileConductor мгновенно перенаправляет клиента (HTTP 302) на S3 Presigned URL с корректным заголовком Content-Disposition. Файл сразу скачивается с его оригинальным именем (например, photo_bridge.jpg):

# Пример скачивания через cURL с сохранением оригинального имени:
curl -L -O -J -H "X-API-Key: whsec_your_key_here" \
  "https://suraqhub.dostyq.app/api/files/download/c0378e90-f99a-4c28-bb8d-d602ff59187a?redirect=true"

Б. JSON-режим (по умолчанию) — для двухэтапной интеграции

Без флага redirect=true эндпоинт возвращает JSON с метаданными и временной подписанной ссылкой на S3 (срок жизни 15 минут):

GET /api/files/download/c0378e90-f99a-4c28-bb8d-d602ff59187a
X-API-Key: whsec_your_key_here

Ответ:
{
  "status": "success",
  "file_id": "c0378e90-f99a-4c28-bb8d-d602ff59187a",
  "filename": "photo_bridge.jpg",
  "download_url": "https://s3.ktz.dostyq.app/suraqhub-clean/c0378e90-...?X-Amz-Signature=..."
}

4. Примеры интеграции

Python (requests / httpx)

import requests

FILE_URL = "https://suraqhub.dostyq.app/api/files/download/c0378e90-f99a-4c28-bb8d-d602ff59187a"
HEADERS = {"X-API-Key": "whsec_your_secret_api_key"}

# Вариант 1: Прямое скачивание с редиректом
response = requests.get(f"{FILE_URL}?redirect=true", headers=HEADERS, stream=True)
if response.status_code == 200:
    with open("downloaded_media.jpg", "wb") as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
    print("✅ Файл успешно сохранен!")

# Вариант 2: Получение метаданных и presigned URL
meta_resp = requests.get(FILE_URL, headers=HEADERS).json()
print("Имя файла:", meta_resp["filename"])
print("S3 ссылка:", meta_resp["download_url"])

Node.js / JavaScript (fetch)

const fs = require('fs');
const { pipeline } = require('stream/promises');

async function downloadFile(fileId, apiKey) {
  const url = `https://suraqhub.dostyq.app/api/files/download/${fileId}?redirect=true`;
  const res = await fetch(url, {
    headers: { 'X-API-Key': apiKey }
  });

  if (!res.ok) throw new Error(`Download failed: ${res.statusText}`);
  await pipeline(res.body, fs.createWriteStream('downloaded_media.jpg'));
  console.log('✅ Файл успешно загружен');
}
🛡️ Политики безопасности и роль suraqhub_downloader
  • Роль Keycloak: в системе заведена специализированная роль suraqhub_downloader (Право на скачивание файлов и медиавложений). Вы можете назначить её доверенным сотрудникам или сервисному аккаунту внешней системы и выбрать в «Настройках системы». Администраторы (suraqhub_admin) имеют доступ к скачиванию всегда.
  • Антивирусный карантин: файл невозможно скачать до тех пор, пока служба ClamAV не присвоит ему статус CLEAN. При попытке скачать зараженный файл возвращается ошибка 403 Forbidden.
  • Управление доступом: в «Настройках системы» администратор может включить/отключить скачивание по M2M API-ключам и выбрать требуемую роль.

Выгрузка отчетов в собственное S3-хранилище Storage

Возможность сохранять сгенерированные отчеты напрямую в корпоративный бакет AWS S3, MinIO или Ceph.

🔒 Сквозное шифрование Fernet

Реквизиты доступа к вашему S3 (Access Key, Secret Key) на лету шифруются алгоритмом Fernet при вызове GraphQL-мутации createReport и расшифровываются исключительно внутри изолированного фонового воркера.

Безопасность, ClamAV и Доступность Security

🛡 Антивирусный барьер ClamAV

Файлы пользователей не сохраняются напрямую в общую файловую систему. Они проходят автоматическую проверку через изолированный демон ClamAV. Зараженные файлы блокируются до того, как они смогут попасть к операторам или в выгрузки.

🔐 Content Security Policy (CSP)

Интерфейс SuraqHub спроектирован по стандарту строгого CSP: исключены unsafe-inline скрипты, все события вынесены в модульные слушатели, что предотвращает XSS-атаки и кражу сессий.

♿ Доступность интерфейса (WCAG 2.2 AA)

Формы полностью доступны для людей с ограниченными возможностями: поддерживается навигация с клавиатуры, семантические WAI-ARIA атрибуты (aria-live, role="region"), экранные дикторы (Screen Readers) и контрастные цветовые палитры.

👥 Роли и права доступа

Роли назначаются администратором в Keycloak Admin Console; внутри платформы ведется read-only обзор пользователей:

  • admin — полный доступ: настройки системы, интеграции, справочники, все публикации и отчёты;
  • creator — создание форм и работа только со своими публикациями и отчётностью по ним;
  • dictionary_manager — ведение справочников (НСИ) без прав создателя;
  • suraqhub_downloader — право скачивать файлы и медиавложения через File API (см. Скачивание файлов).

Каждая публикация, кроме того, имеет уровень доступа: Public (доступна всем по ссылке) или Role Based (доступ только пользователям с выбранными ролями Keycloak). Периодическая отчётность доступна только в режиме Role Based.

Часто задаваемые вопросы (FAQ) База знаний

Как изменить адрес формы после ее публикации?
Вы можете изменить значение в поле «URL (Адрес)» на экране «Основное» и нажать кнопку «Сохранить публикацию». Обратите внимание: старая ссылка перестанет работать, поэтому разошлите респондентам обновленный адрес.
Что произойдет со старыми ответами при редактировании вопросов формы?
Все ранее собранные ответы сохраняются в базе данных в исходном виде. При редактировании создается новая ревизия формы. В выгрузке Excel будут присутствовать ответы со всеми полями, существовавшими на момент их отправки.
Почему выгрузка Excel формируется асинхронно?
Асинхронная обработка через RabbitMQ гарантирует, что даже при выгрузке 500 000+ ответов с файлами веб-сервер не зависнет по таймауту, а пользовательский интерфейс останется быстрым и отзывчивым.
Как вернуть предыдущую версию формы, если новая была сохранена с ошибкой?
На экране «Основное» в блоке «История ревизий» найдите нужную дату/время и кликните по записи. Система восстановит схему этой ревизии в конструкторе. После проверки нажмите «Сохранить форму».
Что делать, если вебхук не доходит до сторонней системы?
1. Проверьте доступность вашего URL-эндпоинта из сети кластера.
2. Убедитесь, что ваш сервер возвращает HTTP-код 200 OK.
3. Проверьте правильность валидации подписи X-SuraqHub-Signature с вашим секретом подписки.
4. Скачайте пакет диагностики на экране «Настройки» для анализа очереди suraqhub.webhooks.failed.

Периодическая отчётность

В «Настройках заполнения» можно включить ежедневную, еженедельную или ежемесячную отчётность. Эта настройка доступна только для публикаций с доступом Role Based.

  • Продолжать текущий отчёт: при повторном открытии в том же периоде показывается последняя отправка; новая отправка сохраняется отдельной версией.
  • Один раз: в периоде допускается одна отправка.
  • Повторное заполнение: каждая отправка самостоятельна.

Начальные данные нового периода можно оставить пустыми или скопировать из прошлого периода. Эта настройка применяется только при первом открытии формы в новом периоде. Подробности для разработчиков: docs/periodic-reporting.md.