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