├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── babel.config.js ├── functions ├── google-auth.js ├── hello.js ├── net.js └── net │ └── lify.js ├── netlify.toml ├── package.json ├── public ├── favicon.ico ├── googlea4d610c375f0e6bc.html └── index.html ├── src ├── App.vue ├── assets │ ├── github.png │ ├── google.png │ ├── logo.png │ └── outlook.png ├── components │ ├── ApiDocs.vue │ ├── Home.vue │ ├── Pictures.vue │ ├── VideoDetail.vue │ ├── Videos.vue │ ├── Vip.vue │ ├── layout │ │ ├── GithubRibbon.vue │ │ └── NotFound.vue │ ├── meta │ │ ├── Meta.vue │ │ ├── PrivacyPolicy.vue │ │ └── TermsOfUse.vue │ └── oauth │ │ ├── GithubOauth.vue │ │ ├── GoogleOauth.vue │ │ └── Oauth.vue ├── helpers │ ├── fuliapi.js │ └── oauth.js ├── main.js ├── router │ └── index.js └── store │ ├── index.js │ └── modules │ ├── user.js │ └── videos.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # production code 5 | /dist 6 | /lambda 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Local dev 18 | .firebase 19 | 20 | # Editor directories and files 21 | .idea 22 | .vscode 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw* 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | sudo: enabled 5 | branches: 6 | only: 7 | - master 8 | install: 9 | - yarn install 10 | script: 11 | - yarn build 12 | 13 | notifications: 14 | slack: 15 | rooms: 16 | - whizjs:1Yj4kddwBKy8c4CRFLdZFrf3#superjs 17 | on_success: always 18 | on_failure: always -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Yumin Gui erichui329@gmail.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # super.js 2 | 3 | ## Badges 4 | 5 |
6 | 7 | [![StyleCI](https://github.styleci.io/repos/152890558/shield?branch=master)](https://github.styleci.io/repos/152890558) 8 | [![Build Status](https://travis-ci.org/whizjs/superjs.svg?branch=master)](https://travis-ci.org/whizjs/superjs) 9 | [![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](LICENSE) 10 | 11 |
12 | 13 | ## Welcome to super.js 欢迎来到super.js 14 | ``` 15 | 欢迎提交issue和PR! 16 | Issues and Pull Requests are welcome 17 | ``` 18 | 19 | ## Tech Stack 技术栈 20 | 21 | - Vue.js 2 22 | - @vue/cli 3 23 | - vuex 24 | - vue-router 25 | - libraries: 26 | - axios 27 | - Vuetify UI Framework 28 | - validator 29 | - Cloud Services 30 | - StyleCI 31 | - Mailgun 32 | - Travis-CI 33 | - Netlify Deploys 34 | - Netlify Lambda Functions 35 | - APIs consumed: 36 | - Google Oauth 37 | 38 | ## Get Started 快速上手 39 | - 其实这个项目已经用了`netlify`,下面的步骤都自动运行和部署了。 40 | 41 | 42 | - ### Dependency Installation 安装依赖 43 | ``` 44 | yarn install 45 | ``` 46 | 47 | - ### Local development 本地运行 48 | ``` 49 | yarn serve:app 50 | yarn serve:lambda 51 | ``` 52 | 53 | - ### Go production 生产环境构建 54 | ``` 55 | yarn build 56 | ``` 57 | 58 | - ### Lints and fixes files 59 | ``` 60 | yarn lint 61 | ``` 62 | ## Features 项目功能 63 | ### 本项目全面拥抱云计算,能使用第三方云服务的,都尽量使用第三方云服务 64 | 65 | - [x] 注册登录服务使用`Google Oauth`和`Github`第三方服务 66 | - [ ] 找工作信息汇聚 67 | - [ ] 比特币等加密币行情 -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /functions/google-auth.js: -------------------------------------------------------------------------------- 1 | const { 2 | OAuth2Client 3 | } = require('google-auth-library'); 4 | 5 | const googleAppId = '820664319776-kratepkqp0lnq1kgsk6ob34pc7u3meug.apps.googleusercontent.com'; 6 | const googleAppKey = 'aBDbuxOOAFYJN3q5CdXpvj7_'; 7 | const googleCallback = 'https://superjs.org/oauth/google'; 8 | 9 | 10 | exports.handler = function (event, context, callback) { 11 | 12 | const oauth2Client = new OAuth2Client(googleAppId, googleAppKey, googleCallback) 13 | 14 | oauth2Client.getToken(event.queryStringParameters.code).then((res) => { 15 | const profileUrl = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json'; 16 | // res.tokens 需要保存在服务器,不能返回给用户 17 | oauth2Client.setCredentials(res.tokens); 18 | oauth2Client.request({ 19 | url: profileUrl 20 | }).then(res => { 21 | callback(null, { 22 | statusCode: 200, 23 | headers: { 24 | 'Access-Control-Allow-Origin': '*', 25 | 'Content-Type': 'application/json' 26 | }, 27 | body: JSON.stringify(res.data) 28 | }) 29 | }).catch(err => console.log(err)); 30 | }).catch(err => console.log(err)); 31 | 32 | 33 | } -------------------------------------------------------------------------------- /functions/hello.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (event, context, callback) { 2 | callback(null, { 3 | statusCode: 200, 4 | body: "world" 5 | }); 6 | } -------------------------------------------------------------------------------- /functions/net.js: -------------------------------------------------------------------------------- 1 | import lify from './net/lify'; 2 | 3 | exports.handler = function (event, context, callback) { 4 | let subPath = event.path.split('/').pop(); // retrieve last element 5 | switch (subPath) { 6 | case 'lify': 7 | lify(event, context, callback) 8 | break; 9 | default: 10 | callback('404 Path Not Found!'); 11 | } 12 | } -------------------------------------------------------------------------------- /functions/net/lify.js: -------------------------------------------------------------------------------- 1 | let lify = function (event, context, callback) { 2 | callback(null, { 3 | statusCode: 200, 4 | body: "Netlify: You made it!" 5 | }); 6 | } 7 | 8 | export default lify; -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Default build command. 3 | command = "yarn build" 4 | 5 | # Directory with the lambda functions (generated by the lambda `build` command) to deploy to AWS. 6 | functions = "lambda" 7 | 8 | # [[redirects]] 9 | # from = "/api/auth/google/callback" 10 | # to = "/.netlify/functions/auth" 11 | # status = 301 12 | # force = false 13 | # query = { code = "code"} 14 | 15 | [[redirects]] 16 | from = "/api/*" 17 | to = "/.netlify/functions/:splat" 18 | status = 200 19 | force = true 20 | 21 | [[redirects]] 22 | from = "https://superjs.netlify.com/*" 23 | to = "https://superjs.org/:splat" 24 | status = 301 25 | force = true 26 | 27 | [[redirects]] 28 | from = "/*" 29 | to = "/index.html" 30 | status = 200 31 | force = false -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "superjs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve:app": "vue-cli-service serve", 7 | "serve:lambda": "netlify-lambda serve functions", 8 | "build:app": "vue-cli-service build", 9 | "build:lambda": "netlify-lambda build functions", 10 | "build": "yarn build:app; yarn build:lambda", 11 | "lint": "vue-cli-service lint" 12 | }, 13 | "dependencies": { 14 | "axios": "^0.18.0", 15 | "css-loader": "^1.0.0", 16 | "date-fns": "^1.29.0", 17 | "google-auth-library": "^2.0.2", 18 | "jwt-decode": "^2.2.0", 19 | "netlify-identity-widget": "^1.5.2", 20 | "netlify-lambda": "^1.0.3", 21 | "request": "^2.88.0", 22 | "sweetalert2": "^7.28.5", 23 | "validator": "^10.7.1", 24 | "vue": "^2.5.17", 25 | "vue-analytics": "^5.16.0", 26 | "vue-router": "^3.0.1", 27 | "vuetify": "^1.3.12", 28 | "vuex": "^3.0.1" 29 | }, 30 | "devDependencies": { 31 | "@fortawesome/fontawesome-free": "^5.5.0", 32 | "@vue/cli-plugin-babel": "^3.0.0", 33 | "@vue/cli-plugin-eslint": "^3.0.0", 34 | "@vue/cli-service": "^3.0.0", 35 | "stylus": "^0.54.5", 36 | "stylus-loader": "^3.0.2", 37 | "vue-cli-plugin-vuetify": "^0.4.5", 38 | "vue-template-compiler": "^2.5.17", 39 | "vuetify-loader": "^1.0.5" 40 | }, 41 | "eslintConfig": { 42 | "root": true, 43 | "env": { 44 | "node": true 45 | }, 46 | "extends": [ 47 | "plugin:vue/essential", 48 | "eslint:recommended" 49 | ], 50 | "rules": {}, 51 | "parserOptions": { 52 | "parser": "babel-eslint" 53 | } 54 | }, 55 | "postcss": { 56 | "plugins": { 57 | "autoprefixer": {} 58 | } 59 | }, 60 | "browserslist": [ 61 | "> 1%", 62 | "last 2 versions", 63 | "not ie <= 8" 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizjs/superjs/2e1b7141454c2abc6e365690242981e2ff05d2c9/public/favicon.ico -------------------------------------------------------------------------------- /public/googlea4d610c375f0e6bc.html: -------------------------------------------------------------------------------- 1 | google-site-verification: googlea4d610c375f0e6bc.html -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | super.js 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 180 | 181 | 330 | 331 | -------------------------------------------------------------------------------- /src/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizjs/superjs/2e1b7141454c2abc6e365690242981e2ff05d2c9/src/assets/github.png -------------------------------------------------------------------------------- /src/assets/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizjs/superjs/2e1b7141454c2abc6e365690242981e2ff05d2c9/src/assets/google.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizjs/superjs/2e1b7141454c2abc6e365690242981e2ff05d2c9/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/outlook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizjs/superjs/2e1b7141454c2abc6e365690242981e2ff05d2c9/src/assets/outlook.png -------------------------------------------------------------------------------- /src/components/ApiDocs.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/Pictures.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/components/VideoDetail.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 51 | 52 | 58 | 59 | -------------------------------------------------------------------------------- /src/components/Videos.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 39 | 40 | 41 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/Vip.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/layout/GithubRibbon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /src/components/layout/NotFound.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/meta/Meta.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/meta/PrivacyPolicy.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 88 | 89 | -------------------------------------------------------------------------------- /src/components/meta/TermsOfUse.vue: -------------------------------------------------------------------------------- 1 | 117 | 118 | -------------------------------------------------------------------------------- /src/components/oauth/GithubOauth.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /src/components/oauth/GoogleOauth.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 22 | -------------------------------------------------------------------------------- /src/components/oauth/Oauth.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /src/helpers/fuliapi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; -------------------------------------------------------------------------------- /src/helpers/oauth.js: -------------------------------------------------------------------------------- 1 | const googleAppId = '820664319776-kratepkqp0lnq1kgsk6ob34pc7u3meug.apps.googleusercontent.com'; 2 | const googleScope = 'profile%20email'; 3 | const googleCallback = 'https://superjs.org/oauth/google'; 4 | 5 | export function getGoogleOauthRedirectLink() { 6 | return `https://accounts.google.com/o/oauth2/auth?scope=${googleScope}&redirect_uri=${googleCallback}&client_id=${googleAppId}&response_type=code&ts=${Date.now()}`; 7 | } 8 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | import router from './router'; 4 | import store from './store'; 5 | import Vuetify from 'vuetify'; 6 | 7 | import 'vuetify/dist/vuetify.min.css' 8 | import '@fortawesome/fontawesome-free/css/all.css' 9 | 10 | import VueAnalytics from 'vue-analytics'; 11 | 12 | 13 | // '820664319776-kratepkqp0lnq1kgsk6ob34pc7u3meug.apps.googleusercontent.com' 14 | 15 | const isProd = process.env.NODE_ENV === 'production'; 16 | // console.log(isProd); // true in netlify 17 | 18 | Vue.config.productionTip = false 19 | 20 | Vue.use(VueAnalytics, { 21 | id: 'UA-114773079-3', 22 | router, 23 | debug: { 24 | enable: !isProd, 25 | sendHitTask: isProd 26 | } 27 | }) 28 | 29 | Vue.use(Vuetify, { 30 | iconfont: 'fa', 31 | }) 32 | 33 | 34 | 35 | new Vue({ 36 | store, 37 | router, 38 | render: h => h(App) 39 | }).$mount('#app') -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Router from 'vue-router'; 3 | 4 | import Home from '@/components/Home'; 5 | 6 | import Oauth from '@/components/oauth/Oauth'; 7 | import GoogleOauth from '@/components/oauth/GoogleOauth'; 8 | import GithubOauth from '@/components/oauth/GithubOauth'; 9 | 10 | import Videos from '@/components/Videos'; 11 | import Pictures from '@/components/Pictures'; 12 | import Vip from '@/components/Vip'; 13 | 14 | Vue.use(Router); 15 | 16 | const router = new Router({ 17 | mode: 'history', 18 | routes: [ 19 | { 20 | path: '/', 21 | name: 'Home', 22 | component: Home 23 | }, 24 | { 25 | path: '/videos', 26 | name: 'Videos', 27 | component: Videos 28 | }, 29 | { 30 | path: '/pictures', 31 | name: 'Pictures', 32 | component: Pictures 33 | }, 34 | { 35 | path: '/vip', 36 | name: 'Vip', 37 | component: Vip 38 | }, 39 | { 40 | path: '/meta', 41 | component: () => import('@/components/meta/Meta.vue'), 42 | children: [ 43 | { 44 | path: 'privacy-policy', 45 | name: 'MetaPrivacyPolicy', 46 | component: () => import('@/components/meta/PrivacyPolicy.vue') 47 | }, 48 | { 49 | path: 'terms-of-use', 50 | name: 'MetaTermsOfUse', 51 | component: () => import('@/components/meta/TermsOfUse.vue') 52 | } 53 | ] 54 | }, 55 | { 56 | path: "/oauth", 57 | component: Oauth, 58 | children: [ 59 | { 60 | path: 'google', 61 | name: 'GoogleOauth', 62 | component: GoogleOauth 63 | }, 64 | { 65 | path: 'github', 66 | name: 'GithubOauth', 67 | component: GithubOauth 68 | } 69 | ] 70 | }, 71 | { 72 | path: '*', 73 | component: () => import('@/components/layout/NotFound.vue') 74 | } 75 | ] 76 | }); 77 | 78 | export default router; -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import videos from './modules/videos' 4 | import user from './modules/user' 5 | 6 | Vue.use(Vuex); 7 | 8 | export default new Vuex.Store({ 9 | strict: true, 10 | modules: { 11 | videos, 12 | user 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | userprofile: window.localStorage.getItem('userprofile') 3 | }; 4 | 5 | const getters = { 6 | getUserStatus: state => !!state.userprofile, 7 | getUserprofile: state => JSON.parse(state.userprofile) 8 | }; 9 | 10 | //Mutations Must Be Synchronous 11 | const mutations = { 12 | setUserprofile: (state, currentUserprofile) => { 13 | if (!currentUserprofile) { 14 | state.userprofile = null; 15 | window.localStorage.removeItem('userprofile'); 16 | return; 17 | } 18 | let profile = JSON.stringify(currentUserprofile); 19 | state.userprofile = profile; 20 | window.localStorage.setItem('userprofile', profile); 21 | } 22 | }; 23 | 24 | const actions = { 25 | async updateUserprofile({ commit }, payload) { 26 | await commit('setUserprofile', payload.currentUserprofile); 27 | } 28 | }; 29 | 30 | export default { 31 | namespaced: true, 32 | state, 33 | getters, 34 | actions, 35 | mutations 36 | } -------------------------------------------------------------------------------- /src/store/modules/videos.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const API_KEY = "AIzaSyDHP_kDAUYedhrinol6evpd6I_vmgdcAmY"; 4 | 5 | const state = { 6 | videos: [] 7 | }; 8 | 9 | const getters = { 10 | getVideos: state => state.videos 11 | }; 12 | 13 | //Mutations Must Be Synchronous 14 | const mutations = { 15 | setVideos: (state, newVideos) => { 16 | state.videos = newVideos; 17 | } 18 | }; 19 | 20 | const actions = { 21 | async getYouTubeVideos({ commit }, payload) { 22 | 23 | const keyword = payload.keyword; 24 | const ytURL = "https://www.googleapis.com/youtube/v3/search"; 25 | 26 | const newVideos = await axios.get(ytURL, { 27 | params: { 28 | key: API_KEY, 29 | type: "video", 30 | part: "snippet", 31 | q: keyword, 32 | maxResults: 8 //optional 33 | } 34 | }); 35 | 36 | commit('setVideos', newVideos.data.items); 37 | 38 | } 39 | }; 40 | 41 | export default { 42 | namespaced: true, 43 | state, 44 | getters, 45 | actions, 46 | mutations 47 | } --------------------------------------------------------------------------------