REST API — Общая архитектура Wepps Platform

REST API Wepps Platform — это система предоставления данных через HTTP endpoints.

Состоит из версионированных API с разными подходами: M2M для машинных интеграций (1С, CRM), APP для мобильных приложений и фронтенда, wepps для админки, v0 для тестовых endpoints и cli для командной строки.

08.07.2026

REST API - Общая архитектура Wepps Platform

REST API Wepps Platform — это система предоставления данных через HTTP endpoints. Состоит из версионированных API с разными подходами: M2M для машинных интеграций (1С, CRM), APP для мобильных приложений и фронтенда, wepps для админки, v0 для тестовых endpoints и cli для командной строки.

Структура REST API

Все REST-запросы обрабатываются единым фронт-контроллером Rest::Request.php:

┌─────────────────────────────────────────────────────────┐
│         HTTP Request с Bearer Token                      │
│                                                         │
│  GET    /rest/v1/goods                                   │
│  POST   /rest/v1/cart                                    │
│  PUT    /rest/m2m/goods.stocks                           │
│  DELETE /rest/m2m/orders                                 │
└──────────────────┬────────────────────────────────────┘
                   │
                   ▼
┌──────────────────────────────────────────────────────────┐
│      Rest.php (фреймворк)                                │
│  parseRequest() → version, method, type                 │
│  routeRequest() → класс из RestConfig.php               │
│  executeHandler() → валидация + auth + вызов метода     │
│                                                          │
│  /v1/goods     → RestV1APP::getGoods()                   │
│  /m2m/users    → RestV1M2M::getUsers()                   │
│  /wepps/token  → RestAd::getToken()                      │
│  /cli/tasks    → RestCli::tasksProcess()                 │
└──────────────┬────────────────────────────────────────┘
               │
       ┌───────┴─────────┬──────────────┐
       ▼                 ▼              ▼
   RestV1APP        RestV1M2M       RestV1 / RestAd / RestCli
   (Бизнес v1)      (Generic m2m)   (Auth/Profile/Admin/CLI)

   Endpoint          Endpoint        Endpoint
   │                 │               │
   └─► getHome()     └─► fetch()     └─► getToken()
   └─► postCart()    └─► addBatch()  └─► getProfile()
   └─► placeOrder()  └─► setBatch()  └─► tasksProcess()
                     └─► remove()

Авторизация

Bearer Token

Все REST endpoints требуют авторизацию через Bearer Token в заголовке:

GET /rest/v1/cart HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Токен содержит (payload JWT):

  • typ — тип токена: auth (доступ), refresh (обновление), confirm (подтверждение)
  • id — ID пользователя
  • exp — срок истечения

Типы токенов:

Токен typ Срок жизни Назначение
access auth 1 час (3600 с) Аутентификация запросов
refresh refresh 30 дней (2592000 с) Получение новой пары токенов
confirm confirm 10 минут (600 с) Подтверждение входа кодом из письма

Получение токена (v1)

POST /rest/v1/auth.login HTTP/1.1
Content-Type: application/json

{
  "data": {
    "login": "user@example.com",
    "password": "password123"
  }
}

Response:
{
  "status": 200,
  "message": "Login successful",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "access_exp": 1774567890,
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_exp": 1777159890
  }
}

Обновление токена (v1):

POST /rest/v1/auth.refresh HTTP/1.1
Content-Type: application/json

{
  "data": {
    "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
  }
}

💡 Режим CONFIRM_AUTH: если на сервере включён CONFIRM_AUTH, auth.login возвращает confirm_token (тип confirm, 10 мин) и отправляет код на почту. Токены выдаются после POST /rest/v1/auth.confirm с телом {"data": {"token": "<confirm_token>", "code": 154566}} — поле code необязательно (для 2FA).

Токен для админки (wepps)

Админка получает токен через GET-параметры:

GET /rest/wepps/token?login=admin@example.com&password=secret HTTP/1.1

Response:
{
  "status": 200,
  "message": "Login successful",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "exp": 1774567890
  }
}

Токен для M2M (машинная интеграция)

M2M-эндпоинты требуют auth_required => true + role_required => [1], т.е. токен пользователя с правами UserPermissions = 1 (администратор M2M).

Авторизация общая с APP — токен получается через тот же POST /rest/v1/auth.login (в Bruno-коллекциях это auth/auth.login.bru, он сохраняет access_token/refresh_token в переменные окружения):

POST /rest/v1/auth.login HTTP/1.1
Content-Type: application/json

{
  "data": {
    "login": "m2m-admin@example.com",
    "password": "secret"
  }
}

Response:
{
  "status": 200,
  "message": "Login successful",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "access_exp": 1774567890,
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_exp": 1777159890
  }
}

Полученный access_token передаётся в заголовке во всех M2M-запросах:

GET /rest/m2m/users?page=1&limit=20 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Примечание: также подойдёт GET /rest/wepps/token (способ админки) — он возвращает такой же токен. Главное, чтобы у учётной записи была роль 1 (UserPermissions = 1), иначе M2M вернёт 403 Forbidden.

Как настраивается защита endpoints

В конфиге RestConfig.php для каждого endpoint можно указать:

Ключ Значение Поведение
auth_required true Запрос без токена → 401
auth_optional true Токен проверяется, но его отсутствие не блокирует запрос
role_required [1, 2] Допустимые UserPermissions; иначе → 403

Проверка выполняется в Rest::executeHandler():

// Проверка аутентификации, если требуется (или если заданы роли)
if (!empty($config['auth_required']) || isset($config['role_required'])) {
    $this->authenticateBearerToken();
} elseif (!empty($config['auth_optional'])) {
    try {
        $this->authenticateBearerToken();
    } catch (\Exception $e) {
        // токен опционален — игнорируем
    }
}

// Проверка прав доступа по UserPermissions
if (isset($config['role_required'])) {
    $userPermissions = (int) ($this->user['UserPermissions'] ?? 0);
    if (!in_array($userPermissions, $config['role_required'], true)) {
        throw new \Exception('Access denied: insufficient permissions', 403);
    }
}

Конфигурация Endpoints (RestConfig.php)

Все endpoints регистрируются в едином реестре WeppsExtensions\Addons\Rest\RestConfig.php:

'v1' => [
    'get' => [
        'goods' => [
            'class' => RestV1APP::class,
            'method' => 'getGoods',
            'note' => 'Список товаров с фильтрацией и пагинацией',
            'query_validation' => [
                'page' => ['type' => 'int2', 'required' => false],
                'limit' => ['type' => 'int2', 'required' => false],
                'sort' => ['type' => 'string', 'required' => false],
                'search' => ['type' => 'string', 'required' => false],
                'category' => ['type' => 'int2', 'required' => false],
            ],
        ],
        'cart.metrics' => [
            'class' => RestV1APP::class,
            'method' => 'getCartMetrics',
            'note' => 'Количество позиций корзины и их id',
            'auth_optional' => true,
        ],
    ],
    'post' => [
        'auth.login' => [
            'class' => RestV1::class,
            'method' => 'postAuthLogin',
            'note' => 'Аутентификация пользователя и выдача JWT-токенов',
            'log' => false,  // не логировать пароли
            'validation' => [
                'login' => ['type' => 'email', 'required' => true],
                'password' => ['type' => 'string', 'required' => true],
            ],
        ],
        'cart' => [
            'class' => RestV1APP::class,
            'method' => 'postCart',
            'note' => 'Добавление товара в корзину',
            'auth_required' => true,
            'validation' => [
                'id' => ['type' => 'string', 'required' => true],
                'quantity' => ['type' => 'int2', 'required' => false],
            ],
        ],
    ],
    // put / delete / cli — аналогично
],

Структура конфига:

