Сайты и настройки
Создание сайта сети клоном донора, карточка сайта и настройки уровня сайта, для которых раньше нужна была админка.
Эти ручки создают сайт сети клоном донора, при желании очищенным до дизайна, и управляют тем, для чего раньше нужна была админка WordPress: названием и подзаголовком, видимостью для поисковиков, иконкой сайта, счётчиками аналитики, статической главной и кэшами.
Контракт живёт в Node-сервисе, работу делает PHP-исполнитель. Создание сайта — операция сети, она выполняется на главном сайте. Всё под /site/* выполняется на собственном домене целевого сайта: запись опций запускает хуки WordPress — update_blog_public, Polylang, кэши, — которые под switch_to_blog() не срабатывают.
| Ручка | Кому можно |
|---|---|
POST /sites | только суперадмину сети |
GET /sites/{id} | любому, у кого есть доступ к этому сайту |
/site/* | администратору сайта или суперадмину; редактор и автор получают 403 forbidden |
POST /sites
| Поле | Тип | Описание |
|---|---|---|
name | string | поддомен: строчная латиница, цифры и дефисы, до 50 символов. www, api, admin, mail, ftp, cdn, static, app, apps, stage, test зарезервированы. Сайт получает <name> под доменом сети |
title | string | название сайта; по умолчанию name |
donor | integer | blog_id сайта, который клонируется |
empty | boolean | после клона вычистить содержимое нового сайта; по умолчанию false |
keep_frames | array | id или слаги фреймов, которые empty сохраняет; только вместе с empty: true |
copy_files | boolean | копировать загрузки донора; по умолчанию true. С empty: true на эти файлы ничто не ссылается, передавайте false |
public | boolean | false сразу ставит blog_public и blogs.public в 0 — noindex с первого запроса. Не передан — как у донора, и в ответе public: null |
Что удаляет empty — только в таблицах нового сайта, каждый запрос проверяет, что у таблицы префикс нового сайта:
- все записи всех типов — страницы, посты, вложения, пункты меню, ревизии, служебные типы — с метой, связями с термами и комментариями;
- группы переводов Polylang и меню навигации вместе с назначениями областей в теме и в Polylang;
- аппы и наборы данных;
- фреймы, которых нет в
keep_frames: записи реестра, исходники и испечённые копии; очередь печати очищается; - транзиенты списка языков и переведённых слагов Polylang и транзиенты критического CSS;
- индексы Yoast, все таблицы
yoast_*, кроме миграций; - аналитику донора: счётчики обнуляются, данные индексации и просмотры удаляются, состояние мастера домена и прокси приложения сбрасываются;
- опции, указывавшие на удалённые записи: главная возвращается к записям,
page_on_front,page_for_posts,site_iconиsite_logoстановятся 0.
Что остаётся: тема, языки и настройки Polylang, произвольный CSS и токены дизайна, пользователи и роли, сохранённые фреймы.
curl -s -X POST "$BASE/sites" \
-H "Authorization: Bearer $OP_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"name": "example",
"title": "Example",
"donor": 233,
"empty": true,
"keep_frames": ["site__header", "site__footer", "article"],
"copy_files": false,
"public": false
}'
Ответ — 201:
{
"success": true,
"blog_id": 241,
"domain": "example.onpress.pro",
"donor": 233,
"public": false,
"files": "files not copied (copy_files=false)",
"purged": {
"pages": 21,
"menus": 6,
"datasets": 4,
"frames": 20,
"apps": 2,
"transients": 2,
"posts_total": 58,
"post_types": { "page": 21, "nav_menu_item": 30, "revision": 7 },
"translation_groups": 5,
"frames_kept": { "cb7ccee58078d1fb": "site__header", "1dc0afb7671182c6": "site__footer", "91399e3637b7aa2b": "article" },
"frames_missing": [],
"yoast_tables": ["yoast_indexable", "yoast_indexable_hierarchy", "yoast_primary_term", "yoast_seo_links"]
}
}
Без empty поле purged равно null. frames_missing перечисляет элементы keep_frames, которых у донора нет. Сохранённые фреймы по-прежнему держат копии, испечённые на доноре, с его меню и текстами: перепеките их, когда у нового сайта появятся свои меню и данные, см. Как это устроено.
Клон выполняется синхронно и занимает десятки секунд; сервис ждёт до пяти минут (SITE_CREATE_TIMEOUT_MS). Повторный вызов с тем же name безопасен: он отвечает 409 site_exists с blog_id существующего сайта.
| Ошибка | HTTP | Когда |
|---|---|---|
forbidden | 403 | владелец токена не суперадмин сети |
invalid_name | 400 | имя нарушает правила выше или зарезервировано |
invalid_donor | 400 | donor не положительное целое |
invalid_keep_frames | 400 | не массив строк или передан без empty: true |
donor_not_found | 404 | такого сайта в сети нет |
site_exists | 409 | домен занят; в ответе blog_id и domain |
purge_failed | 500 | клон создан (blog_id в ответе), но чистка остановилась; за пределами нового сайта ничего не тронуто |
clone_failed | 500 | клонировщик упал по другой причине |
internal_unavailable | 502 | PHP-исполнитель не ответил вовремя или не выложен |
GET /sites/{id}
Карточка сайта: домен, видимость, тема, главная, языки и сколько в нём всего. Доступ проверяется дважды — к сайту, выбранному для запроса, и к {id}, — поэтому, если вы не суперадмин, передавайте X-OnPress-Blog-Id с тем же id.
curl -s "$BASE/sites/235" \
-H "Authorization: Bearer $OP_TOKEN" \
-H "X-OnPress-Blog-Id: 235"
{
"success": true,
"blog_id": 235,
"domain": "example.onpress.pro",
"path": "/",
"public": 0,
"blog_public": 0,
"archived": 0,
"deleted": 0,
"registered": "2026-09-21T19:26:03.000Z",
"last_updated": "2026-09-21T20:38:14.000Z",
"blogname": "Pomelli",
"blogdescription": "Free AI Marketing Tool by Google",
"home": "https://example.onpress.pro",
"theme": { "template": "onpress-v2", "stylesheet": "onpress-v2", "current_theme": "onpress-v2" },
"show_on_front": "page",
"page_on_front": 6409,
"site_icon": 6425,
"languages": { "default": "en", "list": ["en", "ru"] },
"counts": {
"post_types": { "attachment": 73, "nav_menu_item": 23, "page": 24 },
"pages": 24,
"menus": 6,
"frames": 6,
"datasets": 6
}
}
public — флаг сети в таблице blogs, blog_public — опция сайта; оба 0 значат, что сайт просит его не индексировать. languages равно null на сайте без Polylang. counts.post_types не считает авточерновики и корзину. Поля frame_defaults нет: у фреймов больше нет значений по умолчанию.
Ошибки: 400 invalid_blog_id, 404 site_not_found, 403 forbidden_site.
GET /site/settings, PATCH /site/settings
Сайт выбирается как везде: blog_id в теле или строке запроса, X-OnPress-Blog-Id или domain.
| Поле | Тип | Описание |
|---|---|---|
blogname | string | название сайта, не пустое |
blogdescription | string | подзаголовок |
blog_public | 0, 1 или boolean | видимость для поисковиков; пишется в опцию blog_public и в blogs.public |
site_icon | integer | id вложения-картинки этого сайта; 0 снимает иконку |
analytics | object | ключи onpress_analytics, которые нужно задать; непереданные остаются, неизвестные отклоняются |
Неизвестные поля верхнего уровня отклоняются с unknown_field. Запись, которая что-то поменяла, сбрасывает страничный и объектный кэш сайта; что именно сброшено — в поле cache ответа.
curl -s "$BASE/site/settings?blog_id=235" -H "Authorization: Bearer $OP_TOKEN"
{
"success": true,
"blog_id": 235,
"settings": {
"blogname": "Pomelli",
"blogdescription": "Free AI Marketing Tool by Google",
"blog_public": 0,
"blogs_public": 0,
"site_icon": 6425,
"site_icon_url": "https://example.onpress.pro/wp-content/uploads/sites/235/2026/09/cropped-993838e9_1771708768.png",
"analytics": {
"ym_counter_id": "",
"ym_enabled": 0,
"ym_counter_name": "",
"ywm_client_token": "",
"ywm_client_verified": 0,
"gsc_verification_token": "",
"gsc_site_url": "",
"gsc_verified": 0,
"ga_property_id": "",
"ga_measurement_id": "",
"ga_property_name": "",
"ga_enabled": 0,
"…": "…"
},
"domain": "example.onpress.pro",
"template": "onpress-v2",
"stylesheet": "onpress-v2",
"show_on_front": "page",
"page_on_front": 6409
}
}
PATCH принимает те же поля и отвечает changed — именами полей, которые действительно изменились, — новыми settings и cache:
curl -s -X PATCH "$BASE/site/settings" \
-H "Authorization: Bearer $OP_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"blog_id": 241, "blogname": "Example", "blogdescription": "Guide to Example", "blog_public": 0}'
{
"success": true,
"blog_id": 241,
"changed": ["blogdescription"],
"settings": { "blogname": "Example", "blogdescription": "Guide to Example", "blog_public": 0, "…": "…" },
"cache": { "object": true, "supercache": { "status": "flushed", "dir": "…/cache/supercache/example.onpress.pro" } }
}
Ошибки, все 400: unknown_field, nothing_to_change (не передано ни одного известного поля), invalid_blogname, invalid_blogdescription, invalid_site_icon, invalid_analytics. Не администратор сайта — 403 forbidden.
PUT /site/front-page
| Поле | Тип | Описание |
|---|---|---|
page | integer или string | id страницы или её путь |
lang | string | если путь общий для нескольких языков — взять страницу этого языка |
Ставит show_on_front в page и задаёт page_on_front. С Polylang опция всегда хранит страницу языка по умолчанию: передайте любую страницу группы переводов — запишется её участник на языке по умолчанию, а остальных участников Polylang отдаёт главными их языков. Страница должна быть опубликована. Кэш языков Polylang, страничный и объектный кэш сбрасываются.
{
"success": true,
"blog_id": 241,
"changed": true,
"show_on_front": "page",
"page_on_front": 12,
"front_pages": {
"en": { "id": 12, "url": "https://example.onpress.pro/" },
"ru": { "id": 14, "url": "https://example.onpress.pro/ru/" }
},
"before": { "show_on_front": "posts", "page_on_front": 0 }
}
| Ошибка | HTTP | Когда |
|---|---|---|
invalid_page | 400 | page — не положительный id и не непустой путь |
invalid_lang | 400 | lang — не слаг языка |
page_not_found | 404 | такой страницы нет |
page_not_published | 409 | страница — черновик или на модерации |
no_default_translation | 409 | страница не на языке по умолчанию и перевода на него у неё нет; сначала свяжите переводы |
Главная переходит между группами переводов только через эту ручку: публикация с translation_of отклоняется с front_page_group_conflict, см. Страницы и markdown.
POST /site/cache/flush
| Поле | Тип | Описание |
|---|---|---|
transients | array | имена транзиентов для удаления; * в конце удаляет все транзиенты с таким префиксом |
supercache | boolean | удалить каталог WP Super Cache сайта |
object | boolean | сбросить объектный кэш |
polylang | boolean | сбросить кэши списка языков и переведённых слагов Polylang |
Нужно хотя бы одно, иначе 400 nothing_to_flush; кривое имя — 400 invalid_transients.
curl -s -X POST "$BASE/site/cache/flush" \
-H "Authorization: Bearer $OP_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"blog_id": 241, "transients": ["pll_languages_list", "onpress_critical_*"], "object": true, "supercache": true}'
{
"success": true,
"blog_id": 241,
"flushed": {
"transients": { "deleted": ["pll_languages_list", "onpress_critical_singular"], "missing": [] },
"object": true,
"supercache": { "status": "empty", "dir": "…/cache/supercache/example.onpress.pro" }
}
}