## API контракт: deleted-статус и FullDelete эндпоинт **Контекст:** Добавлен статус `deleted` для Place (мягкое удаление пользователем) и модальное окно с кнопкой полного удаления для админа. API контракт не документирован. ## Текущее состояние ```mermaid graph LR User[Пользователь] -->|Soft Delete| PATCH["PATCH /places/{id} {status:deleted}"] Admin[Администратор] -->|Full Delete| DEL["DELETE /places/{id}?hard=true"] PATCH -->|SET status='deleted'| DB[(PostgreSQL)] DEL -->|CASCADE + MinIO cleanup| DB DEL -->|Delete files| S3[(MinIO)] ``` ### Soft Delete (`PATCH /places/{id}`) - **Кто вызывает**: владелец места - **Тело**: `{ "status": "deleted" }` (любые другие поля игнорируются для deleted) - **Логика**: `UPDATE places SET status = 'deleted' WHERE id = $1 AND owner_id = $2` - **Ответ**: `200 OK` с обновлённым place - **Безопасность**: только владелец места - **Обратимость**: администратор может сменить статус обратно через тот же PATCH ### Full Delete (`DELETE /places/{id}?hard=true`) - **Кто вызывает**: администратор (роль `admin`/`superadmin`) - **Параметры**: `hard=true` (обязательно) - **Логика**: 1. Удалить файлы из MinIO (связанные с place через `UploadHandler.RemoveFile`) 2. DELETE CASCADE в БД (place_tags, place_features, reviews, bookings) 3. Запись в `moderation_log` (нужна модель `ModerationLog`) - **Ответ**: `204 No Content` - **Безопасность**: middleware проверяет роль; `DELETE /places/{id}` без `hard=true` — `400 Bad Request` ## Чего не хватает ### 1. Логирование мягких удалений Сейчас `PATCH status=deleted` не пишет в `moderation_log`. Нужно: ```go // services/places.go func (s *PlaceService) SoftDelete(ctx context.Context, placeID, userID string) error { // 1. Проверить права // 2. UPDATE status = 'deleted' // 3. s.moderationRepo.Log(ctx, ModerationLog{ // ModeratorID: userID, // TargetType: "place", // TargetID: placeID, // Action: "soft_delete", // NewStatus: "deleted", // }) } ``` ### 2. Эндпоинт восстановления Нет способа восстановить место (кроме прямого PATCH админом). Нужен отдельный эндпоинт или разрешить `PATCH /places/{id} {status:active}` владельцу, если место в `deleted`. ### 3. Список удалённых мест Сейчас фильтр `?status=deleted` не реализован в `GET /places`. Нужен для админской вкладки "Удалённые". ### 4. FullDelete без `ModerationLog` модели ```go // handlers/places.go — ⚠️ заглушка func (h *PlaceHandler) HardDelete(w http.ResponseWriter, r *http.Request) { // НЕТ записи в audit log — нужно добавить после создания ModerationLog модели } ``` ## Тесты ### Soft Delete ```go // services/places_test.go func TestPlaceService_SoftDelete(t *testing.T) { // given: создано место ownerID=userA // when: SoftDelete(ctx, placeID, userA) // then: статус места == "deleted" // when: SoftDelete(ctx, placeID, userB) // не владелец // then: ошибка (403) } ``` ### Full Delete ```go // Если админ не передал hard=true — 400 // Если не админ — 403 // Если успешно — 204, место не существует в БД // Если место уже deleted — 404 (не надо удалять дважды) ``` ## Связанные заметки - [[atomic-missing-go-models]] — нужна модель ModerationLog - [[atomic-csrf-protection]] — DELETE требует CSRF ## Источник Реализация admin/page.tsx вкладка "Удалённые" + кнопка полного удаления. #api #contract #security #golang #frontend