# XML-интеграция на Linux/VPS У мастерской два разных способа применения. Готовый public — статические файлы сайта: браузер выполняет библиотеку сам. Node CLI — обработка файла на сервере, которую можно включить в импорт по расписанию. Ни один вариант не требует публичного сервера с произвольным запуском кода пользователя. ## Статическая мастерская Соберите public в CI или локально проверенной версией Node, затем перенесите только его содержимое в каталог сайта. Например, в /var/www/xml-workshop. Пользователь Nginx должен иметь право чтения; запись ему не нужна. Node, исходники, node_modules, файлы поставщиков и ключи в document root не размещайте. Если используете Nginx, отдельный location может выглядеть так. Это фрагмент существующего server-блока, а не команда замены всего системного конфига. ```nginx location /xml-workshop/ { alias /var/www/xml-workshop/; index index.html; add_header X-Content-Type-Options nosniff always; } ``` Включённый стандартный mime.types должен отдавать .js как JavaScript, .css как CSS, .json как JSON. Перед применением проверьте nginx -t и только затем выполните reload. HTTPS настраивается на существующем виртуальном хосте. Если сайт использует CSP, для мастерской нужны script-src 'self' и worker-src 'self', а также чтение локального sources.json. В iframe учитывайте CSP родительской страницы и разрешение скриптов. Не отключайте политику целиком ради одного Worker. ## CLI под отдельным пользователем Пример рассчитан на systemd и Node 24, установленный в /usr/bin/node. На конкретном VPS сначала выполните command -v node и укажите фактический абсолютный путь. Способ установки Node выбирайте по дистрибутиву и официальным инструкциям, не копируйте случайную команду curl|sh. ```bash sudo useradd --system --home /opt/xml-import --shell /usr/sbin/nologin xml-import sudo install -d -o root -g xml-import -m 0750 /opt/xml-import sudo install -d -o xml-import -g xml-import -m 0750 /var/lib/xml-import/incoming ``` Скопируйте package.json, package-lock.json, src и tools/cli.mjs в /opt/xml-import. Установите production-зависимости при развёртывании: npm ci --ignore-scripts --omit=dev --no-audit --no-fund. Дайте сервису чтение проекта, без права изменения исходников. Файл поставщика разместите как /var/lib/xml-import/incoming/catalog.xml с владельцем/группой, доступными сервису. Права каталога и файла — отдельные условия: одного chmod файла недостаточно, если родительский каталог закрыт. ```ini # /etc/systemd/system/xml-import.service [Unit] Description=Validate supplier XML into a normalized result After=local-fs.target [Service] Type=oneshot User=xml-import Group=xml-import WorkingDirectory=/opt/xml-import ExecStart=/usr/bin/node /opt/xml-import/tools/cli.mjs contract /var/lib/xml-import/incoming/catalog.xml TimeoutStartSec=15 NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true UMask=0077 ``` ```bash sudo systemctl daemon-reload sudo systemctl start xml-import.service sudo systemctl status xml-import.service sudo journalctl -u xml-import.service -n 50 --no-pager ``` Этот unit **проверяет и печатает результат**, не выполняет запись в CRM. Код 2 от CLI означает неуспешное завершение unit. Успех нельзя подменить проглатыванием исключения. Для реальной интеграции добавьте отдельный этап применения проверенных строк: транзакцию, уникальный внешний ID, журнал запуска и повторяемость импорта. Не отправляйте частично проверенные rows в базу: наш контракт при любой ошибке возвращает пустой массив rows вместе со списком проблем. ## Расписание и передача файла Поставщик или отдельный загрузчик должен сначала записать новый файл во временное имя, завершить запись и атомарно переименовать его в catalog.xml в пределах одной файловой системы. Так обработчик не увидит недописанный документ. Пример timer не загружает файл из сети; загрузка, аутентификация поставщика и повтор доставки остаются отдельным процессом. ```ini # /etc/systemd/system/xml-import.timer [Unit] Description=Check supplier XML every hour [Timer] OnCalendar=hourly Persistent=true [Install] WantedBy=timers.target ``` ```bash sudo systemctl enable --now xml-import.timer systemctl list-timers xml-import.timer ``` Идемпотентность требуется даже при timer: один и тот же файл может быть проверен повторно. Запоминайте контрольную сумму файла/идентификатор партии и результат применения в базе. Загрузка обновлённого каталога, его проверка и применение — три разных события, каждое должно иметь наблюдаемый статус. ## Ограничения и диагностика Учебный CLI принимает только локальные UTF-8 файлы до 32 KiB, читает размер до readFile, затем ещё раз проверяет фактические байты в обработчике. Изменение файла между stat и readFile может изменить прочитанный размер: потому повторная проверка обязательна. Такой путь не подходит для многогигабайтного каталога. Для него нужен потоковый parser и пакетная транзакционная обработка с измерением памяти. Не журналируйте полный коммерческий каталог или персональные данные без необходимости. В демонстрации stdout содержит исходные поля для обучения; в production выводите ID партии, количество записей и пути ошибок, а полные документы храните отдельно с ограниченным доступом и сроком хранения. Наличие корректного XML не подтверждает полномочия отправителя, допустимость цены или принадлежность клиента. Режим процесса Worker ограничивает ожидание и сохраняет основной поток отзывчивым, но не заменяет лимиты ресурсов ОС, размер HTTP body и квоты по пользователю. Если переносите адаптер в API, ограничьте вход **до** чтения всего body, добавьте авторизацию и rate limit. Нельзя предоставлять пользователю путь к серверному файлу напрямую. В этом архиве публичного HTTP endpoint импорта нет. Linux-проверки проекта выполняются локально в контейнере. Эти инструкции для VPS — воспроизводимый образец конфигурации, а не утверждение о развёртывании вашего сервера.