<repo_name>MinhPhu1999/tlcn_mern<file_sep>/api/routers/cart.router.js
const cart_controller = require('../controllers/cart.controller');
const auth = require('../utils/auth');
module.exports = app => {
    app.route('/cart/:id_user').get(auth.authLogin, cart_controller.getCart);

    app.route('/cart/get/all').get(cart_controller.getAll);

    app.route('/cart/addcart').post(auth.authLogin, cart_controller.addToCart);

    app.route('/cart/updatetang').put(auth.authLogin, cart_controller.updateTang);

    app.route('/cart/updategiam').put(auth.authLogin, cart_controller.updateGiam);

    app.route('/cart/delete/:id_user').delete(auth.authLogin, cart_controller.deleteCart);

    app.route('/cart/remove').put(auth.authLogin, cart_controller.deleteProductInCart);
};
<file_sep>/api/models/color_product.js
const mongoose = require('mongoose');

const Schema = mongoose.Schema;
const colorProductSchema = new mongoose.Schema({
    colorProduct: [
        {
            _id: { type: Schema.Types.ObjectId, ref: 'color' },
        },
    ],
    products: { type: Schema.Types.ObjectId, ref: 'product' },
});

module.exports = mongoose.model('colorproduct', colorProductSchema);
<file_sep>/api/models/bill.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const order = new Schema(
    {
        id_user: {
            type: String,
        },
        cart: {
            type: [
                {
                    name: String,
                    price: Number,
                    id_category: String,
                    image: String,
                    id_brand: String,
                    count: Number,
                },
            ],
            required: true,
        },
        order_status: {
            type: Boolean,
            required: true,
        },
        order_subtotal: {
            type: Number,
            required: true,
        },
        order_date: {
            type: Date,
            $dateToString: { format: '%Y-%m-%d', date: '$date' },
            default: new Date(),
        },
        shipping_address: {
            type: [
                {
                    city: String,
                    country: String,
                    posteCode: Number,
                    number: String,
                    phone: String,
                    address: String,
                },
            ],
            required: true,
        },
        // status:{
        //     type:Boolean
        // }
    },
    { timestamps: true },
);

module.exports = mongoose.model('bill', order);
<file_sep>/api/controllers/product.controller.js
const product = require('../models/product.model');
const order = require('../models/order.model');
const brandController = require('../controllers/brand.controller');
const categoryController = require('../controllers/category.controller');
const { performance } = require('perf_hooks');

exports.sortProduct = async (req, res) => {
    //khai báo các biến cần thiết
    const sapXep = req.params.inc;
    const listProduct = await product.find({ status: true });
    const sortListProduct = listProduct.sort(function (a, b) {
        if (sapXep == 'increase') return parseFloat(a.price) - parseFloat(b.price); //sắp xếp sản phẩm tăng dần theo giá
        return parseFloat(b.price) - parseFloat(a.price); //sắp xếp sản phẩm giảm dần theo giá
    });
    sortListProduct
        ? res.status(200).send(sortListProduct)
        : res.status(404).send({ message: 'products not found' });
};

exports.getProductByID = async (req, res) => {
    let productFind;
    try {
        productFind = await product.findOne({ _id: req.params.id });
    } catch (err) {
        return res.status(500).send({ message: 'Fail' });
    }
    productFind
        ? res.status(200).send(productFind)
        : res.status(404).send({ message: 'product not found' });
};

exports.getOne = async (req, res) => {
    try {
        const productFind = await product
            .findOne({ _id: req.params.id })
            .populate('id_category')
            .populate({
                path: 'id_category',
                select: 'name',
            })
            .populate('id_brand')
            .populate({
                path: 'id_brand',
                select: 'name',
            })
            .populate('colorProducts')
            .populate({
                path: 'colorProducts',
                populate: {
                    path: 'colorProduct',
                    populate: {
                        path: '_id',
                        select: 'name',
                    },
                },
            })
            .populate('sizeProducts')
            .populate({
                path: 'sizeProducts',
                populate: {
                    path: 'sizeProduct',
                    populate: {
                        path: '_id',
                        select: 'name',
                    },
                },
            });
        if (productFind) {
            return res.status(200).json(productFind);
        } else {
            return res.status(404).json({ message: 'Fail' });
        }
    } catch (err) {
        return res.status(404).json({ message: err });
    }
};

