├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── README.md ├── components ├── fans.vue ├── footer.vue ├── header.vue ├── hero.vue ├── ranking.vue ├── statsbar.vue └── tabs.vue ├── layouts └── default.vue ├── now.json ├── nuxt.config.js ├── nuxt_schems.jpeg ├── package.json ├── pages ├── _slug.vue ├── choose.vue └── index.vue ├── plugins ├── auth.js └── sockets.js ├── server-middleware ├── github-callback.js └── logout.js ├── static ├── aquaman.jpg ├── batman.jpg ├── black-widow.jpg ├── captain-america.jpg ├── cyborg.jpg ├── dc.svg ├── doctor-strange.jpg ├── favicon.ico ├── flash.jpg ├── hulk.jpg ├── icon.png ├── iron-man.jpg ├── marvel.svg ├── superman.jpg ├── thor.jpg └── wonder-woman.jpg ├── store ├── auth.js ├── heroes.js └── 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 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 12 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 13 | 'plugin:vue/essential', 14 | ], 15 | // required to lint *.vue files 16 | plugins: [ 17 | 'vue' 18 | ], 19 | // add your custom rules here 20 | rules: {} 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # logs 5 | npm-debug.log 6 | yarn-error.log 7 | 8 | # PWA 9 | sw.* 10 | 11 | # Nuxt build 12 | .nuxt 13 | 14 | # Nuxt generate 15 | dist 16 | 17 | # Coverage 18 | coverage 19 | .nyc_output 20 | 21 | # Editors 22 | .vscode 23 | .idea 24 | 25 | # Misc 26 | .DS_Store 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![marvel-vs-dc](https://user-images.githubusercontent.com/904724/38923201-5649f35e-42fa-11e8-890a-30eec5a8e4ba.png) 2 | 3 | # nuxt-heroes 4 | 5 | > Marvel vs Avengers Heroes demo (for JS Heroes). 6 | 7 | ## Setup 8 | 9 | > You need to run [nuxt-heroes-api](https://github.com/Atinux/nuxt-heroes-api) before running the Nuxt app 10 | 11 | ```bash 12 | yarn # or npm install 13 | yarn dev # or npm run dev 14 | ``` 15 | 16 | ### Deployment 17 | 18 | ```bash 19 | npm run build 20 | npm start 21 | ``` 22 | 23 | Please run `npm start` with the following envs: 24 | 25 | ``` 26 | HEROES_API= 27 | GITHUB_CLIENT_ID= 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /components/fans.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | 27 | 50 | -------------------------------------------------------------------------------- /components/footer.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 31 | -------------------------------------------------------------------------------- /components/header.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 37 | 38 | 65 | -------------------------------------------------------------------------------- /components/hero.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | 36 | 62 | -------------------------------------------------------------------------------- /components/ranking.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | 31 | 87 | -------------------------------------------------------------------------------- /components/statsbar.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 46 | 47 | 80 | -------------------------------------------------------------------------------- /components/tabs.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 28 | 29 | 54 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | 21 | 54 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "heroes.nuxtjs.org", 3 | "env": { 4 | "GITHUB_CLIENT_ID": "55440d3936ba0961241e", 5 | "HEROES_API": "https://api.heroes.nuxtjs.org" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | process.env.HEROES_API = process.env.HEROES_API || 'https://api.heroes.nuxtjs.org' 2 | process.env.GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID || '55440d3936ba0961241e' 3 | 4 | export default { 5 | mode: 'universal', // server-side rendering activated 6 | /* 7 | ** Headers of the page 8 | */ 9 | head: { 10 | htmlAttrs: { lang: 'en' }, 11 | title: 'Heroes: Marvel vs DC', 12 | meta: [ 13 | { charset: 'utf-8' }, 14 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 15 | { hid: 'description', name: 'description', content: 'Marvel vs DC Heroes demo' }, 16 | { 'http-equiv': 'Content-Language', content: 'en' }, 17 | { property: 'og:type', content: 'website' }, 18 | { property: 'og:image', content: 'https://user-images.githubusercontent.com/904724/38987861-88525af8-43d1-11e8-84eb-cb69962594e2.png' } 19 | ], 20 | link: [ 21 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 22 | ] 23 | }, 24 | 25 | /* 26 | ** Customize the progress-bar color 27 | */ 28 | loading: { color: '#000000' }, 29 | 30 | /* 31 | ** Plugins to load before mounting the App 32 | */ 33 | plugins: [ 34 | '~/plugins/auth', 35 | { src: '~/plugins/sockets', ssr: false } 36 | ], 37 | /* 38 | ** Env 39 | */ 40 | env: { 41 | api: process.env.HEROES_API, 42 | github: { 43 | oauthUrl: `https://github.com/login/oauth/authorize?client_id=${process.env.GITHUB_CLIENT_ID}` 44 | } 45 | }, 46 | /* 47 | ** Server middleware 48 | */ 49 | serverMiddleware: [ 50 | '~/server-middleware/github-callback', 51 | '~/server-middleware/logout' 52 | ], 53 | /* 54 | ** Nuxt.js modules 55 | */ 56 | modules: [ 57 | // Doc: https://axios.nuxtjs.org/usage.html 58 | '@nuxtjs/axios' 59 | ], 60 | /* 61 | ** modules configuration 62 | */ 63 | axios: { 64 | // See https://axios.nuxtjs.org/options.html 65 | proxy: true, 66 | proxyHeaders: false 67 | }, 68 | proxy: { 69 | '/api': { 70 | target: process.env.HEROES_API, 71 | pathRewrite: { 72 | '^/api/': '/' 73 | } 74 | } 75 | }, 76 | /* 77 | ** Build configuration 78 | */ 79 | build: { 80 | /* 81 | ** You can extend webpack config here 82 | */ 83 | extend(config, ctx) { 84 | // Run ESLint on save 85 | if (ctx.isDev && ctx.isClient) { 86 | config.module.rules.push({ 87 | enforce: 'pre', 88 | test: /\.(js|vue)$/, 89 | loader: 'eslint-loader', 90 | exclude: /(node_modules)/ 91 | }) 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /nuxt_schems.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/nuxt_schems.jpeg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heroes", 3 | "version": "1.0.0", 4 | "description": "Marvel vs DC Heroes", 5 | "author": "Chopin brothers", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate", 12 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", 13 | "precommit": "npm run lint" 14 | }, 15 | "dependencies": { 16 | "@nuxtjs/axios": "^5.0.0", 17 | "cookies": "^0.7.1", 18 | "nuxt-edge": "^2.0.0-25621351.88c9bae", 19 | "socket.io-client": "^2.1.0" 20 | }, 21 | "devDependencies": { 22 | "babel-eslint": "^8.2.1", 23 | "cross-env": "^5.0.1", 24 | "eslint": "^4.15.0", 25 | "eslint-loader": "^2.0.0", 26 | "eslint-plugin-vue": "^4.0.0", 27 | "node-sass": "^4.8.3", 28 | "sass-loader": "^7.0.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pages/_slug.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 33 | -------------------------------------------------------------------------------- /pages/choose.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 51 | 52 | 83 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 36 | 37 | 53 | -------------------------------------------------------------------------------- /plugins/auth.js: -------------------------------------------------------------------------------- 1 | export default ({ app, store }) => { 2 | // Set token to axios 3 | if (store.state.auth.jwt) { 4 | app.$axios.setToken(store.state.auth.jwt, 'Bearer') 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /plugins/sockets.js: -------------------------------------------------------------------------------- 1 | import io from 'socket.io-client' 2 | 3 | export default ({ store, env }, inject) => { 4 | const socket = io(`${env.api}/push`) 5 | 6 | inject('socket', socket) 7 | socket.on('connect', () => store.dispatch('heroes/syncHeroes')) 8 | } 9 | -------------------------------------------------------------------------------- /server-middleware/github-callback.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const Cookies = require('cookies') 3 | const { parse } = require('url') 4 | 5 | module.exports = { 6 | path: '/github/callback', 7 | handler: async (req, res) => { 8 | // Get cookies 9 | const cookies = new Cookies(req, res) 10 | // Get req.query 11 | req.query = parse(req.url, true).query 12 | // Call Api to verify Github OAuth 2.0 code 13 | try { 14 | const { data } = await axios.get(`https://api.heroes.nuxtjs.org/github/auth?code=${req.query.code}`) 15 | // Add JWT cookie 16 | cookies.set('jwt', data.token, { httpOnly: true }) 17 | // Redirect to home 18 | res.setHeader('Location', '/choose') 19 | } catch (err) { 20 | cookies.set('jwt') // Remove jwt cookie 21 | res.setHeader('Location', '/') 22 | } 23 | // Set status code to 302 - Found and end request 24 | res.statusCode = 302 25 | res.end() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server-middleware/logout.js: -------------------------------------------------------------------------------- 1 | const Cookies = require('cookies') 2 | 3 | module.exports = { 4 | path: '/logout', 5 | handler: async (req, res) => { 6 | // Get cookies 7 | const cookies = new Cookies(req, res) 8 | cookies.set('jwt', '') // Remove jwt cookie 9 | res.status = 200 10 | res.end() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /static/aquaman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/aquaman.jpg -------------------------------------------------------------------------------- /static/batman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/batman.jpg -------------------------------------------------------------------------------- /static/black-widow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/black-widow.jpg -------------------------------------------------------------------------------- /static/captain-america.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/captain-america.jpg -------------------------------------------------------------------------------- /static/cyborg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/cyborg.jpg -------------------------------------------------------------------------------- /static/dc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | path4182 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/doctor-strange.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/doctor-strange.jpg -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/favicon.ico -------------------------------------------------------------------------------- /static/flash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/flash.jpg -------------------------------------------------------------------------------- /static/hulk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/hulk.jpg -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/icon.png -------------------------------------------------------------------------------- /static/iron-man.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/iron-man.jpg -------------------------------------------------------------------------------- /static/marvel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /static/superman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/superman.jpg -------------------------------------------------------------------------------- /static/thor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/thor.jpg -------------------------------------------------------------------------------- /static/wonder-woman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atinux/nuxt-heroes/7d578a58209943c206a60c633384c92c44c08757/static/wonder-woman.jpg -------------------------------------------------------------------------------- /store/auth.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | user: null, 3 | jwt: null 4 | }) 5 | 6 | export const mutations = { 7 | SET_JWT(state, jwt) { 8 | state.jwt = jwt 9 | }, 10 | SET_USER(state, user) { 11 | state.user = user 12 | } 13 | } 14 | 15 | export const getters = { 16 | connected: (state) => !!state.user, 17 | } 18 | 19 | export const actions = { 20 | async checkConnectedUser({ dispatch, commit, rootState }, { error, req, res }) { 21 | const Cookies = (process.server ? require('cookies') : null) 22 | const cookies = new Cookies(req, res) 23 | 24 | // Get hwt cookie 25 | let jwt = cookies.get('jwt') 26 | if (!jwt) return 27 | // Check if JWT is good 28 | try { 29 | const user = await this.$axios.$get(`/api/user`, { 30 | headers: { 31 | Authorization: `Bearer ${jwt}` 32 | } 33 | }) 34 | // Update state 35 | commit('SET_JWT', jwt) 36 | commit('SET_USER', user) 37 | } catch (err) { 38 | cookies.set('jwt') // Remove jwt cookie 39 | } 40 | }, 41 | async logout({ commit }) { 42 | commit('SET_JWT', null) 43 | commit('SET_USER', null) 44 | this.$axios.setToken(false) // Remove jwt in axios requests 45 | await this.$axios.get('/logout') 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /store/heroes.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | heroes: [] 3 | }) 4 | 5 | const sortHeroes = (heroes) => heroes.sort((h1, h2) => h2.nbFans - h1.nbFans) 6 | 7 | export const mutations = { 8 | SET_HEROES(state, heroes) { 9 | state.heroes = sortHeroes(heroes) 10 | }, 11 | SET_HERO_FANS(state, hero) { 12 | const foundHero = state.heroes.find((h) => h.slug === hero.slug) 13 | 14 | if (!foundHero) return 15 | foundHero.fans = hero.fans 16 | foundHero.nbFans = hero.nbFans 17 | // Sort heroes 18 | state.heroes = sortHeroes(state.heroes) 19 | } 20 | } 21 | 22 | export const actions = { 23 | async fetchHeroes({ commit }) { 24 | const heroes = await this.$axios.$get('/api/heroes') 25 | 26 | commit('SET_HEROES', heroes) 27 | }, 28 | async syncHeroes({ commit }, socket) { 29 | this.$socket.on('hero:updated', (hero) => commit('SET_HERO_FANS', hero)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({}) 2 | 3 | export const getters = { 4 | oauthUrl: (state) => `${process.env.github.oauthUrl}` 5 | } 6 | 7 | export const actions = { 8 | // Called on page load on server-side 9 | async nuxtServerInit({ getters, dispatch, commit }, context) { 10 | await dispatch('auth/checkConnectedUser', context) 11 | await dispatch('heroes/fetchHeroes', context) 12 | } 13 | } 14 | --------------------------------------------------------------------------------