Commit 234c1d1fae6a7f2c78e456f992f91622ca599060

Authored by vben
1 parent 3a132f3f

feat: the cache can be configured to be encrypted

CHANGELOG.zh_CN.md
1 ## Wip 1 ## Wip
2 2
  3 +### ✨ Features
  4 +
  5 +- 缓存可以配置是否加密
  6 +- 多语言支持
  7 +
3 ### 🎫 Chores 8 ### 🎫 Chores
4 9
5 - 移除 messageSetting 配置 10 - 移除 messageSetting 配置
package.json
@@ -37,16 +37,16 @@ @@ -37,16 +37,16 @@
37 "vditor": "^3.6.3", 37 "vditor": "^3.6.3",
38 "vue": "^3.0.2", 38 "vue": "^3.0.2",
39 "vue-i18n": "^9.0.0-beta.8", 39 "vue-i18n": "^9.0.0-beta.8",
40 - "vue-router": "^4.0.0-rc.3", 40 + "vue-router": "^4.0.0-rc.5",
41 "vuex": "^4.0.0-rc.1", 41 "vuex": "^4.0.0-rc.1",
42 "vuex-module-decorators": "^1.0.1", 42 "vuex-module-decorators": "^1.0.1",
43 - "xlsx": "^0.16.8", 43 + "xlsx": "^0.16.9",
44 "zxcvbn": "^4.4.2" 44 "zxcvbn": "^4.4.2"
45 }, 45 },
46 "devDependencies": { 46 "devDependencies": {
47 "@commitlint/cli": "^11.0.0", 47 "@commitlint/cli": "^11.0.0",
48 "@commitlint/config-conventional": "^11.0.0", 48 "@commitlint/config-conventional": "^11.0.0",
49 - "@iconify/json": "^1.1.261", 49 + "@iconify/json": "^1.1.262",
50 "@ls-lint/ls-lint": "^1.9.2", 50 "@ls-lint/ls-lint": "^1.9.2",
51 "@purge-icons/generated": "^0.4.1", 51 "@purge-icons/generated": "^0.4.1",
52 "@types/echarts": "^4.9.1", 52 "@types/echarts": "^4.9.1",
@@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
72 "cross-env": "^7.0.2", 72 "cross-env": "^7.0.2",
73 "dot-prop": "^6.0.1", 73 "dot-prop": "^6.0.1",
74 "dotenv": "^8.2.0", 74 "dotenv": "^8.2.0",
75 - "eslint": "^7.13.0", 75 + "eslint": "^7.14.0",
76 "eslint-config-prettier": "^6.15.0", 76 "eslint-config-prettier": "^6.15.0",
77 "eslint-plugin-prettier": "^3.1.4", 77 "eslint-plugin-prettier": "^3.1.4",
78 "eslint-plugin-vue": "^7.1.0", 78 "eslint-plugin-vue": "^7.1.0",
src/components/Application/index.ts
1 import AppLocalPicker from './src/AppLocalPicker.vue'; 1 import AppLocalPicker from './src/AppLocalPicker.vue';
  2 +import AppFooterToolbar from './src/AppFooterToolbar.vue';
  3 +import { withInstall } from '../util';
2 4
3 -export { AppLocalPicker }; 5 +export { AppLocalPicker, AppFooterToolbar };
  6 +
  7 +export default withInstall(AppLocalPicker, AppFooterToolbar);
src/components/Footer/src/index.vue renamed to src/components/Application/src/AppFooterToolbar.vue
@@ -10,11 +10,14 @@ @@ -10,11 +10,14 @@
10 </template> 10 </template>
11 <script lang="ts"> 11 <script lang="ts">
12 import { defineComponent, computed, unref } from 'vue'; 12 import { defineComponent, computed, unref } from 'vue';
  13 +
13 import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; 14 import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
  15 +
14 import { appStore } from '/@/store/modules/app'; 16 import { appStore } from '/@/store/modules/app';
15 import { menuStore } from '/@/store/modules/menu'; 17 import { menuStore } from '/@/store/modules/menu';
  18 +
16 export default defineComponent({ 19 export default defineComponent({
17 - name: 'AppFooter', 20 + name: 'AppFooterToolbar',
18 setup() { 21 setup() {
19 const getMiniWidth = computed(() => { 22 const getMiniWidth = computed(() => {
20 const { 23 const {
src/components/Authority/index.ts
1 -import type { App } from 'vue';  
2 import Authority from './src/index.vue'; 1 import Authority from './src/index.vue';
3 2
4 -export default (app: App): void => {  
5 - app.component(Authority.name, Authority);  
6 -}; 3 +import { withInstall } from '../util';
  4 +
  5 +export default withInstall(Authority);
7 6
8 export { Authority }; 7 export { Authority };
src/components/Footer/index.ts deleted 100644 → 0
1 -export { default as AppFooter } from './src/index.vue';  
src/components/registerGlobComp.ts
1 import Icon from './Icon/index'; 1 import Icon from './Icon/index';
2 import Button from './Button/index.vue'; 2 import Button from './Button/index.vue';
3 -import { AppFooter } from './Footer';  
4 import { 3 import {
5 // Need 4 // Need
6 Button as AntButton, 5 Button as AntButton,
@@ -35,7 +34,7 @@ import { @@ -35,7 +34,7 @@ import {
35 } from 'ant-design-vue'; 34 } from 'ant-design-vue';
36 import { getApp } from '/@/setup/App'; 35 import { getApp } from '/@/setup/App';
37 36
38 -const compList = [Icon, Button, AntButton.Group, AppFooter]; 37 +const compList = [Icon, Button, AntButton.Group];
39 38
40 // Fix hmr multiple registered components 39 // Fix hmr multiple registered components
41 let registered = false; 40 let registered = false;
src/components/util.ts
1 import type { VNodeChild } from 'vue'; 1 import type { VNodeChild } from 'vue';
  2 +import type { App, Component } from 'vue';
  3 +
  4 +export function withInstall(...components: Component[]) {
  5 + return (app: App) => {
  6 + components.forEach((comp) => {
  7 + comp.name && app.component(comp.name, comp);
  8 + });
  9 + };
  10 +}
2 11
3 export function convertToUnit( 12 export function convertToUnit(
4 str: string | number | null | undefined, 13 str: string | number | null | undefined,
src/settings/cipherSetting.ts renamed to src/settings/encryptionSetting.ts
  1 +import { isDevMode } from '/@/utils/env';
  2 +
1 // System default cache time, in seconds 3 // System default cache time, in seconds
2 export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; 4 export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
3 5
4 -/**  
5 - * @description:  
6 - */  
7 -export const storageCipher = { 6 +// aes encryption key
  7 +export const cacheCipher = {
8 key: '_12345678901234@', 8 key: '_12345678901234@',
9 iv: '@12345678901234_', 9 iv: '@12345678901234_',
10 }; 10 };
  11 +
  12 +// Whether the system cache is encrypted using aes
  13 +export const enableStorageEncryption = !isDevMode();
src/utils/cache/cookie.ts 0 → 100644
  1 +import { DEFAULT_CACHE_TIME } from '../../settings/encryptionSetting';
  2 +import { getStorageShortName } from '/@/utils/helper/envHelper';
  3 +import { cacheCipher } from '/@/settings/encryptionSetting';
  4 +import Encryption from '/@/utils/encryption/aesEncryption';
  5 +
  6 +export default class WebCookie {
  7 + private encryption: Encryption;
  8 + private hasEncrypt: boolean;
  9 +
  10 + constructor(hasEncrypt = true, key = cacheCipher.key, iv = cacheCipher.iv) {
  11 + const encryption = new Encryption({ key, iv });
  12 + this.encryption = encryption;
  13 + this.hasEncrypt = hasEncrypt;
  14 + }
  15 +
  16 + private getKey(key: string) {
  17 + return `${getStorageShortName()}${key}`.toUpperCase();
  18 + }
  19 +
  20 + /**
  21 + * Add cookie
  22 + * @param name cookie key
  23 + * @param value cookie value
  24 + * @param expire
  25 + * If the expiration time is not set, the default management browser will automatically delete
  26 + * e.g:
  27 + * cookieData.set('name','value',)
  28 + */
  29 + setCookie(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
  30 + value = this.hasEncrypt ? this.encryption.encryptByAES(JSON.stringify(value)) : value;
  31 + document.cookie = this.getKey(key) + '=' + value + '; Max-Age=' + expire;
  32 + }
  33 +
  34 + /**
  35 + * Get the cook value according to the key
  36 + * @param key cookie key
  37 + */
  38 + getCookie(key: string) {
  39 + const arr = document.cookie.split('; ');
  40 + for (let i = 0; i < arr.length; i++) {
  41 + const arr2 = arr[i].split('=');
  42 + if (arr2[0] === this.getKey(key)) {
  43 + let message: any = null;
  44 + const str = arr2[1];
  45 + if (this.hasEncrypt && str) {
  46 + message = this.encryption.decryptByAES(str);
  47 + try {
  48 + return JSON.parse(message);
  49 + } catch (e) {
  50 + return str;
  51 + }
  52 + }
  53 + return str;
  54 + }
  55 + }
  56 + return '';
  57 + }
  58 +
  59 + /**
  60 + * Delete cookie based on cookie key
  61 + * @param key cookie key
  62 + */
  63 + removeCookie(key: string) {
  64 + this.setCookie(key, 1, -1);
  65 + }
  66 +
  67 + /**
  68 + * clear cookie
  69 + */
  70 + clearCookie(): void {
  71 + const keys = document.cookie.match(/[^ =;]+(?==)/g);
  72 + if (keys) {
  73 + for (let i = keys.length; i--; ) {
  74 + document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString();
  75 + }
  76 + }
  77 + }
  78 +}
src/utils/storage/index.ts renamed to src/utils/cache/index.ts
1 import { getStorageShortName } from '/@/utils/helper/envHelper'; 1 import { getStorageShortName } from '/@/utils/helper/envHelper';
2 -import { createStorage as create } from './Storage';  
3 -  
4 -// debug模式下不加密 2 +import { createStorage as create } from './storageCache';
  3 +import { enableStorageEncryption } from '/@/settings/encryptionSetting';
5 4
6 const createOptions = (storage = sessionStorage) => { 5 const createOptions = (storage = sessionStorage) => {
7 return { 6 return {
  7 + // No encryption in debug mode
  8 + hasEncrypt: enableStorageEncryption,
8 storage, 9 storage,
9 prefixKey: getStorageShortName(), 10 prefixKey: getStorageShortName(),
10 }; 11 };
11 }; 12 };
  13 +
12 export const WebStorage = create(createOptions()); 14 export const WebStorage = create(createOptions());
13 15
14 export const createStorage = (storage: Storage = sessionStorage) => { 16 export const createStorage = (storage: Storage = sessionStorage) => {
15 return create(createOptions(storage))!; 17 return create(createOptions(storage))!;
16 }; 18 };
  19 +
17 export default WebStorage; 20 export default WebStorage;
src/utils/storage/Storage.ts renamed to src/utils/cache/storageCache.ts
1 -import { DEFAULT_CACHE_TIME } from '/@/settings/cipherSetting'; 1 +import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
  2 +import { cacheCipher } from '/@/settings/encryptionSetting';
  3 +import Encryption, { EncryptionParams } from '/@/utils/encryption/aesEncryption';
2 4
3 -// import { EncryptionParams } from '/@/utils/cipher/aesEncryption';  
4 -export interface CreateStorageParams { 5 +export interface CreateStorageParams extends EncryptionParams {
5 storage: Storage; 6 storage: Storage;
  7 +
6 hasEncrypt: boolean; 8 hasEncrypt: boolean;
7 } 9 }
8 -export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) => { 10 +export const createStorage = ({
  11 + prefixKey = '',
  12 + storage = sessionStorage,
  13 + key = cacheCipher.key,
  14 + iv = cacheCipher.iv,
  15 + hasEncrypt = true,
  16 +} = {}) => {
  17 + if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
  18 + throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
  19 + }
  20 +
  21 + const encryption = new Encryption({ key, iv });
  22 +
9 /** 23 /**
10 - *缓存类  
11 - *构造参数可以传入 sessionStorage,localStorage, 24 + *Cache class
  25 + *Construction parameters can be passed into sessionStorage, localStorage,
12 * @class Cache 26 * @class Cache
13 * @example 27 * @example
14 */ 28 */
15 const WebStorage = class WebStorage { 29 const WebStorage = class WebStorage {
16 private storage: Storage; 30 private storage: Storage;
17 private prefixKey?: string; 31 private prefixKey?: string;
18 - 32 + private encryption: Encryption;
  33 + private hasEncrypt: boolean;
19 /** 34 /**
20 * 35 *
21 * @param {*} storage 36 * @param {*} storage
@@ -23,6 +38,8 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {}) @@ -23,6 +38,8 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {})
23 constructor() { 38 constructor() {
24 this.storage = storage; 39 this.storage = storage;
25 this.prefixKey = prefixKey; 40 this.prefixKey = prefixKey;
  41 + this.encryption = encryption;
  42 + this.hasEncrypt = hasEncrypt;
26 } 43 }
27 44
28 private getKey(key: string) { 45 private getKey(key: string) {
@@ -31,10 +48,10 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {}) @@ -31,10 +48,10 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {})
31 48
32 /** 49 /**
33 * 50 *
34 - * 设置缓存  
35 - * @param {string} key 缓存键  
36 - * @param {*} value 缓存值  
37 - * @expire 过期时间 单位秒 51 + * Set cache
  52 + * @param {string} key
  53 + * @param {*} value
  54 + * @expire Expiration time in seconds
38 * @memberof Cache 55 * @memberof Cache
39 */ 56 */
40 set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { 57 set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
@@ -42,21 +59,23 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {}) @@ -42,21 +59,23 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {})
42 value, 59 value,
43 expire: expire !== null ? new Date().getTime() + expire * 1000 : null, 60 expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
44 }); 61 });
45 - this.storage.setItem(this.getKey(key), stringData); 62 + const stringifyValue = this.hasEncrypt
  63 + ? this.encryption.encryptByAES(stringData)
  64 + : stringData;
  65 + this.storage.setItem(this.getKey(key), stringifyValue);
46 } 66 }
47 67
48 /** 68 /**
49 - *  
50 - *读取缓存  
51 - * @param {string} key 缓存键  
52 - * @returns 缓存值 69 + *Read cache
  70 + * @param {string} key
53 * @memberof Cache 71 * @memberof Cache
54 */ 72 */
55 get(key: string, def: any = null): any { 73 get(key: string, def: any = null): any {
56 const item = this.storage.getItem(this.getKey(key)); 74 const item = this.storage.getItem(this.getKey(key));
57 if (item) { 75 if (item) {
58 try { 76 try {
59 - const data = JSON.parse(item); 77 + const decItem = this.hasEncrypt ? this.encryption.decryptByAES(item) : item;
  78 + const data = JSON.parse(decItem);
60 const { value, expire } = data; 79 const { value, expire } = data;
61 if (expire === null || expire >= new Date().getTime()) { 80 if (expire === null || expire >= new Date().getTime()) {
62 return value; 81 return value;
@@ -70,9 +89,8 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {}) @@ -70,9 +89,8 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {})
70 } 89 }
71 90
72 /** 91 /**
73 - *  
74 - *删除缓存  
75 - * @param {string} key 缓存键 92 + * Delete cache based on key
  93 + * @param {string} key
76 * @memberof Cache 94 * @memberof Cache
77 */ 95 */
78 remove(key: string) { 96 remove(key: string) {
@@ -80,59 +98,11 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {}) @@ -80,59 +98,11 @@ export const createStorage = ({ prefixKey = &#39;&#39;, storage = sessionStorage } = {})
80 } 98 }
81 99
82 /** 100 /**
83 - *  
84 - *删除该实例所有缓存  
85 - * @memberof Cache 101 + * Delete all caches of this instance
86 */ 102 */
87 clear(): void { 103 clear(): void {
88 this.storage.clear(); 104 this.storage.clear();
89 } 105 }
90 -  
91 - /**  
92 - * 添加cookie  
93 - * @param name cookie名字  
94 - * @param value cookie内容  
95 - * @param expire  
96 - * 如果过期时间未设置,默认管理浏览器自动删除  
97 - * 例子:  
98 - * cookieData.set('name','value',)  
99 - */  
100 - setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {  
101 - document.cookie = this.getKey(name) + '=' + value + '; Max-Age=' + expire;  
102 - }  
103 -  
104 - /**  
105 - * 根据名字获取cooki值  
106 - * @param name cookie名  
107 - * @returns {*} cookie值  
108 - */  
109 - getCookie(name: string) {  
110 - const arr = document.cookie.split('; ');  
111 - for (let i = 0; i < arr.length; i++) {  
112 - const arr2 = arr[i].split('=');  
113 - if (arr2[0] === this.getKey(name)) {  
114 - return arr2[1];  
115 - }  
116 - }  
117 - return '';  
118 - }  
119 -  
120 - /**  
121 - * 根据cookie名字删除cookie  
122 - * @param name cookie名字  
123 - */  
124 - removeCookie(key: string) {  
125 - this.setCookie(key, 1, -1);  
126 - }  
127 -  
128 - clearCookie(): void {  
129 - const keys = document.cookie.match(/[^ =;]+(?==)/g);  
130 - if (keys) {  
131 - for (let i = keys.length; i--; ) {  
132 - document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString();  
133 - }  
134 - }  
135 - }  
136 }; 106 };
137 return new WebStorage(); 107 return new WebStorage();
138 }; 108 };
src/utils/encryption/aesEncryption.ts 0 → 100644
  1 +import CryptoES from 'crypto-es';
  2 +export interface EncryptionParams {
  3 + key: string;
  4 + iv: string;
  5 +}
  6 +export class Encryption {
  7 + private key;
  8 +
  9 + private iv;
  10 +
  11 + constructor(opt: EncryptionParams) {
  12 + const { key, iv } = opt;
  13 + this.key = CryptoES.enc.Utf8.parse(key);
  14 + this.iv = CryptoES.enc.Utf8.parse(iv);
  15 + }
  16 +
  17 + get getOpt(): CryptoES.lib.CipherCfg {
  18 + return {
  19 + mode: CryptoES.mode.CBC as any,
  20 + padding: CryptoES.pad.Pkcs7,
  21 + iv: this.iv,
  22 + };
  23 + }
  24 +
  25 + encryptByAES(str: string) {
  26 + const encrypted = CryptoES.AES.encrypt(str, this.key, this.getOpt);
  27 + return encrypted.toString();
  28 + }
  29 +
  30 + decryptByAES(str: string) {
  31 + const decrypted = CryptoES.AES.decrypt(str, this.key, this.getOpt);
  32 + return decrypted.toString(CryptoES.enc.Utf8);
  33 + }
  34 +}
  35 +export default Encryption;
src/utils/file/base64.ts renamed to src/utils/file/base64Conver.ts
src/utils/file/download.ts
1 -import { dataURLtoBlob, urlToBase64 } from './base64'; 1 +import { dataURLtoBlob, urlToBase64 } from './base64Conver';
2 2
3 /** 3 /**
4 * Download online pictures 4 * Download online pictures
src/utils/helper/persistent.ts
1 -import { createStorage } from '/@/utils/storage'; 1 +import { createStorage } from '/@/utils/cache';
2 import { isIeFn } from '/@/utils/browser'; 2 import { isIeFn } from '/@/utils/browser';
3 3
4 import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum'; 4 import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum';
src/views/demo/page/form/high/index.vue
@@ -16,21 +16,23 @@ @@ -16,21 +16,23 @@
16 </a-card> 16 </a-card>
17 </div> 17 </div>
18 18
19 - <app-footer> 19 + <AppFooterToolbar>
20 <template #right> 20 <template #right>
21 <a-button type="primary" @click="submitAll">提交</a-button> 21 <a-button type="primary" @click="submitAll">提交</a-button>
22 </template> 22 </template>
23 - </app-footer> 23 + </AppFooterToolbar>
24 </div> 24 </div>
25 </template> 25 </template>
26 <script lang="ts"> 26 <script lang="ts">
27 import { BasicForm, useForm } from '/@/components/Form'; 27 import { BasicForm, useForm } from '/@/components/Form';
28 import { defineComponent, ref } from 'vue'; 28 import { defineComponent, ref } from 'vue';
29 import PersonTable from './PersonTable.vue'; 29 import PersonTable from './PersonTable.vue';
  30 + import { AppFooterToolbar } from '/@/components/Application';
  31 +
30 import { schemas, taskSchemas } from './data'; 32 import { schemas, taskSchemas } from './data';
31 33
32 export default defineComponent({ 34 export default defineComponent({
33 - components: { BasicForm, PersonTable }, 35 + components: { BasicForm, PersonTable, AppFooterToolbar },
34 setup() { 36 setup() {
35 const tableRef = ref<{ getDataSource: () => any } | null>(null); 37 const tableRef = ref<{ getDataSource: () => any } | null>(null);
36 38
yarn.lock
@@ -1050,10 +1050,10 @@ @@ -1050,10 +1050,10 @@
1050 resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" 1050 resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b"
1051 integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw== 1051 integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw==
1052 1052
1053 -"@iconify/json@^1.1.261":  
1054 - version "1.1.261"  
1055 - resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.261.tgz#9a6986b6b36d77ca147c4be149db9a43280a8fb2"  
1056 - integrity sha512-lnRk1OBqNxZ593oZyOXEMp/O+cr+lF54xaW6+F3krWdWhzxQgi0W1ffzvdiLySdbTEorQ2NvVU4e0+Af27rXPA== 1053 +"@iconify/json@^1.1.262":
  1054 + version "1.1.262"
  1055 + resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.262.tgz#a67067bad418d59c729ec514e3aa629d0ab3710b"
  1056 + integrity sha512-PfKUS/Ue9Rn2oO0ez/Yj4Cdodvv6vDHgPjIZNSElu2+149CYaPmWahHI87ZY+r8l3bijPIu6+blyAixdJtPPMg==
1057 1057
1058 "@koa/cors@^3.1.0": 1058 "@koa/cors@^3.1.0":
1059 version "3.1.0" 1059 version "3.1.0"
@@ -3418,10 +3418,10 @@ eslint-visitor-keys@^2.0.0: @@ -3418,10 +3418,10 @@ eslint-visitor-keys@^2.0.0:
3418 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" 3418 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
3419 integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== 3419 integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
3420 3420
3421 -eslint@^7.13.0:  
3422 - version "7.13.0"  
3423 - resolved "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz#7f180126c0dcdef327bfb54b211d7802decc08da"  
3424 - integrity sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ== 3421 +eslint@^7.14.0:
  3422 + version "7.14.0"
  3423 + resolved "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz#2d2cac1d28174c510a97b377f122a5507958e344"
  3424 + integrity sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==
3425 dependencies: 3425 dependencies:
3426 "@babel/code-frame" "^7.0.0" 3426 "@babel/code-frame" "^7.0.0"
3427 "@eslint/eslintrc" "^0.2.1" 3427 "@eslint/eslintrc" "^0.2.1"
@@ -3686,6 +3686,11 @@ fastq@^1.6.0: @@ -3686,6 +3686,11 @@ fastq@^1.6.0:
3686 dependencies: 3686 dependencies:
3687 reusify "^1.0.4" 3687 reusify "^1.0.4"
3688 3688
  3689 +fflate@^0.3.8:
  3690 + version "0.3.10"
  3691 + resolved "https://registry.npmjs.org/fflate/-/fflate-0.3.10.tgz#0e581839a53203d2eeac7e61ce3652d855e24dcd"
  3692 + integrity sha512-s5j69APkUPPbzdI20Ix4pPtQP+1Qi58YcFRpE7aO/P1kEywUYjbl2RjZRVEMdnySO9pr4MB0BHPbxkiahrtD/Q==
  3693 +
3689 figures@^2.0.0: 3694 figures@^2.0.0:
3690 version "2.0.0" 3695 version "2.0.0"
3691 resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 3696 resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
@@ -8196,10 +8201,10 @@ vue-i18n@^9.0.0-beta.8: @@ -8196,10 +8201,10 @@ vue-i18n@^9.0.0-beta.8:
8196 dependencies: 8201 dependencies:
8197 source-map "0.6.1" 8202 source-map "0.6.1"
8198 8203
8199 -vue-router@^4.0.0-rc.3:  
8200 - version "4.0.0-rc.3"  
8201 - resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.3.tgz#70d18e90030bc6a25e81a30401d673223998ec6b"  
8202 - integrity sha512-NnPqWIfanEhJC4wu8BEFBmnEDIrx9ST0/HtmBiE+oV2MQlhyRk1TmdttWwVqx6Sh7kONsrI10GQV9l3YEkcWXg== 8204 +vue-router@^4.0.0-rc.5:
  8205 + version "4.0.0-rc.5"
  8206 + resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.5.tgz#191d32e3d5276641ff21e881d34e33a71dc6e8f0"
  8207 + integrity sha512-Q8Tt6VGwGMN5qASeIdjSydU3uRADK9AUkqnbnzmTz+zZKS0W6GZOAuP235lf3y5/MqEFSKRJGaTWPEY0t+Rjmg==
8203 8208
8204 vue-types@^3.0.0: 8209 vue-types@^3.0.0:
8205 version "3.0.1" 8210 version "3.0.1"
@@ -8480,10 +8485,10 @@ ws@^7.3.1: @@ -8480,10 +8485,10 @@ ws@^7.3.1:
8480 resolved "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" 8485 resolved "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7"
8481 integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== 8486 integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==
8482 8487
8483 -xlsx@^0.16.8:  
8484 - version "0.16.8"  
8485 - resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.16.8.tgz#5546de9b0ba15169b36770d4e43b24790d3ff1b8"  
8486 - integrity sha512-qWub4YCn0xLEGHI7WWhk6IJ73MDu7sPSJQImxN6/LiI8wsHi0hUhICEDbyqBT+jgFgORZxrii0HvhNSwBNAPoQ== 8488 +xlsx@^0.16.9:
  8489 + version "0.16.9"
  8490 + resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.16.9.tgz#dacd5bb46bda6dd3743940c9c3dc1e2171826256"
  8491 + integrity sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw==
8487 dependencies: 8492 dependencies:
8488 adler-32 "~1.2.0" 8493 adler-32 "~1.2.0"
8489 cfb "^1.1.4" 8494 cfb "^1.1.4"
@@ -8491,6 +8496,7 @@ xlsx@^0.16.8: @@ -8491,6 +8496,7 @@ xlsx@^0.16.8:
8491 commander "~2.17.1" 8496 commander "~2.17.1"
8492 crc-32 "~1.2.0" 8497 crc-32 "~1.2.0"
8493 exit-on-epipe "~1.0.1" 8498 exit-on-epipe "~1.0.1"
  8499 + fflate "^0.3.8"
8494 ssf "~0.11.2" 8500 ssf "~0.11.2"
8495 wmf "~1.0.1" 8501 wmf "~1.0.1"
8496 word "~0.3.0" 8502 word "~0.3.0"