exports.getProducts = async (req, res) => {
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }
    let count;
    try {
        count = await product.countDocuments({ status: true }); // đém sản phẩm có bao nhiêu
    } catch (err) {
        return res.status(500).send({ message: 'fail' });
    }

    const totalPage = parseInt((count - 1) / 8 + 1); // từ số lượng sản phẩm sẽ tính ra số trang
    const { page } = req.params;
    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }
    product
        .find({ status: true })
        .populate('colorProducts')
        .populate({
            path: 'colorProducts',
            populate: {
                path: 'colorProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .populate('sizeProducts')
        .populate({
            path: 'sizeProducts',
            populate: {
                path: 'sizeProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .skip(8 * (parseInt(page) - 1))
        .limit(8) // giới hạn hiển thị sản phẩm mỗi trang
        .exec((err, docs) => {
            err
                ? res.status(500).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

exports.reView = async (req, res) => {
    try {
        const { rating } = req.body;

        if (rating && rating !== 0) {
            const productFind = await product.findById(req.params.id);
            if (!productFind) return res.status(400).json({ msg: 'Product does not exist.' });

            // let num = productFind.numReviews;
            // let rate = productFind.rating;
            let num = 0;
            let rate = 0;
            if (productFind.numReviews) {
                num = productFind.numReviews;
            }
            if (productFind.rating) {
                rate = productFind.rating;
            }

            await product.findOneAndUpdate(
                { _id: req.params.id },
                {
                    $set: {
                        rating: rate + rating,
                        numReviews: num + 1,
                    },
                },
            );

            res.json({ msg: 'Update success' });
        }
    } catch (err) {
        return res.status(500).json({ msg: err.message });
    }
};

exports.getAllProduct = async (req, res) => {
    const productFind = await product.find({ status: true });
    productFind
        ? res.status(200).send(productFind)
        : res.status(404).send({ message: 'products not found' });
};

const fullTextSearch = require('fulltextsearch');
var fullTextSearchVi = fullTextSearch.vi;

function escapeRegex(text) {
    if (text.indexOf(' ') != -1) return text.split(' ');
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, ' ');
}

exports.searchProduct = async (req, res) => {
    if (typeof req.query.name === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    let regex = new RegExp(fullTextSearchVi(req.query.name), 'i');

    console.log(regex);

    product
        .find({
            $or: [{ name: { $in: [regex] }, status: true }],
        })
        .exec((err, docs) => {
            err
                ? res.status(404).send({ message: 'products not found' })
                : res.send({ data: docs });
        });

    // let regex1;
    // if (Array.isArray(escapeRegex(req.query.name))) {
    //     regex1 = new RegExp('.' + escapeRegex(req.query.name)[1], 'i');
    // } else {
    //     regex1 = new RegExp('.' + escapeRegex(req.query.name), 'i');
    // }
    // const regex2 = new RegExp('^' + escapeRegex(req.query.name), 'i');

    // product
    //     .find({
    //         $or: [{ name: { $in: [regex1, regex2] }, status: true }],
    //     })
    //     .exec((err, docs) => {
    //         err
    //             ? res.status(404).send({ message: 'products not found' })
    //             : res.send({ data: docs });
    //     });
};

exports.getProductByBrand = async (req, res) => {
    if (typeof req.params.brand === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    const searchIDBrand = await brandController.getIDBySearchText(req.params.brand);

    let productFind;
    try {
        productFind = await product.find({
            $or: [{ id_brand: new RegExp(searchIDBrand, 'i') }],
        });
    } catch (err) {
        return res.status(500).send({ message: 'products not found' });
    }
    productFind
        ? res.status(200).send(productFind)
        : res.status(404).send({ message: 'products not found' });
};

exports.getProductByCategory = async (req, res) => {
    if (typeof req.body.categoryName === 'undefined' || typeof req.body.disCount === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }
    const t0 = performance.now();
    const { categoryName, disCount, startDate, endDate } = req.body;

    const searchIDCategory = await categoryController.getIDBySearchText(categoryName);
    let productFind;
    try {
        productFind = await product.find({
            id_category: searchIDCategory,
        });
    } catch (err) {
        return res.status(500).send({ message: 'products not found' });
    }

    for (let i in productFind) {
        product
            .updateOne(
                { _id: productFind[i]._id },
                {
                    $set: {
                        startDate: new Date(startDate),
                        endDate: new Date(endDate),
                        disCount: disCount,
                    },
                },
                { upsert: true },
            )
            .then(err => {
                // if(err) console.log('');
            });
    }

    let productFind1 = await product.find({
        id_category: searchIDCategory,
        // $or: [{ id_category: new RegExp(searchIDCatefory, 'i') }],
    });

    // const t1 = performance.now();
    // console.log(t1 - t0);
    res.status(200).send({ productFind1 });
};

exports.updatePriceByCategory = async (req, res) => {
    if (
        typeof req.body.categoryName === 'undefined' ||
        typeof req.body.disCount === 'undefined' ||
        typeof req.body.increase === 'undefined'
    ) {
        return res.status(402).send({ message: 'Data invalid' });
    }

    const { categoryName, disCount, increase } = req.body;
    let discount = disCount;

    let searchIDCatefory = await categoryController.getIDBySearchText(categoryName, res);
    let productFind;
    try {
        productFind = await product.find({
            id_category: searchIDCatefory,
            // $or: [{ id_category: new RegExp(searchIDCatefory, 'i') }],
        });
    } catch (err) {
        return res.status(500).send({ message: 'products not found' });
    }

    if (increase === false) {
        disCount = -disCount;
    }

    for (let i in productFind) {
        product
            .updateOne(
                { _id: productFind[i]._id },
                {
                    $set: {
                        price: productFind[i].price + (productFind[i].price * disCount) / 100,
                    },
                },
                { upsert: true },
            )
            .then(err => {
                // console.log(err);
            });
    }
    let productFind1 = await product.find({
        id_category: searchIDCatefory,
        // $or: [{ id_category: new RegExp(searchIDCatefory, 'i') }],
    });

    res.status(200).send({ productFind1 });
};

exports.getNameByID = async (req, res) => {
    if (typeof req.params.id === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }
    let result;
    try {
        result = await product.findOne({ _id: req.params.id });
    } catch (err) {
        return res.status(404).send({ message: 'product not found' });
    }

    result
        ? res.status(200).send({ name: result.name })
        : res.status(500).send({ message: 'fail' });
};

exports.getProductTop10 = async (req, res) => {
    const orderFind = await order.find({ paymentStatus: 'paid' });

    if (orderFind === null) {
        return res.status(404).send({ message: 'products not found' });
    }

    let len = orderFind.length;
    let productFind;
    let arrProduct = [];
    let arr = [];

    //lay id product trong order
    for (let i = 0; i < len; i++) {
        let lenP = orderFind[i].cart.length;
        for (let j = 0; j < lenP; j++) {
            arr.push(orderFind[i].cart[j].id);
        }
    }

    //chi lay 1 phan tu trong nhung pnan tu trung nhau trong mang
    arr = [...new Set(arr)];

    //lay thong tin product theo id
    for (let id_product of arr) {
        productFind = await product.findById(id_product);
        arrProduct.push(productFind);
    }

    res.status(200).json({
        data: arrProduct.length > 10 ? arrProduct.slice(0, 10) : arrProduct,
    });
};

exports.getProductCategory = async (req, res) => {
    // kiểm tra tham số truyền vào có hay không
    if (typeof req.query.page === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    let count = null;

    try {
        count = await product.countDocuments({ id_category: req.query.id, status: true }); // đém sản phẩm có bao nhiêu
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    const totalPage = parseInt((count - 1) / 8 + 1); // từ số lượng sản phẩm sẽ tính ra số trang
    const { page } = req.query;
    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }

    product
        .find({ id_category: req.query.id, status: true })
        .populate('colorProducts')
        .populate({
            path: 'colorProducts',
            populate: {
                path: 'colorProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .populate('sizeProducts')
        .populate({
            path: 'sizeProducts',
            populate: {
                path: 'sizeProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .skip(8 * (parseInt(page) - 1))
        .limit(8) // giới hạn hiển thị sản phẩm mỗi trang
        .exec((err, docs) => {
            err
                ? res.status(404).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};
<file_sep>/api/models/user.model.js
require('dotenv').config();
const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
const Schema = mongoose.Schema;
const user = new Schema(
    {
        is_admin: {
            type: Boolean,
            default: false,
        },
        name: {
            type: String,
        },
        email: {
            type: String,
            lowercase: true,
            match: [/\S+@\S+\.\S+/, 'is invalid'],
        },
        address: {
            type: {
                name: String,
                phone: String,
                city: String,
                district: String,
                ward: String,
                infoDetail: String,
            },
        },
        fbEmail: {
            type: String,
        },
        ggEmail: {
            type: String,
        },
        password: {
            type: String,
        },
        is_verify: {
            type: Boolean,
            default: false,
        },
        token: {
            type: String,
        },
        otp: {
            type: String,
        },
        status: {
            type: Boolean,
            default: true,
        },
    },
    { timestamps: true },
);
user.methods.generateJWT = async function () {
    const user = this;
    const token = jwt.sign({ _id: user._id }, process.env.JWT_KEY, {
        expiresIn: '3h',
    });

    user.token = token;
    await user.save();
    return token;
};
module.exports = mongoose.model('user', user);
<file_sep>/api/utils/auth.js
const user = require('../models/user.model');
const jwt = require('jsonwebtoken');
require('dotenv').config();

exports.verify = async (req, res) => {
    if (typeof req.body.token === 'undefined' || typeof req.body.email === 'undefined') {
        res.status(422).send({ message: 'Invalid data' });
        return;
    }

    let token = req.body.token;
    let email = req.body.email;
    let userFind = await user.findOne({ email: email });
    try {
        let decoded = await jwt.verify(token, process.env.JWT_KEY);
        if (decoded._id == userFind._id) {
            res.status(200).send({ message: 'success' });
            return;
        }
    } catch (err) {
        res.status(404).send({ message: 'unsuccess1' });
        return;
    }
    res.status(404).send({ message: 'unsuccess' });
};

exports.authLogin = (req, res, next) => {
    try {
        const token = req.header('Authorization').replace('Bearer ', '');
        const data = jwt.verify(token, process.env.JWT_KEY);
        user.findOne({ _id: data._id, token: token })
            .then(user => {
                if (!user) {
                    return res.status(401).send({
                        message: 'Please login',
                    });
                }
                req.user = user;
                next();
            })
            .catch(err => {
                return res.status(401).send({
                    message: 'Not authorized to access this resource',
                });
            });
    } catch (error) {
        res.status(401).send({ message: 'Time out, Please login again' });
    }
};
<file_sep>/api/controllers/user.controller.js
const user = require('../models/user.model');
const nodemailer = require('../utils/nodemailer');
const { OAuth2Client } = require('google-auth-library');
const fetch = require('node-fetch');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const maotp = require('../utils/otp');
const validate = require('../utils/validate');
require('dotenv').config();
const client = new OAuth2Client(process.env.GOOGLE_API_KEY);

exports.register = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (
        typeof req.body.email === 'undefined' ||
        typeof req.body.password === '<PASSWORD>' ||
        typeof req.body.name === 'undefined' ||
        typeof req.body.repassword === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }

    //khai báo các biến cần thiết
    let { email, password, name, repassword } = req.body;
    //kiểm tra điều kiện password hợp lệ
    if (!validate.isValidPassWord(password) || password.trim().length < 8) {
        return res.status(422).send({
            message:
                'Passwords with a length of 8-16 characters must contain numbers, lowercase letters and uppercase letters ',
        });
    }

    //kiểm tra tên có hợp lệ không
    if (name.trim().length < 6 || !validate.isValidName(name)) {
        return res.status(422).send({ message: 'Name must be at least 6 characters long' });
    }

    //kiểm tra điều kiện email và password
    if (email.indexOf('@') === -1 && email.indexOf('.') === -1) {
        return res.status(422).send({ message: 'Invalid data' });
    }
    //nếu password và repassword khác nhau
    if (password != repassword) {
        return res.status(422).send({ message: 'Password incorect' });
    }
    let userFind = null;
    try {
        userFind = await user.find({ email: email }); //tìm kiếm user theo email
    } catch (err) {
        return res.status(500).send({ message: 'user not found' });
    }
    if (userFind.length > 0) {
        //trường hợp có user trong db
        return res.status(409).send({ message: 'Email already exist' });
    }
    //hash password
    password = bcrypt.hashSync(password, 10);
    //tạo mới user
    const newUser = new user({
        email: email,
        name: name.trim(),
        password: <PASSWORD>,
    });
    try {
        await newUser
            .save() //lưu user
            .then(function () {
                newUser.generateJWT(); //tạo token
            });
    } catch (err) {
        return res.status(500).send({ message: err });
    }
    const sendEmail = await nodemailer.sendEmail(email, newUser.token); //gửi mail để verify account
    if (!sendEmail) {
        await newUser.remove();
        return res.status(500).send({ message: 'Send email fail' });
    }
    res.status(201).send({ message: 'Register success' });
};

exports.verifyAccount = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.params.token === 'undefined') {
        return res.status(402).send({ message: '!invalid' });
    }
    //khai báo các biến cần thiết
    let tokenFind = null;
    try {
        tokenFind = await user.findOne({ token: req.params.token }); //tìm kiếm user theo token
    } catch (err) {
        return res.status(500).send({ message: 'user not found' });
    }

    try {
        //lưu các thay đổi
        await user.findByIdAndUpdate(tokenFind._id, { $set: { is_verify: true } }, err => {
            err
                ? res.status(500).send({ message: 'verify account fail' })
                : res.status(200).send({ message: 'verify account success' });
        });
    } catch (err) {
        return res.status(500).send({ message: err });
    }
};

exports.login = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.body.email === 'undefined' || typeof req.body.password == 'undefined') {
        return res.status(402).send({ message: 'email or password wrrong' });
    }
    //khai báo các biến cần thiết
    const { email, password } = req.body;
    let userFind = null;
    try {
        userFind = await user.findOne({ email: email }); //tìm kiếm user theo email
    } catch (err) {
        return res.status(402).send({ message: 'user not found' });
    }
    if (userFind === null) {
        //trường hợp không có user trong db
        return res.status(422).send({ message: 'not found user in database' });
    }
    if (!userFind.is_verify) {
        //trường hợp account chưa verify
        return res.status(401).send({ message: 'no_registration_confirmation' });
    }

    if (!bcrypt.compareSync(password, userFind.password)) {
        //trường hợp sai mật  khẩu
        return res.status(422).send({ message: 'password wrong' });
    }
    userFind.generateJWT(); //tạo token
    //thông báo login success
    res.status(200).send({
        message: 'login success',
        token: userFind.token,
        newUser: {
            email: userFind.email,
            name: userFind.name,
            _id: userFind._id,
        },
    });
};

exports.getUser = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.params.id === 'undefined') {
        return res.status(402).send({ message: 'Invalid data' });
    }
    //khai báo các biến cần thiết
    const { id } = req.params;
    let email;
    let userFind = null;
    try {
        userFind = await user.findOne({ _id: id }); //tìm kiếm user theo id
    } catch (err) {
        return res.send({ message: 'user not found' });
    }

    if (userFind.fbEmail != null) {
        email = userFind.fbEmail;
    }
    if (userFind.ggEmail != null) {
        email = userFind.ggEmail;
    } else {
        email = userFind.email;
    }

    res.status(200).send({
        user: {
            //trả về email và name của user
            email: email,
            name: userFind.name,
            address: userFind.address,
        },
    });
};

exports.requestForgotPassword = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.params.email === 'undefined') {
        return res.status(402).send({ message: 'Invalid data' });
    }
    //khai báo các biến cần thiết
    const { email } = req.params;
    let userFind = null;
    try {
        userFind = await user.findOne({ email: email }); //tìm kiếm user theo email
    } catch (err) {
        return res.send({ message: 'user not found' });
    }
    if (userFind === null) {
        //trường hợp không có user trong db
        return res.status(422).send({ message: 'Email not exist in database' });
    }
    if (!userFind.is_verify) {
        //trường hợp account chưa verify
        return res.status(401).send({ message: 'no_registration_confirmation' });
    }
    //sinh mã otp
    let otp = maotp.generateOTP();
    //gửi otp qua email của email
    const sendEmail = await nodemailer.sendEmailForgotPassword(email, otp);
    if (!sendEmail) {
        //trường hợp gửi mail fail
        return res.status(500).send({ message: 'Send email fail' });
    }
    userFind.otp = otp; //cập nhật mã otp
    try {
        userFind.save(err => {
            err
                ? res.status(500).send({ message: 'fail' })
                : res.status(201).send({ message: 'success', email: email });
        }); //lưu các thay đổi
    } catch (err) {
        return res.status(500).send({ message: err });
    }
    //thông báo thành công
};

exports.verifyForgotPassword = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.body.email === 'undefined' || typeof req.body.otp === 'undefined') {
        return res.status(402).send({ message: 'Invalid data' });
    }
    //khai báo các biến cần thiết
    const { email, otp } = req.body;
    let userFind = null;
    try {
        userFind = await user.findOne({ email: email, otp: otp }); //tìm kiếm user theo email
    } catch (err) {
        return res.send({ message: 'user not found' });
    }

    userFind
        ? res.status(200).send({ message: 'success', otp: otp })
        : res.status(422).send({ message: 'OTP fail' });
};

exports.forgotPassword = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (
        typeof req.body.email === 'undefined' ||
        typeof req.body.otp === 'undefined' ||
        typeof req.body.newPassword === 'undefined'
    ) {
        return res.status(402).send({ message: 'Invalid data' });
    }
    //khai báo các biến cần thiết
    const { email, otp, newPassword } = req.body;
    let userFind = null;
    try {
        userFind = await user.findOne({ email: email }); //tìm kiếm user theo email
    } catch (err) {
        return res.send({ message: 'user not found' });
    }
    if (userFind === null) {
        //trường hợp không có user trong db
        return res.status(422).send({ message: 'User not found in database' });
    }
    //trường hợp kiểm tra otp nhập vào khác với otp trong db
    if (userFind.otp != otp) {
        return res.status(422).send({ message: 'OTP fail' });
    }

    // if (!validate.isValidPassWord(newPassword) || newPassword.trim().length < 8) {
    //     return res.status(422).send({
    //         message:
    //             'Passwords with a length of 8-16 characters must contain numbers, lowercase letters and uppercase letters ',
    //     });
    // }

    //hash password
    userFind.password = bcrypt.hashSync(newPassword, 10);
    try {
        await userFind.save(err => {
            err
                ? res.status(500).send({ message: 'fail' })
                : res.status(201).send({ message: 'success' });
        }); //lưu các thay đổi
    } catch (err) {
        return res.status(500).send({ message: err });
    }
};

exports.updateInfor = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (
        typeof req.body.name === 'undefined' ||
        typeof req.body.id === 'undefined' ||
        typeof req.body.email === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }
    //khai báo các biến cần thiết
    const { email, name, id } = req.body;

    // if (name.trim().length < 6 || !validate.isValidName(name)) {
    //     return res.status(422).send({ message: 'Name must be at least 6 characters long' });
    // }

    let newUser = await user.findById(id);
    //tìm kiếm user theo email
    let userFind = await user.findOne({ email: email });
    //trường hợp email đã có trong db
    if (userFind != null && newUser.email !== email) {
        return res.status(422).send({ message: 'Email already exist' });
    }
    //cập nhật thay đổi
    newUser.name = name;
    newUser.email = email;
    try {
        await newUser.save(); //lưu các thay đổi
    } catch (err) {
        return res.status(500).send({ message: 'Invalid email' });
    }
    //thông báo update infor thành công
    res.status(200).send({
        message: 'success',
        token: newUser.token,
        newUser: {
            email: newUser.email,
            name: newUser.name,
            _id: newUser._id,
        },
    });
};

exports.updatePassword = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (
        typeof req.body.oldpassword === 'undefined' ||
        typeof req.body.newpassword === 'undefined' ||
        typeof req.body.id === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }
    //khai báo các biến cần thiết
    let { id, oldpassword, newpassword } = req.body;

    if (oldpassword === newpassword) {
        return res
            .status(422)
            .send({ message: 'The new password must not be the same as the old password' });
    }
    let userFind = null;
    try {
        userFind = await user.findOne({ _id: id }); //tìm kiếm user theo id
    } catch (err) {
        return res.send({ message: 'user not found' });
    }
    if (!userFind) {
        //trường hợp không có user trong db
        return res.status(422).send({ message: 'User not found' });
    }
    //trường hợp nhập mật khẩu cũ không khớp
    if (!bcrypt.compareSync(oldpassword, userFind.password)) {
        return res.status(422).send({ message: 'Password wrong' });
    }

    if (!validate.isValidPassWord(newpassword) || newpassword.trim().length < 8) {
        return res.status(422).send({
            message:
                'Passwords with a length of 8-16 characters must contain numbers, lowercase letters and uppercase letters ',
        });
    }

    //hash newpassword
    userFind.password = bcrypt.hashSync(newpassword, 10);
    try {
        userFind.save(err => {
            err
                ? res.status(500).send({ message: 'Update password fail' })
                : res.status(200).send({ message: 'Update password success' });
        }); //lưu các thay đổi
    } catch (err) {
        return res.status(500).send({ message: err });
    }
};

exports.getDataByID = async id_user => {
    let userFind = await user.findOne({ _id: id_user });
    if (userFind.fbEmail != null) {
        email = userFind.fbEmail;
    } else if (userFind.ggEmail != null) {
        email = userFind.ggEmail;
    } else {
        email = userFind.email;
    }
    return [userFind.name, email];
};

exports.googleController = async (req, res) => {
    const { idToken } = req.body;
    // console.log(idToken);
    client.verifyIdToken({ idToken, audience: process.env.GOOGLE_API_KEY }).then(response => {
        // console.log('GOOGLE LOGIN RESPONSE',response)
        const { email_verified, name, email } = response.payload;
        if (email_verified) {
            user.findOne({ ggEmail: email }).exec((err, newUser) => {
                if (newUser) {
                    //newUser.generateJWT();
                    const token = jwt.sign({ _id: newUser._id }, process.env.JWT_KEY, {
                        expiresIn: '3h',
                    });
                    newUser.token = token;
                    newUser.save();
                    //const token = newUser.token;
                    const { _id, email, name } = newUser;
                    return res.json({
                        token,
                        newUser: { _id, email, name },
                    });
                } else {
                    let password = email + process.env.JWT_KEY;
                    newUser = new user({
                        name: name,
                        ggEmail: email,
                        password: <PASSWORD>,
                        is_verify: true,
                    });

                    newUser.save((err, data) => {
                        if (err) {
                            console.log('ERROR GOOGLE LOGIN ON USER SAVE', err);
                            return res.status(400).json({
                                error: 'User signup failed with google',
                            });
                        }
                        const token = jwt.sign({ _id: data._id }, process.env.JWT_KEY, {
                            expiresIn: '3h',
                        });
                        data.token = token;
                        data.save();
                        const { _id, email, name, role } = data;
                        return res.json({
                            token,
                            newUser: { _id, email, name, role },
                        });
                    });
                }
            });
        } else {
            return res.status(400).json({
                error: 'Google login failed. Try again',
            });
        }
    });
};

exports.facebookController = (req, res) => {
    const { userID, accessToken } = req.body;

    const url = `https://graph.facebook.com/v2.11/${userID}/?fields=id,name,email&access_token=${accessToken}`;

    return (
        fetch(url, {
            method: 'GET',
        })
            .then(response => response.json())
            // .then(response => console.log(response))
            .then(response => {
                const { email, name } = response;
                user.findOne({ fbEmail: email }).exec((err, newUser) => {
                    if (newUser) {
                        //newUser.generateJWT();
                        const token = jwt.sign({ _id: newUser._id }, process.env.JWT_KEY, {
                            expiresIn: '3h',
                        });
                        newUser.token = token;
                        newUser.save();
                        const { _id, email, name } = newUser;
                        return res.json({
                            token,
                            newUser: { _id, email, name },
                        });
                    } else {
                        let password = email + process.env.JWT_KEY;
                        newUser = new user({
                            name: name,
                            fbEmail: email,
                            password: <PASSWORD>,
                            is_verify: true,
                        });
                        newUser.save((err, data) => {
                            if (err) {
                                console.log('ERROR FACEBOOK LOGIN ON USER SAVE', err);
                                return res.status(400).json({
                                    error: 'User signup failed with facebook',
                                });
                            }
                            const token = jwt.sign({ _id: data._id }, process.env.JWT_KEY, {
                                expiresIn: '3h',
                            });
                            data.token = token;
                            data.save();
                            const { _id, email, name } = data;
                            return res.json({
                                token,
                                newUser: { _id, email, name },
                            });
                        });
                    }
                });
            })
            .catch(error => {
                res.json({
                    error: 'Facebook login failed. Try later',
                });
            })
    );
};

exports.addAddress = async (req, res) => {
    try {
        const userF = await user.findById(req.body.id);
        if (!userF) return res.status(400).json({ message: 'User not found' });
        if (req.body.address.name.length === 0 || req.body.address.name.length >= 30)
            return res.status(400).json({ msg: 'Form is not format' });
        if (req.body.address.phone.length !== 10)
            return res.status(400).json({ msg: 'Please enter the correct phone number' });

        await user.findOneAndUpdate(
            { _id: req.body.id },
            {
                $set: {
                    address: req.body.address,
                },
            },
        );
        return res.status(200).json('Add address');
    } catch (err) {
        return res.status(500).json({ msg: error.message });
    }
};

exports.updateAddress = async (req, res) => {
    try {
        const userF = await user.findById(req.body.id);
        if (!user) return res.status(400).json({ message: 'User not found' });
        if (req.body.address.name.length === 0 || req.body.address.name.length >= 30)
            return res.status(400).json({ msg: 'Form is not format' });
        if (req.body.address.phone.length !== 10)
            return res.status(400).json({ msg: 'Please enter the correct phone number' });

        await user.findOneAndUpdate(
            { _id: req.body.id },
            {
                $set: {
                    address: req.body.address,
                },
            },
        );
        return res.status(200).json('Update address');
    } catch (err) {
        return res.status(500).json({ msg: err.message });
    }
};
<file_sep>/api/models/brand.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const brand = new Schema(
    {
        name: {
            type: String,
            required: [true, 'Không được bỏ trống'],
        },
        status: {
            type: Boolean,
            default: true,
        },
    },
    { timestamps: true },
);
module.exports = mongoose.model('brand', brand);
<file_sep>/api/routers/size.router.js
const size_controller = require('../controllers/size.controller');

module.exports = app => {
    app.route('/size/getsizes').get(size_controller.getSizes);
};
<file_sep>/api/config/connectDB.js
const mongoose = require('mongoose');
require('dotenv').config();

const connectDB = async () => {
    await mongoose
        .connect(process.env.Mongo_URL, {
            useNewUrlParser: true,
            useCreateIndex: true,
            useFindAndModify: false,
            useUnifiedTopology: true,
        })
        .catch(error => console.log(error.reason));
};

module.exports = connectDB;
<file_sep>/api/config/multer.js
const multer = require('multer');
const path = require('path');

module.exports = multer({
    storage: multer.diskStorage({
        destination: './files/',
        filename: (req, file, cb) => {
            let filename = `${Date.now()}-${file.originalname}`;
            cb(null, filename);
        },
    }),
    // limits: (file, cb) => {
    //     let sizeFile = file.fileSize
    //     if(sizeFile > 400000){
    //         return cb(new Error("Limited is 2MB"));
    //     }
    //     cb(null, true)

    // },
    fileFilter: (req, file, cb) => {
        let ext = path.extname(file.originalname);
        if (ext !== '.jpg' && ext !== '.jpeg' && ext !== '.png' && ext !== '.jfif') {
            cb(new Error('File type is not supported'));
            return;
        }
        cb(null, true);
    },
});
<file_sep>/api/routers/brand.router.js
const brand_controller = require('../controllers/brand.controller');
module.exports = (app) => {
    app.route('/brands').get(brand_controller.getBrands);

    app.route('/brand/name/:id').get(brand_controller.getNameByID);
};
<file_sep>/api/controllers/stock.controller.js
'usr strict';
const stock = require('../models/stock.model');

exports.getDataByID = async id_stock => {
    let result = null;
    try {
        result = await stock.findById(id_stock);
    } catch (err) {
        console.log(err);
        return;
    }
    if (result === null) {
        console.log('user not found');
        return;
    }
    return [result.name_category, result.name_brand];
};
<file_sep>/api/models/banner.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const banner = new Schema({
    content: {
        type: String,
        require: [true, 'không được để trống'],
    },
    categoryName: String,
    id_category: String,
    disCount: Number,
    status: Boolean,
});

module.exports = mongoose.model('banner', banner);
<file_sep>/api/controllers/category.controller.js
const category = require('../models/category.model');

exports.getCategorys = async (req, res) => {
    category.find({ status: true }, (err, docs) => {
        err
            ? res.status(500).json({ message: 'category not found' })
            : res.status(200).json({ data: docs });
    });
};

exports.getNameByID = async (req, res) => {
    if (req.params.id === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }
    let result;
    try {
        result = await category.findById(req.params.id);
    } catch (err) {
        return res.status(404).send({ message: 'category not found catch' });
    }
    result
        ? res.status(200).send({ name: result.name })
        : res.status(404).send({ message: 'category not found' });
};

exports.getIDBySearchText = async (searchText, res) => {
    let arr = [];
    try {
        arr = await category.findOne({ name: searchText }); //, 'i',{name: 0}
        // arr = await category.find({ name: new RegExp(searchText, 'i') }); //, 'i',{name: 0}
    } catch (err) {
        res.status(500).send({ message: err });
        return;
    }
    return arr._id;
};
<file_sep>/api/models/category.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const category = new Schema(
    {
        name: {
            type: String,
            required: [true, 'Không được bỏ trống'],
        },
        status: {
            type: Boolean,
            default: true,
        },
    },
    { timestamps: true },
);
module.exports = mongoose.model('category', category);
<file_sep>/api/routers/banner.router.js
const banner_controller = require('../controllers/banner.controller');

module.exports = app => {
    app.route('/banners').get(banner_controller.getBanners);
};
<file_sep>/api/utils/quantity.js
const product = require('../models/product.model');

exports.changeQuantity = async (id_product, quantity) => {
    let productFind = await product.findOne({ _id: id_product });
    productFind.quantity -= quantity;
    productFind.save();
};

exports.calPrice = (price, quantity) => {
    return price * quantity;
};

exports.valid = (productCart, id_product, size, color) => {
    if (productCart.id == id_product && productCart.color == color && productCart.size == size) {
        return true;
    }
    return false;
};
<file_sep>/api/routers/category.router.js
const category_controller = require('../controllers/category.controller');
module.exports = app => {
    app.route('/categorys').get(category_controller.getCategorys);

    app.route('/category/name/:id').get(category_controller.getNameByID);
};
<file_sep>/index.js
//Khai báo các thư viện cần thiết
require('dotenv').config();
const express = require('express');
const app = express();
const port = process.env.PORT || 8080;
const path = require('path');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
const connectDB = require('./api/config/connectDB');

const userRouter = require('./api/routers/user.router');
const categoryRouter = require('./api/routers/category.router');
const productRouter = require('./api/routers/product.router');
const brandRouter = require('./api/routers/brand.router');
const cartRouter = require('./api/routers/cart.router');
const orderRouter = require('./api/routers/order.router');
const adminRouter = require('./api/routers/admin.router');
const commentRouter = require('./api/routers/comment.router');
const bannerRouter = require('./api/routers/banner.router');
const colorRouter = require('./api/routers/color.router');
const sizeRouter = require('./api/routers/size.router');
const promoRouter = require('./api/routers/promocode.router');

//model comment
const Comments = require('./api/models/comment.model');

//kết nối tới database mongo
connectDB();

//có phép nhận dữ liệu từ form
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

//cors
app.use(cors());

const http = require('http').createServer(app);
const io = require('socket.io')(http, {
	cors: {
		origin: '*'
	}
});


// Soketio
let users = [];
// io.on('connection', socket => {
//     console.log(socket.id + 'connected.');

//     socket.on('disconnect', () => {
//         console.log(socket.id + 'disconnected');
//     });
// });
io.on('connection', socket => {
    // console.log(socket.id + 'connected');

    socket.on('joinRoom', id => {
        const user = { userId: socket.id, room: id };

        const check = users.every(user => user.userId !== socket.id);

        if (check) {
            users.push(user);
            socket.join(user.room);
        } else {
            users.map(user => {
                if (user.userId === socket.id) {
                    if (user.room !== id) {
                        socket.leave(user.room);
                        socket.join(id);
                        user.room = id;
                    }
                }
            });
        }
    });

    socket.on('createComment', async msg => {
        const { username, content, product_id, createdAt, rating, send } = msg;
		// console.log('create comment');

        const newComment = new Comments({
            username,
            content,
            product_id,
            createdAt,
            rating,
        });

    //     // console.log(newComment);
        // await newComment.save();

        if (send === 'replyComment') {
            const { _id, username, content, product_id, createdAt, rating } = newComment;

            const comment = await Comments.findById(product_id);

            if (comment) {
                comment.reply.push({ _id, username, content, createdAt, rating });

                await comment.save();
                io.to(comment.product_id).emit('sendReplyCommentToClient', comment);
            }
        } else {
            await newComment.save();
            io.to(newComment.product_id).emit('sendCommentToClient', newComment);
        }
    });

    socket.on('disconnect', () => {
        // console.log(socket.id + 'disconnect');
        users = users.filter(user => user.userId !== socket.id);
    });
});

userRouter(app);
categoryRouter(app);
brandRouter(app);
productRouter(app);
cartRouter(app);
orderRouter(app);
adminRouter(app);
commentRouter(app);
bannerRouter(app);
colorRouter(app);
sizeRouter(app);
promoRouter(app);

app.get('/', (req, res) => {
    res.send('welcome to e_store');
});

http.listen(port, () => console.log('server running on port ' + port));
<file_sep>/api/routers/product.router.js
const product_controller = require('../controllers/product.controller');

module.exports = app => {
    app.route('/product/getproducts/:page').get(product_controller.getProducts);

    app.route('/products').get(product_controller.getAllProduct);

    app.route('/product/:id').get(product_controller.getOne);

    app.route('/updateRate/:id').patch(product_controller.reView);

    app.route('/product/search/s').get(product_controller.searchProduct);

    app.route('/product/brand/:brand').get(product_controller.getProductByBrand);

    app.route('/product/category').put(product_controller.getProductByCategory);

    app.route('/product/amount/:id').get(product_controller.getNameByID);

    app.route('/product/sort/:inc').get(product_controller.sortProduct);

    app.route('/product/banchay/top10').get(product_controller.getProductTop10);

    app.route('/product/category/cate').get(product_controller.getProductCategory);
};
<file_sep>/api/routers/color.router.js
const color_controller = require('../controllers/color.controller');

module.exports = app => {
    app.route('/color/getcolors').get(color_controller.getColors);
};
<file_sep>/api/controllers/comment.controller.js
const comment = require('../models/comment.model');

class APIfeatures {
    constructor(query, queryString) {
        this.query = query;
        this.queryString = queryString;
    }
    sorting() {
        this.query = this.query.sort('-createdAt');
        return this;
    }
    paginating() {
        const page = this.queryString.page * 1 || 1;
        const limit = this.queryString.limit * 1 || 5;
        const skip = (page - 1) * limit;
        this.query = this.query.skip(skip).limit(limit);
        return this;
    }
}

exports.getComment = async (req, res) => {
    try {
        const features = new APIfeatures(comment.find({ product_id: req.params.id }), req.query)
            .sorting()
            .paginating();

        const comments = await features.query;

        res.json({
            status: 'success',
            result: comments.length,
            comments,
        });
    } catch (err) {
        return res.status(500).json({ msg: err.message });
    }
};

exports.updateComment = async (req, res) => {
    if (typeof req.body.id === 'undefined' || typeof req.body.content === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }

    const { id, content } = req.body;

    comment
        .updateOne(
            { _id: id },
            {
                $set: {
                    content: content,
                },
            },
        )
        .exec(err => {
            if (err) {
                return res.status(400).send({ error });
            }
            res.status(201).send({ message: 'success' });
        });
};

exports.deleteComment = async (req, res) => {
    //kiểm tra tham số truyền vào đúng hay không
    if (typeof req.body.id === 'undefined' || typeof req.body.user_id === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }

    //tìm kiếm theo id và user_id trong model comment
    const commentFind = await comment.find({ id: id, user_id: user_id });
    if (commentFind === null)
        return res.status(404).send({ message: 'You cannot delete other people\'s comments' });

    //update lại status của comment
    comment
        .updateOne(
            { _id: req.params.id },
            {
                $set: {
                    status: false,
                },
            },
        )
        .exec(err => {
            if (err) {
                return res.status(400).send({ error });
            }
            res.status(201).send({ message: 'delete success' });
        });
};
<file_sep>/api/models/cart.model.js
const mongoose = require('mongoose');
const product = require('../models/product.model');
const Schema = mongoose.Schema;

const cart = new Schema({
    id_user: {
        type: String,
    },
    products: {
        type: [
            {
                name: String,
                price: Number,
                img: String,
                quantity: Number,
                id: String,
                color: { type: Schema.Types.ObjectId, ref: 'color' },
                size: { type: Schema.Types.ObjectId, ref: 'size' },
            },
        ],
        required: true,
        minlength: 1,
    },
    grandTotal: {
        type: Number,
    },
    status: {
        type: Boolean,
        default: true,
    },
    date_cart: {
        type: Date,
        default: Date.now(),
    },
});

cart.methods.updateCountProduct = async function () {
    const cart = this;
    let index;
    for (let i = 0; i < cart.products.length; i++) {
        index = cart.products.findIndex(element => cart.products[i]._id === element._id);
        if (index !== -1) {
            cart.grandTotal = cart.products[index].price * cart.products[index].quantity;
        }
    }
};

cart.methods.minusQuantity = async function (id_size, res) {
    const cart = this;
};

cart.methods.plusProduct = async function (id_product) {
    let productFind = await product.findById(id_product);
    productFind.quantity += 1;

    try {
        await productFind.save();
    } catch (err) {
        console.log(err);
        return;
    }
};

module.exports = mongoose.model('cart', cart);
<file_sep>/api/utils/orderby.js
exports.ordered = (type, orderFind) => {
    if (type === 1) {
        if (
            orderFind.orderStatus[0].isCompleted &&
            !orderFind.orderStatus[1].isCompleted &&
            !orderFind.orderStatus[2].isCompleted &&
            !orderFind.orderStatus[3].isCompleted
        )
            return true;
    }
    if (type === 2) {
        if (
            orderFind.orderStatus[1].isCompleted &&
            !orderFind.orderStatus[2].isCompleted &&
            !orderFind.orderStatus[3].isCompleted
        )
            return true;
    }
    if (type === 3) {
        if (orderFind.orderStatus[2].isCompleted && !orderFind.orderStatus[3].isCompleted)
            return true;
    }
    if (type === 4) {
        if (orderFind.orderStatus[3].isCompleted) return true;
    }
    return false;
};
<file_sep>/api/controllers/brand.controller.js
const brand = require('../models/brand.model');
exports.getBrands = async (req, res) => {
    //get tất cả brand theo status = true
    brand.find({ status: true }, (err, docs) => {
        err
            ? res.status(500).json({ message: 'brand not found' })
            : res.status(200).json({ data: docs });
    });
};

exports.getNameByID = async (req, res) => {
    //kiểm tra có truyền vô id hay không
    if (req.params.id === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }
    //khai báo biến result
    let result = null;
    try {
        result = await brand.findOne({ _id: req.params.id }); //tìm kiếm brand theo id
    } catch (err) {
        return res.status(404).send({ message: 'brand not found' });
    }
    result
        ? res.status(200).send({ name: result.name })
        : res.status(404).send({ message: 'not found' });
};

exports.getIDBySearchText = async searchText => {
    //khai báo mảng
    let arr = [];
    try {
        arr = await brand.find({ name: new RegExp(searchText, 'i') }); //tìm kiếm theo tên
    } catch (err) {
        res.status(500).send({ message: err });
        return;
    }
    return arr.map(i => i.id); //trả về cái mảng
};
<file_sep>/api/controllers/color.controller.js
const color = require('../models/color.model');

// Get all color -- find(query, projection)
module.exports.getColors = (req, res) => {
    color.find({ status: true }, (err, colors) => {
        err
            ? res.status(500).json({ message: 'colors not found' })
            : res.status(200).json({ data: colors });
    });
};
<file_sep>/api/controllers/admin.controller.js
// load  các models và thư viện cần thiết
const category = require('../models/category.model');
const brand = require('../models/brand.model');
const user = require('../models/user.model');
const stock = require('../models/stock.model');
const size = require('../models/size.model');
const color = require('../models/color.model');
const promocode = require('../models/promocode.model');
const size_product = require('../models/size_product');
const color_product = require('../models/color_product');
const banner = require('../models/banner.model');
const product = require('../models/product.model');
const categoryCtrl = require('./category.controller');

const image_product = require('../models/image_product');
const stockController = require('../controllers/stock.controller');
const bcrypt = require('bcrypt');
require('dotenv').config();
const cloudinary = require('../config/cloudinary');

const uploadImg = async path => {
    let res;
    try {
        res = await cloudinary.uploader.upload(path); //upload ảnh lên cloudinary
    } catch (err) {
        return false;
    }
    return res.secure_url;
};

exports.addProduct = async (req, res) => {
    //kiểm tra có đủ tham số truyền vào hay không
    if (
        typeof req.files === 'undefined' ||
        typeof req.body.name === 'undefined' ||
        typeof req.body.id_category === 'undefined' ||
        typeof req.body.price === 'undefined' ||
        typeof req.body.id_brand === 'undefined' ||
        typeof req.body.description === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }

    req.body.sizeProduct = JSON.parse(req.body.sizeProduct);
    req.body.colorProduct = JSON.parse(req.body.colorProduct);

    const { name, id_category, id_brand, quantity, price, description, sizeProduct, colorProduct } =
        req.body;

    let urls = [];
    let id_product;
    const files = req.files;

    for (const file of files) {
        const { path } = file;
        const result = await uploadImg(path);
        urls.push(result);
    }

    const nColor = new color_product({ colorProduct });
    const nSize = new size_product({ sizeProduct });

    const nProduct = new product({
        name,
        id_category,
        id_brand,
        images: urls,
        description,
        price,
        quantity,
    });

    const sP = await nProduct.save((err, docProduct) => {
        if (docProduct) id_product = docProduct._id;
    });

    const sS = await nSize.save(async (err, doc) => {
        if (doc) {
            doc.products = id_product;
            await doc.save();
            product
                .updateOne(
                    { _id: id_product },
                    {
                        $set: {
                            sizeProducts: doc._id,
                        },
                    },
                )
                .then(data => {
                    if (data) {
                        console.log('size ngoai');
                    }
                });
        }
    });

    const sC = await nColor.save(async (err, doc) => {
        if (doc) {
            doc.products = id_product;
            await doc.save();
            product
                .updateOne(
                    { _id: id_product },
                    {
                        $set: {
                            colorProducts: doc._id,
                        },
                    },
                )
                .then(data => {
                    if (data) {
                        console.log('color ngoai');
                    }
                });
        }
    });

    res.status(201).send({ message: 'add product success' });
};

exports.updateProduct = async (req, res) => {
    // kiểm tra có đủ tham số truyền vào hay không
    if (
        typeof req.body.name === 'undefined' ||
        typeof req.body.id === 'undefined' ||
        typeof req.body.id_category === 'undefined' ||
        typeof req.body.price === 'undefined' ||
        typeof req.body.id_brand === 'undefined' ||
        typeof req.body.description === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }

    let {
        id,
        name,
        id_category,
        id_brand,
        price,
        description,
        quantity,
        status,
        sizeProduct,
        colorProduct,
    } = req.body;

    const productFind = await product.findOne({ _id: id }); //tìm kiếm product bằng id

    const id_colorP = productFind.colorProducts;
    const id_sizeP = productFind.sizeProducts;

    let urls = [];
    if (req.files.length > 0) {
        const files = req.files;

        for (const file of files) {
            const { path } = file;
            const result = await uploadImg(path);
            urls.push(result);
        }
    } else {
        urls = productFind.images;
    }

    if (colorProduct != 'null') {
        colorProduct = JSON.parse(colorProduct);
        color_product
            .updateOne(
                { _id: id_colorP },
                {
                    $set: {
                        colorProduct: colorProduct,
                    },
                },
            )
            .then(err => {
                // if (err) console.log('color product');
            });
    }

    if (sizeProduct != 'null') {
        sizeProduct = JSON.parse(sizeProduct);
        size_product
            .updateOne(
                { _id: id_sizeP },
                {
                    $set: {
                        sizeProduct: sizeProduct,
                    },
                },
            )
            .then(err => {
                // if (err) console.log('size product');
            });
    }

    product.updateOne(
        { _id: id },
        {
            $set: {
                name,
                id_category,
                price,
                quantity,
                images: urls,
                id_brand,
                description,
                status,
            },
        },
        (err, data) => {
            err
                ? res.status(500).json({ message: 'Fail' })
                : res.status(200).json({ message: 'Success' });
        },
    );
};

exports.deleteProduct = async (req, res) => {
    product.updateOne({ _id: req.params.id }, { $set: { status: false } }).exec(err => {
        err
            ? res.status(400).send({ message: err })
            : res.status(200).send('delete product success');
    });
};

exports.getOne = async (req, res) => {
    product
        .findOne({ _id: req.params.id })
        .populate('colorProducts')
        .populate({
            path: 'colorProducts',
            populate: {
                path: 'colorProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .populate('sizeProducts')
        .populate({
            path: 'sizeProducts',
            populate: {
                path: 'sizeProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .exec(function (err, data) {
            err ? res.status(404).json({ message: err }) : res.status(200).json({ data });
        });
};

exports.getAllProducts = async (req, res) => {
    // kiểm tra tham số truyền vào có hay không
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    let count = null;

    try {
        count = await product.countDocuments({}); // đém sản phẩm có bao nhiêu
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    const totalPage = parseInt((count - 1) / 5 + 1); // từ số lượng sản phẩm sẽ tính ra số trang
    const { page } = req.params;
    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }

    product
        .find({})
        .populate('colorProducts')
        .populate({
            path: 'colorProducts',
            populate: {
                path: 'colorProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .populate('sizeProducts')
        .populate({
            path: 'sizeProducts',
            populate: {
                path: 'sizeProduct',
                populate: {
                    path: '_id',
                },
            },
        })
        .skip(5 * (parseInt(page) - 1))
        .limit(5) // giới hạn hiển thị sản phẩm mỗi trang
        .exec((err, docs) => {
            err
                ? res.status(404).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

//stock
exports.addStock = async (req, res) => {
    //kiểm tra các tham số truyền vào có đủ hay không
    if (
        typeof req.body.name_category === 'undefined' ||
        typeof req.body.name_brand === 'undefined' ||
        typeof req.body.quantity === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }

    const { name_category, name_brand, quantity } = req.body; //khai cái tham số cần thiết
    let stockFind;

    try {
        stockFind = await stock.find({
            name_category: name_category,
            name_brand: name_brand,
        }); //tìm kiếm tên category và tên brand
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    if (stockFind.length > 0) {
        //nếu độ dài lớn hơn 0 thì thông báo đã có trong database
        return res.status(409).send({ message: 'stock already exist' });
    }

    const newStock = new stock({
        //tạo mới stock
        name_category: name_category,
        name_brand: name_brand,
        quantity: quantity,
        status: true,
    });

    const newCategory = new category({
        //tạo mới category
        name: name_category,
        path: path,
        status: true,
    });

    const newBrand = new brand({
        // tạo mới brand
        name: name_brand,
        status: true,
    });
    try {
        // lưu tất cả vào trong mongo
        await newStock.save();
        await newCategory.save();
        await newBrand.save();
    } catch (err) {
        // console.log(err);
        return res.status(500).send({ message: err });
    }
    res.status(201).send({ message: 'add stock success' }); // thông báo thên thành công
};

exports.updateStock = async (req, res) => {
    // kiểm tra các tham số truyền vào có đủ hay không
    if (
        typeof req.body.id === 'undefined' ||
        typeof req.body.name_category === 'undefined' ||
        typeof req.body.path === 'undefined' ||
        typeof req.body.name_brand === 'undefined' ||
        typeof req.body.quantity === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }

    const { id, name_category, path, name_brand, quantity, status } = req.body; //khai báo tham số
    const getNameStock = await stockController.getDataByID(id); // lấy tên theo id
    let stockFind = null;
    let categoryFind = null;
    let brandFind = null;

    try {
        stockFind = await stock.findById(id); //tìm bằng id
        categoryFind = await category.findOne({
            name: getNameStock[0],
            status: true,
        }); //tìm kiếm bằng tên và status là true
        brandFind = await brand.findOne({
            name: getNameStock[1],
            status: true,
        }); //tìm kiếm bằng tên và status là true
    } catch (err) {
        return res.status(500).send({ message: err });
    }
    if (stockFind === null) {
        return res.status(422).send({ message: 'stock not found' });
    }
    // update lại thông tin
    stockFind.name_category = name_category;

    stockFind.name_brand = name_brand;
    stockFind.quantity = quantity;
    stockFind.status = status;

    categoryFind.name = name_category;
    categoryFind.path = path;
    categoryFind.status = status;

    brandFind.name = name_brand;
    brandFind.status = status;
    try {
        //lưu các thay đổi vào mongo
        await stockFind.save();
        await categoryFind.save();
        await brandFind.save();
    } catch (err) {
        // console.log(err);
        return res.status(500).send({ message: err });
    }
    res.status(201).send({
        message: 'update stock success',
        stock: {
            name_category: name_category,
            name_brand: name_brand,
            quantity: quantity,
        },
    });
};

exports.deleteStock = async (req, res) => {
    //kiểm tra các tham số truyền vào có đủ hay không
    if (typeof req.params.id === 'undefined') {
        return res.status(402).send({ message: 'data invalid' });
    }

    const getNameStock = await stockController.getDataByID(req.params.id); //tìm kiếm tên theo id
    let stockFind = null;
    let categoryFind = null;
    let brandFind = null;

    try {
        stockFind = await stock.findById(id); //tìm kiếm bằng id
        categoryFind = await category.findOne({
            name: getNameStock[0],
            status: true,
        }); //tìm kiếm bằng tên và status là true
        brandFind = await brand.findOne({
            name: getNameStock[1],
            status: true,
        }); //tìm kiếm bằng tên và status là true
    } catch (err) {
        // console.log(err);
        return res.status(500).send({ message: 'server found' });
    }
    if (brandFind === null) {
        return res.status(400).send({ message: 'stock not found' });
    }
    //update lại status
    stockFind.status = false;
    categoryFind.status = false;
    brandFind.status = false;
    try {
        //lưu các thay đổi vào mongo
        await stockFind.save();
        await categoryFind.save();
        await brandFind.save();
    } catch (err) {
        // console.log(err);
        return res.status(500).send({ message: err });
    }
    res.status(200).send({ message: 'delete stock success' }); //thông báo xóa thành công
};

exports.getAllStocks = async (req, res) => {
    //kiểm tra có đủ tham số hay không
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data Invalid' });
    }

    let count = null;
    try {
        count = await stock.countDocuments({}); //đém số lượng cho việc phân trang
    } catch (err) {
        // console.log(err);
        return res.status(500).send({ message: err });
    }

    let totalPage = parseInt((count - 1) / 9 + 1); //tính số trang cần phân chia
    let { page } = req.params;
    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }

    stock
        .find() //lấy stock
        .skip(9 * (parseInt(page) - 1))
        .limit(9)
        .exec((err, docs) => {
            err
                ? res.status(404).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

exports.getStocks = async (req, res) => {
    stock.find({ status: true }, (err, docs) => {
        err ? res.status(404).send({ message: err }) : res.status(200).send({ data: docs });
    });
};

//brand
exports.addBrand = async (req, res) => {
    //kiểm tra có đủ tham số truyền vào hay không
    if (typeof req.body.name === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }

    let brandFind = null;
    const { name } = req.body;
    try {
        brandFind = await brand.find({ name: name }); //tìm kiếm brand theo tên
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    if (brandFind.length > 0) {
        // trường hợp có brand
        return res.status(409).send({ message: 'Brand already exist' });
    }

    const newBrand = new brand({ name });

    try {
        await newBrand.save(); //lưu brand
    } catch (err) {
        // xuất ra lỗi
        return res.status(500).send({ message: err });
    }
    res.status(201).send({ message: 'add brand success', data: newBrand }); // thông báo add brand thành công
};

exports.updateBrand = async (req, res) => {
    //kiểm tra có đủ tham số truyền vào hay không
    if (typeof req.body.id === 'undefined' || typeof req.body.name === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }

    const { id, name, status } = req.body; // khai báo biến
    let brandFind;

    try {
        brandFind = await brand.findById(id); //tìm kiếm brand theo id
    } catch (err) {
        return res.status(500).send({ message: err }); //xuất lỗi
    }

    if (brandFind === null) {
        //không tìm thấy brand thì xuất ra lỗi
        return res.status(422).send({ message: 'brand not found' });
    }

    //update các thông tin của brand
    brandFind.name = name;
    brandFind.status = status;
    try {
        //lưu lại branđ đã thay đổi
        await brandFind.save(err => {
            err
                ? res.status(404).send({ message: 'add brand fail' })
                : res.status(201).send({
                      message: 'update brand success',
                      brand: { name: name },
                  });
        });
    } catch (err) {
        return res.status(500).send({ message: err }); //xuất lỗi
    }
};

exports.deleteBrand = async (req, res) => {
    brand.updateOne({ _id: req.params.id }, { $set: { status: false } }).exec(error => {
        error ? res.status(400).send({ error }) : res.status(201).send('delete brand success');
    });
};

exports.getAllBrands = async (req, res) => {
    //kiểm tra có truyền đủ tham số hay không
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    let count = null; //khai báo biến
    try {
        count = await brand.countDocuments(); //đếm brand
    } catch (err) {
        //xuất lỗi
        return res.status(500).send({ message: err });
    }

    const totalPage = parseInt((count - 1) / 5 + 1); //tính tổng số trang
    const { page } = req.params;

    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }
    brand
        .find() //lấy brand
        .skip(5 * (parseInt(page) - 1))
        .limit(5)
        .exec((err, docs) => {
            err
                ? res.status(500).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

// category
exports.addCategory = async (req, res) => {
    //kiểm tra có truyền đủ tham số hay không
    if (typeof req.body.name === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }

    //khai báo biến
    const { name } = req.body;
    let categoryFind;

    try {
        categoryFind = await category.find({ name: name }); //tìm kiếm theo name và path
    } catch (err) {
        //xuất lỗi
        return res.status(500).send({ message: err });
    }

    if (categoryFind.length > 0) {
        //trường hợp tìm thấy category
        return res.status(409).send({ message: 'category already exist' });
    }

    const newCategory = new category({ name });
    try {
        newCategory.save(err => {
            err
                ? res.status(500).send({ message: 'add categoy fail' })
                : res.status(201).send({ message: 'add category success' });
        });
    } catch (err) {
        return res.status(500).send({ message: err });
    }
};

exports.updateCategory = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.body.id === 'undefined' || typeof req.body.name === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }

    //khai báo biến cần thiết
    const { id, name, status } = req.body;
    let categoryFind = null;

    try {
        categoryFind = await category.findById(id); //tiến hành tìm kiếm category theo id
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    if (categoryFind === null) {
        //trường hợp không có category trong cơ sở dữ liệu
        return res.status(422).send({ message: 'category not found' });
    }

    //tiến hành update các thông tin cho category
    categoryFind.name = name;
    categoryFind.status = status;

    try {
        //lưu các thay đổi
        categoryFind.save(err => {
            err
                ? res.status(500).send({ message: err })
                : res.status(201).send({
                      message: 'update category success',
                      category: { name: name },
                  });
        });
    } catch (err) {
        //xuất lỗi nếu không lưu được
        return res.status(500).send({ message: err });
    }

    //thông báo update thành công
};

exports.deleteCategory = async (req, res) => {
    category.updateOne({ _id: req.params.id }, { $set: { status: false } }).exec(error => {
        error ? res.status(400).send({ error }) : res.status(200).send('delete product success');
    });
};

exports.getAllCategorys = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data Invalid' });
    }

    //khai báo biến cần thiết
    let count = null;
    try {
        count = await category.countDocuments(); //đém category
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    const totalPage = parseInt((count - 1) / 5 + 1); //tính số trang
    const { page } = req.params;

    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }

    category
        .find() //get category them status = true
        .skip(5 * (parseInt(page) - 1))
        .limit(5)
        .exec((err, docs) => {
            err
                ? res.status(500).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

// user
exports.updateUser = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.body.email === 'undefined' || typeof req.body.name === 'undefined') {
        return res.status(422).send({ message: 'Invalid data' });
    }

    //khai báo biến cần thiết
    const { email, name, status } = req.body;
    let userFind;

    try {
        userFind = await user.findOne({ email: email, is_admin: true }); //tiến hành tìm kiếm user theo email
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    if (userFind === null) {
        //trường hợp không có user trong cơ sở dữ liệu
        return res.status(422).send({ message: 'user not found' });
    }

    //update thông tin cho user
    userFind.name = name;
    userFind.status = status;

    try {
        await userFind.save(); //lưu lại các thay đổi
    } catch (err) {
        //xuất lỗi nếu không lưu lại được
        return res.status(500).send({ message: err });
    }

    //thông báo update thành công
    res.status(200).send({
        message: 'update user success',
        user: {
            email: userFind.email,
            name: userFind.name,
        },
    });
};

exports.deleteUser = async (req, res) => {
    user.updateOne({ _id: req.params.id }, { $set: { status: false } }).exec(error => {
        error ? res.status(400).send({ error }) : res.status(201).send('delete product success');
    });
};

exports.addUser = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (
        typeof req.body.email === 'undefined' ||
        typeof req.body.password === '<PASSWORD>' ||
        typeof req.body.name === 'undefined' ||
        typeof req.body.is_admin === 'undefined'
    ) {
        return res.status(422).send({ message: 'Invalid data' });
    }

    //khai báo biến cần thiết
    let { email, password, name, is_admin } = req.body;
    let userFind = null;

    //kiểm tra email có hợp lệ không, password phải trên 6 kí tự
    if ((email.indexOf('@') === -1 && email.indexOf('.') === -1) || password.length < 6) {
        res.status(422).send({ message: 'Invalid data or password too short' });
    }

    try {
        userFind = await user.find({ email: email }); //tiến hành tìm kiếm user theo email
    } catch (err) {
        return res.status(500).send({ message: err });
    }
    if (userFind.length > 0) {
        //trường hợp đã có user trong cơ sở dữ liệu
        return res.status(409).send({ message: 'Email already exist' });
    }

    password = <PASSWORD>.hashSync(password, 10); //hash password
    const newUser = new user({
        //tạo mới user để thêm vào databasr
        email: email,
        name: name,
        is_verify: true,
        password: <PASSWORD>,
        is_admin: is_admin,
    });

    try {
        await newUser.save().then(function () {
            newUser.generateJWT(); //tạo token
        }); //lưu user vào database
    } catch (err) {
        //xuất lỗi nếu không lưu lại được
        return res.status(500).send({ message: err });
    }
    res.status(201).send({ message: 'add user success' }); //thông báo thêm thành công
};

exports.login = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.body.email === 'undefined' || typeof req.body.password == 'undefined') {
        return res.status(402).send({ message: 'Invalid data' });
    }

    //khai báo biến cần thiết
    const { email, password } = req.body;
    let userFind = null;

    try {
        userFind = await user.findOne({ email: email, is_admin: true }); //tiến hành tìm kiếm user theo email và is_admin
    } catch (err) {
        return res.send({ message: err });
    }

    if (userFind == null) {
        //trường hợp không có user trong cơ sở dữ liệu
        return res.status(422).send({ message: 'user not found' });
    }

    if (!userFind.is_verify) {
        //trường hợp chưa verify
        return res.status(401).send({ message: 'no_registration_confirmation' });
    }

    if (!bcrypt.compareSync(password, userFind.password)) {
        //trường hợp sai mật khẩu
        return res.status(422).send({ message: 'wrong password' });
    }
    //tạo token cho user khi đăng nhập
    userFind.generateJWT();
    //thông báo đăng nhập thành công
    res.status(200).send({
        message: 'success',
        token: userFind.token,
        user: {
            email: userFind.email,
            name: userFind.name,
            id: userFind._id,
        },
    });
};

exports.getUsers = async (req, res) => {
    //get toàn bộ user
    user.find({ status: true }, (err, docs) => {
        err ? res.status(500).send({ message: err }) : res.status(200).send({ data: docs });
    });
};

exports.getAllUsers = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    //khai báo biến cần thiết
    let count = null;

    try {
        count = await user.countDocuments({ is_admin: true }); //đếm admin
    } catch (err) {
        // console.log(err);
        return res.status(500).send({ message: err });
    }

    const totalPage = parseInt((count - 1) / 9 + 1); //tính số trang
    const { page } = req.params;

    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }

    //get user
    user.find({ is_admin: true })
        .skip(9 * (parseInt(page) - 1))
        .limit(9)
        .exec((err, docs) => {
            err
                ? res.status(500).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

// Add new size
module.exports.addSize = (req, res) => {
    const nSize = new size(req.body);

    nSize.save((err, doc) => {
        err
            ? res.status(500).json({ message: 'add size fail' })
            : res.status(200).json({
                  message: 'add size success',
                  size: doc,
              });
    });
};

// Get all size -- find(query, projection)
module.exports.getAllSizes = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    //khai báo biến cần thiết
    let count = null;

    try {
        count = await size.countDocuments({}); //đếm color
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    if (count === null) {
        return res.status(500).json({ message: 'sizes not found' });
    }

    const totalPage = parseInt((count - 1) / 5 + 1); //tính số trang
    const { page } = req.params;

    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }

    //get colors
    size.find({})
        .skip(5 * (parseInt(page) - 1))
        .limit(5)
        .exec((err, docs) => {
            err
                ? res.status(500).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

// Update size by ID
module.exports.updateSize = (req, res) => {
    const { id, name, description, status } = req.body;
    size.updateOne({ _id: id }, { $set: { name, description, status } }, (err, data) => {
        err
            ? res.status(500).send({ message: err })
            : res.status(200).send({ message: 'update size success' });
    });
};

//Delete size
module.exports.deleteSize = (req, res) => {
    size.updateOne({ _id: req.params.id }, { status: false }, (err, data) => {
        err
            ? res.status(500).send({ message: err })
            : res.status(200).send({ message: 'delete size success' });
    });
};

// Add new color
module.exports.addColor = (req, res) => {
    const nColor = new color(req.body);

    nColor.save((err, doc) => {
        err
            ? res.status(500).json({ message: err })
            : res.status(200).json({
                  color: doc,
              });
    });
};

// Get all color -- find(query, projection)
module.exports.getAllColors = async (req, res) => {
    //kiểm tra có truyền tham số đủ hay không
    if (typeof req.params.page === 'undefined') {
        return res.status(402).send({ message: 'Data invalid' });
    }

    //khai báo biến cần thiết
    let count = null;

    try {
        count = await color.countDocuments({}); //đếm color
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    if (count === null) return res.status(500).json({ message: 'colors not found' });

    const totalPage = parseInt((count - 1) / 5 + 1); //tính số trang
    const { page } = req.params;

    if (parseInt(page) < 1 || parseInt(page) > totalPage) {
        return res.status(200).send({ data: [], message: 'Invalid page', totalPage });
    }

    //get colors
    color
        .find({})
        .skip(5 * (parseInt(page) - 1))
        .limit(5)
        .exec((err, docs) => {
            err
                ? res.status(500).send({ message: err })
                : res.status(200).send({ data: docs, totalPage });
        });
};

// Update color
module.exports.updateColor = (req, res) => {
    const { id, name, description, status } = req.body;
    color.updateOne({ _id: id }, { $set: { name, description, status } }, (err, data) => {
        err
            ? res.status(500).send({ message: err })
            : res.status(200).send({ message: 'update color success' });
    });
};

// Delete color
module.exports.deleteColor = (req, res) => {
    color.updateOne({ _id: req.params.id }, { status: false }, (err, data) => {
        err
            ? res.status(500).send({ message: err })
            : res.status(200).send({ message: 'delete color success' });
    });
};

//add promotion code
exports.addPromotionCode = async (req, res) => {
    const exits = await promocode.findOne({ promotion_code: req.body.promotion_code });

    if (exits) {
        return res.json({ message: 'promotion code exits' });
    }
    const newPro = new promocode(req.body);

    newPro.save((err, doc) => {
        err ? res.status(500).json({ message: err }) : res.status(200).json({ doc });
    });
};

//update promotion code
exports.updatePromoCode = async (req, res) => {
    promocode.updateOne(
        { _id: req.body.id },
        {
            $set: req.body,
        },
        (err, data) => {
            err
                ? res.status(500).send({ message: err })
                : res.status(200).send({ message: 'update promotion code success' });
        },
    );
};

exports.getPromoCodes = async (req, res) => {
    promocode.find({}, (err, data) => {
        err ? res.status(500).send({ message: err }) : res.status(200).json(data);
    });
};

//add banner
exports.addBanner = async (req, res) => {
    let productFind;
    try {
        productFind = await product.find({
            id_category: req.body.id_category,
        });
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    for (let pro of productFind) {
        product
            .updateOne(
                { _id: pro._id },
                {
                    $set: {
                        sellPrice: pro.price - (pro.price * req.body.disCount) / 100,
                    },
                },
                { upsert: true },
            )
            .then(err => {
                // if(err) console.log('');
            });
    }

    const newBanner = new banner(req.body);

    newBanner.save((err, doc) => {
        err
            ? res.status(500).send({ message: err })
            : res.status(201).send({ message: 'add banner success' });
    });
};

//update banner
exports.updateBanner = async (req, res) => {
    let productFind;
    try {
        productFind = await product.find({ id_category: req.body.id_category });
    } catch (err) {
        return res.status(500).send({ message: err });
    }

    for (let pro of productFind) {
        product
            .updateOne(
                { _id: pro._id },
                {
                    $set: {
                        sellPrice: pro.price - (pro.price * req.body.disCount) / 100,
                    },
                },
                { upsert: true },
            )
            .then(err => {
                // if(err) console.log('');
            });
    }

    banner.updateOne({ _id: req.body.id }, { $set: req.body }, (err, data) => {
        err
            ? res.status(500).send({ message: 'fail' })
            : res.status(200).send({ message: 'Update success' });
    });
};

//update status banner
exports.updateStatus = async (req, res) => {
    banner.updateOne({ _id: req.params.id }, { status: false }, err => {
        err
            ? res.status(500).send({ message: 'fail' })
            : res.status(200).send({ message: 'Success' });
    });
};

//get banner theo id
exports.getBanner = async (req, res) => {
    banner.findOne({ _id: req.params.id }, (err, data) => {
        err ? res.status(404).json({ message: 'Banner not found' }) : res.status(200).json(data);
    });
};

//get all banner
exports.getBanners = async (req, res) => {
    banner.find({}, (err, data) => {
        err ? res.status(404).json({ message: 'Banners not found' }) : res.status(200).json(data);
    });
};
<file_sep>/api/controllers/size.controller.js
const size = require('../models/size.model');

// Get all size -- find(query, projection)
module.exports.getSizes = (req, res) => {
    size.find({ status: true }, (err, sizes) => {
        err ? res.status(500).json({ message: 'size not found' }) : res.status(200).json({ sizes });
    });
};
<file_sep>/api/routers/admin.router.js
const admin_controller = require('../controllers/admin.controller');
const order_controller = require('../controllers/order.controller');
const product_controller = require('../controllers/product.controller');

const upload = require('../config/multer');

module.exports = app => {
    //product
    app.route('/admin/addproduct').post(upload.array('files'), admin_controller.addProduct);

    app.route('/admin/updateproduct').patch(upload.array('files'), admin_controller.updateProduct);

    app.route('/admin/deleteproduct/:id').patch(admin_controller.deleteProduct);

    app.route('/admin/getallproduct/:page').get(admin_controller.getAllProducts);

    app.route('/admin/product/updateprice').post(product_controller.updatePriceByCategory);

    //brand
    app.route('/admin/addbrand').post(admin_controller.addBrand);

    app.route('/admin/updatebrand').put(admin_controller.updateBrand);

    app.route('/admin/deletebrand/:id').put(admin_controller.deleteBrand);

    app.route('/admin/getallbrand/:page').get(admin_controller.getAllBrands);

    //category
    app.route('/admin/addcategory').post(admin_controller.addCategory);

    app.route('/admin/updatecategory').put(admin_controller.updateCategory);

    app.route('/admin/deletecategory/:id').put(admin_controller.deleteCategory);

    app.route('/admin/getallcategory/:page').get(admin_controller.getAllCategorys);

    //user
    app.route('/admin/adduser').post(admin_controller.addUser);

    app.route('/admin/deleteuser/:id').put(admin_controller.deleteUser);

    app.route('/admin/updateuser').put(admin_controller.updateUser);

    app.route('/admin/getAllUser/:page').get(admin_controller.getAllUsers);

    app.route('/admin/getusers').get(admin_controller.getUsers);

    app.route('/admin/login').post(admin_controller.login);

    //order
    app.route('/admin/order/:id').put(order_controller.deleteOrder);

    app.route('/admin/order/byday').get(order_controller.getOrderByDay);

    app.route('/admin/order/bymonth').get(order_controller.getOrderByMonth);

    app.route('/admin/order/byyear/:year').get(order_controller.getOrderByYear);

    app.route('/admin/order/top10').get(order_controller.getOrderTop10);

    app.route('/admin/order/quantitybyyear/:year').get(order_controller.getQuantityByYear);

    app.route('/admin/order/subtotal/:year').get(order_controller.getOrderSubTotalByYear);

    app.route('/admin/order/subtotalcategory').post(
        order_controller.getOrderSubTotalByYearAndCategory,
    );

    app.route('/admin/order/yearandcategory').post(order_controller.getQuantityByYearAndCategory);

    app.route('/admin/order/countorder').post(order_controller.getQuantityOrderByYearAndCategory);

    //stock
    app.route('/admin/addstock').post(admin_controller.addStock);

    app.route('/admin/updatestock').put(admin_controller.updateStock);

    app.route('/admin/deletestock/:id').put(admin_controller.deleteStock);

    app.route('/admin/getallstock/:page').get(admin_controller.getAllStocks);

    //size
    app.route('/admin/addsize').post(admin_controller.addSize);

    app.route('/admin/updatesize').put(admin_controller.updateSize);

    app.route('/admin/deletesize/:id').put(admin_controller.deleteSize);

    app.route('/admin/getsizes/:page').get(admin_controller.getAllSizes);

    //color
    app.route('/admin/addcolor').post(admin_controller.addColor);

    app.route('/admin/updatecolor').put(admin_controller.updateColor);

    app.route('/admin/deletecolor/:id').put(admin_controller.deleteColor);

    app.route('/admin/getcolors/:page').get(admin_controller.getAllColors);

    //promotion code
    app.route('/admin/promocodes/add').post(admin_controller.addPromotionCode);

    app.route('/admin/promocodes/update').put(admin_controller.updatePromoCode);

    app.route('/admin/promocodes').get(admin_controller.getPromoCodes);

    //banner
    app.route('/admin/banners/add').post(admin_controller.addBanner);

    app.route('/admin/banners/update').put(admin_controller.updateBanner);

    app.route('/admin/banners/:id').get(admin_controller.getBanner);

    app.route('/admin/banners').get(admin_controller.getBanners);
};
<file_sep>/api/models/product.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
sizeProductSchema = require('../models/size_product').schema;
colorProductSchema = require('../models/color_product').schema;

const product = new Schema({
    name: {
        type: String,
        required: [true, 'Không được bỏ trống'],
    },
    price: {
        type: Number,
        required: [true, 'Không được bỏ trống'],
    },
    sellPrice: Number,
    id_category: {
        type: Schema.Types.ObjectId,
        ref: 'category',
    },
    quantity: {
        type: Number,
        require: true,
    },
    images: {
        type: Array,
        default: [],
    },
    id_brand: {
        type: Schema.Types.ObjectId,
        ref: 'brand',
    },
    description: {
        type: String,
        required: [true, 'Không  được bỏ trống'],
    },
    colorProducts: {
        type: Schema.Types.ObjectId,
        ref: 'colorproduct',
    },
    sizeProducts: {
        type: Schema.Types.ObjectId,
        ref: 'sizeproduct',
    },
    numReviews: Number,
    rating: Number,
    status: {
        type: Boolean,
        default: true,
    },
});

module.exports = mongoose.model('product', product);
<file_sep>/api/models/image_product.js
const mongoose = require('mongoose');

const Schema = mongoose.Schema;
const imageProduct = new mongoose.Schema({
    images: {
        type: Array,
        default: [],
    },
    product: {
        type: Schema.Types.ObjectId,
        ref: 'product',
    },
});

module.exports = mongoose.model('image_product', imageProduct);
<file_sep>/api/models/promocode.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const promocode = new Schema({
    promotion_code: String,
    status: {
        type: Boolean,
        default: true,
    },
    content: String,
    price_discount: Number,
});

module.exports = mongoose.model('promocode', promocode);
<file_sep>/api/routers/comment.router.js
const comment_controller = require('../controllers/comment.controller');

module.exports = app => {
    app.route('/comment/:id').get(comment_controller.getComment);

    app.route('/comment').put(comment_controller.updateComment);

    app.route('/comment/:id').put(comment_controller.deleteComment);
};
