mongoose + tests

This commit is contained in:
Primakov Alexandr Alexandrovich
2024-10-16 11:06:23 +03:00
parent 2cfcd7347b
commit 4b0d9b4dbc
1295 changed files with 4579 additions and 1719 deletions

View File

@@ -0,0 +1,22 @@
const hash = require('pbkdf2-password')()
const users = [{
salt: '1iKffJhuhGpQGx7JjiGc+ShnorhVcJDtEqTTBQjX7tnuMxfdN3nMpYpcxY0MZSPSCCGP+9l4swhit4E/ZGgVZA==',
hash: 'tQ0V8293708V2/sjyVG6yMSAlizKmTezTpoLxBc0hpfxEOImRNRGQbEx3HuGrowE4HYjzdshJbu52E5rsDE0Hfrmy1unoL/8tjeWGRF04d2sF1lhUrpxt3v1pf3du0rhR2PLTL5d2BWEtT3pSStiWeetw/zdZRDNMo9PVQqnjX0=',
login: 'task-boss',
id: '1',
}]
function authenticate(login, pass, fn) {
let user = users.find((u) => u.login === login)
if (!user) return fn(new Error('невозможно найти пользователя'))
hash({
password: pass, salt: user.salt,
}, (err, pass, salt, hash) => {
if (err) return fn(err)
if (hash === user.hash) return fn(null, user)
fn(new Error('неверный пароль'))
})
}
module.exports = authenticate

View File

@@ -0,0 +1,5 @@
const JWT_SECRET_KEY = 'hefiowaehfipwehafcpoweHPOCHopcvhophvnpx dsl;sad sl;dn lsdn lsd'
module.exports = {
JWT_SECRET_KEY,
}

View File

