From 084ee40bba89b11020ebc84861146e20d29eabc2 Mon Sep 17 00:00:00 2001 From: Amal Shaji Date: Sun, 12 Mar 2023 16:39:49 +0530 Subject: [PATCH] refactor: Change store to sqlite and UI improvements (#36) - Move backend to sqlite (from badgerdb) - Update dashboard UI - Delete users - Add connection status (online, lastActiveAt) --- .gitignore | 3 +- go.mod | 22 +- go.sum | 205 +------ internal/server/admin/dashboard.go | 11 - internal/server/admin/models.go | 53 +- internal/server/admin/users.go | 218 ++++--- internal/server/admin/users_test.go | 178 ++++-- internal/server/app/app.go | 21 +- internal/server/db/db.go | 25 +- internal/server/handlers/admin.go | 34 +- internal/server/tunnel/server.go | 31 +- internal/server/web/package.json | 4 + internal/server/web/pnpm-lock.yaml | 18 + .../server/web/src/lib/CopyToClipboard.svelte | 47 ++ internal/server/web/src/lib/Loader.svelte | 2 +- internal/server/web/src/lib/Stats.svelte | 195 ++++--- .../server/web/src/lib/TunnelUsers.svelte | 418 ++++++++------ .../web/src/lib/modals/AddTunnelUser.svelte | 172 ++++++ .../web/src/lib/modals/DeleteUser.svelte | 139 +++++ .../server/web/src/lib/modals/Modal.svelte | 132 +++++ .../web/src/lib/modals/ShowSecretKey.svelte | 121 ++++ internal/server/web/src/lib/store.ts | 5 + internal/server/web/src/lib/types.ts | 26 + .../server/web/src/pages/Dashboard.svelte | 533 +++++++++++++++++- internal/utils/id.go | 31 +- tests/beaver_client.yaml | 2 +- tests/data/000001.sst | Bin 516 -> 0 bytes tests/data/000002.sst | Bin 720 -> 0 bytes tests/data/000002.vlog | Bin 20 -> 0 bytes tests/data/000003.vlog | Bin 20 -> 0 bytes tests/data/DISCARD | Bin 1048576 -> 0 bytes tests/data/KEYREGISTRY | 1 - tests/data/MANIFEST | Bin 44 -> 0 bytes tests/data/beaver.db | Bin 0 -> 32768 bytes 34 files changed, 1980 insertions(+), 667 deletions(-) delete mode 100644 internal/server/admin/dashboard.go create mode 100644 internal/server/web/src/lib/CopyToClipboard.svelte create mode 100644 internal/server/web/src/lib/modals/AddTunnelUser.svelte create mode 100644 internal/server/web/src/lib/modals/DeleteUser.svelte create mode 100644 internal/server/web/src/lib/modals/Modal.svelte create mode 100644 internal/server/web/src/lib/modals/ShowSecretKey.svelte create mode 100644 internal/server/web/src/lib/store.ts create mode 100644 internal/server/web/src/lib/types.ts delete mode 100644 tests/data/000001.sst delete mode 100644 tests/data/000002.sst delete mode 100644 tests/data/000002.vlog delete mode 100644 tests/data/000003.vlog delete mode 100644 tests/data/DISCARD delete mode 100644 tests/data/KEYREGISTRY delete mode 100644 tests/data/MANIFEST create mode 100644 tests/data/beaver.db diff --git a/.gitignore b/.gitignore index 41ce3a2..15410a2 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ dist/ !beaver_server/ data/ testdata/ -!tests/data/ \ No newline at end of file +!tests/data/ +internal/server/admin/test_beaver.db diff --git a/go.mod b/go.mod index 05937f9..ba40aba 100644 --- a/go.mod +++ b/go.mod @@ -11,33 +11,23 @@ require ( github.com/shirou/gopsutil/v3 v3.23.1 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.1 - github.com/timshannon/badgerhold/v4 v4.0.2 golang.org/x/crypto v0.6.0 golang.org/x/term v0.5.0 gopkg.in/yaml.v3 v3.0.1 + gorm.io/driver/sqlite v1.4.4 + gorm.io/gorm v1.24.6 ) require ( - github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/badger/v3 v3.2103.1 // indirect - github.com/dgraph-io/ristretto v0.1.1 // indirect - github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.0.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/flatbuffers v2.0.0+incompatible // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.13.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -46,10 +36,8 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opencensus.io v0.23.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index d2e6451..e00dc1a 100644 --- a/go.sum +++ b/go.sum @@ -1,97 +1,22 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger/v3 v3.2103.1 h1:zaX53IRg7ycxVlkd5pYdCeFp1FynD6qBGQoQql3R3Hk= -github.com/dgraph-io/badger/v3 v3.2103.1/go.mod h1:dULbq6ehJ5K0cGW/1TQ9iSfUk0gbSiToDWmWmTsJ53E= -github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= -github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= -github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI= -github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -103,7 +28,6 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0= github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g= @@ -114,173 +38,70 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4= github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/timshannon/badgerhold/v3 v3.0.0-20210909134927-2b6764d68c1e h1:zWSVsQaifg0cVH9VvR+cMguV7exK6U+SoW8YD1cZpR4= -github.com/timshannon/badgerhold/v3 v3.0.0-20210909134927-2b6764d68c1e/go.mod h1:/Seq5xGNo8jLhSbDX3jdbeZrp4yFIpQ6/7n4TjziEWs= -github.com/timshannon/badgerhold/v4 v4.0.2 h1:83OLY/NFnEaMnHEPd84bYtkLipVkjTsMbzQRYbk47g4= -github.com/timshannon/badgerhold/v4 v4.0.2/go.mod h1:rh6RyXLQFsvrvcKondPQQFZnNovpRzu+gS0FlLxYuHY= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= +gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s= +gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= diff --git a/internal/server/admin/dashboard.go b/internal/server/admin/dashboard.go deleted file mode 100644 index 3c1596b..0000000 --- a/internal/server/admin/dashboard.go +++ /dev/null @@ -1,11 +0,0 @@ -package admin - -import "github.com/timshannon/badgerhold/v4" - -type Dashboard struct { - Store *badgerhold.Store -} - -func NewDashboardService(store *badgerhold.Store) *Dashboard { - return &Dashboard{Store: store} -} diff --git a/internal/server/admin/models.go b/internal/server/admin/models.go index 55d55b1..9f4c942 100644 --- a/internal/server/admin/models.go +++ b/internal/server/admin/models.go @@ -6,26 +6,22 @@ import ( "github.com/amalshaji/beaver/internal/utils" "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" ) -var ErrWrongPassword = errors.New("wrong password") - -type BaseModel struct { - ID uint64 `badgerhold:"key" json:"id"` - CreatedAt time.Time `json:"created_at"` -} - -func (b *BaseModel) MarkAsNew() { - b.CreatedAt = time.Now() -} +var ( + ErrWrongPassword = errors.New("wrong password") + ErrWrongSecretKey = errors.New("wrong secret key") +) type AdminUser struct { - BaseModel + gorm.Model - Email string `badgerhold:"unique" json:"email"` - PasswordHash string `json:"-"` - SessionToken string `json:"-"` - IsSuperUser bool `json:"is_super_user"` + Email string `gorm:"index,unique"` + PasswordHash string `gorm:"unique" json:"-"` + SuperUser bool + + Session Session } func (a *AdminUser) SetPassword(rawPassword string) error { @@ -47,24 +43,29 @@ func (a *AdminUser) CheckPassword(rawPassword string) error { return nil } -func (a *AdminUser) GenerateSessionToken() error { - a.SessionToken = utils.GenerateUUIDV4().String() - return nil +type Session struct { + gorm.Model + + Token string `gorm:"index, unique"` + AdminUserId uint } -func (a *AdminUser) ResetSessionToken() error { - a.SessionToken = "" +func (s *Session) GenerateSessionToken() error { + s.Token = utils.GenerateSessionToken() return nil } type TunnelUser struct { - BaseModel + gorm.Model - Email string `badgerhold:"unique" json:"email"` - SecretKey string `json:"secret_key"` + Email string `gorm:"index,unique"` + SecretKey *string `gorm:"index,unique" json:"-"` + Active bool + LastActiveAt *time.Time } -func (t *TunnelUser) RotateSecretKey() error { - t.SecretKey = utils.GenerateUUIDV4().String() - return nil +func (t *TunnelUser) RotateSecretKey() string { + newSecretKey := utils.GenerateSecretKey() + t.SecretKey = &newSecretKey + return newSecretKey } diff --git a/internal/server/admin/users.go b/internal/server/admin/users.go index b35b87b..2fbef35 100644 --- a/internal/server/admin/users.go +++ b/internal/server/admin/users.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/amalshaji/beaver/internal/utils" - "github.com/timshannon/badgerhold/v4" + "gorm.io/gorm" ) var ErrAdminUserNotFound = errors.New("admin user does not exist") @@ -17,28 +17,31 @@ var ErrDuplicateAdminUser = errors.New("admin user with the same email exists") var ErrDuplicateTunnelUser = errors.New("tunnel user with the same email exists") var ErrMultipleSuperuserError = errors.New("you cannot create more than one superuser") -type User struct { - Store *badgerhold.Store +type UserService struct { + DB *gorm.DB } -func NewUserService(store *badgerhold.Store) *User { - return &User{Store: store} +func NewUserService(store *gorm.DB) *UserService { + return &UserService{DB: store} } -func (u *User) findUserByEmail(ctx context.Context, email string) (*AdminUser, error) { +func (u *UserService) findUserByEmail(ctx context.Context, email string) (*AdminUser, error) { email = utils.SanitizeString(email) - var superUser AdminUser - if err := u.Store.FindOne(&superUser, badgerhold.Where("Email").Eq(email)); err != nil { - if errors.Is(err, badgerhold.ErrNotFound) { + var user AdminUser + result := u.DB.Where(&AdminUser{Email: email}).First(&user) + + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, ErrAdminUserNotFound } - return nil, err + return nil, result.Error } - return &superUser, nil + + return &user, nil } -func (u *User) CreateUser(ctx context.Context, email, password string, isSuperUser bool) (*AdminUser, error) { +func (u *UserService) CreateUser(ctx context.Context, email, password string, superUser bool) (*AdminUser, error) { email = utils.SanitizeString(email) password = utils.SanitizeString(password) @@ -51,46 +54,47 @@ func (u *User) CreateUser(ctx context.Context, email, password string, isSuperUs return nil, ErrDuplicateAdminUser } - var adminUser AdminUser - - adminUser.Email = email + adminUser := AdminUser{ + Email: email, + SuperUser: superUser, + } adminUser.SetPassword(password) - adminUser.IsSuperUser = isSuperUser - adminUser.MarkAsNew() - if err := u.Store.Insert(badgerhold.NextSequence(), &adminUser); err != nil { - if errors.Is(err, badgerhold.ErrUniqueExists) { - return nil, ErrDuplicateAdminUser - } - return nil, err + result := u.DB.Create(&adminUser) + if result.Error != nil { + return nil, result.Error } return &adminUser, nil } -func (u *User) CreateAdminUser(ctx context.Context, email, password string) (*AdminUser, error) { +func (u *UserService) CreateAdminUser(ctx context.Context, email, password string) (*AdminUser, error) { return u.CreateUser(ctx, email, password, false) } -func (u *User) CanCreateSuperUser(ctx context.Context) error { - count, err := u.Store.Count(&AdminUser{}, badgerhold.Where("IsSuperUser").Eq(true)) - if err != nil { - return err +func (u *UserService) CanCreateSuperUser(ctx context.Context) error { + var count int64 + + result := u.DB.Model(&AdminUser{}).Where("super_user = ?", true).Count(&count) + if result.Error != nil { + return result.Error } + if count == 0 { return nil } + return ErrMultipleSuperuserError } -func (u *User) CreateSuperUser(ctx context.Context, email, password string) (*AdminUser, error) { +func (u *UserService) CreateSuperUser(ctx context.Context, email, password string) (*AdminUser, error) { if err := u.CanCreateSuperUser(ctx); err != nil { return nil, err } return u.CreateUser(ctx, email, password, true) } -func (u *User) Login(ctx context.Context, email, password string) (string, error) { +func (u *UserService) Login(ctx context.Context, email, password string) (string, error) { email = utils.SanitizeString(email) password = utils.SanitizeString(password) @@ -105,67 +109,66 @@ func (u *User) Login(ctx context.Context, email, password string) (string, error return "", ErrWrongEmailOrPassword } - adminUser.GenerateSessionToken() + session := Session{ + Token: utils.GenerateSessionToken(), + } + adminUser.Session = session - u.Store.UpdateMatching(&AdminUser{}, badgerhold.Where("Email").Eq(email), func(record interface{}) error { - update, ok := record.(*AdminUser) - if !ok { - return fmt.Errorf("error while updating superuser") - } - update.SessionToken = adminUser.SessionToken - return nil - }) + result := u.DB.Save(&adminUser) + if result.Error != nil { + return "", result.Error + } - return adminUser.SessionToken, nil + return adminUser.Session.Token, nil } -func (u *User) Logout(ctx context.Context, sessionToken string) error { - var err error +func (u *UserService) Logout(ctx context.Context, sessionToken string) error { + result := u.DB.Where(&Session{Token: sessionToken}).Delete(&Session{}) - if _, err = u.ValidateSession(ctx, sessionToken); err != nil { - return err + if result.Error != nil { + return result.Error } - u.Store.UpdateMatching(&AdminUser{}, badgerhold.Where("SessionToken").Eq(sessionToken), func(record interface{}) error { - update, ok := record.(*AdminUser) - if !ok { - return fmt.Errorf("error while updating superuser") - } - - update.SessionToken = "" - return nil - }) + if result.RowsAffected == 0 { + return ErrInvalidUserSession + } return nil } -func (u *User) ValidateSession(ctx context.Context, sessionToken string) (*AdminUser, error) { +func (u *UserService) ValidateSession(ctx context.Context, sessionToken string) (*AdminUser, error) { var adminUser AdminUser - if err := u.Store.FindOne(&adminUser, badgerhold.Where("SessionToken").Eq(sessionToken)); err != nil { - if errors.Is(err, badgerhold.ErrNotFound) { + result := u.DB. + Joins("JOIN sessions on sessions.admin_user_id = admin_users.id"). + Where("sessions.token = ?", sessionToken).First(&adminUser) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, ErrInvalidUserSession } - return nil, err + return nil, result.Error } + return &adminUser, nil } -func (u *User) findTunnelUserByEmail(ctx context.Context, email string) (*TunnelUser, error) { +func (u *UserService) findTunnelUserByEmail(ctx context.Context, email string) (*TunnelUser, error) { email = utils.SanitizeString(email) var tunnelUser TunnelUser - if err := u.Store.FindOne(&tunnelUser, badgerhold.Where("Email").Eq(email)); err != nil { - if errors.Is(err, badgerhold.ErrNotFound) { + result := u.DB.Where(&TunnelUser{Email: email}).First(&tunnelUser) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, ErrTunnelUserNotFound } - return nil, err + return nil, result.Error } + return &tunnelUser, nil } -func (u *User) CreateTunnelUser(ctx context.Context, email string) (*TunnelUser, error) { +func (u *UserService) CreateTunnelUser(ctx context.Context, email string) (*TunnelUser, error) { email = utils.SanitizeString(email) existingTunnelUser, err := u.findTunnelUserByEmail(ctx, email) @@ -181,65 +184,104 @@ func (u *User) CreateTunnelUser(ctx context.Context, email string) (*TunnelUser, return nil, fmt.Errorf("enter a valid email address") } - var tunnelUser TunnelUser - - tunnelUser.Email = email + tunnelUser := TunnelUser{ + Email: email, + } tunnelUser.RotateSecretKey() - tunnelUser.MarkAsNew() - if err := u.Store.Insert(badgerhold.NextSequence(), &tunnelUser); err != nil { - if errors.Is(err, badgerhold.ErrUniqueExists) { - return nil, ErrDuplicateTunnelUser - } - return nil, err + result := u.DB.Save(&tunnelUser) + if result.Error != nil { + return nil, result.Error } return &tunnelUser, nil } -func (u *User) GetTunnelUserBySecret(ctx context.Context, secretKey string) (*TunnelUser, error) { +func (u *UserService) GetTunnelUserBySecret(ctx context.Context, secretKey string) (*TunnelUser, error) { secretKey = utils.SanitizeString(secretKey) var tunnelUser TunnelUser - if err := u.Store.FindOne(&tunnelUser, badgerhold.Where("SecretKey").Eq(secretKey)); err != nil { - if errors.Is(err, badgerhold.ErrNotFound) { + result := u.DB.Where(&TunnelUser{SecretKey: &secretKey}).First(&tunnelUser) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, ErrTunnelUserNotFound } - return nil, err + return nil, result.Error } + return &tunnelUser, nil } -func (u *User) ListTunnelUsers(ctx context.Context) ([]TunnelUser, error) { +func (u *UserService) ListTunnelUsers(ctx context.Context) ([]TunnelUser, error) { var tunnelUsers []TunnelUser - if err := u.Store.Find(&tunnelUsers, nil); err != nil { - return nil, err + result := u.DB.Find(&tunnelUsers) + if result.Error != nil { + return nil, result.Error } - if tunnelUsers == nil { + + if len(tunnelUsers) == 0 { return []TunnelUser{}, nil } + return tunnelUsers, nil } -func (u *User) RotateTunnelUserSecretKey(ctx context.Context, email string) (*TunnelUser, error) { +func (u *UserService) RotateTunnelUserSecretKey(ctx context.Context, email string) (*TunnelUser, error) { tunnelUser, err := u.findTunnelUserByEmail(ctx, email) - if err != nil { return nil, err } tunnelUser.RotateSecretKey() - u.Store.UpdateMatching(&TunnelUser{}, badgerhold.Where("Email").Eq(email), func(record interface{}) error { - update, ok := record.(*TunnelUser) - if !ok { - return fmt.Errorf("error while updating superuser") - } - update.SecretKey = tunnelUser.SecretKey - return nil - }) + result := u.DB.Save(&tunnelUser) + if result.Error != nil { + return nil, result.Error + } return tunnelUser, nil } + +func (u *UserService) SetActiveConnection(ctx context.Context, tunnelUser *TunnelUser) error { + tunnelUser.Active = true + + result := u.DB.Save(tunnelUser) + if result.Error != nil { + return result.Error + } + + return nil +} + +func (u *UserService) SetInactiveConnectionStatusForUsers(ctc context.Context, userIdentifiers ...string) error { + if len(userIdentifiers) == 0 { + return fmt.Errorf("atleast one userIdentifier is required") + } + + result := u.DB.Model(&TunnelUser{}).Where("email", userIdentifiers).Delete(&TunnelUser{}) + if result.Error != nil { + return result.Error + } + + return nil +} + +func (u *UserService) GetUserConnectionStatus(ctx context.Context) ([]TunnelUser, error) { + var connectionStatus []TunnelUser + + result := u.DB.Model(&TunnelUser{}).Select("ID", "Active", "LastActiveAt").Find(&connectionStatus) + if result.Error != nil { + return nil, result.Error + } + return connectionStatus, nil +} + +func (u *UserService) DeleteTunnelUser(ctx context.Context, id uint) error { + result := u.DB.Unscoped().Delete(&TunnelUser{}, id) + if result.Error != nil { + return result.Error + } + return nil +} diff --git a/internal/server/admin/users_test.go b/internal/server/admin/users_test.go index 0a55fdb..ceac8d4 100644 --- a/internal/server/admin/users_test.go +++ b/internal/server/admin/users_test.go @@ -6,39 +6,45 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/timshannon/badgerhold/v4" + "gorm.io/driver/sqlite" + "gorm.io/gorm" ) -func newTestStore() *badgerhold.Store { - options := badgerhold.DefaultOptions - options.Dir = "./testdata" - options.ValueDir = "./testdata" - options.Logger = nil - - store, err := badgerhold.Open(options) +func newTestStore() *gorm.DB { + // create database directory if not exists + db, err := gorm.Open(sqlite.Open("./test_beaver.db"), &gorm.Config{}) if err != nil { log.Fatal(err) } - return store + // should automigrate here? + db.AutoMigrate(AdminUser{}, TunnelUser{}, Session{}) + + return db +} + +func resetTestStores() { + db.Unscoped().Where("1 = 1").Delete(&AdminUser{}) + db.Unscoped().Where("1 = 1").Delete(&TunnelUser{}) + db.Unscoped().Where("1 = 1").Delete(&Session{}) } -var store = newTestStore() +var db = newTestStore() func TestCreateSuperUser(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() var err error ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) // No error while creating superuser superUser, err := user.CreateSuperUser(ctx, "test@beaver.com", "password") assert.NoError(t, err) - assert.True(t, superUser.IsSuperUser) + assert.True(t, superUser.SuperUser) // Creating multiple superusers should fail _, err = user.CreateSuperUser(ctx, "test@beaver.com", "password") @@ -48,18 +54,17 @@ func TestCreateSuperUser(t *testing.T) { func TestAdminSuperUser(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() - var err error ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) // No error while creating adminuser adminUser, err := user.CreateAdminUser(ctx, "test@beaver.com", "password") assert.NoError(t, err) - assert.False(t, adminUser.IsSuperUser) + assert.False(t, adminUser.SuperUser) // Creating adminuser with duplicate email should throw error _, err = user.CreateAdminUser(ctx, "test@beaver.com", "password") @@ -74,33 +79,37 @@ func TestAdminSuperUser(t *testing.T) { func TestLoginAdminUser(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) _, _ = user.CreateAdminUser(ctx, "test@beaver.com", "password") token, _ := user.Login(ctx, "test@beaver.com", "password") superUser, _ := user.findUserByEmail(ctx, "test@beaver.com") - assert.NotEqual(t, superUser.SessionToken, "") - assert.Equal(t, superUser.SessionToken, token) + + var session Session + _ = db.Where(&Session{AdminUserId: superUser.ID}).First(&session) + + assert.NotNil(t, session.Token) + assert.Equal(t, session.Token, token) token, err := user.Login(ctx, "test2@beaver.com", "password") assert.Error(t, err) assert.Equal(t, ErrWrongEmailOrPassword, err) - assert.Equal(t, token, "") + assert.Equal(t, "", token) } func TestValidateSession(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) superUser, _ := user.CreateAdminUser(ctx, "test@beaver.com", "password") @@ -118,11 +127,11 @@ func TestValidateSession(t *testing.T) { func TestLogoutAdminUser(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) _, _ = user.CreateAdminUser(ctx, "test@beaver.com", "password") @@ -132,9 +141,14 @@ func TestLogoutAdminUser(t *testing.T) { assert.NoError(t, err) superUser, err := user.findUserByEmail(ctx, "test@beaver.com") - assert.Equal(t, superUser.SessionToken, "") assert.NoError(t, err) + var session Session + result := db.Where(&Session{AdminUserId: superUser.ID}).First(&session) + + assert.Error(t, result.Error) + assert.ErrorIs(t, result.Error, gorm.ErrRecordNotFound) + err = user.Logout(ctx, "test2@beaver.com") assert.Error(t, err) assert.Equal(t, ErrInvalidUserSession, err) @@ -142,11 +156,11 @@ func TestLogoutAdminUser(t *testing.T) { func TestCreateTunnelUser(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) tu, err := user.CreateTunnelUser(ctx, "test@beaver.com") assert.NoError(t, err) @@ -156,26 +170,26 @@ func TestCreateTunnelUser(t *testing.T) { func TestGetTunnelUserBySecretKey(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) tu, _ := user.CreateTunnelUser(ctx, "test@beaver.com") - ntu, err := user.GetTunnelUserBySecret(ctx, tu.SecretKey) + ntu, err := user.GetTunnelUserBySecret(ctx, *tu.SecretKey) assert.NoError(t, err) assert.Equal(t, tu.Email, ntu.Email) } func TestRotateTunnelUserSecretKey(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) tu, _ := user.CreateTunnelUser(ctx, "test@beaver.com") @@ -193,11 +207,11 @@ func TestRotateTunnelUserSecretKey(t *testing.T) { func TestListTunnelUsers(t *testing.T) { defer func() { - store.Badger().DropAll() + resetTestStores() }() ctx := context.Background() - user := NewUserService(store) + user := NewUserService(db) tunnelUsers, err := user.ListTunnelUsers(ctx) assert.NoError(t, err) @@ -214,3 +228,95 @@ func TestListTunnelUsers(t *testing.T) { assert.Equal(t, "test2@beaver.com", tunnelUsers[1].Email) assert.Equal(t, "test3@beaver.com", tunnelUsers[2].Email) } + +func TestSetActiveConnection(t *testing.T) { + defer func() { + resetTestStores() + }() + + ctx := context.Background() + user := NewUserService(db) + + tunnelUser, _ := user.CreateTunnelUser(ctx, "test@beaver.com") + + _ = user.SetActiveConnection(ctx, tunnelUser) + + var tu TunnelUser + _ = db.Model(&TunnelUser{}).Where(map[string]any{"Active": true}).First(&tu) + + assert.Equal(t, tunnelUser.ID, tu.ID) + assert.Equal(t, tunnelUser.Email, tu.Email) +} + +func TestSetInactiveConnectionStatusForUsers(t *testing.T) { + defer func() { + resetTestStores() + }() + + ctx := context.Background() + user := NewUserService(db) + + tunnelUser1, _ := user.CreateTunnelUser(ctx, "test1@beaver.com") + tunnelUser2, _ := user.CreateTunnelUser(ctx, "test2@beaver.com") + _, _ = user.CreateTunnelUser(ctx, "test3@beaver.com") + + _ = user.SetActiveConnection(ctx, tunnelUser1) + _ = user.SetActiveConnection(ctx, tunnelUser2) + + var count int64 + + _ = db.Model(&TunnelUser{}).Where(map[string]any{"Active": true}).Count(&count) + assert.Equal(t, int64(2), count) + + _ = user.SetInactiveConnectionStatusForUsers(ctx, tunnelUser1.Email, tunnelUser2.Email) + + _ = db.Model(&TunnelUser{}).Where(map[string]any{"Active": true}).Count(&count) + assert.Equal(t, int64(0), count) +} + +func TestGetUserConnectionStatus(t *testing.T) { + defer func() { + resetTestStores() + }() + + ctx := context.Background() + user := NewUserService(db) + + tunnelUser1, _ := user.CreateTunnelUser(ctx, "test1@beaver.com") + tunnelUser2, _ := user.CreateTunnelUser(ctx, "test2@beaver.com") + _, _ = user.CreateTunnelUser(ctx, "test3@beaver.com") + + _ = user.SetActiveConnection(ctx, tunnelUser1) + _ = user.SetActiveConnection(ctx, tunnelUser2) + + cs, _ := user.GetUserConnectionStatus(ctx) + + assert.True(t, cs[0].Active) + assert.True(t, cs[1].Active) + assert.False(t, cs[2].Active) +} + +func TestDeleteTunnelUser(t *testing.T) { + defer func() { + resetTestStores() + }() + + ctx := context.Background() + user := NewUserService(db) + + tunnelUser1, _ := user.CreateTunnelUser(ctx, "test1@beaver.com") + tunnelUser2, _ := user.CreateTunnelUser(ctx, "test2@beaver.com") + + _ = user.DeleteTunnelUser(ctx, tunnelUser1.ID) + + var count int64 + + _ = db.Model(&TunnelUser{}).Where("1 = 1").Count(&count) + assert.Equal(t, int64(1), count) + + var tu []TunnelUser + _ = db.Model(&TunnelUser{}).Find(&tu) + + assert.Equal(t, 1, len(tu)) + assert.Equal(t, tunnelUser2.ID, tu[0].ID) +} diff --git a/internal/server/app/app.go b/internal/server/app/app.go index e832e3e..1173c4e 100644 --- a/internal/server/app/app.go +++ b/internal/server/app/app.go @@ -4,23 +4,21 @@ import ( "github.com/amalshaji/beaver/internal/server/admin" "github.com/amalshaji/beaver/internal/server/db" "github.com/amalshaji/beaver/internal/server/tunnel" - "github.com/timshannon/badgerhold/v4" + "gorm.io/gorm" ) type App struct { - Store *badgerhold.Store - Dashboard *admin.Dashboard - User *admin.User - Server *tunnel.Server + DB *gorm.DB + User *admin.UserService + Server *tunnel.Server } func NewApp(configFile string) *App { - store := db.NewStore() + db := db.NewStore() return &App{ - Store: store, - Dashboard: admin.NewDashboardService(store), - User: admin.NewUserService(store), - Server: tunnel.NewServer(configFile), + DB: db, + User: admin.NewUserService(db), + Server: tunnel.NewServer(configFile, db), } } @@ -31,7 +29,4 @@ func (app *App) Start() { func (app *App) Shutdown() { // Shutdown the tunnel server app.Server.Shutdown() - - // Close database connection - app.Store.Close() } diff --git a/internal/server/db/db.go b/internal/server/db/db.go index 318b6dd..a1aac28 100644 --- a/internal/server/db/db.go +++ b/internal/server/db/db.go @@ -2,20 +2,27 @@ package db import ( "log" + "os" - "github.com/timshannon/badgerhold/v4" + "github.com/amalshaji/beaver/internal/server/admin" + "gorm.io/driver/sqlite" + "gorm.io/gorm" ) -func NewStore() *badgerhold.Store { - options := badgerhold.DefaultOptions - options.Dir = "data" - options.ValueDir = "data" - options.Logger = nil - - store, err := badgerhold.Open(options) +func NewStore() *gorm.DB { + // create database directory if not exists + if _, err := os.Stat("./data"); err != nil { + if os.IsNotExist(err) { + os.Mkdir("./data", os.ModePerm) + } + } + db, err := gorm.Open(sqlite.Open("./data/beaver.db"), &gorm.Config{}) if err != nil { log.Fatal(err) } - return store + // should automigrate here? + db.AutoMigrate(&admin.AdminUser{}, &admin.TunnelUser{}, &admin.Session{}) + + return db } diff --git a/internal/server/handlers/admin.go b/internal/server/handlers/admin.go index fcf42aa..4528bfd 100644 --- a/internal/server/handlers/admin.go +++ b/internal/server/handlers/admin.go @@ -3,6 +3,7 @@ package handler import ( "errors" "io/fs" + "log" "net/http" "os" "strconv" @@ -69,6 +70,12 @@ func register(c echo.Context) error { // Add the WebSocket connection to the pool pool.Register(ws) + // Set tunnelUser as active + err = app.User.SetActiveConnection(c.Request().Context(), tunnelUser) + if err != nil { + log.Printf("Unable to set connection as active for tunnelUser %s: %v", tunnelUser.Email, err.Error()) + } + return nil } @@ -119,6 +126,7 @@ func setupApiRoutes(e *echo.Echo) { g.GET("/tunnel-users", getTunnelUsers, authRequiredMiddleware) g.POST("/tunnel-users", createTunnelUser, authRequiredMiddleware) g.PUT("/tunnel-users", rotateTunnelUserSecretKey, authRequiredMiddleware) + g.DELETE("/tunnel-users/:id", deleteTunnelUser, authRequiredMiddleware) } func superUserSignupApi(c echo.Context) error { @@ -204,6 +212,10 @@ func serverStats(c echo.Context) error { result["active_connections"] = len(app.Server.Pools) + connectionStatus, _ := app.User.GetUserConnectionStatus(c.Request().Context()) + + result["connection_status"] = connectionStatus + return c.JSON(200, result) } @@ -224,7 +236,7 @@ func createTunnelUser(c echo.Context) error { return utils.HttpBadRequest(c, err.Error()) } - return c.JSON(http.StatusOK, tunnelUser) + return c.JSON(http.StatusOK, map[string]string{"SecretKey": *tunnelUser.SecretKey}) } func getTunnelUsers(c echo.Context) error { @@ -243,11 +255,27 @@ func rotateTunnelUserSecretKey(c echo.Context) error { } app := c.Get("app").(*app.App) - tunnelUsers, err := app.User.RotateTunnelUserSecretKey(c.Request().Context(), payload.Email) + tunnelUser, err := app.User.RotateTunnelUserSecretKey(c.Request().Context(), payload.Email) if err != nil { return utils.HttpBadRequest(c, err.Error()) } - return c.JSON(http.StatusOK, tunnelUsers) + return c.JSON(http.StatusOK, map[string]string{"SecretKey": *tunnelUser.SecretKey}) +} + +func deleteTunnelUser(c echo.Context) error { + userIdStr := c.Param("id") + userId, err := strconv.Atoi(userIdStr) + if err != nil { + return utils.HttpBadRequest(c, "id must be a positive number") + + } + + app := c.Get("app").(*app.App) + err = app.User.DeleteTunnelUser(c.Request().Context(), uint(userId)) + if err != nil { + return utils.HttpBadRequest(c, err.Error()) + } + return c.JSON(http.StatusOK, map[string]string{}) } func GetAdminHandler(app *app.App) *echo.Echo { diff --git a/internal/server/tunnel/server.go b/internal/server/tunnel/server.go index 12b7a31..bfc8fee 100644 --- a/internal/server/tunnel/server.go +++ b/internal/server/tunnel/server.go @@ -10,7 +10,9 @@ import ( "sync" "time" + "github.com/amalshaji/beaver/internal/server/admin" "github.com/gorilla/websocket" + "gorm.io/gorm" ) // Server is a Reverse HTTP Proxy over WebSocket @@ -40,6 +42,9 @@ type Server struct { // "server" thread sends the value to this channel when accepting requests in the endpoint /requests, // and "Dispatcher" thread reads this channel. Dispatcher chan *ConnectionRequest + + // DB connection + DB *gorm.DB } // ConnectionRequest is used to request a proxy connection from the dispatcher @@ -59,7 +64,7 @@ func NewConnectionRequest(timeout time.Duration, connectionFor string) (cr *Conn } // NewServer return a new Server instance -func NewServer(configFile string) (server *Server) { +func NewServer(configFile string, db *gorm.DB) (server *Server) { rand.Seed(time.Now().Unix()) // Load configuration @@ -76,6 +81,8 @@ func NewServer(configFile string) (server *Server) { server.done = make(chan struct{}) server.Dispatcher = make(chan *ConnectionRequest) + server.DB = db + return } @@ -111,9 +118,13 @@ func (s *Server) clean() { idle := 0 busy := 0 + var inactiveConnections = make([]string, 0) + pools := make(map[string]*Pool) for subdomain, pool := range s.Pools { if pool.IsEmpty() { + inactiveConnections = append(inactiveConnections, pool.UserIdentifier) + log.Printf("Removing empty connection pool : %s", pool.ID) pool.Shutdown() } else { @@ -125,11 +136,29 @@ func (s *Server) clean() { busy += ps.Busy } + s.updateInactiveStatusForClosedConnections(inactiveConnections...) + log.Printf("%d pools, %d idle, %d busy", len(pools), idle, busy) s.Pools = pools } +func (s *Server) updateInactiveStatusForClosedConnections(connections ...string) { + if len(connections) == 0 { + return + } + + currentTime := time.Now() + + result := s.DB.Model(admin.TunnelUser{}). + Where("email in (?)", connections). + Updates(map[string]any{"Active": false, "LastActiveAt": ¤tTime}) + + if result.Error != nil { + log.Println("unable to update status for inactive connections: ", result.Error.Error()) + } +} + // Dispatch connection from available pools to clients requests func (s *Server) DispatchConnections() { for { diff --git a/internal/server/web/package.json b/internal/server/web/package.json index 97cbe73..82620b2 100644 --- a/internal/server/web/package.json +++ b/internal/server/web/package.json @@ -10,6 +10,7 @@ "check": "svelte-check --tsconfig ./tsconfig.json" }, "devDependencies": { + "@rgossiaux/svelte-headlessui": "^1.0.2", "@svelte-plugins/tooltips": "^0.1.6", "@sveltejs/vite-plugin-svelte": "^2.0.2", "@tailwindcss/forms": "^0.5.3", @@ -26,5 +27,8 @@ "tslib": "^2.5.0", "typescript": "^4.9.3", "vite": "^4.1.0" + }, + "dependencies": { + "moment": "^2.29.4" } } diff --git a/internal/server/web/pnpm-lock.yaml b/internal/server/web/pnpm-lock.yaml index 06e810a..727dd7b 100644 --- a/internal/server/web/pnpm-lock.yaml +++ b/internal/server/web/pnpm-lock.yaml @@ -1,11 +1,13 @@ lockfileVersion: 5.4 specifiers: + '@rgossiaux/svelte-headlessui': ^1.0.2 '@svelte-plugins/tooltips': ^0.1.6 '@sveltejs/vite-plugin-svelte': ^2.0.2 '@tailwindcss/forms': ^0.5.3 '@tsconfig/svelte': ^3.0.0 autoprefixer: ^10.4.7 + moment: ^2.29.4 postcss: ^8.4.14 postcss-load-config: ^4.0.1 svelte: ^3.55.1 @@ -18,7 +20,11 @@ specifiers: typescript: ^4.9.3 vite: ^4.1.0 +dependencies: + moment: 2.29.4 + devDependencies: + '@rgossiaux/svelte-headlessui': 1.0.2_svelte@3.55.1 '@svelte-plugins/tooltips': 0.1.6 '@sveltejs/vite-plugin-svelte': 2.0.2_svelte@3.55.1+vite@4.1.1 '@tailwindcss/forms': 0.5.3_tailwindcss@3.2.6 @@ -273,6 +279,14 @@ packages: fastq: 1.15.0 dev: true + /@rgossiaux/svelte-headlessui/1.0.2_svelte@3.55.1: + resolution: {integrity: sha512-sauopYTSivhzXe1kAvgawkhyYJcQlK8Li3p0d2OtcCIVprOzdbard5lbqWB4xHDv83zAobt2mR08oizO2poHLQ==} + peerDependencies: + svelte: ^3.44.0 + dependencies: + svelte: 3.55.1 + dev: true + /@svelte-plugins/tooltips/0.1.6: resolution: {integrity: sha512-UiQfOyU0mssIf8ABhXU8Er55hiHZeY7Axu8KTIiGR73dC/pwdK54GdtXavZN2cJPN/n09Uh/Fg2PfWfRxJ+hWw==} dev: true @@ -740,6 +754,10 @@ packages: minimist: 1.2.8 dev: true + /moment/2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: false + /mri/1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} diff --git a/internal/server/web/src/lib/CopyToClipboard.svelte b/internal/server/web/src/lib/CopyToClipboard.svelte new file mode 100644 index 0000000..0939d28 --- /dev/null +++ b/internal/server/web/src/lib/CopyToClipboard.svelte @@ -0,0 +1,47 @@ + + +{#if copied} + + + +{:else} + + + + +{/if} diff --git a/internal/server/web/src/lib/Loader.svelte b/internal/server/web/src/lib/Loader.svelte index 6e5d3bc..41e0a55 100644 --- a/internal/server/web/src/lib/Loader.svelte +++ b/internal/server/web/src/lib/Loader.svelte @@ -5,7 +5,7 @@ xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - class="mx-auto h-6 w-6" + class="mx-auto h-4 w-4" viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve" diff --git a/internal/server/web/src/lib/Stats.svelte b/internal/server/web/src/lib/Stats.svelte index e48da83..c2eb2b0 100644 --- a/internal/server/web/src/lib/Stats.svelte +++ b/internal/server/web/src/lib/Stats.svelte @@ -1,5 +1,6 @@ -
-
-
-
-
- - - - +
+

+ Server Stats +

+
-
-

{active_connections}

-
-
+
+ -
-
-
- - - - +
  • +
    + + + +
    +
    +
    + + CPU + +

    {cpu_used.toFixed(2)}%

    -

    CPU Used

    -
  • -
    -

    - {cpu_used.toFixed(2)}% -

    -
    -
    + + -
    -
    -
    - - - - +
  • +
    + + + +
    +
    +
    + + Memory + +

    {memory_used.toFixed(2)}%

    -

    - Memory Used -

    -
  • -
    -

    - {memory_used.toFixed(2)}% -

    -
    -
    - + + + diff --git a/internal/server/web/src/lib/TunnelUsers.svelte b/internal/server/web/src/lib/TunnelUsers.svelte index ed22d99..65ae19d 100644 --- a/internal/server/web/src/lib/TunnelUsers.svelte +++ b/internal/server/web/src/lib/TunnelUsers.svelte @@ -1,28 +1,30 @@ -
    -
    -
    -

    Tunnel Users

    -

    - A list of all the registered tunnel users -

    -
    -
    -
    - - - - + + +
    + + + + +
    + + + diff --git a/internal/server/web/src/lib/modals/AddTunnelUser.svelte b/internal/server/web/src/lib/modals/AddTunnelUser.svelte new file mode 100644 index 0000000..d8cd122 --- /dev/null +++ b/internal/server/web/src/lib/modals/AddTunnelUser.svelte @@ -0,0 +1,172 @@ + + +
    + { + inputRef?.focus(); + }} + on:afterLeave={(event) => { + email = ""; + }} + > +
    + +
    +
    +
    + + +
    + +
    diff --git a/internal/server/web/src/lib/modals/DeleteUser.svelte b/internal/server/web/src/lib/modals/DeleteUser.svelte new file mode 100644 index 0000000..743089c --- /dev/null +++ b/internal/server/web/src/lib/modals/DeleteUser.svelte @@ -0,0 +1,139 @@ + + +
    + { + userToDelete = undefined; + }} + > +
    + +
    +
    +
    + + +
    + +
    diff --git a/internal/server/web/src/lib/modals/Modal.svelte b/internal/server/web/src/lib/modals/Modal.svelte new file mode 100644 index 0000000..8f25844 --- /dev/null +++ b/internal/server/web/src/lib/modals/Modal.svelte @@ -0,0 +1,132 @@ + + +
    + { + inputRef?.focus(); + }} + on:afterLeave={(event) => { + email = ""; + }} + > +
    + +
    +
    +
    + + +
    + +
    diff --git a/internal/server/web/src/lib/modals/ShowSecretKey.svelte b/internal/server/web/src/lib/modals/ShowSecretKey.svelte new file mode 100644 index 0000000..446cb35 --- /dev/null +++ b/internal/server/web/src/lib/modals/ShowSecretKey.svelte @@ -0,0 +1,121 @@ + + +
    + +
    + +
    +
    +
    + + +
    + +
    diff --git a/internal/server/web/src/lib/store.ts b/internal/server/web/src/lib/store.ts new file mode 100644 index 0000000..1203cb7 --- /dev/null +++ b/internal/server/web/src/lib/store.ts @@ -0,0 +1,5 @@ +import { writable } from "svelte/store"; + +export const tunnelUserConnectionStatus = writable( + [] +); diff --git a/internal/server/web/src/lib/types.ts b/internal/server/web/src/lib/types.ts new file mode 100644 index 0000000..5eeda60 --- /dev/null +++ b/internal/server/web/src/lib/types.ts @@ -0,0 +1,26 @@ +interface IStats { + active_connections: number; + cpu_used: number; + memory_used: number; + connection_status: IActiveConnectionStatus[]; +} + +interface IActiveConnectionStatus { + ID: number; + Active: boolean; + LastActiveAt: string; +} + +interface ITunnelUser { + ID: number; + CreatedAt: string; + UpdatedAt: string; + DeletedAt: string; + Email: string; + Active: boolean; + LastActiveAt: string | null; +} + +interface IError { + error: string; +} diff --git a/internal/server/web/src/pages/Dashboard.svelte b/internal/server/web/src/pages/Dashboard.svelte index 4e6be59..f8393ab 100644 --- a/internal/server/web/src/pages/Dashboard.svelte +++ b/internal/server/web/src/pages/Dashboard.svelte @@ -1,12 +1,535 @@ - +
    + + diff --git a/internal/utils/id.go b/internal/utils/id.go index 8bf84e0..919e330 100644 --- a/internal/utils/id.go +++ b/internal/utils/id.go @@ -1,8 +1,33 @@ package utils -import uuid "github.com/nu7hatch/gouuid" +import gonanoid "github.com/matoous/go-nanoid/v2" -func GenerateUUIDV4() *uuid.UUID { - id, _ := uuid.NewV4() +const ( + RANDOM_SUBDOMAIN_LENGTH = 6 + CONNECTION_ID_LENGTH = 10 + SECRET_KEY_LENGTH = 32 + SESSION_ID_LENGTH = 16 + + NANOID_ALPHABETS = "abcdefghijklmnopqrstuvwxyz0123456789" +) + +func generateNanoid(size int) string { + id, _ := gonanoid.Generate(NANOID_ALPHABETS, size) return id } + +func GenerateRandomSubdomain() string { + return generateNanoid(RANDOM_SUBDOMAIN_LENGTH) +} + +func GenerateConnectionId() string { + return generateNanoid(CONNECTION_ID_LENGTH) +} + +func GenerateSecretKey() string { + return generateNanoid(SECRET_KEY_LENGTH) +} + +func GenerateSessionToken() string { + return generateNanoid(SESSION_ID_LENGTH) +} diff --git a/tests/beaver_client.yaml b/tests/beaver_client.yaml index cddc6d8..98dc956 100644 --- a/tests/beaver_client.yaml +++ b/tests/beaver_client.yaml @@ -1,2 +1,2 @@ target: ws://localhost:8080 -secretkey: 751a1d6f-ddee-4899-5aa0-13347e6aa88e +secretkey: upn5ygtf01mq0sa8a8vg0xz60zxmkwc0 diff --git a/tests/data/000001.sst b/tests/data/000001.sst deleted file mode 100644 index 6b39eeab4dcf3407686ce133c845f4030caebf27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 516 zcmYLGO=}ZT6uoyQ6KCpZXTX#cQgo4pf~_^SO^rn+X%dnqmC&RRH!YLQ(+o{!hIvW9 zR&CW(cjCey;L@dkK-?7sapBHC;mVzT-YJD1xbNI=&gGrS3jikMoq_9Zaux~=)pH9> zTyOv~u-)lZJ%{b*68BberGf4CbA}E#hKvs_RP?#7KhUhc_-6qm#HoaMt;P=OE+3+J z3pfEVg{p3LdfU^rlpF`bG!zmrv!3jTV;NCPEVfac04k^zY|hqwhxJjsriAj*LLE?V z*qrbAp;NJWPciQ|8RxF=wfr6Cp;^CP<;_vR!bm$xw0~B-!tT-ryq8d_7BLQ}t%l4V zGAB=>#?)bmHwt2`Wp#obkHmn}<9l!yAW9SuqdVMKS2s%AL5`&RiU~gju;D=7jaa<9H zC_Y*g>aGCfZo=pbzs_$t5rvT_i^#VC-6-TVi(PCRtcm;%Io!XqC<2Nl&9YY8T_dkq zb+<2EW|q}xo0ax;W}+{6c-AWSJFF`wgXo~r3crojBl>exF)15Rm=IcLAN4;h`TZDxM|!sR>fIoHd;GN6BEcyLf}gY(L~xM?cujq_$Gq(Pr|mp1 zF-!=AB)FXN{)c53+4jHLdjJM#zn$eG3m`{l*zqCazh}z-$^kh6Hl;@|_utqv_51-h CMy(kD diff --git a/tests/data/000002.vlog b/tests/data/000002.vlog deleted file mode 100644 index 741d39b8bc5aa4db2c7330377f5847b0838e1543..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20 WcmZQzfB+`WjMq|TsSY+yBG~{H69Z8I diff --git a/tests/data/000003.vlog b/tests/data/000003.vlog deleted file mode 100644 index d8d31b406c492285ea79f26eccfe5ff0bdaa3083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20 XcmZQzfB>@@%sXVS*|)xtox%kGAIJpg diff --git a/tests/data/DISCARD b/tests/data/DISCARD deleted file mode 100644 index 9e0f96a2a253b173cb45b41868209a5d043e1437..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1048576 zcmeIuF#!Mo0K%a4Pi+Wah(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ T0|pEjFkrxd0RsjM82APT0Pp|- diff --git a/tests/data/KEYREGISTRY b/tests/data/KEYREGISTRY deleted file mode 100644 index 1610a9c..0000000 --- a/tests/data/KEYREGISTRY +++ /dev/null @@ -1 +0,0 @@ -Ž¤kÌ·VÚØj1…CM²4~Hello Badger \ No newline at end of file diff --git a/tests/data/MANIFEST b/tests/data/MANIFEST deleted file mode 100644 index 20fe60bc7357cb5361f3d195940a1bec15c3b857..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 pcmZ=tNiSkxVBi2^7+`yQRO%lW3kRbCBT$Hq{q?P0Adks_5dfJ)2S@+_ diff --git a/tests/data/beaver.db b/tests/data/beaver.db new file mode 100644 index 0000000000000000000000000000000000000000..0ba33868344980bd262f92e97afccc03906f8d32 GIT binary patch literal 32768 zcmeI)(QevS7zc1W2_ab(=&q19F?mX&H5#S12?W?pXy&w(Vj72bO{K~{ffI1RhF}9p zq)npTO|N#fyS>DoVA^|3nzWbLE^;xOl;jYa-n5b0=8ps%`#ZKj|2}NV(&21xJv3b^ z?6o=;afJkTljC`AT@W~q3wiH3?>&0WdX1UUg7?ic?aP`WF0OyE5dMz~-so_lzr%lq zDhn?crf>Y?E#d_M2tWV=5P$##AOL}DAYccBk?*4X;B(Wi(nGU)s5{hg%$DuwRobL3 zt?I-bt(x1=lw4U6^6K}>Q^B~Tw;_~N!5D30+%nF0WURdSYBm^&M){w^F4=9;QLoW= z@bwYOxw{V)W+%ce?`Vc#+AgipPRyuuC^^l|5UO6wH7y!5x^2JhXUwi;4$t&;TMcR( zf=dtGm_e$RY3p5wc69bIkFl$s7BQQnVmZ{SzNZJCZt zb81|ciCi^on>bFt)v4-r;?z%Vg~w`s>#-6uoNn8@IQ%Qb5bUl+J7y*J2HoV;Y$U(P zb3!jJt78AE6Ns#5Nl*o#5TuP@_*HYh#$!tQLh)YsDnOV)G z*G}WaBF(r{C;R4lyJ_}X@k+~DlF5>kSbAX9o5WS`Xyu(kwZD3>SCRVZl4|whqTRkz z-X!-PHWM`Gi#-z?I*n-q-x*YE#7a$y*z*Q)A?pH zokUjZ@^iyr@nWi8EEN|sVoH)P`-{MM+@+4Y?tl0RciVRIsOIj8l64?DBu&!2ns|7e z5|0n9M!zEZ