[версия] => [
    [http-метод: get|post|put|delete|cli] => [
        [имя endpoint'а] => [
            'class'  => Класс-обработчик,
            'method' => Метод обработчика,
            'note'   => Описание для документации,
            // опционально:
            'auth_required'    => true,
            'auth_optional'    => true,
            'role_required'    => [1, 2],
            'validation'       => [...],  // правила тела JSON
            'query_validation' => [...],  // правила GET-параметров
            'custom_response'  => true,   // ответ без обёртки status/message/data
            'log'              => false,  // отключить логирование
            'async'            => true,   // поставить в очередь s_Tasks (202 Accepted)
        ],
    ],
],

Версионирование API

REST API Wepps поддерживает несколько версий одновременно:

v1 — Мобильное приложение и фронтенд

Endpoints:

  • /rest/v1/home — главный экран приложения
  • /rest/v1/goods — каталог товаров
  • /rest/v1/cart — операции с корзиной
  • /rest/v1/orders — заказы пользователя
  • /rest/v1/auth.login — аутентификация

Особенности:

  • ✅ Бизнес-ориентированные endpoints
  • ✅ camelCase ответы
  • ✅ Auth: access + refresh токены
  • ✅ Опциональная авторизация (auth_optional) для публичных данных

m2m — Generic CRUD

Endpoints (явные методы на каждую таблицу):

  • /rest/m2m/users — CRUD пользователей
  • /rest/m2m/goods — CRUD товаров (+ goods.navigator, goods.statuses, goods.attributes, goods.variations, goods.stocks)
  • /rest/m2m/orders — CRUD заказов
  • /rest/m2m/files — CRUD файлов (загрузка base64/url)
  • /rest/m2m/tasks.result — результат async-задачи

Особенности:

  • ✅ Универсальный CRUD через один RestV1M2M + RestV1M2MUtils
  • ✅ Batch-операции: одиночная запись → 201, массив (до 100) → 207 Multi-Status
  • ✅ Валидация из БД (s_ConfigFields), кэш в Memcached
  • ✅ Перед/после-колбэки (setBefore/setAfter) для бизнес-логики
  • async режим для тяжёлых операций (202 Accepted → s_Tasks)

wepps — Админка

Endpoints:

  • /rest/wepps/token — получение JWT-токена админки
  • /rest/wepps/list_items — поиск по спискам админки (custom_response)

Особенности:

  • ✅ Требует ShowAdmin = 1 у пользователя
  • ✅ Токен для внутренних AJAX-запросов админки

v0 — Тестовые endpoints

Endpoints:

  • /rest/v0/test — тестовая валидация всех типов (int, float, email, phone, guid, barcode)

cli — Командная строка

Endpoints:

  • php Request.php tasks.process — обработка async-задач из s_Tasks
  • php Request.php tasks.result — результат задачи
  • php Request.php removeLogLocal — очистка логов

Формат ответа

Все endpoints возвращают стандартный JSON envelope:

{
  "status": 200,
  "message": "OK",
  "data": { ... }
}

Дополнительные поля (например pagination) добавляются как соседние ключи:

{
  "status": 200,
  "message": "OK",
  "data": [ ... ],
  "pagination": {
    "count": 120,
    "limit": 20,
    "page": 1
  }
}

Статус коды

Код Значение Пример
200 OK Успешный запрос
201 Created Ресурс создан (одиночная запись)
202 Accepted Запрос поставлен в async-очередь (s_Tasks)
207 Multi-Status Batch-операция с per-item статусами
400 Bad Request Ошибка валидации
401 Unauthorized Требуется авторизация / неверный токен
403 Forbidden Недостаточно прав (role_required)
404 Not Found Ресурс не найден
409 Conflict Дубликат уникального ключа
429 Too Many Requests Превышен лимит запросов
500 Server Error Ошибка сервера

