✨ 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 для создания и получения встреч
15 KiB
Стартовый промт — 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; десктоп — расширение, не отдельная версия |
Что такое это приложение
Дейтинг-приложение. Пользователь:
- Регистрируется по номеру телефона + пароль
- Создаёт один или несколько публичных профилей (у одного аккаунта может быть много профилей)
- Листает ленту других профилей с фильтрацией
- Ставит лайки / дизлайки на профили
- При взаимном лайке создаётся матч
- Открывает чат с одним матчем за раз (один активный чат на профиль)
- В чате обменивается текстом, фото, голосовыми и видео-сообщениями
- Договаривается о реальных встречах (дата, координаты, статус)
- Может подать жалобу на профиль или сообщение
Архитектура бэкенда (важно для интеграции)
Базовый URL
http://localhost:3000/api/v1
Формат ответа
Все успешные ответы обёрнуты:
{ "data": <payload> }
Ошибки:
{ "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=50POST /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 /tagsGET /citiesGET /cities/:cityId/districtsGET /greetings— готовые приветственные фразы
WebSocket (real-time чат)
Библиотека: socket.io-client
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 → может подтвердить / отменить / перенести.
Хранение состояния авторизации
// stores/auth.store.ts (Pinia)
// accessToken + refreshToken хранить в localStorage
// При 401 — автоматически вызывать POST /auth/refresh
// При неудаче refresh — очистить стор, редирект на /login
Интерцептор axios:
// При 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. Сгенерируй типизированный клиент:
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 не нужно)
Старт проекта
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).