Диплом, часть 3. MongoDB, API, Cloudinary
Формат, когда я просто рассказываю, что я сделал меня не совсем устраивает, ибо в будущем я хочу проанализировать ход разработки. Поэтому воспользуюсь форматом, похожим на то, как герой фильма «Социальная сеть» описывал разработку Facemash: короткие записи о каждом небольшом шаге.
—
Прикрутить базу оказалось несложно, работать с ней тоже проще простого. Начнём с написания авторизации пользователей.
В этот раз я решил юзать `passport.js` и ещё некоторые обвесы. Писать буду на основе своих существующих знаний и серии видео по написанию системы логина с использованием выше названной библиотеки.
В общем и целом объясняют не совсем так, как мне хотелось бы и как объяснял бы я именно взаимодействие NodeJS, работу Express. Но непосредственно по `passport.js` меня всё устраивает.
—
На самом деле многие моменты этих видео-уроков оставляют желать лучшего. Единтсвенное, что оказалось полезным... да ничего, наверно, просто нужно было понять в каком месте в общей системе надо подцеплять библиотеку и что как и где использовать.
—-
Долго сидел и искал непонятную ошибку, которая всё мне роняла, причиной оказалась моя невнимательность и особенность Express, в 1 и 2 строке сниппета res не одно и то же, т. к. в зависимости от числа параметров в функцию пробрасываются отличающиеся параметры:
export default (err, req, res, next) => ...
export default (err, req, res) => ...
Помимо всего `passport.js` как-то не особо укладывается в архитектуру приложения, которую я сначала хотел реализовать. Пока сделаю чисто так, чтобы просто работало, а затем уже буду рефакторить.
—
У этой библиотеки ну очень странная документация и очень странное поведение. Довольно долго разбирался со всем этим. Пока готова простейшая регистрация и логин. Нужно прикрутить токены, и, в принципе, серверная часть с авторизацией будет готова.
—
После деплоя все вызовы API работают, это гуд. Прикручиваю токены.
—
Всё оказалось гораздо проще, и Passport мне вообще не нужен будет, ибо есть JWT. День в пустую.
—
Окей, с API логина и регистрации разобрались. Теперь накидаем простые варианты API и чуть расширим схемы моделей.
Конечно, я шифрую пароли bcrypt’ом. Сейчас объект юзера в БД хранится в примерно таком виде:
{
"_id": {
"$oid": "5a5cbe2425ac100014571bd4"
},
"login": "root",
"password": "$2a$10$YWEDx35UrhtLOg1x2FPP5ezxPP7XSbFfuYsvpdWblGGbo9nqLLzZe",
"email": "[email protected]",
"__v": 0
}
—
Сделал API для юзеров. Вроде бы всё ок, нужно только продумать, как быть с аватарами и всякими такими файлами.
—
Серверная часть готова абсолютно, если исключить всякие особенности и, конечно, загрузку файлов.
—
Несколько часов промучался с загрузкой файлов, а все проблемы были в правах и неверных заголовках. Теперь вроде бы окей работает загрузка отдельных файлов в виде `form-data`. За это спасибо `Multer.js`. Теперь ещё впилю поддержку облака `cloudinary`.
—
Собирался накостылять своё решение, но вовремя нашёл `multer-storage-cloudinary.js`. Закинул в деплой, теперь осталось чуть-чуть протестировать.
Окончательная версия выглядит примерно так:
Routes.js
import express from 'express';
import checkToken from '../middlewares/checkToken';
import * as FileController from '../controllers/file';
const router = express.Router();
router.post('/avatar', checkToken, FileController.uploadFile('avatar'), FileController.uploadController);
router.post('/event', checkToken, FileController.uploadFile('event'), FileController.uploadController);
export default router;
Controllers.js
import path from 'path';
import multer from 'multer';
import cloudinary from 'cloudinary';
import cloudinaryStorage from 'multer-storage-cloudinary';
import config from '../config';
cloudinary.config(config.cloudinary);
const storage = fieldName => (config.isProduction ?
cloudinaryStorage({
cloudinary,
folder: `${config.uploadDirectory}/${fieldName}`,
allowedFormats: ['jpg', 'jpeg', 'png', 'gif'],
filename: (req, file, callback) => {
callback(null, req.user._id);
},
}) :
multer.diskStorage({
destination: config.uploadDirectory,
filename: (req, file, callback) => {
callback(null, req.user._id + path.extname(file.originalname));
},
})
);
export const uploadFile = fieldName => multer({
storage: storage(fieldName),
limits: { fileSize: config.maxFileSize },
}).single(fieldName);
export const uploadController = async (req, res, next) => {
if (req.file) {
if (config.isProduction) {
return res.json({
fileUrl: req.file.url,
});
}
const filePath = req.file.destination.slice(req.file.destination.indexOf('/') + 1);
return res.json({
fileUrl: `/${filePath}${req.file.filename}`,
});
}
return next({
status: 500,
message: 'Upload error!',
});
};
Проверить работу можно следующим образом (отправлять запросы можно, например, Postman’ом):
- Регистрируемся, послав post-запрос на http://diploma.ifedyukin.ru/api/user/register, формат `x-www-form-urlencoded`, поля: login, password, email (что в них отправлять, думаю, не стоит комментировать); последующий вход осуществляется отправкой такого же запроса, только на http://diploma.ifedyukin.ru/api/user/login и без поля «email» — в ответ придёт токен авторизации;
- В ответе получаем объект, в котором нас интересует поле `token`, содержимое этого поля вносим в заголовок `Authorization`;
- Посылаем post-запрос на http://diploma.ifedyukin.ru/api/file/avatar, формата `form-data`, с полем `avatar`, в котором будет лежать картинка.
—
Прикрутил аналитику от «Keen.io» и логгирование от «Logentries». Теперь могу смотреть статистику всяких событий, отслеживать «атаки» (сегодня в 4 утра кто-то пытался подбирать пароли к учетным записям, которых нет) и много-много всего интересного, а так же анализировать ошибки, внезапно возникающие при работе.
—
В планах прикрутить авторизацию на основе блокчейна (воспользуюсь готовым сервисом, но главное — блокчейн антихайп), server-side рендеринг страничек полностью реализовать и сделать нормальную систему service-worker’ов, чтобы клиентская часть работала быстро в нескольких потоках (вероятно) и вообще: всё было здорово.
Но к этому я перейду после того, как сделаю базовую часть клиентской части, ибо, по сравнению с сервером, времени на это уйдёт ну очень много.
—
Впилил защиту от бесконечных регистраций и брутфорса паролей, ну и, конечно, закинул сбор статистики кого и с каким IP заблокировала «защита».