# API, HTTP-методы и Swagger

## Swagger / OpenAPI

Backend API:

- UI: `http://localhost:8081/docs`
- JSON: `http://localhost:8081/api/doc.json`
- Конфиг: `apps/backend/config/routes/nelmio_api_doc.yaml`
- В документацию backend сейчас попадают только path patterns из `apps/backend/config/packages/nelmio_api_doc.yaml`.

Cabinet:

- UI: `http://localhost:8082/api/swagger`
- JSON: `http://localhost:8082/api/swagger.json`
- Реализовано вручную в `apps/cabinet/src/Controller/InternalAPIController.php`.

## Backend API

### Пользователь

| Метод | Путь | Контроллер | Назначение |
| --- | --- | --- | --- |
| `GET` | `/user/logout` | `UserController::logout` | Logout-заглушка для клиента |
| `POST` | `/user/login` | `UserController::login` | Логин по email/password, выдача JWT |
| `GET` | `/user/` | `UserController::index` | Текущий пользователь по JWT |
| `PUT` | `/user/change-region` | `UserController::changeRegion` | Смена региона пользователя |
| `POST` | `/user/auth` | `UserController::auth` | Регистрация/авторизация |
| `POST` | `/user/auth-by-pcode` | `UserController::authByPcode` | Авторизация по pcode и дате рождения |

### Справочники и контент

| Метод | Путь | Контроллер | Назначение |
| --- | --- | --- | --- |
| `GET` | `/filial/list` | `FilialController` | Список филиалов |
| `GET` | `/filial/by-region/{regionId}` | `FilialController` | Филиалы по региону |
| `PUT` | `/filial/{fid}` | `FilialController` | Обновление филиала |
| `POST` | `/filial/create` | `FilialController` | Создание филиала |
| `GET` | `/filial/picture/{id}` | `FilialController` | Картинка филиала |
| `POST` | `/filial/picture/{id}` | `FilialController` | Загрузка картинки филиала |
| `GET` | `/department/list` | `DepartmentController` | Список отделений |
| `PUT` | `/department/{did}` | `DepartmentController` | Обновление отделения |
| `POST` | `/department/create` | `DepartmentController` | Создание отделения |
| `GET` | `/pricelist/list` | `PriceListController` | Прайс |
| `GET` | `/pricelist/department` | `PriceDepartmentController` | Группы прайса |

### Врачи и расписание

| Метод | Путь | Контроллер | Назначение |
| --- | --- | --- | --- |
| `POST` | `/specialist/create` | `SpecialistController` | Создание врача |
| `PUT` | `/specialist/{id}` | `SpecialistController` | Обновление врача |
| `DELETE` | `/specialist/{id}` | `SpecialistController` | Удаление врача |
| `GET` | `/specialist/list` | `SpecialistController` | Список врачей |
| `GET` | `/specialist/post` | `SpecialistController` | Список должностей |
| `GET` | `/specialist/picture/{id}` | `SpecialistController` | Фото врача |
| `POST` | `/specialist/picture/{id}` | `SpecialistController` | Загрузка фото врача |
| `GET` | `/specialist/schedule` | `SpecialistController` | Расписание врача |
| `GET` | `/specialist/{id}` | `SpecialistController` | Детальная карточка по id |
| `GET` | `/specialist/by/{identifier}` | `SpecialistController` | Детальная карточка по alias/id |
| `POST` | `/specialist/{id}/location/create` | `LocationController` | Создание локации врача |
| `PUT` | `/specialist/{specialistId}/location/{id}` | `LocationController` | Обновление локации врача |
| `GET` | `/specialist-dcode-description/list` | `SpecialistDcodeDescriptionController` | Описания dcode |
| `GET` | `/specialist-dcode-description/{id}` | `SpecialistDcodeDescriptionController` | Описание dcode |
| `POST` | `/specialist/{specialistId}/specialist-dcode-description/create` | `SpecialistDcodeDescriptionController` | Создание описания dcode |
| `PUT` | `/specialist/{specialistId}/specialist-dcode-description/{id}` | `SpecialistDcodeDescriptionController` | Обновление описания dcode |
| `DELETE` | `/specialist-dcode-description/{id}` | `SpecialistDcodeDescriptionController` | Удаление описания dcode |
| `GET` | `/specialist-docs/list` | `SpecialistDocsController` | Документы врачей |
| `GET` | `/specialist-docs/{id}` | `SpecialistDocsController` | Документ врача |
| `GET` | `/specialist-docs/picture/{id}` | `SpecialistDocsController` | Картинка документа |
| `POST` | `/specialist/{id}/specialist-docs/create` | `SpecialistDocsController` | Создание документа |
| `PUT` | `/specialist/{specialistId}/specialist-docs/{id}` | `SpecialistDocsController` | Обновление документа |
| `DELETE` | `/specialist-docs/{id}` | `SpecialistDocsController` | Удаление документа |
| `POST` | `/specialist-docs/picture/{id}` | `SpecialistDocsController` | Загрузка картинки документа |
| `DELETE/PUT` | `/stock/{id}/specialist/{specialistId}` | `StockSpecialistController` | Связь акции и врача |