@@ -0,0 +1,288 @@
/* eslint-disable no-restricted-syntax */
const hash = require('pbkdf2-password')()
const { getDB } = require('../../../utils/mongo')
// eslint-disable-next-line import/order
const ObjectId = require('mongodb').ObjectID
let db = null
const connect = async () => {
db = await getDB('task-boss')
}
const _idToId = (data) => {
const { _id, ...rest } = data
return {
id: _id,
...rest,
}
}
const DEPTS_COLLECTION = 'depts4'
const PEOPLES_COLLECTION = 'peoples4'
const USERS_COLL = 'users3'
const getDepts = async () => {
if (db === null) throw new Error('no db connection')
const deptsCollection = db.collection(DEPTS_COLLECTION)
let responseData = null
try {
responseData = await deptsCollection.aggregate([{
$lookup: {
from: 'peoples',
let: {
id_fio_resp: '$idFio',
},
pipeline: [
{
$match:
{
$expr:
{
$eq: ['$id', '$$id_fio_resp'],
},
},
},
{
$project: {
id: '$_id',
_id: 0,
family: 1,
name: 1,
secondName: 1,
email: 1,
phone: 1,
},
}],
as: 'responsible',
},
},
{
$unwind: '$responsible',
},
{
$project: {
id: '$_id',
_id: 0,
parentId: '$parentDept',
name: 1,
qty: {
$size: '$employees',
},
responsible: '$responsible',
notes: '$note',
tasks: {
inWork: '0',
newTasks: '0',
},
},
},
]).toArray()
} catch (e) {
console.log(e.message)
}
if (!responseData || responseData?.length === 0) {
responseData = require('./stubs/depts.json').body
}
return responseData
}
const getDeptData = async (deptId) => {
if (db === null) throw new Error('no db connection')
const deptsCollection = db.collection(DEPTS_COLLECTION)
const data = await deptsCollection.aggregate([
{
$match: {
_id: new ObjectId(deptId),
},
},
{
$lookup: {
from: 'peoples',
pipeline: [{
$project: {
id: '$_id',
_id: 0,
family: 1,
name: 1,
secondName: 1,
email: 1,
phone: 1,
},
}],
localField: 'idFio',
foreignField: 'id',
as: 'responsible',
},
},
{
$unwind: '$responsible',
},
{
$project: {
id: '$_id',
_id: 0,
parentId: '$parentDept',
name: 1,
qty: {
$size: '$employees',
},
responsible: '$responsible',
notes: '$note',
tasks: {
inWork: '0',
newTasks: '0',
},
},
},
]).toArray()
if (data.length === 0) {
throw new Error('No data')
}
return _idToId(data[0])
}
const dropDocuments = async (id) => {
try {
const deptsCollection = db.collection(DEPTS_COLLECTION)
const data = await deptsCollection.find({
parentDept: id,
}).toArray()
data.forEach((element) => {
dropDocuments(element._id)
})
deptsCollection.deleteOne({
_id: new ObjectId(id),
})
} catch (e) {
console.log(e)
}
}
const deleteDeptById = async (body) => {
if (db === null) throw new Error('no db connection');
try {
// eslint-disable-next-line guard-for-in
for (let deptId in body) {
dropDocuments(deptId)
}
} catch (e) {
console.log(e)
}
}
const getPeoplesData = async () => {
if (db === null) throw new Error('no db connection')
const peoplesCollection = db.collection(PEOPLES_COLLECTION)
const data = await peoplesCollection.find().toArray()
if (data.length === 0) {
const newData = require('./stubs/peoples/success.json').data
peoplesCollection.insertMany(newData)
return _idToId(newData)
}
return data
}
const createDept = async ({ form, peoples }) => {
if (db === null) throw new Error('no db connection')
const deptsCollection = db.collection(DEPTS_COLLECTION)
const data = await deptsCollection.find({
name: form.name,
}).toArray()
if (data.length > 0) throw new Error('duplication of stirng')
const employees = []
peoples.forEach((item) => employees.push({
id: item.id,
}))
const dataToInsert = {
...form, employees,
}
deptsCollection.insertMany([dataToInsert])
return dataToInsert
}
const authenticate = async (login, pass, fn) => {
if (db === null) return fn(new Error('no db connection'))
const usersCollection = db.collection(USERS_COLL)
const users = await usersCollection.find({
login,
}).toArray()
if (!users.length) return fn(new Error('невозможно найти пользователя'))
const [user] = users
hash({
password: pass, salt: user.salt,
}, (err, pass, salt, hash) => {
if (err) return fn(err)
if (hash === user.hash) return fn(null, user)
fn(new Error('неверный пароль'))
})
}
const setUsers = async (dataUsers) => {
if (db === null) throw new Error('no db connection')
const usersCollection = db.collection(USERS_COLL)
const data = await usersCollection.find({
$or: [{
login: dataUsers.login,
}, {
mail: dataUsers.mail,
}],
}).toArray()
if (data.length === 0) {
hash({
password: dataUsers.password, saltLength: 15,
}, (err, pass, salt, hash) => {
const users = {
}
const { login, password, mail } = {
...dataUsers,
}
users.login = login
users.name = login
users.mail = mail
users.hash = hash
users.salt = salt
usersCollection.insertMany([users])
})
return dataUsers
}
if (data.length !== 0) throw new Error('Почта или логин уже существует')
}
module.exports = {
connect,
getDepts,
getDeptData,
getPeoplesData,
createDept,
setUsers,
deleteDeptById,
authenticate,
}

View File

