├── .editorconfig ├── .gitignore ├── README.md ├── api └── auth.js ├── assets ├── README.md └── logo.png ├── components ├── Logo.vue ├── README.md └── StaffDirectory.vue ├── layouts ├── README.md └── default.vue ├── middleware └── README.md ├── nuxt.config.js ├── package-lock.json ├── package.json ├── pages ├── README.md ├── index.vue ├── login.vue └── users │ ├── _id.vue │ └── index.vue ├── plugins └── README.md ├── static ├── README.md └── favicon.ico ├── store ├── README.md └── index.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jump Start Vue.js - Chapter 8 Demo 2 | 3 | This example builds upon everything covered in the book. It uses the concepts learned to create a Nuxt.js app with a Vuex store and authentication that fetches data from a remote API and displays it in a custom component, linking to a detail page for each item. 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | $ npm install 10 | 11 | # serve with hot reload at localhost:3000 12 | $ npm run dev 13 | 14 | # build for production and launch server 15 | $ npm run build 16 | $ npm start 17 | 18 | # generate static project 19 | $ npm run generate 20 | ``` 21 | 22 | For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org). 23 | -------------------------------------------------------------------------------- /api/auth.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import bodyParser from 'body-parser' 3 | import cookieParser from 'cookie-parser' 4 | import jwt from 'express-jwt' 5 | import jsonwebtoken from 'jsonwebtoken' 6 | 7 | // Create app 8 | const app = express() 9 | 10 | // Install middleware 11 | app.use(cookieParser()) 12 | app.use(bodyParser.json()) 13 | 14 | // JWT middleware 15 | app.use( 16 | jwt({ 17 | secret: 'dummy', 18 | algorithms: ['sha1', 'RS256', 'HS256'] 19 | }).unless({ 20 | path: ['/api/auth/login', '/api/auth/refresh'] 21 | }) 22 | ) 23 | 24 | // Refresh tokens 25 | const refreshTokens = {} 26 | 27 | // -- Routes -- 28 | 29 | // [POST] /login 30 | app.post('/login', (req, res) => { 31 | const { username, password } = req.body 32 | const valid = username.length && password === '123' 33 | const expiresIn = 15 34 | const refreshToken = 35 | Math.floor(Math.random() * (1000000000000000 - 1 + 1)) + 1 36 | 37 | if (!valid) { 38 | throw new Error('Invalid username or password') 39 | } 40 | 41 | const accessToken = jsonwebtoken.sign( 42 | { 43 | username, 44 | picture: 'https://github.com/nuxt.png', 45 | name: 'User ' + username, 46 | scope: ['test', 'user'] 47 | }, 48 | 'dummy', 49 | { 50 | expiresIn 51 | } 52 | ) 53 | 54 | refreshTokens[refreshToken] = { 55 | accessToken, 56 | user: { 57 | username, 58 | picture: 'https://github.com/nuxt.png', 59 | name: 'User ' + username 60 | } 61 | } 62 | 63 | res.json({ 64 | token: { 65 | accessToken, 66 | refreshToken 67 | } 68 | }) 69 | }) 70 | 71 | app.post('/refresh', (req, res) => { 72 | const { refreshToken } = req.body 73 | 74 | if (refreshToken in refreshTokens) { 75 | const user = refreshTokens[refreshToken].user 76 | const expiresIn = 15 77 | const newRefreshToken = 78 | Math.floor(Math.random() * (1000000000000000 - 1 + 1)) + 1 79 | delete refreshTokens[refreshToken] 80 | const accessToken = jsonwebtoken.sign( 81 | { 82 | user: user.username, 83 | picture: 'https://github.com/nuxt.png', 84 | name: 'User ' + user.username, 85 | scope: ['test', 'user'] 86 | }, 87 | 'dummy', 88 | { 89 | expiresIn 90 | } 91 | ) 92 | 93 | refreshTokens[newRefreshToken] = { 94 | accessToken, 95 | user 96 | } 97 | 98 | res.json({ 99 | token: { 100 | accessToken, 101 | refreshToken: newRefreshToken 102 | } 103 | }) 104 | } else { 105 | res.sendStatus(401) 106 | } 107 | }) 108 | 109 | // [GET] /user 110 | app.get('/user', (req, res) => { 111 | res.json({ user: req.user }) 112 | }) 113 | 114 | // [POST] /logout 115 | app.post('/logout', (_req, res) => { 116 | res.json({ status: 'OK' }) 117 | }) 118 | 119 | // Error handler 120 | app.use((err, _req, res) => { 121 | console.error(err) // eslint-disable-line no-console 122 | res.status(401).send(err + '') 123 | }) 124 | 125 | // -- export app -- 126 | export default { 127 | path: '/api/auth', 128 | handler: app 129 | } 130 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spbooks/jsvuejs2/6ce99fd69ee5687ec7e3cee6f8d4c7452939a2f6/assets/logo.png -------------------------------------------------------------------------------- /components/Logo.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /components/StaffDirectory.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 70 | 71 | -------------------------------------------------------------------------------- /layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 59 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Global page headers: https://go.nuxtjs.dev/config-head 3 | head: { 4 | title: 'staff-manager', 5 | htmlAttrs: { 6 | lang: 'en' 7 | }, 8 | meta: [ 9 | { charset: 'utf-8' }, 10 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 11 | { hid: 'description', name: 'description', content: '' } 12 | ], 13 | link: [ 14 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, 15 | { 16 | rel: 'stylesheet', 17 | type: 'text/css', 18 | href: 19 | 'https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css' 20 | } 21 | ] 22 | }, 23 | 24 | // Global CSS: https://go.nuxtjs.dev/config-css 25 | css: [ 26 | ], 27 | 28 | // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins 29 | plugins: [ 30 | ], 31 | 32 | // Auto import components: https://go.nuxtjs.dev/config-components 33 | components: true, 34 | 35 | // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules 36 | buildModules: [ 37 | ], 38 | 39 | // Modules: https://go.nuxtjs.dev/config-modules 40 | modules: [ 41 | // https://go.nuxtjs.dev/axios 42 | '@nuxtjs/axios', 43 | '@nuxtjs/auth' 44 | ], 45 | 46 | // Axios module configuration: https://go.nuxtjs.dev/config-axios 47 | axios: {}, 48 | 49 | // Build Configuration: https://go.nuxtjs.dev/config-build 50 | build: { 51 | }, 52 | 53 | serverMiddleware: ['~/api/auth'], 54 | 55 | auth: { 56 | strategies: { 57 | local: { 58 | endpoints: { 59 | login: { propertyName: 'token.accessToken' } 60 | } 61 | } 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "staff-manager", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "nuxt", 7 | "build": "nuxt build", 8 | "start": "nuxt start", 9 | "generate": "nuxt generate" 10 | }, 11 | "dependencies": { 12 | "@nuxtjs/auth": "^4.9.1", 13 | "@nuxtjs/axios": "^5.13.1", 14 | "cookie-parser": "^1.4.5", 15 | "core-js": "^3.9.1", 16 | "express": "^4.17.1", 17 | "express-jwt": "^6.0.0", 18 | "nuxt": "^2.15.3" 19 | }, 20 | "devDependencies": {} 21 | } 22 | -------------------------------------------------------------------------------- /pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /pages/login.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | -------------------------------------------------------------------------------- /pages/users/_id.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | -------------------------------------------------------------------------------- /pages/users/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spbooks/jsvuejs2/6ce99fd69ee5687ec7e3cee6f8d4c7452939a2f6/static/favicon.ico -------------------------------------------------------------------------------- /store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | employees: [], 3 | selectedEmployee: null 4 | }); 5 | 6 | export const getters = { 7 | employeeList(state) { 8 | return state.employees.map(member => ({ 9 | id: member.login.uuid, 10 | firstName: member.name.first, 11 | lastName: member.name.last, 12 | email: member.email, 13 | phone: member.phone, 14 | nationality: member.nat, 15 | avatar: member.picture.thumbnail 16 | })); 17 | } 18 | }; 19 | 20 | export const mutations = { 21 | setEmployees(state, { employees }) { 22 | state.employees = employees 23 | }, 24 | 25 | setSelectedEmployee(state, { employee }) { 26 | state.selectedEmployee = employee 27 | } 28 | }; 29 | 30 | export const actions = { 31 | async getEmployees({ commit }) { 32 | const { results } = await this.$axios.$get( 33 | 'https://randomuser.me/api/?nat=gb,us,au&results=10&seed=abc' 34 | ); 35 | commit('setEmployees', { employees: results }); 36 | }, 37 | 38 | async getEmployee({ commit, dispatch, state }, { id }) { 39 | if (state.employees.length === 0) { 40 | await dispatch('getEmployees'); 41 | } 42 | 43 | const employee = state.employees.find(user => user.login.uuid === id); 44 | commit('setSelectedEmployee', { employee }); 45 | } 46 | }; 47 | --------------------------------------------------------------------------------