├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── README.md ├── babel.config.js ├── deploy.sh ├── package.json ├── postcss.config.js ├── public ├── favicon.ico └── index.html ├── server.js ├── src ├── App.vue ├── assets │ └── css │ │ └── style.css ├── components │ ├── Menu.vue │ ├── PageNotFound.vue │ └── SocialLogin.vue ├── config │ ├── facebook_oAuth.js │ ├── google_oAuth.js │ └── utils.js ├── main.js ├── registerServiceWorker.js ├── router │ └── router.js ├── store │ └── store.js └── views │ ├── Home.vue │ ├── Login.vue │ └── SignUp.vue └── tests └── unit ├── .eslintrc.js └── example.spec.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | }, 17 | overrides: [ 18 | { 19 | files: [ 20 | '**/__tests__/*.{j,t}s?(x)' 21 | ], 22 | env: { 23 | mocha: true 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | package-lock.json 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Signup with Facebook / Google using Vue 2 | 3 | Handling SignUp or SignIn with Google and Facebook using Pure VueJS applications without any external package. 4 | 5 | ## Project setup 6 | ``` 7 | npm install 8 | ``` 9 | 10 | ## Google Setup Client ID 11 | 12 | import GoogleAuth from '@/config/google.js' 13 | const gauthOption = { 14 | clientId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com', 15 | scope: 'profile email', 16 | prompt: 'select_account' 17 | } 18 | Vue.use(GoogleAuth, gauthOption) 19 | 20 | 21 | ## Options 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
PropertyTypeRequiredDescription
clientIdStringRequired.The app's client ID, found and created in the Google Developers Console.
scopeStringOptional.Default value is profile email. Full list of scopes.
promptStringOptional.This value using for authCode. The possible values are select_account or consent. Default value is select_account. To get refresh token from auth code, use consent.
fetch_basic_profileBooleanOptional.If set to true, email profile openid will be automatically added as scope. Default value is true.
59 | 60 | ## Methods 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
PropertyDescriptionType
GoogleAuthreturn of gapi.auth2.getAuthInstance()Object
isAuthorizedWhether or not you have authBoolean
isInitWhether or not api initBoolean
isLoadedWhether or not api init. will be deprecated.Function
signInfunction for sign-inFunction
getAuthCodefunction for getting authCodeFunction
signOutfunction for sign-outFunction
108 | 109 | ## Usage 110 | 111 | We already initalized GoogleAuth and directly we can add click event for the login button as below, 112 | 113 | loginWithGoogle () { 114 | this.$gAuth 115 | .signIn() 116 | .then(GoogleUser => { 117 | // on success do something 118 | console.log('GoogleUser', GoogleUser) 119 | console.log('getId', GoogleUser.getId()) 120 | console.log('getBasicProfile', GoogleUser.getBasicProfile()) 121 | console.log('getAuthResponse', GoogleUser.getAuthResponse()) 122 | }) 123 | .catch(error => { 124 | console.log('error', error) 125 | }) 126 | } 127 | 128 | ## Facebook App ID Setup 129 | Important: The Facebook SDK must first be loaded asynchronously for the plugin to work. Something like this will do: 130 | 131 | export const initFbsdk = () => { 132 | return new Promise(resolve => { 133 | window.fbAsyncInit = function() { 134 | FB.init({ 135 | appId : 'xxxxxxxxxxxxxxxxxxx', 136 | cookie : true, // enable cookies to allow the server to access the session 137 | xfbml : true, // parse social plugins on this page 138 | version : 'v2.8' // use graph api version 2.8 139 | }); 140 | }; 141 | (function(d, s, id) { 142 | var js, fjs = d.getElementsByTagName(s)[0]; 143 | if (d.getElementById(id)) return; 144 | js = d.createElement(s); js.id = id; 145 | js.src = "//connect.facebook.net/en_US/sdk.js"; 146 | fjs.parentNode.insertBefore(js, fjs); 147 | }(document, 'script', 'facebook-jssdk')); 148 | }) 149 | } 150 | 151 | If you're not in a modular environment, just include it as a 154 | 155 | ### Usage 156 | Step 1: import and use the plugin if you're in a modular environment otherwise plugin will register itself. 157 | 158 | import { initFbsdk } from '@/config/facebook_oAuth.js' 159 | 160 | Step 2: Initialize the Facebook instance with the app id 161 | 162 | mounted () { 163 | initFbsdk() 164 | } 165 | Step 3: Add the button click event 166 | 167 | loginWithFacebook () { 168 | window.FB.login(response => { 169 | console.log('fb response', response) 170 | }, this.params) 171 | } 172 | 173 | ### Compiles and hot-reloads for development 174 | ``` 175 | npm run serve 176 | ``` 177 | 178 | ## [Demo Link](https://young-savannah-57214.herokuapp.com/login) 179 | # Login Screen 180 | 181 | Login 182 | 183 | 184 | # SignUp Screen 185 | 186 | SignUp 187 | 188 | # Facebook with SignUp/SignIn 189 | Facebook SignUp/SignIn 190 | 191 | # Google with SignUp/SignIn 192 | Google SignUp/SignIn 193 | 194 | ### Compiles and minifies for production 195 | ``` 196 | npm run build 197 | ``` 198 | 199 | ### Run your tests 200 | ``` 201 | npm run test 202 | ``` 203 | 204 | ### Lints and fixes files 205 | ``` 206 | npm run lint 207 | ``` 208 | 209 | ### Run your unit tests 210 | ``` 211 | npm run test:unit 212 | ``` 213 | 214 | ### Customize configuration 215 | See [Configuration Reference](https://cli.vuejs.org/config/). 216 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # abort on errors 3 | set -e 4 | # build 5 | npm run build 6 | # navigate into the build output directory 7 | cd dist 8 | # if you are deploying to a custom domain 9 | # echo 'www.example.com' > CNAME 10 | git init 11 | git add -A 12 | git commit -m 'deploy' 13 | git push -f git@github.com/Jebasuthan/Vue-Facebook-Google-oAuth.git master:gh-pages 14 | cd - -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "signup_with_facebook_google_vue", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "postinstall": "if test \"$NODE_ENV\" = \"production\" ; then npm run build ; fi ", 7 | "start": "HTTPS=true node server.js", 8 | "serve": "vue-cli-service serve", 9 | "build": "vue-cli-service build", 10 | "lint": "vue-cli-service lint", 11 | "test:unit": "vue-cli-service test:unit", 12 | "deploy": "sh deploy.sh" 13 | }, 14 | "dependencies": { 15 | "connect-history-api-fallback": "^1.6.0", 16 | "core-js": "^2.6.5", 17 | "express": "^4.17.1", 18 | "register-service-worker": "^1.6.2", 19 | "vue": "^2.6.10", 20 | "vue-router": "^3.0.3", 21 | "vuex": "^3.0.1" 22 | }, 23 | "devDependencies": { 24 | "@vue/cli-plugin-babel": "^3.11.0", 25 | "@vue/cli-plugin-eslint": "^3.11.0", 26 | "@vue/cli-plugin-unit-mocha": "^3.11.0", 27 | "@vue/cli-service": "^3.11.0", 28 | "@vue/eslint-config-standard": "^4.0.0", 29 | "@vue/test-utils": "1.0.0-beta.29", 30 | "babel-eslint": "^10.0.1", 31 | "chai": "^4.1.2", 32 | "eslint": "^5.16.0", 33 | "eslint-plugin-vue": "^5.0.0", 34 | "vue-template-compiler": "^2.6.10" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jebasuthan/Vue-Facebook-Google-oAuth/0433de43d331b8375a628e1733392ce2500381dd/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Signup With Facebook Google Using Vue 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const history = require('connect-history-api-fallback'); 4 | 5 | const app = express(); 6 | const staticFileMiddleware = express.static(path.join(__dirname + '/dist')); 7 | 8 | app.use(staticFileMiddleware); 9 | app.use(history({ 10 | disableDotRule: true, 11 | verbose: true 12 | })); 13 | app.use(staticFileMiddleware); 14 | 15 | app.get('/', function (req, res) { 16 | res.render(path.join(__dirname + '/dist/index.html')); 17 | }); 18 | 19 | var server = app.listen(process.env.PORT || 8080, function () { 20 | var port = server.address().port; 21 | console.log("App now running on port", port); 22 | }); -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 11 | -------------------------------------------------------------------------------- /src/assets/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, Helvetica, sans-serif; 3 | font-size: 14px; 4 | background: #0069ff; 5 | } 6 | /* Style the tab */ 7 | .tab { 8 | overflow: hidden; 9 | border: 1px solid #ccc; 10 | background-color: #f1f1f1; 11 | } 12 | /* Style the buttons inside the tab */ 13 | .tab a { 14 | background-color: inherit; 15 | float: right; 16 | cursor: pointer; 17 | padding: 14px 16px; 18 | transition: 0.3s; 19 | font-size: 17px; 20 | text-decoration: none; 21 | border: 1px solid; 22 | } 23 | /* Change background color of buttons on hover */ 24 | .tab a:hover { 25 | background-color: #ddd; 26 | } 27 | /* Create an active/current tablink class */ 28 | .tab a.active { 29 | background-color: #ccc; 30 | } 31 | /* Style the tab content */ 32 | .tabcontent { 33 | display: none; 34 | padding: 6px 12px; 35 | border: 1px solid #ccc; 36 | border-top: none; 37 | } 38 | .loginsuccess-container { 39 | padding: 20px; 40 | margin: 0 auto; 41 | width: 80%; 42 | box-shadow: beige; 43 | border: 1px solid #ccc; 44 | border-radius: 5px; 45 | background: #fff; 46 | word-break: break-all; 47 | } 48 | .main-container { 49 | margin-top: 10%; 50 | } 51 | .box-container { 52 | padding: 20px; 53 | margin: 0 auto; 54 | width: 400px; 55 | box-shadow: beige; 56 | border: 1px solid #ccc; 57 | border-radius: 5px; 58 | background: #fff; 59 | } 60 | .heading { 61 | text-align: center; 62 | font-weight: 300; 63 | color: #444; 64 | margin: 0 auto 45px; 65 | font-size: 35px; 66 | line-height: 38px; 67 | text-transform: none; 68 | letter-spacing: 0; 69 | } 70 | .form-fields, .form-fields button { 71 | width: 100%; 72 | margin: 5px 0; 73 | line-height: 28px; 74 | border-radius: 5px; 75 | } 76 | .form-fields input { 77 | width: 100%; 78 | line-height: 40px; 79 | border-radius: 5px; 80 | border-radius: 5px; 81 | border: 1px solid #f1f1f1; 82 | background: #fff; 83 | padding: 0 5px; 84 | font-size: 14px; 85 | } 86 | .signIn { 87 | padding: 10px 32px; 88 | color: white; 89 | font-size: 16px; 90 | font-weight: 400; 91 | background: #15CD72; 92 | text-align: center; 93 | cursor: pointer; 94 | height: auto; 95 | -webkit-appearance: none; 96 | } 97 | .createaccount { 98 | padding: 15px; 99 | background-color: #0069ff; 100 | border: none; 101 | color: #fff; 102 | font-size: 16px; 103 | font-weight: 400; 104 | height: 48px; 105 | line-height: 48px; 106 | padding: 0 32px; 107 | text-align: center; 108 | border-radius: 5px; 109 | } 110 | .center { 111 | text-align: center 112 | } 113 | .login-choice span { 114 | color: #5b6987; 115 | display: -ms-grid; 116 | display: grid; 117 | font-size: 16px; 118 | width: 100%; 119 | line-height: 40px; 120 | -webkit-box-align: center; 121 | -ms-flex-align: center; 122 | align-items: center; 123 | text-align: center; 124 | -ms-grid-columns: minmax(20px,1fr) auto minmax(20px,1fr); 125 | grid-template-columns: minmax(20px,1fr) auto minmax(20px,1fr); 126 | grid-gap: 19px; 127 | } 128 | .login-choice span:after, .login-choice span:before { 129 | content: ""; 130 | border-top: 1px solid #e5e8ed; 131 | } 132 | .signup-buttons { 133 | margin-top: 15px; 134 | display: -webkit-box; 135 | display: -ms-flexbox; 136 | display: flex; 137 | -webkit-box-pack: justify; 138 | -ms-flex-pack: justify; 139 | justify-content: space-between; 140 | position: relative; 141 | } 142 | .facebook-signup, .google-signup { 143 | color: #031b4e; 144 | background: #f2f8ff; 145 | border: 1px solid rgba(0,105,255,.2); 146 | -webkit-box-sizing: border-box; 147 | box-sizing: border-box; 148 | border-radius: 3px; 149 | display: inline-block; 150 | margin-top: 0; 151 | width: 47.5%; 152 | padding: 15px; 153 | text-align: center; 154 | position: inherit; 155 | } 156 | .signup-buttons a { 157 | vertical-align: middle; 158 | text-decoration: none; 159 | } 160 | .signup-buttons svg { 161 | left: 16px; 162 | position: absolute; 163 | top: 50%; 164 | -webkit-transform: translateY(-50%); 165 | transform: translateY(-50%); 166 | } 167 | .footer, .footer a { 168 | text-align: center; 169 | color: #fff 170 | } -------------------------------------------------------------------------------- /src/components/Menu.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | -------------------------------------------------------------------------------- /src/components/PageNotFound.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /src/components/SocialLogin.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 84 | -------------------------------------------------------------------------------- /src/config/facebook_oAuth.js: -------------------------------------------------------------------------------- 1 | export const initFbsdk = () => { 2 | return new Promise(resolve => { 3 | window.fbAsyncInit = function() { 4 | FB.init({ 5 | appId: '2507113472858546', 6 | cookie: true, // enable cookies to allow the server to access the session 7 | xfbml: true, // parse social plugins on this page 8 | version: 'v2.8' // use graph api version 2.8 9 | }) 10 | }(function (d, s, id) { 11 | var js, fjs = d.getElementsByTagName(s)[0] 12 | if (d.getElementById(id)) return 13 | js = d.createElement(s); js.id = id 14 | js.src = '//connect.facebook.net/en_US/all.js' 15 | fjs.parentNode.insertBefore(js, fjs) 16 | }(document, 'script', 'facebook-jssdk')) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /src/config/google_oAuth.js: -------------------------------------------------------------------------------- 1 | var googleAuth = (function () { 2 | function installClient () { 3 | var apiUrl = 'https://apis.google.com/js/api.js' 4 | return new Promise((resolve) => { 5 | var script = document.createElement('script') 6 | script.src = apiUrl 7 | script.onreadystatechange = script.onload = function () { 8 | if (!script.readyState || /loaded|complete/.test(script.readyState)) { 9 | setTimeout(function () { 10 | resolve() 11 | }, 500) 12 | } 13 | } 14 | document.getElementsByTagName('head')[0].appendChild(script) 15 | }) 16 | } 17 | 18 | function initClient (config) { 19 | return new Promise((resolve) => { 20 | window.gapi.load('auth2', () => { 21 | window.gapi.auth2.init(config) 22 | .then(() => { 23 | resolve(window.gapi) 24 | }) 25 | }) 26 | }) 27 | } 28 | 29 | function Auth () { 30 | if (!(this instanceof Auth)) 31 | return new Auth() 32 | this.GoogleAuth = null /* window.gapi.auth2.getAuthInstance() */ 33 | this.isAuthorized = false 34 | this.isInit = false 35 | this.prompt = null 36 | this.isLoaded = function () { 37 | /* eslint-disable */ 38 | console.warn('isLoaded() will be deprecated. You can use "this.$gAuth.isInit"') 39 | return !!this.GoogleAuth 40 | } 41 | 42 | this.load = (config, prompt) => { 43 | installClient() 44 | .then(() => { 45 | return initClient(config) 46 | }) 47 | .then((gapi) => { 48 | this.GoogleAuth = gapi.auth2.getAuthInstance() 49 | this.isInit = true 50 | this.prompt = prompt 51 | this.isAuthorized = this.GoogleAuth.isSignedIn.get() 52 | }) 53 | } 54 | 55 | this.signIn = (successCallback, errorCallback) => { 56 | return new Promise((resolve, reject) => { 57 | if (!this.GoogleAuth) { 58 | if (typeof errorCallback === 'function') errorCallback(false) 59 | reject(false) 60 | return 61 | } 62 | this.GoogleAuth.signIn() 63 | .then(googleUser => { 64 | if (typeof successCallback === 'function') successCallback(googleUser) 65 | this.isAuthorized = this.GoogleAuth.isSignedIn.get() 66 | resolve(googleUser) 67 | }) 68 | .catch(error => { 69 | if (typeof errorCallback === 'function') errorCallback(error) 70 | reject(error) 71 | }) 72 | }) 73 | } 74 | 75 | this.getAuthCode = (successCallback, errorCallback) => { 76 | return new Promise((resolve, reject) => { 77 | if (!this.GoogleAuth) { 78 | if (typeof errorCallback === 'function') errorCallback(false) 79 | reject(false) 80 | return 81 | } 82 | this.GoogleAuth.grantOfflineAccess({ prompt: this.prompt }) 83 | .then(function (resp) { 84 | if (typeof successCallback === 'function') successCallback(resp.code) 85 | resolve(resp.code) 86 | }) 87 | .catch(function (error) { 88 | if (typeof errorCallback === 'function') errorCallback(error) 89 | reject(error) 90 | }) 91 | }) 92 | } 93 | 94 | this.signOut = (successCallback, errorCallback) => { 95 | return new Promise((resolve, reject) => { 96 | if (!this.GoogleAuth) { 97 | if (typeof errorCallback === 'function') errorCallback(false) 98 | reject(false) 99 | return 100 | } 101 | this.GoogleAuth.signOut() 102 | .then(() => { 103 | if (typeof successCallback === 'function') successCallback() 104 | this.isAuthorized = false 105 | resolve(true) 106 | }) 107 | .catch(error => { 108 | if (typeof errorCallback === 'function') errorCallback(error) 109 | reject(error) 110 | }) 111 | }) 112 | } 113 | } 114 | 115 | return new Auth() 116 | })() 117 | 118 | function installGoogleAuthPlugin(Vue, options) { 119 | //set config 120 | let GoogleAuthConfig = null 121 | let GoogleAuthDefaultConfig = { scope: 'profile email', discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'] } 122 | let prompt = 'select_account' 123 | if (typeof options === 'object') { 124 | GoogleAuthConfig = Object.assign(GoogleAuthDefaultConfig, options) 125 | if (options.scope) GoogleAuthConfig.scope = options.scope 126 | if (options.prompt) prompt = options.prompt 127 | if (!options.clientId) { 128 | console.warn('clientId is required') 129 | } 130 | } else { 131 | console.warn('invalid option type. Object type accepted only') 132 | } 133 | 134 | //Install Vue plugin 135 | Vue.gAuth = googleAuth 136 | Object.defineProperties(Vue.prototype, { 137 | $gAuth: { 138 | get: function () { 139 | return Vue.gAuth 140 | } 141 | } 142 | }) 143 | Vue.gAuth.load(GoogleAuthConfig, prompt) 144 | } 145 | 146 | export default installGoogleAuthPlugin -------------------------------------------------------------------------------- /src/config/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set localStorage 3 | */ 4 | export const setStore = (name, content) => { 5 | if (!name) return 6 | if (typeof content !== 'string') { 7 | content = JSON.stringify(content) 8 | } 9 | return window.localStorage.setItem(name, content) 10 | } 11 | /** 12 | * Get localStorage 13 | */ 14 | export const getStore = (name) => { 15 | if (!name) return 16 | return JSON.parse(window.localStorage.getItem(name)) 17 | } 18 | /** 19 | * Clear localStorage 20 | */ 21 | export const removeItem = (name) => { 22 | if (!name) return 23 | return window.localStorage.removeItem(name) 24 | } 25 | /** 26 | * Validate Email address 27 | */ 28 | export const isValidEmail = (value) => { 29 | return value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,64}$/i.test(value) ? false : true 30 | } 31 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router/router' 4 | import store from './store/store' 5 | 6 | import '@/assets/css/style.css' 7 | import './registerServiceWorker' 8 | 9 | import GoogleAuth from '@/config/google_oAuth.js' 10 | const gauthOption = { 11 | clientId: '707231563844-e5cpkqrlt62gncmj6b84of5sml9lp8g9.apps.googleusercontent.com', 12 | scope: 'profile email', 13 | prompt: 'select_account' 14 | } 15 | Vue.use(GoogleAuth, gauthOption) 16 | Vue.config.productionTip = false 17 | 18 | new Vue({ 19 | router, 20 | store, 21 | render: h => h(App) 22 | }).$mount('#app') -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from "register-service-worker"; 4 | 5 | if (process.env.NODE_ENV === "production") { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | "App is being served from cache by a service worker.\n" + 10 | "For more details, visit https://goo.gl/AFskqB" 11 | ); 12 | }, 13 | cached() { 14 | console.log("Content has been cached for offline use."); 15 | }, 16 | updated() { 17 | console.log("New content is available; please refresh."); 18 | }, 19 | offline() { 20 | console.log( 21 | "No internet connection found. App is running in offline mode." 22 | ); 23 | }, 24 | error(error) { 25 | console.error("Error during service worker registration:", error); 26 | } 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/router/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from '@/views/Home' 4 | import Login from '@/views/Login' 5 | import SignUp from '@/views/SignUp' 6 | import PageNotFound from '@/components/PageNotFound' 7 | 8 | Vue.use(Router) 9 | 10 | let baseRoutes = [ 11 | { 12 | path: '/', 13 | redirect: '/login' 14 | }, 15 | { 16 | path: '/home', 17 | name: 'Home', 18 | component: Home 19 | }, 20 | { 21 | path: '/login', 22 | name: 'Login', 23 | component: Login 24 | }, 25 | { 26 | path: '/signup', 27 | name: 'SignUp', 28 | component: SignUp 29 | }, 30 | { 31 | path: '*', 32 | name: 'PageNotFound', 33 | component: PageNotFound 34 | } 35 | ] 36 | 37 | const router = new Router({ 38 | mode: 'history', 39 | linkExactActiveClass: 'active', 40 | base: process.env.BASE_URL, 41 | routes: baseRoutes 42 | }) 43 | 44 | router.beforeEach((to, from, next) => { 45 | // redirect to login page if not logged in and trying to access a restricted page 46 | const publicPages = ['/login', '/signup'] 47 | const authRequired = !publicPages.includes(to.path) 48 | const loggedIn = localStorage.getItem('user') 49 | if (authRequired && !loggedIn) { 50 | return next('/login') 51 | } 52 | next() 53 | }) 54 | 55 | export default router 56 | -------------------------------------------------------------------------------- /src/store/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import { setStore, getStore } from '@/config/utils' 4 | 5 | Vue.use(Vuex) 6 | 7 | const user = getStore('user') 8 | 9 | export default new Vuex.Store({ 10 | state: { 11 | loginUser: user 12 | }, 13 | mutations: { 14 | setLoginUser(state, user) { 15 | state.loginUser = user 16 | setStore('user', user) 17 | } 18 | }, 19 | actions: { 20 | 21 | }, 22 | getters: { 23 | getLoginUserInfo(state) { 24 | return state.loginUser 25 | } 26 | } 27 | }) -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 39 | -------------------------------------------------------------------------------- /src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 36 | -------------------------------------------------------------------------------- /src/views/SignUp.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 47 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | mocha: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/example.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai' 2 | import { shallowMount } from '@vue/test-utils' 3 | import HelloWorld from '@/components/HelloWorld.vue' 4 | 5 | describe('HelloWorld.vue', () => { 6 | it('renders props.msg when passed', () => { 7 | const msg = 'new message' 8 | const wrapper = shallowMount(HelloWorld, { 9 | propsData: { msg } 10 | }) 11 | expect(wrapper.text()).to.include(msg) 12 | }) 13 | }) 14 | --------------------------------------------------------------------------------