@@ -0,0 +1,162 @@
const router = require('express').Router()
const { promisify } = require('util')
const jwt = require('jsonwebtoken')
const fs = require('fs')
const path = require('path')
const md5 = require('crypto-js/md5')
const { JWT_SECRET_KEY } = require('./constants')
const { getResponse } = require('../../../utils/common')
const { connect, getPeoplesData, createDept, getDepts, getDeptData, deleteDeptById, setUsers, authenticate } = require('./controllers')
connect()
router.get('/depts', async (req, res) => {
const errors = []
const depts = await getDepts().catch((e) => errors.push(e.message))
res.send(getResponse(errors, depts))
})
router.get('/peoples', async (req, res) => {
const errors = []
const peoplesData = await getPeoplesData().catch((e) => errors.push(e.message))
res.send(getResponse(errors, peoplesData))
})
router.post('/depts/create', async (req, res) => {
const errors = []
const deptData = await createDept(req.body.data).catch((e) => errors.push(e.message))
res.send(getResponse(errors, deptData))
})
router.get('/dept/:id', async (req, res) => {
const errors = []
const data = await getDeptData(req.params.id)
res.send(getResponse(errors, data))
})
router.post('/dept/delete', async (req, res) => {
const errors = []
await deleteDeptById(req.body)
res.send(getResponse(errors, 'success'))
});
router.post('/login', async (req, res, next) => {
const auth = promisify(authenticate)
try {
console.log(req.body.login)
console.log(req.body.password)
const { hash, salt, ...user } = await auth(req.body.login, req.body.password)
const token = jwt.sign({
user,
}, JWT_SECRET_KEY)
req.session.regenerate(() => {
req.session.user = user
res.send({
success: true,
body: {
...user,
token,
},
errors: [],
warnings: [],
})
})
} catch (error) {
next(error)
}
})
router.get('/tasks', (req, resp) => {
resp.send(require('./stubs/tasks/tasks.json'))
})
router.post('/tasks/create', (req, resp) => {
try {
const { dept } = req.body
const rawTasksData = fs.readFileSync(path.resolve(__dirname, './stubs/tasks/tasks.json'))
const tasksData = JSON.parse(rawTasksData)
const deptTasksData = tasksData.body.filter((data) => data.label === dept)
const task = {
id: md5(new Date()).toString(),
number: 100,
task: req.body.task,
status: 'open',
priority: req.body.priority,
performer: req.body.performer,
deadline: new Date(req.body.deadline).getTime(),
lastChanged: new Date().getTime(),
description: req.body.description,
}
if (!deptTasksData.length) {
const newDeptTasksData = {
label: req.body.dept,
data: [task],
}
tasksData.body.push(newDeptTasksData)
} else {
tasksData.body.map((data) => {
if (data.label === dept) {
return data.data.push(task)
}
return data
})
}
fs.writeFileSync(path.resolve(__dirname, './stubs/tasks/tasks.json'), JSON.stringify(tasksData))
resp.send({
success: true,
body: task,
errors: [],
warnings: [],
})
} catch (e) {
resp.send('./stubs/error.json')
}
})
router.post('/tasks/delete', (req, resp) => {
resp.send(require('./stubs/tasks/delete.json'))
})
router.post('/tasks/edit', (req, resp) => {
resp.send({
success: true,
body: req.body,
errors: [],
warnings: [],
})
})
router.post('/sign-up', async (req, res, next) => {
try {
const { hash, salt, ...user } = await setUsers({
login: req.body.login,
password: req.body.password,
mail: req.body.mail,
})
const token = jwt.sign({
user,
}, JWT_SECRET_KEY)
req.session.regenerate(() => {
req.session.user = user
res.send({
success: true,
body: {
...user,
token,
},
errors: [],
warnings: [],
})
})
} catch (error) {
next(error)
}
})
module.exports = router

View File