Особенности ответов

  • Для v1 (APP) ключи данных автоматически конвертируются из PascalCase БД в camelCase (OStatusstatus, OSumsum).
  • Для m2m данные приходят с camelCase маппингом из s_ConfigFields.ApiMapping.
  • Для custom_response endpoints ответ отправляется без обёртки status/message/data.

Примеры ответов

Успех (200):

{
  "status": 200,
  "message": "OK",
  "data": [
    { "id": 1, "name": "Product 1", "price": 100 },
    { "id": 2, "name": "Product 2", "price": 200 }
  ]
}

Ошибка валидации (400):

{
  "status": 400,
  "message": "Validation error",
  "data": {
    "id": "Field is required",
    "quantity": "Must be at least 1"
  }
}

Ошибка авторизации (401):

{
  "status": 401,
  "message": "Unauthorized",
  "data": null
}

Ошибка прав (403):

{
  "status": 403,
  "message": "Forbidden",
  "data": null
}

Ресурс не найден (404):

{
  "status": 404,
  "message": "Product not found",
  "data": null
}

Валидация запросов

Валидация происходит в двух местах:

1. Config-level валидация (RestConfig.php)

Выполняется в Rest::validateData() перед вызовом метода:

'validation' => [
    'id' => ['type' => 'string', 'required' => true],
    'quantity' => ['type' => 'int2', 'required' => false],
],

Поддерживаемые типы:

Тип Проверка Пример
int Целое число (strict) 42 (не "42")
int2 Целое число или строка с числом 42, "100"
float Число с плавающей точкой (strict) 3.14
float2 Число или строка с числом 3.14, "2.5"
string Любая строка "hello"
email Валидный email адрес "user@example.com"
date Валидная дата (strtotime) "2026-04-07"
phone Телефон (10 цифр) "9123456789"
guid GUID формат UUID "550e8400-e29b-41d4-a716-446655440000"
barcode Штрих-код EAN13 "1234567890128"
object Ассоциативный массив {"a": 1}
int[], string[] и т.д. Массив элементов указанного типа [1, 2, 3]

Вложенная валидация (dot-notation):

'validation' => [
    'data' => ['type' => 'object', 'required' => true],
    'data.items[]' => ['type' => 'object[]', 'required' => true],
    'data.items[].id' => ['type' => 'int', 'required' => true],
    'data.items[].name' => ['type' => 'string', 'required' => true],
    'data.items[].sum' => ['type' => 'float2', 'required' => true],
],

Валидация также проверяет лишние поля — любое поле, не описанное в правилах, вызывает ошибку Unexpected field '...'.

2. Business-logic валидация (в методе)

Сложная бизнес-логика, которую нельзя описать в конфиге:

public function postCart($data = null): array
{
    $id = trim($data['data']['id'] ?? '');

    // Config валидация уже прошла, данные безопасны
    // Теперь проверяем бизнес-логику

    $productsUtils = new ProductsUtils();
    $validation = $productsUtils->validateProductId($id);

    if (!$validation['exists']) {
        return ['status' => 404, 'message' => $validation['message'], 'data' => null];
    }

    // Товар существует, можно добавлять в корзину
    ...
}

Request Format

Все POST/PUT/DELETE запросы используют JSON со структурой:

{
  "data": {
    "id": "325",
    "quantity": 2,
    "active": 1
  }
}

Поле data — содержит данные запроса. Разворачивается в RestV1M2M::normalizeInput() для batch-операций:

{
  "data": [
    { "guid": "...", "name": "Товар 1", "price": "100.00" },
    { "guid": "...", "name": "Товар 2", "price": "200.00" }
  ]
}

Поле type — раньше использовалось для указания endpoint'а; сейчас не требуется.

Response Envelope

{
  "status": <HTTP Status Code>,
  "message": <Human-readable message>,
  "data": <Payload> | <Errors> | null
}

status — HTTP статус код (200, 400, 404 и т.д.)

message — человеческое сообщение ("OK", "Validation error", "Not found")

data — в зависимости от типа ответа:

  • Успех: массив данных или объект
  • Ошибка валидации: объект ошибок по полям
  • Ошибка: null

