Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions docs/subscribed-projects-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Subscribed Projects API

## Назначение

`GET /projects/subscribed/` возвращает карточки проектов, на обновления которых
подписан текущий пользователь. Endpoint предназначен для будущей React-вкладки
«Мои подписки» и требует авторизации.

## Источник данных и видимость

Список использует существующую M2M-связь `Project.subscribers`; отдельная модель
подписок не создаётся. Queryset формируется в базе данных как пересечение:

1. проектов, где текущий пользователь присутствует в `subscribers`;
2. проектов, доступных ему по общим workspace-правилам.

Опубликованный публичный проект доступен любому авторизованному пользователю.
Private или draft видны руководителю, collaborator, staff и superuser. Если
пользователь потерял доступ к private/draft проекту, карточка исчезает из выдачи,
но историческая M2M-связь подписки не удаляется.

## Запрос

```http
GET /projects/subscribed/
GET /projects/subscribed/?page=2
GET /projects/subscribed/?search=лаборатория
GET /projects/subscribed/?page=2&search=лаборатория
```

`search` применяется только к подпискам текущего пользователя, ищет по названию
без учёта регистра и игнорирует пробелы по краям. Пустое значение эквивалентно
отсутствию поиска. Персональные данные пользователей в поиск не входят.

## Пагинация и сортировка

Размер страницы равен 10, а ответ сохраняет общий формат project lists:

```json
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 42,
"name": "Лаборатория городской среды",
"short_description": "Краткое описание",
"image_address": null,
"cover_image_address": null,
"draft": false,
"is_public": true,
"current_user_role": null,
"can_edit": false,
"can_use_in_application": false,
"activities": [],
"datetime_updated": "2026-08-11T09:30:00Z"
}
]
}
```

Несуществующая или некорректная страница возвращает безопасный `404`. Проекты
сортируются по `-datetime_updated`, затем по `-id`, поэтому порядок остаётся
стабильным при одинаковом времени обновления.

## Контракт карточки и запросы

Endpoint переиспользует `ProjectWorkspaceListSerializer`, применяемый в
`/projects/my/` и `/projects/catalog/`. Он не возвращает подписчиков, email,
телефоны, даты рождения, приглашения или другие закрытые данные.

Роли текущего пользователя и связанные активности загружаются заранее. Serializer
не обращается к базе, а фильтрация видимости не выполняется отдельно для каждой
карточки. GET не изменяет Project, подписки, collaborators, приглашения и новости.

## Совместимость

Workspace endpoints состояния и изменения подписки сохраняются без изменений:

```http
GET /projects/<project_id>/workspace/subscription/
POST /projects/<project_id>/workspace/subscription/
DELETE /projects/<project_id>/workspace/subscription/
```

Legacy Angular endpoints `subscribe`, `unsubscribe` и `subscribers` также не
изменяются. После успешной workspace-подписки доступный проект появляется в новом
списке, после отписки — исчезает.

## Ограничения этапа

DEV‑084.3 не добавляет React-интерфейс, список пользователей-подписчиков,
уведомления, автоматическую подписку, очистку потерявших доступ связей или новую
модель данных. Новостная лента, зависимости и deploy-конфигурация не меняются.
6 changes: 6 additions & 0 deletions projects/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ class ProjectsPagination(pagination.LimitOffsetPagination):
default_limit = 10
limit_query_param = "limit"
offset_query_param = "offset"


class SubscribedProjectsPagination(pagination.PageNumberPagination):
"""Сохраняет размер и структуру списков проектов с параметром `page`."""

page_size = ProjectsPagination.default_limit
Loading
Loading