From 234c1d1fae6a7f2c78e456f992f91622ca599060 Mon Sep 17 00:00:00 2001 From: vben <anncwb@126.com> Date: 星期一, 23 十一月 2020 23:24:13 +0800 Subject: [PATCH] feat: the cache can be configured to be encrypted --- src/utils/helper/persistent.ts | 2 src/utils/cache/index.ts | 9 src/views/demo/page/form/high/index.vue | 8 src/components/Application/src/AppFooterToolbar.vue | 5 src/components/Authority/index.ts | 7 src/utils/file/base64Conver.ts | 0 CHANGELOG.zh_CN.md | 5 src/utils/file/download.ts | 2 src/components/Application/index.ts | 6 /dev/null | 138 ----------------- src/components/util.ts | 9 + src/settings/encryptionSetting.ts | 13 + src/utils/encryption/aesEncryption.ts | 35 ++++ yarn.lock | 38 ++-- package.json | 8 src/utils/cache/cookie.ts | 78 +++++++++ src/components/registerGlobComp.ts | 3 src/utils/cache/storageCache.ts | 108 +++++++++++++ 18 files changed, 300 insertions(+), 174 deletions(-) diff --git a/CHANGELOG.zh_CN.md b/CHANGELOG.zh_CN.md index 77f37ee..e17d4d3 100644 --- a/CHANGELOG.zh_CN.md +++ b/CHANGELOG.zh_CN.md @@ -1,5 +1,10 @@ ## Wip +### 鉁� Features + +- 缂撳瓨鍙互閰嶇疆鏄惁鍔犲瘑 +- 澶氳瑷�鏀寔 + ### 馃帿 Chores - 绉婚櫎 messageSetting 閰嶇疆 diff --git a/package.json b/package.json index 8b73d15..91abafb 100644 --- a/package.json +++ b/package.json @@ -37,16 +37,16 @@ "vditor": "^3.6.3", "vue": "^3.0.2", "vue-i18n": "^9.0.0-beta.8", - "vue-router": "^4.0.0-rc.3", + "vue-router": "^4.0.0-rc.5", "vuex": "^4.0.0-rc.1", "vuex-module-decorators": "^1.0.1", - "xlsx": "^0.16.8", + "xlsx": "^0.16.9", "zxcvbn": "^4.4.2" }, "devDependencies": { "@commitlint/cli": "^11.0.0", "@commitlint/config-conventional": "^11.0.0", - "@iconify/json": "^1.1.261", + "@iconify/json": "^1.1.262", "@ls-lint/ls-lint": "^1.9.2", "@purge-icons/generated": "^0.4.1", "@types/echarts": "^4.9.1", @@ -72,7 +72,7 @@ "cross-env": "^7.0.2", "dot-prop": "^6.0.1", "dotenv": "^8.2.0", - "eslint": "^7.13.0", + "eslint": "^7.14.0", "eslint-config-prettier": "^6.15.0", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-vue": "^7.1.0", diff --git a/src/components/Application/index.ts b/src/components/Application/index.ts index e52eab2..b48b927 100644 --- a/src/components/Application/index.ts +++ b/src/components/Application/index.ts @@ -1,3 +1,7 @@ import AppLocalPicker from './src/AppLocalPicker.vue'; +import AppFooterToolbar from './src/AppFooterToolbar.vue'; +import { withInstall } from '../util'; -export { AppLocalPicker }; +export { AppLocalPicker, AppFooterToolbar }; + +export default withInstall(AppLocalPicker, AppFooterToolbar); diff --git a/src/components/Footer/src/index.vue b/src/components/Application/src/AppFooterToolbar.vue similarity index 97% rename from src/components/Footer/src/index.vue rename to src/components/Application/src/AppFooterToolbar.vue index 706a078..b0550ba 100644 --- a/src/components/Footer/src/index.vue +++ b/src/components/Application/src/AppFooterToolbar.vue @@ -10,11 +10,14 @@ </template> <script lang="ts"> import { defineComponent, computed, unref } from 'vue'; + import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; + import { appStore } from '/@/store/modules/app'; import { menuStore } from '/@/store/modules/menu'; + export default defineComponent({ - name: 'AppFooter', + name: 'AppFooterToolbar', setup() { const getMiniWidth = computed(() => { const { diff --git a/src/components/Authority/index.ts b/src/components/Authority/index.ts index 364cca2..0e1a213 100644 --- a/src/components/Authority/index.ts +++ b/src/components/Authority/index.ts @@ -1,8 +1,7 @@ -import type { App } from 'vue'; import Authority from './src/index.vue'; -export default (app: App): void => { - app.component(Authority.name, Authority); -}; +import { withInstall } from '../util'; + +export default withInstall(Authority); export { Authority }; diff --git a/src/components/Footer/index.ts b/src/components/Footer/index.ts deleted file mode 100644 index c8d0f62..0000000 --- a/src/components/Footer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as AppFooter } from './src/index.vue'; diff --git a/src/components/registerGlobComp.ts b/src/components/registerGlobComp.ts index 8ce339e..a7984fe 100644 --- a/src/components/registerGlobComp.ts +++ b/src/components/registerGlobComp.ts @@ -1,6 +1,5 @@ import Icon from './Icon/index'; import Button from './Button/index.vue'; -import { AppFooter } from './Footer'; import { // Need Button as AntButton, @@ -35,7 +34,7 @@ } from 'ant-design-vue'; import { getApp } from '/@/setup/App'; -const compList = [Icon, Button, AntButton.Group, AppFooter]; +const compList = [Icon, Button, AntButton.Group]; // Fix hmr multiple registered components let registered = false; diff --git a/src/components/util.ts b/src/components/util.ts index 22302c8..d3b72ad 100644 --- a/src/components/util.ts +++ b/src/components/util.ts @@ -1,4 +1,13 @@ import type { VNodeChild } from 'vue'; +import type { App, Component } from 'vue'; + +export function withInstall(...components: Component[]) { + return (app: App) => { + components.forEach((comp) => { + comp.name && app.component(comp.name, comp); + }); + }; +} export function convertToUnit( str: string | number | null | undefined, diff --git a/src/settings/cipherSetting.ts b/src/settings/cipherSetting.ts deleted file mode 100644 index 0b9e93c..0000000 --- a/src/settings/cipherSetting.ts +++ /dev/null @@ -1,10 +0,0 @@ -// System default cache time, in seconds -export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; - -/** - * @description: - */ -export const storageCipher = { - key: '_12345678901234@', - iv: '@12345678901234_', -}; diff --git a/src/settings/encryptionSetting.ts b/src/settings/encryptionSetting.ts new file mode 100644 index 0000000..9490e57 --- /dev/null +++ b/src/settings/encryptionSetting.ts @@ -0,0 +1,13 @@ +import { isDevMode } from '/@/utils/env'; + +// System default cache time, in seconds +export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; + +// aes encryption key +export const cacheCipher = { + key: '_12345678901234@', + iv: '@12345678901234_', +}; + +// Whether the system cache is encrypted using aes +export const enableStorageEncryption = !isDevMode(); diff --git a/src/utils/cache/cookie.ts b/src/utils/cache/cookie.ts new file mode 100644 index 0000000..26a854d --- /dev/null +++ b/src/utils/cache/cookie.ts @@ -0,0 +1,78 @@ +import { DEFAULT_CACHE_TIME } from '../../settings/encryptionSetting'; +import { getStorageShortName } from '/@/utils/helper/envHelper'; +import { cacheCipher } from '/@/settings/encryptionSetting'; +import Encryption from '/@/utils/encryption/aesEncryption'; + +export default class WebCookie { + private encryption: Encryption; + private hasEncrypt: boolean; + + constructor(hasEncrypt = true, key = cacheCipher.key, iv = cacheCipher.iv) { + const encryption = new Encryption({ key, iv }); + this.encryption = encryption; + this.hasEncrypt = hasEncrypt; + } + + private getKey(key: string) { + return `${getStorageShortName()}${key}`.toUpperCase(); + } + + /** + * Add cookie + * @param name cookie key + * @param value cookie value + * @param expire + * If the expiration time is not set, the default management browser will automatically delete + * e.g: + * cookieData.set('name','value',) + */ + setCookie(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { + value = this.hasEncrypt ? this.encryption.encryptByAES(JSON.stringify(value)) : value; + document.cookie = this.getKey(key) + '=' + value + '; Max-Age=' + expire; + } + + /** + * Get the cook value according to the key + * @param key cookie key + */ + getCookie(key: string) { + const arr = document.cookie.split('; '); + for (let i = 0; i < arr.length; i++) { + const arr2 = arr[i].split('='); + if (arr2[0] === this.getKey(key)) { + let message: any = null; + const str = arr2[1]; + if (this.hasEncrypt && str) { + message = this.encryption.decryptByAES(str); + try { + return JSON.parse(message); + } catch (e) { + return str; + } + } + return str; + } + } + return ''; + } + + /** + * Delete cookie based on cookie key + * @param key cookie key + */ + removeCookie(key: string) { + this.setCookie(key, 1, -1); + } + + /** + * clear cookie + */ + clearCookie(): void { + const keys = document.cookie.match(/[^ =;]+(?==)/g); + if (keys) { + for (let i = keys.length; i--; ) { + document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString(); + } + } + } +} diff --git a/src/utils/storage/index.ts b/src/utils/cache/index.ts similarity index 64% rename from src/utils/storage/index.ts rename to src/utils/cache/index.ts index 7ba35d0..b7006d0 100644 --- a/src/utils/storage/index.ts +++ b/src/utils/cache/index.ts @@ -1,17 +1,20 @@ import { getStorageShortName } from '/@/utils/helper/envHelper'; -import { createStorage as create } from './Storage'; - -// debug妯″紡涓嬩笉鍔犲瘑 +import { createStorage as create } from './storageCache'; +import { enableStorageEncryption } from '/@/settings/encryptionSetting'; const createOptions = (storage = sessionStorage) => { return { + // No encryption in debug mode + hasEncrypt: enableStorageEncryption, storage, prefixKey: getStorageShortName(), }; }; + export const WebStorage = create(createOptions()); export const createStorage = (storage: Storage = sessionStorage) => { return create(createOptions(storage))!; }; + export default WebStorage; diff --git a/src/utils/cache/storageCache.ts b/src/utils/cache/storageCache.ts new file mode 100644 index 0000000..d5cb61c --- /dev/null +++ b/src/utils/cache/storageCache.ts @@ -0,0 +1,108 @@ +import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting'; +import { cacheCipher } from '/@/settings/encryptionSetting'; +import Encryption, { EncryptionParams } from '/@/utils/encryption/aesEncryption'; + +export interface CreateStorageParams extends EncryptionParams { + storage: Storage; + + hasEncrypt: boolean; +} +export const createStorage = ({ + prefixKey = '', + storage = sessionStorage, + key = cacheCipher.key, + iv = cacheCipher.iv, + hasEncrypt = true, +} = {}) => { + if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) { + throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!'); + } + + const encryption = new Encryption({ key, iv }); + + /** + *Cache class + *Construction parameters can be passed into sessionStorage, localStorage, + * @class Cache + * @example + */ + const WebStorage = class WebStorage { + private storage: Storage; + private prefixKey?: string; + private encryption: Encryption; + private hasEncrypt: boolean; + /** + * + * @param {*} storage + */ + constructor() { + this.storage = storage; + this.prefixKey = prefixKey; + this.encryption = encryption; + this.hasEncrypt = hasEncrypt; + } + + private getKey(key: string) { + return `${this.prefixKey}${key}`.toUpperCase(); + } + + /** + * + * Set cache + * @param {string} key + * @param {*} value + * @expire Expiration time in seconds + * @memberof Cache + */ + set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { + const stringData = JSON.stringify({ + value, + expire: expire !== null ? new Date().getTime() + expire * 1000 : null, + }); + const stringifyValue = this.hasEncrypt + ? this.encryption.encryptByAES(stringData) + : stringData; + this.storage.setItem(this.getKey(key), stringifyValue); + } + + /** + *Read cache + * @param {string} key + * @memberof Cache + */ + get(key: string, def: any = null): any { + const item = this.storage.getItem(this.getKey(key)); + if (item) { + try { + const decItem = this.hasEncrypt ? this.encryption.decryptByAES(item) : item; + const data = JSON.parse(decItem); + const { value, expire } = data; + if (expire === null || expire >= new Date().getTime()) { + return value; + } + this.remove(this.getKey(key)); + } catch (e) { + return def; + } + } + return def; + } + + /** + * Delete cache based on key + * @param {string} key + * @memberof Cache + */ + remove(key: string) { + this.storage.removeItem(this.getKey(key)); + } + + /** + * Delete all caches of this instance + */ + clear(): void { + this.storage.clear(); + } + }; + return new WebStorage(); +}; diff --git a/src/utils/encryption/aesEncryption.ts b/src/utils/encryption/aesEncryption.ts new file mode 100644 index 0000000..425d207 --- /dev/null +++ b/src/utils/encryption/aesEncryption.ts @@ -0,0 +1,35 @@ +import CryptoES from 'crypto-es'; +export interface EncryptionParams { + key: string; + iv: string; +} +export class Encryption { + private key; + + private iv; + + constructor(opt: EncryptionParams) { + const { key, iv } = opt; + this.key = CryptoES.enc.Utf8.parse(key); + this.iv = CryptoES.enc.Utf8.parse(iv); + } + + get getOpt(): CryptoES.lib.CipherCfg { + return { + mode: CryptoES.mode.CBC as any, + padding: CryptoES.pad.Pkcs7, + iv: this.iv, + }; + } + + encryptByAES(str: string) { + const encrypted = CryptoES.AES.encrypt(str, this.key, this.getOpt); + return encrypted.toString(); + } + + decryptByAES(str: string) { + const decrypted = CryptoES.AES.decrypt(str, this.key, this.getOpt); + return decrypted.toString(CryptoES.enc.Utf8); + } +} +export default Encryption; diff --git a/src/utils/file/base64.ts b/src/utils/file/base64Conver.ts similarity index 100% rename from src/utils/file/base64.ts rename to src/utils/file/base64Conver.ts diff --git a/src/utils/file/download.ts b/src/utils/file/download.ts index 87c870c..3511486 100644 --- a/src/utils/file/download.ts +++ b/src/utils/file/download.ts @@ -1,4 +1,4 @@ -import { dataURLtoBlob, urlToBase64 } from './base64'; +import { dataURLtoBlob, urlToBase64 } from './base64Conver'; /** * Download online pictures diff --git a/src/utils/helper/persistent.ts b/src/utils/helper/persistent.ts index 79ccbce..62449d1 100644 --- a/src/utils/helper/persistent.ts +++ b/src/utils/helper/persistent.ts @@ -1,4 +1,4 @@ -import { createStorage } from '/@/utils/storage'; +import { createStorage } from '/@/utils/cache'; import { isIeFn } from '/@/utils/browser'; import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum'; diff --git a/src/utils/storage/Storage.ts b/src/utils/storage/Storage.ts deleted file mode 100644 index 6a0fad5..0000000 --- a/src/utils/storage/Storage.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { DEFAULT_CACHE_TIME } from '/@/settings/cipherSetting'; - -// import { EncryptionParams } from '/@/utils/cipher/aesEncryption'; -export interface CreateStorageParams { - storage: Storage; - hasEncrypt: boolean; -} -export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) => { - /** - *缂撳瓨绫� - *鏋勯�犲弬鏁板彲浠ヤ紶鍏� sessionStorage,localStorage, - * @class Cache - * @example - */ - const WebStorage = class WebStorage { - private storage: Storage; - private prefixKey?: string; - - /** - * - * @param {*} storage - */ - constructor() { - this.storage = storage; - this.prefixKey = prefixKey; - } - - private getKey(key: string) { - return `${this.prefixKey}${key}`.toUpperCase(); - } - - /** - * - * 璁剧疆缂撳瓨 - * @param {string} key 缂撳瓨閿� - * @param {*} value 缂撳瓨鍊� - * @expire 杩囨湡鏃堕棿 鍗曚綅绉� - * @memberof Cache - */ - set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { - const stringData = JSON.stringify({ - value, - expire: expire !== null ? new Date().getTime() + expire * 1000 : null, - }); - this.storage.setItem(this.getKey(key), stringData); - } - - /** - * - *璇诲彇缂撳瓨 - * @param {string} key 缂撳瓨閿� - * @returns 缂撳瓨鍊� - * @memberof Cache - */ - get(key: string, def: any = null): any { - const item = this.storage.getItem(this.getKey(key)); - if (item) { - try { - const data = JSON.parse(item); - const { value, expire } = data; - if (expire === null || expire >= new Date().getTime()) { - return value; - } - this.remove(this.getKey(key)); - } catch (e) { - return def; - } - } - return def; - } - - /** - * - *鍒犻櫎缂撳瓨 - * @param {string} key 缂撳瓨閿� - * @memberof Cache - */ - remove(key: string) { - this.storage.removeItem(this.getKey(key)); - } - - /** - * - *鍒犻櫎璇ュ疄渚嬫墍鏈夌紦瀛� - * @memberof Cache - */ - clear(): void { - this.storage.clear(); - } - - /** - * 娣诲姞cookie - * @param name cookie鍚嶅瓧 - * @param value cookie鍐呭 - * @param expire - * 濡傛灉杩囨湡鏃堕棿鏈缃�,榛樿绠$悊娴忚鍣ㄨ嚜鍔ㄥ垹闄� - * 渚嬪瓙: - * cookieData.set('name','value',) - */ - setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { - document.cookie = this.getKey(name) + '=' + value + '; Max-Age=' + expire; - } - - /** - * 鏍规嵁鍚嶅瓧鑾峰彇cooki鍊� - * @param name cookie鍚� - * @returns {*} cookie鍊� - */ - getCookie(name: string) { - const arr = document.cookie.split('; '); - for (let i = 0; i < arr.length; i++) { - const arr2 = arr[i].split('='); - if (arr2[0] === this.getKey(name)) { - return arr2[1]; - } - } - return ''; - } - - /** - * 鏍规嵁cookie鍚嶅瓧鍒犻櫎cookie - * @param name cookie鍚嶅瓧 - */ - removeCookie(key: string) { - this.setCookie(key, 1, -1); - } - - clearCookie(): void { - const keys = document.cookie.match(/[^ =;]+(?==)/g); - if (keys) { - for (let i = keys.length; i--; ) { - document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString(); - } - } - } - }; - return new WebStorage(); -}; diff --git a/src/views/demo/page/form/high/index.vue b/src/views/demo/page/form/high/index.vue index 5568501..ca1f050 100644 --- a/src/views/demo/page/form/high/index.vue +++ b/src/views/demo/page/form/high/index.vue @@ -16,21 +16,23 @@ </a-card> </div> - <app-footer> + <AppFooterToolbar> <template #right> <a-button type="primary" @click="submitAll">鎻愪氦</a-button> </template> - </app-footer> + </AppFooterToolbar> </div> </template> <script lang="ts"> import { BasicForm, useForm } from '/@/components/Form'; import { defineComponent, ref } from 'vue'; import PersonTable from './PersonTable.vue'; + import { AppFooterToolbar } from '/@/components/Application'; + import { schemas, taskSchemas } from './data'; export default defineComponent({ - components: { BasicForm, PersonTable }, + components: { BasicForm, PersonTable, AppFooterToolbar }, setup() { const tableRef = ref<{ getDataSource: () => any } | null>(null); diff --git a/yarn.lock b/yarn.lock index 6ea3543..8147c93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1050,10 +1050,10 @@ resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw== -"@iconify/json@^1.1.261": - version "1.1.261" - resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.261.tgz#9a6986b6b36d77ca147c4be149db9a43280a8fb2" - integrity sha512-lnRk1OBqNxZ593oZyOXEMp/O+cr+lF54xaW6+F3krWdWhzxQgi0W1ffzvdiLySdbTEorQ2NvVU4e0+Af27rXPA== +"@iconify/json@^1.1.262": + version "1.1.262" + resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.262.tgz#a67067bad418d59c729ec514e3aa629d0ab3710b" + integrity sha512-PfKUS/Ue9Rn2oO0ez/Yj4Cdodvv6vDHgPjIZNSElu2+149CYaPmWahHI87ZY+r8l3bijPIu6+blyAixdJtPPMg== "@koa/cors@^3.1.0": version "3.1.0" @@ -3418,10 +3418,10 @@ resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.13.0: - version "7.13.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz#7f180126c0dcdef327bfb54b211d7802decc08da" - integrity sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ== +eslint@^7.14.0: + version "7.14.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz#2d2cac1d28174c510a97b377f122a5507958e344" + integrity sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA== dependencies: "@babel/code-frame" "^7.0.0" "@eslint/eslintrc" "^0.2.1" @@ -3685,6 +3685,11 @@ integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w== dependencies: reusify "^1.0.4" + +fflate@^0.3.8: + version "0.3.10" + resolved "https://registry.npmjs.org/fflate/-/fflate-0.3.10.tgz#0e581839a53203d2eeac7e61ce3652d855e24dcd" + integrity sha512-s5j69APkUPPbzdI20Ix4pPtQP+1Qi58YcFRpE7aO/P1kEywUYjbl2RjZRVEMdnySO9pr4MB0BHPbxkiahrtD/Q== figures@^2.0.0: version "2.0.0" @@ -8196,10 +8201,10 @@ dependencies: source-map "0.6.1" -vue-router@^4.0.0-rc.3: - version "4.0.0-rc.3" - resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.3.tgz#70d18e90030bc6a25e81a30401d673223998ec6b" - integrity sha512-NnPqWIfanEhJC4wu8BEFBmnEDIrx9ST0/HtmBiE+oV2MQlhyRk1TmdttWwVqx6Sh7kONsrI10GQV9l3YEkcWXg== +vue-router@^4.0.0-rc.5: + version "4.0.0-rc.5" + resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.5.tgz#191d32e3d5276641ff21e881d34e33a71dc6e8f0" + integrity sha512-Q8Tt6VGwGMN5qASeIdjSydU3uRADK9AUkqnbnzmTz+zZKS0W6GZOAuP235lf3y5/MqEFSKRJGaTWPEY0t+Rjmg== vue-types@^3.0.0: version "3.0.1" @@ -8480,10 +8485,10 @@ resolved "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== -xlsx@^0.16.8: - version "0.16.8" - resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.16.8.tgz#5546de9b0ba15169b36770d4e43b24790d3ff1b8" - integrity sha512-qWub4YCn0xLEGHI7WWhk6IJ73MDu7sPSJQImxN6/LiI8wsHi0hUhICEDbyqBT+jgFgORZxrii0HvhNSwBNAPoQ== +xlsx@^0.16.9: + version "0.16.9" + resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.16.9.tgz#dacd5bb46bda6dd3743940c9c3dc1e2171826256" + integrity sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw== dependencies: adler-32 "~1.2.0" cfb "^1.1.4" @@ -8491,6 +8496,7 @@ commander "~2.17.1" crc-32 "~1.2.0" exit-on-epipe "~1.0.1" + fflate "^0.3.8" ssf "~0.11.2" wmf "~1.0.1" word "~0.3.0" -- Gitblit v1.8.0