├── .gitignore ├── src ├── routes │ ├── messageRouter.js │ ├── index.js │ └── userRouter.js ├── middleware │ └── ErrorHandlerMiddleware.js ├── error │ └── ApiError.js ├── db.js ├── models │ └── models.js ├── index.js └── controllers │ ├── messageController.js │ └── userController.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env -------------------------------------------------------------------------------- /src/routes/messageRouter.js: -------------------------------------------------------------------------------- 1 | const Router = require('express'); 2 | const MessageController = require('../controllers/messageController'); 3 | 4 | const router = new Router(); 5 | 6 | router.post('/get', MessageController.getAll); 7 | router.post('/send', MessageController.add); 8 | 9 | module.exports = router; -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('express'); 2 | const userRouter = require('./userRouter'); 3 | const messageRouter = require('./messageRouter'); 4 | 5 | const router = new Router(); 6 | 7 | router.use('/user', userRouter); 8 | router.use('/message', messageRouter); 9 | 10 | module.exports = router; -------------------------------------------------------------------------------- /src/middleware/ErrorHandlerMiddleware.js: -------------------------------------------------------------------------------- 1 | const ApiError = require('../error/ApiError'); 2 | 3 | module.exports = function (err, req, res, next) { 4 | if(err instanceof ApiError) { 5 | return res.status(err.status).json({message: err.message}); 6 | } 7 | return res.status(500).json({message: 'Unexpected error'}); 8 | } -------------------------------------------------------------------------------- /src/error/ApiError.js: -------------------------------------------------------------------------------- 1 | class ApiError extends Error { 2 | constructor(status, message) { 3 | super(); 4 | this.status = status; 5 | this.message = message; 6 | } 7 | 8 | static badRequest(message) { 9 | return new ApiError(404, message); 10 | } 11 | } 12 | 13 | module.exports = ApiError; -------------------------------------------------------------------------------- /src/routes/userRouter.js: -------------------------------------------------------------------------------- 1 | const Router = require('express'); 2 | const UserController = require('../controllers/userController'); 3 | 4 | const router = new Router(); 5 | 6 | router.get('/', UserController.getAll); 7 | router.post('/register', UserController.register); 8 | router.post('/login', UserController.login); 9 | router.post('/online', UserController.setIsOnline); 10 | 11 | module.exports = router; -------------------------------------------------------------------------------- /src/db.js: -------------------------------------------------------------------------------- 1 | const {Sequelize} = require('sequelize'); 2 | 3 | module.exports = new Sequelize( 4 | process.env.DB_NAME, 5 | process.env.DB_USER, 6 | process.env.DB_PASSWORD, 7 | { 8 | dialect: 'postgres', 9 | host: process.env.DB_HOST, 10 | port: process.env.DB_PORT, 11 | timezone: 'utc', 12 | dialectOptions: { 13 | ssl: { 14 | require: true, 15 | rejectUnauthorized: false, 16 | } 17 | }, 18 | logging: false, 19 | } 20 | ); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telemost-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node ./src/index.js", 8 | "dev": "nodemon ./src/index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcrypt": "^5.0.1", 15 | "cors": "^2.8.5", 16 | "crypto-js": "^4.1.1", 17 | "dotenv": "^16.0.2", 18 | "express": "^4.18.1", 19 | "pg": "^8.8.0", 20 | "sequelize": "^6.21.4" 21 | }, 22 | "devDependencies": { 23 | "nodemon": "^2.0.19" 24 | }, 25 | "engines": { 26 | "node": "16.x" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/models/models.js: -------------------------------------------------------------------------------- 1 | const sequelize = require('../db'); 2 | const { DataTypes } = require('sequelize'); 3 | 4 | const User = sequelize.define('user', { 5 | id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true}, 6 | login: {type: DataTypes.STRING, unique: true, allowNull: false}, 7 | password: {type: DataTypes.STRING, allowNull: false}, 8 | name: {type: DataTypes.STRING, allowNull: false}, 9 | isOnline: {type: DataTypes.BOOLEAN, defaultValue: false} 10 | }); 11 | 12 | const Message = sequelize.define('message', { 13 | id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true}, 14 | text: {type: DataTypes.STRING, allowNull: false} 15 | }); 16 | 17 | User.hasMany(Message, {onDelete: 'cascade'}); 18 | Message.belongsTo(User); 19 | 20 | module.exports = { 21 | User, 22 | Message 23 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const cors = require('cors'); 4 | const router = require('./routes/index'); 5 | const errorHandler = require('./middleware/ErrorHandlerMiddleware'); 6 | const sequelize = require('./db'); 7 | const models = require('./models/models'); 8 | 9 | const app = express(); 10 | 11 | app.use(cors()); 12 | app.use(express.json()); 13 | app.use('/api', router); 14 | app.use(errorHandler); 15 | 16 | const PORT = process.env.PORT || 3030; 17 | 18 | const start = async () => { 19 | try { 20 | await sequelize.authenticate(); 21 | await sequelize.sync(); 22 | app.listen(PORT, () => { 23 | console.log('Server started on port: ' + PORT); 24 | }) 25 | } catch(e) { 26 | console.log(e.message); 27 | } 28 | } 29 | 30 | start(); -------------------------------------------------------------------------------- /src/controllers/messageController.js: -------------------------------------------------------------------------------- 1 | const ApiError = require("../error/ApiError"); 2 | const { Message, User } = require("../models/models"); 3 | const CryptoJS = require("crypto-js"); 4 | const bcrypt = require('bcrypt'); 5 | 6 | class MessageController { 7 | async getAll(req, res, next) { 8 | const {login, password} = req.body; 9 | 10 | if(!login || !password) { 11 | return next(ApiError.badRequest('Not all params passed.')); 12 | } 13 | 14 | const user = await User.findOne({where: {login}}); 15 | 16 | if(!user) { 17 | return next(ApiError.badRequest('User not found')); 18 | } else { 19 | if(password !== user.password) { 20 | return next(ApiError.badRequest('Password is incorrect')); 21 | } 22 | 23 | try { 24 | let messages = await Message.findAll({include: [{model: User, attributes: ['name', 'login', 'isOnline']}], raw: true}); 25 | 26 | messages = messages.map((message) => { 27 | message.text = CryptoJS.AES.decrypt(message.text, process.env.SECRET_KEY).toString(CryptoJS.enc.Utf8); 28 | if(message.userId === user.id) { 29 | message.isUserMessage = true; 30 | } else { 31 | message.isUserMessage = false; 32 | } 33 | return message; 34 | }); 35 | 36 | return res.json(messages); 37 | } catch(e) { 38 | next(ApiError.badRequest(e.message)); 39 | } 40 | } 41 | } 42 | 43 | async add(req, res, next) { 44 | const {user, text} = req.body; 45 | 46 | if(!user || !user.login || !text) { 47 | return next(ApiError.badRequest('Not all params passed.')); 48 | } 49 | 50 | try { 51 | const DBuser = await User.findOne({where: {login: user.login.toLowerCase()}}); 52 | 53 | if(DBuser) { 54 | const encryptedText = CryptoJS.AES.encrypt(text, process.env.SECRET_KEY).toString(); 55 | const message = await Message.create({text: encryptedText, userId: DBuser.id}); 56 | return res.json(message); 57 | } 58 | return next(ApiError.badRequest('User not found.')); 59 | } catch (e) { 60 | next(ApiError.badRequest(e.message)); 61 | } 62 | } 63 | } 64 | 65 | module.exports = new MessageController(); -------------------------------------------------------------------------------- /src/controllers/userController.js: -------------------------------------------------------------------------------- 1 | const ApiError = require('../error/ApiError'); 2 | const { User } = require('../models/models'); 3 | const bcrypt = require('bcrypt'); 4 | 5 | class UserController { 6 | async getAll(req, res, next) { 7 | try { 8 | const users = await User.findAll({order: [ 9 | ['isOnline', 'DESC'], 10 | ['name', 'ASC'] 11 | ]}); 12 | return res.json(users); 13 | } catch(e) { 14 | next(ApiError.badRequest(e.message)); 15 | } 16 | } 17 | 18 | async register(req, res, next) { 19 | const {login, password, name} = req.body; 20 | 21 | if(!login || !password || !name) { 22 | return next(ApiError.badRequest('Not all params passed.')); 23 | } 24 | 25 | try { 26 | const hashPassword = await bcrypt.hash(password, 5); 27 | 28 | const user = await User.create({login: login.toLowerCase(), password: hashPassword, name, isOnline: true}); 29 | return res.json(user); 30 | } catch(e) { 31 | if(e.message === 'Validation error') { 32 | return next(ApiError.badRequest('Login already taken')); 33 | } 34 | next(ApiError.badRequest(e.message)); 35 | } 36 | } 37 | 38 | async login(req, res, next) { 39 | const {login, password} = req.body; 40 | 41 | if(!login || !password) { 42 | return next(ApiError.badRequest('Not all params passed.')); 43 | } 44 | 45 | try { 46 | const user = await User.findOne({where: {login}}); 47 | 48 | if(user) { 49 | const isPasswordCorrect = await bcrypt.compare(password, user.password); 50 | 51 | if(isPasswordCorrect) { 52 | return res.json({ok: true, name: user.name, login: user.login, password: user.password}); 53 | } 54 | return next(ApiError.badRequest('Password is incorrect')); 55 | } 56 | return next(ApiError.badRequest('User not found')); 57 | } catch (e) { 58 | next(ApiError.badRequest(e.message)); 59 | } 60 | } 61 | 62 | async setIsOnline(req, res, next) { 63 | const {login, isOnline} = req.body; 64 | 65 | if(isOnline == undefined || isOnline === null || !login) { 66 | return next(ApiError.badRequest('Not all params passed.')); 67 | } 68 | 69 | try { 70 | const user = await User.findOne({where: {login}}); 71 | if(user) { 72 | await user.update({isOnline}); 73 | return res.json({ok: true}); 74 | } 75 | return next(ApiError.badRequest('User not found.')); 76 | } catch(e) { 77 | next(ApiError.badRequest(e.message)); 78 | } 79 | } 80 | } 81 | 82 | module.exports = new UserController(); --------------------------------------------------------------------------------