✨ feat(src/modules/cities/cities.controller.ts): добавляет ответы API для получения всех городов и районов
✨ feat(src/modules/chat/chat.controller.ts): добавляет ответы API для создания чата и получения сообщений ✨ feat(src/modules/greetings/greetings.controller.ts): добавляет ответы API для получения и создания приветствий ✨ feat(src/modules/likes/likes.controller.ts): добавляет ответы API для создания лайков и получения совпадений ✨ feat(src/modules/reports/reports.controller.ts): добавляет ответы API для создания и получения отчетов ✨ feat(src/modules/feed/feed.controller.ts): добавляет ответ API для получения отфильтрованного фида ✨ feat(src/auth/auth.controller.ts): добавляет ответы API для регистрации, входа и выхода пользователей ✨ feat(src/modules/media/media.controller.ts): добавляет ответы API для загрузки и получения медиа ✨ feat(src/modules/users/users.controller.ts): добавляет ответы API для получения текущего пользователя и управления пользователями ✨ feat(src/modules/tags/tags.controller.ts): добавляет ответы API для получения и создания тегов ✨ feat(src/modules/profiles/profiles.controller.ts): добавляет ответы API для управления профилями пользователей ✨ feat(src/modules/dates/dates.controller.ts): добавляет ответы API для создания и получения встреч
This commit is contained in:
341
frontend-starter-prompt.md
Normal file
341
frontend-starter-prompt.md
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
# Стартовый промт — Frontend Dating App
|
||||||
|
|
||||||
|
## Контекст проекта
|
||||||
|
|
||||||
|
Ты помогаешь разрабатывать фронтенд мобильного/десктопного дейтинг-приложения.
|
||||||
|
OpenAPI-схема бэкенда лежит в корне проекта (`openapi.json`).
|
||||||
|
Генерируй типизированный HTTP-клиент из неё автоматически.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Технологический стек
|
||||||
|
|
||||||
|
| Слой | Выбор |
|
||||||
|
|---|---|
|
||||||
|
| UI-фреймворк | **Vue 3** (Composition API + `<script setup>`) |
|
||||||
|
| Язык | **TypeScript** везде |
|
||||||
|
| Сборщик | **Vite** |
|
||||||
|
| Десктоп / нативный слой | **Tauri 2** |
|
||||||
|
| Стейт-менеджер | **Pinia** |
|
||||||
|
| Роутер | **Vue Router 4** |
|
||||||
|
| HTTP-клиент | **axios** + автогенерированные типы из OpenAPI (`openapi-typescript` + `axios`) |
|
||||||
|
| WebSocket | **socket.io-client** |
|
||||||
|
| UI-библиотека | **Naive UI** (или UnoCSS + headless — на твоё усмотрение) |
|
||||||
|
| Формы | **VeeValidate** + **Zod** |
|
||||||
|
| Пакетный менеджер | **pnpm** |
|
||||||
|
| Анимации | **@vueuse/motion** |
|
||||||
|
| Утилиты | **VueUse** |
|
||||||
|
| Иконки | **@iconify/vue** |
|
||||||
|
| Адаптивность | mobile-first; десктоп — расширение, не отдельная версия |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что такое это приложение
|
||||||
|
|
||||||
|
Дейтинг-приложение. Пользователь:
|
||||||
|
1. Регистрируется по номеру телефона + пароль
|
||||||
|
2. Создаёт один или несколько публичных **профилей** (у одного аккаунта может быть много профилей)
|
||||||
|
3. Листает **ленту** других профилей с фильтрацией
|
||||||
|
4. Ставит **лайки / дизлайки** на профили
|
||||||
|
5. При взаимном лайке создаётся **матч**
|
||||||
|
6. Открывает **чат** с одним матчем за раз (один активный чат на профиль)
|
||||||
|
7. В чате обменивается текстом, фото, голосовыми и видео-сообщениями
|
||||||
|
8. Договаривается о реальных **встречах** (дата, координаты, статус)
|
||||||
|
9. Может подать **жалобу** на профиль или сообщение
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Архитектура бэкенда (важно для интеграции)
|
||||||
|
|
||||||
|
### Базовый URL
|
||||||
|
```
|
||||||
|
http://localhost:3000/api/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Формат ответа
|
||||||
|
**Все** успешные ответы обёрнуты:
|
||||||
|
```json
|
||||||
|
{ "data": <payload> }
|
||||||
|
```
|
||||||
|
Ошибки:
|
||||||
|
```json
|
||||||
|
{ "statusCode": 400, "message": "...", "timestamp": "...", "path": "..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Авторизация
|
||||||
|
- Тип: **Bearer JWT**
|
||||||
|
- Заголовок: `Authorization: Bearer <accessToken>`
|
||||||
|
- `POST /auth/register` — регистрация `{ phone, password }`
|
||||||
|
- `POST /auth/login` — вход `{ phone, password }` → `{ accessToken, refreshToken }`
|
||||||
|
- `POST /auth/refresh` — обновление `{ refreshToken }` → `{ accessToken, refreshToken }`
|
||||||
|
- `POST /auth/logout` — выход (требует Bearer)
|
||||||
|
- `POST /auth/fcm-token` — обновить push-токен `{ fcmToken }`
|
||||||
|
- Access-токен: TTL **7 дней** (настраивается на бэке)
|
||||||
|
- Refresh-токен: TTL **30 дней**, хранится в Redis
|
||||||
|
|
||||||
|
### Профили (один аккаунт → много профилей)
|
||||||
|
- `POST /profiles` — создать профиль
|
||||||
|
- `GET /profiles/my` — все мои профили
|
||||||
|
- `GET /profiles/:profileId` — публичный профиль
|
||||||
|
- `PUT /profiles/:profileId` — обновить (только владелец)
|
||||||
|
- `DELETE /profiles/:profileId` — удалить (только владелец)
|
||||||
|
|
||||||
|
Поля профиля: `name`, `birthDate`, `gender` (`male|female`), `cityId`, `districtId`,
|
||||||
|
`description`, `nation`, `height`, `weight`, `tagIds[]`
|
||||||
|
|
||||||
|
Ответ профиля включает: `city`, `district`, `tags[]`, `media[]` (отсортировано по `sortOrder`)
|
||||||
|
|
||||||
|
### Медиа профиля
|
||||||
|
Маршруты: `POST /profiles/:profileId/media/upload?type=photo|video|audio`
|
||||||
|
|
||||||
|
Медиафайлы профиля: **фото, видео, аудио** — хранятся в MinIO.
|
||||||
|
В ответе профиля приходит массив `media[]` с полями `{ id, path, type, sortOrder }`.
|
||||||
|
|
||||||
|
### Лента
|
||||||
|
```
|
||||||
|
GET /feed?profileId=<uuid>&page=1&limit=20
|
||||||
|
&cityId=&districtId=
|
||||||
|
&ageMin=&ageMax=
|
||||||
|
&keyword=
|
||||||
|
&tagIds[]=
|
||||||
|
```
|
||||||
|
- Уже лайкнутые профили **не показываются**
|
||||||
|
- Порядок случайный
|
||||||
|
- При достижении лимита матчей (`MAX_MATCHES_BEFORE_PAUSE=10`) лайк вернёт `400` —
|
||||||
|
клиент должен показать экран «разбери матчи»
|
||||||
|
|
||||||
|
### Лайки и матчи
|
||||||
|
- `POST /likes` — `{ sourceProfileId, targetProfileId, type: "like"|"dislike" }`
|
||||||
|
Ответ: `{ like, match }` — если `match != null`, это взаимный лайк
|
||||||
|
- `GET /likes/matches?profileId=<uuid>` — все матчи профиля
|
||||||
|
|
||||||
|
### Чат
|
||||||
|
- Один профиль — один активный чат одновременно (`profile.active_chat_id`)
|
||||||
|
- `POST /chats` — `{ profileId, matchId }` → открыть чат для матча
|
||||||
|
- `GET /chats?profileId=<uuid>` — активные чаты
|
||||||
|
- `GET /chats/:chatId/messages?profileId=<uuid>&page=1&limit=50`
|
||||||
|
- `POST /chats/:chatId/messages?profileId=<uuid>` — отправить `{ text?, mediaUrl?, mediaType? }`
|
||||||
|
- `DELETE /chats/:chatId?profileId=<uuid>` — закрыть чат
|
||||||
|
|
||||||
|
### Встречи (dates)
|
||||||
|
- `POST /dates` — `{ profileId, partnerProfileId, lat, lng, time, statusId? }`
|
||||||
|
- `GET /dates?profileId=<uuid>`
|
||||||
|
- `PATCH /dates/:id/status?profileId=<uuid>` — `{ statusId }`
|
||||||
|
- `GET /dates/statuses` — справочник статусов (`pending`, `confirmed`, `cancelled`, `rescheduled`)
|
||||||
|
|
||||||
|
### Репорты
|
||||||
|
- `POST /reports` — `{ sourceProfileId, entityId, entityType: "profile"|"message", description? }`
|
||||||
|
|
||||||
|
### Справочники (публичные, без токена)
|
||||||
|
- `GET /tags`
|
||||||
|
- `GET /cities`
|
||||||
|
- `GET /cities/:cityId/districts`
|
||||||
|
- `GET /greetings` — готовые приветственные фразы
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WebSocket (real-time чат)
|
||||||
|
|
||||||
|
Библиотека: **socket.io-client**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const socket = io('http://localhost:3000/chat', {
|
||||||
|
auth: {
|
||||||
|
token: '<accessToken>',
|
||||||
|
profileId: '<activeProfileId>', // обязательно!
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Клиент отправляет:**
|
||||||
|
|
||||||
|
| Событие | Payload | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `join_chat` | `{ chatId }` | Войти в комнату чата |
|
||||||
|
| `leave_chat` | `{ chatId }` | Покинуть комнату |
|
||||||
|
| `send_message` | `{ chatId, text?, mediaUrl?, mediaType? }` | Отправить сообщение |
|
||||||
|
| `typing` | `{ chatId, isTyping: bool }` | Индикатор печати |
|
||||||
|
|
||||||
|
**Сервер отправляет:**
|
||||||
|
|
||||||
|
| Событие | Payload | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `new_message` | `Message` | Новое сообщение в комнате |
|
||||||
|
| `user_typing` | `{ profileId, isTyping }` | Кто-то печатает |
|
||||||
|
|
||||||
|
**Важно:** при смене активного профиля переподключи сокет с новым `profileId`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
api/ # Автогенерированный клиент из openapi.json
|
||||||
|
client.ts # axios-инстанс с интерцептором токена и рефреша
|
||||||
|
index.ts # реэкспорт всех операций
|
||||||
|
stores/
|
||||||
|
auth.store.ts # user, accessToken, refreshToken, login/logout
|
||||||
|
profile.store.ts # activeProfile, myProfiles, switch
|
||||||
|
feed.store.ts # лента, пагинация
|
||||||
|
chat.store.ts # активный чат, сообщения, WS-соединение
|
||||||
|
match.store.ts # матчи
|
||||||
|
composables/
|
||||||
|
useSocket.ts # хук socket.io с авто-реконнектом
|
||||||
|
useInfiniteScroll.ts # бесконечная прокрутка
|
||||||
|
useMediaUpload.ts # загрузка файлов с прогрессом
|
||||||
|
router/
|
||||||
|
index.ts
|
||||||
|
guards.ts # редирект на /login если нет токена, /setup-profile если нет профиля
|
||||||
|
views/
|
||||||
|
auth/
|
||||||
|
LoginView.vue
|
||||||
|
RegisterView.vue
|
||||||
|
onboarding/
|
||||||
|
CreateProfileView.vue # создание первого профиля после регистрации
|
||||||
|
feed/
|
||||||
|
FeedView.vue # свайп-лента или карточки
|
||||||
|
matches/
|
||||||
|
MatchesView.vue
|
||||||
|
chat/
|
||||||
|
ChatsListView.vue
|
||||||
|
ChatView.vue # реалтайм переписка
|
||||||
|
dates/
|
||||||
|
DatesView.vue
|
||||||
|
profile/
|
||||||
|
MyProfilesView.vue # список профилей аккаунта
|
||||||
|
ProfileEditView.vue
|
||||||
|
ProfilePublicView.vue # как видят другие
|
||||||
|
settings/
|
||||||
|
SettingsView.vue
|
||||||
|
components/
|
||||||
|
profile/
|
||||||
|
ProfileCard.vue # карточка в ленте
|
||||||
|
ProfileAvatar.vue
|
||||||
|
MediaGallery.vue # фото/видео/аудио галерея профиля
|
||||||
|
MediaUploader.vue # загрузка с preview
|
||||||
|
chat/
|
||||||
|
MessageBubble.vue
|
||||||
|
TypingIndicator.vue
|
||||||
|
AudioPlayer.vue # проигрыватель голосовых
|
||||||
|
feed/
|
||||||
|
SwipeCard.vue # свайп-карточка (мобильная)
|
||||||
|
LikeButtons.vue
|
||||||
|
common/
|
||||||
|
BottomNav.vue # навигация для мобилки
|
||||||
|
SideNav.vue # навигация для десктопа
|
||||||
|
AppLayout.vue # адаптивный layout
|
||||||
|
tauri/ # Tauri-специфичный код
|
||||||
|
notifications.ts # нативные уведомления
|
||||||
|
deepLinks.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Адаптивность: мобилка и десктоп в одном приложении
|
||||||
|
|
||||||
|
Приложение собирается через **Tauri 2**. Один кодовой базой покрывается:
|
||||||
|
- **Мобилка** (Tauri + iOS/Android через Tauri Mobile) — свайп-жесты, нижняя навигация, полноэкранные карточки
|
||||||
|
- **Десктоп** (Tauri + Windows/macOS/Linux) — боковое меню, двухколоночный layout, drag-and-drop для медиа
|
||||||
|
|
||||||
|
Используй CSS-брейкпоинты **mobile-first**:
|
||||||
|
- `< 768px` — мобильный layout (BottomNav, полноэкранная лента)
|
||||||
|
- `≥ 768px` — планшет/десктоп (SideNav, split-view для чата)
|
||||||
|
|
||||||
|
Определяй режим через `window.innerWidth` или VueUse `useBreakpoints`.
|
||||||
|
Tauri-специфичное поведение (нативные уведомления, файловый пикер) оборачивай в composable с graceful degradation до браузерного API.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ключевые UX-сценарии
|
||||||
|
|
||||||
|
### 1. Первый запуск
|
||||||
|
`Регистрация` → `Создание профиля` (name, birthDate, gender обязательны) →
|
||||||
|
`Загрузка фото` → `Лента`
|
||||||
|
|
||||||
|
### 2. Лента и лайк
|
||||||
|
Карточка профиля → свайп вправо (лайк) / влево (дизлайк) → если матч →
|
||||||
|
показать `MatchModal` с кнопкой «Написать» → открыть чат
|
||||||
|
|
||||||
|
### 3. Лимит матчей
|
||||||
|
Попытка лайкнуть при `N >= 10 матчей` → API вернёт `400` →
|
||||||
|
показать экран/модал «У тебя X матчей. Разберись с ними, чтобы продолжить поиск»
|
||||||
|
|
||||||
|
### 4. Чат
|
||||||
|
Список чатов → выбор → join WS-комнаты → обмен сообщениями в реалтайме →
|
||||||
|
закрытие чата = новый чат стал доступен.
|
||||||
|
Перед открытием чата — можно выбрать приветственную фразу из `GET /greetings`.
|
||||||
|
|
||||||
|
### 5. Смена активного профиля
|
||||||
|
В настройках — список всех профилей аккаунта →
|
||||||
|
при смене переподключить WS с новым `profileId`.
|
||||||
|
|
||||||
|
### 6. Встреча
|
||||||
|
В активном чате кнопка «Назначить встречу» → форма (карта/координаты + дата) →
|
||||||
|
`POST /dates` → партнёр видит `pending` → может подтвердить / отменить / перенести.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Хранение состояния авторизации
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// stores/auth.store.ts (Pinia)
|
||||||
|
// accessToken + refreshToken хранить в localStorage
|
||||||
|
// При 401 — автоматически вызывать POST /auth/refresh
|
||||||
|
// При неудаче refresh — очистить стор, редирект на /login
|
||||||
|
```
|
||||||
|
|
||||||
|
Интерцептор axios:
|
||||||
|
```typescript
|
||||||
|
// При 401: один раз попытаться рефрешнуть токен,
|
||||||
|
// повторить исходный запрос с новым токеном,
|
||||||
|
// если рефреш тоже 401 — logout
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Нотификации
|
||||||
|
|
||||||
|
- В браузере / PWA: Web Notifications API
|
||||||
|
- В Tauri: `@tauri-apps/plugin-notification`
|
||||||
|
- FCM-токен: после логина отправить на `POST /auth/fcm-token`
|
||||||
|
- Обрабатывать WS-события `new_message` и `match_created` для in-app уведомлений
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Генерация API-клиента
|
||||||
|
|
||||||
|
В корне проекта лежит `openapi.json`. Сгенерируй типизированный клиент:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add -D openapi-typescript
|
||||||
|
pnpm exec openapi-typescript openapi.json -o src/api/schema.d.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Используй типы из схемы для всех запросов/ответов.
|
||||||
|
Создай обёртку над axios, которая автоматически разворачивает `{ data: T }`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что НЕ нужно реализовывать сейчас
|
||||||
|
|
||||||
|
- Логика оплаты (таблицы `tariff`/`payment` есть в БД, но API оплаты нет)
|
||||||
|
- Админ-панель (модераторские эндпоинты — ban/activate пользователей — есть в API, но отдельного UI не нужно)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Старт проекта
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm create tauri-app@latest
|
||||||
|
# выбрать: Vue + TypeScript + Vite
|
||||||
|
|
||||||
|
pnpm add pinia vue-router axios socket.io-client vee-validate zod @vueuse/core @vueuse/motion naive-ui @iconify/vue
|
||||||
|
pnpm add -D openapi-typescript @types/node
|
||||||
|
|
||||||
|
# Сгенерировать типы из схемы
|
||||||
|
pnpm exec openapi-typescript openapi.json -o src/api/schema.d.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Не спрашивай подтверждения на каждый шаг — работай до тех пор,
|
||||||
|
пока весь фронтенд не будет готов к первому запуску (`pnpm tauri dev`).
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { LoginDto } from './dto/login.dto';
|
import { LoginDto } from './dto/login.dto';
|
||||||
import { RefreshTokenDto } from './dto/refresh-token.dto';
|
import { RefreshTokenDto } from './dto/refresh-token.dto';
|
||||||
import { RegisterDto } from './dto/register.dto';
|
import { RegisterDto } from './dto/register.dto';
|
||||||
|
import { TokensResponseDto } from './dto/tokens-response.dto';
|
||||||
import { Public } from '../common/decorators/public.decorator';
|
import { Public } from '../common/decorators/public.decorator';
|
||||||
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
||||||
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
|
||||||
|
import { MessageResponseDto } from '../common/dto/message-response.dto';
|
||||||
|
|
||||||
@ApiTags('auth')
|
@ApiTags('auth')
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
@@ -16,6 +18,7 @@ export class AuthController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Post('register')
|
@Post('register')
|
||||||
@ApiOperation({ summary: 'Register new user' })
|
@ApiOperation({ summary: 'Register new user' })
|
||||||
|
@ApiResponse({ status: 201, type: TokensResponseDto })
|
||||||
register(@Body() dto: RegisterDto) {
|
register(@Body() dto: RegisterDto) {
|
||||||
return this.authService.register(dto);
|
return this.authService.register(dto);
|
||||||
}
|
}
|
||||||
@@ -23,6 +26,7 @@ export class AuthController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Post('login')
|
@Post('login')
|
||||||
@ApiOperation({ summary: 'Login with phone and password' })
|
@ApiOperation({ summary: 'Login with phone and password' })
|
||||||
|
@ApiResponse({ status: 201, type: TokensResponseDto })
|
||||||
login(@Body() dto: LoginDto) {
|
login(@Body() dto: LoginDto) {
|
||||||
return this.authService.login(dto);
|
return this.authService.login(dto);
|
||||||
}
|
}
|
||||||
@@ -31,6 +35,7 @@ export class AuthController {
|
|||||||
@Post('logout')
|
@Post('logout')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Logout current user' })
|
@ApiOperation({ summary: 'Logout current user' })
|
||||||
|
@ApiResponse({ status: 201, type: MessageResponseDto })
|
||||||
logout(@CurrentUser('id') userId: string) {
|
logout(@CurrentUser('id') userId: string) {
|
||||||
return this.authService.logout(userId);
|
return this.authService.logout(userId);
|
||||||
}
|
}
|
||||||
@@ -38,6 +43,7 @@ export class AuthController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Post('refresh')
|
@Post('refresh')
|
||||||
@ApiOperation({ summary: 'Refresh access token' })
|
@ApiOperation({ summary: 'Refresh access token' })
|
||||||
|
@ApiResponse({ status: 201, type: TokensResponseDto })
|
||||||
refresh(@Body() dto: RefreshTokenDto) {
|
refresh(@Body() dto: RefreshTokenDto) {
|
||||||
return this.authService.refreshTokens(dto.refreshToken);
|
return this.authService.refreshTokens(dto.refreshToken);
|
||||||
}
|
}
|
||||||
@@ -46,6 +52,7 @@ export class AuthController {
|
|||||||
@Post('fcm-token')
|
@Post('fcm-token')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Update FCM push token' })
|
@ApiOperation({ summary: 'Update FCM push token' })
|
||||||
|
@ApiResponse({ status: 201, type: MessageResponseDto })
|
||||||
updateFcmToken(
|
updateFcmToken(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Body('fcmToken') fcmToken: string,
|
@Body('fcmToken') fcmToken: string,
|
||||||
|
|||||||
9
src/auth/dto/tokens-response.dto.ts
Normal file
9
src/auth/dto/tokens-response.dto.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class TokensResponseDto {
|
||||||
|
@ApiProperty({ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' })
|
||||||
|
accessToken: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' })
|
||||||
|
refreshToken: string;
|
||||||
|
}
|
||||||
6
src/common/dto/message-response.dto.ts
Normal file
6
src/common/dto/message-response.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class MessageResponseDto {
|
||||||
|
@ApiProperty({ example: 'Operation successful' })
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, Query, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Post, Query, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { ChatService } from './chat.service';
|
import { ChatService } from './chat.service';
|
||||||
import { CreateChatDto } from './dto/create-chat.dto';
|
import { CreateChatDto } from './dto/create-chat.dto';
|
||||||
import { SendMessageDto } from './dto/send-message.dto';
|
import { SendMessageDto } from './dto/send-message.dto';
|
||||||
|
import { ChatDto, MessageDto } from './dto/chat-response.dto';
|
||||||
|
import { MessageResponseDto } from '../../common/dto/message-response.dto';
|
||||||
|
|
||||||
@ApiTags('chat')
|
@ApiTags('chat')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -15,12 +17,14 @@ export class ChatController {
|
|||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Open a chat for a match' })
|
@ApiOperation({ summary: 'Open a chat for a match' })
|
||||||
|
@ApiResponse({ status: 201, type: ChatDto })
|
||||||
createChat(@CurrentUser('id') userId: string, @Body() dto: CreateChatDto) {
|
createChat(@CurrentUser('id') userId: string, @Body() dto: CreateChatDto) {
|
||||||
return this.chatService.createChat(userId, dto);
|
return this.chatService.createChat(userId, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get active chats for a profile' })
|
@ApiOperation({ summary: 'Get active chats for a profile' })
|
||||||
|
@ApiResponse({ status: 200, type: [ChatDto] })
|
||||||
getChats(
|
getChats(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Query('profileId') profileId: string,
|
@Query('profileId') profileId: string,
|
||||||
@@ -30,6 +34,9 @@ export class ChatController {
|
|||||||
|
|
||||||
@Get(':chatId/messages')
|
@Get(':chatId/messages')
|
||||||
@ApiOperation({ summary: 'Get chat messages' })
|
@ApiOperation({ summary: 'Get chat messages' })
|
||||||
|
@ApiQuery({ name: 'page', required: false, schema: { default: 1, type: 'number' } })
|
||||||
|
@ApiQuery({ name: 'limit', required: false, schema: { default: 50, type: 'number' } })
|
||||||
|
@ApiResponse({ status: 200, type: [MessageDto] })
|
||||||
getMessages(
|
getMessages(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Query('profileId') profileId: string,
|
@Query('profileId') profileId: string,
|
||||||
@@ -42,6 +49,7 @@ export class ChatController {
|
|||||||
|
|
||||||
@Post(':chatId/messages')
|
@Post(':chatId/messages')
|
||||||
@ApiOperation({ summary: 'Send a message' })
|
@ApiOperation({ summary: 'Send a message' })
|
||||||
|
@ApiResponse({ status: 201, type: MessageDto })
|
||||||
sendMessage(
|
sendMessage(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Query('profileId') profileId: string,
|
@Query('profileId') profileId: string,
|
||||||
@@ -53,6 +61,7 @@ export class ChatController {
|
|||||||
|
|
||||||
@Delete(':chatId')
|
@Delete(':chatId')
|
||||||
@ApiOperation({ summary: 'Close a chat' })
|
@ApiOperation({ summary: 'Close a chat' })
|
||||||
|
@ApiResponse({ status: 200, type: MessageResponseDto })
|
||||||
closeChat(
|
closeChat(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Query('profileId') profileId: string,
|
@Query('profileId') profileId: string,
|
||||||
|
|||||||
18
src/modules/chat/dto/chat-response.dto.ts
Normal file
18
src/modules/chat/dto/chat-response.dto.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class ChatDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() profile1Id: string;
|
||||||
|
@ApiProperty() profile2Id: string;
|
||||||
|
@ApiProperty({ enum: ['active', 'closed'] }) status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MessageDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() chatId: string;
|
||||||
|
@ApiProperty() profileId: string;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) text: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) mediaUrl: string | null;
|
||||||
|
@ApiPropertyOptional({ enum: ['photo', 'voice', 'video'], nullable: true }) mediaType: string | null;
|
||||||
|
@ApiProperty() createdAt: string;
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { Public } from '../../common/decorators/public.decorator';
|
import { Public } from '../../common/decorators/public.decorator';
|
||||||
import { Roles } from '../../common/decorators/roles.decorator';
|
import { Roles } from '../../common/decorators/roles.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from '../../common/guards/roles.guard';
|
import { RolesGuard } from '../../common/guards/roles.guard';
|
||||||
import { CitiesService } from './cities.service';
|
import { CitiesService } from './cities.service';
|
||||||
|
import { CityResponseDto, DistrictResponseDto } from './dto/city-response.dto';
|
||||||
|
|
||||||
@ApiTags('cities')
|
@ApiTags('cities')
|
||||||
@Controller('cities')
|
@Controller('cities')
|
||||||
@@ -14,6 +15,7 @@ export class CitiesController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get all cities' })
|
@ApiOperation({ summary: 'Get all cities' })
|
||||||
|
@ApiResponse({ status: 200, type: [CityResponseDto] })
|
||||||
findAll() {
|
findAll() {
|
||||||
return this.citiesService.findAll();
|
return this.citiesService.findAll();
|
||||||
}
|
}
|
||||||
@@ -21,6 +23,7 @@ export class CitiesController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Get(':cityId/districts')
|
@Get(':cityId/districts')
|
||||||
@ApiOperation({ summary: 'Get districts for a city' })
|
@ApiOperation({ summary: 'Get districts for a city' })
|
||||||
|
@ApiResponse({ status: 200, type: [DistrictResponseDto] })
|
||||||
findDistricts(@Param('cityId') cityId: string) {
|
findDistricts(@Param('cityId') cityId: string) {
|
||||||
return this.citiesService.findDistricts(cityId);
|
return this.citiesService.findDistricts(cityId);
|
||||||
}
|
}
|
||||||
@@ -30,6 +33,7 @@ export class CitiesController {
|
|||||||
@Roles('admin')
|
@Roles('admin')
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Create city (admin only)' })
|
@ApiOperation({ summary: 'Create city (admin only)' })
|
||||||
|
@ApiResponse({ status: 201, type: CityResponseDto })
|
||||||
createCity(@Body() body: { name: string; lat: number; lng: number }) {
|
createCity(@Body() body: { name: string; lat: number; lng: number }) {
|
||||||
return this.citiesService.createCity(body.name, body.lat, body.lng);
|
return this.citiesService.createCity(body.name, body.lat, body.lng);
|
||||||
}
|
}
|
||||||
@@ -39,6 +43,7 @@ export class CitiesController {
|
|||||||
@Roles('admin')
|
@Roles('admin')
|
||||||
@Post(':cityId/districts')
|
@Post(':cityId/districts')
|
||||||
@ApiOperation({ summary: 'Create district (admin only)' })
|
@ApiOperation({ summary: 'Create district (admin only)' })
|
||||||
|
@ApiResponse({ status: 201, type: DistrictResponseDto })
|
||||||
createDistrict(@Param('cityId') cityId: string, @Body() body: { name: string }) {
|
createDistrict(@Param('cityId') cityId: string, @Body() body: { name: string }) {
|
||||||
return this.citiesService.createDistrict(cityId, body.name);
|
return this.citiesService.createDistrict(cityId, body.name);
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/modules/cities/dto/city-response.dto.ts
Normal file
14
src/modules/cities/dto/city-response.dto.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class CityResponseDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() name: string;
|
||||||
|
@ApiProperty() lat: string;
|
||||||
|
@ApiProperty() lng: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DistrictResponseDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() cityId: string;
|
||||||
|
@ApiProperty() name: string;
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Body, Controller, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { DatesService } from './dates.service';
|
import { DatesService } from './dates.service';
|
||||||
import { CreateDateDto } from './dto/create-date.dto';
|
import { CreateDateDto } from './dto/create-date.dto';
|
||||||
import { UpdateDateStatusDto } from './dto/update-date-status.dto';
|
import { UpdateDateStatusDto } from './dto/update-date-status.dto';
|
||||||
|
import { DateDto, DateStatusDto, DateWithStatusDto } from './dto/dates-response.dto';
|
||||||
|
|
||||||
@ApiTags('dates')
|
@ApiTags('dates')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -15,12 +16,14 @@ export class DatesController {
|
|||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Propose a meetup' })
|
@ApiOperation({ summary: 'Propose a meetup' })
|
||||||
|
@ApiResponse({ status: 201, type: DateDto })
|
||||||
create(@CurrentUser('id') userId: string, @Body() dto: CreateDateDto) {
|
create(@CurrentUser('id') userId: string, @Body() dto: CreateDateDto) {
|
||||||
return this.datesService.create(userId, dto);
|
return this.datesService.create(userId, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get dates for a profile' })
|
@ApiOperation({ summary: 'Get dates for a profile' })
|
||||||
|
@ApiResponse({ status: 200, type: [DateWithStatusDto] })
|
||||||
getDates(
|
getDates(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Query('profileId') profileId: string,
|
@Query('profileId') profileId: string,
|
||||||
@@ -30,6 +33,7 @@ export class DatesController {
|
|||||||
|
|
||||||
@Patch(':id/status')
|
@Patch(':id/status')
|
||||||
@ApiOperation({ summary: 'Update date status' })
|
@ApiOperation({ summary: 'Update date status' })
|
||||||
|
@ApiResponse({ status: 200, type: DateDto })
|
||||||
updateStatus(
|
updateStatus(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Query('profileId') profileId: string,
|
@Query('profileId') profileId: string,
|
||||||
@@ -41,6 +45,7 @@ export class DatesController {
|
|||||||
|
|
||||||
@Get('statuses')
|
@Get('statuses')
|
||||||
@ApiOperation({ summary: 'Get available date statuses' })
|
@ApiOperation({ summary: 'Get available date statuses' })
|
||||||
|
@ApiResponse({ status: 200, type: [DateStatusDto] })
|
||||||
getStatuses() {
|
getStatuses() {
|
||||||
return this.datesService.getStatuses();
|
return this.datesService.getStatuses();
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/modules/dates/dto/dates-response.dto.ts
Normal file
21
src/modules/dates/dto/dates-response.dto.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class DateStatusDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DateDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() profile1Id: string;
|
||||||
|
@ApiProperty() profile2Id: string;
|
||||||
|
@ApiProperty() lat: string;
|
||||||
|
@ApiProperty() lng: string;
|
||||||
|
@ApiProperty() time: string;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) statusId: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DateWithStatusDto {
|
||||||
|
@ApiProperty({ type: DateDto }) date: DateDto;
|
||||||
|
@ApiPropertyOptional({ type: DateStatusDto, nullable: true }) date_status: DateStatusDto | null;
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { FeedFilterDto } from './dto/feed-filter.dto';
|
import { FeedFilterDto } from './dto/feed-filter.dto';
|
||||||
import { FeedService } from './feed.service';
|
import { FeedService } from './feed.service';
|
||||||
|
import { ProfileResponseDto } from '../profiles/dto/profile-response.dto';
|
||||||
|
|
||||||
@ApiTags('feed')
|
@ApiTags('feed')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -14,6 +15,7 @@ export class FeedController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get filtered feed (requires profileId)' })
|
@ApiOperation({ summary: 'Get filtered feed (requires profileId)' })
|
||||||
|
@ApiResponse({ status: 200, type: [ProfileResponseDto] })
|
||||||
getFeed(@CurrentUser('id') userId: string, @Query() filter: FeedFilterDto) {
|
getFeed(@CurrentUser('id') userId: string, @Query() filter: FeedFilterDto) {
|
||||||
return this.feedService.getFeed(userId, filter);
|
return this.feedService.getFeed(userId, filter);
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/modules/greetings/dto/greeting-response.dto.ts
Normal file
6
src/modules/greetings/dto/greeting-response.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class GreetingDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() text: string;
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { Public } from '../../common/decorators/public.decorator';
|
import { Public } from '../../common/decorators/public.decorator';
|
||||||
import { Roles } from '../../common/decorators/roles.decorator';
|
import { Roles } from '../../common/decorators/roles.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from '../../common/guards/roles.guard';
|
import { RolesGuard } from '../../common/guards/roles.guard';
|
||||||
import { GreetingsService } from './greetings.service';
|
import { GreetingsService } from './greetings.service';
|
||||||
|
import { GreetingDto } from './dto/greeting-response.dto';
|
||||||
|
import { MessageResponseDto } from '../../common/dto/message-response.dto';
|
||||||
|
|
||||||
@ApiTags('greetings')
|
@ApiTags('greetings')
|
||||||
@Controller('greetings')
|
@Controller('greetings')
|
||||||
@@ -14,6 +16,7 @@ export class GreetingsController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get all greeting phrases' })
|
@ApiOperation({ summary: 'Get all greeting phrases' })
|
||||||
|
@ApiResponse({ status: 200, type: [GreetingDto] })
|
||||||
findAll() {
|
findAll() {
|
||||||
return this.greetingsService.findAll();
|
return this.greetingsService.findAll();
|
||||||
}
|
}
|
||||||
@@ -23,6 +26,7 @@ export class GreetingsController {
|
|||||||
@Roles('admin')
|
@Roles('admin')
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Add greeting phrase (admin only)' })
|
@ApiOperation({ summary: 'Add greeting phrase (admin only)' })
|
||||||
|
@ApiResponse({ status: 201, type: GreetingDto })
|
||||||
create(@Body('text') text: string) {
|
create(@Body('text') text: string) {
|
||||||
return this.greetingsService.create(text);
|
return this.greetingsService.create(text);
|
||||||
}
|
}
|
||||||
@@ -32,6 +36,7 @@ export class GreetingsController {
|
|||||||
@Roles('admin')
|
@Roles('admin')
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@ApiOperation({ summary: 'Delete greeting phrase (admin only)' })
|
@ApiOperation({ summary: 'Delete greeting phrase (admin only)' })
|
||||||
|
@ApiResponse({ status: 200, type: MessageResponseDto })
|
||||||
delete(@Param('id') id: string) {
|
delete(@Param('id') id: string) {
|
||||||
return this.greetingsService.delete(id);
|
return this.greetingsService.delete(id);
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/modules/likes/dto/likes-response.dto.ts
Normal file
21
src/modules/likes/dto/likes-response.dto.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class LikeDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() sourceProfileId: string;
|
||||||
|
@ApiProperty() targetProfileId: string;
|
||||||
|
@ApiProperty({ enum: ['like', 'dislike'] }) type: string;
|
||||||
|
@ApiProperty() createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MatchDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() profile1Id: string;
|
||||||
|
@ApiProperty() profile2Id: string;
|
||||||
|
@ApiProperty() createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateLikeResponseDto {
|
||||||
|
@ApiProperty({ type: LikeDto }) like: LikeDto;
|
||||||
|
@ApiPropertyOptional({ type: MatchDto, nullable: true }) match: MatchDto | null;
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Body, Controller, Get, Post, Query, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Get, Post, Query, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { CreateLikeDto } from './dto/create-like.dto';
|
import { CreateLikeDto } from './dto/create-like.dto';
|
||||||
|
import { CreateLikeResponseDto, MatchDto } from './dto/likes-response.dto';
|
||||||
import { LikesService } from './likes.service';
|
import { LikesService } from './likes.service';
|
||||||
|
|
||||||
@ApiTags('likes')
|
@ApiTags('likes')
|
||||||
@@ -14,12 +15,14 @@ export class LikesController {
|
|||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Like or dislike a profile' })
|
@ApiOperation({ summary: 'Like or dislike a profile' })
|
||||||
|
@ApiResponse({ status: 201, type: CreateLikeResponseDto })
|
||||||
createLike(@CurrentUser('id') userId: string, @Body() dto: CreateLikeDto) {
|
createLike(@CurrentUser('id') userId: string, @Body() dto: CreateLikeDto) {
|
||||||
return this.likesService.createLike(userId, dto);
|
return this.likesService.createLike(userId, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('matches')
|
@Get('matches')
|
||||||
@ApiOperation({ summary: 'Get matches for a profile' })
|
@ApiOperation({ summary: 'Get matches for a profile' })
|
||||||
|
@ApiResponse({ status: 200, type: [MatchDto] })
|
||||||
getMyMatches(
|
getMyMatches(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Query('profileId') profileId: string,
|
@Query('profileId') profileId: string,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Controller, Delete, Get, Param, Post, Query, Req, UseGuards } from '@nestjs/common';
|
import { Controller, Delete, Get, Param, Post, Query, Req, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiConsumes, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { FastifyRequest } from 'fastify';
|
import { FastifyRequest } from 'fastify';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { MediaService } from './media.service';
|
import { MediaService } from './media.service';
|
||||||
|
import { MediaItemDto } from '../profiles/dto/profile-response.dto';
|
||||||
|
import { MessageResponseDto } from '../../common/dto/message-response.dto';
|
||||||
|
|
||||||
@ApiTags('media')
|
@ApiTags('media')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -15,6 +17,7 @@ export class MediaController {
|
|||||||
@Post('upload')
|
@Post('upload')
|
||||||
@ApiOperation({ summary: 'Upload photo / video / audio to profile' })
|
@ApiOperation({ summary: 'Upload photo / video / audio to profile' })
|
||||||
@ApiConsumes('multipart/form-data')
|
@ApiConsumes('multipart/form-data')
|
||||||
|
@ApiResponse({ status: 201, type: MediaItemDto })
|
||||||
async upload(
|
async upload(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Param('profileId') profileId: string,
|
@Param('profileId') profileId: string,
|
||||||
@@ -34,12 +37,14 @@ export class MediaController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get all media for a profile' })
|
@ApiOperation({ summary: 'Get all media for a profile' })
|
||||||
|
@ApiResponse({ status: 200, type: [MediaItemDto] })
|
||||||
getMedia(@Param('profileId') profileId: string) {
|
getMedia(@Param('profileId') profileId: string) {
|
||||||
return this.mediaService.getByProfileId(profileId);
|
return this.mediaService.getByProfileId(profileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':mediaId')
|
@Delete(':mediaId')
|
||||||
@ApiOperation({ summary: 'Delete media item' })
|
@ApiOperation({ summary: 'Delete media item' })
|
||||||
|
@ApiResponse({ status: 200, type: MessageResponseDto })
|
||||||
deleteMedia(
|
deleteMedia(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Param('mediaId') mediaId: string,
|
@Param('mediaId') mediaId: string,
|
||||||
|
|||||||
46
src/modules/profiles/dto/profile-response.dto.ts
Normal file
46
src/modules/profiles/dto/profile-response.dto.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class CityDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() name: string;
|
||||||
|
@ApiProperty() lat: string;
|
||||||
|
@ApiProperty() lng: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DistrictDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() cityId: string;
|
||||||
|
@ApiProperty() name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TagDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MediaItemDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() profileId: string;
|
||||||
|
@ApiProperty() path: string;
|
||||||
|
@ApiProperty({ enum: ['photo', 'video', 'audio'] }) type: string;
|
||||||
|
@ApiProperty() sortOrder: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProfileResponseDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() userId: string;
|
||||||
|
@ApiProperty() name: string;
|
||||||
|
@ApiProperty({ example: '1995-06-15' }) birthDate: string;
|
||||||
|
@ApiProperty({ enum: ['male', 'female'] }) gender: string;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) cityId: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) districtId: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) description: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) nation: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) height: number | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) weight: number | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) activeChatId: string | null;
|
||||||
|
@ApiPropertyOptional({ type: CityDto, nullable: true }) city: CityDto | null;
|
||||||
|
@ApiPropertyOptional({ type: DistrictDto, nullable: true }) district: DistrictDto | null;
|
||||||
|
@ApiProperty({ type: [TagDto] }) tags: TagDto[];
|
||||||
|
@ApiProperty({ type: [MediaItemDto] }) media: MediaItemDto[];
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, Put, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Post, Put, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { CreateProfileDto } from './dto/create-profile.dto';
|
import { CreateProfileDto } from './dto/create-profile.dto';
|
||||||
import { UpdateProfileDto } from './dto/update-profile.dto';
|
import { UpdateProfileDto } from './dto/update-profile.dto';
|
||||||
|
import { ProfileResponseDto } from './dto/profile-response.dto';
|
||||||
import { ProfilesService } from './profiles.service';
|
import { ProfilesService } from './profiles.service';
|
||||||
|
import { MessageResponseDto } from '../../common/dto/message-response.dto';
|
||||||
|
|
||||||
@ApiTags('profiles')
|
@ApiTags('profiles')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -15,18 +17,21 @@ export class ProfilesController {
|
|||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Create a new profile (one user can have many)' })
|
@ApiOperation({ summary: 'Create a new profile (one user can have many)' })
|
||||||
|
@ApiResponse({ status: 201, type: ProfileResponseDto })
|
||||||
create(@CurrentUser('id') userId: string, @Body() dto: CreateProfileDto) {
|
create(@CurrentUser('id') userId: string, @Body() dto: CreateProfileDto) {
|
||||||
return this.profilesService.create(userId, dto);
|
return this.profilesService.create(userId, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('my')
|
@Get('my')
|
||||||
@ApiOperation({ summary: 'Get all my profiles' })
|
@ApiOperation({ summary: 'Get all my profiles' })
|
||||||
|
@ApiResponse({ status: 200, type: [ProfileResponseDto] })
|
||||||
getMyProfiles(@CurrentUser('id') userId: string) {
|
getMyProfiles(@CurrentUser('id') userId: string) {
|
||||||
return this.profilesService.findAllByUserId(userId);
|
return this.profilesService.findAllByUserId(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':profileId')
|
@Put(':profileId')
|
||||||
@ApiOperation({ summary: 'Update profile by ID (must be owner)' })
|
@ApiOperation({ summary: 'Update profile by ID (must be owner)' })
|
||||||
|
@ApiResponse({ status: 200, type: ProfileResponseDto })
|
||||||
update(
|
update(
|
||||||
@CurrentUser('id') userId: string,
|
@CurrentUser('id') userId: string,
|
||||||
@Param('profileId') profileId: string,
|
@Param('profileId') profileId: string,
|
||||||
@@ -37,12 +42,14 @@ export class ProfilesController {
|
|||||||
|
|
||||||
@Get(':profileId')
|
@Get(':profileId')
|
||||||
@ApiOperation({ summary: 'Get profile by ID' })
|
@ApiOperation({ summary: 'Get profile by ID' })
|
||||||
|
@ApiResponse({ status: 200, type: ProfileResponseDto })
|
||||||
findOne(@Param('profileId') profileId: string) {
|
findOne(@Param('profileId') profileId: string) {
|
||||||
return this.profilesService.findByProfileId(profileId);
|
return this.profilesService.findByProfileId(profileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':profileId')
|
@Delete(':profileId')
|
||||||
@ApiOperation({ summary: 'Delete profile (must be owner)' })
|
@ApiOperation({ summary: 'Delete profile (must be owner)' })
|
||||||
|
@ApiResponse({ status: 200, type: MessageResponseDto })
|
||||||
delete(@CurrentUser('id') userId: string, @Param('profileId') profileId: string) {
|
delete(@CurrentUser('id') userId: string, @Param('profileId') profileId: string) {
|
||||||
return this.profilesService.delete(userId, profileId);
|
return this.profilesService.delete(userId, profileId);
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/modules/reports/dto/report-response.dto.ts
Normal file
9
src/modules/reports/dto/report-response.dto.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class ReportDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() sourceProfileId: string;
|
||||||
|
@ApiProperty() entityId: string;
|
||||||
|
@ApiProperty({ enum: ['profile', 'message'] }) entityType: string;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) description: string | null;
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { Roles } from '../../common/decorators/roles.decorator';
|
import { Roles } from '../../common/decorators/roles.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from '../../common/guards/roles.guard';
|
import { RolesGuard } from '../../common/guards/roles.guard';
|
||||||
import { CreateReportDto } from './dto/create-report.dto';
|
import { CreateReportDto } from './dto/create-report.dto';
|
||||||
|
import { ReportDto } from './dto/report-response.dto';
|
||||||
import { ReportsService } from './reports.service';
|
import { ReportsService } from './reports.service';
|
||||||
|
|
||||||
@ApiTags('reports')
|
@ApiTags('reports')
|
||||||
@@ -16,6 +17,7 @@ export class ReportsController {
|
|||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Submit a report' })
|
@ApiOperation({ summary: 'Submit a report' })
|
||||||
|
@ApiResponse({ status: 201, type: ReportDto })
|
||||||
create(@CurrentUser('id') userId: string, @Body() dto: CreateReportDto) {
|
create(@CurrentUser('id') userId: string, @Body() dto: CreateReportDto) {
|
||||||
return this.reportsService.create(userId, dto);
|
return this.reportsService.create(userId, dto);
|
||||||
}
|
}
|
||||||
@@ -24,6 +26,7 @@ export class ReportsController {
|
|||||||
@Roles('admin', 'moderator')
|
@Roles('admin', 'moderator')
|
||||||
@UseGuards(RolesGuard)
|
@UseGuards(RolesGuard)
|
||||||
@ApiOperation({ summary: 'Get all reports (admin/moderator)' })
|
@ApiOperation({ summary: 'Get all reports (admin/moderator)' })
|
||||||
|
@ApiResponse({ status: 200, type: [ReportDto] })
|
||||||
getAll() {
|
getAll() {
|
||||||
return this.reportsService.getAll();
|
return this.reportsService.getAll();
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/modules/tags/dto/tag-response.dto.ts
Normal file
6
src/modules/tags/dto/tag-response.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class TagResponseDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() value: string;
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { Public } from '../../common/decorators/public.decorator';
|
import { Public } from '../../common/decorators/public.decorator';
|
||||||
import { Roles } from '../../common/decorators/roles.decorator';
|
import { Roles } from '../../common/decorators/roles.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from '../../common/guards/roles.guard';
|
import { RolesGuard } from '../../common/guards/roles.guard';
|
||||||
import { TagsService } from './tags.service';
|
import { TagsService } from './tags.service';
|
||||||
|
import { TagResponseDto } from './dto/tag-response.dto';
|
||||||
|
import { MessageResponseDto } from '../../common/dto/message-response.dto';
|
||||||
|
|
||||||
@ApiTags('tags')
|
@ApiTags('tags')
|
||||||
@Controller('tags')
|
@Controller('tags')
|
||||||
@@ -14,6 +16,7 @@ export class TagsController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get all tags' })
|
@ApiOperation({ summary: 'Get all tags' })
|
||||||
|
@ApiResponse({ status: 200, type: [TagResponseDto] })
|
||||||
findAll() {
|
findAll() {
|
||||||
return this.tagsService.findAll();
|
return this.tagsService.findAll();
|
||||||
}
|
}
|
||||||
@@ -23,6 +26,7 @@ export class TagsController {
|
|||||||
@Roles('admin')
|
@Roles('admin')
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Create tag (admin only)' })
|
@ApiOperation({ summary: 'Create tag (admin only)' })
|
||||||
|
@ApiResponse({ status: 201, type: TagResponseDto })
|
||||||
create(@Body('value') value: string) {
|
create(@Body('value') value: string) {
|
||||||
return this.tagsService.create(value);
|
return this.tagsService.create(value);
|
||||||
}
|
}
|
||||||
@@ -32,6 +36,7 @@ export class TagsController {
|
|||||||
@Roles('admin')
|
@Roles('admin')
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@ApiOperation({ summary: 'Delete tag (admin only)' })
|
@ApiOperation({ summary: 'Delete tag (admin only)' })
|
||||||
|
@ApiResponse({ status: 200, type: MessageResponseDto })
|
||||||
delete(@Param('id') id: string) {
|
delete(@Param('id') id: string) {
|
||||||
return this.tagsService.delete(id);
|
return this.tagsService.delete(id);
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/modules/users/dto/user-response.dto.ts
Normal file
27
src/modules/users/dto/user-response.dto.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class RoleDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProfileSummaryDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() name: string;
|
||||||
|
@ApiProperty({ enum: ['male', 'female'] }) gender: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserResponseDto {
|
||||||
|
@ApiProperty() id: string;
|
||||||
|
@ApiProperty() phone: string;
|
||||||
|
@ApiProperty({ enum: ['active', 'banned', 'pending'] }) status: string;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) roleId: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) tariffId: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) paymentId: string | null;
|
||||||
|
@ApiPropertyOptional({ nullable: true }) fcmToken: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MeResponseDto extends UserResponseDto {
|
||||||
|
@ApiPropertyOptional({ type: RoleDto, nullable: true }) role: RoleDto | null;
|
||||||
|
@ApiProperty({ type: [ProfileSummaryDto] }) profiles: ProfileSummaryDto[];
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Controller, Get, Param, Patch, UseGuards } from '@nestjs/common';
|
import { Controller, Get, Param, Patch, UseGuards } from '@nestjs/common';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||||
import { Roles } from '../../common/decorators/roles.decorator';
|
import { Roles } from '../../common/decorators/roles.decorator';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from '../../common/guards/roles.guard';
|
import { RolesGuard } from '../../common/guards/roles.guard';
|
||||||
import { UsersService } from './users.service';
|
import { UsersService } from './users.service';
|
||||||
|
import { MeResponseDto, UserResponseDto } from './dto/user-response.dto';
|
||||||
|
import { MessageResponseDto } from '../../common/dto/message-response.dto';
|
||||||
|
|
||||||
@ApiTags('users')
|
@ApiTags('users')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -15,12 +17,14 @@ export class UsersController {
|
|||||||
|
|
||||||
@Get('me')
|
@Get('me')
|
||||||
@ApiOperation({ summary: 'Get current user with profile list' })
|
@ApiOperation({ summary: 'Get current user with profile list' })
|
||||||
|
@ApiResponse({ status: 200, type: MeResponseDto })
|
||||||
getMe(@CurrentUser('id') userId: string) {
|
getMe(@CurrentUser('id') userId: string) {
|
||||||
return this.usersService.getMe(userId);
|
return this.usersService.getMe(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@ApiOperation({ summary: 'Get user by ID' })
|
@ApiOperation({ summary: 'Get user by ID' })
|
||||||
|
@ApiResponse({ status: 200, type: UserResponseDto })
|
||||||
findOne(@Param('id') id: string) {
|
findOne(@Param('id') id: string) {
|
||||||
return this.usersService.findById(id);
|
return this.usersService.findById(id);
|
||||||
}
|
}
|
||||||
@@ -29,6 +33,7 @@ export class UsersController {
|
|||||||
@Roles('admin', 'moderator')
|
@Roles('admin', 'moderator')
|
||||||
@UseGuards(RolesGuard)
|
@UseGuards(RolesGuard)
|
||||||
@ApiOperation({ summary: 'Ban user' })
|
@ApiOperation({ summary: 'Ban user' })
|
||||||
|
@ApiResponse({ status: 200, type: MessageResponseDto })
|
||||||
ban(@Param('id') id: string) {
|
ban(@Param('id') id: string) {
|
||||||
return this.usersService.banUser(id);
|
return this.usersService.banUser(id);
|
||||||
}
|
}
|
||||||
@@ -37,6 +42,7 @@ export class UsersController {
|
|||||||
@Roles('admin', 'moderator')
|
@Roles('admin', 'moderator')
|
||||||
@UseGuards(RolesGuard)
|
@UseGuards(RolesGuard)
|
||||||
@ApiOperation({ summary: 'Activate user' })
|
@ApiOperation({ summary: 'Activate user' })
|
||||||
|
@ApiResponse({ status: 200, type: MessageResponseDto })
|
||||||
activate(@Param('id') id: string) {
|
activate(@Param('id') id: string) {
|
||||||
return this.usersService.activateUser(id);
|
return this.usersService.activateUser(id);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user