From b09bd6abf740c417285d3539e3f50b953e5d5c7f Mon Sep 17 00:00:00 2001 From: NikitaKalitov Date: Thu, 16 Jul 2026 16:23:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D0=BB=20=D0=BA=20=D0=B8=D0=BD=D1=82=D0=B5=D0=B3?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=20=D0=B1=D0=B8=D1=82?= =?UTF-8?q?=D1=80=D0=B8=D0=BA=D1=81=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 5 + Dockerfile | 14 ++ docs/bitrix-integration.md | 261 +++++++++++++++++++++++++++++++++++++ src/App.tsx | 22 +++- src/host/HostAdapter.ts | 8 +- src/host/bitrixAdapter.ts | 56 ++++++++ src/host/devAdapter.ts | 11 +- src/vite-env.d.ts | 6 + vite.config.ts | 4 + 9 files changed, 374 insertions(+), 13 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docs/bitrix-integration.md create mode 100644 src/host/bitrixAdapter.ts create mode 100644 src/vite-env.d.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..46be5b5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +dist +dist-ssr +.git +docs diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7a74a91 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# Static build served by nginx. VITE_BITRIX_ORIGIN must be provided at build +# time (build arg) — it gets baked into the JS bundle, see docs/bitrix-integration.md §7. +FROM node:22-alpine AS build +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +ARG VITE_BITRIX_ORIGIN +ENV VITE_BITRIX_ORIGIN=$VITE_BITRIX_ORIGIN +RUN npm run build + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +EXPOSE 80 diff --git a/docs/bitrix-integration.md b/docs/bitrix-integration.md new file mode 100644 index 0000000..6f6ef70 --- /dev/null +++ b/docs/bitrix-integration.md @@ -0,0 +1,261 @@ +# Интеграция с Bitrix — спецификация для backend-разработчика + +Это документ для человека, который не участвовал в разработке самого React-приложения (`portal-chart-builder`) и не обязан знать TypeScript — здесь только то, что нужно на стороне PHP/Bitrix. + +## 1. Что это + +Конструктор диаграмм — отдельное React-приложение (не часть Bitrix24, продукт — «1С-Битрикс: Управление сайтом»). Встраивается как **пользовательский тип свойства инфоблока**: в админке, на странице редактирования элемента, вместо обычного поля отображается iframe с редактором диаграммы. Значение свойства — JSON-строка со схемой диаграммы, хранится как обычное строковое свойство (`PROPERTY_TYPE => 'S'`). + +Никакого REST-модуля, вебхуков или JS SDK Bitrix24 не требуется — это нативный механизм модуля `iblock`. + +## 2. Регистрация типа свойства + +В `local/php_interface/init.php`: + +```php +AddEventHandler("iblock", "OnIBlockPropertyBuildList", ["DiagramProperty", "GetUserTypeDescription"]); +``` + +После регистрации новый тип появляется в **Контент → Инфоблоки → [нужный] → Свойства → Тип свойства**. Свойство должно быть **одиночным** (`MULTIPLE = N`) — одна диаграмма на элемент. + +## 3. Скелет PHP-класса + +Ниже — иллюстративный каркас, не готовый к продакшену код. Реализация валидации, экранирования и т.д. — на усмотрение разработчика, но с обязательным соблюдением требований из раздела 5. + +```php +class DiagramProperty +{ + public static function GetUserTypeDescription(): array + { + return [ + 'PROPERTY_TYPE' => 'S', + 'USER_TYPE' => 'DiagramEditor', + 'DESCRIPTION' => 'Конструктор диаграмм процессов', + 'GetPropertyFieldHtml' => [__CLASS__, 'GetPropertyFieldHtml'], + 'ConvertToDB' => [__CLASS__, 'ConvertToDB'], + 'ConvertFromDB' => [__CLASS__, 'ConvertFromDB'], + ]; + } + + public static function GetPropertyFieldHtml($arProperty, $value, $strHTMLControlName): string + { + $inputName = htmlspecialcharsbx($strHTMLControlName['VALUE']); + $rawValue = $value['VALUE'] ?? ''; + // JSON_HEX_* флаги обязательны — см. раздел 5. + $jsValue = json_encode($rawValue, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT); + + // Вариант A — рекомендуемый, приложение лежит внутри дерева портала (см. раздел 7): + // appOrigin — это просто origin текущего запроса, appUrl — конкретный файл. + $appOrigin = (CMain::IsHTTPS() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST']; + $appUrl = $appOrigin . '/local/diagram-app/index.html'; + // Вариант B — если приложение развёрнуто отдельным контейнером (см. раздел 7), + // appUrl/appOrigin вместо этого указывают на его отдельный адрес, например: + // $appOrigin = 'http://localhost:5178'; + // $appUrl = $appOrigin; + + ob_start(); + ?> + + + + } +5. Пользователь редактирует → с debounce ~400мс: + Приложение → мост: { type: 'diagram:change', payload: '' } +6. Мост при каждом diagram:change пишет payload в value hidden input +7. Пользователь жмёт штатное «Сохранить» → браузер сабмитит форму → ConvertToDB → сохранение +``` + +Важно: `payload` в `diagram:change` — **уже сериализованная JSON-строка** (`JSON.stringify` со стороны приложения), не объект. Мост должен положить её в `input.value` как есть, без повторной обработки. + +`diagram:init` с `payload: null` (или пустой строкой) — валидный случай: новый элемент без диаграммы, приложение стартует с чистого холста. + +## 5. Требования безопасности (обязательны, не опциональны) + +- При встраивании `initialValue` в инлайн-скрипт — **обязательно** `JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT` (см. пример в разделе 3). Без этого — риск XSS через выход из `