Курсовой проект представляет собой службу доставки. Ваша задача — создать работающее бэкенд-приложение, всеми основными функциями которого можно пользоваться.
Цель проекта — разработка работающего бэкенд-приложения с API, которое описано в этом документе.
Реализовывать клиент не нужно.
- Приложение должно содержать следующие базовые модули:
- 1.1. Пользователи.
- 1.2. Объявления.
- 1.3. Чат.
- Приложение должно содержать следующие функциональные модули:
- 2.1. Регистрация.
- 2.2. Аутентификация.
- 2.3. Просмотр объявлений.
- 2.4. Управление объявлениями.
- 2.5. Общение.
Базовые модули служат для описания бизнес-логики и хранения данных.
Модуль «Пользователи» предназначен для создания, хранения и поиска профилей пользователей.
Модуль «Пользователи» используется функциональными модулями для регистрации и аутентификации.
Данные пользователя должны храниться в MongoDB.
Модель данных User
пользователя должна содержать следующие поля:
Название | Тип | Обязательное | Уникальное |
---|---|---|---|
_id | ObjectId |
да | да |
string |
да | да | |
passwordHash | string |
да | нет |
name | string |
да | нет |
contactPhone | string |
нет | нет |
const user = await UserModule.create(data);
Аргумент data
должен соответствовать полям модели User
, кроме _id.
Результатом работы функции должен быть Promise
, который резолвится с объектом модели User
.
const user = await UserModule.findByEmail(email);
Аргумент email
должен быть строкой.
Результатом работы функции должен быть Promise
, который резолвится с объектом модели User
или null
, если пользователь
не существует.
Модуль «Объявления» предназначается для хранения и поиска объявлений.
Модуль «Объявления» используется функциональными модулями для показа списка объявлений, размещения и удаления объявлений.
Данные объявлений должны храниться в MongoDB.
Модель данных Advertisement
должна содержать следующие поля:
Название | Тип | Обязательное | Уникальное |
---|---|---|---|
_id | ObjectId |
да | да |
shortText | string |
да | нет |
description | string |
нет | нет |
images | string[] |
нет | нет |
userId | ObjectId |
да | нет |
createdAt | Date |
да | нет |
updatedAt | Date |
да | нет |
tags | string[] |
нет | нет |
isDeleted | boolean |
да | нет |
const advertisements = await Advertisement.find(params);
В объекте params
должны учитываться следующие поля:
shortText
— поиск регулярным выражением;description
— поиск регулярным выражением;userId
— точное совпадение;tags
— значение в базе данных должно включать все искомые значения.
Поиск должен игнорировать записи, которые помечены удалёнными isDeleted = true
.
Результатом работы функции должен быть Promise
, который резолвится с массивом объектов модели Advertisement
или пустым
массивом.
const advertisement = await Advertisement.create(data);
Аргумент data
должен соответствовать полям модели Advertisement
, кроме _id.
Результатом работы функции должен быть Promise
, который резолвится с объектом модели Advertisement
.
const advertisement = await Advertisement.remove(id);
Аргумент id
должен быть типа string
или ObjectId
.
Функция поиска не должна удалять запись из базы данных, а только выставлять значение флага isDeleted = true
.
Модуль «Чат» предназначается для хранения чатов и сообщений в чате.
Модуль «Чат» используется функциональными модулями для реализации возможности общения пользователей.
Данные чатов должны храниться в MongoDB.
Модель данных чата Chat
должна содержать следующие поля:
Название | Тип | Обязательное | Уникальное |
---|---|---|---|
_id | ObjectId |
да | да |
users | [ObjectId , ObjectId ] |
да | нет |
createdAt | Date |
да | нет |
messages | Message[] |
нет | нет |
Модель сообщения Message
должна содержать следующие поля:
Название | Тип | Обязательное | Уникальное |
---|---|---|---|
_id | ObjectId |
да | да |
author | ObjectId |
да | нет |
sentAt | Date |
да | нет |
text | string |
да | нет |
readAt | Date |
нет | нет |
Сообщение считается прочитанным, когда поле readAt
не пустое.
const chat = await Chat.find(users);
Аргумент функции [ObjectId, ObjectId]
— ID пользователей.
Результатом работы функции должен быть Promise
, который является объектом модели Chat
или null
.
const message = await Chat.sendMessage(data);
Параметры:
Название | Тип | Обязательное |
---|---|---|
author | ObjectId |
да |
receiver | ObjectId |
да |
text | string | да |
При отправке сообщения нужно:
- Найти чат между
author
иreceiver
по полюChat.users
. Если чата нет, то создать его. - Добавить в поле
Chat.messages
новое сообщениеMessage
. ПолеsentAt
должно соответствовать текущей дате.
Результатом работы функции должен быть Promise
, который резолвится с объектом модели Message
.
Chat.subscribe((data) => {});
Функция Chat.subscribe
должна принимать функцию обратного вызова.
Каждый раз при добавлении сообщения функция обратного вызова должна вызываться со следующими параметрами:
Название | Тип | Обязательное |
---|---|---|
chatId | ObjectId |
да |
message | Message |
да |
Оповещения должны быть реализованы через механизм EventEmitter
.
const messages = await Chat.getHistory(id);
Аргумент функции ObjectId
— _id
чата.
Результатом работы функции должен быть Promise
, который резолвится с массивом объектов модели Message
.
Функциональные модули предназначены для реализации функций, доступных конечным пользователям.
POST /api/signup
— зарегистрироваться.
Пароль не должен храниться в чистом виде. Его нужно хешировать перед отправкой в модуль «Пользователи».
Формат данных при отправке — JSON-объект. Пример запроса:
{
"email": "kulagin@netology.ru",
"password": "ad service",
"name": "Alex Kulagin",
"contactPhone": "+7 123 456 78 90"
}
В ответ приходит либо сообщение об ошибке, либо JSON-объект с данными:
{
"data": {
"id": "507f1f77bcf86cd799439011",
"email": "kulagin@netology.ru",
"name": "Alex Kulagin",
"contactPhone": "+7 123 456 78 90"
},
"status": "ok"
}
{
"error": "email занят",
"status": "error"
}
POST /api/signin
— залогиниться.
Для реализации аутентификации пользователя должен использоваться механизм сессий и модуль Passport.js
.
Если пользователь не существует или пароль не совпадает, то нужно выдавать ошибку Неверный логин или пароль
.
Так как пароль не должен храниться в чистом виде, его нужно хешировать и сравнивать с хешем пароля пользователя из модуля «Пользователи».
Формат данных при отправке — JSON-объект. Пример запроса:
{
"email": "kulagin@netology.ru",
"password": "ad service"
}
В ответ приходит либо сообщение об ошибке, либо JSON-объект с данными:
{
"data": {
"id": "507f1f77bcf86cd799439011",
"email": "kulagin@netology.ru",
"name": "Alex Kulagin",
"contantPhone": "+7 123 456 78 90"
},
"status": "ok"
}
{
"error": "Неверный логин или пароль",
"status": "error"
}
GET /api/advertisements
— получить список объявлений.
Эти данные публичные, поэтому аутентификация не требуется.
В ответ приходит либо сообщение об ошибке, либо JSON-объект с данными:
{
"data": [
{
"id": "507f1f77bcf86cd799439012",
"shortTitle": "Продам слона",
"description": "kulagin@netology.ru",
"images": [
"/uploads/507f1f77bcf86cd799439011/slon_v_profil.jpg",
"/uploads/507f1f77bcf86cd799439011/slon_v_fas.jpg",
"/uploads/507f1f77bcf86cd799439011/slon_hobot.jpg"
],
"user": {
"id": "507f1f77bcf86cd799439011",
"name": "Alex Kulagin"
},
"createdAt": "2020-12-12T10:00:00.000Z"
}
],
"status": "ok"
}
GET /api/advertisements/:id
— получить данные объявления.
Эти данные публичные, поэтому аутентификация не требуется.
В ответ приходит либо сообщение об ошибке, либо JSON-объект с данными:
{
"data": {
"id": "507f1f77bcf86cd799439012",
"shortTitle": "Продам слона",
"description": "kulagin@netology.ru",
"images": [
"/uploads/507f1f77bcf86cd799439011/slon_v_profil.jpg",
"/uploads/507f1f77bcf86cd799439011/slon_v_fas.jpg",
"/uploads/507f1f77bcf86cd799439011/slon_hobot.jpg"
],
"user": {
"id": "507f1f77bcf86cd799439011",
"name": "Alex Kulagin"
},
"createdAt": "2020-12-12T10:00:00.000Z"
},
"status": "ok"
}
POST /api/advertisements
— создать объявление.
Эти данные приватные и требуют проверки аутентификации.
Формат данных при отправке — FormData
. Пример запроса:
Поле | Тип |
---|---|
shortTitle | string |
description | string |
images | File[] |
Обработка загруженных файлов должна производиться с помощью библиотеки multer.
В ответ приходит либо сообщение об ошибке, либо JSON-объект с данными:
{
"data": [
{
"id": "507f1f77bcf86cd799439012",
"shortTitle": "Продам слона",
"description": "kulagin@netology.ru",
"images": [
"/uploads/507f1f77bcf86cd799439011/slon_v_profil.jpg",
"/uploads/507f1f77bcf86cd799439011/slon_v_fas.jpg",
"/uploads/507f1f77bcf86cd799439011/slon_hobot.jpg"
],
"user": {
"id": "507f1f77bcf86cd799439011",
"name": "Alex Kulagin"
},
"createdAt": "2020-12-12T10:00:00.000Z"
}
],
"status": "ok"
}
Если пользователь не аутентифицирован и пытается создать объявление, то в ответ должен получить JSON-объект с ошибкой и HTTP-код 401.
DELETE /api/advertisements/:id
— удалить объявление.
Эти данные приватные и требуют проверки аутентификации.
Если пользователь не аутентифицирован и пытается создать объявление, то в ответ должен получить JSON-объект с ошибкой и HTTP-код 401.
Если пользователь аутентифицирован, но не является автором объявления, то в ответ должен получить JSON-объект с ошибкой и HTTP-код 403.
Модуль «Общение» предназначен для онлайн-общения пользователей.
Модуль должен использовать библиотеку Socket.IO.
Для подписки на обновления в чатах модуль должен использовать функционал «Подписка» модуля «Чат».
Сообщения, приходящие в socket
:
getHistory
— получить историю сообщений из чата;sendMessage
— отправить сообщение пользователю.
События, отправляемые через socket
:
newMessage
— отправлено новое сообщение;chatHistory
— ответ на событиеgetHistory
.
Событие getHistory
Событие getHistory
должно принимать в данных ID собеседника.
По id
собеседника и id
текущего пользователя нужно найти чат через функцию «Получить чат между пользователями». Далее для этого чата нужно получить историю сообщений и отправить её в ответ c событием chatHistory
.
Событие sendMessage
Событие sendMessage
должно получить следующие данные:
Поле | Тип |
---|---|
receiver | string |
text | string |
Полученные данные должны передаваться в функцию «Отправить сообщение» модуля «Чат». Для поля author
должен использоваться ID текущего пользователя.
Событие newMessage
Событие newMessage
должно вызываться каждый раз, когда в чат отправляется сообщение.
При подключении нового клиента должна создаваться подписка на новые сообщения в чате (модуль «Чат»). Полученное сообщение передаётся целиком клиенту.
Для запуска приложения в корне проекта должны находиться следующие файлы:
package.json
иpackage-lock.json
с описанными зависимостями,Dockerfile
для сборки образа приложения,docker-compose.yaml
с сервисом приложения и сервисом MongoDB,README.md
с описанием проекта и вариантами его запуска.
Настройка параметров приложения должна производиться через переменные окружения. Это требование как для запуска в окружении хоста, так и при работе с Docker.
Список переменных окружения должен быть описан в файле .env-example
. Этот файл не должен содержать значений. Пример файла:
HTTP_HOST=
HTTP_PORT=
MONGO_URL=
Для запуска приложения должен использоваться скрипт npm start
, описанный в package.json
.