@@ -0,0 +1,119 @@
{
"body": [{
"id": "001",
"parentId": "0",
"name": "Головной",
"qty": 56,
"responsible": {
"id": 1,
"family": "Иванов",
"name": "Иван",
"secondName": "Иванович",
"phone": "",
"email": ""
},
"tasks": {
"inWork": 12,
"newTasks": 54
},
"notes": ""
},
{
"id": "002",
"parentId": "0",
"name": "Цех",
"qty": 52,
"responsible": {
"id": 2,
"family": "Буйнов",
"name": "Александр",
"secondName": "Александрович",
"phone": "",
"email": ""
},
"tasks": {
"inWork": 67,
"newTasks": 12
},
"notes": ""
},
{
"id": "003",
"parentId": "002",
"name": "Токарка",
"qty": 6,
"responsible": {
"id": 3,
"family": "Петров",
"name": "Иван",
"secondName": "Иванович",
"phone": "",
"email": ""
},
"tasks": {
"inWork": 22,
"newTasks": 2
},
"notes": "1 этаж. красный сектор"
},
{
"id": "004",
"parentId": "002",
"name": "Слесарка",
"qty": 34,
"responsible": {
"id": 3,
"family": "Петров",
"name": "Иван",
"secondName": "Иванович",
"phone": "",
"email": ""
},
"tasks": {
"inWork": 34,
"newTasks": 10
},
"notes": "1 этаж. синий сектор"
},
{
"id": "005",
"parentId": "002",
"name": "Отрезной",
"qty": 12,
"responsible": {
"id": 4,
"family": "Сидоров",
"name": "Сергей",
"secondName": "Викторович",
"phone": "",
"email": ""
},
"tasks": {
"inWork": 11,
"newTasks": 0
},
"notes": "1 этаж. оранжевый сектор"
},
{
"id": "006",
"parentId": "0",
"name": "Снабжение",
"qty": 1,
"responsible": {
"id": 5,
"family": "Зыков",
"name": "Александр",
"secondName": "Геннадьевич",
"phone": "",
"email": ""
},
"tasks": {
"inWork": 2,
"newTasks": 0
},
"notes": ""
}
],
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,6 @@
{
"success": true,
"body": null,
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,30 @@
{
"data": [
{
"id": 1,
"family": "Иванов",
"name": "Иван",
"secondName": "Иванович",
"phone": "",
"email": ""
},
{
"id": 2,
"family": "Буйнов",
"name": "Александр",
"secondName": "Александрович",
"phone": "",
"email": ""
},
{
"id": 3,
"family": "Зыков",
"name": "Александр",
"secondName": "Григорьевич",
"phone": "",
"email": ""
}
],
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,8 @@
{
"success": true,
"body": {
"id": "1"
},
"errors": [],
"warnings": []
}

View File

@@ -0,0 +1,165 @@
{
"success": true,
"body": [{
"label": "Головной",
"data": [{
"id": "1",
"number": 10,
"task": "Установить Microsoft Office",
"status": "inProgress",
"priority": "middle",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "2",
"number": 11,
"task": "Замена картриджа",
"status": "open",
"priority": "high",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "3",
"number": 12,
"task": "Установка второго монитора",
"status": "inProgress",
"priority": "low",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "4",
"number": 13,
"task": "Замена клавиатуры",
"status": "inProgress",
"priority": "high",
"performer": "Иванов Иван Иванович",
"deadline": 1629534740538,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "5",
"number": 14,
"task": "Закончилась подписка на ПО",
"status": "needInfo",
"priority": "middle",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}]
}, {
"label": "Цех",
"data": [{
"id": "6",
"number": 20,
"task": "Установить Microsoft Office",
"status": "inProgress",
"priority": "middle",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "7",
"number": 21,
"task": "Замена картриджа",
"status": "open",
"priority": "high",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "8",
"number": 22,
"task": "Установка второго монитора",
"status": "inProgress",
"priority": "low",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "9",
"number": 23,
"task": "Замена клавиатуры",
"status": "inProgress",
"priority": "high",
"performer": "Иванов Иван Иванович",
"deadline": 1629534740538,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "10",
"number": 24,
"task": "Закончилась подписка на ПО",
"status": "needInfo",
"priority": "middle",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}]
}, {
"label": "Токарка",
"data": [{
"id": "11",
"number": 30,
"task": "Установить Microsoft Office",
"status": "inProgress",
"priority": "middle",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "12",
"number": 31,
"task": "Замена картриджа",
"status": "open",
"priority": "high",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "13",
"number": 32,
"task": "Установка второго монитора",
"status": "inProgress",
"priority": "low",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "14",
"number": 33,
"task": "Замена клавиатуры",
"status": "inProgress",
"priority": "high",
"performer": "Иванов Иван Иванович",
"deadline": 1629534740538,
"lastChanged": 1629534740538,
"description": "Описание"
}, {
"id": "15",
"number": 34,
"task": "Закончилась подписка на ПО",
"status": "needInfo",
"priority": "middle",
"performer": "Иванов Иван Иванович",
"deadline": 1640927340000,
"lastChanged": 1629534740538,
"description": "Описание"
}]
}],
"errors": [],
"warnings": []
}