ecliptica/server/routers/basket/controller.js

511 lines
13 KiB
JavaScript

const ObjectId = require('mongodb').ObjectID
const getHash = require('pbkdf2-password')()
const { getDB } = require('../../utils/mongo')
const USERS_COLLECTION = 'users'
const LISTS_COLLECTION = 'lists'
const CATEGORY_COLLECTION = 'default_categories'
const USER_CATEGROY_COLLECTION = 'user_categories'
const ITEM_COLLECTION = 'items'
const fakeUserId = 'fakeUserId'
let db = null
const connect = async () => {
db = await getDB('basket')
}
const init = async () => {
await connect()
const categoriesCollection = db.collection(CATEGORY_COLLECTION)
const findData = await categoriesCollection.find({
}).toArray()
if (findData.length === 0) {
await categoriesCollection.insertMany([
{
name: 'Продукты',
color: '#08AE0F',
},
{
name: 'Одежда',
color: '#9D79B9',
},
{
name: 'Бытовая химия',
color: '#B11F1F',
},
{
name: 'Лекарства',
color: '#3414F5',
},
])
}
}
init()
const _idToId = (data) => {
const { _id, ...rest } = data
return {
id: _id,
...rest,
}
}
const _idToIdArray = (data) => {
const _idToIdMap = data.map((item) => _idToId(item))
return _idToIdMap
}
const getResponse = (error, data, success = true) => {
if (error) {
return {
success: false,
error,
}
}
return {
success,
data,
}
}
const signUp = async ({ email, login, password }) => {
if (db === null) throw new Error('no db connection')
try {
const usersCollection = db.collection(USERS_COLLECTION)
const userData = await usersCollection.findOne({
$or: [{
login,
}, {
email,
}],
})
if (userData?.login === login) {
throw new Error('Логин занят')
}
if (userData?.email === email) {
throw new Error('Email занят')
}
getHash({ password }, async (err, pass, salt, hash) => {
if (err) throw new Error(err)
// eslint-disable-next-line max-len
const { insertedCount } = await usersCollection.insertOne({ email, login, pwd: hash, salt })
if (!insertedCount) throw new Error('insert error')
})
return {}
} catch (e) {
throw new Error(e)
}
}
const getUser = async ({ email }) => {
if (db === null) throw new Error('no db connection')
try {
const usersCollection = db.collection(USERS_COLLECTION)
const userData = await usersCollection.findOne(
{
email,
},
)
if (userData) return userData
throw new Error('Неправильный email или пароль')
} catch (e) {
throw new Error(e)
}
}
const addList = async ({ userId = fakeUserId, ...data }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const insertData = await listsCollection.insertOne({
userId,
timeStamp: Date.now(),
...data,
})
const { insertedCount, ops } = insertData
if (insertedCount) { return _idToId(ops[0]) }
throw new Error('insert error')
} catch (e) {
throw new Error(e)
}
}
const getLists = async ({ userId = fakeUserId }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const itemsCollection = db.collection(ITEM_COLLECTION)
let newLists = []
const data = await listsCollection.find({
userId,
}).toArray()
await Promise.all(data.map(async (element) => {
const total = await itemsCollection.countDocuments({
parentId: element._id,
})
const purchased = await itemsCollection.countDocuments({
parentId: element._id,
bought: true,
})
newLists.push({
...element, total, purchased,
})
}))
newLists.sort((a, b) => (b.timeStamp - a.timeStamp))
return _idToIdArray(newLists)
} catch (e) {
throw new Error(e)
}
}
/* добавил логику рекурсивного удаления дочерних документов */
const deleteDoc = async ({ id, tag = false }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const itemsCollection = db.collection(ITEM_COLLECTION)
const findData = await itemsCollection.find({
parentId: new ObjectId(id),
}).toArray()
findData.forEach(async (element) => {
await deleteDoc({
id: element._id,
tag: true,
})
})
let delData = null
if (tag) {
delData = await itemsCollection.deleteOne({
_id: new ObjectId(id),
})
} else {
delData = await listsCollection.deleteOne({
_id: new ObjectId(id),
})
}
const { deletedCount } = delData
if (deletedCount) {
return {
}
} throw new Error('no data to delete')
} catch (e) {
throw new Error(e)
}
}
const renameList = async ({ id, listName }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const data = await listsCollection.updateOne({
_id: new ObjectId(id),
},
{
$set: {
listName,
},
})
const { matchedCount } = data
if (matchedCount) {
const findData = await listsCollection.findOne({
_id: new ObjectId(id),
})
return _idToId(findData)
} throw new Error('no data to rename')
} catch (e) {
throw new Error(e)
}
}
const duplicateList = async ({ id, parentId = null }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(LISTS_COLLECTION)
const itemsCollection = db.collection(ITEM_COLLECTION)
let addListData = null
let newId = null
if (parentId) {
const findData = await itemsCollection.findOne(
{
_id: new ObjectId(id),
},
)
const { _id, ...item } = findData
item.parentId = parentId
const insertData = await itemsCollection.insertOne({
...item,
})
const { insertedCount } = insertData
if (!insertedCount) throw new Error('insert new item error')
} else {
const findData = await listsCollection.findOne(
{
_id: new ObjectId(id),
},
)
const { _id, timeStamp, ...item } = findData
item.listName = `(КОПИЯ) ${item.listName}`
addListData = await addList({
...item,
})
newId = addListData.id
}
const childData = await itemsCollection.find({
parentId: new ObjectId(id),
}).toArray()
childData.forEach(async (element) => {
await duplicateList({
id: element._id, parentId: newId,
})
})
if (addListData) return _idToId(addListData)
} catch (e) {
throw new Error(e)
}
}
const getCategory = async ({ userId }) => {
if (db === null) throw new Error('no db connection')
try {
const categoriesCollection = db.collection(CATEGORY_COLLECTION)
const defaultCategories = await categoriesCollection.find({
}).toArray()
const defaultCategoriesData = _idToIdArray(defaultCategories).map((dc) => ({
...dc, userId,
}))
const userCollection = db.collection(USER_CATEGROY_COLLECTION)
const userCategoriesFilter = {}
if (userId) {
userCategoriesFilter.userId = userId
}
const userFindData = await userCollection.find(userCategoriesFilter).toArray()
return [...defaultCategoriesData, ..._idToIdArray(userFindData)]
} catch (e) {
throw new Error(e)
}
}
const postCategory = async ({ userId = fakeUserId, ...categoryData }) => {
if (db === null) throw new Error('no db connection')
try {
const userCollection = db.collection(USER_CATEGROY_COLLECTION)
const insertData = await userCollection.insertOne({
userId, ...categoryData,
})
// const {insertedCount, ops} = insertData
// if (insertedCount)
// _idToId(ops[0])
// else
// throw new Error('insert error')
const userFindData = await userCollection.find({
userId,
}).toArray()
return _idToIdArray(userFindData)
} catch (e) {
throw new Error(e)
}
}
const getShoppingList = async ({ userId = fakeUserId, id }) => {
if (db === null) throw new Error('no db connection')
try {
const listsCollection = db.collection(ITEM_COLLECTION)
const itemsList = await listsCollection.find({
parentId: new ObjectId(id),
}).toArray()
const categoryList = await getCategory({ })
const coloredItemsList = itemsList.map((item) => ({
...item,
// eslint-disable-next-line max-len
color: categoryList.find((category) => String(category.id) === String(item.categoryId))?.color,
}))
return _idToIdArray(coloredItemsList)
} catch (e) {
throw new Error(e)
}
}
const addListItem = async ({ userId = fakeUserId, listId, categoryId, text }) => {
if (db === null) throw new Error('no db connection')
try {
const dataToInsert = {
parentId: new ObjectId(listId),
categoryId: new ObjectId(categoryId),
text,
count: 1,
bought: false,
createdBy: userId,
createdDt: Date.now(),
modifiedBy: userId,
modifiedDt: Date.now(),
}
const itemCollection = db.collection(ITEM_COLLECTION)
await itemCollection.insertOne(dataToInsert)
return _idToId(dataToInsert)
} catch (e) {
throw new Error(e)
}
}
const boughtItem = async ({ userId = fakeUserId, itemId, bought }) => {
if (db === null) throw new Error('no db connection')
try {
const itemCollection = db.collection(ITEM_COLLECTION)
const chengedData = await itemCollection.findOneAndUpdate({
_id: new ObjectId(itemId),
},
[{
$set: {
bought: { $eq: [false, '$bought'] },
modifiedBy: userId,
modifiedDt: Date.now(),
},
}])
return _idToId(chengedData)
} catch (e) {
throw new Error(e)
}
}
const incCountItem = async ({ userId = fakeUserId, itemId, count }) => {
if (db === null) throw new Error('no db connection')
try {
const itemCollection = db.collection(ITEM_COLLECTION)
const chengedData = await itemCollection.findOneAndUpdate({
_id: new ObjectId(itemId),
},
{
$inc: {
count,
},
},
{
$set: {
modifiedBy: userId,
modifiedDt: Date.now(),
},
})
const chengeData = await itemCollection.findOneAndUpdate({
_id: new ObjectId(itemId),
count: {
$lt: 1,
},
},
{
$set: {
count: 1,
modifiedBy: userId,
modifiedDt: Date.now(),
},
})
return _idToId(chengedData || chengeData)
} catch (e) {
throw new Error(e)
}
}
const deleteItem = async ({ itemId }) => {
if (db === null) throw new Error('no db connection')
try {
const itemCollection = db.collection(ITEM_COLLECTION)
const findItemData = await itemCollection.find({
_id: new ObjectId(itemId),
})
findItemData.forEach((item) => {
deleteItem({
id: item._id,
})
})
const deleteItemData = await itemCollection.deleteOne({
_id: new ObjectId(itemId),
})
const { deletedButton } = deleteItemData
if (deletedButton) {
return {
}
}
} catch (e) {
throw new Error(e)
}
}
const requiredFields = (fields) => (req, res, next) => {
// eslint-disable-next-line no-restricted-syntax
for (const fieldName of fields) {
if (!req.body[fieldName]) {
throw new Error(`Параметр ${fieldName} не установлен`)
}
}
next()
}
module.exports = {
getResponse,
addList,
getLists,
deleteDoc,
renameList,
duplicateList,
getCategory,
postCategory,
getShoppingList,
addListItem,
boughtItem,
deleteItem,
incCountItem,
signUp,
getUser,
_idToId,
requiredFields,
}