Лучшие практики

1. Валидация — в конфиге, в методе — только бизнес-логика

В Wepps проверка полей (тип, обязательность, лишние поля) выполняется автоматически на уровне конфига в RestConfig.php — метод получает уже провалидированные данные:

// RestConfig.php — декларативные правила валидации
'cart' => [
    'validation' => [
        'id' => ['type' => 'string', 'required' => true],
        'quantity' => ['type' => 'int2', 'required' => false],
    ],
],
// ❌ Плохо — дублировать проверку, которую уже сделал фреймворк
$id = trim($data['id'] ?? '');
if (empty($id)) {
    return ['status' => 400, 'message' => 'ID required', 'data' => null];
}

// ✅ Хорошо — конфиг уже проверил тип и обязательность,
// в методе просто берём готовое значение
$records = $this->normalizeInput();   // разворачивает {data: ...} → [запись]
$id = $records[0]['id'];              // id гарантирован есть (required = true)
$quantity = $records[0]['quantity'] ?? 1;  // необязательное поле — с дефолтом

В методе проверяйте только то, что невозможно описать в конфиге: существование записи в БД, права доступа, бизнес-состояния (см. Business-logic валидация).

2. Использовать стандартные HTTP коды

// ❌ Плохо
return ['status' => 200, 'message' => 'Product not found'];

// ✅ Хорошо
return ['status' => 404, 'message' => 'Product not found', 'data' => null];

3. Возвращать структурированные ошибки

// ❌ Плохо
return ['status' => 400, 'message' => 'Validation error: id is required, quantity must be > 0'];

// ✅ Хорошо
return [
    'status' => 400,
    'message' => 'Validation error',
    'data' => [
        'id' => 'Field is required',
        'quantity' => 'Must be greater than 0',
    ],
];

4. Использовать guard clause (ранний return)

// ❌ Глубокая вложенность
if ($condition) {
    // много кода
} else {
    return ['status' => 400, 'message' => 'Error', 'data' => null];
}

// ✅ Guard clause
if (!$condition) {
    return ['status' => 400, 'message' => 'Error', 'data' => null];
}
// основной код

5. Не выдавать внутренние ошибки клиенту

// ❌ Плохо
try {
    $result = $database->query($sql);
} catch (Exception $e) {
    return ['status' => 500, 'message' => $e->getMessage()]; // Утечка информации!
}

// ✅ Хорошо
try {
    $result = $database->query($sql);
} catch (Exception $e) {
    return ['status' => 500, 'message' => 'Server error', 'data' => null];
}

Async-режим (очередь задач)

Тяжёлые операции (например, массовое создание товаров из 1С) можно поставить в очередь:

'goods' => [
    'class' => RestV1M2M::class,
    'method' => 'postGoods',
    'async' => true,   // ← поставить в очередь s_Tasks
    ...
],

Поведение:

  1. Запрос получает ответ 202 Accepted с task_id
  2. Задача сохраняется в таблицу s_Tasks (класс, метод, данные, пользователь)
  3. Обработка выполняется CLI-скриптом: php Request.php tasks.process
  4. Результат получается по GET /rest/m2m/tasks.result?id={task_id}
// 202 Accepted
{
  "status": 202,
  "message": "Accepted",
  "data": { "task_id": 123 }
}
// GET /rest/m2m/tasks.result?id=123
// Пока задача в очереди:
{
  "status": 200,
  "message": "OK",
  "data": {
    "id": 123,
    "is_processed": false,   // ещё не выполнена
    "http_status": null,     // нет результата
    "response": null
  }
}

// После выполнения:
{
  "status": 200,
  "message": "OK",
  "data": {
    "id": 123,
    "is_processed": true,
    "http_status": 201,       // статус исходной операции
    "response": {
      "status": 201,
      "data": { "id": 321 }   // результат операции
    }
  }
}

