atomic-api-contract-deleted-fulldelete.md 4.4 KB

API контракт: deleted-статус и FullDelete эндпоинт

Контекст: Добавлен статус deleted для Place (мягкое удаление пользователем) и модальное окно с кнопкой полного удаления для админа. API контракт не документирован.

Текущее состояние

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=true400 Bad Request

Чего не хватает

1. Логирование мягких удалений

Сейчас PATCH status=deleted не пишет в moderation_log. Нужно:

// 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 модели

// handlers/places.go — ⚠️ заглушка
func (h *PlaceHandler) HardDelete(w http.ResponseWriter, r *http.Request) {
    // НЕТ записи в audit log — нужно добавить после создания ModerationLog модели
}

Тесты

Soft Delete

// 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

// Если админ не передал 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