From 6bd56b8706b15bb9b44d1ec29f61b92ab2ee4121 Mon Sep 17 00:00:00 2001 From: Jono Wenger <jono@elixxir.io> Date: Mon, 31 Oct 2022 22:57:30 +0000 Subject: [PATCH] Fix Purge --- storage/localStorage.go | 55 ++++++++++++++++++++++++++++-------- storage/localStorage_test.go | 33 ++++++++++++++++++---- storage/purge.go | 12 ++++---- utils/utils.go | 12 +++++--- 4 files changed, 86 insertions(+), 26 deletions(-) diff --git a/storage/localStorage.go b/storage/localStorage.go index b0fcf33e..cf7aa2d2 100644 --- a/storage/localStorage.go +++ b/storage/localStorage.go @@ -11,6 +11,9 @@ package storage import ( "encoding/base64" + "encoding/json" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/xxdk-wasm/utils" "os" "strings" "syscall/js" @@ -26,6 +29,11 @@ const localStorageWasmPrefix = "xxdkWasmStorage/" type LocalStorage struct { // The Javascript value containing the localStorage object v js.Value + + // The prefix appended to each key name. This is so that all keys created by + // this structure can be deleted without affecting other keys in local + // storage. + prefix string } // jsStorage is the global that stores Javascript as window.localStorage. @@ -34,11 +42,19 @@ type LocalStorage struct { // https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage-dev // - Documentation: // https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage -var jsStorage = LocalStorage{js.Global().Get("localStorage")} +var jsStorage = newLocalStorage(localStorageWasmPrefix) + +// newLocalStorage creates a new LocalStorage object with the specified prefix. +func newLocalStorage(prefix string) *LocalStorage { + return &LocalStorage{ + v: js.Global().Get("localStorage"), + prefix: prefix, + } +} // GetLocalStorage returns Javascript's local storage. func GetLocalStorage() *LocalStorage { - return &jsStorage + return jsStorage } // GetItem returns a key's value from the local storage given its name. Returns @@ -50,7 +66,7 @@ func GetLocalStorage() *LocalStorage { // - Documentation: // https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) { - keyValue := ls.getItem(localStorageWasmPrefix + keyName) + keyValue := ls.getItem(ls.prefix + keyName) if keyValue.IsNull() { return nil, os.ErrNotExist } @@ -72,7 +88,7 @@ func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) { // https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) { encodedKeyValue := base64.StdEncoding.EncodeToString(keyValue) - ls.setItem(localStorageWasmPrefix+keyName, encodedKeyValue) + ls.setItem(ls.prefix+keyName, encodedKeyValue) } // RemoveItem removes a key's value from local storage given its name. If there @@ -84,7 +100,7 @@ func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) { // - Documentation: // https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem func (ls *LocalStorage) RemoveItem(keyName string) { - ls.removeItem(localStorageWasmPrefix + keyName) + ls.removeItem(ls.prefix + keyName) } // Clear clears all the keys in storage. Underneath, it calls @@ -101,14 +117,14 @@ func (ls *LocalStorage) Clear() { // ClearPrefix clears all keys with the given prefix. func (ls *LocalStorage) ClearPrefix(prefix string) { // Get a copy of all key names at once - keys := js.Global().Get("Object").Call("keys", ls.v) + keys := ls.keys() // Loop through each key for i := 0; i < keys.Length(); i++ { if v := keys.Index(i); !v.IsNull() { - keyName := strings.TrimPrefix(v.String(), localStorageWasmPrefix) + keyName := strings.TrimPrefix(v.String(), ls.prefix) if strings.HasPrefix(keyName, prefix) { - ls.RemoveItem(keyName) + ls.removeItem(v.String()) } } } @@ -117,14 +133,14 @@ func (ls *LocalStorage) ClearPrefix(prefix string) { // ClearWASM clears all the keys in storage created by WASM. func (ls *LocalStorage) ClearWASM() { // Get a copy of all key names at once - keys := js.Global().Get("Object").Call("keys", ls.v) + keys := ls.keys() // Loop through each key for i := 0; i < keys.Length(); i++ { if v := keys.Index(i); !v.IsNull() { keyName := v.String() - if strings.HasPrefix(keyName, localStorageWasmPrefix) { - ls.RemoveItem(strings.TrimPrefix(keyName, localStorageWasmPrefix)) + if strings.HasPrefix(keyName, ls.prefix) { + ls.RemoveItem(strings.TrimPrefix(keyName, ls.prefix)) } } } @@ -145,7 +161,21 @@ func (ls *LocalStorage) Key(n int) (string, error) { return "", os.ErrNotExist } - return strings.TrimPrefix(keyName.String(), localStorageWasmPrefix), nil + return strings.TrimPrefix(keyName.String(), ls.prefix), nil +} + +// Keys returns a list of all key names in local storage. +func (ls *LocalStorage) Keys() []string { + keyNamesJson := utils.JSON.Call("stringify", ls.keys()) + + var keyNames []string + err := json.Unmarshal([]byte(keyNamesJson.String()), &keyNames) + if err != nil { + jww.FATAL.Panicf( + "Failed to JSON unmarshal localStorage key name list: %+v", err) + } + + return keyNames } // Length returns the number of keys in localStorage. Underneath, it accesses @@ -166,3 +196,4 @@ func (ls *LocalStorage) removeItem(keyName string) { ls.v.Call("removeIte func (ls *LocalStorage) clear() { ls.v.Call("clear") } func (ls *LocalStorage) key(n int) js.Value { return ls.v.Call("key", n) } func (ls *LocalStorage) length() js.Value { return ls.v.Get("length") } +func (ls *LocalStorage) keys() js.Value { return utils.Object.Call("keys", ls.v) } diff --git a/storage/localStorage_test.go b/storage/localStorage_test.go index 6e96814e..5c347308 100644 --- a/storage/localStorage_test.go +++ b/storage/localStorage_test.go @@ -88,7 +88,8 @@ func TestLocalStorage_Clear(t *testing.T) { // Tests that LocalStorage.ClearPrefix deletes only the keys with the given // prefix. func TestLocalStorage_ClearPrefix(t *testing.T) { - jsStorage.clear() + s := newLocalStorage("") + s.clear() prng := rand.New(rand.NewSource(11)) var yesPrefix, noPrefix []string prefix := "keyNamePrefix/" @@ -102,18 +103,18 @@ func TestLocalStorage_ClearPrefix(t *testing.T) { noPrefix = append(noPrefix, keyName) } - jsStorage.SetItem(keyName, []byte(strconv.Itoa(i))) + s.SetItem(keyName, []byte(strconv.Itoa(i))) } - jsStorage.ClearPrefix(prefix) + s.ClearPrefix(prefix) for _, keyName := range noPrefix { - if _, err := jsStorage.GetItem(keyName); err != nil { + if _, err := s.GetItem(keyName); err != nil { t.Errorf("Could not get keyName %q: %+v", keyName, err) } } for _, keyName := range yesPrefix { - keyValue, err := jsStorage.GetItem(keyName) + keyValue, err := s.GetItem(keyName) if err == nil || !errors.Is(err, os.ErrNotExist) { t.Errorf("Found keyName %q: %q", keyName, keyValue) } @@ -235,3 +236,25 @@ func TestLocalStorage_Length(t *testing.T) { } } } + +// Tests that LocalStorage.Keys return a list that contains all the added keys. +func TestLocalStorage_Keys(t *testing.T) { + s := newLocalStorage("") + s.clear() + values := map[string][]byte{ + "key1": []byte("key value"), + "key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + "key3": {0, 49, 0, 0, 0, 38, 249, 93}, + } + + for keyName, keyValue := range values { + s.SetItem(keyName, keyValue) + } + + keys := s.Keys() + for i, keyName := range keys { + if _, exists := values[keyName]; !exists { + t.Errorf("Key %q does not exist (%d).", keyName, i) + } + } +} diff --git a/storage/purge.go b/storage/purge.go index b0d8fc88..3aa1d47e 100644 --- a/storage/purge.go +++ b/storage/purge.go @@ -53,6 +53,8 @@ func DecrementNumClientsRunning() { func Purge(_ js.Value, args []js.Value) interface{} { storageDirectory := args[0].String() userPassword := args[1].String() + // Clear all EKV from local storage + GetLocalStorage().ClearPrefix("speakeasyapp") // Check the password if !verifyPassword(userPassword) { @@ -61,11 +63,11 @@ func Purge(_ js.Value, args []js.Value) interface{} { } // Verify all Cmix followers are stopped - if n := atomic.LoadUint64(&numClientsRunning); n != 0 { - utils.Throw(utils.TypeError, errors.Errorf( - "%d cMix followers running; all need to be stopped", n)) - return nil - } + // if n := atomic.LoadUint64(&numClientsRunning); n != 0 { + // utils.Throw(utils.TypeError, errors.Errorf( + // "%d cMix followers running; all need to be stopped", n)) + // return nil + // } // Get all indexedDb database names databaseList, err := GetIndexedDbList() diff --git a/utils/utils.go b/utils/utils.go index 07b9782d..c2ee6b41 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -16,19 +16,23 @@ import ( ) var ( - // Error is the Javascript Error object. It used to create new Javascript + // Error is the Javascript Error type. It used to create new Javascript // errors. Error = js.Global().Get("Error") - // JSON is the Javascript JSON object. It is used to perform JSON operations + // JSON is the Javascript JSON type. It is used to perform JSON operations // on the Javascript layer. JSON = js.Global().Get("JSON") - // Promise is the Javascript Promise object. It is used to generate new + // Object is the Javascript Object type. It is used to perform Object + // operations on the Javascript layer. + Object = js.Global().Get("Object") + + // Promise is the Javascript Promise type. It is used to generate new // promises. Promise = js.Global().Get("Promise") - // Uint8Array is the Javascript Uint8Array object. It is used to create new + // Uint8Array is the Javascript Uint8Array type. It is used to create new // Uint8Array. Uint8Array = js.Global().Get("Uint8Array") ) -- GitLab