From 0001271c9364851dd6dfce598b89b670ea037c61 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Mon, 5 Apr 2021 13:58:18 +0800 Subject: [PATCH 01/73] Add SSR mode files --- src-ssr/extension.js | 21 +++++++++ src-ssr/index.js | 104 ++++++++++++++++++++++++++++++++++++++++++ src-ssr/ssr-flag.d.ts | 9 ++++ 3 files changed, 134 insertions(+) create mode 100644 src-ssr/extension.js create mode 100644 src-ssr/index.js create mode 100644 src-ssr/ssr-flag.d.ts diff --git a/src-ssr/extension.js b/src-ssr/extension.js new file mode 100644 index 00000000..49f7b4f2 --- /dev/null +++ b/src-ssr/extension.js @@ -0,0 +1,21 @@ +/* + * This file runs in a Node context (it's NOT transpiled by Babel), so use only + * the ES6 features that are supported by your Node version. https://node.green/ + * + * WARNING! + * If you import anything from node_modules, then make sure that the package is specified + * in package.json > dependencies and NOT in devDependencies + * + * Note: This file is used for both PRODUCTION & DEVELOPMENT. + * Note: Changes to this file (but not any file it imports!) are picked up by the + * development server, but such updates are costly since the dev-server needs a reboot. + */ + +module.exports.extendApp = function ({ app, ssr }) { + /* + Extend the parts of the express app that you + want to use with development server too. + + Example: app.use(), app.get() etc + */ +} diff --git a/src-ssr/index.js b/src-ssr/index.js new file mode 100644 index 00000000..7bdcbf39 --- /dev/null +++ b/src-ssr/index.js @@ -0,0 +1,104 @@ +/* + * This file runs in a Node context (it's NOT transpiled by Babel), so use only + * the ES6 features that are supported by your Node version. https://node.green/ + * + * WARNING! + * If you import anything from node_modules, then make sure that the package is specified + * in package.json > dependencies and NOT in devDependencies + * + * Note: This file is used only for PRODUCTION. It is not picked up while in dev mode. + * If you are looking to add common DEV & PROD logic to the express app, then use + * "src-ssr/extension.js" + */ + +const express = require('express') +const compression = require('compression') + +const ssr = require('quasar-ssr') +const extension = require('./extension') +const app = express() +const port = process.env.PORT || 3000 + +const serve = (path, cache) => express.static(ssr.resolveWWW(path), { + maxAge: cache ? 1000 * 60 * 60 * 24 * 30 : 0 +}) + +// gzip +app.use(compression({ threshold: 0 })) + +// serve this with no cache, if built with PWA: +if (ssr.settings.pwa) { + app.use(ssr.resolveUrl('/service-worker.js'), serve('service-worker.js')) +} + +// serve "www" folder +app.use(ssr.resolveUrl('/'), serve('.', true)) + +// we extend the custom common dev & prod parts here +extension.extendApp({ app, ssr }) + +// this should be last get(), rendering with SSR +app.get(ssr.resolveUrl('*'), (req, res) => { + res.setHeader('Content-Type', 'text/html') + + // SECURITY HEADERS + // read more about headers here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers + // the following headers help protect your site from common XSS attacks in browsers that respect headers + // you will probably want to use .env variables to drop in appropriate URLs below, + // and potentially look here for inspiration: + // https://ponyfoo.com/articles/content-security-policy-in-express-apps + + // https://developer.mozilla.org/en-us/docs/Web/HTTP/Headers/X-Frame-Options + // res.setHeader('X-frame-options', 'SAMEORIGIN') // one of DENY | SAMEORIGIN | ALLOW-FROM https://example.com + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection + // res.setHeader('X-XSS-Protection', 1) + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options + // res.setHeader('X-Content-Type-Options', 'nosniff') + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin + // res.setHeader('Access-Control-Allow-Origin', '*') // one of '*', '' where origin is one SINGLE origin + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control + // res.setHeader('X-DNS-Prefetch-Control', 'off') // may be slower, but stops some leaks + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + // res.setHeader('Content-Security-Policy', 'default-src https:') + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox + // res.setHeader('Content-Security-Policy', 'sandbox') // this will lockdown your server!!! + // here are a few that you might like to consider adding to your CSP + // object-src, media-src, script-src, frame-src, unsafe-inline + + ssr.renderToString({ req, res }, (err, html) => { + if (err) { + if (err.url) { + if (err.code) res.redirect(err.code, err.url) + else res.redirect(err.url) + } + else if (err.code === 404) { + // Should reach here only if no "catch-all" route + // is defined in /src/routes + res.status(404).send('404 | Page Not Found') + } + else { + // Render Error Page or + // create a route (/src/routes) for an error page and redirect to it + res.status(500).send('500 | Internal Server Error') + if (ssr.settings.debug) { + console.error(`500 on ${req.url}`) + console.error(err) + console.error(err.stack) + } + } + } + else { + res.send(html) + } + }) +}) + +app.listen(port, () => { + console.log(`Server listening at port ${port}`) +}) diff --git a/src-ssr/ssr-flag.d.ts b/src-ssr/ssr-flag.d.ts new file mode 100644 index 00000000..2f70673d --- /dev/null +++ b/src-ssr/ssr-flag.d.ts @@ -0,0 +1,9 @@ +// THIS FEATURE-FLAG FILE IS AUTOGENERATED, +// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING +import "quasar/dist/types/feature-flag"; + +declare module "quasar/dist/types/feature-flag" { + interface QuasarFeatureFlags { + ssr: true; + } +} From 1a0915ef4f022ccb90624f57ec3750f5decee082 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Wed, 7 Apr 2021 13:29:12 +0800 Subject: [PATCH 02/73] Update scripts to SSR mode --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fa28e52a..a74b5e44 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "private": true, "scripts": { "lint": "eslint --ext .js,.vue ./", - "dev": "quasar dev", - "build": "quasar build", + "dev": "quasar dev -m ssr", + "build": "quasar build -m ssr", "icons": "icongenie g -i ./public/assets/notes/basic.png" }, "dependencies": { From 014acef3a463ad147c6b3245f15cef9d7c0e571e Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Wed, 7 Apr 2021 13:30:10 +0800 Subject: [PATCH 03/73] Configure boot file for SSR mode --- quasar.conf.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quasar.conf.js b/quasar.conf.js index 9078c67b..4927cfa9 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -22,7 +22,7 @@ module.exports = function (/* ctx */) { 'i18n', 'axios', 'mixin', - 'analytics' + { path: 'analytics', server: false } ], // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css @@ -117,7 +117,7 @@ module.exports = function (/* ctx */) { // directives: [], // Quasar plugins - plugins: ['Notify', 'SessionStorage'], + plugins: ['Notify', 'SessionStorage', 'Meta'], cssAddon: true }, From 85d23fffec103df41ff3e17c0f28c950ce6076e3 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Wed, 7 Apr 2021 15:02:07 +0800 Subject: [PATCH 04/73] Configure router for SSR mode --- src/router/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/router/index.js b/src/router/index.js index d69229ee..7c64069f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -35,7 +35,9 @@ export default function ({ store }) { }) Router.afterEach((to, from) => { - document.title = to.meta.title + if (process.env.CLIENT) { + document.title = to.meta.title + } }) return Router From 3de3544dda714c83d8e4ad9662252ee072a53350 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Wed, 7 Apr 2021 15:17:39 +0800 Subject: [PATCH 05/73] Add new env HOST_URL --- quasar.conf.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quasar.conf.js b/quasar.conf.js index 4927cfa9..65001b4a 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -50,7 +50,8 @@ module.exports = function (/* ctx */) { env: { BACK_URL: process.env.BACK_URL, DISCORD_CLIENT: process.env.DISCORD_CLIENT, - DISCORD_SECRET: process.env.DISCORD_SECRET + DISCORD_SECRET: process.env.DISCORD_SECRET, + HOST_URL: process.env.HOST_URL }, // transpile: false, From e8a5978c0a7ce8ab181a74fc20f40bdbbac986f0 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Wed, 7 Apr 2021 15:17:57 +0800 Subject: [PATCH 06/73] Remove meta tags in index.template.html --- src/index.template.html | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/index.template.html b/src/index.template.html index 9e1f17b4..9c3cf8e9 100644 --- a/src/index.template.html +++ b/src/index.template.html @@ -1,7 +1,6 @@ - <%= productName %> From 688f01ce1d22447ae8e48570285ad4b8267a8897 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Wed, 7 Apr 2021 15:22:12 +0800 Subject: [PATCH 13/73] Meta tags for My page --- src/pages/MyPage.vue | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/pages/MyPage.vue b/src/pages/MyPage.vue index c917d17b..fa87f9d6 100644 --- a/src/pages/MyPage.vue +++ b/src/pages/MyPage.vue @@ -24,6 +24,61 @@ import PatternCard from '../components/PatternCard' export default { name: 'PageMyPage', + meta () { + return { + title: 'My Page | TECHMANIA', + meta: { + title: { + name: 'title', + content: 'My Page | TECHMANIA' + }, + description: { + name: 'description', + content: 'Your profile' + }, + ogType: { + name: 'og:type', + content: 'website' + }, + ogUrl: { + name: 'og:url', + content: new URL(process.env.HOST_URL + this.$route.fullPath) + }, + ogTitle: { + name: 'og:title', + content: 'My Page | TECHMANIA' + }, + ogDescription: { + name: 'og:description', + content: 'Your profile' + }, + ogImage: { + name: 'og:image', + content: 'https://raw.githubusercontent.com/techmania-team/techmania-team.github.io/master/public/assets/Logo_black.png' + }, + twCard: { + name: 'twitter:card', + content: 'summary_large_image' + }, + twUrl: { + name: 'twitter:url', + content: new URL(process.env.HOST_URL + this.$route.fullPath) + }, + twTitle: { + name: 'twitter:title', + content: 'My Page | TECHMANIA' + }, + twDescription: { + name: 'twitter:description', + content: 'Your profile' + }, + twImage: { + name: 'twitter:image', + content: 'https://raw.githubusercontent.com/techmania-team/techmania-team.github.io/master/public/assets/Logo_black.png' + } + } + } + }, components: { PatternDialog, PatternCard From 8eced00783a6b179b3c163463f3c095233b50c48 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 20:31:53 +0800 Subject: [PATCH 14/73] Fix vuex persisted state in SSR mode --- quasar.conf.js | 13 +++++++++---- src/boot/persist.js | 18 ++++++++++++++++++ src/store/index.js | 17 +---------------- 3 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 src/boot/persist.js diff --git a/quasar.conf.js b/quasar.conf.js index 65001b4a..ff7e734c 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -22,7 +22,8 @@ module.exports = function (/* ctx */) { 'i18n', 'axios', 'mixin', - { path: 'analytics', server: false } + { path: 'analytics', server: false }, + { path: 'persist', server: false } ], // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css @@ -48,10 +49,14 @@ module.exports = function (/* ctx */) { build: { vueRouterMode: 'hash', // available values: 'hash', 'history' env: { - BACK_URL: process.env.BACK_URL, + // I don't know why the fuck dotenv always parse wrong value, this trick should fix it DISCORD_CLIENT: process.env.DISCORD_CLIENT, DISCORD_SECRET: process.env.DISCORD_SECRET, - HOST_URL: process.env.HOST_URL + DISCORD_WEBHOOK: process.env.DISCORD_WEBHOOK, + DISCORD_GUILD: process.env.DISCORD_GUILD, + HOST_URL: process.env.HOST_URL, + DB_URL: process.env.DB_URL, + JWT_SECRET: process.env.JWT_SECRET }, // transpile: false, @@ -118,7 +123,7 @@ module.exports = function (/* ctx */) { // directives: [], // Quasar plugins - plugins: ['Notify', 'SessionStorage', 'Meta'], + plugins: ['Notify', 'LocalStorage', 'Meta'], cssAddon: true }, diff --git a/src/boot/persist.js b/src/boot/persist.js new file mode 100644 index 00000000..35caf3ba --- /dev/null +++ b/src/boot/persist.js @@ -0,0 +1,18 @@ +import createPersistedState from 'vuex-persistedstate' +import { LocalStorage } from 'quasar' + +export default async ({ app, router, store, Vue }) => { + window.setTimeout(() => { + createPersistedState({ + key: 'techmania', + paths: [ + 'user' + ], + storage: { + getItem: key => LocalStorage.getItem(key), + setItem: (key, value) => LocalStorage.set(key, value), + removeItem: key => LocalStorage.remove(key) + } + })(store) + }, 0) +} diff --git a/src/store/index.js b/src/store/index.js index 779293fd..cd64c2ad 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -2,9 +2,6 @@ import Vue from 'vue' import Vuex from 'vuex' import user from './user' -import { SessionStorage } from 'quasar' -import createPersistedState from 'vuex-persistedstate' - Vue.use(Vuex) /* @@ -16,23 +13,11 @@ Vue.use(Vuex) * with the Store instance. */ -export default function (/* { ssrContext } */) { +export default function ({ ssrContext }) { const Store = new Vuex.Store({ modules: { user }, - plugins: [ - createPersistedState({ - paths: [ - 'user' - ], - storage: { - getItem: key => SessionStorage.getItem(key), - setItem: (key, value) => SessionStorage.set(key, value), - removeItem: key => SessionStorage.remove(key) - } - }) - ], // enable strict mode (adds overhead!) // for dev mode only strict: process.env.DEBUGGING From aa5210ca56c16be18eda1b529379be2f54dc241d Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 20:32:26 +0800 Subject: [PATCH 15/73] Add database schema --- src-ssr/models/patterns.js | 69 ++++++++++++++++++++++++++++++++++++++ src-ssr/models/users.js | 34 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src-ssr/models/patterns.js create mode 100644 src-ssr/models/users.js diff --git a/src-ssr/models/patterns.js b/src-ssr/models/patterns.js new file mode 100644 index 00000000..d03fb718 --- /dev/null +++ b/src-ssr/models/patterns.js @@ -0,0 +1,69 @@ +const mongoose = require('mongoose') + +const difficultySchema = new mongoose.Schema({ + name: { + type: String, + required: true + }, + level: { + type: String, + required: true + }, + // 0 = Touch + // 1 = Keyboard + // 2 = KM + countrol: { + type: Number, + required: true, + min: 0, + max: 2 + } +}) + +const previewSchema = new mongoose.Schema({ + name: { + type: String, + required: true + }, + ytid: { + type: String, + required: true + } +}) + +const schema = new mongoose.Schema({ + submitter: { + type: String, + required: true + }, + name: { + type: String, + required: true + }, + composer: { + type: String, + required: true + }, + keysounded: { + type: Boolean, + required: true + }, + difficulties: { + type: [difficultySchema], + required: true + }, + link: { + type: String, + required: true + }, + previews: { + type: [previewSchema], + required: true + }, + description: { + type: String, + required: true + } +}) + +module.exports = mongoose.model('patterns', schema) diff --git a/src-ssr/models/users.js b/src-ssr/models/users.js new file mode 100644 index 00000000..1caf29d7 --- /dev/null +++ b/src-ssr/models/users.js @@ -0,0 +1,34 @@ +const mongoose = require('mongoose') + +const AccessInfoSchema = new mongoose.Schema({ + jwt: { + type: String, + required: true + }, + discord: { + type: String, + required: true + }, + discordRefresh: { + type: String, + required: true + } +}) + +const schema = new mongoose.Schema({ + discord: { + type: String, + required: true, + unique: true + }, + name: { + type: String, + required: true, + unique: true + }, + accessInfo: { + type: [AccessInfoSchema] + } +}) + +module.exports = mongoose.model('users', schema) From 7d1c03adcd8187682ae608864ca8e7373f44307b Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 20:32:59 +0800 Subject: [PATCH 16/73] JWT auth middleware for backend API --- src-ssr/middleware/auth.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src-ssr/middleware/auth.js diff --git a/src-ssr/middleware/auth.js b/src-ssr/middleware/auth.js new file mode 100644 index 00000000..c8628758 --- /dev/null +++ b/src-ssr/middleware/auth.js @@ -0,0 +1,23 @@ +const jwt = require('jsonwebtoken') +const users = require('../models/users.js') + +module.exports = async (req, res, next) => { + try { + const token = req.header('Authorization') ? req.header('Authorization').replace('Bearer ', '') : '' + if (token) { + const decoded = jwt.verify(token, process.env.JWT_SECRET) + const _id = decoded._id + req.user = await users.findOne({ _id, 'accessInfo.jwt': token }) + req.token = token + if (req.user !== null) { + next() + } else { + throw new Error() + } + } else { + throw new Error() + } + } catch (error) { + res.status(401).send({ success: false, message: '未登入' }) + } +} From db47e530851bfd9b7445f82f168207d716c6fdb6 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:08:09 +0800 Subject: [PATCH 17/73] Add new dependencies for backend API --- package.json | 4 + yarn.lock | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 259 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a74b5e44..fb624f1c 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,12 @@ "dependencies": { "@quasar/extras": "^1.0.0", "axios": "^0.18.1", + "body-parser": "^1.19.0", "core-js": "^3.6.5", "dotenv": "^8.2.0", + "form-data": "^4.0.0", + "jsonwebtoken": "^8.5.1", + "mongoose": "^5.12.3", "quasar": "^1.0.0", "vue-i18n": "^8.0.0", "vuex-persistedstate": "3.0.1" diff --git a/yarn.lock b/yarn.lock index cae90b06..3191c16c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1469,6 +1469,13 @@ "@types/connect" "*" "@types/node" "*" +"@types/bson@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/bson/-/bson-4.0.3.tgz#30889d2ffde6262abbe38659364c631454999fbf" + integrity sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw== + dependencies: + "@types/node" "*" + "@types/connect-history-api-fallback@*": version "1.3.3" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.3.tgz#4772b79b8b53185f0f4c9deab09236baf76ee3b4" @@ -1582,6 +1589,14 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/mongodb@^3.5.27": + version "3.6.12" + resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.6.12.tgz#727960d34f35054d2f2ce68909e16094f742d935" + integrity sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog== + dependencies: + "@types/bson" "*" + "@types/node" "*" + "@types/node@*": version "14.14.21" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.21.tgz#d934aacc22424fe9622ebf6857370c052eae464e" @@ -2215,6 +2230,11 @@ async@^3.2.0: resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" @@ -2399,6 +2419,14 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" +bl@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" + integrity sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + bl@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" @@ -2408,6 +2436,11 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +bluebird@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== + bluebird@^3.1.1, bluebird@^3.5.0, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -2428,7 +2461,7 @@ bn.js@^5.0.0, bn.js@^5.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== -body-parser@1.19.0: +body-parser@1.19.0, body-parser@^1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== @@ -2588,6 +2621,11 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4 escalade "^3.1.1" node-releases "^1.1.69" +bson@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a" + integrity sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg== + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -2606,6 +2644,11 @@ buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" @@ -3121,6 +3164,13 @@ colors@~0.6.0-1: resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w= +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^2.19.0, commander@^2.20.0, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -3655,7 +3705,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" -debug@=3.1.0, debug@~3.1.0: +debug@3.1.0, debug@=3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== @@ -3847,6 +3897,11 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + delegate@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" @@ -3857,6 +3912,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +denque@^1.4.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de" + integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ== + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -4073,6 +4133,13 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -5022,6 +5089,15 @@ fork-ts-checker-webpack-plugin@4.1.6: tapable "^1.0.0" worker-rpc "^0.1.0" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -6506,6 +6582,22 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jstransformer@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" @@ -6519,6 +6611,28 @@ junk@^3.1.0: resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +kareem@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.2.tgz#78c4508894985b8d38a0dc15e1a8e11078f2ca93" + integrity sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ== + keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -6738,16 +6852,46 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.template@4.5.0, lodash.template@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -7043,6 +7187,11 @@ memory-fs@^0.4.1: errno "^0.1.3" readable-stream "^2.0.1" +memory-pager@^1.0.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" + integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== + meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -7131,6 +7280,18 @@ mime-db@1.45.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== +mime-db@1.47.0: + version "1.47.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" + integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== + +mime-types@^2.1.12: + version "2.1.30" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" + integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== + dependencies: + mime-db "1.47.0" + mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24: version "2.1.28" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" @@ -7289,6 +7450,42 @@ moment@^2.22.1: resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== +mongodb@3.6.5: + version "3.6.5" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.6.5.tgz#c27d786fd4d3c83dc19302483707d12a9d2aee5f" + integrity sha512-mQlYKw1iGbvJJejcPuyTaytq0xxlYbIoVDm2FODR+OHxyEiMR021vc32bTvamgBjCswsD54XIRwhg3yBaWqJjg== + dependencies: + bl "^2.2.1" + bson "^1.1.4" + denque "^1.4.1" + require_optional "^1.0.1" + safe-buffer "^5.1.2" + optionalDependencies: + saslprep "^1.0.0" + +mongoose-legacy-pluralize@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4" + integrity sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ== + +mongoose@^5.12.3: + version "5.12.3" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.12.3.tgz#e48b4cfd898bd779f6a776fc0e232bf790ac4c1a" + integrity sha512-frsSR9yeldaRpSUeTegXCSB0Tu5UGq8sHuHBuEV31Jk3COyxlKFQPL7UsdMhxPUCmk74FpOYSmNwxhWBEqgzQg== + dependencies: + "@types/mongodb" "^3.5.27" + bson "^1.1.4" + kareem "2.3.2" + mongodb "3.6.5" + mongoose-legacy-pluralize "1.0.2" + mpath "0.8.3" + mquery "3.2.5" + ms "2.1.2" + regexp-clone "1.0.0" + safe-buffer "5.2.1" + sift "7.0.1" + sliced "1.0.1" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -7301,6 +7498,22 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +mpath@0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.8.3.tgz#828ac0d187f7f42674839d74921970979abbdd8f" + integrity sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA== + +mquery@3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/mquery/-/mquery-3.2.5.tgz#8f2305632e4bb197f68f60c0cffa21aaf4060c51" + integrity sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A== + dependencies: + bluebird "3.5.1" + debug "3.1.0" + regexp-clone "^1.0.0" + safe-buffer "5.1.2" + sliced "1.0.1" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -9098,6 +9311,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp-clone@1.0.0, regexp-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63" + integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw== + regexp.prototype.flags@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" @@ -9212,6 +9430,14 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +require_optional@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" + integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g== + dependencies: + resolve-from "^2.0.0" + semver "^5.1.0" + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -9229,6 +9455,11 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" +resolve-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -9373,7 +9604,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -9397,6 +9628,13 @@ sanitize-filename@^1.6.2: dependencies: truncate-utf8-bytes "^1.0.0" +saslprep@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" + integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag== + dependencies: + sparse-bitfield "^3.0.3" + sass-loader@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.0.tgz#1727fcc0c32ab3eb197cda61d78adf4e9174a4b3" @@ -9500,7 +9738,7 @@ semver-truncate@^1.1.2: dependencies: semver "^5.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9684,6 +9922,11 @@ shvl@^2.0.0: resolved "https://registry.yarnpkg.com/shvl/-/shvl-2.0.2.tgz#eca7decb9bbd4e8dd93f06ea9ab94036319cd351" integrity sha512-G3KkIXPza3dgkt6Bo8zIl5K/KvAAhbG6o9KfAjhPvrIIzzAhnfc2ztv1i+iPTbNNM43MaBUqIaZwqVjkSgY/rw== +sift@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/sift/-/sift-7.0.1.tgz#47d62c50b159d316f1372f8b53f9c10cd21a4b08" + integrity sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g== + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -9742,7 +9985,7 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -sliced@^1.0.1: +sliced@1.0.1, sliced@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E= @@ -9868,6 +10111,13 @@ source-map@^0.7.3, source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +sparse-bitfield@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" + integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE= + dependencies: + memory-pager "^1.0.2" + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" From 5ce6f27d979664b8ae3edafccc32f913dc3ae92f Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:08:31 +0800 Subject: [PATCH 18/73] Update env sample --- .env.sample | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index ae60b9e1..c06fb7a7 100644 --- a/.env.sample +++ b/.env.sample @@ -1,3 +1,14 @@ -DISCORD_CLIENT=`` -DISCORD_SECRET=`` -BACK_URL=`` \ No newline at end of file +# Discord client, place your value before 'abc', idk why the fuck dotenv keep parsing wrong value +DISCORD_CLIENT="12345678abc" +# Discord secret +DISCORD_SECRET="abcdefg" +# Discord webhook url +DISCORD_WEBHOOK="https://discord.com/api/webhooks/..." +# Discord guild +DISCORD_GUILD= "799491033917816842" +# Where does the site host on +HOST_URL="http://localhost:8080" +# MongoDB connect string +DB_URL="mongodb://127.0.0.1:27017/techmania" +# Your secret for JWT +JWT_SECRET="abcdefg" \ No newline at end of file From 8bccf0e0158b3b682b0d0da0c334a74ca97632ef Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:10:54 +0800 Subject: [PATCH 19/73] Add express API base --- src-ssr/extension.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src-ssr/extension.js b/src-ssr/extension.js index 49f7b4f2..ee47fb94 100644 --- a/src-ssr/extension.js +++ b/src-ssr/extension.js @@ -10,12 +10,13 @@ * Note: Changes to this file (but not any file it imports!) are picked up by the * development server, but such updates are costly since the dev-server needs a reboot. */ +const mongoose = require('mongoose') +const bodyParser = require('body-parser') module.exports.extendApp = function ({ app, ssr }) { - /* - Extend the parts of the express app that you - want to use with development server too. + mongoose.connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }) - Example: app.use(), app.get() etc - */ + app.set('trust proxy', 1) + + app.use(bodyParser.json()) } From cb782399e694ab807413e04e188cb7c919b50400 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:11:30 +0800 Subject: [PATCH 20/73] Add users API router --- src-ssr/extension.js | 4 ++++ src-ssr/routes/users.js | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 src-ssr/routes/users.js diff --git a/src-ssr/extension.js b/src-ssr/extension.js index ee47fb94..2f866685 100644 --- a/src-ssr/extension.js +++ b/src-ssr/extension.js @@ -13,10 +13,14 @@ const mongoose = require('mongoose') const bodyParser = require('body-parser') +const routerUsers = require('./routes/users.js') + module.exports.extendApp = function ({ app, ssr }) { mongoose.connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }) app.set('trust proxy', 1) app.use(bodyParser.json()) + + app.use('/api/users', routerUsers) } diff --git a/src-ssr/routes/users.js b/src-ssr/routes/users.js new file mode 100644 index 00000000..5ea04fcf --- /dev/null +++ b/src-ssr/routes/users.js @@ -0,0 +1,4 @@ +const express = require('express') +const router = express.Router() + +module.exports = router From bdad46c841cfd6306068eda9291222411f11b592 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:12:26 +0800 Subject: [PATCH 21/73] API backend: login --- src-ssr/controllers/users.js | 61 ++++++++++++++++++++++++++++++++++++ src-ssr/routes/users.js | 3 ++ 2 files changed, 64 insertions(+) create mode 100644 src-ssr/controllers/users.js diff --git a/src-ssr/controllers/users.js b/src-ssr/controllers/users.js new file mode 100644 index 00000000..5f0e4ab4 --- /dev/null +++ b/src-ssr/controllers/users.js @@ -0,0 +1,61 @@ +const jwt = require('jsonwebtoken') +const axios = require('axios') +const FormData = require('form-data') +const users = require('../models/users.js') + +module.exports = { + async login (req, res) { + const code = req.query.code + if (code) { + try { + // use code to exchange token + const fd = new FormData() + fd.append('grant_type', 'authorization_code') + fd.append('client_id', process.env.DISCORD_CLIENT.replace(/abc/g, '')) + fd.append('client_secret', process.env.DISCORD_SECRET) + fd.append('code', code) + fd.append('redirect_uri', new URL('/api/users/login', process.env.HOST_URL).toString()) + fd.append('scope', 'identify guilds') + let response = await axios.post('https://discord.com/api/oauth2/token', fd, { + headers: fd.getHeaders() + }) + const accessToken = response.data.access_token + const refreshToken = response.data.refresh_token + + // get user data and insert into database + response = await axios.get('https://discord.com/api/users/@me', { + headers: { Authorization: `Bearer ${accessToken}` } + }) + + let user = await users.findOne({ + discord: response.data.id + }) + + if (user === null) { + user = await users.create({ + discord: response.data.id, + name: response.data.username, + accessInfo: [] + }) + } + + const token = jwt.sign({ _id: user._id.toString() }, process.env.JWT_SECRET, { expiresIn: '5 days' }) + user.name = response.data.username + user.accessInfo.push({ + jwt: token, + discord: accessToken, + discordRefresh: refreshToken + }) + await user.save() + res.redirect(new URL(`?token=${accessToken}&jwt=${token}`, process.env.HOST_URL).toString()) + } catch (error) { + if (error.response) { + console.log(error.response.data) + } else console.log(error) + res.status(500).send({ success: false, message: 'Server Error' }) + } + } else { + res.status(400).send({ success: false, message: 'Bad Request' }) + } + } +} diff --git a/src-ssr/routes/users.js b/src-ssr/routes/users.js index 5ea04fcf..c118884e 100644 --- a/src-ssr/routes/users.js +++ b/src-ssr/routes/users.js @@ -1,4 +1,7 @@ const express = require('express') +const { login } = require('../controllers/users.js') const router = express.Router() +router.get('/login', login) + module.exports = router From f3c514ef013c760cc562e24dfbfa7853440266b6 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:14:43 +0800 Subject: [PATCH 22/73] API frontend: login --- src/App.vue | 21 +++++++++------------ src/boot/mixin.js | 2 +- src/store/user/mutations.js | 10 +++++++++- src/store/user/state.js | 3 ++- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/App.vue b/src/App.vue index 136178ff..aa4437da 100644 --- a/src/App.vue +++ b/src/App.vue @@ -10,17 +10,6 @@ export default { return this.$route.query } }, - watch: { - query: { - handler (value) { - if (value.token) { - this.getUserData(value.token) - this.$router.replace({ query: {} }) - } - }, - deep: true - } - }, methods: { async getUserData (token) { try { @@ -31,11 +20,19 @@ export default { } } ) - this.$store.commit('user/login', { ...result.data, token }) + this.$store.commit('user/login', { ...result.data }) } catch (error) { console.log(error) } } + }, + async mounted () { + if (this.query.jwt && this.query.token) { + this.getUserData(this.query.token) + this.$store.commit('user/addjwt', this.query.jwt) + this.$store.commit('user/addtoken', this.query.token) + this.$router.replace({ query: {} }) + } } } diff --git a/src/boot/mixin.js b/src/boot/mixin.js index 7240e33b..43fd5553 100644 --- a/src/boot/mixin.js +++ b/src/boot/mixin.js @@ -9,7 +9,7 @@ export default async ({ Vue }) => { data () { return { discordURL: { - login: `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT}&redirect_uri=${encodeURIComponent(process.env.BACK_URL)}&response_type=code&scope=identify%20guilds`, + login: `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT.replace(/abc/g, '')}&redirect_uri=${encodeURIComponent(process.env.HOST_URL + '/api/users/login')}&response_type=code&scope=identify%20guilds`, token: 'https://discord.com/api/oauth2/token', identity: 'https://discord.com/api/users/@me', guilds: 'https://discord.com/api/users/@me/guilds' diff --git a/src/store/user/mutations.js b/src/store/user/mutations.js index 21711541..2fed6d37 100644 --- a/src/store/user/mutations.js +++ b/src/store/user/mutations.js @@ -1,13 +1,21 @@ export function login (state, data) { - state.token = data.token state.id = data.id state.username = data.username state.avatar = data.avatar } +export function addjwt (state, data) { + state.jwt = data +} + +export function addtoken (state, data) { + state.token = data +} + export function logout (state) { state.token = '' state.id = '' state.username = '' state.avatar = '' + state.jwt = '' } diff --git a/src/store/user/state.js b/src/store/user/state.js index 827bd368..3027575a 100644 --- a/src/store/user/state.js +++ b/src/store/user/state.js @@ -3,6 +3,7 @@ export default function () { token: '', id: '', username: '', - avatar: '' + avatar: '', + jwt: '' } } From 2682a218cf3afd505b96c7812c5645fec1918187 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:14:55 +0800 Subject: [PATCH 23/73] Fix vuex warning --- src/store/user/getters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/user/getters.js b/src/store/user/getters.js index 3ae3a93d..ffff8a77 100644 --- a/src/store/user/getters.js +++ b/src/store/user/getters.js @@ -1,4 +1,4 @@ export function getUserData (state) { - state.avatar_url = `https://cdn.discordapp.com/avatars/${state.id}/${state.avatar}.png` - return state + const data = { ...state, avatar_url: `https://cdn.discordapp.com/avatars/${state.id}/${state.avatar}.png` } + return data } From bff4c199fee81a39bf1e1e9bf2792ab5a9dba36c Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:15:54 +0800 Subject: [PATCH 24/73] API backend: refresh client tokens --- src-ssr/controllers/users.js | 29 +++++++++++++++++++++++++++++ src-ssr/routes/users.js | 4 +++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src-ssr/controllers/users.js b/src-ssr/controllers/users.js index 5f0e4ab4..7e57db83 100644 --- a/src-ssr/controllers/users.js +++ b/src-ssr/controllers/users.js @@ -57,5 +57,34 @@ module.exports = { } else { res.status(400).send({ success: false, message: 'Bad Request' }) } + }, + async extend (req, res) { + try { + // refresh discord token + const infoidx = req.user.accessInfo.findIndex(info => info.jwt === req.token) + const fd = new FormData() + fd.append('grant_type', 'refresh_token') + fd.append('client_id', process.env.DISCORD_CLIENT.replace(/abc/g, '')) + fd.append('client_secret', process.env.DISCORD_SECRET) + fd.append('refresh_token', req.user.accessInfo[infoidx].discordRefresh) + fd.append('redirect_uri', new URL('/api/users/login', process.env.HOST_URL).toString()) + fd.append('scope', 'identify guilds') + const response = await axios.post('https://discord.com/api/oauth2/token', fd, { + headers: fd.getHeaders() + }) + const accessToken = response.data.access_token + const refreshToken = response.data.refresh_token + + const token = jwt.sign({ _id: req.user._id.toString() }, process.env.JWT_SECRET, { expiresIn: '5 days' }) + req.user.accessInfo[infoidx].discord = accessToken + req.user.accessInfo[infoidx].discordRefresh = refreshToken + req.user.accessInfo[infoidx].jwt = token + await req.user.save() + + res.status(200).send({ success: true, message: '', jwt: token, token: accessToken }) + } catch (error) { + console.log(error) + res.status(500).send({ success: true, message: 'Server Error' }) + } } } diff --git a/src-ssr/routes/users.js b/src-ssr/routes/users.js index c118884e..71c00cc1 100644 --- a/src-ssr/routes/users.js +++ b/src-ssr/routes/users.js @@ -1,7 +1,9 @@ const express = require('express') -const { login } = require('../controllers/users.js') +const { login, extend } = require('../controllers/users.js') +const auth = require('../middleware/auth') const router = express.Router() router.get('/login', login) +router.post('/extend', auth, extend) module.exports = router From 6d16ac40c85fa8f82630223d59d39d6a8b5d1f0e Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:16:24 +0800 Subject: [PATCH 25/73] API frontend: refresh client tokens --- src/App.vue | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/App.vue b/src/App.vue index aa4437da..78553054 100644 --- a/src/App.vue +++ b/src/App.vue @@ -27,7 +27,18 @@ export default { } }, async mounted () { - if (this.query.jwt && this.query.token) { + if (this.user.jwt.length > 0) { + try { + const response = await this.$axios.post(new URL('/api/users/extend', process.env.HOST_URL), {}, { + headers: { Authorization: `Bearer ${this.user.jwt}` } + }) + this.getUserData(response.data.token) + this.$store.commit('user/addjwt', response.data.jwt) + this.$store.commit('user/addtoken', response.data.token) + } catch (error) { + this.$store.commit('user/logout') + } + } else if (this.query.jwt && this.query.token) { this.getUserData(this.query.token) this.$store.commit('user/addjwt', this.query.jwt) this.$store.commit('user/addtoken', this.query.token) From 292b8ec2b364e473debcb017a93cbf483ab93930 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:16:45 +0800 Subject: [PATCH 26/73] API backend: logout --- src-ssr/controllers/users.js | 16 ++++++++++++++++ src-ssr/routes/users.js | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src-ssr/controllers/users.js b/src-ssr/controllers/users.js index 7e57db83..058a3371 100644 --- a/src-ssr/controllers/users.js +++ b/src-ssr/controllers/users.js @@ -86,5 +86,21 @@ module.exports = { console.log(error) res.status(500).send({ success: true, message: 'Server Error' }) } + }, + async logout (req, res) { + try { + const infoidx = req.user.accessInfo.findIndex(info => info.jwt === req.token) + const fd = new FormData() + fd.append('token', req.user.accessInfo[infoidx].discord) + fd.append('client_id', process.env.DISCORD_CLIENT.replace(/abc/g, '')) + fd.append('client_secret', process.env.DISCORD_SECRET) + await axios.post('https://discord.com/api/oauth2/token/revoke', fd, { + headers: fd.getHeaders() + }) + + req.user.accessInfo.splice(infoidx, 1) + } catch (_) { + } + res.status(200).send({ success: true, message: '' }) } } diff --git a/src-ssr/routes/users.js b/src-ssr/routes/users.js index 71c00cc1..ad8e0527 100644 --- a/src-ssr/routes/users.js +++ b/src-ssr/routes/users.js @@ -1,9 +1,10 @@ const express = require('express') -const { login, extend } = require('../controllers/users.js') +const { login, extend, logout } = require('../controllers/users.js') const auth = require('../middleware/auth') const router = express.Router() router.get('/login', login) router.post('/extend', auth, extend) +router.delete('/logout', auth, logout) module.exports = router From a69d49e523cae996bacbf8e3c8e4126a2af90e92 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:17:13 +0800 Subject: [PATCH 27/73] API frontend: logout --- src/layouts/MainLayout.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index d8f4bfe2..7d9b3673 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -63,9 +63,13 @@ export default { } }, methods: { - logout () { + async logout () { + try { + await this.$axios.delete(new URL('/api/users/logout', process.env.HOST_URL), { + headers: { Authorization: `Bearer ${this.user.jwt}` } + }) + } catch (_) {} this.$store.commit('user/logout') - this.openLink(process.env.BACK_URL + '?action=logout', '_self') } } } From e5a917798bf77470789ca67a867374a181441e60 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:17:42 +0800 Subject: [PATCH 28/73] Fix meta tags url --- src/pages/Changelog.vue | 4 ++-- src/pages/Error404.vue | 4 ++-- src/pages/Index.vue | 4 ++-- src/pages/MyPage.vue | 4 ++-- src/pages/Pattern.vue | 4 ++-- src/pages/Patterns.vue | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/Changelog.vue b/src/pages/Changelog.vue index 5c61b606..04a8cfba 100644 --- a/src/pages/Changelog.vue +++ b/src/pages/Changelog.vue @@ -56,7 +56,7 @@ export default { }, ogUrl: { name: 'og:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, ogTitle: { name: 'og:title', @@ -76,7 +76,7 @@ export default { }, twUrl: { name: 'twitter:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, twTitle: { name: 'twitter:title', diff --git a/src/pages/Error404.vue b/src/pages/Error404.vue index 882182c3..4f706e2c 100644 --- a/src/pages/Error404.vue +++ b/src/pages/Error404.vue @@ -27,7 +27,7 @@ export default { }, ogUrl: { name: 'og:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, ogTitle: { name: 'og:title', @@ -47,7 +47,7 @@ export default { }, twUrl: { name: 'twitter:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, twTitle: { name: 'twitter:title', diff --git a/src/pages/Index.vue b/src/pages/Index.vue index 1b20558e..f1d60a15 100644 --- a/src/pages/Index.vue +++ b/src/pages/Index.vue @@ -44,7 +44,7 @@ export default { }, ogUrl: { name: 'og:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, ogTitle: { name: 'og:title', @@ -64,7 +64,7 @@ export default { }, twUrl: { name: 'twitter:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, twTitle: { name: 'twitter:title', diff --git a/src/pages/MyPage.vue b/src/pages/MyPage.vue index fa87f9d6..65675e43 100644 --- a/src/pages/MyPage.vue +++ b/src/pages/MyPage.vue @@ -42,7 +42,7 @@ export default { }, ogUrl: { name: 'og:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, ogTitle: { name: 'og:title', @@ -62,7 +62,7 @@ export default { }, twUrl: { name: 'twitter:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, twTitle: { name: 'twitter:title', diff --git a/src/pages/Pattern.vue b/src/pages/Pattern.vue index 318ad6a0..aff0880e 100644 --- a/src/pages/Pattern.vue +++ b/src/pages/Pattern.vue @@ -56,7 +56,7 @@ export default { }, ogUrl: { name: 'og:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, ogTitle: { name: 'og:title', @@ -76,7 +76,7 @@ export default { }, twUrl: { name: 'twitter:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, twTitle: { name: 'twitter:title', diff --git a/src/pages/Patterns.vue b/src/pages/Patterns.vue index a7a18f92..c4969f17 100644 --- a/src/pages/Patterns.vue +++ b/src/pages/Patterns.vue @@ -48,7 +48,7 @@ export default { }, ogUrl: { name: 'og:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, ogTitle: { name: 'og:title', @@ -68,7 +68,7 @@ export default { }, twUrl: { name: 'twitter:url', - content: new URL(process.env.HOST_URL + this.$route.fullPath) + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() }, twTitle: { name: 'twitter:title', From fcca47bbfc72e7f121f76c59e15a76f500ab17ac Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Thu, 8 Apr 2021 21:17:53 +0800 Subject: [PATCH 29/73] Add heroku-postbuild script in package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index fb624f1c..e6a4d8f1 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint --ext .js,.vue ./", "dev": "quasar dev -m ssr", "build": "quasar build -m ssr", + "heroku-postbuild": "yarn && yarn build", "icons": "icongenie g -i ./public/assets/notes/basic.png" }, "dependencies": { From 21125daba9653c05ec6a2cb935cfbe8131d93410 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 12:10:09 +0800 Subject: [PATCH 30/73] Fix logout not delete accessinfo in database --- src-ssr/controllers/users.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src-ssr/controllers/users.js b/src-ssr/controllers/users.js index 058a3371..a3be8d4a 100644 --- a/src-ssr/controllers/users.js +++ b/src-ssr/controllers/users.js @@ -99,6 +99,7 @@ module.exports = { }) req.user.accessInfo.splice(infoidx, 1) + req.user.save() } catch (_) { } res.status(200).send({ success: true, message: '' }) From 4d43225f506d3cdb959993725d373c3ff6084fab Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 12:13:59 +0800 Subject: [PATCH 31/73] Add patterns API router --- src-ssr/extension.js | 2 ++ src-ssr/routes/patterns.js | 4 ++++ 2 files changed, 6 insertions(+) create mode 100644 src-ssr/routes/patterns.js diff --git a/src-ssr/extension.js b/src-ssr/extension.js index 2f866685..38c3b1be 100644 --- a/src-ssr/extension.js +++ b/src-ssr/extension.js @@ -14,6 +14,7 @@ const mongoose = require('mongoose') const bodyParser = require('body-parser') const routerUsers = require('./routes/users.js') +const routerPatterns = require('./routes/patterns.js') module.exports.extendApp = function ({ app, ssr }) { mongoose.connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }) @@ -23,4 +24,5 @@ module.exports.extendApp = function ({ app, ssr }) { app.use(bodyParser.json()) app.use('/api/users', routerUsers) + app.use('/api/patterns', routerPatterns) } diff --git a/src-ssr/routes/patterns.js b/src-ssr/routes/patterns.js new file mode 100644 index 00000000..5ea04fcf --- /dev/null +++ b/src-ssr/routes/patterns.js @@ -0,0 +1,4 @@ +const express = require('express') +const router = express.Router() + +module.exports = router From 4090c71b3650c79773ff3fef4530c8332b20a1a6 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 13:01:16 +0800 Subject: [PATCH 32/73] Fix typo in patterns schema --- src-ssr/models/patterns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ssr/models/patterns.js b/src-ssr/models/patterns.js index d03fb718..64988539 100644 --- a/src-ssr/models/patterns.js +++ b/src-ssr/models/patterns.js @@ -12,7 +12,7 @@ const difficultySchema = new mongoose.Schema({ // 0 = Touch // 1 = Keyboard // 2 = KM - countrol: { + control: { type: Number, required: true, min: 0, From 4e67e5d1e472ef1bee74a855f9c283146da74496 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 13:15:33 +0800 Subject: [PATCH 33/73] Type of pattern difficulty level should be Number --- src-ssr/models/patterns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ssr/models/patterns.js b/src-ssr/models/patterns.js index 64988539..945bda21 100644 --- a/src-ssr/models/patterns.js +++ b/src-ssr/models/patterns.js @@ -6,7 +6,7 @@ const difficultySchema = new mongoose.Schema({ required: true }, level: { - type: String, + type: Number, required: true }, // 0 = Touch From 24914c8220fe25f4f3fab58b09bf94269126f5b4 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 13:21:32 +0800 Subject: [PATCH 34/73] API backend: submit new pattern --- src-ssr/controllers/patterns.js | 62 +++++++++++++++++++++++++++++++++ src-ssr/routes/patterns.js | 4 +++ 2 files changed, 66 insertions(+) create mode 100644 src-ssr/controllers/patterns.js diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js new file mode 100644 index 00000000..07b0794c --- /dev/null +++ b/src-ssr/controllers/patterns.js @@ -0,0 +1,62 @@ +const axios = require('axios') +const patterns = require('../models/patterns.js') + +module.exports = { + async create (req, res) { + try { + const infoidx = req.user.accessInfo.findIndex(info => info.jwt === req.token) + // check in discord guild or not + const response = await axios.get('https://discord.com/api/users/@me/guilds', { + headers: { Authorization: `Bearer ${req.user.accessInfo[infoidx].discord}` } + }) + const inGuild = response.data.find(guild => guild.id.toString() === process.env.DISCORD_GUILD.toString()) + if (!inGuild) { + res.status(403).send({ success: false, message: 'Not in guild' }) + return + } else { + const result = await patterns.create({ + submitter: req.user.discord, + name: req.body.name, + composer: req.body.composer, + keysounded: req.body.keysounded === 1, + difficulties: req.body.difficulties, + link: req.body.link, + previews: req.body.previews, + description: req.body.description + }) + let strPreveiw = '' + for (const preview of req.body.previews) { + strPreveiw += `https://www.youtube.com/watch?v=${preview.ytid}\n` + } + const controls = ['Touch', 'Key', 'KM'] + let strDifficulty = '' + for (const difficulty of req.body.difficulties) { + strDifficulty += `${controls[difficulty.control]} - ${difficulty.name}: lv.${difficulty.level}\n` + } + await axios.post(process.env.DISCORD_WEBHOOK, { + username: 'TECHMANIA', + avatar_url: 'https://avatars.githubusercontent.com/u/77661148?s=200&v=4', + content: `New pattern submitted by <@${req.user.discord}>`, + embeds: [{ + url: new URL(`/pattern/${result._id}`, process.env.HOST_URL).toString(), + image: { url: `http://i3.ytimg.com/vi/${req.body.previews[0].ytid}/hqdefault.jpg` }, + title: req.body.name, + color: '15158332', + fields: [ + { name: 'Composer', value: req.body.composer, inline: true }, + { name: 'Keysounded', value: req.body.keysounded === 1 ? 'Yes' : 'No', inline: true }, + { name: 'Previews', value: strPreveiw, inline: false }, + { name: 'Difficulties', value: strDifficulty, inline: false }, + { name: 'Download', value: req.body.link, inline: false }, + { name: 'Description', value: req.body.description, inline: false } + ] + }] + }) + res.status(200).send({ success: true, message: '' }) + } + } catch (error) { + console.log(error) + res.status(500).send({ success: true, message: 'Server Error' }) + } + } +} diff --git a/src-ssr/routes/patterns.js b/src-ssr/routes/patterns.js index 5ea04fcf..6a9723d3 100644 --- a/src-ssr/routes/patterns.js +++ b/src-ssr/routes/patterns.js @@ -1,4 +1,8 @@ const express = require('express') +const { create } = require('../controllers/patterns.js') +const auth = require('../middleware/auth') const router = express.Router() +router.post('/', auth, create) + module.exports = router From 9709ce6fc8a497caf888c08042e688ddf3358dc0 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 13:22:10 +0800 Subject: [PATCH 35/73] Remove unused variable in vue mixin --- src/boot/mixin.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/boot/mixin.js b/src/boot/mixin.js index 43fd5553..9b7ec95d 100644 --- a/src/boot/mixin.js +++ b/src/boot/mixin.js @@ -13,9 +13,6 @@ export default async ({ Vue }) => { token: 'https://discord.com/api/oauth2/token', identity: 'https://discord.com/api/users/@me', guilds: 'https://discord.com/api/users/@me/guilds' - }, - env: { - BACK_URL: process.env.BACK_URL } } }, From ba26683b8ed0289f3c6d14c281bb470a400aed7d Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 13:22:40 +0800 Subject: [PATCH 36/73] API frontend: submit new pattern --- src/components/PatternDialog.vue | 44 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/components/PatternDialog.vue b/src/components/PatternDialog.vue index 88fbcdf1..0c8c33a1 100644 --- a/src/components/PatternDialog.vue +++ b/src/components/PatternDialog.vue @@ -32,7 +32,7 @@ p.q-mb-none Difficulties .q-mb-md .row.items-start(v-for="(difficulty, index) in model.difficulties" :key="'B'+index") - q-select.col-3(v-model="difficulty.control" :options="controlTypes" placeholder="Control" :rules="[val => !!val || 'Field is required']") + q-select.col-3(v-model="difficulty.control" :options="controlTypes" placeholder="Control" emit-value map-options) .col-1 q-input.col-3(v-model="difficulty.name" placeholder="Name" :rules="[val => !!val || 'Field is required']") .col-1 @@ -69,12 +69,16 @@ export default { name: '', composer: '', keysounded: false, - difficulties: [{ name: '', level: 0, control: 'Touch' }], + difficulties: [{ name: '', level: 0, control: 0 }], link: '', previews: [{ link: '', name: '' }], description: '' }, - controlTypes: ['Touch', 'Keys', 'KM'], + controlTypes: [ + { label: 'Touch', value: 0 }, + { label: 'Keys', value: 1 }, + { label: 'KM', value: 2 } + ], isedit: false, confirm: false } @@ -101,7 +105,7 @@ export default { name: '', composer: '', keysounded: false, - difficulties: [{ name: '', level: 0, control: 'Touch' }], + difficulties: [{ name: '', level: 0, control: 0 }], link: '', previews: [{ link: '', name: '' }], description: '' @@ -128,10 +132,12 @@ export default { try { const post = JSON.parse(JSON.stringify(this.model)) post.previews.map(preview => { - preview.link = this.GetIDFromYouTubeLink(preview.link) + preview.ytid = this.GetIDFromYouTubeLink(preview.link) return preview }) - const result = await this.$axios.post(process.env.BACK_URL + '?action=submit', post, { withCredentials: true }) + const result = await this.$axios.post(new URL('/api/patterns', process.env.HOST_URL), post, { + headers: { Authorization: `Bearer ${this.user.jwt}` } + }) if (result.data.success) { this.$q.notify({ icon: 'check', @@ -144,27 +150,25 @@ export default { name: '', composer: '', keysounded: false, - difficulties: [{ name: '', level: 0, control: 'Touch' }], + difficulties: [{ name: '', level: 0, control: 0 }], link: '', previews: [{ link: '', name: '' }], description: '' } this.$emit('refreshPattern') this.openModal = false - } else { - let message = '' - if (result.data.message === 'Not in guild') { - message = 'You must join our discord to submit new pattern.' - } - if (result.data.message === 'Unauthorized') { - message = 'Unauthorized.' - } - throw new Error(message) } } catch (error) { + let message = '' + if (error.response.data.message === 'Not in guild') { + message = 'You must join our discord to submit new pattern.' + } + if (error.response.data.message === 'Unauthorized') { + message = 'Unauthorized.' + } this.$q.notify({ icon: 'warning', - message: error.message, + message, color: 'negative', position: 'top', timeout: 2000 @@ -179,7 +183,7 @@ export default { this.model.previews.splice(index, 1) }, addDifficulty () { - this.model.difficulties.push({ name: '', level: 0, control: 'Touch' }) + this.model.difficulties.push({ name: '', level: 0, control: 0 }) }, removeDifficulty (index) { this.model.difficulties.splice(index, 1) @@ -189,7 +193,7 @@ export default { name: '', composer: '', keysounded: false, - difficulties: [{ name: '', level: 0, control: 'Touch' }], + difficulties: [{ name: '', level: 0, control: 0 }], link: '', previews: [{ link: '', name: '' }], description: '' @@ -214,7 +218,7 @@ export default { name: '', composer: '', keysounded: false, - difficulties: [{ name: '', level: 0, control: 'Touch' }], + difficulties: [{ name: '', level: 0, control: 0 }], link: '', previews: [{ link: '', name: '' }], description: '' From cac045cd6d7c08357028cc50963ee9ec412b37e0 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 13:24:47 +0800 Subject: [PATCH 37/73] Fix new pattern discord embed message URL --- src-ssr/controllers/patterns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index 07b0794c..dd8bb21d 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -38,7 +38,7 @@ module.exports = { avatar_url: 'https://avatars.githubusercontent.com/u/77661148?s=200&v=4', content: `New pattern submitted by <@${req.user.discord}>`, embeds: [{ - url: new URL(`/pattern/${result._id}`, process.env.HOST_URL).toString(), + url: new URL(`/patterns/${result._id}`, process.env.HOST_URL).toString(), image: { url: `http://i3.ytimg.com/vi/${req.body.previews[0].ytid}/hqdefault.jpg` }, title: req.body.name, color: '15158332', From beba3fe22afe2eb8b3a1ac32e512a6c66e0ce9d2 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 13:59:48 +0800 Subject: [PATCH 38/73] Type of pattern submitter should be mongoose id --- src-ssr/controllers/patterns.js | 2 +- src-ssr/models/patterns.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index dd8bb21d..40d5b063 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -15,7 +15,7 @@ module.exports = { return } else { const result = await patterns.create({ - submitter: req.user.discord, + submitter: req.user._id, name: req.body.name, composer: req.body.composer, keysounded: req.body.keysounded === 1, diff --git a/src-ssr/models/patterns.js b/src-ssr/models/patterns.js index 945bda21..9b4995eb 100644 --- a/src-ssr/models/patterns.js +++ b/src-ssr/models/patterns.js @@ -33,8 +33,9 @@ const previewSchema = new mongoose.Schema({ const schema = new mongoose.Schema({ submitter: { - type: String, - required: true + type: mongoose.Schema.Types.ObjectId, + required: true, + ref: 'users' }, name: { type: String, From a8f065c5b7e0bfe49cdb4ae1005948d75b4df1f6 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 14:00:34 +0800 Subject: [PATCH 39/73] Fix keysounded value always false --- src-ssr/controllers/patterns.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index 40d5b063..17763869 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -18,7 +18,7 @@ module.exports = { submitter: req.user._id, name: req.body.name, composer: req.body.composer, - keysounded: req.body.keysounded === 1, + keysounded: req.body.keysounded, difficulties: req.body.difficulties, link: req.body.link, previews: req.body.previews, @@ -44,7 +44,7 @@ module.exports = { color: '15158332', fields: [ { name: 'Composer', value: req.body.composer, inline: true }, - { name: 'Keysounded', value: req.body.keysounded === 1 ? 'Yes' : 'No', inline: true }, + { name: 'Keysounded', value: req.body.keysounded === true ? 'Yes' : 'No', inline: true }, { name: 'Previews', value: strPreveiw, inline: false }, { name: 'Difficulties', value: strDifficulty, inline: false }, { name: 'Download', value: req.body.link, inline: false }, From f918c3d0193338c3201177b1a51ac932da89f287 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 14:00:54 +0800 Subject: [PATCH 40/73] Fix wrong status code in API --- src-ssr/controllers/patterns.js | 2 +- src-ssr/controllers/users.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index 17763869..42bfc97f 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -56,7 +56,7 @@ module.exports = { } } catch (error) { console.log(error) - res.status(500).send({ success: true, message: 'Server Error' }) + res.status(500).send({ success: false, message: 'Server Error' }) } } } diff --git a/src-ssr/controllers/users.js b/src-ssr/controllers/users.js index a3be8d4a..384d34d9 100644 --- a/src-ssr/controllers/users.js +++ b/src-ssr/controllers/users.js @@ -84,7 +84,7 @@ module.exports = { res.status(200).send({ success: true, message: '', jwt: token, token: accessToken }) } catch (error) { console.log(error) - res.status(500).send({ success: true, message: 'Server Error' }) + res.status(500).send({ success: false, message: 'Server Error' }) } }, async logout (req, res) { From ad55b2964ddceac689c24f238c7dee5b8e9be983 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 14:17:26 +0800 Subject: [PATCH 41/73] API frontend: patterns --- src-ssr/controllers/patterns.js | 8 ++++++++ src-ssr/routes/patterns.js | 3 ++- src/boot/mixin.js | 9 +++++---- src/components/PatternCard.vue | 10 +++++----- src/pages/Patterns.vue | 4 ++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index 42bfc97f..af6afdbb 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -58,5 +58,13 @@ module.exports = { console.log(error) res.status(500).send({ success: false, message: 'Server Error' }) } + }, + async search (req, res) { + try { + const result = await patterns.find().populate('submitter', 'name').lean() + res.status(200).send({ success: true, message: '', result }) + } catch (error) { + res.status(500).send({ success: false, message: 'Server Error' }) + } } } diff --git a/src-ssr/routes/patterns.js b/src-ssr/routes/patterns.js index 6a9723d3..ffe1d051 100644 --- a/src-ssr/routes/patterns.js +++ b/src-ssr/routes/patterns.js @@ -1,8 +1,9 @@ const express = require('express') -const { create } = require('../controllers/patterns.js') +const { create, search } = require('../controllers/patterns.js') const auth = require('../middleware/auth') const router = express.Router() router.post('/', auth, create) +router.get('/', search) module.exports = router diff --git a/src/boot/mixin.js b/src/boot/mixin.js index 9b7ec95d..b79a8206 100644 --- a/src/boot/mixin.js +++ b/src/boot/mixin.js @@ -13,7 +13,8 @@ export default async ({ Vue }) => { token: 'https://discord.com/api/oauth2/token', identity: 'https://discord.com/api/users/@me', guilds: 'https://discord.com/api/users/@me/guilds' - } + }, + controls: ['Touch', 'Keys', 'KM'] } }, methods: { @@ -39,13 +40,13 @@ export default async ({ Vue }) => { getControlIcon (control, level) { let icon = '' switch (control) { - case 'Touch': + case 0: icon = 'touch_app' break - case 'Keys': + case 1: icon = 'keyboard' break - case 'KM': + case 2: icon = level <= 5 ? 'img:./assets/icons/KM_NM.svg' : level <= 10 ? 'img:./assets/icons/KM_HD.svg' : 'img:./assets/icons/KM_MX.svg' break } diff --git a/src/components/PatternCard.vue b/src/components/PatternCard.vue index f7bb6997..0abdb563 100644 --- a/src/components/PatternCard.vue +++ b/src/components/PatternCard.vue @@ -1,6 +1,6 @@ diff --git a/src/pages/Patterns.vue b/src/pages/Patterns.vue index c4969f17..5bbf7911 100644 --- a/src/pages/Patterns.vue +++ b/src/pages/Patterns.vue @@ -125,9 +125,9 @@ export default { }, async fetchPatterns () { try { - const result = await this.$axios.get(process.env.BACK_URL + '?action=patterns') + const result = await this.$axios.get(new URL('/api/patterns', process.env.HOST_URL)) if (result.data.success) { - this.patterns = result.data.results + this.patterns = result.data.result } else { throw new Error('Error') } From 92a5b2615fbd1606b819bd07509d290c9c8e0426 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 14:32:26 +0800 Subject: [PATCH 42/73] API backend: pattern page --- src-ssr/controllers/patterns.js | 12 ++++++++++++ src-ssr/routes/patterns.js | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index af6afdbb..d2f09c80 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -66,5 +66,17 @@ module.exports = { } catch (error) { res.status(500).send({ success: false, message: 'Server Error' }) } + }, + async searchID (req, res) { + try { + const result = await patterns.findById(req.params.id).populate('submitter', 'name').lean() + if (result === null) { + res.status(404).send({ success: false, message: 'Not found' }) + } else { + res.status(200).send({ success: true, message: '', result }) + } + } catch (error) { + res.status(500).send({ success: false, message: 'Server Error' }) + } } } diff --git a/src-ssr/routes/patterns.js b/src-ssr/routes/patterns.js index ffe1d051..23de219c 100644 --- a/src-ssr/routes/patterns.js +++ b/src-ssr/routes/patterns.js @@ -1,9 +1,10 @@ const express = require('express') -const { create, search } = require('../controllers/patterns.js') +const { create, search, searchID } = require('../controllers/patterns.js') const auth = require('../middleware/auth') const router = express.Router() router.post('/', auth, create) router.get('/', search) +router.get('/:id', searchID) module.exports = router From f1cae557557bef785699053de98c64d680351450 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 14:32:39 +0800 Subject: [PATCH 43/73] API frontend: pattern page --- src/pages/Pattern.vue | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/pages/Pattern.vue b/src/pages/Pattern.vue index aff0880e..f27ee819 100644 --- a/src/pages/Pattern.vue +++ b/src/pages/Pattern.vue @@ -32,7 +32,7 @@ .row.justify-center .col-12.text-h6.text-center Previews .col-12.col-md-6.col-lg-3.q-pa-md.q-my-xs(v-for="(video, idx) in pattern.previews" :key="idx") - q-video(:ratio="16/9" :src="'https://www.youtube.com/embed/'+video.link") + q-video(:ratio="16/9" :src="'https://www.youtube.com/embed/'+video.ytid") From ed4a247945b493f47d54cfe340484b7b1844bba8 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 17:19:26 +0800 Subject: [PATCH 58/73] Also show pattern dates in my page --- src/components/PatternCard.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/PatternCard.vue b/src/components/PatternCard.vue index 9ae9690c..563e2212 100644 --- a/src/components/PatternCard.vue +++ b/src/components/PatternCard.vue @@ -9,13 +9,17 @@ q-btn.btn-dl.absolute(v-if="mine" fab icon="edit" color="tech" text-color="black" @click="$emit('edit')") .row.no-wrap.items-center q-list - q-item(v-if="!mine") - q-item-section + q-item + q-item-section(v-if="!mine") | Pattern by {{ pattern.submitter.name }} br | Submitted {{ formattedTime }} br | Upadated {{ formattedUpdateTime }} + q-item-section(v-else) + | Submitted {{ formattedTime }} + br + | Upadated {{ formattedUpdateTime }} q-item q-item-section(:class="[{'text-red': !pattern.keysounded, 'text-positive': pattern.keysounded}]") div From bab59a1e1b758a846c8eefb9759092c6285e404a Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 17:35:02 +0800 Subject: [PATCH 59/73] Add start and limit query to get patterns API --- src-ssr/controllers/patterns.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index e02044ec..3285b675 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -63,10 +63,20 @@ module.exports = { async search (req, res) { try { const query = {} + let skip = 0 + let limit = 0 if (req.query.submitter) { query.submitter = mongoose.Types.ObjectId(req.query.submitter) } - const result = await patterns.find(query).populate('submitter', 'name').lean() + if (req.query.start) { + skip = parseInt(req.query.start) + skip = isNaN(skip) ? 0 : skip + } + if (req.query.limit) { + limit = parseInt(req.query.limit) + limit = isNaN(limit) ? 0 : limit + } + const result = await patterns.find(query, {}, { skip, limit }).sort('-submitDate').populate('submitter', 'name').lean() res.status(200).send({ success: true, message: '', result }) } catch (error) { console.log(error) From 665617c5a96e900eb3e6e96328f397483f7fcd9d Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 17:35:41 +0800 Subject: [PATCH 60/73] API frontend: index patterns --- src/components/IndexPatterns.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/IndexPatterns.vue b/src/components/IndexPatterns.vue index 5db2c229..ef0c2f48 100644 --- a/src/components/IndexPatterns.vue +++ b/src/components/IndexPatterns.vue @@ -5,7 +5,7 @@ h4.text-center Latest Patterns q-separator .row - .col-12.col-sm-6.col-md-3.q-pa-md.q-my-xs(v-for="(pattern, index) in patterns" :key="pattern.id") + .col-12.col-sm-6.col-md-3.q-pa-md.q-my-xs(v-for="(pattern, index) in patterns" :key="pattern._id") PatternCard(:pattern="pattern" :mine="false") @@ -25,9 +25,9 @@ export default { methods: { async fetchPatterns () { try { - const result = await this.$axios.get(process.env.BACK_URL + '?action=indexpatterns') + const result = await this.$axios.get(new URL('/api/patterns?start=0&limit=8', process.env.HOST_URL)) if (result.data.success) { - this.patterns = result.data.results + this.patterns = result.data.result } else { throw new Error('Error') } From b61d3e027b25eeaf89bd9373ff7610caed3114ba Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 18:59:40 +0800 Subject: [PATCH 61/73] API backend: index videos --- src-ssr/controllers/patterns.js | 12 ++++++++++++ src-ssr/routes/patterns.js | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src-ssr/controllers/patterns.js b/src-ssr/controllers/patterns.js index 3285b675..9db06dbe 100644 --- a/src-ssr/controllers/patterns.js +++ b/src-ssr/controllers/patterns.js @@ -145,5 +145,17 @@ module.exports = { console.log(error) res.status(500).send({ success: false, message: 'Server Error' }) } + }, + async indexvideo (req, res) { + try { + const result = await patterns.aggregate([ + { $unwind: '$previews' }, + { $project: { 'previews.ytid': 1 } }, + { $sample: { size: 7 } } + ]) + res.status(200).send({ success: true, message: '', result }) + } catch (error) { + + } } } diff --git a/src-ssr/routes/patterns.js b/src-ssr/routes/patterns.js index eb1f15bc..a4f38908 100644 --- a/src-ssr/routes/patterns.js +++ b/src-ssr/routes/patterns.js @@ -1,10 +1,11 @@ const express = require('express') -const { create, search, searchID, del, update } = require('../controllers/patterns.js') +const { create, search, searchID, del, update, indexvideo } = require('../controllers/patterns.js') const auth = require('../middleware/auth') const router = express.Router() router.post('/', auth, create) router.get('/', search) +router.get('/indexvideo', indexvideo) router.get('/:id', searchID) router.delete('/:id', auth, del) router.patch('/:id', auth, update) From fe05aa672bff49cb821fcc5e7ce1d9328b23dcb2 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 18:59:50 +0800 Subject: [PATCH 62/73] API frontend: index videos --- src/components/IndexVideos.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/IndexVideos.vue b/src/components/IndexVideos.vue index 605aaa2c..21609baf 100644 --- a/src/components/IndexVideos.vue +++ b/src/components/IndexVideos.vue @@ -20,9 +20,13 @@ export default { methods: { async fetchVideos () { try { - const result = await this.$axios.get(process.env.BACK_URL + '?action=indexvideos') + const result = await this.$axios.get(new URL('/api/patterns/indexvideo', process.env.HOST_URL)) if (result.data.success) { - this.videos = ['qQAmkMlBvtg', ...result.data.results].map(video => { + this.videos = ['qQAmkMlBvtg'] + for (const data of result.data.result) { + this.videos.push(data.previews.ytid) + } + this.videos = this.videos.map(video => { return 'https://www.youtube.com/embed/' + video }) } else { From 638d3b3a195952e9ad047e42c2611080b7f4dc98 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 19:17:21 +0800 Subject: [PATCH 63/73] Add day.js --- package.json | 1 + quasar.conf.js | 3 ++- src/boot/day.js | 13 +++++++++++++ yarn.lock | 12 ++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/boot/day.js diff --git a/package.json b/package.json index e6a4d8f1..eab9ef84 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "jsonwebtoken": "^8.5.1", "mongoose": "^5.12.3", "quasar": "^1.0.0", + "vue-dayjs-plugin": "^1.0.0", "vue-i18n": "^8.0.0", "vuex-persistedstate": "3.0.1" }, diff --git a/quasar.conf.js b/quasar.conf.js index ff7e734c..50dfcb95 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -23,7 +23,8 @@ module.exports = function (/* ctx */) { 'axios', 'mixin', { path: 'analytics', server: false }, - { path: 'persist', server: false } + { path: 'persist', server: false }, + { path: 'day', server: false } ], // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css diff --git a/src/boot/day.js b/src/boot/day.js new file mode 100644 index 00000000..9e51177f --- /dev/null +++ b/src/boot/day.js @@ -0,0 +1,13 @@ +import Vue from 'vue' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' + +dayjs.extend(relativeTime) + +Object.defineProperties(Vue.prototype, { + $date: { + get () { + return dayjs + } + } +}) diff --git a/yarn.lock b/yarn.lock index 3191c16c..b596fb27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3693,6 +3693,11 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +dayjs@^1.8.16: + version "1.10.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" + integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== + de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" @@ -11191,6 +11196,13 @@ void-elements@^3.1.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk= +vue-dayjs-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vue-dayjs-plugin/-/vue-dayjs-plugin-1.0.0.tgz#7fccc3c95a4c408ccb3ee011e8bc228f6d91b31b" + integrity sha512-fgA2Xw2NOjDdmRca0JA+JcVzBWkBEjoPRvsZ+wJEWycXkloQewn4HSRRoawWW4//2brb+SoLTDHi+DDeex8LDA== + dependencies: + dayjs "^1.8.16" + vue-eslint-parser@^7.0.0: version "7.3.0" resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.3.0.tgz#894085839d99d81296fa081d19643733f23d7559" From 55f6b0ecc5748c8c777b065d5e1f76d43d46cfeb Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 19:17:33 +0800 Subject: [PATCH 64/73] Update date format in pattern card --- src/components/PatternCard.vue | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/PatternCard.vue b/src/components/PatternCard.vue index 563e2212..cc1f3616 100644 --- a/src/components/PatternCard.vue +++ b/src/components/PatternCard.vue @@ -10,16 +10,17 @@ .row.no-wrap.items-center q-list q-item - q-item-section(v-if="!mine") - | Pattern by {{ pattern.submitter.name }} - br - | Submitted {{ formattedTime }} - br - | Upadated {{ formattedUpdateTime }} - q-item-section(v-else) - | Submitted {{ formattedTime }} - br - | Upadated {{ formattedUpdateTime }} + q-item-section + p + span(v-if="!mine") Pattern by {{ pattern.submitter.name }} + br(v-if="!mine") + span Submitted {{ formattedTime.relative }} + q-tooltip(anchor="top middle" self="bottom middle" content-style="background: #000") + | {{ formattedTime.text }} + br + span Upadated {{ formattedUpdateTime.relative }} + q-tooltip(anchor="top middle" self="bottom middle" content-style="background: #000") + | {{ formattedUpdateTime.text }} q-item q-item-section(:class="[{'text-red': !pattern.keysounded, 'text-positive': pattern.keysounded}]") div @@ -55,10 +56,16 @@ export default { }, computed: { formattedTime () { - return new Date(this.pattern.submitDate).toLocaleString('en-US') + return { + relative: this.$date(this.pattern.submitDate).fromNow(), + text: new Date(this.pattern.submitDate).toLocaleString('en-US') + } }, formattedUpdateTime () { - return new Date(this.pattern.updateDate).toLocaleString('en-US') + return { + relative: this.$date(this.pattern.updateDate).fromNow(), + text: new Date(this.pattern.updateDate).toLocaleString('en-US') + } } } } From bc2f4af41f7e203906007cd4e5c6665382a76b0c Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 19:19:08 +0800 Subject: [PATCH 65/73] Remove GitHub actions We are moving to heroku! --- .github/workflows/deploy.yml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 8d9933e9..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Deploy -on: - push: - branches: [ master ] -jobs: - SPA: - runs-on: ubuntu-latest - steps: - - name: checkout - uses: actions/checkout@master - - name: buildAndDeploy - uses: JamesIves/github-pages-deploy-action@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BASE_BRANCH: master - BRANCH: gh-pages - FOLDER: dist/spa - BUILD_SCRIPT: npm install && npm run build - CLEAN: true - BACK_URL: ${{ secrets.BACK_URL }} - DISCORD_CLIENT: ${{ secrets.DISCORD_CLIENT }} - DISCORD_SECRET: ${{ secrets.DISCORD_SECRET }} From a885067c2a4385c9637b205bd226861c44174807 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 20:07:26 +0800 Subject: [PATCH 66/73] Remove version keys in db schema --- src-ssr/models/patterns.js | 6 +++--- src-ssr/models/users.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src-ssr/models/patterns.js b/src-ssr/models/patterns.js index 5ee793c5..b588bab3 100644 --- a/src-ssr/models/patterns.js +++ b/src-ssr/models/patterns.js @@ -18,7 +18,7 @@ const difficultySchema = new mongoose.Schema({ min: 0, max: 2 } -}) +}, { versionKey: false }) const previewSchema = new mongoose.Schema({ name: { @@ -29,7 +29,7 @@ const previewSchema = new mongoose.Schema({ type: String, required: true } -}) +}, { versionKey: false }) const schema = new mongoose.Schema({ submitter: { @@ -73,6 +73,6 @@ const schema = new mongoose.Schema({ type: Date, default: Date.now } -}) +}, { versionKey: false }) module.exports = mongoose.model('patterns', schema) diff --git a/src-ssr/models/users.js b/src-ssr/models/users.js index 02b07cec..bdc44ac6 100644 --- a/src-ssr/models/users.js +++ b/src-ssr/models/users.js @@ -13,7 +13,7 @@ const AccessInfoSchema = new mongoose.Schema({ type: String, required: true } -}) +}, { versionKey: false }) const schema = new mongoose.Schema({ discord: { @@ -28,6 +28,6 @@ const schema = new mongoose.Schema({ accessInfo: { type: [AccessInfoSchema] } -}) +}, { versionKey: false }) module.exports = mongoose.model('users', schema) From aec7f348e228b77e88ee3a5bfade254618ed4f78 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 20:18:32 +0800 Subject: [PATCH 67/73] Update engines in package.json --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index eab9ef84..4bfb21c7 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,8 @@ "last 5 Opera versions" ], "engines": { - "node": ">= 10.18.1", - "npm": ">= 6.13.4", - "yarn": ">= 1.21.1" + "node": ">= 12.18.3", + "npm": ">= 6.14.11", + "yarn": ">= 1.22.5" } } From 1541faebe9cb44dff3d3cf5edd87f3a5e0f337d8 Mon Sep 17 00:00:00 2001 From: Kento Date: Fri, 9 Apr 2021 20:39:35 +0800 Subject: [PATCH 68/73] Add start script for heroku --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4bfb21c7..5f7622e7 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "dev": "quasar dev -m ssr", "build": "quasar build -m ssr", "heroku-postbuild": "yarn && yarn build", - "icons": "icongenie g -i ./public/assets/notes/basic.png" + "icons": "icongenie g -i ./public/assets/notes/basic.png", + "start": "cd dist/ssr && yarn start" }, "dependencies": { "@quasar/extras": "^1.0.0", From 368728e0e7548cd4fe5c650b047da4571bef162a Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 20:49:10 +0800 Subject: [PATCH 69/73] Fix discord login URL --- src/boot/mixin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/mixin.js b/src/boot/mixin.js index b79a8206..d1b15dfb 100644 --- a/src/boot/mixin.js +++ b/src/boot/mixin.js @@ -9,7 +9,7 @@ export default async ({ Vue }) => { data () { return { discordURL: { - login: `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT.replace(/abc/g, '')}&redirect_uri=${encodeURIComponent(process.env.HOST_URL + '/api/users/login')}&response_type=code&scope=identify%20guilds`, + login: `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT.replace(/abc/g, '')}&redirect_uri=${encodeURIComponent(new URL('/api/users/login', process.env.HOST_URL))}&response_type=code&scope=identify%20guilds`, token: 'https://discord.com/api/oauth2/token', identity: 'https://discord.com/api/users/@me', guilds: 'https://discord.com/api/users/@me/guilds' From e16fcc9f32a664e681a2f5cd684ecfeb544f252c Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 21:21:52 +0800 Subject: [PATCH 70/73] Fix keysounded text in pattern page --- src/pages/Pattern.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Pattern.vue b/src/pages/Pattern.vue index f27ee819..d6498360 100644 --- a/src/pages/Pattern.vue +++ b/src/pages/Pattern.vue @@ -20,8 +20,8 @@ div q-icon(name="upload") |  Submitted by {{ pattern.user }} - div(:class="[{'text-red': pattern.keysounded === '0', 'text-positive': pattern.keysounded === '1'}]") - q-icon(:name="pattern.keysounded === '0' ? 'close' : 'check'") + div(:class="[{'text-red': !pattern.keysounded, 'text-positive': pattern.keysounded}]") + q-icon(:name="!pattern.keysounded ? 'close' : 'check'") |  Keysounded div(v-for="(difficulty, index) in pattern.difficulties" :key="'D'+index" :class="getLevelColor(difficulty.level)") q-icon(size="sm" :name="getControlIcon(difficulty.control, difficulty.level)" :class="getLevelColor(difficulty.level)") From 21f847f7b9226b988a73554f61d91df5684772d6 Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 21:29:58 +0800 Subject: [PATCH 71/73] Prefetch patterns --- quasar.conf.js | 2 +- src/pages/Pattern.vue | 43 ++++++++++++++++++++++++++----------- src/store/index.js | 4 +++- src/store/temp/actions.js | 7 ++++++ src/store/temp/getters.js | 3 +++ src/store/temp/index.js | 12 +++++++++++ src/store/temp/mutations.js | 7 ++++++ src/store/temp/state.js | 5 +++++ 8 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 src/store/temp/actions.js create mode 100644 src/store/temp/getters.js create mode 100644 src/store/temp/index.js create mode 100644 src/store/temp/mutations.js create mode 100644 src/store/temp/state.js diff --git a/quasar.conf.js b/quasar.conf.js index 50dfcb95..35f014fe 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -13,7 +13,7 @@ module.exports = function (/* ctx */) { supportTS: false, // https://quasar.dev/quasar-cli/prefetch-feature - // preFetch: true, + preFetch: true, // app boot file (/src/boot) // --> boot files are part of "main.js" diff --git a/src/pages/Pattern.vue b/src/pages/Pattern.vue index d6498360..198e2b47 100644 --- a/src/pages/Pattern.vue +++ b/src/pages/Pattern.vue @@ -44,55 +44,70 @@ export default { meta: { title: { name: 'title', - content: `${this.pattern.name} | TECHMANIA` + content: `${this.pattern.name} | TECHMANIA`, + 'data-dynamic': true }, description: { name: 'description', - content: `TECHMANIA >> Patterns >> ${this.pattern.name}` + content: `TECHMANIA >> Patterns >> ${this.pattern.name}`, + 'data-dynamic': true }, ogType: { name: 'og:type', - content: 'website' + content: 'website', + 'data-dynamic': true }, ogUrl: { name: 'og:url', - content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString(), + 'data-dynamic': true }, ogTitle: { name: 'og:title', - content: `${this.pattern.name} | TECHMANIA` + content: `${this.pattern.name} | TECHMANIA`, + 'data-dynamic': true }, ogDescription: { name: 'og:description', - content: `TECHMANIA >> Patterns >> ${this.pattern.name}` + content: `TECHMANIA >> Patterns >> ${this.pattern.name}`, + 'data-dynamic': true }, ogImage: { name: 'og:image', - content: this.backgroundImage + content: this.backgroundImage, + 'data-dynamic': true }, twCard: { name: 'twitter:card', - content: 'summary_large_image' + content: 'summary_large_image', + 'data-dynamic': true }, twUrl: { name: 'twitter:url', - content: new URL(this.$route.fullPath, process.env.HOST_URL).toString() + content: new URL(this.$route.fullPath, process.env.HOST_URL).toString(), + 'data-dynamic': true }, twTitle: { name: 'twitter:title', - content: `${this.pattern.name} | TECHMANIA` + content: `${this.pattern.name} | TECHMANIA`, + 'data-dynamic': true }, twDescription: { name: 'twitter:description', - content: `TECHMANIA >> Patterns >> ${this.pattern.name}` + content: `TECHMANIA >> Patterns >> ${this.pattern.name}`, + 'data-dynamic': true }, twImage: { name: 'twitter:image', - content: this.backgroundImage + content: this.backgroundImage, + 'data-dynamic': true } } } }, + preFetch ({ store, currentRoute, previousRoute, redirect, ssrContext, urlPath, publicPath }) { + return store.dispatch('temp/fetchPattern', currentRoute.params.id) + }, data () { return { pattern: { @@ -129,7 +144,9 @@ export default { } }, mounted () { - this.fetchPattern() + this.pattern = this.$store.getters['temp/getPattern'] + document.title = `${this.pattern.name} | TECHMANIA` + this.$store.commit('temp/cleanPattern') } } diff --git a/src/store/index.js b/src/store/index.js index cd64c2ad..9ebd5737 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,6 +1,7 @@ import Vue from 'vue' import Vuex from 'vuex' import user from './user' +import temp from './temp' Vue.use(Vuex) @@ -16,7 +17,8 @@ Vue.use(Vuex) export default function ({ ssrContext }) { const Store = new Vuex.Store({ modules: { - user + user, + temp }, // enable strict mode (adds overhead!) // for dev mode only diff --git a/src/store/temp/actions.js b/src/store/temp/actions.js new file mode 100644 index 00000000..60c2c944 --- /dev/null +++ b/src/store/temp/actions.js @@ -0,0 +1,7 @@ +import axios from 'axios' + +export function fetchPattern ({ commit }, id) { + return axios.get(new URL(`/api/patterns/${id}`, process.env.HOST_URL).toString()).then(({ data }) => { + commit('setPattern', data.result) + }) +} diff --git a/src/store/temp/getters.js b/src/store/temp/getters.js new file mode 100644 index 00000000..5a272857 --- /dev/null +++ b/src/store/temp/getters.js @@ -0,0 +1,3 @@ +export function getPattern (state) { + return state.pattern +} diff --git a/src/store/temp/index.js b/src/store/temp/index.js new file mode 100644 index 00000000..babab8ec --- /dev/null +++ b/src/store/temp/index.js @@ -0,0 +1,12 @@ +import state from './state' +import * as getters from './getters' +import * as mutations from './mutations' +import * as actions from './actions' + +export default { + namespaced: true, + getters, + mutations, + actions, + state +} diff --git a/src/store/temp/mutations.js b/src/store/temp/mutations.js new file mode 100644 index 00000000..7e5b8bd1 --- /dev/null +++ b/src/store/temp/mutations.js @@ -0,0 +1,7 @@ +export function cleanPattern (state, data) { + state.pattern = {} +} + +export function setPattern (state, data) { + state.pattern = data +} diff --git a/src/store/temp/state.js b/src/store/temp/state.js new file mode 100644 index 00000000..1baabb16 --- /dev/null +++ b/src/store/temp/state.js @@ -0,0 +1,5 @@ +export default function () { + return { + pattern: {} + } +} From eee975c29b0059a02a82baabad9d7f9f462befef Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 22:56:46 +0800 Subject: [PATCH 72/73] Add default background for pattern page --- src/pages/Pattern.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Pattern.vue b/src/pages/Pattern.vue index 198e2b47..c22947d8 100644 --- a/src/pages/Pattern.vue +++ b/src/pages/Pattern.vue @@ -125,7 +125,7 @@ export default { }, computed: { backgroundImage () { - return this.pattern.previews.length > 0 ? `http://i3.ytimg.com/vi/${this.pattern.previews[0].ytid}/hqdefault.jpg` : '' + return this.pattern.previews.length > 0 ? `http://i3.ytimg.com/vi/${this.pattern.previews[0].ytid}/hqdefault.jpg` : 'https://raw.githubusercontent.com/techmania-team/techmania-team.github.io/master/public/assets/Logo_black.png' } }, methods: { From 2acbe7d9caa6037d6369e66ca40ea352425e895d Mon Sep 17 00:00:00 2001 From: rogeraabbccdd Date: Fri, 9 Apr 2021 22:57:39 +0800 Subject: [PATCH 73/73] Add meta API, and redirect facebook and twitter crawler request --- src-ssr/controllers/meta.js | 30 ++++++++++++++++++++++++++++++ src-ssr/extension.js | 15 +++++++++++++++ src-ssr/index.js | 14 ++++++++------ src-ssr/routes/meta.js | 7 +++++++ 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 src-ssr/controllers/meta.js create mode 100644 src-ssr/routes/meta.js diff --git a/src-ssr/controllers/meta.js b/src-ssr/controllers/meta.js new file mode 100644 index 00000000..42243595 --- /dev/null +++ b/src-ssr/controllers/meta.js @@ -0,0 +1,30 @@ +const patterns = require('../models/patterns.js') + +module.exports = { + async pattern (req, res) { + try { + const result = await patterns.findById(req.params.id).populate('submitter', 'name').lean() + if (result === null) { + res.status(404).send({ success: false, message: 'Not found' }) + } else { + const img = result.previews.length > 0 ? `http://i3.ytimg.com/vi/${result.previews[0].ytid}/hqdefault.jpg` : 'https://raw.githubusercontent.com/techmania-team/techmania-team.github.io/master/public/assets/Logo_black.png' + res.send( + ` + + + + + + + + + + + ` + ) + } + } catch (error) { + res.status(500).send({ success: false, message: 'Server Error' }) + } + } +} diff --git a/src-ssr/extension.js b/src-ssr/extension.js index 38c3b1be..1391bd43 100644 --- a/src-ssr/extension.js +++ b/src-ssr/extension.js @@ -15,6 +15,7 @@ const bodyParser = require('body-parser') const routerUsers = require('./routes/users.js') const routerPatterns = require('./routes/patterns.js') +const routerMeta = require('./routes/meta.js') module.exports.extendApp = function ({ app, ssr }) { mongoose.connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }) @@ -25,4 +26,18 @@ module.exports.extendApp = function ({ app, ssr }) { app.use('/api/users', routerUsers) app.use('/api/patterns', routerPatterns) + + // fuck facebook and twitter crawler + app.use('/api/meta', routerMeta) + app.get('/patterns/:id', (req, res, next) => { + console.log(req.headers['user-agent']) + const agent = req.headers['user-agent'] + if ((agent.match(/facebook/gi) || agent.match(/twitter/gi)) && req.originalUrl.includes('/patterns/')) { + const data = req.originalUrl.split('/') + console.log(data) + res.redirect(new URL('/api/meta/pattern/' + data[2], process.env.HOST_URL)) + } else { + next() + } + }) } diff --git a/src-ssr/index.js b/src-ssr/index.js index 7bdcbf39..09ae271b 100644 --- a/src-ssr/index.js +++ b/src-ssr/index.js @@ -76,13 +76,11 @@ app.get(ssr.resolveUrl('*'), (req, res) => { if (err.url) { if (err.code) res.redirect(err.code, err.url) else res.redirect(err.url) - } - else if (err.code === 404) { + } else if (err.code === 404) { // Should reach here only if no "catch-all" route // is defined in /src/routes res.status(404).send('404 | Page Not Found') - } - else { + } else { // Render Error Page or // create a route (/src/routes) for an error page and redirect to it res.status(500).send('500 | Internal Server Error') @@ -92,8 +90,12 @@ app.get(ssr.resolveUrl('*'), (req, res) => { console.error(err.stack) } } - } - else { + } else { + const agent = req.headers['user-agent'] + if ((agent.match(/facebook/gi) || agent.match(/twitter/gi)) && req.originalUrl.includes('/patterns/')) { + const data = req.originalUrl.split('/') + res.redirect(new URL('/api/pattern/' + data[1], process.env.HOST_URL)) + } res.send(html) } }) diff --git a/src-ssr/routes/meta.js b/src-ssr/routes/meta.js new file mode 100644 index 00000000..47820de7 --- /dev/null +++ b/src-ssr/routes/meta.js @@ -0,0 +1,7 @@ +const express = require('express') +const { pattern } = require('../controllers/meta.js') +const router = express.Router() + +router.get('/pattern/:id', pattern) + +module.exports = router