/* * Copyright (c) 2022. * Author Peter Placzek (tada5hi) * For the full copyright and license information, * view the LICENSE file that was distributed with this source code. */ import Vue from 'vue'; import {CookieSerializeOptions, parse as parseCookie, serialize as serializeCookie} from 'cookie'; import {Context} from "@nuxt/types"; import {decodeValue, encodeValue, isSet, isUnset} from "~/modules/storage/utils"; import {StorageOptionsInterface} from "~/modules/storage/types"; export default class BaseStorage { public ctx: Context public options: StorageOptionsInterface; public state : {[key: string] : any} = {}; // ------------------------------------ constructor (ctx: Context, options: StorageOptionsInterface) { this.ctx = ctx this.options = options; this.initState(); } // ------------------------------------ getKeyWithNamespace(key: string) : string { let fullKey: string = ''; if(typeof this.options.namespace !== 'undefined') { fullKey += this.options.namespace+'_'; } return fullKey+key; } // ------------------------------------ // Universal // ------------------------------------ set (key: string, value: any) { // Unset null, undefined if (isUnset(value)) { console.log('Unset key:'+key) return this.remove(key) } this.setState(key,value); this.setCookie(key, value) this.setLocalStorageItem(key, value); this.setSessionStorageItem(key, value); return value } get (key: string) { let value : any = this.getState(key); // Cookies if (isUnset(value)) { value = this.getCookie(key); } // Local Storage if (isUnset(value)) { value = this.getLocalStorageItem(key); } if(isUnset(value)) { value = this.getSessionStorageItem(key); } if (isUnset(value)) { value = this.getState(key) } return value } getAll() { let value : any = {}; let storageValue : any; storageValue = this.getCookies(); if(storageValue) { value = {...value, ...storageValue} } storageValue = this.getLocalStorageItems(); if(storageValue) { value = {...value, ...storageValue} } storageValue = this.getSessionStorageItems(); if(storageValue) { value = {...value, ...storageValue} } return value; } sync (key: string, defaultValue?: any) { let value = this.get(key) if (isUnset(value) && isSet(defaultValue)) { value = defaultValue } if (isSet(value)) { this.set(key, value) } return value } remove (key: string) { this.removeState(key); this.removeSessionStorageItem(key); this.removeLocalStorageItem(key) this.removeCookie(key) } // ------------------------------------ // State // ------------------------------------ initState() { Vue.set(this, 'state', {}); } setState(key: string, value: T) : T { Vue.set(this.state, key, value); return value; } getState(key: string) { return this.state[key]; } removeState(key: string) { this.setState(key, undefined); } // ------------------------------------ // Browser Storage // ------------------------------------ getBrowserStorageItems(type: 'sessionStorage' | 'localStorage') { let storage : any; switch (type) { case 'sessionStorage': storage = sessionStorage; break; case 'localStorage': storage = localStorage; break; } if (typeof storage === 'undefined' || !this.options[type]) { return; } let items : {[key: string] : any} = {}, keys = Object.keys(storage), i = keys.length; while ( i-- ) { let key = keys[i]; if(typeof this.options.namespace !== "undefined") { if(key.substr(0, this.options.namespace.length) !== this.options.namespace) { continue; } key = key.replace(this.options.namespace+'_', ''); } items[key] = decodeValue(storage.getItem(keys[i])); } return items; } // ------------------------------------ // Local storage // ------------------------------------ setLocalStorageItem (key: string, value: any) { // Unset null, undefined if (isUnset(value)) { return this.removeLocalStorageItem(key) } if (typeof localStorage === 'undefined' || !this.options.localStorage) { return } const _key = this.getKeyWithNamespace(key); try { localStorage.setItem(_key, encodeValue(value)) } catch (e) { if (!this.options.ignoreExceptions) { throw e } } return value } getLocalStorageItem (key: string) : any { if (typeof localStorage === 'undefined' || !this.options.localStorage) { return } const _key = this.getKeyWithNamespace(key); const value = localStorage.getItem(_key) return decodeValue(value) } getLocalStorageItems() : any { return this.getBrowserStorageItems('localStorage'); } removeLocalStorageItem (key: string) { if (typeof localStorage === 'undefined' || !this.options.localStorage) { return } const _key = this.getKeyWithNamespace(key); localStorage.removeItem(_key) } // ------------------------------------ // Session storage // ------------------------------------ setSessionStorageItem (key: string, value: any) { // Unset null, undefined if (isUnset(value)) { return this.removeSessionStorageItem(key) } if (typeof sessionStorage === 'undefined' || !this.options.sessionStorage) { return } const _key = this.getKeyWithNamespace(key); try { sessionStorage.setItem(_key, encodeValue(value)) } catch (e) { throw e; } return value } getSessionStorageItem (key: string) { if (typeof sessionStorage === 'undefined' || !this.options.sessionStorage) { return } const _key = this.getKeyWithNamespace(key); const value = sessionStorage.getItem(_key) return decodeValue(value) } getSessionStorageItems() : any { return this.getBrowserStorageItems('sessionStorage'); } removeSessionStorageItem (key: string) { if (typeof sessionStorage === 'undefined' || !this.options.sessionStorage) { return } const _key = this.getKeyWithNamespace(key); sessionStorage.removeItem(_key) } // ------------------------------------ // Cookies // ------------------------------------ getCookies () { const cookieStr = process.client ? document.cookie : this.ctx.req.headers.cookie let items : {[key: string] : any} = decodeValue(parseCookie(cookieStr || '') || {}); for(let key in items) { items[key] = decodeValue(decodeURIComponent(items[key])); } return items; } setCookie (key: string, value: any) { if (!this.options.cookie || (process.server && !this.ctx.res)) { return } const _key = this.getKeyWithNamespace(key); const _options = Object.assign({}, this.options.cookie); const _value = encodeValue(value) // Unset null, undefined if (isUnset(value)) { _options.maxAge = -1 } const serializedCookie = serializeCookie(_key, _value, _options); if (process.client) { // Set in browser document.cookie = serializedCookie } else if (process.server && this.ctx.res) { // Send Set-Cookie header from server side let cookies = this.ctx.res.getHeader('Set-Cookie') || []; if(typeof cookies === 'number' || typeof cookies === 'string') { if(typeof cookies === 'number') { cookies = cookies.toString(); } cookies = [cookies]; } cookies.unshift(serializedCookie); this.ctx.res.setHeader('Set-Cookie', cookies.filter((v, i, arr) => arr.findIndex(val => val.startsWith(v.substr(0, v.indexOf('=')))) === i)); } return value } getCookie (key: string) { if (!this.options.cookie || (process.server && !this.ctx.req)) { return } const _key = this.getKeyWithNamespace(key); const cookies = this.getCookies() return cookies[_key] ?? undefined; } removeCookie (key: string) { this.setCookie(key, undefined) } } export function createStorage(ctx: Context, options: StorageOptionsInterface) { return new BaseStorage(ctx, options); }