### CRUD-контент

Эти контроллеры имеют одинаковый набор методов:

| Контроллер | Базовый путь | Методы |
| --- | --- | --- |
| `ArticleController` | `/article` | `GET /list`, `GET /alias/{alias}`, `GET /{id}`, `POST /create`, `PUT /{id}`, `DELETE /{id}` |
| `DiseaseController` | `/disease` | `GET /list`, `GET /{id}`, `POST /create`, `PUT /{id}`, `DELETE /{id}` |
| `MedicalCenterController` | `/medical-center` | `GET /list`, `GET /{id}`, `POST /create`, `PUT /{id}`, `DELETE /{id}` |
| `NewsController` | `/news` | `GET /list`, `GET /{id}`, `POST /create`, `PUT /{id}`, `DELETE /{id}` |
| `PromoController` | `/promo` | `GET /list`, `GET /{id}`, `POST /create`, `PUT /{id}`, `DELETE /{id}` |
| `ReviewController` | `/review` | `GET /list`, `GET /{id}`, `POST /create`, `PUT /{id}`, `DELETE /{id}` |
| `SiteServiceController` | `/site-services` | `GET /list`, `GET /{id}`, `POST /create`, `PUT /{id}`, `DELETE /{id}` |

Для контентных сущностей `POST`, `PUT` и `DELETE` доступны только с JWT пользователя с ролью `ROLE_ADMIN`.

Списки `news`, `promo`, `medical-center`, `disease`, `site-services` возвращают единый формат пагинации:

```json
{
  "data": [],
  "pagination": {
    "total": 1,
    "count": 1,
    "per_page": 50,
    "current_page": 1,
    "total_pages": 1,
    "has_previous_page": false,
    "has_next_page": false
  }
}
```

Основные query-параметры списков:

- `page` - номер страницы;
- `perPage` - размер страницы;
- `regionId` - фильтр по региону;
- `active` - фильтр активности.

`/article/list` сохраняет старый контракт фронтенда: размер страницы задаётся параметром `limit`, а метаданные возвращаются в ключе `meta`:

```json
{
  "data": [],
  "meta": {
    "total": 1,
    "page": 1,
    "limit": 20,
    "totalPages": 1
  }
}
```

Пример создания новости:

```bash
curl -X POST http://localhost:8081/news/create \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
    "id": 900001,
    "name": "Локальная новость",
    "active": true,
    "regionId": 91,
    "alias": "local-admin-news",
    "anons": "Краткий анонс",
    "content": "Полный текст новости"
  }'
```

### Интеграции и служебные методы