Polling: опрашивайте GET /rest/m2m/tasks.result?id={task_id} с интервалом (например, раз в 5–10 секунд), пока is_processed не станет true. Если задачи с таким id нет — вернётся 404.

Интеграция с внешними системами

Для интеграции с внешними системами используйте RestV1M2M (/rest/m2m/). Все M2M-эндпоинты требуют Bearer-токен пользователя с ролью 1 (s_Users.UserPermissions = 1).

Шаг 1 — получить токен через общую авторизацию POST /rest/v1/auth.login (см. Токен для M2M):

POST /rest/v1/auth.login HTTP/1.1
Content-Type: application/json

{
  "data": {
    "login": "m2m-admin@example.com",
    "password": "secret"
  }
}

Шаг 2 — использовать токен во всех запросах:

# Создать товар (batch из 1С)
POST /rest/m2m/goods HTTP/1.1
Authorization: Bearer token...

{
  "data": [
    {
      "guid": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Свитер",
      "alias": "sviter",
      "navigatorId": "8",
      "price": "17450.00"
    }
  ]
}

# Обновить остатки товара
PUT /rest/m2m/goods.stocks HTTP/1.1
Authorization: Bearer token...

{
  "data": [
    { "id": 1, "stocks": 5 },
    { "id": 2, "stocks": 0 }
  ]
}

# Удалить заказы (batch)
DELETE /rest/m2m/orders HTTP/1.1
Authorization: Bearer token...

{
  "data": [5001, 5002, 5003]
}

Краткий путеводитель

Для мобильного приложения (v1):

  • Используйте /rest/v1/ endpoints (RestV1APP, RestV1)
  • Примеры: /rest/v1/home, /rest/v1/goods, /rest/v1/cart, /rest/v1/auth.login
  • Готовая бизнес-логика, camelCase ответы, авторизация JWT

Для внешней интеграции (m2m):

  • Используйте /rest/m2m/ endpoints (RestV1M2M)
  • Универсальный CRUD для таблиц: users, goods, orders, files, goods.variations, goods.stocks и т.д.
  • Batch-операции (207 Multi-Status), валидация из БД (s_ConfigFields)
  • Требует роль 1 (администратор M2M)

Для админки (wepps):

  • Используйте /rest/wepps/ endpoints (RestAd)
  • Токен, поиск по спискам админки

Для командной строки (cli):

  • php Request.php tasks.process — обработка async-задач
  • php Request.php removeLogLocal — очистка логов

Как фреймворк: Гибкость разработки

Полный контроль над кодом, архитектурой и расширениями для сложных проектов

wapps framework

Как CMS: Простота управления

Интуитивная админ-панель для редакторов контента без программирования

wapps cms

Как платформа: Готовые решения

Быстрый старт проектов с возможностью глубокой кастомизации под любые задачи

wapps platform
A
AI-Ready проект: как сделать код понятным для искусственного интеллекта

AI-Ready — это когда искусственный интеллект может:

- Быстро понять архитектуру проекта
- Генерировать корректный код в стиле проекта
- Находить нужную информацию без долгих поисков
- Предлагать релевантные изменения

02.02.2026
C
Расширение конфигурации таблиц в Wepps Platform: Action-обработчики

Система управления списками данных в Wepps Platform предоставляет мощный механизм расширения стандартного функционала через Action-обработчики. Это специальные классы, которые автоматически выполняются при различных операциях со списками (просмотр, создание, изменение, удаление), позволяя добавить кастомную логику без изменения ядра системы.

31.01.2026
T
Очереди задач (Tasks Queue) в Wepps Platform: Асинхронная обработка задач

Система очередей задач (Tasks Queue) в Wepps Platform — это механизм для отложенной асинхронной обработки тяжелых или длительных операций. Задачи регистрируются в базе данных и обрабатываются отдельным процессом, что позволяет не блокировать основной поток выполнения приложения и обеспечивает надежную обработку даже при сбоях.

29.01.2026