Курсовой проект представляет собой службу доставки. Ваша задача — создать работающее бэкенд-приложение, всеми основными функциями которого можно пользоваться.
Цель проекта — разработка работающего бэкенд-приложения с 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.