├── README.md
├── client
├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── README.md
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── HelloWorld.vue
│ │ ├── auth
│ │ │ └── LoginUser.vue
│ │ ├── dash
│ │ │ └── UserDash.vue
│ │ ├── layouts
│ │ │ └── AppLayout.vue
│ │ ├── product
│ │ │ ├── manufacturer
│ │ │ │ └── ManufacturerList.vue
│ │ │ ├── option
│ │ │ │ ├── ProductOptionGroupList.vue
│ │ │ │ └── ProductOptionList.vue
│ │ │ ├── product
│ │ │ │ ├── ProductAttribute.vue
│ │ │ │ ├── ProductCreate.vue
│ │ │ │ ├── ProductImage.vue
│ │ │ │ └── ProductList.vue
│ │ │ └── section
│ │ │ │ ├── ProductCategoryList.vue
│ │ │ │ └── ProductSubCategoryList.vue
│ │ └── utils
│ │ │ └── Loader.vue
│ ├── helpers
│ │ └── ucWords.js
│ ├── main.js
│ ├── router
│ │ ├── index.js
│ │ └── metaCheck.js
│ ├── store
│ │ ├── index.js
│ │ └── modules
│ │ │ ├── auth
│ │ │ └── auth.js
│ │ │ ├── manufacturer
│ │ │ └── manufacturer.js
│ │ │ ├── product
│ │ │ ├── category.js
│ │ │ ├── option.js
│ │ │ ├── optionGroup.js
│ │ │ ├── product.js
│ │ │ └── subCategory.js
│ │ │ └── utils
│ │ │ ├── error.js
│ │ │ └── loader.js
│ └── utils
│ │ └── setAuthToken.js
└── static
│ ├── .gitkeep
│ └── favicon.ico
└── server
├── .gitignore
├── api
├── index.js
└── routes
│ ├── admin
│ ├── auth.js
│ └── product
│ │ ├── option.js
│ │ ├── optionGroup.js
│ │ ├── product.js
│ │ ├── productCategory.js
│ │ ├── productManufacturer.js
│ │ └── productSubCategory.js
│ └── client
│ ├── auth.js
│ ├── order
│ └── order.js
│ └── product
│ ├── category.js
│ ├── product.js
│ └── subCategory.js
├── app.js
├── config
├── custom-environment-variables.json
├── default.json
└── production.json
├── helpers
├── errors_response.js
├── jwt_access_token.js
└── something_error.js
├── middlewares
└── admin
│ ├── admin.js
│ └── auth.js
├── models
├── admin
│ ├── product
│ │ ├── option.js
│ │ ├── optionGroup.js
│ │ ├── product.js
│ │ ├── productCategory.js
│ │ ├── productManufacturer.js
│ │ └── productSubCategory.js
│ └── user.js
└── client
│ └── customer.js
├── package-lock.json
├── package.json
├── utils
├── cloudinary.js
├── passport.js
└── setValidationErrors.js
└── validations
├── admin
├── auth
│ ├── login.js
│ └── register.js
└── product
│ ├── option.js
│ ├── optionGroup.js
│ ├── product.js
│ ├── productCategory.js
│ ├── productManufacturer.js
│ └── productSubCategory.js
└── client
└── auth
├── login.js
└── register.js
/README.md:
--------------------------------------------------------------------------------
1 | # Ecom-backend
2 | [Live Demo](https://alphaecom.herokuapp.com)
3 |
--------------------------------------------------------------------------------
/client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/client/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/client/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 |
--------------------------------------------------------------------------------
/client/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parserOptions: {
6 | parser: 'babel-eslint'
7 | },
8 | env: {
9 | browser: true,
10 | },
11 | extends: [
12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
14 | 'plugin:vue/essential',
15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
16 | 'standard'
17 | ],
18 | // required to lint *.vue files
19 | plugins: [
20 | 'vue'
21 | ],
22 | // add your custom rules here
23 | rules: {
24 | // allow async-await
25 | 'generator-star-spacing': 'off',
26 | // allow debugger during development
27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 |
--------------------------------------------------------------------------------
/client/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # client
2 |
3 | > A Vue.js project
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 |
17 | # build for production and view the bundle analyzer report
18 | npm run build --report
19 | ```
20 |
21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
22 |
--------------------------------------------------------------------------------
/client/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/client/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | }
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 |
30 | for (let i = 0; i < versionRequirements.length; i++) {
31 | const mod = versionRequirements[i]
32 |
33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34 | warnings.push(mod.name + ': ' +
35 | chalk.red(mod.currentVersion) + ' should be ' +
36 | chalk.green(mod.versionRequirement)
37 | )
38 | }
39 | }
40 |
41 | if (warnings.length) {
42 | console.log('')
43 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
44 | console.log()
45 |
46 | for (let i = 0; i < warnings.length; i++) {
47 | const warning = warnings[i]
48 | console.log(' ' + warning)
49 | }
50 |
51 | console.log()
52 | process.exit(1)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/client/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudio-qui/node-vue-ecommerce-backend/6fd72ad226b3bc3715fd34351efc5afa9659497d/client/build/logo.png
--------------------------------------------------------------------------------
/client/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const packageConfig = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 |
12 | return path.posix.join(assetsSubDirectory, _path)
13 | }
14 |
15 | exports.cssLoaders = function (options) {
16 | options = options || {}
17 |
18 | const cssLoader = {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | const postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 |
36 | if (loader) {
37 | loaders.push({
38 | loader: loader + '-loader',
39 | options: Object.assign({}, loaderOptions, {
40 | sourceMap: options.sourceMap
41 | })
42 | })
43 | }
44 |
45 | // Extract CSS when that option is specified
46 | // (which is the case during production build)
47 | if (options.extract) {
48 | return ExtractTextPlugin.extract({
49 | use: loaders,
50 | fallback: 'vue-style-loader'
51 | })
52 | } else {
53 | return ['vue-style-loader'].concat(loaders)
54 | }
55 | }
56 |
57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 | return {
59 | css: generateLoaders(),
60 | postcss: generateLoaders(),
61 | less: generateLoaders('less'),
62 | sass: generateLoaders('sass', { indentedSyntax: true }),
63 | scss: generateLoaders('sass'),
64 | stylus: generateLoaders('stylus'),
65 | styl: generateLoaders('stylus')
66 | }
67 | }
68 |
69 | // Generate loaders for standalone style files (outside of .vue)
70 | exports.styleLoaders = function (options) {
71 | const output = []
72 | const loaders = exports.cssLoaders(options)
73 |
74 | for (const extension in loaders) {
75 | const loader = loaders[extension]
76 | output.push({
77 | test: new RegExp('\\.' + extension + '$'),
78 | use: loader
79 | })
80 | }
81 |
82 | return output
83 | }
84 |
85 | exports.createNotifierCallback = () => {
86 | const notifier = require('node-notifier')
87 |
88 | return (severity, errors) => {
89 | if (severity !== 'error') return
90 |
91 | const error = errors[0]
92 | const filename = error.file && error.file.split('!').pop()
93 |
94 | notifier.notify({
95 | title: packageConfig.name,
96 | message: severity + ': ' + error.name,
97 | subtitle: filename || '',
98 | icon: path.join(__dirname, 'logo.png')
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/client/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 | module.exports = {
10 | loaders: utils.cssLoaders({
11 | sourceMap: sourceMapEnabled,
12 | extract: isProduction
13 | }),
14 | cssSourceMap: sourceMapEnabled,
15 | cacheBusting: config.dev.cacheBusting,
16 | transformToRequire: {
17 | video: ['src', 'poster'],
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/client/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 | const createLintingRule = () => ({
12 | test: /\.(js|vue)$/,
13 | loader: 'eslint-loader',
14 | enforce: 'pre',
15 | include: [resolve('src'), resolve('test')],
16 | options: {
17 | formatter: require('eslint-friendly-formatter'),
18 | emitWarning: !config.dev.showEslintErrorsInOverlay
19 | }
20 | })
21 |
22 | module.exports = {
23 | context: path.resolve(__dirname, '../'),
24 | entry: {
25 | app: './src/main.js'
26 | },
27 | output: {
28 | path: config.build.assetsRoot,
29 | filename: '[name].js',
30 | publicPath: process.env.NODE_ENV === 'production'
31 | ? config.build.assetsPublicPath
32 | : config.dev.assetsPublicPath
33 | },
34 | resolve: {
35 | extensions: ['.js', '.vue', '.json'],
36 | alias: {
37 | 'vue$': 'vue/dist/vue.esm.js',
38 | '@': resolve('src'),
39 | }
40 | },
41 | module: {
42 | rules: [
43 | ...(config.dev.useEslint ? [createLintingRule()] : []),
44 | {
45 | test: /\.vue$/,
46 | loader: 'vue-loader',
47 | options: vueLoaderConfig
48 | },
49 | {
50 | test: /\.js$/,
51 | loader: 'babel-loader',
52 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
53 | },
54 | {
55 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
56 | loader: 'url-loader',
57 | options: {
58 | limit: 10000,
59 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
60 | }
61 | },
62 | {
63 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
64 | loader: 'url-loader',
65 | options: {
66 | limit: 10000,
67 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
68 | }
69 | },
70 | {
71 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
72 | loader: 'url-loader',
73 | options: {
74 | limit: 10000,
75 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
76 | }
77 | }
78 | ]
79 | },
80 | node: {
81 | // prevent webpack from injecting useless setImmediate polyfill because Vue
82 | // source contains it (although only uses it if it's native).
83 | setImmediate: false,
84 | // prevent webpack from injecting mocks to Node native modules
85 | // that does not make sense for the client
86 | dgram: 'empty',
87 | fs: 'empty',
88 | net: 'empty',
89 | tls: 'empty',
90 | child_process: 'empty'
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/client/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const path = require('path')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
11 | const portfinder = require('portfinder')
12 |
13 | const HOST = process.env.HOST
14 | const PORT = process.env.PORT && Number(process.env.PORT)
15 |
16 | const devWebpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
19 | },
20 | // cheap-module-eval-source-map is faster for development
21 | devtool: config.dev.devtool,
22 |
23 | // these devServer options should be customized in /config/index.js
24 | devServer: {
25 | clientLogLevel: 'warning',
26 | historyApiFallback: {
27 | rewrites: [
28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
29 | ],
30 | },
31 | hot: true,
32 | contentBase: false, // since we use CopyWebpackPlugin.
33 | compress: true,
34 | host: HOST || config.dev.host,
35 | port: PORT || config.dev.port,
36 | open: config.dev.autoOpenBrowser,
37 | overlay: config.dev.errorOverlay
38 | ? { warnings: false, errors: true }
39 | : false,
40 | publicPath: config.dev.assetsPublicPath,
41 | proxy: config.dev.proxyTable,
42 | quiet: true, // necessary for FriendlyErrorsPlugin
43 | watchOptions: {
44 | poll: config.dev.poll,
45 | }
46 | },
47 | plugins: [
48 | new webpack.DefinePlugin({
49 | 'process.env': require('../config/dev.env')
50 | }),
51 | new webpack.HotModuleReplacementPlugin(),
52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
53 | new webpack.NoEmitOnErrorsPlugin(),
54 | // https://github.com/ampedandwired/html-webpack-plugin
55 | new HtmlWebpackPlugin({
56 | filename: 'index.html',
57 | template: 'index.html',
58 | inject: true
59 | }),
60 | // copy custom static assets
61 | new CopyWebpackPlugin([
62 | {
63 | from: path.resolve(__dirname, '../static'),
64 | to: config.dev.assetsSubDirectory,
65 | ignore: ['.*']
66 | }
67 | ])
68 | ]
69 | })
70 |
71 | module.exports = new Promise((resolve, reject) => {
72 | portfinder.basePort = process.env.PORT || config.dev.port
73 | portfinder.getPort((err, port) => {
74 | if (err) {
75 | reject(err)
76 | } else {
77 | // publish the new Port, necessary for e2e tests
78 | process.env.PORT = port
79 | // add port to devServer config
80 | devWebpackConfig.devServer.port = port
81 |
82 | // Add FriendlyErrorsPlugin
83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
84 | compilationSuccessInfo: {
85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
86 | },
87 | onErrors: config.dev.notifyOnErrors
88 | ? utils.createNotifierCallback()
89 | : undefined
90 | }))
91 |
92 | resolve(devWebpackConfig)
93 | }
94 | })
95 | })
96 |
--------------------------------------------------------------------------------
/client/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
13 |
14 | const env = require('../config/prod.env')
15 |
16 | const webpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({
19 | sourceMap: config.build.productionSourceMap,
20 | extract: true,
21 | usePostCSS: true
22 | })
23 | },
24 | devtool: config.build.productionSourceMap ? config.build.devtool : false,
25 | output: {
26 | path: config.build.assetsRoot,
27 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
29 | },
30 | plugins: [
31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
32 | new webpack.DefinePlugin({
33 | 'process.env': env
34 | }),
35 | new UglifyJsPlugin({
36 | uglifyOptions: {
37 | compress: {
38 | warnings: false
39 | }
40 | },
41 | sourceMap: config.build.productionSourceMap,
42 | parallel: true
43 | }),
44 | // extract css into its own file
45 | new ExtractTextPlugin({
46 | filename: utils.assetsPath('css/[name].[contenthash].css'),
47 | // Setting the following option to `false` will not extract CSS from codesplit chunks.
48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
51 | allChunks: true,
52 | }),
53 | // Compress extracted CSS. We are using this plugin so that possible
54 | // duplicated CSS from different components can be deduped.
55 | new OptimizeCSSPlugin({
56 | cssProcessorOptions: config.build.productionSourceMap
57 | ? { safe: true, map: { inline: false } }
58 | : { safe: true }
59 | }),
60 | // generate dist index.html with correct asset hash for caching.
61 | // you can customize output by editing /index.html
62 | // see https://github.com/ampedandwired/html-webpack-plugin
63 | new HtmlWebpackPlugin({
64 | filename: config.build.index,
65 | template: 'index.html',
66 | inject: true,
67 | minify: {
68 | removeComments: true,
69 | collapseWhitespace: true,
70 | removeAttributeQuotes: true
71 | // more options:
72 | // https://github.com/kangax/html-minifier#options-quick-reference
73 | },
74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
75 | chunksSortMode: 'dependency'
76 | }),
77 | // keep module.id stable when vendor modules does not change
78 | new webpack.HashedModuleIdsPlugin(),
79 | // enable scope hoisting
80 | new webpack.optimize.ModuleConcatenationPlugin(),
81 | // split vendor js into its own file
82 | new webpack.optimize.CommonsChunkPlugin({
83 | name: 'vendor',
84 | minChunks (module) {
85 | // any required modules inside node_modules are extracted to vendor
86 | return (
87 | module.resource &&
88 | /\.js$/.test(module.resource) &&
89 | module.resource.indexOf(
90 | path.join(__dirname, '../node_modules')
91 | ) === 0
92 | )
93 | }
94 | }),
95 | // extract webpack runtime and module manifest to its own file in order to
96 | // prevent vendor hash from being updated whenever app bundle is updated
97 | new webpack.optimize.CommonsChunkPlugin({
98 | name: 'manifest',
99 | minChunks: Infinity
100 | }),
101 | // This instance extracts shared chunks from code splitted chunks and bundles them
102 | // in a separate chunk, similar to the vendor chunk
103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
104 | new webpack.optimize.CommonsChunkPlugin({
105 | name: 'app',
106 | async: 'vendor-async',
107 | children: true,
108 | minChunks: 3
109 | }),
110 |
111 | // copy custom static assets
112 | new CopyWebpackPlugin([
113 | {
114 | from: path.resolve(__dirname, '../static'),
115 | to: config.build.assetsSubDirectory,
116 | ignore: ['.*']
117 | }
118 | ])
119 | ]
120 | })
121 |
122 | if (config.build.productionGzip) {
123 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
124 |
125 | webpackConfig.plugins.push(
126 | new CompressionWebpackPlugin({
127 | asset: '[path].gz[query]',
128 | algorithm: 'gzip',
129 | test: new RegExp(
130 | '\\.(' +
131 | config.build.productionGzipExtensions.join('|') +
132 | ')$'
133 | ),
134 | threshold: 10240,
135 | minRatio: 0.8
136 | })
137 | )
138 | }
139 |
140 | if (config.build.bundleAnalyzerReport) {
141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
143 | }
144 |
145 | module.exports = webpackConfig
146 |
--------------------------------------------------------------------------------
/client/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/client/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 | // Use Eslint Loader?
24 | // If true, your code will be linted during bundling and
25 | // linting errors and warnings will be shown in the console.
26 | useEslint: true,
27 | // If true, eslint errors and warnings will also be shown in the error overlay
28 | // in the browser.
29 | showEslintErrorsInOverlay: false,
30 |
31 | /**
32 | * Source Maps
33 | */
34 |
35 | // https://webpack.js.org/configuration/devtool/#development
36 | devtool: 'cheap-module-eval-source-map',
37 |
38 | // If you have problems debugging vue-files in devtools,
39 | // set this to false - it *may* help
40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
41 | cacheBusting: true,
42 |
43 | cssSourceMap: true
44 | },
45 |
46 | build: {
47 | // Template for index.html
48 | index: path.resolve(__dirname, '../dist/index.html'),
49 |
50 | // Paths
51 | assetsRoot: path.resolve(__dirname, '../dist'),
52 | assetsSubDirectory: 'static',
53 | assetsPublicPath: '/',
54 |
55 | /**
56 | * Source Maps
57 | */
58 |
59 | productionSourceMap: true,
60 | // https://webpack.js.org/configuration/devtool/#production
61 | devtool: '#source-map',
62 |
63 | // Gzip off by default as many popular static hosts such as
64 | // Surge or Netlify already gzip all static assets for you.
65 | // Before setting to `true`, make sure to:
66 | // npm install --save-dev compression-webpack-plugin
67 | productionGzip: false,
68 | productionGzipExtensions: ['js', 'css'],
69 |
70 | // Run the build command with an extra argument to
71 | // View the bundle analyzer report after build finishes:
72 | // `npm run build --report`
73 | // Set to `true` or `false` to always turn it on or off
74 | bundleAnalyzerReport: process.env.npm_config_report
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/client/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Alpha Ecom
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "Mir HB Rahman ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "lint": "eslint --ext .js,.vue src",
11 | "build": "node build/build.js"
12 | },
13 | "dependencies": {
14 | "axios": "^0.18.0",
15 | "vue": "^2.5.2",
16 | "vue-jwt-decode": "^0.1.0",
17 | "vue-loading-overlay": "^3.2.0",
18 | "vue-moment": "^4.0.0",
19 | "vue-router": "^3.0.1",
20 | "vuetify": "^1.5.7",
21 | "vuex": "^3.1.0"
22 | },
23 | "devDependencies": {
24 | "autoprefixer": "^7.1.2",
25 | "babel-core": "^6.22.1",
26 | "babel-eslint": "^8.2.1",
27 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
28 | "babel-loader": "^7.1.1",
29 | "babel-plugin-syntax-jsx": "^6.18.0",
30 | "babel-plugin-transform-runtime": "^6.22.0",
31 | "babel-plugin-transform-vue-jsx": "^3.5.0",
32 | "babel-preset-env": "^1.3.2",
33 | "babel-preset-stage-2": "^6.22.0",
34 | "chalk": "^2.0.1",
35 | "copy-webpack-plugin": "^4.0.1",
36 | "css-loader": "^0.28.0",
37 | "eslint": "^4.15.0",
38 | "eslint-config-standard": "^10.2.1",
39 | "eslint-friendly-formatter": "^3.0.0",
40 | "eslint-loader": "^1.7.1",
41 | "eslint-plugin-import": "^2.7.0",
42 | "eslint-plugin-node": "^5.2.0",
43 | "eslint-plugin-promise": "^3.4.0",
44 | "eslint-plugin-standard": "^3.0.1",
45 | "eslint-plugin-vue": "^4.0.0",
46 | "extract-text-webpack-plugin": "^3.0.0",
47 | "file-loader": "^1.1.4",
48 | "friendly-errors-webpack-plugin": "^1.6.1",
49 | "html-webpack-plugin": "^2.30.1",
50 | "node-notifier": "^5.1.2",
51 | "optimize-css-assets-webpack-plugin": "^3.2.0",
52 | "ora": "^1.2.0",
53 | "portfinder": "^1.0.13",
54 | "postcss-import": "^11.0.0",
55 | "postcss-loader": "^2.0.8",
56 | "postcss-url": "^7.2.1",
57 | "rimraf": "^2.6.0",
58 | "semver": "^5.3.0",
59 | "shelljs": "^0.7.6",
60 | "uglifyjs-webpack-plugin": "^1.1.1",
61 | "url-loader": "^0.5.8",
62 | "vue-loader": "^13.3.0",
63 | "vue-style-loader": "^3.0.1",
64 | "vue-template-compiler": "^2.5.2",
65 | "webpack": "^3.6.0",
66 | "webpack-bundle-analyzer": "^2.9.0",
67 | "webpack-dev-server": "^2.9.1",
68 | "webpack-merge": "^4.1.0"
69 | },
70 | "engines": {
71 | "node": ">= 6.0.0",
72 | "npm": ">= 3.0.0"
73 | },
74 | "browserslist": [
75 | "> 1%",
76 | "last 2 versions",
77 | "not ie <= 8"
78 | ]
79 | }
80 |
--------------------------------------------------------------------------------
/client/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
26 |
27 |
42 |
--------------------------------------------------------------------------------
/client/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudio-qui/node-vue-ecommerce-backend/6fd72ad226b3bc3715fd34351efc5afa9659497d/client/src/assets/logo.png
--------------------------------------------------------------------------------
/client/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
Essential Links
5 |
48 |
Ecosystem
49 |
83 |
84 |
85 |
86 |
96 |
97 |
98 |
114 |
--------------------------------------------------------------------------------
/client/src/components/auth/LoginUser.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Login
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
{{ errors.email}}
25 |
26 |
27 |
28 |
36 |
37 |
38 |
39 |
{{ errors.password}}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Login
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
95 |
96 |
98 |
--------------------------------------------------------------------------------
/client/src/components/dash/UserDash.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Dashboard
5 |
6 |
7 |
8 |
9 |
20 |
21 |
24 |
--------------------------------------------------------------------------------
/client/src/components/layouts/AppLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ item.heading }}
9 |
10 |
11 | EDIT
12 |
13 |
14 |
21 |
22 |
23 |
24 | {{ item.text }}
25 |
26 |
27 |
28 |
29 |
30 | {{ child.icon }}
31 |
32 |
33 | {{ child.text }}
34 |
35 |
36 |
37 |
38 |
39 | {{ item.icon }}
40 |
41 |
42 | {{ item.text }}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | Dashboard
52 |
53 |
61 |
62 |
63 | apps
64 |
65 |
66 | notifications
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
171 |
172 |
177 |
--------------------------------------------------------------------------------
/client/src/components/product/option/ProductOptionGroupList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Option Group
9 |
10 |
17 |
18 |
19 |
20 | {{ props.item.name |ucWords }} |
21 | {{ props.item.created_at | moment("dddd, MMMM Do YYYY") }} |
22 |
23 |
24 |
25 |
26 | edit
27 |
28 |
29 |
30 |
31 | delete
36 |
37 |
38 |
39 | |
40 |
41 | Your search for "{{ search }}" found no results.
47 |
48 |
49 |
50 |
51 |
52 | add
53 |
54 |
55 |
56 | Create Option Group
57 |
58 |
59 |
60 |
66 |
67 |
68 |
69 |
{{ errors.name}}
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | Cancel
79 | Save
80 |
81 |
82 |
83 |
84 |
85 |
86 | Update Option Group
87 |
88 |
89 |
90 | {{updateOptionGroupName}}
96 |
97 |
98 |
99 |
{{ errors.name}}
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | Cancel
109 | Update
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
221 |
222 |
224 |
--------------------------------------------------------------------------------
/client/src/components/product/option/ProductOptionList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Options
9 |
10 |
17 |
18 |
19 |
20 | {{ props.item.optionGroup.name | ucWords }} |
21 | {{ props.item.name }} |
22 | {{ props.item.created_at | moment("dddd, MMMM Do YYYY") }} |
23 |
24 |
25 |
26 |
27 | edit
28 |
29 |
30 |
31 |
32 | delete
37 |
38 |
39 |
40 | |
41 |
42 | Your search for "{{ search }}" found no results.
48 |
49 |
50 |
51 |
52 |
53 | add
54 |
55 |
56 |
57 | Create Option
58 |
59 |
60 |
61 |
70 |
71 |
72 |
73 |
{{ errors.name}}
74 |
75 |
76 |
77 |
78 |
79 |
85 |
86 |
87 |
88 |
{{ errors.name}}
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | Cancel
98 | Save
99 |
100 |
101 |
102 |
103 |
104 |
105 | Update Option
106 |
107 |
108 |
109 |
118 |
119 |
120 |
121 |
{{ errors.name}}
122 |
123 |
124 |
125 |
126 |
127 | {{updateOptionName}}
133 |
134 |
135 |
136 |
{{ errors.name}}
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | Cancel
146 | Update
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
281 |
282 |
284 |
--------------------------------------------------------------------------------
/client/src/components/product/product/ProductAttribute.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Add Product Attribute
10 |
11 |
12 |
13 |
14 |
23 |
24 |
25 |
26 |
{{ errors.name }}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
42 |
43 |
44 |
45 |
{{ errors.name }}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | Submit
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Attributes
66 |
67 |
68 |
69 |
70 |
71 | {{ props.item.optionGroupName | ucWords}} |
72 |
73 | {{option.name}},
74 | |
75 |
76 |
77 |
78 |
79 | delete
84 |
85 |
86 |
87 | |
88 |
89 | Your search for "{{ search }}" found no results.
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
190 |
191 |
193 |
--------------------------------------------------------------------------------
/client/src/components/product/product/ProductImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
34 |
35 |
36 |
37 | Images
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
61 | |
62 |
63 |
64 |
65 |
66 | delete
71 |
72 |
73 |
74 | |
75 |
76 | Your search for "{{ search }}" found no results.
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
173 |
174 |
176 |
--------------------------------------------------------------------------------
/client/src/components/product/product/ProductList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Products
10 |
11 |
18 |
19 |
26 |
27 |
28 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | |
43 | 😢 |
44 | {{ props.item.name | ucWords }} |
45 |
46 |
47 |
48 | SKU:
49 | {{ props.item.sku }}
50 |
51 |
52 | Category:
53 | {{ props.item.category.name }}
54 |
55 |
56 | Sub Category:
57 | {{ props.item.subCategory.name }}
58 |
59 |
60 | Price($):
61 | {{ props.item.price }}
62 |
63 |
64 | |
65 |
66 |
67 |
68 | Created:
69 | {{ props.item.created_at | moment("dddd, MMMM Do YYYY") }}
70 |
71 |
72 |
73 |
74 |
75 | Flash Price($):
76 | {{ props.item.flashSale.flashPrice }}
77 |
78 |
79 | Start:
80 | {{ props.item.flashSale.flashStart }}
81 |
82 |
83 | End:
84 | {{ props.item.flashSale.flashEnd }}
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | Flash Price($):
95 | {{ props.item.specialSale.specialPrice }}
96 |
97 |
98 | Start:
99 | {{ props.item.specialSale.specailExpire }}
100 |
101 |
102 |
103 |
104 | |
105 |
106 |
107 |
108 |
109 |
110 |
117 | list
118 |
119 |
120 |
121 |
126 | {{ item.title }}
127 |
128 |
129 |
130 |
131 |
132 | |
133 |
134 | Your search for "{{ search }}" found no results.
140 |
141 |
142 |
143 |
144 |
145 | add
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | Ok
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
257 |
258 |
280 |
--------------------------------------------------------------------------------
/client/src/components/product/section/ProductCategoryList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Category
9 |
10 |
17 |
18 |
19 |
20 | {{ props.item.name | ucWords}} |
21 | {{ props.item.created_at | moment("dddd, MMMM Do YYYY") }} |
22 |
23 |
24 |
25 |
26 | edit
27 |
28 |
29 |
30 |
31 | delete
36 |
37 |
38 |
39 | |
40 |
41 | Your search for "{{ search }}" found no results.
47 |
48 |
49 |
50 |
51 |
52 | add
53 |
54 |
55 |
56 | Create Category
57 |
58 |
59 |
60 |
66 |
67 |
68 |
69 |
{{ errors.name}}
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | Cancel
79 | Save
80 |
81 |
82 |
83 |
84 |
85 |
86 | Update Category
87 |
88 |
89 |
90 | {{updateCategoryName}}
96 |
97 |
98 |
99 |
{{ errors.name}}
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | Cancel
109 | Update
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
221 |
222 |
224 |
--------------------------------------------------------------------------------
/client/src/components/product/section/ProductSubCategoryList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Sub Category
9 |
10 |
17 |
18 |
19 |
20 | {{ props.item.category.name | ucWords }} |
21 | {{ props.item.name | ucWords}} |
22 | {{ props.item.created_at | moment("dddd, MMMM Do YYYY") }} |
23 |
24 |
25 |
26 |
27 | edit
28 |
29 |
30 |
31 |
32 | delete
37 |
38 |
39 |
40 | |
41 |
42 | Your search for "{{ search }}" found no results.
48 |
49 |
50 |
51 |
52 |
53 | add
54 |
55 |
56 |
57 | Create Sub Category
58 |
59 |
60 |
61 |
70 |
71 |
72 |
73 |
{{ errors.name}}
74 |
75 |
76 |
77 |
78 |
79 |
85 |
86 |
87 |
88 |
{{ errors.name}}
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | Cancel
98 | Save
99 |
100 |
101 |
102 |
103 |
104 |
105 | Update Sub Category
106 |
107 |
108 |
109 |
118 |
119 |
120 |
121 |
{{ errors.name}}
122 |
123 |
124 |
125 |
126 |
127 | {{updateSubCategoryName}}
133 |
134 |
135 |
136 |
{{ errors.name}}
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | Cancel
146 | Update
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
281 |
282 |
284 |
--------------------------------------------------------------------------------
/client/src/components/utils/Loader.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
13 | Please stand by
14 |
19 |
20 |
21 |
22 |
23 |
24 |
34 |
35 |
38 |
--------------------------------------------------------------------------------
/client/src/helpers/ucWords.js:
--------------------------------------------------------------------------------
1 | module.exports = (str) => {
2 | let text = str.toLowerCase()
3 | .split(' ')
4 | .map((s) => s.charAt(0).toUpperCase() + s.substring(1))
5 | .join(' ')
6 |
7 | return text
8 | }
9 |
--------------------------------------------------------------------------------
/client/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import axios from 'axios'
6 | import router from './router'
7 | import store from './store'
8 | import jwtDecode from 'vue-jwt-decode'
9 | import setAuthToken from './utils/setAuthToken'
10 |
11 | Vue.use(require('vue-moment'))
12 |
13 | Vue.config.productionTip = false
14 |
15 | // Setup axios to be available globally through Vue
16 | Vue.axios = Vue.prototype.$http = axios.create({
17 | baseURL: 'http://localhost:3000/api'
18 | })
19 |
20 | // Check for token
21 | if (localStorage.getItem('jwt')) {
22 | // Getting token form local storage
23 | const bearerToken = localStorage.getItem('jwt')
24 | // Set auth token to header auth
25 | setAuthToken(bearerToken)
26 | const tokenArray = bearerToken.split(' ')
27 | const token = tokenArray[1]
28 | // Decoded token
29 | const decoded = jwtDecode.decode(token)
30 | // Check for expired token
31 | const currentTime = Date.now() / 1000
32 | if (decoded.exp < currentTime) {
33 | // Logout user
34 | store.dispatch('auth/logout')
35 | // Redirect to login
36 | router.push({name: 'loginUser'})
37 | }
38 | }
39 |
40 | /* eslint-disable no-new */
41 | new Vue({
42 | el: '#app',
43 | router,
44 | store,
45 | components: { App },
46 | template: ''
47 | })
48 |
--------------------------------------------------------------------------------
/client/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import LoginUser from '@/components/auth/LoginUser'
4 | import UserDash from '@/components/dash/UserDash'
5 | import ProductCategoryList from '@/components/product/section/ProductCategoryList'
6 | import ManufacturerList from '@/components/product/manufacturer/ManufacturerList'
7 | import ProductSubCategoryList from '@/components/product/section/ProductSubCategoryList'
8 | import ProductOptionGroup from '@/components/product/option/ProductOptionGroupList'
9 | import ProductOption from '@/components/product/option/ProductOptionList'
10 | import ProductCreate from '@/components/product/product/ProductCreate'
11 | import ProductList from '@/components/product/product/ProductList'
12 | import ProductAttribute from '@/components/product/product/ProductAttribute'
13 | import ProductImage from '@/components/product/product/ProductImage'
14 |
15 | Vue.use(Router)
16 |
17 | let router = new Router({
18 | mode: 'history',
19 | routes: [
20 | {
21 | path: '/',
22 | name: 'LoginUser',
23 | component: LoginUser,
24 | meta: {
25 | guest: true
26 | }
27 | },
28 | {
29 | path: '/login',
30 | name: 'loginUser',
31 | component: LoginUser,
32 | meta: {
33 | guest: true
34 | }
35 | },
36 | {
37 | path: '/dashboard',
38 | name: 'userDashboard',
39 | component: UserDash,
40 | meta: {
41 | requiresAuth: true
42 | }
43 | },
44 | {
45 | path: '/product/categories',
46 | name: 'productCategoryList',
47 | component: ProductCategoryList,
48 | meta: {
49 | requiresAuth: true
50 | }
51 | },
52 | {
53 | path: '/product/sub-categories',
54 | name: 'productSubCategoryList',
55 | component: ProductSubCategoryList,
56 | meta: {
57 | requiresAuth: true
58 | }
59 | },
60 | {
61 | path: '/product/option-groups',
62 | name: 'productOptionGroupList',
63 | component: ProductOptionGroup,
64 | meta: {
65 | requiresAuth: true
66 | }
67 | },
68 | {
69 | path: '/product/options',
70 | name: 'productOptionList',
71 | component: ProductOption,
72 | meta: {
73 | requiresAuth: true
74 | }
75 | },
76 | {
77 | path: '/product/manufacturers',
78 | name: 'manufacturerList',
79 | component: ManufacturerList,
80 | meta: {
81 | requiresAuth: true
82 | }
83 | },
84 | {
85 | path: '/products/create',
86 | name: 'productCreate',
87 | component: ProductCreate,
88 | meta: {
89 | requiresAuth: true
90 | }
91 | },
92 | {
93 | path: '/products',
94 | name: 'productList',
95 | component: ProductList,
96 | meta: {
97 | requiresAuth: true
98 | }
99 | },
100 | {
101 | path: '/products/attributes/:productId',
102 | name: 'productAttribute',
103 | component: ProductAttribute,
104 | props: true,
105 | meta: {
106 | requiresAuth: true
107 | }
108 | },
109 | {
110 | path: '/products/images/:productId',
111 | name: 'productImage',
112 | component: ProductImage,
113 | props: true,
114 | meta: {
115 | requiresAuth: true
116 | }
117 | }
118 | ]
119 | })
120 |
121 | // check route meta
122 | require('./metaCheck')(router)
123 |
124 | export default router
125 |
--------------------------------------------------------------------------------
/client/src/router/metaCheck.js:
--------------------------------------------------------------------------------
1 | module.exports = (router) => {
2 | router.beforeEach((to, from, next) => {
3 | if (to.matched.some(record => record.meta.guest)) {
4 | if (localStorage.getItem('jwt') == null) {
5 | next()
6 | } else {
7 | next({name: 'userDashboard'})
8 | }
9 | } else if (to.matched.some(record => record.meta.requiresAuth)) {
10 | if (localStorage.getItem('jwt') != null) {
11 | next()
12 | } else {
13 | next({name: 'loginUser'})
14 | }
15 | } else {
16 | next()
17 | }
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/client/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import error from './modules/utils/error'
4 | import loader from './modules/utils/loader'
5 | import auth from './modules/auth/auth'
6 | import productCategory from './modules/product/category'
7 | import manufacturer from './modules/manufacturer/manufacturer'
8 | import productSubCategory from './modules/product/subCategory'
9 | import productOptionGroup from './modules/product/optionGroup'
10 | import productOption from './modules/product/option'
11 | import product from './modules/product/product'
12 |
13 | Vue.use(Vuex)
14 |
15 | export default new Vuex.Store({
16 | modules: {
17 | error,
18 | loader,
19 | auth,
20 | manufacturer,
21 | productCategory,
22 | productSubCategory,
23 | productOptionGroup,
24 | productOption,
25 | product
26 | }
27 | })
28 |
--------------------------------------------------------------------------------
/client/src/store/modules/auth/auth.js:
--------------------------------------------------------------------------------
1 | import router from '../../../router'
2 | import setAuthToken from '../../../utils/setAuthToken'
3 | import Vue from 'vue'
4 | // initial state
5 | const state = {
6 | user: {},
7 | isLoggedIn: false,
8 | errors: {},
9 | isError: false
10 | }
11 |
12 | // getters
13 | const getters = {
14 | isError (state) {
15 | return state.isError
16 | },
17 | getErrors (state) {
18 | return state.errors
19 | }
20 | }
21 |
22 | // actions
23 | const actions = {
24 | login ({commit}, payload) {
25 | Vue.axios.post('/admin/login', payload)
26 | .then(response => {
27 | if (response.data.success) {
28 | commit('loginUser', response.data.user)
29 | // Set token to local storage
30 | localStorage.setItem('jwt', response.data.token)
31 | // Set auth token to header auth
32 | setAuthToken(localStorage.getItem('jwt'))
33 | router.push({name: 'userDashboard'})
34 | }
35 | })
36 | .catch(err => {
37 | commit('setErrors', err.response.data)
38 | })
39 | },
40 | logout ({commit}) {
41 | if (localStorage.getItem('jwt')) {
42 | localStorage.removeItem('jwt')
43 | commit('clearCurrentUser')
44 | router.push({name: 'loginUser'})
45 | }
46 | }
47 | }
48 |
49 | // mutations
50 | const mutations = {
51 | setErrors (state, errors) {
52 | state.errors = errors
53 | state.isError = true
54 | },
55 | loginUser (state, user) {
56 | state.user = user
57 | state.isLoggedIn = true
58 | // Clear error
59 | state.isError = false
60 | state.errors = {}
61 | },
62 | clearCurrentUser (state) {
63 | state.user = null
64 | state.isLoggedIn = false
65 | }
66 | }
67 |
68 | export default {
69 | namespaced: true,
70 | state,
71 | getters,
72 | actions,
73 | mutations
74 | }
75 |
--------------------------------------------------------------------------------
/client/src/store/modules/manufacturer/manufacturer.js:
--------------------------------------------------------------------------------
1 | import store from '../../../store'
2 | import Vue from 'vue'
3 | // Initial state
4 | const state = {
5 | manufacturers: []
6 | }
7 |
8 | // Getters
9 | const getters = {
10 | getManufacturers (state) {
11 | return state.manufacturers
12 | }
13 | }
14 |
15 | // Actions
16 | const actions = {
17 | getManufacturers ({ commit }) {
18 | // Active preloader
19 | store.dispatch('loader/activeLoader')
20 | // Make server request
21 | Vue.axios.get('/admin/product/manufacturers')
22 | .then(response => {
23 | if (response.data.success) {
24 | // Store to state
25 | commit('setmanufacturers', response.data.manufacturers)
26 | // Deactive loader
27 | store.dispatch('loader/deactiveLoader')
28 | // Clear error
29 | store.dispatch('error/clearErrors')
30 | }
31 | })
32 | .catch(err => {
33 | // Deactive loader
34 | store.dispatch('loader/deactiveLoader')
35 | // Handle all error by error state
36 | store.dispatch('error/setErrors', err.response.data)
37 | })
38 | },
39 | createManufacturer ({ commit }, payload) {
40 | // Active preloader
41 | store.dispatch('loader/activeLoader')
42 | // Make form data
43 | const fd = new FormData()
44 | fd.append('name', payload.name)
45 | fd.append('url', payload.url)
46 | fd.append('image', payload.image)
47 | // Make server request
48 | Vue.axios.post('/admin/product/manufacturers', fd)
49 | .then(response => {
50 | if (response.data.success) {
51 | commit('createManufacturer', response.data.manufacturer)
52 | // Deactive loader
53 | store.dispatch('loader/deactiveLoader')
54 | // Clear error
55 | store.dispatch('error/clearErrors')
56 | }
57 | })
58 | .catch(err => {
59 | // Deactive loader
60 | store.dispatch('loader/deactiveLoader')
61 | // Handle all error by error state
62 | store.dispatch('error/setErrors', err.response.data)
63 | })
64 | },
65 | updateManufacturer ({ commit }, payload) {
66 | // Active preloader
67 | store.dispatch('loader/activeLoader')
68 | // Make form data
69 | const fd = new FormData()
70 | fd.append('name', payload.data.name)
71 | fd.append('url', payload.data.url)
72 | fd.append('image', payload.data.image)
73 | // Make server request
74 | store.dispatch('loader/activeLoader')
75 | Vue.axios.put('/admin/product/manufacturers/' + payload._id, fd)
76 | .then(response => {
77 | if (response.data.success) {
78 | commit('updateManufacturer', response.data.manufacturer)
79 | // Deactive loader
80 | store.dispatch('loader/deactiveLoader')
81 | // Clear error
82 | store.dispatch('error/clearErrors')
83 | }
84 | })
85 | .catch(err => {
86 | // Deactive loader
87 | store.dispatch('loader/deactiveLoader')
88 | // Handle all error by error state
89 | store.dispatch('error/setErrors', err.response.data)
90 | })
91 | },
92 | deleteManufacturer ({ commit }, payload) {
93 | // Active preloader
94 | store.dispatch('loader/activeLoader')
95 | // Make server request
96 | Vue.axios.delete('/admin/product/manufacturers/' + payload._id)
97 | .then(response => {
98 | if (response.data.success) {
99 | commit('deleteManufacturer', response.data.manufacturer)
100 | // Deactive loader
101 | store.dispatch('loader/deactiveLoader')
102 | // Clear error
103 | store.dispatch('error/clearErrors')
104 | }
105 | })
106 | .catch(err => {
107 | // Deactive loader
108 | store.dispatch('loader/deactiveLoader')
109 | // Handle all error by error state
110 | store.dispatch('error/setErrors', err.response.data)
111 | })
112 | }
113 | }
114 |
115 | // Mutations
116 | const mutations = {
117 | setmanufacturers (state, manufacturers) {
118 | state.manufacturers = manufacturers
119 | },
120 | createManufacturer (state, manufacturer) {
121 | // Push new data to state
122 | state.manufacturers.push(manufacturer)
123 | // Clear error
124 | store.dispatch('error/clearErrors')
125 | },
126 | updateManufacturer (state, manufacturer) {
127 | state.manufacturers = state.manufacturers.map(cat => cat._id === manufacturer._id ? (cat = manufacturer) : cat)
128 | // Clear error
129 | store.dispatch('error/clearErrors')
130 | },
131 | deleteManufacturer (state, manufacturer) {
132 | state.manufacturers = state.manufacturers.filter(cat => cat._id !== manufacturer._id)
133 | }
134 | }
135 |
136 | export default {
137 | namespaced: true,
138 | state,
139 | getters,
140 | actions,
141 | mutations
142 | }
143 |
--------------------------------------------------------------------------------
/client/src/store/modules/product/category.js:
--------------------------------------------------------------------------------
1 | import store from '../../../store'
2 | import Vue from 'vue'
3 | // Initial state
4 | const state = {
5 | categories: [],
6 | subCategories: []
7 | }
8 |
9 | // Getters
10 | const getters = {
11 | getCategories (state) {
12 | return state.categories
13 | },
14 | getSubCategoriesByCategory (state) {
15 | return state.subCategories
16 | }
17 | }
18 |
19 | // Actions
20 | const actions = {
21 | getCategories ({ commit }) {
22 | // Active preloader
23 | store.dispatch('loader/activeLoader')
24 | // Make server request
25 | Vue.axios.get('/admin/product/categories')
26 | .then(response => {
27 | if (response.data.success) {
28 | // Store to state
29 | commit('setCategories', response.data.categories)
30 | // Deactive loader
31 | store.dispatch('loader/deactiveLoader')
32 | // Clear error
33 | store.dispatch('error/clearErrors')
34 | }
35 | })
36 | .catch(err => {
37 | // Deactive loader
38 | store.dispatch('loader/deactiveLoader')
39 | // Handle all error by error state
40 | store.dispatch('error/setErrors', err.response.data)
41 | })
42 | },
43 | getSubCategoriesByCategory ({ commit }, category) {
44 | // Active preloader
45 | store.dispatch('loader/activeLoader')
46 | // Make server request
47 | Vue.axios.get(`/admin/product/categories/${category._id}/sub-categories`)
48 | .then(response => {
49 | if (response.data.success) {
50 | // Store to state
51 | commit('setSubCategories', response.data.subCategories)
52 | // Deactive loader
53 | store.dispatch('loader/deactiveLoader')
54 | // Clear error
55 | store.dispatch('error/clearErrors')
56 | }
57 | })
58 | .catch(err => {
59 | // Deactive loader
60 | store.dispatch('loader/deactiveLoader')
61 | // Handle all error by error state
62 | store.dispatch('error/setErrors', err.response.data)
63 | })
64 | },
65 | createCategory ({ commit }, payload) {
66 | // Active preloader
67 | store.dispatch('loader/activeLoader')
68 | // Make server request
69 | Vue.axios.post('/admin/product/categories', payload)
70 | .then(response => {
71 | if (response.data.success) {
72 | commit('createCategory', response.data.category)
73 | // Deactive loader
74 | store.dispatch('loader/deactiveLoader')
75 | // Clear error
76 | store.dispatch('error/clearErrors')
77 | }
78 | })
79 | .catch(err => {
80 | // Deactive loader
81 | store.dispatch('loader/deactiveLoader')
82 | // Handle all error by error state
83 | store.dispatch('error/setErrors', err.response.data)
84 | })
85 | },
86 | updateCategory ({ commit }, payload) {
87 | // Active preloader
88 | store.dispatch('loader/activeLoader')
89 | // Make server request
90 | store.dispatch('loader/activeLoader')
91 | Vue.axios.put('/admin/product/categories/' + payload._id, payload.data)
92 | .then(response => {
93 | if (response.data.success) {
94 | commit('updateCategory', response.data.category)
95 | // Deactive loader
96 | store.dispatch('loader/deactiveLoader')
97 | // Clear error
98 | store.dispatch('error/clearErrors')
99 | }
100 | })
101 | .catch(err => {
102 | // Deactive loader
103 | store.dispatch('loader/deactiveLoader')
104 | // Handle all error by error state
105 | store.dispatch('error/setErrors', err.response.data)
106 | })
107 | },
108 | deleteCategory ({ commit }, payload) {
109 | // Active preloader
110 | store.dispatch('loader/activeLoader')
111 | // Make server request
112 | Vue.axios.delete('/admin/product/categories/' + payload._id)
113 | .then(response => {
114 | if (response.data.success) {
115 | commit('deleteCategory', response.data.category)
116 | // Deactive loader
117 | store.dispatch('loader/deactiveLoader')
118 | // Clear error
119 | store.dispatch('error/clearErrors')
120 | }
121 | })
122 | .catch(err => {
123 | // Deactive loader
124 | store.dispatch('loader/deactiveLoader')
125 | // Handle all error by error state
126 | store.dispatch('error/setErrors', err.response.data)
127 | })
128 | }
129 | }
130 |
131 | // Mutations
132 | const mutations = {
133 | setCategories (state, categories) {
134 | state.categories = categories
135 | },
136 | setSubCategories (state, subCategories) {
137 | state.subCategories = subCategories
138 | },
139 | createCategory (state, category) {
140 | // Push new data to state
141 | state.categories.push(category)
142 | // Clear error
143 | store.dispatch('error/clearErrors')
144 | },
145 | updateCategory (state, category) {
146 | state.categories = state.categories.map(cat => cat._id === category._id ? (cat = category) : cat)
147 | // Clear error
148 | store.dispatch('error/clearErrors')
149 | },
150 | deleteCategory (state, category) {
151 | state.categories = state.categories.filter(cat => cat._id !== category._id)
152 | }
153 | }
154 |
155 | export default {
156 | namespaced: true,
157 | state,
158 | getters,
159 | actions,
160 | mutations
161 | }
162 |
--------------------------------------------------------------------------------
/client/src/store/modules/product/option.js:
--------------------------------------------------------------------------------
1 | import store from '../../../store'
2 | import Vue from 'vue'
3 | // Initial state
4 | const state = {
5 | options: []
6 | }
7 |
8 | // Getters
9 | const getters = {
10 | getOptions (state) {
11 | return state.options
12 | }
13 | }
14 |
15 | // Actions
16 | const actions = {
17 | getOptions ({ commit }) {
18 | // Active preloader
19 | store.dispatch('loader/activeLoader')
20 | // Make server request
21 | Vue.axios.get('/admin/product/options')
22 | .then(response => {
23 | if (response.data.success) {
24 | // Store to state
25 | commit('setOptions', response.data.options)
26 | // Deactive loader
27 | store.dispatch('loader/deactiveLoader')
28 | // Clear error
29 | store.dispatch('error/clearErrors')
30 | }
31 | })
32 | .catch(err => {
33 | // Deactive loader
34 | store.dispatch('loader/deactiveLoader')
35 | // Handle all error by error state
36 | store.dispatch('error/setErrors', err.response.data)
37 | })
38 | },
39 | createOption ({ commit }, payload) {
40 | // Active preloader
41 | store.dispatch('loader/activeLoader')
42 | // Make server request
43 | Vue.axios.post('/admin/product/options', payload)
44 | .then(response => {
45 | if (response.data.success) {
46 | commit('createOption', response.data.option)
47 | // Deactive loader
48 | store.dispatch('loader/deactiveLoader')
49 | // Clear error
50 | store.dispatch('error/clearErrors')
51 | }
52 | })
53 | .catch(err => {
54 | // Deactive loader
55 | store.dispatch('loader/deactiveLoader')
56 | // Handle all error by error state
57 | store.dispatch('error/setErrors', err.response.data)
58 | })
59 | },
60 | updateOption ({ commit }, payload) {
61 | // Active preloader
62 | store.dispatch('loader/activeLoader')
63 | // Make server request
64 | store.dispatch('loader/activeLoader')
65 | Vue.axios.put('/admin/product/options/' + payload._id, payload.data)
66 | .then(response => {
67 | if (response.data.success) {
68 | commit('updateOption', response.data.option)
69 | // Deactive loader
70 | store.dispatch('loader/deactiveLoader')
71 | // Clear error
72 | store.dispatch('error/clearErrors')
73 | }
74 | })
75 | .catch(err => {
76 | // Deactive loader
77 | store.dispatch('loader/deactiveLoader')
78 | // Handle all error by error state
79 | store.dispatch('error/setErrors', err.response.data)
80 | })
81 | },
82 | deleteOption ({ commit }, payload) {
83 | // Active preloader
84 | store.dispatch('loader/activeLoader')
85 | // Make server request
86 | Vue.axios.delete('/admin/product/options/' + payload._id)
87 | .then(response => {
88 | if (response.data.success) {
89 | commit('deleteOption', response.data.option)
90 | // Deactive loader
91 | store.dispatch('loader/deactiveLoader')
92 | // Clear error
93 | store.dispatch('error/clearErrors')
94 | }
95 | })
96 | .catch(err => {
97 | // Deactive loader
98 | store.dispatch('loader/deactiveLoader')
99 | // Handle all error by error state
100 | store.dispatch('error/setErrors', err.response.data)
101 | })
102 | }
103 | }
104 |
105 | // Mutations
106 | const mutations = {
107 | setOptions (state, options) {
108 | state.options = options
109 | },
110 | createOption (state, option) {
111 | // Push new data to state
112 | state.options.push(option)
113 | // Clear error
114 | store.dispatch('error/clearErrors')
115 | },
116 | updateOption (state, option) {
117 | state.options = state.options.map(opt => opt._id === option._id ? (opt = option) : opt)
118 | // Clear error
119 | store.dispatch('error/clearErrors')
120 | },
121 | deleteOption (state, option) {
122 | state.options = state.options.filter(opt => opt._id !== option._id)
123 | }
124 | }
125 |
126 | export default {
127 | namespaced: true,
128 | state,
129 | getters,
130 | actions,
131 | mutations
132 | }
133 |
--------------------------------------------------------------------------------
/client/src/store/modules/product/optionGroup.js:
--------------------------------------------------------------------------------
1 | import store from '../../../store'
2 | import Vue from 'vue'
3 | // Initial state
4 | const state = {
5 | optionGroups: [],
6 | options: []
7 | }
8 |
9 | // Getters
10 | const getters = {
11 | getOptionGroups (state) {
12 | return state.optionGroups
13 | },
14 | getOptionsByOptionGroup (state) {
15 | return state.options
16 | }
17 | }
18 |
19 | // Actions
20 | const actions = {
21 | getOptionGroups ({ commit }) {
22 | // Active preloader
23 | store.dispatch('loader/activeLoader')
24 | // Make server request
25 | Vue.axios.get('/admin/product/option-groups')
26 | .then(response => {
27 | if (response.data.success) {
28 | // Store to state
29 | commit('setOptionGroups', response.data.optionGroups)
30 | // Deactive loader
31 | store.dispatch('loader/deactiveLoader')
32 | // Clear error
33 | store.dispatch('error/clearErrors')
34 | }
35 | })
36 | .catch(err => {
37 | // Deactive loader
38 | store.dispatch('loader/deactiveLoader')
39 | // Handle all error by error state
40 | store.dispatch('error/setErrors', err.response.data)
41 | })
42 | },
43 | getOptionsByOptionGroup ({ commit }, optionGroup) {
44 | // Active preloader
45 | store.dispatch('loader/activeLoader')
46 | // Make server request
47 | Vue.axios.get(`/admin/product/option-groups/${optionGroup._id}/options`)
48 | .then(response => {
49 | if (response.data.success) {
50 | // Store to state
51 | commit('setOptions', response.data.options)
52 | // Deactive loader
53 | store.dispatch('loader/deactiveLoader')
54 | // Clear error
55 | store.dispatch('error/clearErrors')
56 | }
57 | })
58 | .catch(err => {
59 | // Deactive loader
60 | store.dispatch('loader/deactiveLoader')
61 | // Handle all error by error state
62 | store.dispatch('error/setErrors', err.response.data)
63 | })
64 | },
65 | createOptionGroup ({ commit }, payload) {
66 | // Active preloader
67 | store.dispatch('loader/activeLoader')
68 | // Make server request
69 | Vue.axios.post('/admin/product/option-groups', payload)
70 | .then(response => {
71 | if (response.data.success) {
72 | commit('createOptionGroup', response.data.optionGroup)
73 | // Deactive loader
74 | store.dispatch('loader/deactiveLoader')
75 | // Clear error
76 | store.dispatch('error/clearErrors')
77 | }
78 | })
79 | .catch(err => {
80 | // Deactive loader
81 | store.dispatch('loader/deactiveLoader')
82 | // Handle all error by error state
83 | store.dispatch('error/setErrors', err.response.data)
84 | })
85 | },
86 | updateOptionGroup ({ commit }, payload) {
87 | // Active preloader
88 | store.dispatch('loader/activeLoader')
89 | // Make server request
90 | store.dispatch('loader/activeLoader')
91 | Vue.axios.put('/admin/product/option-groups/' + payload._id, payload.data)
92 | .then(response => {
93 | if (response.data.success) {
94 | commit('updateOptionGroup', response.data.optionGroup)
95 | // Deactive loader
96 | store.dispatch('loader/deactiveLoader')
97 | // Clear error
98 | store.dispatch('error/clearErrors')
99 | }
100 | })
101 | .catch(err => {
102 | // Deactive loader
103 | store.dispatch('loader/deactiveLoader')
104 | // Handle all error by error state
105 | store.dispatch('error/setErrors', err.response.data)
106 | })
107 | },
108 | deleteOptionGroup ({ commit }, payload) {
109 | // Active preloader
110 | store.dispatch('loader/activeLoader')
111 | // Make server request
112 | Vue.axios.delete('/admin/product/option-groups/' + payload._id)
113 | .then(response => {
114 | if (response.data.success) {
115 | commit('deleteOptionGroup', response.data.optionGroup)
116 | // Deactive loader
117 | store.dispatch('loader/deactiveLoader')
118 | // Clear error
119 | store.dispatch('error/clearErrors')
120 | }
121 | })
122 | .catch(err => {
123 | // Deactive loader
124 | store.dispatch('loader/deactiveLoader')
125 | // Handle all error by error state
126 | store.dispatch('error/setErrors', err.response.data)
127 | })
128 | }
129 | }
130 |
131 | // Mutations
132 | const mutations = {
133 | setOptionGroups (state, optionGroups) {
134 | state.optionGroups = optionGroups
135 | },
136 | setOptions (state, options) {
137 | state.options = options
138 | },
139 | createOptionGroup (state, optionGroup) {
140 | // Push new data to state
141 | state.optionGroups.push(optionGroup)
142 | // Clear error
143 | store.dispatch('error/clearErrors')
144 | },
145 | updateOptionGroup (state, optionGroup) {
146 | state.optionGroups = state.optionGroups.map(og => og._id === optionGroup._id ? (og = optionGroup) : og)
147 | // Clear error
148 | store.dispatch('error/clearErrors')
149 | },
150 | deleteOptionGroup (state, optionGroup) {
151 | state.optionGroups = state.optionGroups.filter(og => og._id !== optionGroup._id)
152 | }
153 | }
154 |
155 | export default {
156 | namespaced: true,
157 | state,
158 | getters,
159 | actions,
160 | mutations
161 | }
162 |
--------------------------------------------------------------------------------
/client/src/store/modules/product/product.js:
--------------------------------------------------------------------------------
1 | import router from '../../../router'
2 | import store from '../../../store'
3 | import Vue from 'vue'
4 |
5 | // Initial state
6 | const state = {
7 | products: [],
8 | attributes: [],
9 | images: []
10 | }
11 |
12 | // Getters
13 | const getters = {
14 | getProducts (state) {
15 | return state.products
16 | },
17 | getAttributes (state) {
18 | return state.attributes
19 | },
20 | getImages (state) {
21 | return state.images
22 | }
23 | }
24 | // Actions
25 | const actions = {
26 | getProducts ({commit}) {
27 | // Active preloader
28 | store.dispatch('loader/activeLoader')
29 | // Make server request
30 | Vue.axios.get('/admin/product/products')
31 | .then(res => {
32 | // Set product to state
33 | commit('setProducts', res.data.products)
34 | // Deactive preloader
35 | store.dispatch('loader/deactiveLoader')
36 | // Clear all error data
37 | store.dispatch('error/clearErrors')
38 | })
39 | .catch(err => {
40 | // Deactive preloader
41 | store.dispatch('loader/deactiveLoader')
42 | // Handle all errors by error state
43 | store.dispatch('error/setErrors', err.response.data)
44 | })
45 | },
46 | getAttributes ({commit}, productId) {
47 | // Active preloader
48 | store.dispatch('loader/activeLoader')
49 | // Make server request
50 | Vue.axios.get(`/admin/product/products/${productId}/attributes`)
51 | .then(res => {
52 | // Set attribute to state
53 | commit('setAttributes', res.data.attributes)
54 | // Deactive preloader
55 | store.dispatch('loader/deactiveLoader')
56 | // Clear all error data
57 | store.dispatch('error/clearErrors')
58 | })
59 | .catch(err => {
60 | // Deactive preloader
61 | store.dispatch('loader/deactiveLoader')
62 | // Handle all errors by error state
63 | store.dispatch('error/setErrors', err.response.data)
64 | })
65 | },
66 | getImages ({commit}, productId) {
67 | // Active preloader
68 | store.dispatch('loader/activeLoader')
69 | // Make server request
70 | Vue.axios.get(`/admin/product/products/${productId}/images`)
71 | .then(res => {
72 | // Set images to state
73 | commit('setImages', res.data.images)
74 | // Deactive preloader
75 | store.dispatch('loader/deactiveLoader')
76 | // Clear all error data
77 | store.dispatch('error/clearErrors')
78 | })
79 | .catch(err => {
80 | // Deactive preloader
81 | store.dispatch('loader/deactiveLoader')
82 | // Handle all errors by error state
83 | store.dispatch('error/setErrors', err.response.data)
84 | })
85 | },
86 | createProduct ({commit}, payload) {
87 | // Active preloader
88 | store.dispatch('loader/activeLoader')
89 | // Generate form data
90 | const fd = new FormData()
91 | fd.append('sku', payload.sku)
92 | fd.append('category', payload.category)
93 | fd.append('subCategory', payload.subCategory)
94 | fd.append('model', payload.model)
95 | fd.append('name', payload.name)
96 | fd.append('sortDesc', payload.sortDesc)
97 | fd.append('longDesc', payload.longDesc)
98 | fd.append('weight', payload.weight)
99 | fd.append('price', payload.price)
100 | fd.append('stock', payload.stock)
101 | fd.append('inStock', payload.inStock)
102 | fd.append('isFeature', payload.isFeature)
103 | fd.append('status', payload.status)
104 | fd.append('thumb', payload.thumb)
105 |
106 | // Manufacturer
107 | if (payload.hasManufacturer) {
108 | fd.append('hasManufacturer', payload.hasManufacturer)
109 | fd.append('manufacturerId', payload.manufacturer._id)
110 | fd.append('manufacturerName', payload.manufacturer.name)
111 | fd.append('manufacturerImage', payload.manufacturer.image ? payload.manufacturer.image.url : '')
112 | }
113 | // Flash sale
114 | if (payload.isFlashSale) {
115 | fd.append('isFlashSale', payload.isFlashSale)
116 | fd.append('flashPrice', payload.flashPrice)
117 | fd.append('flashStart', payload.flashStartDate)
118 | fd.append('flashEnd', payload.flashEndDate)
119 | fd.append('flashStatus', payload.flashStatus)
120 | }
121 |
122 | // Special sale
123 | if (payload.isFlashSale) {
124 | fd.append('isSpecialSale', payload.isSpecialSale)
125 | fd.append('specialPrice', payload.specialPrice)
126 | fd.append('specialExpire', payload.specialExpireDate)
127 | fd.append('specialStatus', payload.specialStatus)
128 | }
129 | // Make server request
130 | Vue.axios.post('/admin/product/products', fd)
131 | .then(res => {
132 | // Set new created product to state
133 | commit('createProduct', res.data.product)
134 | // Deactive preloader
135 | store.dispatch('loader/deactiveLoader')
136 | // Clear all error data
137 | store.dispatch('error/clearErrors')
138 | // Redirect to dashboard
139 | router.push({name: 'productList'})
140 | })
141 | .catch(err => {
142 | // Deactive preloader
143 | store.dispatch('loader/deactiveLoader')
144 | // Handle all errors by error state
145 | store.dispatch('error/setErrors', err.response.data)
146 | })
147 | },
148 | addProductAttribute ({commit}, payload) {
149 | // Active preloader
150 | store.dispatch('loader/activeLoader')
151 | Vue.axios.post(`/admin/product/products/${payload.productId}/attributes`, payload)
152 | .then(res => {
153 | // Set attributes to state
154 | commit('setAttributes', res.data.attributes)
155 | // Deactive preloader
156 | store.dispatch('loader/deactiveLoader')
157 | // Clear all error data
158 | store.dispatch('error/clearErrors')
159 | })
160 | .catch(err => {
161 | // Deactive preloader
162 | store.dispatch('loader/deactiveLoader')
163 | // Handle all errors by error state
164 | store.dispatch('error/setErrors', err.response.data)
165 | })
166 | },
167 | deleteAttribute ({commit}, payload) {
168 | // Active preloader
169 | store.dispatch('loader/activeLoader')
170 | Vue.axios.delete(`/admin/product/products/${payload.productId}/attributes/${payload.attId}`)
171 | .then(res => {
172 | // Delete attributes from state
173 | commit('deleteAttributes', res.data.attributes)
174 | // Deactive preloader
175 | store.dispatch('loader/deactiveLoader')
176 | // Clear all error data
177 | store.dispatch('error/clearErrors')
178 | })
179 | .catch(err => {
180 | // Deactive preloader
181 | store.dispatch('loader/deactiveLoader')
182 | // Handle all errors by error state
183 | store.dispatch('error/setErrors', err.response.data)
184 | })
185 | },
186 | addProductImage ({commit}, payload) {
187 | // Make form data
188 | const fd = new FormData()
189 | fd.append('image', payload.image)
190 | // Active preloader
191 | store.dispatch('loader/activeLoader')
192 | Vue.axios.post(`/admin/product/products/${payload.productId}/images`, fd)
193 | .then(res => {
194 | // Set attributes to state
195 | commit('setImages', res.data.images)
196 | // Deactive preloader
197 | store.dispatch('loader/deactiveLoader')
198 | // Clear all error data
199 | store.dispatch('error/clearErrors')
200 | })
201 | .catch(err => {
202 | // Deactive preloader
203 | store.dispatch('loader/deactiveLoader')
204 | // Handle all errors by error state
205 | store.dispatch('error/setErrors', err.response.data)
206 | })
207 | },
208 | deleteImage ({commit}, payload) {
209 | // Active preloader
210 | store.dispatch('loader/activeLoader')
211 | Vue.axios.delete(`/admin/product/products/${payload.productId}/images`)
212 | .then(res => {
213 | // Delete images from state
214 | commit('deleteImages', res.data.images)
215 | // Deactive preloader
216 | store.dispatch('loader/deactiveLoader')
217 | // Clear all error data
218 | store.dispatch('error/clearErrors')
219 | })
220 | .catch(err => {
221 | // Deactive preloader
222 | store.dispatch('loader/deactiveLoader')
223 | // Handle all errors by error state
224 | store.dispatch('error/setErrors', err.response.data)
225 | })
226 | }
227 | }
228 | // Mutations
229 | const mutations = {
230 | setProducts (state, products) {
231 | // set all received product to state
232 | state.products = products
233 | },
234 | setAttributes (state, attributes) {
235 | // set all received product attributes to state
236 | state.attributes = attributes
237 | },
238 | setImages (state, images) {
239 | // set all received product images to state
240 | state.images = images
241 | },
242 | createProduct (state, product) {
243 | // Push new product to product state
244 | state.products.push(product)
245 | },
246 | addProductAttribute (state, attributes) {
247 | state.attributes = attributes
248 | },
249 | deleteAttributes (state, attributes) {
250 | state.attributes = attributes
251 | },
252 | deleteImages (state, images) {
253 | state.images = images
254 | }
255 | }
256 |
257 | export default {
258 | namespaced: true,
259 | state,
260 | getters,
261 | actions,
262 | mutations
263 | }
264 |
--------------------------------------------------------------------------------
/client/src/store/modules/product/subCategory.js:
--------------------------------------------------------------------------------
1 | import store from '../../../store'
2 | import Vue from 'vue'
3 | // Initial state
4 | const state = {
5 | subCategories: []
6 | }
7 |
8 | // Getters
9 | const getters = {
10 | getSubCategories (state) {
11 | return state.subCategories
12 | }
13 | }
14 |
15 | // Actions
16 | const actions = {
17 | getSubCategories ({ commit }) {
18 | // Active preloader
19 | store.dispatch('loader/activeLoader')
20 | // Make server request
21 | Vue.axios.get('/admin/product/sub-categories')
22 | .then(response => {
23 | if (response.data.success) {
24 | // Store to state
25 | commit('setSubCategories', response.data.subCategories)
26 | // Deactive loader
27 | store.dispatch('loader/deactiveLoader')
28 | // Clear error
29 | store.dispatch('error/clearErrors')
30 | }
31 | })
32 | .catch(err => {
33 | // Deactive loader
34 | store.dispatch('loader/deactiveLoader')
35 | // Handle all error by error state
36 | store.dispatch('error/setErrors', err.response.data)
37 | })
38 | },
39 | createSubCategory ({ commit }, payload) {
40 | // Active preloader
41 | store.dispatch('loader/activeLoader')
42 | // Make server request
43 | Vue.axios.post('/admin/product/sub-categories', payload)
44 | .then(response => {
45 | if (response.data.success) {
46 | commit('createSubCategory', response.data.subCategory)
47 | // Deactive loader
48 | store.dispatch('loader/deactiveLoader')
49 | // Clear error
50 | store.dispatch('error/clearErrors')
51 | }
52 | })
53 | .catch(err => {
54 | // Deactive loader
55 | store.dispatch('loader/deactiveLoader')
56 | // Handle all error by error state
57 | store.dispatch('error/setErrors', err.response.data)
58 | })
59 | },
60 | updateSubCategory ({ commit }, payload) {
61 | // Active preloader
62 | store.dispatch('loader/activeLoader')
63 | // Make server request
64 | store.dispatch('loader/activeLoader')
65 | Vue.axios.put('/admin/product/sub-categories/' + payload._id, payload.data)
66 | .then(response => {
67 | if (response.data.success) {
68 | commit('updateSubCategory', response.data.subCategory)
69 | // Deactive loader
70 | store.dispatch('loader/deactiveLoader')
71 | // Clear error
72 | store.dispatch('error/clearErrors')
73 | }
74 | })
75 | .catch(err => {
76 | // Deactive loader
77 | store.dispatch('loader/deactiveLoader')
78 | // Handle all error by error state
79 | store.dispatch('error/setErrors', err.response.data)
80 | })
81 | },
82 | deleteSubCategory ({ commit }, payload) {
83 | // Active preloader
84 | store.dispatch('loader/activeLoader')
85 | // Make server request
86 | Vue.axios.delete('/admin/product/sub-categories/' + payload._id)
87 | .then(response => {
88 | if (response.data.success) {
89 | commit('deleteSubCategory', response.data.subCategory)
90 | // Deactive loader
91 | store.dispatch('loader/deactiveLoader')
92 | // Clear error
93 | store.dispatch('error/clearErrors')
94 | }
95 | })
96 | .catch(err => {
97 | // Deactive loader
98 | store.dispatch('loader/deactiveLoader')
99 | // Handle all error by error state
100 | store.dispatch('error/setErrors', err.response.data)
101 | })
102 | }
103 | }
104 |
105 | // Mutations
106 | const mutations = {
107 | setSubCategories (state, subCategories) {
108 | state.subCategories = subCategories
109 | },
110 | createSubCategory (state, subCategory) {
111 | // Push new data to state
112 | state.subCategories.push(subCategory)
113 | // Clear error
114 | store.dispatch('error/clearErrors')
115 | },
116 | updateSubCategory (state, subCategory) {
117 | state.subCategories = state.subCategories.map(scat => scat._id === subCategory._id ? (scat = subCategory) : scat)
118 | // Clear error
119 | store.dispatch('error/clearErrors')
120 | },
121 | deleteSubCategory (state, subCategory) {
122 | state.subCategories = state.subCategories.filter(scat => scat._id !== subCategory._id)
123 | }
124 | }
125 |
126 | export default {
127 | namespaced: true,
128 | state,
129 | getters,
130 | actions,
131 | mutations
132 | }
133 |
--------------------------------------------------------------------------------
/client/src/store/modules/utils/error.js:
--------------------------------------------------------------------------------
1 | // Initial state
2 | const state = {
3 | errors: {},
4 | isError: false
5 | }
6 | // Getters
7 | const getters = {
8 | getErrors (state) {
9 | return state.errors
10 | },
11 | isError (state) {
12 | return state.isError
13 | }
14 | }
15 |
16 | // Actions
17 | const actions = {
18 | setErrors ({commit}, errors) {
19 | commit('setErrors', errors)
20 | },
21 | clearErrors ({commit}) {
22 | commit('clearErrors')
23 | }
24 | }
25 |
26 | // Mutations
27 | const mutations = {
28 | setErrors (state, errors) {
29 | state.errors = errors
30 | state.isError = true
31 | },
32 | clearErrors (state) {
33 | state.errors = {}
34 | state.isError = false
35 | }
36 | }
37 |
38 | export default {
39 | namespaced: true,
40 | state,
41 | getters,
42 | actions,
43 | mutations
44 | }
45 |
--------------------------------------------------------------------------------
/client/src/store/modules/utils/loader.js:
--------------------------------------------------------------------------------
1 | // Initial state
2 | const state = {
3 | isLoading: true
4 | }
5 | // Getters
6 | const getters = {
7 | isLoading (state) {
8 | return state.isLoading
9 | }
10 | }
11 |
12 | // Actions
13 | const actions = {
14 | activeLoader ({commit}) {
15 | commit('activeLoader')
16 | },
17 | deactiveLoader ({commit}) {
18 | commit('deactiveLoader')
19 | }
20 | }
21 |
22 | // Mutations
23 | const mutations = {
24 | activeLoader (state) {
25 | state.isLoading = true
26 | },
27 | deactiveLoader (state) {
28 | state.isLoading = false
29 | }
30 | }
31 |
32 | export default {
33 | namespaced: true,
34 | state,
35 | getters,
36 | actions,
37 | mutations
38 | }
39 |
--------------------------------------------------------------------------------
/client/src/utils/setAuthToken.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | const setAuthToken = token => {
4 | if (token) {
5 | // Apply to every request
6 | axios.defaults.headers.common['Authorization'] = token
7 | } else {
8 | // Delete auth header
9 | delete axios.defaults.headers.common['Authorization']
10 | }
11 | }
12 |
13 | export default setAuthToken
14 |
--------------------------------------------------------------------------------
/client/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudio-qui/node-vue-ecommerce-backend/6fd72ad226b3bc3715fd34351efc5afa9659497d/client/static/.gitkeep
--------------------------------------------------------------------------------
/client/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/claudio-qui/node-vue-ecommerce-backend/6fd72ad226b3bc3715fd34351efc5afa9659497d/client/static/favicon.ico
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | config/development.json
3 |
--------------------------------------------------------------------------------
/server/api/index.js:
--------------------------------------------------------------------------------
1 | const auth = require('./routes/admin/auth');
2 | const productCategory = require('./routes/admin/product/productCategory');
3 | const productSubCategory = require('./routes/admin/product/productSubCategory');
4 | const optionGroup = require('./routes/admin/product/optionGroup');
5 | const option = require('./routes/admin/product/option');
6 | const productMfr = require('./routes/admin/product/productManufacturer');
7 | const product = require('./routes/admin/product/product');
8 | // Client route data
9 | const clientAuth = require('./routes/client/auth');
10 | const clientProductCategory = require('./routes/client/product/category');
11 | const clientProductSubCategory = require('./routes/client/product/subCategory');
12 | const clientProduct = require('./routes/client/product/product');
13 | const order = require('./routes/client/order/order');
14 |
15 | module.exports = (app)=>{
16 | // All admin routes
17 | // Admin auth
18 | app.use('/api/admin', auth);
19 | // Product category
20 | app.use('/api/admin/product/categories', productCategory);
21 | // Product sub category
22 | app.use('/api/admin/product/sub-categories', productSubCategory);
23 | // Option group
24 | app.use('/api/admin/product/option-groups', optionGroup);
25 | // Option
26 | app.use('/api/admin/product/options', option);
27 | // Manufacturer
28 | app.use('/api/admin/product/manufacturers', productMfr);
29 | // Product
30 | app.use('/api/admin/product/products', product);
31 |
32 | // All client route
33 | // Customer auth
34 | app.use('/api', clientAuth);
35 | // Category
36 | app.use('/api/product/categories', clientProductCategory);
37 | // Sub category
38 | app.use('/api/product/sub-categories', clientProductSubCategory);
39 | // Product
40 | app.use('/api/product/products', clientProduct);
41 | // order
42 | app.use('/api/product/', order);
43 | }
44 |
--------------------------------------------------------------------------------
/server/api/routes/admin/auth.js:
--------------------------------------------------------------------------------
1 | const bcrypt = require('bcryptjs');
2 | const dbDebugger = require('debug')('app:db');
3 | const _ = require('lodash');
4 | const express = require('express');
5 | const router = express.Router();
6 |
7 | // Middlewares
8 | const auth = require('../../../middlewares/admin/auth');
9 | const admin = require('../../../middlewares/admin/admin');
10 |
11 | // Helpers
12 | const { generateJwtToken } = require('../../../helpers/jwt_access_token');
13 |
14 | // Validatins
15 | const validateRegisterInput = require('../../../validations/admin/auth/register');
16 | const validateLoginInput = require('../../../validations/admin/auth/login');
17 |
18 | // Model
19 | const User = require('../../../models/admin/user');
20 |
21 | // @route POST /api/admin/login
22 | // @des Admin login
23 | // @access Public
24 | router.post('/login', (req, res) => {
25 | // Validate user input
26 | const { errors } = validateLoginInput(req.body);
27 | if (errors) return res.status(400).json(errors);
28 |
29 | const email = req.body.email;
30 | const password = req.body.password;
31 |
32 | // Check user email
33 | User.findOne({ email: email })
34 | .then(user => {
35 | if (!user) {
36 | return res.status(404).json({ email: "Email or password incorrect!" });
37 | } else {
38 | bcrypt.compare(password, user.password)
39 | .then(isMatch => {
40 | if (isMatch) {
41 | // Payload
42 | const payload = {
43 | id: user._id,
44 | name: user.name,
45 | email: user.email,
46 | status: user.status
47 | };
48 | // Generate jwt access token
49 | generateJwtToken(payload, (token) => {
50 | if (token) {
51 | return res.status(200).json({
52 | success: true,
53 | token: "Bearer " + token,
54 | user: payload
55 | });
56 | } else {
57 | throw new Error();
58 | }
59 | });
60 | } else {
61 | throw new Error();
62 | }
63 | })
64 | .catch(err => res.status(404).json({ error: true, email: "Email or password incorrect!" }))
65 | }
66 | })
67 | .catch(err => res.status(404).json({ error: true, email: "Email or password incorrect!" }));
68 | });
69 |
70 | // @route Post /api/admin/register
71 | // @des Admin register
72 | // @access Public
73 | router.post('/register', (req, res) => {
74 | // Validate user input
75 | const { errors } = validateRegisterInput(req.body);
76 | if (errors) return res.status(400).json(errors);
77 |
78 | // Check user already exist or not
79 | User.findOne({ email: req.body.email })
80 | .then(user => {
81 | // If user exits return
82 | if (user) {
83 | return res.status(409).json({ "error": "User already exist" })
84 | } else {
85 | // Extrecting user information
86 | const user = new User(_.pick(req.body, ["name", "email", "password"]));
87 | // Generate user password
88 | const salt = bcrypt.genSalt(10, (err, salt) => {
89 | if (err) throw err;
90 | bcrypt.hash(user.password, salt, (err, hash) => {
91 | if (err) throw err;
92 | user.password = hash;
93 | // Store user to DB
94 | user.save()
95 | .then(user => res.status(201).json(_.pick(user, ["_id", "name", "email", "isAdmin","status"])))
96 | .catch(err => {
97 | res.status(400).json({ "error": "Something error!" });
98 | dbDebugger(err)
99 | });
100 | });
101 | });
102 | }
103 | })
104 | .catch(err => {
105 | res.status(400).json({ "error": "Something error!" });
106 | dbDebugger(err)
107 | });
108 | });
109 |
110 | // @route GET /api/admin/me
111 | // @des Admin user info
112 | // @access Private
113 | router.get('/me',[auth, admin], (req, res)=>{
114 | res.status(200).json(_.pick(req.user, ["_id", "name", "email", "isAdmin", "status"]));
115 | });
116 |
117 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/admin/product/option.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | // Middlewares
5 | const auth = require('../../../../middlewares/admin/auth');
6 | const admin = require('../../../../middlewares/admin/admin');
7 |
8 | // Validation
9 | const validateOptionInput = require('../../../../validations/admin/product/option');
10 |
11 | // Model
12 | const Option = require('../../../../models/admin/product/option');
13 |
14 | // Helpers
15 | const somethinError = require('../../../../helpers/something_error');
16 |
17 | // @route GET /api/admin/product/options
18 | // @des Get all options
19 | // @access Private
20 | router.get('/', [auth, admin], (req, res) => {
21 |
22 | Option.find()
23 | .populate('optionGroup', ['_id','name'])
24 | .sort({ name: 1 })
25 | .then(options => {
26 | // No option found
27 | if (!options) return res.status(404).json({ success: false, error: 'No data found!' });
28 |
29 | return res.status(200).json({ success: true, 'options': options });
30 | })
31 | .catch(err => somethinError(res, err));
32 | });
33 |
34 | // @route GET /api/admin/product/options/:id
35 | // @des Get options by id
36 | // @access Private
37 | router.get('/:id', [auth, admin], (req, res) => {
38 |
39 | Option.findById(req.params.id)
40 | .populate('optionGroup', ['_id', 'name'])
41 | .then(option => {
42 | // No option found
43 | if (!option) return res.status(404).json({ success: false, error: 'No data found!' });
44 |
45 | return res.status(200).json({ success: true, 'option': option });
46 | })
47 | .catch(err => somethinError(res, err));
48 | });
49 |
50 | // @route POST /api/admin/product/options
51 | // @des Create options
52 | // @access Private
53 | router.post('/', [auth, admin], (req, res) => {
54 | const { errors } = validateOptionInput(req.body);
55 | if (errors) return res.status(400).json(errors);
56 |
57 | // let optionName = (req.body.name).toLowerCase();
58 | let optionName = req.body.name;
59 | // Check option already exist or not
60 | Option.findOne({ name: optionName })
61 | .then(option => {
62 | if (option) {
63 | return res.status(409).json({ name: "Option already exist!" });
64 | } else {
65 | // Create new option
66 | const newOption = new Option({
67 | optionGroup: req.body.optionGroup,
68 | name: optionName,
69 | other: req.body.other,
70 | note: req.body.note
71 | });
72 | // Save to DB
73 | newOption.save()
74 | .then(option => {
75 | // Return option with oprion group details
76 | Option.findById(option._id)
77 | .populate('optionGroup')
78 | .then(option=>{
79 | res.status(201).json({ success: true, option: option })
80 | })
81 | .catch(err => somethinError(res));
82 | })
83 | .catch(err => somethinError(res));
84 | }
85 | })
86 | .catch(err => somethinError(res, err));
87 |
88 | });
89 |
90 | // @route PUT /api/admin/product/options/:id
91 | // @des Update option by id
92 | // @access Private
93 | router.put('/:id', [auth, admin], (req, res) => {
94 | const { errors } = validateOptionInput(req.body);
95 | if (errors) return res.status(400).json(errors);
96 |
97 | let optionName = req.body.name;
98 | // Check option exist or not
99 | Option.findById(req.params.id)
100 | .then(option => {
101 | if (!option) {
102 | return res.status(404).json({ error: "Option Not Found!" });
103 | } else {
104 | // Update option
105 | option.optionGroup = req.body.optionGroup;
106 | option.name = optionName;
107 | option.other = req.body.other;
108 | option.note = req.body.note
109 |
110 | // Save to DB
111 | option.save()
112 | .then(option => {
113 | // Return option with option group details
114 | Option.findById(option._id)
115 | .populate('optionGroup')
116 | .then(option=>{
117 | res.status(201).json({ success: true, option: option })
118 | })
119 | .catch(err => somethinError(res));
120 | })
121 | .catch(err => somethinError(res));
122 | }
123 | })
124 | .catch(err => somethinError(res, err));
125 |
126 | });
127 |
128 | // @route DELETE /api/admin/product/options/:id
129 | // @des Delete option by id
130 | // @access Private
131 | router.delete('/:id', [auth, admin], (req, res) => {
132 | // Check option exist or not
133 | Option.findById(req.params.id)
134 | .then(option => {
135 | if (!option) {
136 | return res.status(404).json({ error: "Option Not Found!" });
137 | } else {
138 | // Delete option
139 | option.delete()
140 | .then(option => res.status(200).json({ success: true, option: option }))
141 | .catch(err => somethinError(res));
142 | }
143 | })
144 | .catch(err => somethinError(res, err));
145 |
146 | });
147 |
148 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/admin/product/optionGroup.js:
--------------------------------------------------------------------------------
1 | const slugify = require('slugify');
2 | const express = require('express');
3 | const router = express.Router();
4 |
5 | // Middlewares
6 | const auth = require('../../../../middlewares/admin/auth');
7 | const admin = require('../../../../middlewares/admin/admin');
8 |
9 | // Validation
10 | const validateOptionGroupInput = require('../../../../validations/admin/product/optionGroup');
11 |
12 | // Model
13 | const OptionGroup = require('../../../../models/admin/product/optionGroup');
14 | const Option = require('../../../../models/admin/product/option');
15 |
16 | // Helpers
17 | const somethinError = require('../../../../helpers/something_error');
18 |
19 | // @route GET /api/admin/product/option-groups
20 | // @des Get all product option groups
21 | // @access Private
22 | router.get('/', [auth, admin], (req, res) => {
23 |
24 | OptionGroup.find()
25 | .sort({ name: 1 })
26 | .then(optionGroups => {
27 | // No option group found
28 | if (!optionGroups) return res.status(404).json({ success: false, error: 'No data found!' });
29 |
30 | return res.status(200).json({ success: true, 'optionGroups': optionGroups });
31 | })
32 | .catch(err => somethinError(res, err));
33 | });
34 |
35 | // @route GET /api/admin/product/option-groups/:id
36 | // @des Get product option group by id
37 | // @access Private
38 | router.get('/:id', [auth, admin], (req, res) => {
39 |
40 | OptionGroup.findById(req.params.id)
41 | .then(optinGroup => {
42 | // No option group found
43 | if (!optinGroup) return res.status(404).json({ success: false, error: 'No data found!' });
44 |
45 | return res.status(200).json({ success: true, 'optinGroup': optinGroup });
46 | })
47 | .catch(err => somethinError(res, err));
48 | });
49 |
50 | // @route GET /api/admin/product/option-groups/:id/options
51 | // @des Get product option belongs to this group
52 | // @access Private
53 | router.get('/:id/options', [auth, admin], (req, res) => {
54 |
55 | Option.find({optionGroup: req.params.id})
56 | .then(options => {
57 | // No option found
58 | if (!options) return res.status(404).json({ success: false, error: 'No data found!' });
59 |
60 | return res.status(200).json({ success: true, 'options': options });
61 | })
62 | .catch(err => somethinError(res, err));
63 | });
64 |
65 | // @route POST /api/admin/product/option-groups
66 | // @des Create option group
67 | // @access Private
68 | router.post('/', [auth, admin], (req, res) => {
69 | const { errors } = validateOptionGroupInput(req.body);
70 | if (errors) return res.status(400).json(errors);
71 |
72 | let optionGroupName = (req.body.name).toLowerCase();
73 | // Check option group already exist or not
74 | OptionGroup.findOne({ name: optionGroupName })
75 | .then(optionGroup => {
76 | if (optionGroup) {
77 | return res.status(409).json({ name: "Option group already exist!" });
78 | } else {
79 |
80 | // Create new option group
81 | const newOptionGroup = new OptionGroup({
82 | name: optionGroupName,
83 | slug: slugify(optionGroupName)
84 | });
85 | // Save to DB
86 | newOptionGroup.save()
87 | .then(optionGroup => res.status(201).json({ success: true, optionGroup: optionGroup }))
88 | .catch(err => somethinError(res));
89 | }
90 | })
91 | .catch(err => somethinError(res, err));
92 |
93 | });
94 |
95 | // @route PUT /api/admin/product/option-groups/:id
96 | // @des Update option group by id
97 | // @access Private
98 | router.put('/:id', [auth, admin], (req, res) => {
99 | const { errors } = validateOptionGroupInput(req.body);
100 | if (errors) return res.status(400).json(errors);
101 |
102 | let optionGroupName = (req.body.name).toLowerCase();
103 | // Check option group exist or not
104 | OptionGroup.findById(req.params.id)
105 | .then(optionGroup => {
106 | if (!optionGroup) {
107 | return res.status(404).json({ error: "Option Groupe Not Found!" });
108 | } else {
109 | // Update option groupe
110 | optionGroup.name = optionGroupName;
111 | optionGroup.slug = slugify(optionGroupName);
112 | // Save to DB
113 | optionGroup.save()
114 | .then(optionGroup => res.status(200).json({ success: true, optionGroup: optionGroup }))
115 | .catch(err => somethinError(res));
116 | }
117 | })
118 | .catch(err => somethinError(res, err));
119 |
120 | });
121 |
122 | // @route DELETE /api/admin/product/option-groups/:id
123 | // @des Delete option group by id
124 | // @access Private
125 | router.delete('/:id', [auth, admin], (req, res) => {
126 | // Check option group exist or not
127 | OptionGroup.findById(req.params.id)
128 | .then(optionGroup => {
129 | if (!optionGroup) {
130 | return res.status(404).json({ error: "Option Groupe Not Found!" });
131 | } else {
132 | // Delete option groupe
133 | optionGroup.delete()
134 | .then(optionGroup => res.status(200).json({ success: true, optionGroup: optionGroup }))
135 | .catch(err => somethinError(res));
136 | }
137 | })
138 | .catch(err => somethinError(res, err));
139 |
140 | });
141 |
142 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/admin/product/productCategory.js:
--------------------------------------------------------------------------------
1 | const slugify = require('slugify');
2 | const express = require('express');
3 | const router = express.Router();
4 |
5 | // Middlewares
6 | const auth = require('../../../../middlewares/admin/auth');
7 | const admin = require('../../../../middlewares/admin/admin');
8 |
9 | // Validation
10 | const validateCategoryInput = require('../../../../validations/admin/product/productCategory');
11 |
12 | // Model
13 | const ProductCategory = require('../../../../models/admin/product/productCategory');
14 | const ProductSubCategory = require('../../../../models/admin/product/productSubCategory');
15 |
16 | // Helpers
17 | const somethinError = require('../../../../helpers/something_error');
18 |
19 | // @route GET /api/admin/product/categories
20 | // @des Get all product category
21 | // @access Private
22 | router.get('/', [auth, admin], (req, res) => {
23 |
24 | ProductCategory.find()
25 | .sort({ name: 1 })
26 | .then(cats => {
27 | // No category found
28 | if (!cats) return res.status(404).json({ success: false, error: 'No data found!' });
29 |
30 | return res.status(200).json({ success: true, 'categories': cats });
31 | })
32 | .catch(err => somethinError(res, err));
33 | });
34 |
35 | // @route GET /api/admin/product/categories/:id
36 | // @des Get product category by id
37 | // @access Private
38 | router.get('/:id', [auth, admin], (req, res) => {
39 |
40 | ProductCategory.findById(req.params.id)
41 | .then(cat => {
42 | // No category found
43 | if (!cat) return res.status(404).json({ success: false, error: 'No data found!' });
44 |
45 | return res.status(200).json({ success: true, 'category': cat });
46 | })
47 | .catch(err => somethinError(res, err));
48 | });
49 |
50 | // @route GET /api/admin/product/categories/:id/sub-categories
51 | // @des Get product sub categories by category id
52 | // @access Private
53 | router.get('/:id/sub-categories', [auth, admin], (req, res) => {
54 |
55 | ProductSubCategory.where({category: req.params.id})
56 | .then(subCat => {
57 | // No sub category found
58 | if (!subCat) return res.status(404).json({ success: false, error: 'No data found!' });
59 |
60 | return res.status(200).json({ success: true, 'subCategories': subCat });
61 | })
62 | .catch(err => somethinError(res, err));
63 | });
64 |
65 | // @route POST /api/admin/product/categories
66 | // @des Create product category
67 | // @access Private
68 | router.post('/', [auth, admin], (req, res) => {
69 | const { errors } = validateCategoryInput(req.body);
70 | if (errors) return res.status(400).json(errors);
71 |
72 | let catName = (req.body.name).toLowerCase();
73 | // Check category already exist or not
74 | ProductCategory.findOne({ name: catName })
75 | .then(cat => {
76 | if (cat) {
77 | return res.status(409).json({ name: "Category already exist!" });
78 | } else {
79 | // Create new category
80 | const newCategory = new ProductCategory({
81 | name: catName,
82 | slug: slugify(catName)
83 | });
84 | // Save to DB
85 | newCategory.save()
86 | .then(cat => res.status(201).json({ success: true, category: cat }))
87 | .catch(err => somethinError(res, err));
88 | }
89 | })
90 | .catch(err => somethinError(res, err));
91 |
92 | });
93 |
94 | // @route PUT /api/admin/product/categories/:id
95 | // @des Update product category by id
96 | // @access Private
97 | router.put('/:id', [auth, admin], (req, res) => {
98 | const { errors } = validateCategoryInput(req.body);
99 | if (errors) return res.status(400).json(errors);
100 |
101 | let catName = (req.body.name).toLowerCase();
102 | // Check category exist or not
103 | ProductCategory.findById(req.params.id)
104 | .then(cat => {
105 | if (!cat) {
106 | return res.status(404).json({ error: "Category Not Found!" });
107 | } else {
108 | // Update category
109 | cat.name = catName;
110 | cat.slug = slugify(catName);
111 | // Save to DB
112 | cat.save()
113 | .then(cat => res.status(200).json({ success: true, category: cat }))
114 | .catch(err => somethinError(res));
115 | }
116 | })
117 | .catch(err => somethinError(res, err));
118 |
119 | });
120 |
121 | // @route DELETE /api/admin/product/categories/:id
122 | // @des Delete product category by id
123 | // @access Private
124 | router.delete('/:id', [auth, admin], (req, res) => {
125 | // Check category exist or not
126 | ProductCategory.findById(req.params.id)
127 | .then(cat => {
128 | if (!cat) {
129 | return res.status(404).json({ error: "Category Not Found!" });
130 | } else {
131 | // Delete category
132 | cat.delete()
133 | .then(cat => res.status(200).json({ success: true, category: cat }))
134 | .catch(err => somethinError(res, err));
135 | }
136 | })
137 | .catch(err => somethinError(res, err));
138 |
139 | });
140 |
141 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/admin/product/productManufacturer.js:
--------------------------------------------------------------------------------
1 | const slugify = require('slugify');
2 | const express = require('express');
3 | const router = express.Router();
4 |
5 | // Middlewares
6 | const auth = require('../../../../middlewares/admin/auth');
7 | const admin = require('../../../../middlewares/admin/admin');
8 |
9 | // Validation
10 | const validateManufacturerInput = require('../../../../validations/admin/product/productManufacturer');
11 |
12 | // Model
13 | const ProductManufacturer = require('../../../../models/admin/product/productManufacturer');
14 |
15 | // Helpers
16 | const somethinError = require('../../../../helpers/something_error');
17 | // Image upload
18 | const { multerParser } = require('../../../../utils/cloudinary');
19 | // Delete image
20 | const { deleteServerImage } = require('../../../../utils/cloudinary');
21 |
22 |
23 | // @route GET /api/admin/product/manufacturers
24 | // @des Get all product manufacturers
25 | // @access Private
26 | router.get('/', [auth, admin], (req, res) => {
27 |
28 | ProductManufacturer.find()
29 | .sort({
30 | name: 1
31 | })
32 | .then(mfrs => {
33 | // No manufacturers found
34 | if (!mfrs) return res.status(404).json({
35 | success: false,
36 | error: 'No data found!'
37 | });
38 |
39 | return res.status(200).json({
40 | success: true,
41 | 'manufacturers': mfrs
42 | });
43 | })
44 | .catch(err => somethinError(res, err));
45 | });
46 |
47 | // @route GET /api/admin/product/manufacturers/:id
48 | // @des Get product manufacturers by id
49 | // @access Private
50 | router.get('/:id', [auth, admin], (req, res) => {
51 |
52 | ProductManufacturer.findById(req.params.id)
53 | .then(mfr => {
54 | // No manufacturers found
55 | if (!mfr) return res.status(404).json({
56 | success: false,
57 | error: 'No data found!'
58 | });
59 |
60 | return res.status(200).json({
61 | success: true,
62 | 'manufacturers': mfr
63 | });
64 | })
65 | .catch(err => somethinError(res, err));
66 | });
67 |
68 | // @route POST /api/admin/product/manufacturers
69 | // @des Create product manufacturer
70 | // @access Private
71 | router.post('/', [auth, admin, multerParser.single("image")], (req, res) => {
72 | const {
73 | errors
74 | } = validateManufacturerInput(req.body);
75 | if (errors) return res.status(400).json(errors);
76 | let mfrName = (req.body.name).toLowerCase();
77 | // Check manufacturer already exist or not
78 | ProductManufacturer.findOne({ name: mfrName })
79 | .then(mfr => {
80 | if (mfr) {
81 | return res.status(409).json({ name: "Manufacturer already exist!" });
82 | } else {
83 | // Create new manufacturer
84 | const newMfr = new ProductManufacturer({
85 | name: mfrName,
86 | slug: slugify(mfrName),
87 | url : req.body.url
88 | });
89 |
90 | // If image found
91 | if(req.file){
92 | newMfr.image = {
93 | id: req.file.public_id,
94 | url: req.file.url
95 | }
96 | }
97 |
98 | // Save to DB
99 | newMfr.save()
100 | .then(mfr => res.status(201).json({ success: true, manufacturer: mfr }))
101 | .catch(err => somethinError(res, err));
102 | }
103 | })
104 | .catch(err => somethinError(res, err));
105 |
106 | });
107 |
108 | // @route PUT /api/admin/product/manufacturer/:id
109 | // @des Update product manufacturer by id
110 | // @access Private
111 | router.put('/:id', [auth, admin, multerParser.single("image")], (req, res) => {
112 | const {
113 | errors
114 | } = validateManufacturerInput(req.body);
115 | if (errors) return res.status(400).json(errors);
116 |
117 | let mfrName = (req.body.name).toLowerCase();
118 | // Check manufacturer exist or not
119 | ProductManufacturer.findById(req.params.id)
120 | .then(mfr => {
121 | if (!mfr) {
122 | return res.status(404).json({
123 | error: "Manufacturer Not Found!"
124 | });
125 | } else {
126 | // Update manufacturer
127 | mfr.name = mfrName;
128 | mfr.slug = slugify(mfrName);
129 | mfr.url = req.body.url;
130 | // If new image found
131 | if(req.file){
132 | // Delete old image from server
133 | if(mfr.image){
134 | const result = deleteServerImage(mfr.image.id);
135 | if(result) console.log(result);
136 | }
137 | // Set new image
138 | mfr.image = {
139 | id: req.file.public_id,
140 | url: req.file.url
141 | }
142 | }
143 | // Save to DB
144 | mfr.save()
145 | .then(mfr => res.status(200).json({
146 | success: true,
147 | manufacturer: mfr
148 | }))
149 | .catch(err => somethinError(res));
150 | }
151 | })
152 | .catch(err => somethinError(res, err));
153 |
154 | });
155 |
156 | // @route DELETE /api/admin/product/manufacturer/:id
157 | // @des Delete product manufacturer by id
158 | // @access Private
159 | router.delete('/:id', [auth, admin], (req, res) => {
160 | // Check manufacturer exist or not
161 | ProductManufacturer.findById(req.params.id)
162 | .then(mfr => {
163 | if (!mfr) {
164 | return res.status(404).json({
165 | error: "Manufacturer Not Found!"
166 | });
167 | } else {
168 | // Delete old image from server
169 | if(mfr.image){
170 | const result = deleteServerImage(mfr.image.id);
171 | if(result) console.log(result);
172 | }
173 | // Delete manufacturer
174 | mfr.delete()
175 | .then(mfr => res.status(200).json({
176 | success: true,
177 | manufacturer: mfr
178 | }))
179 | .catch(err => somethinError(res, err));
180 | }
181 | })
182 | .catch(err => somethinError(res, err));
183 |
184 | });
185 |
186 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/admin/product/productSubCategory.js:
--------------------------------------------------------------------------------
1 | const slugify = require('slugify');
2 | const express = require('express');
3 | const router = express.Router();
4 |
5 | // Middlewares
6 | const auth = require('../../../../middlewares/admin/auth');
7 | const admin = require('../../../../middlewares/admin/admin');
8 |
9 | // Validation
10 | const validateSubCategoryInput = require('../../../../validations/admin/product/productSubCategory');
11 |
12 | // Model
13 | const ProductSubCategory = require('../../../../models/admin/product/productSubCategory');
14 |
15 | // Helpers
16 | const somethinError = require('../../../../helpers/something_error');
17 |
18 | // @route GET /api/admin/product/sub-categories
19 | // @des Get all product sub category
20 | // @access Private
21 | router.get('/', [auth, admin], (req, res) => {
22 |
23 | ProductSubCategory.find()
24 | .populate('category', ['_id','name'])
25 | .sort({ name: 1 })
26 | .then(subCats => {
27 | // No category found
28 | if (!subCats) return res.status(404).json({ success: false, error: 'No data found!' });
29 |
30 | return res.status(200).json({ success: true, 'subCategories': subCats });
31 | })
32 | .catch(err => somethinError(res, err));
33 | });
34 |
35 | // @route GET /api/admin/product/sub-categories/:id
36 | // @des Get product sub category by id
37 | // @access Private
38 | router.get('/:id', [auth, admin], (req, res) => {
39 |
40 | ProductSubCategory.findById(req.params.id)
41 | .populate('category', ['_id', 'name'])
42 | .then(subCat => {
43 | // No category found
44 | if (!subCat) return res.status(404).json({ success: false, error: 'No data found!' });
45 |
46 | return res.status(200).json({ success: true, 'subCategory': subCat });
47 | })
48 | .catch(err => somethinError(res, err));
49 | });
50 |
51 | // @route POST /api/admin/product/sub-categories
52 | // @des Create product sub category
53 | // @access Private
54 | router.post('/', [auth, admin], (req, res) => {
55 | const { errors } = validateSubCategoryInput(req.body);
56 | if (errors) return res.status(400).json(errors);
57 |
58 | let subCatName = (req.body.name).toLowerCase();
59 | // Check sub category already exist or not
60 | ProductSubCategory.findOne({ name: subCatName })
61 | .then(cat => {
62 | if (cat) {
63 | return res.status(409).json({ name: "Sub Category already exist!" });
64 | } else {
65 | // Create new sub category
66 | const newSubCategory = new ProductSubCategory({
67 | category: req.body.category,
68 | name: subCatName,
69 | slug: slugify(subCatName)
70 | });
71 | // Save to DB
72 | newSubCategory.save()
73 | .then(subCat => {
74 | // Return sub category with category details
75 | ProductSubCategory.findById(subCat._id)
76 | .populate('category')
77 | .then(subCat=>{
78 | res.status(201).json({ success: true, subCategory: subCat })
79 | })
80 | .catch(err => somethinError(res));
81 | })
82 | .catch(err => somethinError(res));
83 | }
84 | })
85 | .catch(err => somethinError(res, err));
86 |
87 | });
88 |
89 | // @route PUT /api/admin/product/sub-categories/:id
90 | // @des Update product sub category by id
91 | // @access Private
92 | router.put('/:id', [auth, admin], (req, res) => {
93 | const { errors } = validateSubCategoryInput(req.body);
94 | if (errors) return res.status(400).json(errors);
95 |
96 | let subCatName = (req.body.name).toLowerCase();
97 | // Check category exist or not
98 | ProductSubCategory.findById(req.params.id)
99 | .then(subCat => {
100 | if (!subCat) {
101 | return res.status(404).json({ error: "Sub Category Not Found!" });
102 | } else {
103 | // Update sub category
104 | subCat.category = req.body.category;
105 | subCat.name = subCatName;
106 | subCat.slug = slugify(subCatName);
107 | // Save to DB
108 | subCat.save()
109 | .then(subCat => {
110 | // Return sub category with category details
111 | ProductSubCategory.findById(subCat._id)
112 | .populate('category')
113 | .then(subCat=>{
114 | res.status(201).json({ success: true, subCategory: subCat })
115 | })
116 | .catch(err => somethinError(res));
117 | })
118 | .catch(err => somethinError(res));
119 | }
120 | })
121 | .catch(err => somethinError(res, err));
122 |
123 | });
124 |
125 | // @route DELETE /api/admin/product/sub-categories/:id
126 | // @des Delete product sub category by id
127 | // @access Private
128 | router.delete('/:id', [auth, admin], (req, res) => {
129 | // Check sub category exist or not
130 | ProductSubCategory.findById(req.params.id)
131 | .then(subCat => {
132 | if (!subCat) {
133 | return res.status(404).json({ error: "Sub Category Not Found!" });
134 | } else {
135 | // Delete sub category
136 | subCat.delete()
137 | .then(subCat => res.status(200).json({ success: true, subCategory: subCat }))
138 | .catch(err => somethinError(res));
139 | }
140 | })
141 | .catch(err => somethinError(res, err));
142 |
143 | });
144 |
145 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/client/auth.js:
--------------------------------------------------------------------------------
1 | const bcrypt = require('bcryptjs');
2 | const express = require('express');
3 | const router = express.Router();
4 |
5 | // Helpers
6 | const { generateJwtToken } = require('../../../helpers/jwt_access_token');
7 | const { somethingError } = require('../../../helpers/errors_response');
8 |
9 | // Validation
10 | const validateRegisterInput = require('../../../validations/client/auth/register');
11 | const validateLoginInput = require('../../../validations/client/auth/login');
12 | // Model
13 | const Customer = require('../../../models/client/customer');
14 |
15 | // @route POST /api/login
16 | // @des Customer login
17 | // @access Public
18 | router.post('/login', (req, res) => {
19 | // Validate customer input
20 | const { errors } = validateLoginInput(req.body);
21 | if (errors) return res.status(400).json(errors);
22 |
23 | const email = req.body.email;
24 | const password = req.body.password;
25 |
26 | // Check cutomer email
27 | Customer.findOne({ email: email })
28 | .then(customer => {
29 | if (!customer) {
30 | return res.status(404).json({ error: true, email: "Email or password incorrect!" });
31 | } else {
32 | bcrypt.compare(password, customer.password)
33 | .then(isMatch => {
34 | if (isMatch) {
35 | // Payload
36 | const payload = {
37 | id: customer._id,
38 | name: customer.name,
39 | email: customer.email,
40 | status: customer.status
41 | };
42 | // Generate jwt access token
43 | generateJwtToken(payload, (token) => {
44 | if (token) {
45 | return res.status(200).json({
46 | success: true,
47 | token: "Bearer " + token,
48 | customer: payload
49 | });
50 | } else {
51 | throw new Error();
52 | }
53 | });
54 | } else {
55 | throw new Error();
56 | }
57 | })
58 | .catch(err => res.status(404).json({ error: true, email: "Email or password incorrect!" }))
59 | }
60 | })
61 | .catch(err => res.status(404).json({ error: true, email: "Email or password incorrect!" }));
62 | });
63 |
64 | // @route POST /api/register
65 | // @des Client register
66 | // @access Public
67 | router.post('/register', (req, res) => {
68 | const { errors } = validateRegisterInput(req.body);
69 | if (errors) return res.status(400).json(errors);
70 |
71 | // Check customer already exist or not
72 | Customer.findOne({email: req.body.email})
73 | .then(customer => {
74 | // If customer exist return
75 | if(customer){
76 | return res.status(409).json({"error": "customer already exist"})
77 | }else{
78 | // Extract customer info
79 | const customer = new Customer({
80 | name: req.body.name,
81 | email: req.body.email,
82 | password: req.body.password
83 | });
84 |
85 | // Generate password
86 | // Salt
87 | bcrypt.genSalt(10, (err, salt)=>{
88 | if(err) throw err;
89 | bcrypt.hash(customer.password, salt, (err, hash)=>{
90 | if(err) throw err;
91 | customer.password = hash;
92 | // Store customer to DB
93 | customer.save()
94 | .then(customer => {
95 | res.status(201).json({
96 | success: true,
97 | customer: {
98 | name: customer.name,
99 | email: customer.email,
100 | status: customer.status
101 | }
102 | })
103 | })
104 | .catch(err => somethingError(res, err));
105 | });
106 | });
107 | }
108 | })
109 | .catch(err => somethingError(res, err));
110 | });
111 |
112 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/client/order/order.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 | const config = require('config');
4 | const stripe = require('stripe')(config.get('stripe.secretKey'));
5 |
6 | // Models
7 | const Product = require('../../../../models/admin/product/product');
8 |
9 | // Helpers
10 | const { noDataFound, somethingError } = require('../../../../helpers/errors_response');
11 |
12 | // @route POST /api/product/checkout
13 | // @des Checkout product
14 | // @access Privte
15 | router.post('/checkout', (req, res) => {
16 | // Extract product id
17 | let productIds = [];
18 | req.body.products.map(item => {
19 | productIds.push(item.productId);
20 | })
21 | // Get all the ordered product
22 | Product.find({
23 | _id: {
24 | $in: productIds
25 | }
26 | }).then(products => {
27 | // Total price
28 | let total = 0;
29 | // Calculate total amount
30 | products.map(product => {
31 | // Get product quantity
32 | let filterProduct = req.body.products.filter(item => item.productId == product._id);
33 | let quantity = filterProduct[0].quantity;
34 |
35 | if (product.isFlashSale) {
36 | total += quantity * product.flashSale.flashPrice;
37 | } else {
38 | total += quantity * product.price;
39 | }
40 | });
41 |
42 | // Create the charge object with data from the Vue.js client
43 | var newCharge = {
44 | amount: total*100,
45 | currency: "usd",
46 | source: req.body.stripeToken, // obtained with Stripe.js on the client side
47 | };
48 |
49 | // Call the stripe objects helper functions to trigger a new charge
50 | stripe.charges.create(newCharge, function (err, charge) {
51 | // send response
52 | if (err) {
53 | console.error(err);
54 | res.json({ error: err, charge: false });
55 | } else {
56 |
57 | // TODO : pass data to order table
58 |
59 | // send response with charge data
60 | res.json({ success: true, charge: charge });
61 | }
62 | });
63 | })
64 |
65 | });
66 |
67 |
68 |
69 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/client/product/category.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | // Models
5 | const Category = require('../../../../models/admin/product/productCategory');
6 | const Product = require('../../../../models/admin/product/product');
7 |
8 | // Helpers
9 | const { noDataFound, somethingError } = require('../../../../helpers/errors_response');
10 |
11 | // @route GET /api/product/categories
12 | // @des Get all category
13 | // @access Public
14 | router.get('/', (req, res) => {
15 | Category.find()
16 | .then(categories => {
17 | if (!categories) return noDataFound(res);
18 | return res.status(200).json({
19 | success: true,
20 | categories: categories
21 | })
22 | })
23 | .catch(err => somethingError(res, err));
24 | });
25 |
26 | // @route GET /api/product/categories/:slug/products
27 | // @des Get all products belongs to this category
28 | // @access Public
29 | router.get('/:slug/products', (req, res) => {
30 | Category.find({ slug: req.params.slug })
31 | .then(cat => {
32 | if (!cat || cat.length < 1) return noDataFound(res);
33 | // Get product belongs to this category
34 | Product.find({ category: cat[0]._id })
35 | .populate('category')
36 | .populate('subCategory')
37 | .then(products => {
38 | if (!products || products.length < 1) return noDataFound(res);
39 |
40 | return res.status(200).json({
41 | success: true,
42 | products: products
43 | })
44 | })
45 | .catch(err => somethingError(res, err));
46 | })
47 | .catch(err => somethingError(res, err));
48 | });
49 |
50 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/client/product/product.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | // Models
5 | const Product = require('../../../../models/admin/product/product');
6 |
7 | // Helpers
8 | const { noDataFound, somethingError } = require('../../../../helpers/errors_response');
9 |
10 | // @route GET /api/product/products
11 | // @des Get all products
12 | // @access Public
13 | router.get('/', (req, res) => {
14 | // Pagination
15 | const perPage = req.query.perPage ? parseInt(req.query.perPage) : 20;
16 | const pageNumber = req.query.pageNumber ? parseInt(req.query.pageNumber) : 1;
17 |
18 | Product.find()
19 | .skip((pageNumber - 1) * perPage)
20 | .limit(perPage)
21 | .populate('category')
22 | .populate('subCategory')
23 | .then(products => {
24 | if (!products) return noDataFound(res);
25 | return res.status(200).json({
26 | success: true,
27 | totoalProduct: products.length,
28 | products: products
29 | })
30 | })
31 | .catch(err => somethingError(res, err));
32 | });
33 |
34 | // @route GET /api/product/products/:slug
35 | // @des Get product by slug
36 | // @access Public
37 | router.get('/:slug', (req, res) => {
38 | Product.find({slug: req.params.slug})
39 | .limit(1)
40 | .populate('category')
41 | .populate('subCategory')
42 | .then(product => {
43 | if (!product) return noDataFound(res);
44 | return res.status(200).json({
45 | success: true,
46 | product: product
47 | })
48 | })
49 | .catch(err => somethingError(res, err));
50 | });
51 |
52 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/routes/client/product/subCategory.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | // Models
5 | const SubCategory = require('../../../../models/admin/product/productSubCategory');
6 | const Product = require('../../../../models/admin/product/product');
7 |
8 | // Helpers
9 | const { noDataFound, somethingError } = require('../../../../helpers/errors_response');
10 |
11 | // @route GET /api/product/sub-categories
12 | // @des Get all sub category
13 | // @access Public
14 | router.get('/', (req, res) => {
15 | SubCategory.find()
16 | .then(subCategories => {
17 | if (!subCategories) return noDataFound(res);
18 | return res.status(200).json({
19 | success: true,
20 | subCategories: subCategories
21 | })
22 | })
23 | .catch(err => somethingError(res, err));
24 | });
25 |
26 | // @route GET /api/product/sub-categories/:slug/products
27 | // @des Get all products belongs to this sub category
28 | // @access Public
29 | router.get('/:slug/products', (req, res) => {
30 | SubCategory.find({ slug: req.params.slug })
31 | .then(scat => {
32 | if (!scat || scat.length < 1) return noDataFound(res);
33 | // Get product belongs to this sub category
34 | Product.find({ subCategory: scat[0]._id })
35 | .populate('category')
36 | .populate('subCategory')
37 | .then(products => {
38 | if (!products || products.length < 1) return noDataFound(res);
39 |
40 | return res.status(200).json({
41 | success: true,
42 | products: products
43 | })
44 | })
45 | .catch(err => somethingError(res, err));
46 | })
47 | .catch(err => somethingError(res, err));
48 | });
49 |
50 | module.exports = router;
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | const passport = require("passport");
2 | const config = require('config');
3 | const startupDebugger = require('debug')('app:startup');
4 | const dbDebugger = require('debug')('app:db');
5 | const morgan = require('morgan');
6 | const helmet = require('helmet');
7 | const bodyParser = require('body-parser');
8 | const mongoose = require('mongoose');
9 | const express = require('express');
10 | const app = express();
11 |
12 | // DB connection
13 | mongoose.connect(config.get('mongoURI'), { useNewUrlParser: true })
14 | .then(() => console.log('Connected to mongoDB'))
15 | .catch(err => console.log(err));
16 |
17 | // Load body parser
18 | app.use(bodyParser.json());
19 | app.use(bodyParser.urlencoded({ extended: false }));
20 |
21 | // Helmet
22 | app.use(helmet());
23 |
24 | // Passport middleware
25 | app.use(passport.initialize());
26 |
27 | // Passport Config
28 | require("./utils/passport")(passport);
29 |
30 | // Helper for development environment
31 | if (app.get('env') == 'development') {
32 | app.use(morgan('tiny'));
33 | startupDebugger('Morgan enable...');
34 | }
35 |
36 | // CORS middleware
37 | const allowCrossDomain = function(req, res, next) {
38 | res.header('Access-Control-Allow-Origin', '*');
39 | res.header('Access-Control-Allow-Methods', '*');
40 | res.header('Access-Control-Allow-Headers', '*');
41 | next();
42 | }
43 |
44 | app.use(allowCrossDomain)
45 |
46 | // All routes
47 | require('./api/index')(app);
48 |
49 | // Listener
50 | const port = process.env.PORT || 3000;
51 | app.listen(port, () => console.log(`Listing on port ${3000}`));
--------------------------------------------------------------------------------
/server/config/custom-environment-variables.json:
--------------------------------------------------------------------------------
1 | {
2 | "mail" : {
3 | "password" : "app_password"
4 | },
5 | "mongoURI" : "MONGO_URI",
6 | "cloudinary": {
7 | "cloud_name": "CLOUD_NAME",
8 | "api_key": "CLOUD_API_KEY",
9 | "api_secret": "CLOUD_API_SECRET"
10 | },
11 | "stripe": {
12 | "secretKey": "STRIPE_KEY"
13 | }
14 | }
--------------------------------------------------------------------------------
/server/config/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "My Express App"
3 | }
--------------------------------------------------------------------------------
/server/config/production.json:
--------------------------------------------------------------------------------
1 | {
2 | "mongoURI" : "",
3 | "jwt": {
4 | "secretOrKey" : "secret",
5 | "tokenExpireIn" : 3600
6 | }
7 | }
--------------------------------------------------------------------------------
/server/helpers/errors_response.js:
--------------------------------------------------------------------------------
1 | const noDataFound = (res) => {
2 | return res.status(404).json({ success: false, error: 'No data found!' });
3 | }
4 |
5 | const somethingError = (res, err) => {
6 | res.status(400).json({ "error": "Something error!" });
7 | console.log(err);
8 | }
9 |
10 | exports.noDataFound = noDataFound;
11 | exports.somethingError = somethingError;
--------------------------------------------------------------------------------
/server/helpers/jwt_access_token.js:
--------------------------------------------------------------------------------
1 | const config = require('config');
2 | const jwt = require('jsonwebtoken');
3 |
4 | module.exports.generateJwtToken = (payload, callback) => {
5 | // Sign jwt
6 | jwt.sign(
7 | payload,
8 | config.get('jwt.secretOrKey'),
9 | { expiresIn: config.get('jwt.tokenExpireIn') },
10 | (err, token) => {
11 | if (token) {
12 | callback(token);
13 | } else {
14 | return false;
15 | }
16 | }
17 | );
18 | }
--------------------------------------------------------------------------------
/server/helpers/something_error.js:
--------------------------------------------------------------------------------
1 | const dbDebugger = require('debug')('app:db');
2 |
3 | module.exports = (res, err)=>{
4 | res.status(400).json({ "error": "Something error!" });
5 | console.log(err);
6 | dbDebugger(err);
7 | }
--------------------------------------------------------------------------------
/server/middlewares/admin/admin.js:
--------------------------------------------------------------------------------
1 | module.exports = (req, res, next)=>{
2 | if(req.user.isAdmin){
3 | next();
4 | }else{
5 | return res.status(401).json({
6 | success: false,
7 | message: 'Unauthorized'
8 | })
9 | }
10 | }
--------------------------------------------------------------------------------
/server/middlewares/admin/auth.js:
--------------------------------------------------------------------------------
1 | const passport = require("passport");
2 | module.exports = passport.authenticate("jwt", { session: false });
--------------------------------------------------------------------------------
/server/models/admin/product/option.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const optionSchema = new Schema({
5 | optionGroup: { type: Schema.Types.ObjectId, ref: "OptionGroup" },
6 | name: { type: String, required: true, minlength: 1, maxlength: 200 },
7 | other: { type: String, maxlength: 200 },
8 | note: { type: String, maxlength:200 },
9 | created_at: { type: Date, default: Date.now() }
10 | });
11 |
12 | const Option = mongoose.model('Option', optionSchema);
13 |
14 | module.exports = Option;
--------------------------------------------------------------------------------
/server/models/admin/product/optionGroup.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const optionGroupSchema = new Schema({
5 | name: { type: String, required: true, minlength: 2, maxlength: 200 },
6 | slug: { type: String, required: true, minlength: 2 },
7 | created_at: {type: Date, default: Date.now()}
8 | });
9 |
10 | const OptionGroup = mongoose.model('OptionGroup', optionGroupSchema);
11 |
12 | module.exports = OptionGroup;
--------------------------------------------------------------------------------
/server/models/admin/product/product.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const productSchema = new Schema({
5 | sku: {
6 | type: String,
7 | required: true
8 | },
9 | category: {
10 | type: mongoose.Types.ObjectId,
11 | ref: "ProductCategory",
12 | required: true
13 | },
14 | subCategory: {
15 | type: mongoose.Types.ObjectId,
16 | ref: "ProductSubCategory",
17 | required: true
18 | },
19 | manufacturer: {
20 | _id: String,
21 | name: String,
22 | image: String
23 | },
24 | name: {
25 | type: String,
26 | minlength: 2,
27 | maxlength: 400,
28 | required: true
29 | },
30 | slug: {
31 | type: String,
32 | minlength: 2,
33 | maxlength: 500,
34 | required: true,
35 | },
36 | model: {
37 | type: String,
38 | minlength: 2,
39 | maxlength: 200
40 | },
41 | weight: Number,
42 | sortDesc: {
43 | type: String,
44 | minlength: 2,
45 | maxlength: 200,
46 | required: true
47 | },
48 | longDesc: {
49 | type: String,
50 | },
51 | price: {
52 | type: Number,
53 | required: true
54 | },
55 | stock: {
56 | type: Number,
57 | required: true
58 | },
59 | inStock: {
60 | type: Boolean,
61 | default: true // Default product in stock
62 | },
63 | isFeature: {
64 | type: Boolean,
65 | default: false // Default not a feature product
66 | },
67 | status: {
68 | type: Boolean,
69 | required: true,
70 | default: true // Default active
71 | },
72 | isFlashSale: {
73 | type: Boolean,
74 | default: false
75 | },
76 | flashSale: Object,
77 | isSpecialSale: {
78 | type: Boolean,
79 | default: false
80 | },
81 | specialSale: Object,
82 | attributes: Array,
83 | thumb: {
84 | type: Object,
85 | required: true
86 | },
87 | images: Array,
88 | created_at: {
89 | type: Date,
90 | default: Date.now()
91 | },
92 | modified_at: {
93 | type: Date,
94 | default: Date.now()
95 | }
96 | });
97 |
98 | const Product = mongoose.model('Product', productSchema);
99 |
100 | module.exports = Product;
--------------------------------------------------------------------------------
/server/models/admin/product/productCategory.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const productCategory = new Schema({
5 | name: { type: String, required: true, minlength: 2, maxlength: 200 },
6 | slug: { type: String, required: true, minlength: 2 },
7 | created_at: {type: Date, default: Date.now()}
8 | });
9 |
10 | const ProductCategory = mongoose.model('ProductCategory', productCategory);
11 |
12 | module.exports = ProductCategory;
--------------------------------------------------------------------------------
/server/models/admin/product/productManufacturer.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const manufacturerSchema = new Schema({
5 | name: { type: String, required: true, minlength: 2, maxlength: 200 },
6 | slug: { type: String, required: true, minlength: 2 },
7 | url: { type: String, minlength: 2 },
8 | image: { type: Object },
9 | created_at: { type: Date, default: Date.now() }
10 | });
11 |
12 | const ProductManufacturer = mongoose.model('ProductManufacturer', manufacturerSchema);
13 |
14 | module.exports = ProductManufacturer;
--------------------------------------------------------------------------------
/server/models/admin/product/productSubCategory.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const subCategorySchema = new Schema({
5 | category: { type: Schema.Types.ObjectId, ref: "ProductCategory" },
6 | name: { type: String, required: true, minlength: 2, maxlength: 200 },
7 | slug: { type: String, required: true, minlength: 2 },
8 | created_at: { type: Date, default: Date.now() }
9 | });
10 |
11 | const ProductSubCategory = mongoose.model('ProductSubCategory', subCategorySchema);
12 |
13 | module.exports = ProductSubCategory;
--------------------------------------------------------------------------------
/server/models/admin/user.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const userSchema = new Schema({
5 | name: { type: String, required: true, minlength: 2, maxlength: 100 },
6 | email: { type: String, required: true, minlength: 5, maxlength: 255 },
7 | password: { type: String, required: true, minlength: 6, maxlength: 255 },
8 | isAdmin: { type: Boolean, default: true },
9 | status: { type: Boolean, default: true }
10 | });
11 |
12 | const User = mongoose.model('User', userSchema);
13 | module.exports = User;
--------------------------------------------------------------------------------
/server/models/client/customer.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const customerSchema = new Schema({
5 | name: { type: String, required: true, minlength: 2, maxlength: 100 },
6 | email: { type: String, required: true, minlength: 5, maxlength: 255 },
7 | password: { type: String, required: true, minlength: 6, maxlength: 255 },
8 | status: { type: Boolean, default: true }
9 | });
10 |
11 | const Customer = mongoose.model('Customer', customerSchema);
12 | module.exports = Customer;
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "bcryptjs": "^2.4.3",
14 | "body-parser": "^1.18.3",
15 | "cloudinary": "^1.14.0",
16 | "config": "^3.0.1",
17 | "debug": "^4.1.1",
18 | "express": "^4.16.4",
19 | "helmet": "^3.16.0",
20 | "joi": "^14.3.1",
21 | "joi-objectid": "^2.0.0",
22 | "jsonwebtoken": "^8.5.1",
23 | "lodash": "^4.17.11",
24 | "mongoose": "^5.4.19",
25 | "morgan": "^1.9.1",
26 | "multer": "^1.4.1",
27 | "multer-storage-cloudinary": "^2.2.1",
28 | "passport": "^0.4.0",
29 | "passport-jwt": "^4.0.0",
30 | "slugify": "^1.3.4",
31 | "stripe": "^6.31.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/server/utils/cloudinary.js:
--------------------------------------------------------------------------------
1 | const multer = require("multer");
2 | const cloudinary = require("cloudinary");
3 | const cloudinaryStorage = require("multer-storage-cloudinary");
4 | const config = require('config');
5 |
6 | cloudinary.config({
7 | cloud_name: config.get('cloudinary.cloud_name'),
8 | api_key: config.get('cloudinary.api_key'),
9 | api_secret: config.get('cloudinary.api_secret')
10 | });
11 | const storage = cloudinaryStorage({
12 | cloudinary: cloudinary,
13 | folder: "mfr",
14 | allowedFormats: ["jpg", "png"],
15 | transformation: [{ width: 500, height: 500, crop: "limit" }]
16 | });
17 | const multerParser = multer({ storage: storage });
18 |
19 | // Delete image from server
20 | const deleteServerImage = (imageId)=>{
21 | cloudinary.v2.uploader.destroy(imageId, function(error,result) {
22 | if(error) return error;
23 | return true;
24 | });
25 | }
26 |
27 | exports.multerParser = multerParser;
28 | exports.deleteServerImage = deleteServerImage;
29 |
30 |
--------------------------------------------------------------------------------
/server/utils/passport.js:
--------------------------------------------------------------------------------
1 | const config = require('config');
2 | const JwtStrategy = require("passport-jwt").Strategy;
3 | const ExtractJwt = require("passport-jwt").ExtractJwt;
4 | const User = require("../models/admin/user");
5 |
6 | const opts = {};
7 | opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
8 | opts.secretOrKey = config.get('jwt.secretOrKey');
9 |
10 | module.exports = passport => {
11 | passport.use(
12 | new JwtStrategy(opts, (jwt_payload, done) => {
13 | User.findById(jwt_payload.id)
14 | .then(user => {
15 | if (user) {
16 | return done(null, user);
17 | }
18 | return done(null, false);
19 | })
20 | .catch(err => console.log(err));
21 | })
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/server/utils/setValidationErrors.js:
--------------------------------------------------------------------------------
1 | module.exports = (errorPayload) => {
2 | let errors = {};
3 | if (errorPayload) {
4 | // Set all errors
5 | errorPayload.details.map(err => {
6 | errors[err.path] = err.message;
7 | });
8 | } else {
9 | errors = false;
10 | }
11 |
12 | return { errors };
13 | }
--------------------------------------------------------------------------------
/server/validations/admin/auth/login.js:
--------------------------------------------------------------------------------
1 | const Joi = require("joi");
2 |
3 | module.exports = data => {
4 | let errors = {};
5 | const schema = {
6 | email: Joi.string()
7 | .min(5)
8 | .max(255)
9 | .required()
10 | .email(),
11 | password: Joi.string()
12 | .min(6)
13 | .max(50)
14 | .required()
15 | };
16 |
17 | const { error } = Joi.validate(data, schema, { abortEarly: false });
18 | if (error) {
19 | // Set all errors
20 | error.details.map(err => {
21 | errors[err.path] = err.message;
22 | });
23 | } else {
24 | errors = false;
25 | }
26 |
27 | return { errors };
28 | };
29 |
--------------------------------------------------------------------------------
/server/validations/admin/auth/register.js:
--------------------------------------------------------------------------------
1 | const Joi = require("joi");
2 |
3 | module.exports = data => {
4 | let errors = {};
5 | const schema = {
6 | name: Joi.string()
7 | .min(2)
8 | .max(100)
9 | .required(),
10 | email: Joi.string()
11 | .min(5)
12 | .max(255)
13 | .required()
14 | .email(),
15 | password: Joi.string()
16 | .min(6)
17 | .max(50)
18 | .required(),
19 | password_confirmation: Joi.any()
20 | .valid(Joi.ref('password'))
21 | .required()
22 | .options({ language: { any: { allowOnly: 'must match password' } } })
23 | };
24 |
25 | const { error } = Joi.validate(data, schema, { abortEarly: false });
26 | if (error) {
27 | // Set all errors
28 | error.details.map(err => {
29 | errors[err.path] = err.message;
30 | });
31 | } else {
32 | errors = false;
33 | }
34 |
35 | return { errors };
36 | };
37 |
--------------------------------------------------------------------------------
/server/validations/admin/product/option.js:
--------------------------------------------------------------------------------
1 | const setValidationErrors = require('../../../utils/setValidationErrors');
2 | const Joi = require('joi')
3 | Joi.objectId = require('joi-objectid')(Joi)
4 |
5 | module.exports = data => {
6 | let errors = {};
7 | const schema = {
8 | optionGroup: Joi.objectId().required(),
9 | name: Joi.string()
10 | .min(1)
11 | .max(200)
12 | .required()
13 | .trim(),
14 | other: Joi.string()
15 | .max(200)
16 | .allow(null)
17 | .allow('')
18 | .trim(),
19 | note: Joi.string()
20 | .max(200)
21 | .allow(null)
22 | .allow('')
23 | .trim()
24 |
25 | };
26 |
27 | const { error } = Joi.validate(data, schema, { abortEarly: false });
28 | // Set validation errors
29 | return setValidationErrors(error);
30 | };
31 |
--------------------------------------------------------------------------------
/server/validations/admin/product/optionGroup.js:
--------------------------------------------------------------------------------
1 | const setValidationErrors = require('../../../utils/setValidationErrors');
2 | const Joi = require("joi");
3 |
4 | module.exports = data => {
5 | const schema = {
6 | name: Joi.string()
7 | .min(2)
8 | .max(200)
9 | .required()
10 | .trim()
11 | };
12 |
13 | const { error } = Joi.validate(data, schema, { abortEarly: false });
14 | // Set validation errors
15 | return setValidationErrors(error);
16 | };
17 |
--------------------------------------------------------------------------------
/server/validations/admin/product/product.js:
--------------------------------------------------------------------------------
1 | const setValidationErrors = require('../../../utils/setValidationErrors');
2 | const Joi = require('joi')
3 | Joi.objectId = require('joi-objectid')(Joi)
4 |
5 | module.exports = data => {
6 | const schema = {
7 | sku: Joi.string().required().trim(),
8 | category: Joi.objectId().required(),
9 | subCategory: Joi.objectId().required(),
10 | hasManufacturer: Joi.allow(null).allow(''),
11 | manufacturerId: Joi.allow(null).allow(''),
12 | manufacturerName: Joi.allow(null).allow(''),
13 | manufacturerImage: Joi.allow(null).allow(''),
14 | name: Joi.string()
15 | .min(1)
16 | .max(400)
17 | .required()
18 | .trim(),
19 | model: Joi.string().min(2).max(200).allow(null).allow(''),
20 | weight: Joi.number().allow(null).allow(''),
21 | sortDesc: Joi.string()
22 | .min(2)
23 | .max(200)
24 | .required()
25 | .trim(),
26 | longDesc: Joi.string().allow(null).allow(''),
27 | price: Joi.number().required(),
28 | stock: Joi.number().required(),
29 | inStock: Joi.boolean().allow(null).allow(''),
30 | isFeature: Joi.boolean().allow(null).allow(''),
31 | status: Joi.boolean().required(),
32 | isFlashSale: Joi.allow(null).allow(''),
33 | flashPrice: Joi.allow(null).allow(''),
34 | flashStart: Joi.allow(null).allow(''),
35 | flashEnd: Joi.allow(null).allow(''),
36 | flashStatus: Joi.allow(null).allow(''),
37 | isSpecialSale: Joi.allow(null).allow(''),
38 | specialPrice: Joi.allow(null).allow(''),
39 | specialExpire: Joi.allow(null).allow(''),
40 | specialStatus: Joi.allow(null).allow(''),
41 | attributes: Joi.array().allow(null).allow(''),
42 | thumb: Joi.string().allow(null).allow(''),
43 | images: Joi.array().allow(null).allow(''),
44 |
45 | };
46 |
47 | const {
48 | error
49 | } = Joi.validate(data, schema, {
50 | abortEarly: false
51 | });
52 | // Set validation errors
53 | return setValidationErrors(error);
54 | };
--------------------------------------------------------------------------------
/server/validations/admin/product/productCategory.js:
--------------------------------------------------------------------------------
1 | const setValidationErrors = require('../../../utils/setValidationErrors');
2 | const Joi = require("joi");
3 |
4 | module.exports = data => {
5 | const schema = {
6 | name: Joi.string()
7 | .min(2)
8 | .max(200)
9 | .required()
10 | .trim()
11 | };
12 |
13 | const { error } = Joi.validate(data, schema, { abortEarly: false });
14 | // Set validation errors
15 | return setValidationErrors(error);
16 | };
17 |
--------------------------------------------------------------------------------
/server/validations/admin/product/productManufacturer.js:
--------------------------------------------------------------------------------
1 | const setValidationErrors = require('../../../utils/setValidationErrors');
2 | const Joi = require("joi");
3 |
4 | module.exports = data => {
5 | const schema = {
6 | name: Joi.string()
7 | .min(2)
8 | .max(200)
9 | .required()
10 | .trim(),
11 | url: Joi.string()
12 | .allow(null)
13 | .allow('')
14 | .uri()
15 | .min(2)
16 | .trim(),
17 | image: Joi.allow(null)
18 | };
19 |
20 | const {
21 | error
22 | } = Joi.validate(data, schema, {
23 | abortEarly: false
24 | });
25 | // Set validation errors
26 | return setValidationErrors(error);
27 | };
--------------------------------------------------------------------------------
/server/validations/admin/product/productSubCategory.js:
--------------------------------------------------------------------------------
1 | const setValidationErrors = require('../../../utils/setValidationErrors');
2 | const Joi = require('joi')
3 | Joi.objectId = require('joi-objectid')(Joi)
4 |
5 | module.exports = data => {
6 | const schema = {
7 | category: Joi.objectId().required(),
8 | name: Joi.string()
9 | .min(2)
10 | .max(200)
11 | .required()
12 | .trim()
13 | };
14 |
15 | const { error } = Joi.validate(data, schema, { abortEarly: false });
16 | // Set validation errors
17 | return setValidationErrors(error);
18 | };
19 |
--------------------------------------------------------------------------------
/server/validations/client/auth/login.js:
--------------------------------------------------------------------------------
1 | const Joi = require("joi");
2 |
3 | module.exports = data => {
4 | let errors = {};
5 | const schema = {
6 | email: Joi.string()
7 | .min(5)
8 | .max(255)
9 | .required()
10 | .email(),
11 | password: Joi.string()
12 | .min(6)
13 | .max(50)
14 | .required()
15 | };
16 |
17 | const { error } = Joi.validate(data, schema, { abortEarly: false });
18 | if (error) {
19 | // Set all errors
20 | error.details.map(err => {
21 | errors[err.path] = err.message;
22 | });
23 | } else {
24 | errors = false;
25 | }
26 |
27 | return { errors };
28 | };
29 |
--------------------------------------------------------------------------------
/server/validations/client/auth/register.js:
--------------------------------------------------------------------------------
1 | const Joi = require("joi");
2 |
3 | module.exports = data => {
4 | let errors = {};
5 | const schema = {
6 | name: Joi.string()
7 | .min(2)
8 | .max(100)
9 | .required(),
10 | email: Joi.string()
11 | .min(5)
12 | .max(255)
13 | .required()
14 | .email(),
15 | password: Joi.string()
16 | .min(6)
17 | .max(50)
18 | .required(),
19 | password_confirmation: Joi.any()
20 | .valid(Joi.ref('password'))
21 | .required()
22 | .options({ language: { any: { allowOnly: 'must match password' } } })
23 | };
24 |
25 | const { error } = Joi.validate(data, schema, { abortEarly: false });
26 | if (error) {
27 | // Set all errors
28 | error.details.map(err => {
29 | errors[err.path] = err.message;
30 | });
31 | } else {
32 | errors = false;
33 | }
34 |
35 | return { errors };
36 | };
37 |
--------------------------------------------------------------------------------