16 |
17 |
18 |
64 |
65 |
73 |
--------------------------------------------------------------------------------
/backend/assets/js/chunk-596f.e000c2e0.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-596f"],{"0c78":function(t,s,e){"use strict";var a=e("c93e"),i=e("2f62"),r=e("92d7"),c=e.n(r);s["a"]={created:function(){this.getProducts(this.currentPage)},data:function(){return{currentPage:1}},watch:{currentPage:function(){this.getProducts(this.currentPage)}},computed:{products:{get:function(){return this.$store.state.Products.products.products}},amountOfProducts:{get:function(){return this.$store.state.Products.products.amountOfProducts}}},methods:Object(a["a"])({encode:function(t){return c.a.encode(t)}},Object(i["b"])(["getProducts"]))}},b61f:function(t,s,e){"use strict";e.r(s);var a=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",[e("div",{staticClass:"d-flex justify-content-center"},[e("b-carousel",{staticClass:"mb-4",attrs:{background:"transparent",interval:3e3,"img-width":"1100","img-height":"200"}},[e("b-carousel-slide",{attrs:{"img-blank":""}},[e("h3",{staticClass:"text-info"},[t._v(t._s(t.t("home.mixin.h3.first")))]),e("p",{staticClass:"text-muted"},[t._v(t._s(t.t("home.mixin.p.first")))])]),e("b-carousel-slide",{attrs:{"img-blank":""}},[e("h3",{staticClass:"text-info"},[t._v(t._s(t.t("home.mixin.h3.second")))]),e("p",{staticClass:"text-muted"},[t._v(t._s(t.t("home.mixin.p.second")))])]),e("b-carousel-slide",{attrs:{"img-blank":""}},[e("h3",{staticClass:"text-info"},[t._v(t._s(t.t("home.mixin.h3.third")))]),e("p",{staticClass:"text-muted"},[t._v(t._s(t.t("home.mixin.p.third")))])])],1)],1),e("div",{staticClass:"row"},t._l(t.products,function(s){return e("div",{key:s.id,staticClass:"col-4"},[e("b-card",{key:s.id,staticClass:"mb-4",attrs:{header:t.encode(s.title),"footer-tag":"footer"}},[e("p",{staticClass:"card-text"},[t._v(t._s(s.description))]),e("small",{staticClass:"text-muted",attrs:{slot:"footer"},slot:"footer"},[e("span",{staticClass:"float-left"},[t._v("$"+t._s(s.price))]),e("span",{staticClass:"float-right"},[t._v(t._s(t.t("home.mixin.span.first"))+" "+t._s(s.user.name))])])])],1)})),e("b-pagination",{attrs:{size:"sm","total-rows":t.amountOfProducts,"per-page":6},model:{value:t.currentPage,callback:function(s){t.currentPage=s},expression:"currentPage"}})],1)},i=[],r=(e("cadf"),e("551c"),e("097d"),e("0c78")),c={mixins:[r["a"]]},n=c,o=e("2877"),u=Object(o["a"])(n,a,i,!1,null,null,null);u.options.__file="Home.desktop.vue";s["default"]=u.exports}}]);
--------------------------------------------------------------------------------
/backend/assets/js/chunk-58f5.62a37769.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-58f5"],{"0469":function(t,e,n){},"0c78":function(t,e,n){"use strict";var i=n("c93e"),s=n("2f62"),c=n("92d7"),o=n.n(c);e["a"]={created:function(){this.getProducts(this.currentPage)},data:function(){return{currentPage:1}},watch:{currentPage:function(){this.getProducts(this.currentPage)}},computed:{products:{get:function(){return this.$store.state.Products.products.products}},amountOfProducts:{get:function(){return this.$store.state.Products.products.amountOfProducts}}},methods:Object(i["a"])({encode:function(t){return o.a.encode(t)}},Object(s["b"])(["getProducts"]))}},"2dba":function(t,e,n){"use strict";n.r(e);var i=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("mt-swipe",{attrs:{"show-indicators":!1}},[n("mt-swipe-item",{staticClass:"slide1"},[t._v("\n "+t._s(t.t("home.mixin.p.first"))+"\n ")]),n("mt-swipe-item",{staticClass:"slide2"},[t._v("\n "+t._s(t.t("home.mixin.p.second"))+"\n ")]),n("mt-swipe-item",{staticClass:"slide3"},[t._v("\n "+t._s(t.t("home.mixin.p.third"))+"\n ")])],1),t._l(t.products,function(e){return n("mt-cell",{key:e.id,attrs:{title:e.title}},[n("mt-button",{attrs:{size:"small"},on:{click:function(n){t.makeProductVisible(e)}}},[t._v(t._s(t.t("home.mixin.button.first")))])],1)}),n("mt-cell",{attrs:{title:""}},[n("mt-button",{attrs:{disabled:t.isDisabledNextButton,size:"small",type:"primary"},on:{click:function(e){t.currentPage++}}},[t._v("\n "+t._s(t.t("home.mixin.button.second"))+"\n ")])],1)],2)},s=[],c=(n("cadf"),n("551c"),n("097d"),n("0c78")),o=n("76a0"),r={mixins:[c["a"]],computed:{isDisabledNextButton:function(){return this.currentPage===Math.ceil(this.amountOfProducts/6)}},methods:{makeProductVisible:function(t){Object(o["MessageBox"])({title:t.title,message:this.encode(t.description),confirmButtonText:"Okay"})}}},u=r,a=(n("d239"),n("2877")),d=Object(a["a"])(u,i,s,!1,null,null,null);d.options.__file="Home.mobile.vue";e["default"]=d.exports},"62e4":function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},d239:function(t,e,n){"use strict";var i=n("0469"),s=n.n(i);s.a}}]);
--------------------------------------------------------------------------------
/backend/api/responses/badRequest.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 400 (Bad Request) Handler
3 | *
4 | * Usage:
5 | * return res.badRequest();
6 | * return res.badRequest(data);
7 | * return res.badRequest(data, 'some/specific/badRequest/view');
8 | *
9 | * e.g.:
10 | * ```
11 | * return res.badRequest(
12 | * 'Please choose a valid `password` (6-12 characters)',
13 | * 'trial/signup'
14 | * );
15 | * ```
16 | */
17 |
18 | module.exports = function badRequest(data, options) {
19 |
20 | // Get access to `req`, `res`, & `sails`
21 | var req = this.req;
22 | var res = this.res;
23 | var sails = req._sails;
24 |
25 | // Set status code
26 | res.status(400);
27 |
28 | // Log error to console
29 | if (data !== undefined) {
30 | sails.log.verbose('Sending 400 ("Bad Request") response: \n',data);
31 | }
32 | else sails.log.verbose('Sending 400 ("Bad Request") response');
33 |
34 | // Only include errors in response if application environment
35 | // is not set to 'production'. In production, we shouldn't
36 | // send back any identifying information about errors.
37 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) {
38 | data = undefined;
39 | }
40 |
41 | // If the user-agent wants JSON, always respond with JSON
42 | // If views are disabled, revert to json
43 | if (req.wantsJSON || sails.config.hooks.views === false) {
44 | return res.json(data);
45 | }
46 |
47 | // If second argument is a string, we take that to mean it refers to a view.
48 | // If it was omitted, use an empty object (`{}`)
49 | options = (typeof options === 'string') ? { view: options } : options || {};
50 |
51 | // Attempt to prettify data for views, if it's a non-error object
52 | var viewData = data;
53 | if (!(viewData instanceof Error) && 'object' == typeof viewData) {
54 | try {
55 | viewData = require('util').inspect(data, {depth: null});
56 | }
57 | catch(e) {
58 | viewData = undefined;
59 | }
60 | }
61 |
62 | // If a view was provided in options, serve it.
63 | // Otherwise try to guess an appropriate view, or if that doesn't
64 | // work, just send JSON.
65 | if (options.view) {
66 | return res.view(options.view, { data: viewData, title: 'Bad Request' });
67 | }
68 |
69 | // If no second argument provided, try to serve the implied view,
70 | // but fall back to sending JSON(P) if no view can be inferred.
71 | else return res.guessView({ data: viewData, title: 'Bad Request' }, function couldNotGuessView () {
72 | return res.json(data);
73 | });
74 |
75 | };
76 |
--------------------------------------------------------------------------------
/backend/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * app.js
3 | *
4 | * Use `app.js` to run your app without `sails lift`.
5 | * To start the server, run: `node app.js`.
6 | *
7 | * This is handy in situations where the sails CLI is not relevant or useful.
8 | *
9 | * For example:
10 | * => `node app.js`
11 | * => `forever start app.js`
12 | * => `node debug app.js`
13 | * => `modulus deploy`
14 | * => `heroku scale`
15 | *
16 | *
17 | * The same command-line arguments are supported, e.g.:
18 | * `node app.js --silent --port=80 --prod`
19 | */
20 |
21 | var SailsApp = require('sails').Sails
22 |
23 | // Ensure we're in the project directory, so cwd-relative paths work as expected
24 | // no matter where we actually lift from.
25 | // > Note: This is not required in order to lift, but it is a convenient default.
26 | process.chdir(__dirname)
27 |
28 | // Attempt to import `sails`.
29 | var sails = new SailsApp()
30 | try {
31 | sails = require('sails')
32 | } catch (e) {
33 | console.error(
34 | 'To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.')
35 | console.error('To do that, run `npm install sails`')
36 | console.error('')
37 | console.error(
38 | 'Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.')
39 | console.error(
40 | 'When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,')
41 | console.error(
42 | 'but if it doesn\'t, the app will run with the global sails instead!')
43 | return
44 | }
45 |
46 | // --•
47 | // Try to get `rc` dependency (for loading `.sailsrc` files).
48 | var rc
49 | try {
50 | rc = require('rc')
51 | } catch (e0) {
52 | try {
53 | rc = require('sails/node_modules/rc')
54 | } catch (e1) {
55 | console.error('Could not find dependency: `rc`.')
56 | console.error('Your `.sailsrc` file(s) will be ignored.')
57 | console.error('To resolve this, run:')
58 | console.error('npm install rc --save')
59 | rc = function () { return {} }
60 | }
61 | }
62 |
63 | // Start server
64 | sails.lift(rc('sails'))
65 |
66 | setInterval(function () {
67 | sails.lower(function (err) {
68 | const fs = require('fs')
69 | fs.unlinkSync(__dirname + '/.tmp/localDiskDb.db')
70 |
71 | if (err) return console.error('Error occurred lowering Sails app: ', err)
72 | console.info('Sails app lowered successfully!')
73 | sails = new SailsApp()
74 | sails.lift(rc('sails'))
75 | }
76 | )
77 | }, 86400000)
78 |
--------------------------------------------------------------------------------
/backend/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gruntfile
3 | *
4 | * This Node script is executed when you run `grunt` or `sails lift`.
5 | * It's purpose is to load the Grunt tasks in your project's `tasks`
6 | * folder, and allow you to add and remove tasks as you see fit.
7 | * For more information on how this works, check out the `README.md`
8 | * file that was generated in your `tasks` folder.
9 | *
10 | * WARNING:
11 | * Unless you know what you're doing, you shouldn't change this file.
12 | * Check out the `tasks` directory instead.
13 | */
14 |
15 | module.exports = function(grunt) {
16 |
17 |
18 | // Load the include-all library in order to require all of our grunt
19 | // configurations and task registrations dynamically.
20 | var includeAll;
21 | try {
22 | includeAll = require('include-all');
23 | } catch (e0) {
24 | try {
25 | includeAll = require('sails/node_modules/include-all');
26 | } catch (e1) {
27 | console.error('Could not find `include-all` module.');
28 | console.error('Skipping grunt tasks...');
29 | console.error('To fix this, please run:');
30 | console.error('npm install include-all --save`');
31 | console.error();
32 |
33 | grunt.registerTask('default', []);
34 | return;
35 | }
36 | }
37 |
38 |
39 | /**
40 | * Loads Grunt configuration modules from the specified
41 | * relative path. These modules should export a function
42 | * that, when run, should either load/configure or register
43 | * a Grunt task.
44 | */
45 | function loadTasks(relPath) {
46 | return includeAll({
47 | dirname: require('path').resolve(__dirname, relPath),
48 | filter: /(.+)\.js$/,
49 | excludeDirs: /^\.(git|svn)$/
50 | }) || {};
51 | }
52 |
53 | /**
54 | * Invokes the function from a Grunt configuration module with
55 | * a single argument - the `grunt` object.
56 | */
57 | function invokeConfigFn(tasks) {
58 | for (var taskName in tasks) {
59 | if (tasks.hasOwnProperty(taskName)) {
60 | tasks[taskName](grunt);
61 | }
62 | }
63 | }
64 |
65 |
66 |
67 | // Load task functions
68 | var taskConfigurations = loadTasks('./tasks/config'),
69 | registerDefinitions = loadTasks('./tasks/register');
70 |
71 | // (ensure that a default task exists)
72 | if (!registerDefinitions.default) {
73 | registerDefinitions.default = function(grunt) {
74 | grunt.registerTask('default', []);
75 | };
76 | }
77 |
78 | // Run task functions to configure Grunt.
79 | invokeConfigFn(taskConfigurations);
80 | invokeConfigFn(registerDefinitions);
81 |
82 | };
83 |
--------------------------------------------------------------------------------
/frontend/cypress/integration/ProductsGet.js:
--------------------------------------------------------------------------------
1 | import mocks from './../fixtures/mocks'
2 |
3 | describe('ProductsGet', () => {
4 | describe('mobile', () => {
5 | beforeEach(() => {
6 | cy.server()
7 | cy.viewport(414, 736)
8 | cy.setCookie('user', '1')
9 |
10 | cy.route({
11 | method: 'GET',
12 | url: '/api/user/products/getProductsByUser',
13 | status: 200,
14 | response: mocks('productsByUser', 12)
15 | })
16 | .as('/api/user/products/getProductsByUser')
17 |
18 | cy.visit('/user/products/index', {
19 | onBeforeLoad: window => {
20 | window.localStorage.setItem('token', 1)
21 | }
22 | })
23 | })
24 |
25 | it('loads the app', () => {
26 | cy.get('.mint-header').should('be.visible')
27 | })
28 |
29 | it('shows products', () => {
30 | cy.get(':nth-child(1) > :nth-child(1) > :nth-child(1) > .mint-cell-wrapper > .mint-cell-title > .mint-cell-text').should('not.be.empty')
31 | })
32 | })
33 |
34 | describe('desktop', () => {
35 | beforeEach(() => {
36 | cy.server()
37 | cy.viewport(1280, 768)
38 | cy.setCookie('user', '1')
39 |
40 | cy.route({
41 | method: 'GET',
42 | url: '/api/user/products/getProductsByUser',
43 | status: 200,
44 | response: mocks('productsByUser', 12)
45 | })
46 | .as('/api/user/products/getProductsByUser')
47 |
48 | cy.route({
49 | method: 'DELETE',
50 | url: '/api/user/products/product/delete?id=0',
51 | status: 200,
52 | response: {}
53 | })
54 | .as('/api/user/products/product/delete?id=0')
55 |
56 | cy.visit('/user/products/index', {
57 | onBeforeLoad: window => {
58 | window.localStorage.setItem('token', 1)
59 | }
60 | })
61 | })
62 |
63 | it('loads the app', () => {
64 | cy.get('.container').should('be.visible')
65 | })
66 |
67 | it('shows products', () => {
68 | cy.get('tbody > :nth-child(1) > [aria-colindex="1"]').should('not.be.empty')
69 | })
70 |
71 | it('removes products', () => {
72 | cy.route({
73 | method: 'GET',
74 | url: '/api/user/products/getProductsByUser',
75 | status: 200,
76 | response: mocks('productsByUser', 11)
77 | })
78 | .as('/api/user/products/product/delete?id=0')
79 | cy.get(':nth-child(1) > [aria-colindex="4"] > .btn-group > .btn-outline-warning').click()
80 | cy.wait('@/api/user/products/product/delete?id=0')
81 | cy.wait('@/api/user/products/getProductsByUser')
82 | cy.get('table tbody').find('tr').should($tbody => expect($tbody).to.have.length(11))
83 | })
84 | })
85 | })
86 |
--------------------------------------------------------------------------------
/backend/assets/js/chunk-5e44.8377145e.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-5e44"],{"91a7":function(s,e,t){"use strict";var a=t("e478"),i=t.n(a);i.a},e478:function(s,e,t){},e636:function(s,e,t){"use strict";t.r(e);var a=function(){var s=this,e=s.$createElement,t=s._self._c||e;return t("b-modal",{attrs:{"no-fade":"","no-close-on-backdrop":!0,"no-close-on-esc":!0,"hide-header-close":!0,"ok-only":!0,id:"help",title:s.t("help.desktop.modal.title"),size:"lg"}},[t("div",{staticClass:"card mb-2"},[t("div",{staticClass:"card-body"},s._l(s.messages,function(e,a){return t("div",{key:a},[e.message.assistant?t("p",{staticClass:"mb-0"},[t("b",{staticClass:"mr-2"},[s._v(s._s(e.message.assistant.name))]),s._v("\n "+s._s(e.message.assistant.message))]):s._e(),e.message.assistant?t("p",{staticClass:"text-muted"},[t("small",[s._v(s._s(e.time))])]):s._e(),e.message.user?t("p",{staticClass:"text-right mb-0"},[s._v(s._s(e.message.user.message)+" "),t("b",{staticClass:"ml-2"},[s._v("You")])]):s._e(),e.message.user?t("p",{staticClass:"text-right text-muted"},[t("small",[s._v(s._s(e.time))])]):s._e()])}))]),t("b-form-input",{attrs:{type:"text"},nativeOn:{keyup:function(e){return"button"in e||!s._k(e.keyCode,"enter",13,e.key,"Enter")?s.postMessage(e):null}},model:{value:s.message,callback:function(e){s.message=e},expression:"message"}}),t("template",{slot:"modal-footer"},[t("b-button",{attrs:{size:"sm",variant:"secondary"},on:{click:function(e){s.setIsVisibleHelp(!1)}}},[s._v("\n "+s._s(s.t("help.desktop.button.first"))+"\n ")])],1)],2)},i=[],n=t("c93e"),o=(t("6b54"),t("2f62")),l={props:["io"],data:function(){return{assistant:"",message:"",messages:[{message:{assistant:{name:"System",message:"Hey, how can we help you?"}},time:(new Date).toString()}]}},mounted:function(){this.$root.$emit("bv::show::modal","help"),this.$emit("helpMounted")},created:function(){this.setRandomAssistantName()},computed:{isHelpVisible:{get:function(){return this.$store.state.isHelpVisible},set:function(s){this.store.commit("SET_IS_HELP_VISIBLE",s)}}},updated:function(){var s=this.$el.querySelector(".card");s.scrollTop=s.scrollHeight},methods:Object(n["a"])({postMessage:function(){var s=this;this.messages.push({message:{user:{message:this.message}},time:(new Date).toString()}),this.io.socket.post("/api/help",{message:this.message},function(e){s.messages.push({message:{assistant:{name:s.assistant,message:e.answer}},time:(new Date).toString()})}),this.$set(this,"message","")},setRandomAssistantName:function(){var s=["Irvin Case","Juliette Cooper","Sheldon James"],e=s[Math.floor(Math.random()*s.length)];this.$set(this,"assistant",e)}},Object(o["c"])({setIsVisibleHelp:"SET_IS_VISIBLE_HELP"}))},m=l,r=(t("91a7"),t("2877")),c=Object(r["a"])(m,a,i,!1,null,"13b6df7c",null);c.options.__file="Help.desktop.vue";e["default"]=c.exports}}]);
--------------------------------------------------------------------------------
/backend/config/globals.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Global Variable Configuration
3 | * (sails.config.globals)
4 | *
5 | * Configure which global variables which will be exposed
6 | * automatically by Sails.
7 | *
8 | * For more information on configuration, check out:
9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.globals.html
10 | */
11 | module.exports.globals = {
12 |
13 | /****************************************************************************
14 | * *
15 | * Expose the lodash installed in Sails core as a global variable. If this *
16 | * is disabled, like any other node module you can always run npm install *
17 | * lodash --save, then var _ = require('lodash') at the top of any file. *
18 | * *
19 | ****************************************************************************/
20 |
21 | _: false,
22 |
23 | /****************************************************************************
24 | * *
25 | * Expose the async installed in Sails core as a global variable. If this is *
26 | * disabled, like any other node module you can always run npm install async *
27 | * --save, then var async = require('async') at the top of any file. *
28 | * *
29 | ****************************************************************************/
30 |
31 | async: false,
32 |
33 | /****************************************************************************
34 | * *
35 | * Expose the sails instance representing your app. If this is disabled, you *
36 | * can still get access via req._sails. *
37 | * *
38 | ****************************************************************************/
39 |
40 | sails: true,
41 |
42 | /****************************************************************************
43 | * *
44 | * Expose each of your app's models as global variables (using their *
45 | * "globalId"). E.g. a model defined in api/models/User.js would have a *
46 | * globalId of User by default. If this is disabled, you can still access *
47 | * your models via sails.models.*. *
48 | * *
49 | ****************************************************************************/
50 |
51 | models: true
52 | };
53 |
--------------------------------------------------------------------------------
/frontend/cypress/integration/ShopIndex.spec.js:
--------------------------------------------------------------------------------
1 | import mocks from './../fixtures/mocks'
2 |
3 | describe('ShopIndex', () => {
4 | describe('mobile', () => {
5 | beforeEach(() => {
6 | cy.server()
7 | cy.viewport(414, 736)
8 | cy.setCookie('user', '1')
9 | cy.route({
10 | method: 'GET',
11 | url: '/api/user/get',
12 | status: 200,
13 | response: {
14 | 'name': 'Joe',
15 | 'createdAt': '2018-01-24T09:50:30.083Z',
16 | 'updatedAt': '2018-01-24T09:50:30.083Z',
17 | 'id': 1
18 | }
19 | })
20 | .as('/api/user/get')
21 |
22 | cy.route({
23 | method: 'GET',
24 | url: '/api/user/products/get?page=1',
25 | status: 200,
26 | response: mocks('products', 12, {amountOfProducts: 12})
27 | })
28 | .as('/api/user/products/get?page=1')
29 |
30 | cy.visit('/user/shop/index', {
31 | onBeforeLoad: window => {
32 | window.localStorage.setItem('token', 1)
33 | }
34 | })
35 | })
36 |
37 | it('loads the app', () => {
38 | cy.get('.mint-header').should('be.visible')
39 | })
40 |
41 | it('adds a product to basket', () => {
42 | cy.wait('@/api/user/get')
43 | cy.wait('@/api/user/products/get?page=1')
44 | cy.get(':nth-child(1) > .mint-cell-wrapper > .mint-cell-value > .mint-button').click()
45 | cy.get('.mint-msgbox-btn.mint-msgbox-confirm').click()
46 | })
47 | })
48 |
49 | describe('desktop', () => {
50 | beforeEach(() => {
51 | cy.server()
52 | cy.viewport(1280, 768)
53 | cy.setCookie('user', '1')
54 | cy.route({
55 | method: 'GET',
56 | url: '/api/user/get',
57 | status: 200,
58 | response: {
59 | 'name': 'Joe',
60 | 'createdAt': '2018-01-24T09:50:30.083Z',
61 | 'updatedAt': '2018-01-24T09:50:30.083Z',
62 | 'id': 1
63 | }
64 | })
65 | .as('/api/user/get')
66 |
67 | cy.route({
68 | method: 'GET',
69 | url: '/api/user/products/get?page=1',
70 | status: 200,
71 | response: mocks('products', 12, {amountOfProducts: 12})
72 | })
73 | .as('/api/user/products/get?page=1')
74 |
75 | cy.visit('/user/shop/index', {
76 | onBeforeLoad: window => {
77 | window.localStorage.setItem('token', 1)
78 | }
79 | })
80 | })
81 |
82 | it('loads the app', () => {
83 | cy.get('.container').should('be.visible')
84 | })
85 |
86 | it('adds a product to basket', () => {
87 | cy.wait('@/api/user/get')
88 | cy.wait('@/api/user/products/get?page=1')
89 | cy.get(':nth-child(1) > .card > .card-body > .btn').click()
90 | cy.get('.ml-auto > :nth-child(2) > .nav-link').contains('Basket (1)')
91 | })
92 | })
93 | })
94 |
--------------------------------------------------------------------------------
/backend/config/i18n.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Internationalization / Localization Settings
3 | * (sails.config.i18n)
4 | *
5 | * If your app will touch people from all over the world, i18n (or internationalization)
6 | * may be an important part of your international strategy.
7 | *
8 | *
9 | * For more informationom i18n in Sails, check out:
10 | * http://sailsjs.org/#!/documentation/concepts/Internationalization
11 | *
12 | * For a complete list of i18n options, see:
13 | * https://github.com/mashpie/i18n-node#list-of-configuration-options
14 | *
15 | *
16 | */
17 |
18 | module.exports.i18n = {
19 |
20 | /***************************************************************************
21 | * *
22 | * Which locales are supported? *
23 | * *
24 | ***************************************************************************/
25 |
26 | // locales: ['en', 'es', 'fr', 'de'],
27 |
28 | /****************************************************************************
29 | * *
30 | * What is the default locale for the site? Note that this setting will be *
31 | * overridden for any request that sends an "Accept-Language" header (i.e. *
32 | * most browsers), but it's still useful if you need to localize the *
33 | * response for requests made by non-browser clients (e.g. cURL). *
34 | * *
35 | ****************************************************************************/
36 |
37 | // defaultLocale: 'en',
38 |
39 | /****************************************************************************
40 | * *
41 | * Automatically add new keys to locale (translation) files when they are *
42 | * encountered during a request? *
43 | * *
44 | ****************************************************************************/
45 |
46 | // updateFiles: false,
47 |
48 | /****************************************************************************
49 | * *
50 | * Path (relative to app root) of directory to store locale (translation) *
51 | * files in. *
52 | * *
53 | ****************************************************************************/
54 |
55 | // localesDirectory: '/config/locales'
56 |
57 | };
58 |
--------------------------------------------------------------------------------
/backend/config/policies.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Policy Mappings
3 | * (sails.config.policies)
4 | *
5 | * Policies are simple functions which run **before** your controllers.
6 | * You can apply one or more policies to a given controller, or protect
7 | * its actions individually.
8 | *
9 | * Any policy file (e.g. `api/policies/authenticated.js`) can be accessed
10 | * below by its filename, minus the extension, (e.g. "authenticated")
11 | *
12 | * For more information on how policies work, see:
13 | * http://sailsjs.org/#!/documentation/concepts/Policies
14 | *
15 | * For more information on configuring policies, check out:
16 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.policies.html
17 | */
18 |
19 |
20 | module.exports.policies = {
21 |
22 | /***************************************************************************
23 | * *
24 | * Default policy for all controllers and actions (`true` allows public *
25 | * access) *
26 | * *
27 | ***************************************************************************/
28 |
29 | // '*': true,
30 |
31 | /***************************************************************************
32 | * *
33 | * Here's an example of mapping some policies to run before a controller *
34 | * and its actions *
35 | * *
36 | ***************************************************************************/
37 | // RabbitController: {
38 |
39 | // Apply the `false` policy as the default for all of RabbitController's actions
40 | // (`false` prevents all access, which ensures that nothing bad happens to our rabbits)
41 | // '*': false,
42 |
43 | // For the action `nurture`, apply the 'isRabbitMother' policy
44 | // (this overrides `false` above)
45 | // nurture : 'isRabbitMother',
46 |
47 | // Apply the `isNiceToAnimals` AND `hasRabbitFood` policies
48 | // before letting any users feed our rabbits
49 | // feed : ['isNiceToAnimals', 'hasRabbitFood']
50 | // }
51 |
52 | 'RegisterController': {
53 | '*': true
54 | },
55 |
56 | 'LoginController': {
57 | '*': true
58 | },
59 |
60 | 'ProductController': {
61 | '*': 'isAuthorized'
62 | },
63 |
64 | 'user/ProductsController': {
65 | '*': 'isAuthorized'
66 | },
67 |
68 | 'user/ProductController': {
69 | '*': 'isAuthorized'
70 | },
71 |
72 | 'ProductsController': {
73 | '*': true
74 | },
75 |
76 | 'user/shop/BasketController': {
77 | '*': 'isAuthorized'
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/backend/api/responses/forbidden.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 403 (Forbidden) Handler
3 | *
4 | * Usage:
5 | * return res.forbidden();
6 | * return res.forbidden(err);
7 | * return res.forbidden(err, 'some/specific/forbidden/view');
8 | *
9 | * e.g.:
10 | * ```
11 | * return res.forbidden('Access denied.');
12 | * ```
13 | */
14 |
15 | module.exports = function forbidden (data, options) {
16 |
17 | // Get access to `req`, `res`, & `sails`
18 | var req = this.req;
19 | var res = this.res;
20 | var sails = req._sails;
21 |
22 | // Set status code
23 | res.status(403);
24 |
25 | // Log error to console
26 | if (data !== undefined) {
27 | sails.log.verbose('Sending 403 ("Forbidden") response: \n',data);
28 | }
29 | else sails.log.verbose('Sending 403 ("Forbidden") response');
30 |
31 | // Only include errors in response if application environment
32 | // is not set to 'production'. In production, we shouldn't
33 | // send back any identifying information about errors.
34 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) {
35 | data = undefined;
36 | }
37 |
38 | // If the user-agent wants JSON, always respond with JSON
39 | // If views are disabled, revert to json
40 | if (req.wantsJSON || sails.config.hooks.views === false) {
41 | return res.json(data);
42 | }
43 |
44 | // If second argument is a string, we take that to mean it refers to a view.
45 | // If it was omitted, use an empty object (`{}`)
46 | options = (typeof options === 'string') ? { view: options } : options || {};
47 |
48 | // Attempt to prettify data for views, if it's a non-error object
49 | var viewData = data;
50 | if (!(viewData instanceof Error) && 'object' == typeof viewData) {
51 | try {
52 | viewData = require('util').inspect(data, {depth: null});
53 | }
54 | catch(e) {
55 | viewData = undefined;
56 | }
57 | }
58 |
59 | // If a view was provided in options, serve it.
60 | // Otherwise try to guess an appropriate view, or if that doesn't
61 | // work, just send JSON.
62 | if (options.view) {
63 | return res.view(options.view, { data: viewData, title: 'Forbidden' });
64 | }
65 |
66 | // If no second argument provided, try to serve the default view,
67 | // but fall back to sending JSON(P) if any errors occur.
68 | else return res.view('403', { data: viewData, title: 'Forbidden' }, function (err, html) {
69 |
70 | // If a view error occured, fall back to JSON(P).
71 | if (err) {
72 | //
73 | // Additionally:
74 | // • If the view was missing, ignore the error but provide a verbose log.
75 | if (err.code === 'E_VIEW_FAILED') {
76 | sails.log.verbose('res.forbidden() :: Could not locate view for error page (sending JSON instead). Details: ',err);
77 | }
78 | // Otherwise, if this was a more serious error, log to the console with the details.
79 | else {
80 | sails.log.warn('res.forbidden() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err);
81 | }
82 | return res.json(data);
83 | }
84 |
85 | return res.send(html);
86 | });
87 |
88 | };
89 |
--------------------------------------------------------------------------------
/backend/config/datastores.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Datastores
3 | * (sails.config.datastores)
4 | *
5 | * A set of datastore configurations which tell Sails where to fetch or save
6 | * data when you execute built-in model methods like `.find()` and `.create()`.
7 | *
8 | * > This file is mainly useful for configuring your development database,
9 | * > as well as any additional one-off databases used by individual models.
10 | * > Ready to go live? Head towards `config/env/production.js`.
11 | *
12 | * For more information on configuring datastores, check out:
13 | * https://sailsjs.com/config/datastores
14 | */
15 |
16 | module.exports.datastores = {
17 |
18 |
19 | /***************************************************************************
20 | * *
21 | * Your app's default datastore. *
22 | * *
23 | * Sails apps read and write to local disk by default, using a built-in *
24 | * database adapter called `sails-disk`. This feature is purely for *
25 | * convenience during development; since `sails-disk` is not designed for *
26 | * use in a production environment. *
27 | * *
28 | * To use a different db _in development_, follow the directions below. *
29 | * Otherwise, just leave the default datastore as-is, with no `adapter`. *
30 | * *
31 | * (For production configuration, see `config/env/production.js`.) *
32 | * *
33 | ***************************************************************************/
34 |
35 | default: {
36 |
37 | /***************************************************************************
38 | * *
39 | * Want to use a different database during development? *
40 | * *
41 | * 1. Choose an adapter: *
42 | * https://sailsjs.com/plugins/databases *
43 | * *
44 | * 2. Install it as a dependency of your Sails app. *
45 | * (For example: npm install sails-mysql --save) *
46 | * *
47 | * 3. Then pass it in, along with a connection URL. *
48 | * (See https://sailsjs.com/config/datastores for help.) *
49 | * *
50 | ***************************************************************************/
51 | // adapter: 'sails-mysql',
52 | // url: 'mysql://user:password@host:port/database',
53 |
54 | },
55 |
56 |
57 | };
58 |
--------------------------------------------------------------------------------
/backend/config/csrf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cross-Site Request Forgery Protection Settings
3 | * (sails.config.csrf)
4 | *
5 | * CSRF tokens are like a tracking chip. While a session tells the server that a user
6 | * "is who they say they are", a csrf token tells the server "you are where you say you are".
7 | *
8 | * When enabled, all non-GET requests to the Sails server must be accompanied by
9 | * a special token, identified as the '_csrf' parameter.
10 | *
11 | * This option protects your Sails app against cross-site request forgery (or CSRF) attacks.
12 | * A would-be attacker needs not only a user's session cookie, but also this timestamped,
13 | * secret CSRF token, which is refreshed/granted when the user visits a URL on your app's domain.
14 | *
15 | * This allows us to have certainty that our users' requests haven't been hijacked,
16 | * and that the requests they're making are intentional and legitimate.
17 | *
18 | * This token has a short-lived expiration timeline, and must be acquired by either:
19 | *
20 | * (a) For traditional view-driven web apps:
21 | * Fetching it from one of your views, where it may be accessed as
22 | * a local variable, e.g.:
23 | *
26 | *
27 | * or (b) For AJAX/Socket-heavy and/or single-page apps:
28 | * Sending a GET request to the `/csrfToken` route, where it will be returned
29 | * as JSON, e.g.:
30 | * { _csrf: 'ajg4JD(JGdajhLJALHDa' }
31 | *
32 | *
33 | * Enabling this option requires managing the token in your front-end app.
34 | * For traditional web apps, it's as easy as passing the data from a view into a form action.
35 | * In AJAX/Socket-heavy apps, just send a GET request to the /csrfToken route to get a valid token.
36 | *
37 | * For more information on CSRF, check out:
38 | * http://en.wikipedia.org/wiki/Cross-site_request_forgery
39 | *
40 | * For more information on this configuration file, including info on CSRF + CORS, see:
41 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.csrf.html
42 | *
43 | */
44 |
45 | /****************************************************************************
46 | * *
47 | * Enabled CSRF protection for your site? *
48 | * *
49 | ****************************************************************************/
50 |
51 | // module.exports.csrf = false;
52 |
53 | /****************************************************************************
54 | * *
55 | * You may also specify more fine-grained settings for CSRF, including the *
56 | * domains which are allowed to request the CSRF token via AJAX. These *
57 | * settings override the general CORS settings in your config/cors.js file. *
58 | * *
59 | ****************************************************************************/
60 |
61 | // module.exports.csrf = {
62 | // grantTokenViaAjax: true,
63 | // origin: ''
64 | // }
65 |
--------------------------------------------------------------------------------
/backend/api/responses/serverError.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 500 (Server Error) Response
3 | *
4 | * Usage:
5 | * return res.serverError();
6 | * return res.serverError(err);
7 | * return res.serverError(err, 'some/specific/error/view');
8 | *
9 | * NOTE:
10 | * If something throws in a policy or controller, or an internal
11 | * error is encountered, Sails will call `res.serverError()`
12 | * automatically.
13 | */
14 |
15 | module.exports = function serverError (data, options) {
16 |
17 | // Get access to `req`, `res`, & `sails`
18 | var req = this.req;
19 | var res = this.res;
20 | var sails = req._sails;
21 |
22 | // Set status code
23 | res.status(500);
24 |
25 | // Log error to console
26 | if (data !== undefined) {
27 | sails.log.error('Sending 500 ("Server Error") response: \n',data);
28 | }
29 | else sails.log.error('Sending empty 500 ("Server Error") response');
30 |
31 | // Only include errors in response if application environment
32 | // is not set to 'production'. In production, we shouldn't
33 | // send back any identifying information about errors.
34 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) {
35 | data = undefined;
36 | }
37 |
38 | // If the user-agent wants JSON, always respond with JSON
39 | // If views are disabled, revert to json
40 | if (req.wantsJSON || sails.config.hooks.views === false) {
41 | return res.json(data);
42 | }
43 |
44 | // If second argument is a string, we take that to mean it refers to a view.
45 | // If it was omitted, use an empty object (`{}`)
46 | options = (typeof options === 'string') ? { view: options } : options || {};
47 |
48 | // Attempt to prettify data for views, if it's a non-error object
49 | var viewData = data;
50 | if (!(viewData instanceof Error) && 'object' == typeof viewData) {
51 | try {
52 | viewData = require('util').inspect(data, {depth: null});
53 | }
54 | catch(e) {
55 | viewData = undefined;
56 | }
57 | }
58 |
59 | // If a view was provided in options, serve it.
60 | // Otherwise try to guess an appropriate view, or if that doesn't
61 | // work, just send JSON.
62 | if (options.view) {
63 | return res.view(options.view, { data: viewData, title: 'Server Error' });
64 | }
65 |
66 | // If no second argument provided, try to serve the default view,
67 | // but fall back to sending JSON(P) if any errors occur.
68 | else return res.view('500', { data: viewData, title: 'Server Error' }, function (err, html) {
69 |
70 | // If a view error occured, fall back to JSON(P).
71 | if (err) {
72 | //
73 | // Additionally:
74 | // • If the view was missing, ignore the error but provide a verbose log.
75 | if (err.code === 'E_VIEW_FAILED') {
76 | sails.log.verbose('res.serverError() :: Could not locate view for error page (sending JSON instead). Details: ',err);
77 | }
78 | // Otherwise, if this was a more serious error, log to the console with the details.
79 | else {
80 | sails.log.warn('res.serverError() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err);
81 | }
82 | return res.json(data);
83 | }
84 |
85 | return res.send(html);
86 | });
87 |
88 | };
89 |
--------------------------------------------------------------------------------
/backend/tasks/pipeline.js:
--------------------------------------------------------------------------------
1 | /**
2 | * grunt/pipeline.js
3 | *
4 | * The order in which your css, javascript, and template files should be
5 | * compiled and linked from your views and static HTML files.
6 | *
7 | * (Note that you can take advantage of Grunt-style wildcard/glob/splat expressions
8 | * for matching multiple files, and ! in front of an expression to ignore files.)
9 | *
10 | * For more information see:
11 | * https://github.com/balderdashy/sails-docs/blob/master/anatomy/myApp/tasks/pipeline.js.md
12 | */
13 |
14 |
15 | // CSS files to inject in order
16 | //
17 | // (if you're using LESS with the built-in default config, you'll want
18 | // to change `assets/styles/importer.less` instead.)
19 | var cssFilesToInject = [
20 | 'styles/**/*.css'
21 | ];
22 |
23 |
24 | // Client-side javascript files to inject in order
25 | // (uses Grunt-style wildcard/glob/splat expressions)
26 | var jsFilesToInject = [
27 |
28 | // Load sails.io before everything else
29 | 'js/dependencies/sails.io.js',
30 |
31 | // Dependencies like jQuery, or Angular are brought in here
32 | 'js/dependencies/**/*.js',
33 |
34 | // All of the rest of your client-side js files
35 | // will be injected here in no particular order.
36 | 'js/**/*.js'
37 | ];
38 |
39 |
40 | // Client-side HTML templates are injected using the sources below
41 | // The ordering of these templates shouldn't matter.
42 | // (uses Grunt-style wildcard/glob/splat expressions)
43 | //
44 | // By default, Sails uses JST templates and precompiles them into
45 | // functions for you. If you want to use jade, handlebars, dust, etc.,
46 | // with the linker, no problem-- you'll just want to make sure the precompiled
47 | // templates get spit out to the same file. Be sure and check out `tasks/README.md`
48 | // for information on customizing and installing new tasks.
49 | var templateFilesToInject = [
50 | 'templates/**/*.html'
51 | ];
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | // Default path for public folder (see documentation for more information)
60 | var tmpPath = '.tmp/public/';
61 |
62 | // Prefix relative paths to source files so they point to the proper locations
63 | // (i.e. where the other Grunt tasks spit them out, or in some cases, where
64 | // they reside in the first place)
65 | module.exports.cssFilesToInject = cssFilesToInject.map(function(cssPath) {
66 | // If we're ignoring the file, make sure the ! is at the beginning of the path
67 | if (cssPath[0] === '!') {
68 | return require('path').join('!.tmp/public/', cssPath.substr(1));
69 | }
70 | return require('path').join('.tmp/public/', cssPath);
71 | });
72 | module.exports.jsFilesToInject = jsFilesToInject.map(function(jsPath) {
73 | // If we're ignoring the file, make sure the ! is at the beginning of the path
74 | if (jsPath[0] === '!') {
75 | return require('path').join('!.tmp/public/', jsPath.substr(1));
76 | }
77 | return require('path').join('.tmp/public/', jsPath);
78 | });
79 | module.exports.templateFilesToInject = templateFilesToInject.map(function(tplPath) {
80 | // If we're ignoring the file, make sure the ! is at the beginning of the path
81 | if (tplPath[0] === '!') {
82 | return require('path').join('!assets/', tplPath.substr(1));
83 | }
84 | return require('path').join('assets/',tplPath);
85 | });
86 |
87 |
88 |
--------------------------------------------------------------------------------
/backend/tasks/README.md:
--------------------------------------------------------------------------------
1 | # About the `tasks` folder
2 |
3 | The `tasks` directory is a suite of Grunt tasks and their configurations, bundled for your convenience. The Grunt integration is mainly useful for bundling front-end assets, (like stylesheets, scripts, & markup templates) but it can also be used to run all kinds of development tasks, from browserify compilation to database migrations.
4 |
5 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, read on!
6 |
7 |
8 | ### How does this work?
9 |
10 | The asset pipeline bundled in Sails is a set of Grunt tasks configured with conventional defaults designed to make your project more consistent and productive.
11 |
12 | The entire front-end asset workflow in Sails is completely customizable-- while it provides some suggestions out of the box, Sails makes no pretense that it can anticipate all of the needs you'll encounter building the browser-based/front-end portion of your application. Who's to say you're even building an app for a browser?
13 |
14 |
15 |
16 | ### What tasks does Sails run automatically?
17 |
18 | Sails runs some of these tasks (the ones in the `tasks/register` folder) automatically when you run certain commands.
19 |
20 | ###### `sails lift`
21 |
22 | Runs the `default` task (`tasks/register/default.js`).
23 |
24 | ###### `sails lift --prod`
25 |
26 | Runs the `prod` task (`tasks/register/prod.js`).
27 |
28 | ###### `sails www`
29 |
30 | Runs the `build` task (`tasks/register/build.js`).
31 |
32 | ###### `sails www --prod` (production)
33 |
34 | Runs the `buildProd` task (`tasks/register/buildProd.js`).
35 |
36 |
37 | ### Can I customize this for SASS, Angular, client-side Jade templates, etc?
38 |
39 | You can modify, omit, or replace any of these Grunt tasks to fit your requirements. You can also add your own Grunt tasks- just add a `someTask.js` file in the `grunt/config` directory to configure the new task, then register it with the appropriate parent task(s) (see files in `grunt/register/*.js`).
40 |
41 |
42 | ### Do I have to use Grunt?
43 |
44 | Nope! To disable Grunt integration in Sails, just delete your Gruntfile or disable the Grunt hook.
45 |
46 |
47 | ### What if I'm not building a web frontend?
48 |
49 | That's ok! A core tenant of Sails is client-agnosticism-- it's especially designed for building APIs used by all sorts of clients; native Android/iOS/Cordova, serverside SDKs, etc.
50 |
51 | You can completely disable Grunt by following the instructions above.
52 |
53 | If you still want to use Grunt for other purposes, but don't want any of the default web front-end stuff, just delete your project's `assets` folder and remove the front-end oriented tasks from the `grunt/register` and `grunt/config` folders. You can also run `sails new myCoolApi --no-frontend` to omit the `assets` folder and front-end-oriented Grunt tasks for future projects. You can also replace your `sails-generate-frontend` module with alternative community generators, or create your own. This allows `sails new` to create the boilerplate for native iOS apps, Android apps, Cordova apps, SteroidsJS apps, etc.
54 |
55 |
--------------------------------------------------------------------------------
/backend/api/responses/notFound.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 404 (Not Found) Handler
3 | *
4 | * Usage:
5 | * return res.notFound();
6 | * return res.notFound(err);
7 | * return res.notFound(err, 'some/specific/notfound/view');
8 | *
9 | * e.g.:
10 | * ```
11 | * return res.notFound();
12 | * ```
13 | *
14 | * NOTE:
15 | * If a request doesn't match any explicit routes (i.e. `config/routes.js`)
16 | * or route blueprints (i.e. "shadow routes", Sails will call `res.notFound()`
17 | * automatically.
18 | */
19 |
20 | module.exports = function notFound (data, options) {
21 |
22 | // Get access to `req`, `res`, & `sails`
23 | var req = this.req;
24 | var res = this.res;
25 | var sails = req._sails;
26 |
27 | // Set status code
28 | res.status(404);
29 |
30 | // Log error to console
31 | if (data !== undefined) {
32 | sails.log.verbose('Sending 404 ("Not Found") response: \n',data);
33 | }
34 | else sails.log.verbose('Sending 404 ("Not Found") response');
35 |
36 | // Only include errors in response if application environment
37 | // is not set to 'production'. In production, we shouldn't
38 | // send back any identifying information about errors.
39 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) {
40 | data = undefined;
41 | }
42 |
43 | // If the user-agent wants JSON, always respond with JSON
44 | // If views are disabled, revert to json
45 | if (req.wantsJSON || sails.config.hooks.views === false) {
46 | return res.json(data);
47 | }
48 |
49 | // If second argument is a string, we take that to mean it refers to a view.
50 | // If it was omitted, use an empty object (`{}`)
51 | options = (typeof options === 'string') ? { view: options } : options || {};
52 |
53 | // Attempt to prettify data for views, if it's a non-error object
54 | var viewData = data;
55 | if (!(viewData instanceof Error) && 'object' == typeof viewData) {
56 | try {
57 | viewData = require('util').inspect(data, {depth: null});
58 | }
59 | catch(e) {
60 | viewData = undefined;
61 | }
62 | }
63 |
64 | // If a view was provided in options, serve it.
65 | // Otherwise try to guess an appropriate view, or if that doesn't
66 | // work, just send JSON.
67 | if (options.view) {
68 | return res.view(options.view, { data: viewData, title: 'Not Found' });
69 | }
70 |
71 | // If no second argument provided, try to serve the default view,
72 | // but fall back to sending JSON(P) if any errors occur.
73 | else return res.view('404', { data: viewData, title: 'Not Found' }, function (err, html) {
74 |
75 | // If a view error occured, fall back to JSON(P).
76 | if (err) {
77 | //
78 | // Additionally:
79 | // • If the view was missing, ignore the error but provide a verbose log.
80 | if (err.code === 'E_VIEW_FAILED') {
81 | sails.log.verbose('res.notFound() :: Could not locate view for error page (sending JSON instead). Details: ',err);
82 | }
83 | // Otherwise, if this was a more serious error, log to the console with the details.
84 | else {
85 | sails.log.warn('res.notFound() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err);
86 | }
87 | return res.json(data);
88 | }
89 |
90 | return res.send(html);
91 | });
92 |
93 | };
94 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kontakt@julian-claus.de. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | ################################################
2 | ############### .gitignore ##################
3 | ################################################
4 | #
5 | # This file is only relevant if you are using git.
6 | #
7 | # Files which match the splat patterns below will
8 | # be ignored by git. This keeps random crap and
9 | # sensitive credentials from being uploaded to
10 | # your repository. It allows you to configure your
11 | # app for your machine without accidentally
12 | # committing settings which will smash the local
13 | # settings of other developers on your team.
14 | #
15 | # Some reasonable defaults are included below,
16 | # but, of course, you should modify/extend/prune
17 | # to fit your needs!
18 | ################################################
19 |
20 |
21 |
22 |
23 | ################################################
24 | # Local Configuration
25 | #
26 | # Explicitly ignore files which contain:
27 | #
28 | # 1. Sensitive information you'd rather not push to
29 | # your git repository.
30 | # e.g., your personal API keys or passwords.
31 | #
32 | # 2. Environment-specific configuration
33 | # Basically, anything that would be annoying
34 | # to have to change every time you do a
35 | # `git pull`
36 | # e.g., your local development database, or
37 | # the S3 bucket you're using for file uploads
38 | # development.
39 | #
40 | ################################################
41 |
42 | config/local.js
43 |
44 |
45 |
46 |
47 |
48 | ################################################
49 | # Dependencies
50 | #
51 | # When releasing a production app, you may
52 | # consider including your node_modules and
53 | # bower_components directory in your git repo,
54 | # but during development, its best to exclude it,
55 | # since different developers may be working on
56 | # different kernels, where dependencies would
57 | # need to be recompiled anyway.
58 | #
59 | # More on that here about node_modules dir:
60 | # http://www.futurealoof.com/posts/nodemodules-in-git.html
61 | # (credit Mikeal Rogers, @mikeal)
62 | #
63 | # About bower_components dir, you can see this:
64 | # http://addyosmani.com/blog/checking-in-front-end-dependencies/
65 | # (credit Addy Osmani, @addyosmani)
66 | #
67 | ################################################
68 |
69 | node_modules
70 | bower_components
71 |
72 |
73 |
74 |
75 | ################################################
76 | # Sails.js / Waterline / Grunt
77 | #
78 | # Files generated by Sails and Grunt, or related
79 | # tasks and adapters.
80 | ################################################
81 | .tmp
82 | dump.rdb
83 |
84 |
85 |
86 |
87 |
88 | ################################################
89 | # Node.js / NPM
90 | #
91 | # Common files generated by Node, NPM, and the
92 | # related ecosystem.
93 | ################################################
94 | lib-cov
95 | *.seed
96 | *.log
97 | *.out
98 | *.pid
99 | npm-debug.log
100 |
101 |
102 |
103 |
104 |
105 | ################################################
106 | # Miscellaneous
107 | #
108 | # Common files generated by text editors,
109 | # operating systems, file systems, etc.
110 | ################################################
111 |
112 | *~
113 | *#
114 | .DS_STORE
115 | .netbeans
116 | nbproject
117 | .idea
118 | .node_history
119 | package-lock.json
120 |
--------------------------------------------------------------------------------
/frontend/src/components/user/products/product/ProductPatch.desktop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ t('productpatch.mixin.first.title') }}
4 |
5 |
6 |
9 |
10 |
11 | {{ name }}
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 | {{ name }}
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 | {{ name }}
34 |
35 |
36 |
37 |
38 |
39 | {{ t('productpatch.mixin.button.first') }}
40 |
41 |
42 | {{ t('productpatch.mixin.button.second') }}
43 |
44 |
45 |
46 |
47 |
48 |
91 |
--------------------------------------------------------------------------------
/backend/config/bootstrap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Bootstrap
3 | * (sails.config.bootstrap)
4 | *
5 | * An asynchronous bootstrap function that runs before your Sails app gets lifted.
6 | * This gives you an opportunity to set up your data model, run jobs, or perform some special logic.
7 | *
8 | * For more information on bootstrapping your app, check out:
9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.bootstrap.html
10 | */
11 |
12 | const users = [{
13 | name: 'Joe',
14 | password: 'toasty'
15 | }, {
16 | name: 'Anna',
17 | password: 'sunflower'
18 | }, {
19 | name: 'Tom',
20 | password: 'jerry'
21 | }]
22 |
23 | const products = [{
24 | title: 'Cell phone',
25 | description: 'Now with a cool touch display and many colors.',
26 | price: 199.95,
27 | user: 1
28 | }, {
29 | title: 'Television',
30 | description: 'This is a brand new television with remote control.',
31 | price: 499.99,
32 | user: 2
33 | }, {
34 | title: 'Keyboard',
35 | description: 'The coolest keyboard in the world. Just buy it.',
36 | price: 10.00,
37 | user: 2
38 | }, {
39 | title: 'Book',
40 | description: 'A entertaining, exciting book with many images.',
41 | price: 14.95,
42 | user: 3
43 | }, {
44 | title: 'Sword',
45 | description: 'Take your enemies apart with this lovely sword.',
46 | price: 300,
47 | user: 2
48 | }, {
49 | title: 'Mouse',
50 | description: 'This is the new generation mouse with multiple inputs.',
51 | price: 12.99,
52 | user: 3
53 | }, {
54 | title: 'Watch',
55 | description: 'There is no better watch in this world. Just buy it.',
56 | price: 199.95,
57 | user: 1
58 | }, {
59 | title: 'Laptop',
60 | description: 'This laptop lacks of nothing. You can do everything with it.',
61 | price: 700,
62 | user: 2
63 | }, {
64 | title: 'Printer',
65 | description: 'You print so many things with this printer. Give it a try.',
66 | price: 395,
67 | user: 1
68 | }, {
69 | title: 'Headphones',
70 | description: 'Feel the music with these headphones. Exclusive and special.',
71 | price: 100.95,
72 | user: 2
73 | }, {
74 | title: 'House',
75 | description: 'Many rooms, many windows, one door. What more to say? It\'s a house.',
76 | price: 100000,
77 | user: 2
78 | }, {
79 | title: 'Water',
80 | description: 'If you feel dry and not liquid you should buy this water instantly.',
81 | price: 0.95,
82 | user: 1
83 | }]
84 |
85 | module.exports.bootstrap = function(cb) {
86 | users.forEach(user => {
87 |
88 | User
89 | .findOne({
90 | name: user.name
91 | })
92 | .exec((error, potentialUser) => {
93 | if (error) sails.log.error(error)
94 |
95 | if (!potentialUser) {
96 | User
97 | .create({
98 | name: user.name,
99 | password: user.password
100 | })
101 | .exec((error, user) => {
102 | sails.log.info('Created fixture user', user)
103 | })
104 | }
105 | })
106 | })
107 |
108 | products.forEach(product => {
109 | Product
110 | .findOrCreate({
111 | title: product.title,
112 | description: product.description,
113 | price: product.price,
114 | user: product.user
115 | })
116 | .exec((error, product) => {
117 | if (error) sails.log.error(error)
118 |
119 | sails.log.info('Created fixture product', product)
120 | })
121 | })
122 |
123 | cb()
124 | }
125 |
--------------------------------------------------------------------------------
/backend/assets/precache-manifest.28eda1f90a81d9bfde2bfaf166d3f21f.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = [
2 | {
3 | "revision": "7cb72ca9be1e52890315",
4 | "url": "/js/0011.d26555d2.js"
5 | },
6 | {
7 | "revision": "1e22d0bbae81de3dc9ce",
8 | "url": "/js/83d0.4eb65a7f.js"
9 | },
10 | {
11 | "revision": "43bf89bcde05089277e9",
12 | "url": "/js/92d7.1f72dc58.js"
13 | },
14 | {
15 | "revision": "560d20ca9ac5658d16bf",
16 | "url": "/js/app.1bcceadd.js"
17 | },
18 | {
19 | "revision": "22b7e24a35f5270dd684",
20 | "url": "/css/chunk-01c9.4ddb07be.css"
21 | },
22 | {
23 | "revision": "22b7e24a35f5270dd684",
24 | "url": "/js/chunk-01c9.9f7230ba.js"
25 | },
26 | {
27 | "revision": "f98a57d24f3005b06f21",
28 | "url": "/css/chunk-04f2.d46b21cd.css"
29 | },
30 | {
31 | "revision": "f98a57d24f3005b06f21",
32 | "url": "/js/chunk-04f2.1b246ded.js"
33 | },
34 | {
35 | "revision": "9aa7061667031e9c2dba",
36 | "url": "/js/chunk-08b7.8512807a.js"
37 | },
38 | {
39 | "revision": "509b072ffa4180493b53",
40 | "url": "/js/chunk-1563.7396ec83.js"
41 | },
42 | {
43 | "revision": "378bbf3854c988d7d08b",
44 | "url": "/js/chunk-1a17.30f2d575.js"
45 | },
46 | {
47 | "revision": "7f31c63beba70bf7b8d6",
48 | "url": "/js/chunk-3887.aa15bb72.js"
49 | },
50 | {
51 | "revision": "4aedc9b2b7e58d8e743e",
52 | "url": "/css/chunk-501f.1eb1bb2f.css"
53 | },
54 | {
55 | "revision": "4aedc9b2b7e58d8e743e",
56 | "url": "/js/chunk-501f.e145111d.js"
57 | },
58 | {
59 | "revision": "7c1e76f802589d41cfd5",
60 | "url": "/css/chunk-58f5.b1ea0394.css"
61 | },
62 | {
63 | "revision": "7c1e76f802589d41cfd5",
64 | "url": "/js/chunk-58f5.62a37769.js"
65 | },
66 | {
67 | "revision": "e9f6f11007417812089e",
68 | "url": "/js/chunk-596f.e000c2e0.js"
69 | },
70 | {
71 | "revision": "afe1091aefbf8c1df9d9",
72 | "url": "/css/chunk-5e44.062decc4.css"
73 | },
74 | {
75 | "revision": "afe1091aefbf8c1df9d9",
76 | "url": "/js/chunk-5e44.8377145e.js"
77 | },
78 | {
79 | "revision": "a875373a8d4bbf4501e2",
80 | "url": "/js/chunk-5e5d.1f401c26.js"
81 | },
82 | {
83 | "revision": "0b06c675490ffd7df576",
84 | "url": "/js/chunk-6017.a01ecfd3.js"
85 | },
86 | {
87 | "revision": "a1aa58fb0899e13511b1",
88 | "url": "/js/chunk-6eaa.c2ea0542.js"
89 | },
90 | {
91 | "revision": "e1d165636ee0de191bc3",
92 | "url": "/js/chunk-727a.61973d24.js"
93 | },
94 | {
95 | "revision": "99b7bb7ae474925e9456",
96 | "url": "/js/chunk-7644.e3f32036.js"
97 | },
98 | {
99 | "revision": "cebb351cda7861785dc2",
100 | "url": "/js/chunk-7bd0.f386b588.js"
101 | },
102 | {
103 | "revision": "435faf69d77a7e1e1478",
104 | "url": "/js/chunk-84ba.0c9cad26.js"
105 | },
106 | {
107 | "revision": "6d5ecc03006c032fb0f1",
108 | "url": "/js/chunk-a85b.82aa834f.js"
109 | },
110 | {
111 | "revision": "a1510bf2918075a75851",
112 | "url": "/js/chunk-abc6.2eb6f8f4.js"
113 | },
114 | {
115 | "revision": "6d05238dd6f7081c6d1c",
116 | "url": "/js/chunk-db80.3deb64de.js"
117 | },
118 | {
119 | "revision": "fb929085840cd998dd28",
120 | "url": "/js/chunk-vendors.3e9c575e.js"
121 | },
122 | {
123 | "revision": "430f0a872990c82c3ca55b9a44cf46e8",
124 | "url": "/index.html"
125 | },
126 | {
127 | "revision": "d2bd7d76fb1ea6f201f8bb1ea1345ba1",
128 | "url": "/img/baseline-info-24px.svg"
129 | }
130 | ];
--------------------------------------------------------------------------------
/backend/config/routes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Route Mappings
3 | * (sails.config.routes)
4 | *
5 | * Your routes map URLs to views and controllers.
6 | *
7 | * If Sails receives a URL that doesn't match any of the routes below,
8 | * it will check for matching files (images, scripts, stylesheets, etc.)
9 | * in your assets directory. e.g. `http://localhost:1337/images/foo.jpg`
10 | * might match an image file: `/assets/images/foo.jpg`
11 | *
12 | * Finally, if those don't match either, the default 404 handler is triggered.
13 | * See `api/responses/notFound.js` to adjust your app's 404 logic.
14 | *
15 | * Note: Sails doesn't ACTUALLY serve stuff from `assets`-- the default Gruntfile in Sails copies
16 | * flat files from `assets` to `.tmp/public`. This allows you to do things like compile LESS or
17 | * CoffeeScript for the front-end.
18 | *
19 | * For more information on configuring custom routes, check out:
20 | * http://sailsjs.org/#!/documentation/concepts/Routes/RouteTargetSyntax.html
21 | */
22 |
23 | module.exports.routes = {
24 |
25 | /***************************************************************************
26 | * *
27 | * Make the view located at `views/homepage.ejs` (or `views/homepage.jade`, *
28 | * etc. depending on your default view engine) your home page. *
29 | * *
30 | * (Alternatively, remove this and add an `index.html` file in your *
31 | * `assets` directory) *
32 | * *
33 | ***************************************************************************/
34 |
35 | '/*': {
36 | controller: 'App',
37 | action: 'serve',
38 | skipAssets: true,
39 | skipRegex: /^\/api\/.*$/
40 | },
41 |
42 | 'post /api/help': 'help/Help.postHelp',
43 |
44 | 'post /api/register/post': 'Register.post',
45 |
46 | 'post /api/login/post': 'LoginController.post',
47 |
48 | 'get /api/user/get': 'user/UserController.get',
49 |
50 | 'get /api/products/get': 'ProductsController.getProducts',
51 |
52 | 'post /api/user/products/product/post': 'user/products/product/ProductController.postProduct',
53 |
54 | 'get /api/user/products/product/get': 'user/products/product/ProductController.getProduct',
55 |
56 | 'patch /api/user/products/product/patch': 'user/products/product/ProductController.patchProduct',
57 |
58 | 'delete /api/user/products/product/delete': 'user/products/product/ProductController.deleteProduct',
59 |
60 | 'get /api/user/products/getProductsByUser': 'user/products/ProductsController.getProductsByUser',
61 |
62 | 'get /api/user/products/get': 'user/products/ProductsController.getProducts',
63 |
64 | 'post /api/user/shop/basket/post': 'user/shop/BasketController.postBasket'
65 |
66 | /***************************************************************************
67 | * *
68 | * Custom routes here... *
69 | * *
70 | * If a request to a URL doesn't match any of the custom routes above, it *
71 | * is matched against Sails route blueprints. See `config/blueprints.js` *
72 | * for configuration options and examples. *
73 | * *
74 | ***************************************************************************/
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/frontend/src/components/help/Help.desktop.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |