# Перенести статью со своего сайта в Аишку

Готовый рецепт Node.js 22+: читает одну вашу публичную HTML-статью или её локальный экспорт, преобразует содержимое в блоки редактора, скачивает иллюстрации и сохраняет **приватный черновик**. Публикация и проверка модератором остаются отдельными действиями. Для нескольких статей повторите рецепт с отдельной папкой на каждый исходный адрес.

## 1. Установите пример

Скачайте [готовый ZIP](https://eval.su/docs/examples/site-import.zip) и распакуйте его. Откройте корень распакованного комплекта — папку, в которой находится `examples`. Доступ к GitHub не нужен. Все команды ниже запускаются из этой папки:

```sh
npm ci --prefix examples/integrations/site-import
```

Используется HTML-парсер `parse5`, а загрузку файлов, подтверждение готовности, создание черновика и повтор после сбоя выполняет общий [пример публикаций](https://eval.su/docs/examples/media.md). Оба соседних каталога `site-import` и `media`, зависимости и тесты уже включены в ZIP. Ключ API на этом шаге не нужен.

## 2. Подготовьте материал локально

Замените адрес на статью своего сайта и выберите новую папку:

```sh
node examples/integrations/site-import/import-article.mjs prepare \
  https://your-site.example/blog/article ./article-review
```

По умолчанию выбирается единственный `article`. `main` не выбирается автоматически: он часто содержит фотографии сотрудников и рекомендации рядом со статьёй. Если на сайте несколько контейнеров либо их нет, укажите **именно тело статьи** — один простой селектор `#id`, `.class` или имя тега. Если картинки расположены на вашем CDN, разрешите его origin явно:

```sh
node examples/integrations/site-import/import-article.mjs prepare \
  https://your-site.example/blog/article ./article-review \
  --selector .article-body \
  --allow-origin https://images.your-site.example
```

Если выбранный контейнер содержит авторскую карточку с портретом, рекомендации или другие соседние блоки, исключите их явно, например `--exclude .author-card --exclude .related-articles`. Можно передать несколько `--exclude`; каждый принимает один простой селектор. Выбранные исключения сохраняются в локальных метаданных для проверки.

Адреса `your-site.example` и `images.your-site.example` — места для вашего домена, не действующие источники. Если `www` перенаправляет на другой origin, добавьте его явно либо используйте конечный адрес. Импорт не выполняет JavaScript; для клиентского сайта экспортируйте HTML из CMS, а изображения положите рядом с относительными путями:

```sh
node examples/integrations/site-import/import-article.mjs prepare \
  https://your-site.example/blog/article ./article-review \
  --html ./export/article.html --selector .article-body
```

Локальные файлы ограничены папкой HTML-экспорта; выход через `..` и символические ссылки отклоняется. Абсолютные пути к локальным файлам не импортируются. Отсутствующий локальный относительный файл пробуется по соответствующему публичному адресу сайта. Оставьте `sourceUrl` адресом настоящей исходной статьи даже при локальном экспорте.

Команда выведет путь к `article-review/article.json`, количество блоков и изображений, предупреждения. Рядом появится папка `media/`; внутренний `.gitignore` защищает материалы и журнал от обычного случайного `git add`. В папку существующего импорта повторная подготовка **не записывает ничего** — это защищает ручные исправления и журнал повторов. Прерванная подготовка оставляет отдельную `.preparing-*` папку; после проверки её можно удалить и снова выполнить `prepare`.

## 3. Проверьте текст, подписи и иллюстрации

Откройте `article.json`. Сохраняются:

- Заголовок, исходный анонс, жирный и курсивный текст, ссылки, списки, таблицы, цитаты, блоки кода с языком.
- Только иллюстрации внутри выбранного тела статьи. Меню, реклама в `aside`, подвал, формы, исполняемые скрипты и элементы с `hidden`, `aria-hidden`, явными inline-стилями `display:none` или `visibility:hidden` исключаются. Внешние стили не вычисляются: скрываемые ими соседние блоки исключите через `--exclude`.
- Подпись из `figcaption` и `alt` исходной картинки. Отсутствующая подпись остаётся пустой и не блокирует приватный черновик; добавьте её в редакторе, только если есть фактическое описание. Если отсутствует alt, заполните его перед сохранением для доступности изображения. Названия файлов, автора и даты пример не выдумывает.
- Имя автора из `meta name="author"` или `rel="author"` сохраняется явной строкой в тексте. Владелец черновика — ваш выбранный профиль; импорт не выдаёт исходного автора за владельца аккаунта.
- Ссылка на исходную статью в `content.article.sources`. В локальном `source` остаются SHA-256 исходного HTML, найденное авторство и исходная дата; дата нового черновика не подменяется исторической датой.

Код переносится как текст без исполнения, включая буквальные знаки `<` и `>` в технических примерах. Иллюстрации внутри списков сохраняются вместе с исходными подписями. Сложные списки раскладываются на последовательность текста и вложенных блоков, сохраняя порядок и явную нумерацию; предупреждение напоминает проверить оформление. Встроенные видео и аудио становятся ссылками с предупреждением: загрузите собственные исходники через редактор, если нужно встроенное воспроизведение. Нестандартные блоки CMS, `srcset`, JavaScript-галереи и особая верстка требуют проверки в редакторе. Не задавайте `body` как контейнер, если там есть навигация, похожие статьи или фотографии команды.

Укажите подходящую категорию в `data.category` (или передайте `--category 'Аналитика'` при подготовке). Анонс `data.description` и `content.article.lead` можно уточнить по фактам материала. Не добавляйте неподтверждённые достижения, даты или связь с ИИ. Статья без фотографий тоже поддерживается.

Проверьте итоговый запрос без изменения сайта:

```sh
node examples/integrations/site-import/import-article.mjs preview ./article-review/article.json
```

`READY_FILE_UUID_*` в этом выводе — обозначения будущих готовых файлов, не значения для ручной отправки API.

## 4. Сохраните приватный черновик

В [настройках интеграций](https://eval.su/settings/integrations/) выберите свой профиль и ключ HTTP API с разрешениями:

`profiles:read`, `files:read`, `files:write`, `articles:read`, `articles:write`.

Передайте ключ через защищённое окружение `AISHKA_ACCESS_TOKEN`. Не вставляйте его в статью, промпт, аргументы команд или репозиторий. По умолчанию используется `https://eval.su`; другой доверенный адрес Аишки задаётся `AISHKA_ORIGIN`.

```sh
node examples/integrations/site-import/import-article.mjs save ./article-review/article.json
```

Пример загружает изображения с SHA-256, завершает загрузку, проверяет `ready`, сохраняет черновик и читает результат обратно. Ответ содержит UUID, статус, версию и `webUrl`. Откройте эту ссылку и проверьте отображение, подписи и обложку. Текущая проверка готовности к модерации может вернуть «Добавьте подписи к изображениям.»: приватный черновик уже сохранён; заполните фактические подписи в редакторе перед отправкой на проверку. Картинки относятся к выбранному профилю. Удалённая HTML-страница не получает ваш токен или cookies: ключ передаётся только HTTP API Аишки.

**Повтор:** запустите ту же команду `save` с тем же `article.json`, медиа и `article.state.json`. При потере ответа используются те же UUID операций. После успешного создания повтор только читает актуальный черновик и сохраняет ваши ручные правки. Не удаляйте журнал и не создавайте новую папку для повторной отправки того же материала. После успешного импорта правьте статью в редакторе; изменённый манифест с прежним журналом будет отклонён вместо молчаливой перезаписи. Журнал имеет права `0600` и не содержит API-ключ.

## Ограничения скачивания

Публичный HTTPS, порты по умолчанию, до четырёх перенаправлений, только разрешённые origin. Частные, loopback и служебные IP отклоняются; проверенный DNS-адрес фиксируется для соединения, каждый redirect проверяется заново. Картинки: JPEG, PNG, WebP, GIF, AVIF, SVG; до 25 МиБ каждая, максимум 40 и 300 МиБ в сумме. HTML — UTF-8, до 2 МиБ; сервер проверяет сигнатуры файлов и безопасность SVG перед привязкой. Пример не открывает скачанные файлы и не исполняет HTML/SVG. Исходные материалы могут быть защищены авторским правом — используйте свой контент или контент, который разрешено переносить.

Скачивание не является резервной копией сайта или универсальным краулером. Доступ к закрытым CMS, логины чужих сайтов, RSS-обход и автоматическая публикация здесь не включены. Для своих экспортов и расписания используйте одну постоянную папку/журнал на статью и проверку подготовленного содержимого.

## Проверки

```sh
npm test --prefix examples/integrations/site-import
```

Проверяются форматирование и подписи, исключение соседних элементов, пустые метаданные, типы/размеры, SSRF и DNS-подмена, перенаправления, локальные пути и symlink, повторяемость импорта, неизвестный исход сохранения и сохранность ручных правок. Тесты используют изолированный транспорт и временные файлы; они не создают публикации на eval.su.

`TestSiteImportCookbookHTTPUploadDraftAndRepeat` в `apps/api/internal/publications/site_import_cookbook_integration_test.go` выполняет настоящий CLI против HTTP-обработчиков, PostgreSQL 18 и временного хранилища. Он проверяет локальный HTML → preview без записей → байтовые загрузки → приватный черновик → повтор → ручную правку → повтор, сохранность форматирования, источника, alt, существующей и отсутствующей подписи, запрет доступа к черновым файлам без авторизации. Запуск из `apps/api`: `go test -tags=integration ./internal/publications -run TestSiteImportCookbookHTTPUploadDraftAndRepeat` с `TEST_DATABASE_URL` отдельной базы `aishka_test*`. Данные и доступы настоящих пользователей для этих тестов не используются.