| Метод | Путь | Контроллер | Назначение |
| --- | --- | --- | --- |
| `GET` | `/infoclinica/clvisitsovacheckpass/{filial}` | `InfoclinicaController` | Проверка прохода |
| `GET` | `/idoctor/list` | `InfoclinicaController` | Список врачей Infoclinica |
| `POST` | `/reservation/anonymous-reserve` | `InfoclinicaController` | Анонимная запись |
| `POST` | `/calltouch/create-lead` | `CalltouchController` | Создание лида |
| `GET` | `/service/sendmail` | `ServiceController` | Отправка письма |
| `POST` | `/smart-captcha` | `ServiceController` | Проверка captcha |
| `GET` | `/xml/feed` | `XmlFeedController` | XML-фид |
| `GET` | `/xml/feed/v1` | `XmlFeedController` | XML-фид v1 |

## Cabinet API и страницы

### Swagger и внутренний API

| Метод | Путь | Контроллер | Назначение |
| --- | --- | --- | --- |
| `ANY` | `/api/swagger.json` | `InternalAPIController::swaggerJson` | OpenAPI JSON |
| `ANY` | `/api/swagger` | `InternalAPIController::swaggerUI` | Swagger UI |
| `POST` | `/api/smart-captcha` | `InternalAPIController::smartCaptcha` | Проверка captcha |
| `GET` | `/api/banner/{regionId}` | `InternalAPIController::show` | Баннер региона |
| `POST` | `/api/log` | `InternalAPIController::log` | Логирование |
| `POST` | `/api/count-record` | `InternalAPIController::countRecord` | Количество записей |
| `POST` | `/api/add-record` | `InternalAPIController` | Создание записи |
| `POST` | `/api/msg` | `InternalAPIController` | Сообщение/SMS |
| `POST` | `/api/veretify` | `InternalAPIController` | Проверка SMS-кода |
| `POST` | `/api/search` | `InternalAPIController` | Поиск |
| `GET` | `/api/departments` | `InternalAPIController` | Отделения |
| `POST` | `/api/add-calltouch` | `CalltouchAPIController` | Заявка Calltouch |

### Публичный API cabinet

| Метод | Путь | Контроллер | Назначение |
| --- | --- | --- | --- |
| `POST` | `/api/anonymous-reserve` | `PublicAPIController` | Анонимная запись |
| `GET` | `/api/interval` | `PublicAPIController` | Интервалы расписания |
| `GET` | `/api/userInfo` | `PublicAPIController` | Информация о пользователе |
| `GET` | `/api/pricelist/departments` | `PublicAPIController` | Группы прайса |
| `GET` | `/api/pricelist` | `PublicAPIController` | Прайс |
| `GET` | `/api/doctor` | `PublicAPIController` | Врач |
| `GET` | `/api/doctors/{region}` | `PublicAPIController` | Врачи региона |

### Страницы cabinet

| Метод | Путь | Контроллер | Назначение |
| --- | --- | --- | --- |
| `ANY` | `/login` | `SecurityController` | Страница входа |
| `ANY` | `/logout` | `SecurityController` | Выход |
| `POST` | `/api/usrlog/logout` | `SecurityController` | API logout |
| `GET/POST` | `/registration` | `SecurityController` | Регистрация |
| `POST` | `/forget` | `SecurityController` | Восстановление |
| `POST` | `/api/authenticated` | `SecurityController` | Авторизация API |
| `GET` | `/` | `DefaultController` | Главная кабинета, требует авторизации |
| `ANY` | `/stoimost-uslug` | `DefaultController` | Стоимость услуг |
| `POST` | `/update/price-list` | `DefaultController` | Обновление прайса |
| `ANY` | `/price-list` | `DefaultController` | Админский прайс |
| `GET` | `/specialists/{alias?}` | `SpecialistController` | Каталог врачей |
| `GET` | `/online-specialists` | `SpecialistController` | Онлайн-врачи |
| `GET` | `/specialist/{alias}` | `SpecialistController` | Карточка врача |
| `ANY` | `/favorites` | `SpecialistController` | Избранное |
