├── .gitattributes ├── .gitignore ├── LICENSE ├── README-kor.md ├── README.md ├── index.js ├── package.json └── plugins ├── bindStorage ├── expire.js ├── index.js └── storageFunction │ ├── cookie.js │ ├── index.js │ └── webStorage.js ├── crypto.js └── main.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 RubystarAshe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README-kor.md: -------------------------------------------------------------------------------- 1 | # nuxt-vuex-localstorage 2 | nuxt의 vuex와 webStorage를 연결하여 localStorage와 sessionStorage를 보다 더 쉽고 유용하게 사용 할 수 있습니다. 3 | 다른 persist플러그인들과는 다르게 webStorage를 vuex에 일부 공간만 할당하여, webStorage의 낭비를 줄이고 기존 vuex의 활용과 병용할 수 있도록 하였습니다. 4 | 5 | 여러 개의 브라우저 탭에서 localStorage를 통한 데이터 바인딩을 매우 쉽게 관리할 수 있습니다! 6 | ![Alt Text](https://i.imgur.com/5DAh6tT.gif) 7 | 8 | 강력한 암호화 기능으로 다양한 webStorage 보안 기능을 제공합니다. 9 | webStorage에서 지원하지 않는 expire설정을 추가로 지원합니다. 10 | Safari 개인정보보호모드 등 webStorage가 지원되지 않는 환경을 위하여 cookie모드가 자동 지원됩니다. 11 | 12 | 일렉트론에서도 잘 작동합니다! 13 | 14 | # 예제 15 | 16 | 17 | # 설치 18 | ``` 19 | npm i nuxt-vuex-localstorage 20 | ``` 21 | 22 | # Default(Auto) mode 23 | 가장 기본적인 로컬스토리지 암호화를 자동적으로 수행하는 기본 모드입니다 24 | ```js 25 | // nuxt.config.js 26 | module.exports = { 27 | modules: [ 28 | 'nuxt-vuex-localstorage' 29 | ] 30 | } 31 | ``` 32 | 33 | nuxt의 vuex 스토어에 localStorage나 sessionStorage 를 추가하여 쉽게 사용, 관리할 수 있습니다. 34 | ```js 35 | // store/localStorage.js 36 | export const state = () => ({ 37 | anyValues: 0 38 | }) 39 | 40 | // store/sessionStorage.js 41 | export const state = () => ({ 42 | anyValues: 0 43 | }) 44 | ``` 45 | 46 | $localStorageLoaded 와 $sessionStorageLoaded 또는 localStorage.status 와 sessionStorage.status 상태를 활용할 수 있습니다. 47 | ```html 48 | 56 | 57 | 66 | ``` 67 | 68 | 스토리지 스토어 이름 변경 및 여러개의 스토어를 스토리지에 저장하는 방법 69 | ```js 70 | // nuxt.config.js 71 | module.exports = { 72 | modules: [ 73 | ['nuxt-vuex-localstorage', { 74 | localStorage: ['foo', 'bar'], // 설정하지 않을 경우 'localStorage' 가 기본값으로 설정됩니다 75 | sessionStorage: ['sfoo', 'sbar'] // 설정하지 않을 경우 'sessionStorage' 가 기본값으로 설정됩니다 76 | }] 77 | ] 78 | } 79 | 80 | // store/index.js 81 | export const state = () => ({ 82 | foo: { 83 | anyValues: 0 84 | }, 85 | bar: { 86 | anyValues: 0 87 | }, 88 | sfoo: { 89 | anyValues: 0 90 | }, 91 | sbar: { 92 | anyValues: 0 93 | } 94 | }) 95 | ``` 96 | 97 | # API mode 98 | 아래와 같이 모듈 옵션을 통해 API 주소와 키 이름을 부여하면, 해당 데이터를 불러와 암호화 키 값에 추가합니다. 99 | 일반적인 사용법은 Default 모드와 동일합니다. 100 | ```js 101 | // nuxt.config.js 102 | module.exports = { 103 | modules: [ 104 | ['nuxt-vuex-localstorage', { 105 | mode: 'api', 106 | api: 'https://ipinfo.io', // 설정하지 않을 경우 기본값으로 이 값이 설정됩니다 107 | keyName: 'ip', // 설정하지 않을 경우 기본값으로 이 값이 설정됩니다 108 | saltName: 'region' // 설정하지 않을 경우 기본값으로 이 값이 설정됩니다 109 | }] 110 | ] 111 | } 112 | ``` 113 | 114 | # Manual mode 115 | WebStorage와 자동으로 연결하지 않고, 수동 이벤트를 통해 키를 설정하여 WebStorage를 연결할 수 있습니다. 116 | ```js 117 | // nuxt.config.js 118 | module.exports = { 119 | modules: [ 120 | ['nuxt-vuex-localstorage', { 121 | mode: 'manual' 122 | }] 123 | ] 124 | } 125 | ``` 126 | 먼저, 사용하실 WebStorage 의 store 파일에 status 상태를 추가하세요 127 | ```js 128 | // store/localStorage.js 또는 store/sessionStorage.js 129 | export const state = () => ({ 130 | ... 131 | status: false 132 | }) 133 | ``` 134 | 그런다음, 원하는 때에 WebStorage를 연결하세요! status상태를 true로 변경하는 것 만으로 즉시 연결됩니다. 135 | $setWebStorageKey 메소드를 사용하여 암호화 키를 설정한 다음 연결하실 수도 있습니다. 136 | ```html 137 | 149 | ``` 150 | 151 | # 추가적인 보안 옵션 152 | ```js 153 | // nuxt.config.js 154 | module.exports = { 155 | modules: [ 156 | ['nuxt-vuex-localstorage', { 157 | ... 158 | keyMixTimes: 64, // 해시 함수의 반복 회수를 설정합니다. 기본값 64 159 | KeyLength: 64 // 다이제스트 길이를 설정합니다. 기본값 64 160 | }] 161 | ] 162 | } 163 | ``` 164 | 165 | # 만료 시간 설정 기능 166 | Expire 값을 설정하는 것으로 각각의 값들의 만료 시간을 설정할 수 있습니다. 167 | Safari 개인정보보호모드 등 webStorage대신 cookie를 사용하는 환경의 경우에도 같은 방식으로 작동됩니다. 168 | cookie방식으로 동작하는 환경에서는 전체 데이터의 만료 시간이 24시간을 기본값으로 설정됩니다. 169 | ```js 170 | export const state = () => ({ 171 | test: { 172 | foo: 'foo', 173 | bar: 'bar', 174 | expire: 12 // 숫자 1당 1시간으로 자동 계산됩니다. 175 | } 176 | }) 177 | ``` 178 | 해당 값으로 생성된 만료 시간은 String으로 치환된 Date형식으로 저장됩니다. 179 | 180 | # 스토리지된 스토어의 버전 관리 181 | 해당 스토어에 버전 항목을 추가하는 것으로 손쉽게 버전을 관리할 수 있습니다. 버전이 변경되면 새로고침 시 해당 스토어의 값으로 스토리지의 값이 초기화됩니다. 182 | ```js 183 | // store/foo.js 184 | export const state = () => ({ 185 | bar: 0, 186 | version: 1 // 버전은 숫자일 필요가 없습니다 187 | }) 188 | ``` 189 | 옵션을 통해 버전 속성의 이름을 변경하여 사용할 수도 있습니다. 190 | ```js 191 | // nuxt.config.js 192 | module.exports = { 193 | modules: [ 194 | ['nuxt-vuex-localstorage', { 195 | ... 196 | versionPropName: 'storageVersion' // 지정하지 않을 경우 기본값은 'version' 197 | }] 198 | ] 199 | } 200 | 201 | // store/foo.js 202 | export const state = () => ({ 203 | bar: 0, 204 | storageVersion: 1 205 | }) 206 | ``` 207 | 208 | # Server side event를 연계한 사용 209 | store의 변경에 따라 localStorage를 즉시 연동하기 때문에, 컴포넌트가 mounted되지 않더라도 접근할 수 있어 fetch, asyncData와 같은 Server Side와 통신하는 단계의 이벤트에서도 localStorage기능을 매끄럽게 사용가능합니다. 210 | ```html 211 | 221 | ``` 222 | 223 | # IE 에서의 사용 224 | ```js 225 | // nuxt.config.js 226 | module.exports = { 227 | ... 228 | build: { 229 | transpile: [ 230 | 'nuxt-vuex-localstorage' 231 | ], 232 | ... 233 | } 234 | } 235 | ``` 236 | 237 | # webStorage가 지원되지 않는 환경에서의 polyfill 238 | Safari 개인정보보호모드 등 webStorage가 지원되지 않는 환경에서는 자동으로 cookie모드를 통해 실행됩니다. 239 | 또한, store데이터가 cookie용량을 낭비하지 않게끔 하기 위해 브라우저가 로딩될때와 종료되기 전에만 동기화 합니다. 240 | 따라서 cookie모드를 사용하더라도 앱이 활성화된 동안에는 cookie가 store데이터를 갖고있지 않기 때문에 더 가볍게 작동합니다. 241 | cookie모드의 경우 24시간의 환기 주기를 갖고 있습니다. 따라서 24시간 안에 cookie모드로 앱을 한번 이상 활성화시키지 않으면 해당 데이터는 초기화됩니다. 242 | 243 | 244 | # Cookie 모드 강제 사용 245 | 브라우저 스토리지를 사용하지 않고 Cookie 를 사용하여 동일한 기능을 구현하려면, 다음과 같이 이용하시면 됩니다. 246 | ```js 247 | // nuxt.config.js 248 | module.exports = { 249 | modules: [ 250 | ['nuxt-vuex-localstorage', { 251 | mode: 'cookie', 252 | expireHours: 24 // 지정하지 않을 경우 기본값은 24 253 | }] 254 | ] 255 | } 256 | ``` 257 | 이 경우, sessionStorage 는 localStorage 처럼 이용되며, 창을 닫더라도 휘발되지 않습니다. 258 | 몇가지 보안 문제와 네트워크 비용 감소를 위하여 쿠키 정보는 사이트가 로딩되거나 종료될 때에만 관리되고, 사이트를 이용 중일때에는 비워집니다. 259 | 260 | 261 | # 디버깅 모드 262 | 디버깅을 위해 웹스토리지 암호화 기능을 비활성화합니다. 이 기능은 암호화 기능을 사용하지 않는 트리거로 활용되어선 안됩니다. 263 | ```js 264 | // nuxt.config.js 265 | module.exports = { 266 | modules: [ 267 | ['nuxt-vuex-localstorage', { 268 | mode: 'debug' 269 | }] 270 | ] 271 | } 272 | ``` 273 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nuxt-vuex-localstorage 2 | Make use of local and session storage more efficiently by connecting Vuex and Web storage. Different from other persist plugins, by allocating just some particular objects of Vuex, Web storage can save more space and it can also be used with existing Vuex usage. 3 | 4 | Data binding through local storage can be easily managed in multiple browser tabs. 5 | ![Alt Text](https://i.imgur.com/5DAh6tT.gif) 6 | 7 | It provides various web storage security systems since it has strong data encrypting functionality. 8 | It provides ‘expireʼ function which is not supported on web storage. 9 | It supports “cookie mode” for some environment on which web storage is not supported, such as ‘Safari private modeʼ. 10 | 11 | It works well in electron! 12 | 13 | # Readme Translation 14 | 한국어 링크: 15 | 16 | # Example 17 | 18 | 19 | # Installation 20 | ``` 21 | npm i nuxt-vuex-localstorage 22 | ``` 23 | 24 | # Default(Auto) mode 25 | Most basic step where local storage is encrypted automatically. 26 | ```js 27 | // nuxt.config.js 28 | module.exports = { 29 | modules: [ 30 | 'nuxt-vuex-localstorage' 31 | ] 32 | } 33 | ``` 34 | 35 | It can easily be used or managed by adding local storage and session storage in Vuex store in next. 36 | ```js 37 | // store/localStorage.js 38 | export const state = () => ({ 39 | anyValues: 0 40 | }) 41 | 42 | // store/sessionStorage.js 43 | export const state = () => ({ 44 | anyValues: 0 45 | }) 46 | ``` 47 | 48 | $localStorageLoaded and $sessionStorageLoaded or localStorage.status and sessionStorage.status are in use. 49 | ```html 50 | 58 | 59 | 68 | ``` 69 | 70 | How to store multiple stores on storage and rename storage store 71 | ```js 72 | // nuxt.config.js 73 | module.exports = { 74 | modules: [ 75 | ['nuxt-vuex-localstorage', { 76 | localStorage: ['foo', 'bar'], // If not entered, “localStorage” is the default value 77 | sessionStorage: ['sfoo', 'sbar'] // If not entered, “sessionStorage” is the default value 78 | }] 79 | ] 80 | } 81 | 82 | // store/index.js 83 | export const state = () => ({ 84 | foo: { 85 | anyValues: 0 86 | }, 87 | bar: { 88 | anyValues: 0 89 | }, 90 | sfoo: { 91 | anyValues: 0 92 | }, 93 | sbar: { 94 | anyValues: 0 95 | } 96 | }) 97 | ``` 98 | 99 | # API mode 100 | If API address and key name are assigned by using module option, corresponding data is added to encryption key value. 101 | Basic usage is same as default mode. 102 | ```js 103 | // nuxt.config.js 104 | module.exports = { 105 | modules: [ 106 | ['nuxt-vuex-localstorage', { 107 | mode: 'api', 108 | api: 'https://ipinfo.io', // If not entered, “https://ipinfo.io” is the default value 109 | keyName: 'ip', // If not entered, “ip” is the default value 110 | saltName: 'region' // If not entered, “region” is the default value 111 | }] 112 | ] 113 | } 114 | ``` 115 | 116 | # Manual mode 117 | Besides web storage can be connected automatically, it can also be manually connected by setting key value. 118 | ```js 119 | // nuxt.config.js 120 | module.exports = { 121 | modules: [ 122 | ['nuxt-vuex-localstorage', { 123 | mode: 'manual' 124 | }] 125 | ] 126 | } 127 | ``` 128 | At first, insert status value (whether true or false) in store file of web storage. 129 | ```js 130 | // store/localStorage.js or store/sessionStorage.js 131 | export const state = () => ({ 132 | ... 133 | status: false 134 | }) 135 | ``` 136 | Then, it may sounds obvious, you can connect to web storage by setting status 137 | to true any time you want to connect. 138 | Also, it can be connect after setting encryption key value by using $setWebStorageKey method. 139 | ```html 140 | 152 | ``` 153 | 154 | # Additional security option 155 | ```js 156 | // nuxt.config.js 157 | module.exports = { 158 | modules: [ 159 | ['nuxt-vuex-localstorage', { 160 | ... 161 | keyMixTimes: 64, // number of repetitions of the hash function. Default is set to 64 162 | KeyLength: 64 // length of the digest. Default is set to 64. 163 | }] 164 | ] 165 | } 166 | ``` 167 | 168 | # Setting expiration time functionality 169 | For each values, expiration time can be set by setting expire value. 170 | It also functions in the same way in such environment where cookies are used instead of web storage(i.e. browser secret mode). But in those environment, the data expiration time is set to 24 hours by default. 171 | ```js 172 | export const state = () => ({ 173 | test: { 174 | foo: 'foo', 175 | bar: 'bar', 176 | expire: 12 // 1 = 1 hour, 12 = 12 hours 177 | } 178 | }) 179 | ``` 180 | These time values are converted into string and saved in date format. 181 | 182 | # Version Management 183 | Version can be managed by adding a version prop. When the version is changed, the value of the storage is initialized with the value of the store when the refresh occurs. 184 | ```js 185 | // store/foo.js 186 | export const state = () => ({ 187 | bar: 0, 188 | version: 1 // The version doesn't have to be a number. 189 | }) 190 | ``` 191 | You can also use the option by changing the name of the version property. 192 | ```js 193 | // nuxt.config.js 194 | module.exports = { 195 | modules: [ 196 | ['nuxt-vuex-localstorage', { 197 | ... 198 | versionPropName: 'storageVersion' // If not entered, “version” is the default value 199 | }] 200 | ] 201 | } 202 | 203 | // store/foo.js 204 | export const state = () => ({ 205 | bar: 0, 206 | storageVersion: 1 207 | }) 208 | ``` 209 | 210 | # Usage with server side event 211 | Since local storage updates as store changes, server side function such as fetch, asyncData can be used before components are mounted. 212 | ```html 213 | 223 | ``` 224 | 225 | # IE transpile 226 | ```js 227 | // nuxt.config.js 228 | module.exports = { 229 | ... 230 | build: { 231 | transpile: [ 232 | 'nuxt-vuex-localstorage' 233 | ], 234 | ... 235 | } 236 | } 237 | ``` 238 | 239 | # Polyfill in environment where web storage is not supported 240 | As mentioned before, in such environment, ‘cookie modeʼ will automatically activated. Also, to reduce unnecessary data junk of store data, synchronization only happens when loading or exiting the browser. Therefore, even though cookie mode is activated, cookie doesnʼt contain store data, so it will improve the app performance. 241 | In this mode, it has 24 hours of expiration time, thus if it is not re-activated in 24 hours, the data will reset. 242 | 243 | 244 | # How to force Cookie mode in an environment with browser storage support 245 | If you want to use the same functionality using Cookie without using browser storage, you can try this. 246 | ```js 247 | // nuxt.config.js 248 | module.exports = { 249 | modules: [ 250 | ['nuxt-vuex-localstorage', { 251 | mode: 'cookie', 252 | expireHours: 24 // If not entered, 24 is the default value 253 | }] 254 | ] 255 | } 256 | ``` 257 | In this case, sessionStorage is used like localStorage and does not removed when the window is closed. 258 | For some security issues and reduced network costs, cookie information is managed only when the site is loaded or terminated, and emptied when the site is in use. 259 | 260 | 261 | # Debugging mode 262 | Without encrypting. 263 | ```js 264 | // nuxt.config.js 265 | module.exports = { 266 | modules: [ 267 | ['nuxt-vuex-localstorage', { 268 | mode: 'debug' 269 | }] 270 | ] 271 | } 272 | ``` 273 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import Vue from 'vue' 3 | Vue.prototype.$localStorageLoaded = false 4 | Vue.prototype.$sessionStorageLoaded = false 5 | 6 | export default function nuxtBootstrapVue (moduleOptions) { 7 | this.addPlugin({ 8 | src: path.resolve(__dirname, './plugins/main.js'), 9 | fileName: 'nuxt-vuex-localstorage.js', 10 | ssr: false, 11 | options: moduleOptions 12 | }) 13 | } 14 | 15 | module.exports.meta = require('./package.json') 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-vuex-localstorage", 3 | "version": "1.3.0", 4 | "description": "Nuxt.js localstorage with vuex store! and polyfill", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/rubystarashe/nuxt-vuex-localstorage.git" 12 | }, 13 | "keywords": [ 14 | "vue", 15 | "nuxt", 16 | "vuex", 17 | "localstorage", 18 | "webstorage", 19 | "sessionstorage", 20 | "persist", 21 | "vuex-persist", 22 | "vuex-localstorage" 23 | ], 24 | "author": "Rubystar Ashe (https://github.io/rubystarashe)", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/rubystarashe/nuxt-vuex-localstorage/issues" 28 | }, 29 | "homepage": "https://github.com/rubystarashe/nuxt-vuex-localstorage#readme", 30 | "dependencies": { 31 | "axios": "latest" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /plugins/bindStorage/expire.js: -------------------------------------------------------------------------------- 1 | export default { 2 | check: (val = {}) => { 3 | const date = new Date().getTime() 4 | let copy = eval('(' + JSON.stringify(val || {}) + ')') 5 | Object.keys(copy || {}).forEach((key) => { 6 | try { 7 | const expireDate = new Date(copy[key].___expireDate).getTime() 8 | delete copy[key].___expireDate 9 | if (expireDate < date) delete copy[key] 10 | } catch (e) {} 11 | }) 12 | return copy 13 | }, 14 | create: (val = {}) => { 15 | const date = new Date().getTime() 16 | let copy = eval('(' + JSON.stringify(val || {}) + ')') 17 | Object.keys(copy || {}).forEach((key) => { 18 | if (typeof (copy[key] || {}).expire === 'number') { 19 | const expireDate = date + (copy[key].expire * 60 * 60 * 1000) 20 | copy[key].___expireDate = new Date(expireDate) 21 | } 22 | }) 23 | return copy 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /plugins/bindStorage/index.js: -------------------------------------------------------------------------------- 1 | import Crypto from 'nuxt-vuex-localstorage/plugins/crypto' 2 | import expire from 'nuxt-vuex-localstorage/plugins/bindStorage/expire' 3 | import Vue from 'vue' 4 | Vue.prototype.$localStorageLoaded = false 5 | Vue.prototype.$sessionStorageLoaded = false 6 | 7 | export default async (ctx, options = {}) => { 8 | const storageFunction = require('nuxt-vuex-localstorage/plugins/bindStorage/storageFunction')(options.mode) 9 | if (options.expireHours) storageFunction.setExpire(options.expireHours) 10 | const store = ctx.store 11 | const crypto = await new Crypto(options, ctx) 12 | let localStoreNames = options.localStorage || ['localStorage'] 13 | if (typeof options.localStorage === 'string') localStoreNames = [options.localStorage] 14 | let sessionStoreNames = options.sessionStorage || ['sessionStorage'] 15 | if (typeof options.sessionStorage === 'string') sessionStoreNames = [options.sessionStorage] 16 | const versionPropName = options.versionPropName || 'version' 17 | const storeNames = { 18 | local: localStoreNames, 19 | session: sessionStoreNames 20 | } 21 | 22 | let watchHandlers = { 23 | local: [], 24 | session: [] 25 | } 26 | 27 | const watchFunction = (type, i, val) => { 28 | const data = JSON.stringify(expire.create(val)) 29 | storageFunction[type].set(storeNames[type][i], crypto.encrypt(data)) 30 | } 31 | 32 | const watcher = (type, name, i) => { 33 | return store.watch(state => { return state[name] }, 34 | val => watchFunction(type, i, val), 35 | { deep: true }) 36 | } 37 | 38 | const bindStorage = (type, name, i) => { 39 | const persist = JSON.parse(crypto.decrypt(storageFunction[type].get(name))) 40 | let data = { ...store.state } 41 | const expireChecked = expire.check(persist) 42 | if (store.state[name] && expireChecked[versionPropName] === store.state[name][versionPropName]) 43 | data[name] = { ...data[name], ...expireChecked, status: true } 44 | store.replaceState(data) 45 | 46 | storeNames[type].forEach((name, i) => { 47 | watchHandlers[type][i] = watcher(type, name, i) 48 | }) 49 | if (i == storeNames[type].length - 1) { 50 | if (type == 'local') Vue.prototype.$localStorageLoaded = true 51 | if (type == 'session') Vue.prototype.$sessionStorageLoaded = true 52 | } 53 | } 54 | 55 | const watchOtherBrowsersLocalStorage = () => { 56 | window.addEventListener('storage', (event) => { 57 | if (event && event.storageArea === localStorage && Object.keys(store.state).indexOf(event.key) >= 0) { 58 | let data = { ...store.state } 59 | data[event.key] = expire.check(JSON.parse(crypto.decrypt(event.newValue))) 60 | if (JSON.stringify(data) !== JSON.stringify(store.state)) 61 | store.replaceState(data) 62 | } 63 | }) 64 | } 65 | 66 | 67 | switch (options.mode) { 68 | case 'manual': 69 | watchOtherBrowsersLocalStorage() 70 | Vue.prototype.$setWebStorageKey = (key, salt, keyMixTimes, keyLength) => crypto.setKey(key, salt, keyMixTimes, keyLength) 71 | let localStorageStatusWatchers = [] 72 | storeNames['local'].forEach((name, i) => { 73 | localStorageStatusWatchers.push(store.watch(state => { return state[name].status }, val => { 74 | if (val) { 75 | bindStorage('local', name, i) 76 | localStorageStatusWatchers[i]() 77 | } 78 | }, { deep: true })) 79 | }) 80 | let sessionStorageStatusWatchers = [] 81 | storeNames['session'].forEach((name, i) => { 82 | sessionStorageStatusWatchers.push(store.watch(state => { return state.sessionStorage }, val => { 83 | if (val.status) { 84 | bindStorage('session', name, i) 85 | sessionStorageStatusWatchers[i]() 86 | } 87 | }, { deep: true })) 88 | }) 89 | break 90 | default: 91 | storeNames['local'].forEach((name, i) => { 92 | bindStorage('local', name, i) 93 | watchOtherBrowsersLocalStorage() 94 | }) 95 | storeNames['session'].forEach((name, i) => { 96 | bindStorage('session', name, i) 97 | }) 98 | break 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /plugins/bindStorage/storageFunction/cookie.js: -------------------------------------------------------------------------------- 1 | function getCookie (name) { 2 | let cookie 3 | document.cookie.split(';').some(e => { 4 | const c = e.replace(/^\s+|\s+$/g, '').split('=') 5 | const check = c[0] === name 6 | if (check) cookie = c 7 | return check 8 | }) 9 | return unescape(cookie) 10 | } 11 | 12 | let expireHours = 24 13 | 14 | function setCookie (name, val) { 15 | const cookie_expireDate = expireHours > 0 ? new Date().getTime() + (expireHours * 60 * 60 * 1000) : new Date().getTime() + (24 * 60 * 60 * 1000) 16 | return document.cookie = name + '=' + escape(val) + '; expires=' + new Date(cookie_expireDate).toUTCString() + '; path=/' 17 | } 18 | 19 | let storageTemp = { 20 | localStorage: {}, 21 | sessionStorage: {} 22 | } 23 | 24 | export const setExpire = hours => { 25 | expireHours = hours 26 | } 27 | 28 | export const local = { 29 | get: name => { 30 | try { 31 | const cookie = getCookie(name) 32 | setCookie(name, '', 0) 33 | return cookie.split(',')[1] 34 | } catch (e) { 35 | return '' 36 | } 37 | }, 38 | set: (name, val) => { 39 | storageTemp.localStorage[name] = val 40 | } 41 | } 42 | 43 | export const session = { 44 | get: name => { 45 | try { 46 | const cookie = getCookie(name) 47 | setCookie(name, '', 0) 48 | return cookie.split(',')[1] 49 | } catch (e) { 50 | return '' 51 | } 52 | }, 53 | set: (name, val) => storageTemp.sessionStorage[name] = val 54 | } 55 | 56 | window.addEventListener('beforeunload', function (event) { 57 | Object.keys(storageTemp.localStorage).forEach(key => { 58 | setCookie(key, storageTemp.localStorage[key]) 59 | }) 60 | Object.keys(storageTemp.sessionStorage).forEach(key => { 61 | setCookie(key, storageTemp.sessionStorage[key]) 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /plugins/bindStorage/storageFunction/index.js: -------------------------------------------------------------------------------- 1 | const storageFunction = mode => { 2 | if (process.client) { 3 | try { 4 | if (mode == 'cookie') return require('nuxt-vuex-localstorage/plugins/bindStorage/storageFunction/cookie') 5 | 6 | const storage = { 7 | local: window.localStorage, 8 | session: window.sessionStorage 9 | } 10 | storage.local.setItem('__local_test', 1) 11 | storage.local.removeItem('__local_test') 12 | storage.session.setItem('__session_test', 1) 13 | storage.session.removeItem('__session_test') 14 | 15 | return require('nuxt-vuex-localstorage/plugins/bindStorage/storageFunction/webStorage') 16 | } catch (e) { 17 | return require('nuxt-vuex-localstorage/plugins/bindStorage/storageFunction/cookie') 18 | } 19 | } 20 | } 21 | 22 | module.exports = storageFunction 23 | -------------------------------------------------------------------------------- /plugins/bindStorage/storageFunction/webStorage.js: -------------------------------------------------------------------------------- 1 | let expireHours = 24 2 | export const setExpire = hours => { 3 | expireHours = hours 4 | } 5 | 6 | export const local = { 7 | get: name => { 8 | return localStorage.getItem(name) 9 | }, 10 | set: (name, val) => { 11 | return localStorage.setItem(name, val) 12 | } 13 | } 14 | 15 | export const session = { 16 | get: name => { 17 | return sessionStorage.getItem(name) 18 | }, 19 | set: (name, val) => { 20 | return sessionStorage.setItem(name, val) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /plugins/crypto.js: -------------------------------------------------------------------------------- 1 | import { pbkdf2Sync, createCipher, createDecipher } from 'crypto' 2 | import axios from 'axios' 3 | 4 | export default class Crypto { 5 | constructor (options = {}, ctx) { 6 | return (async () => { 7 | this.ctx = ctx 8 | this.options = options || {} 9 | const title = this.ctx.app.head.title 10 | let key = (navigator.userAgent.toLowerCase() || '') 11 | let salt = (title || '') 12 | if (options.mode === 'api') { 13 | const get = this.options.api ? await axios.get(this.options.api) : await axios.get('https://ipinfo.io') 14 | const keyName = this.options.keyName || 'ip' 15 | const saltName = this.options.saltName || 'region' 16 | key += get.data[keyName] 17 | salt += get.data[saltName] 18 | } 19 | this.key = pbkdf2Sync(key, salt, 64, 64, 'sha512').toString('base64') 20 | return this 21 | })() 22 | } 23 | 24 | setKey (key, salt, keyMixTimes, keyLength) { 25 | this.key = pbkdf2Sync(key || navigator.userAgent.toLowerCase(), salt || title, keyMixTimes || 64, keyLength || 64, 'sha512').toString('base64') 26 | } 27 | 28 | encrypt (data) { 29 | if (this.options.mode === 'debug') return data 30 | else { 31 | try { 32 | this.cipher = createCipher(this.options.type || 'aes-256-cbc', this.key) 33 | let res = this.cipher.update(data, 'utf8', 'base64') 34 | res += this.cipher.final('base64') 35 | return res 36 | } catch (e) { 37 | return null 38 | } 39 | } 40 | } 41 | decrypt (data) { 42 | try { 43 | this.decipher = createDecipher(this.options.type || 'aes-256-cbc', this.key) 44 | let res = this.decipher.update(data, 'base64', 'utf8') 45 | res += this.decipher.final('utf8') 46 | return res 47 | } catch (e) { 48 | if (this.options.mode === 'debug') return data 49 | else return null 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /plugins/main.js: -------------------------------------------------------------------------------- 1 | import bindStorage from 'nuxt-vuex-localstorage/plugins/bindStorage' 2 | 3 | export default (ctx) => { 4 | const options = <%= JSON.stringify(options) %> 5 | bindStorage(ctx, options) 6 | } 7 | --------------------------------------------------------------------------------