diff --git a/cmd/root.go b/cmd/root.go index 63be465..29d72d3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,6 +1,8 @@ package cmd import ( + "github.com/Unkn0wnCat/calapi/internal/logger" + "github.com/spf13/viper" "os" "github.com/spf13/cobra" @@ -23,5 +25,13 @@ func Execute() { } func init() { + viper.SetDefault("development", true) + viper.SetDefault("data_directory", "./data") + viper.SetDefault("auth.type", "GHOST") + viper.SetDefault("auth.secret", "hunter2") + viper.SetDefault("auth.anonymous_read", true) + viper.SetDefault("auth.ghost.base_url", "https://content.hhga.1in9.net/ghost") + viper.SetDefault("auth.ghost.limit_to_roles", nil) + logger.StartLogger() } diff --git a/cmd/serve.go b/cmd/serve.go index 194b55e..995f5c2 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -2,9 +2,9 @@ package cmd import ( "github.com/Unkn0wnCat/calapi/internal/database" + "github.com/Unkn0wnCat/calapi/internal/logger" "github.com/Unkn0wnCat/calapi/internal/server" "github.com/spf13/cobra" - "log" "os" "os/signal" "syscall" @@ -28,7 +28,7 @@ var serveCmd = &cobra.Command{ go server.Serve() <-c // Wait for quit signal - log.Println("goodbye, shutting down...") + logger.Logger.Info("goodbye, shutting down...") return nil }, diff --git a/go.mod b/go.mod index 0495ae7..ee58a09 100644 --- a/go.mod +++ b/go.mod @@ -3,40 +3,54 @@ module github.com/Unkn0wnCat/calapi go 1.19 require ( + github.com/766b/chi-prometheus v0.0.0-20211217152057-87afa9aa2ca8 github.com/99designs/gqlgen v0.17.24 + github.com/go-chi/chi v1.5.4 + github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 github.com/google/flatbuffers v1.12.0 github.com/objectbox/objectbox-go v1.6.1 + github.com/prometheus/client_golang v1.4.0 github.com/spf13/cobra v1.6.1 github.com/spf13/cobra-cli v1.3.0 + github.com/spf13/viper v1.15.0 github.com/vektah/gqlparser/v2 v2.5.1 + go.uber.org/zap v1.21.0 ) require ( github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/magiconair/properties v1.8.5 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/objectbox/objectbox-generator v0.13.0 // indirect - github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.9.1 // indirect + github.com/prometheus/procfs v0.0.8 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.10.1 // indirect - github.com/subosito/gotenv v1.2.0 // indirect + github.com/subosito/gotenv v1.4.2 // indirect github.com/urfave/cli/v2 v2.8.1 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.8.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect - golang.org/x/text v0.3.8 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect golang.org/x/tools v0.1.12 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 252debe..3d111e6 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -15,6 +16,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -45,7 +47,10 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/766b/chi-prometheus v0.0.0-20211217152057-87afa9aa2ca8 h1:hK1G69lDhhrGqJbRA5i1rmT2KI/W77MSdr7hEGHqWdQ= +github.com/766b/chi-prometheus v0.0.0-20211217152057-87afa9aa2ca8/go.mod h1:X/LhbmoBoRu8TxoGIOIraVNhfz3hhikJoaelrOuhdPY= github.com/99designs/gqlgen v0.17.24 h1:pcd/HFIoSdRvyADYQG2dHvQN2KZqX/nXzlVm6TMMq7E= github.com/99designs/gqlgen v0.17.24/go.mod h1:BMhYIhe4bp7OlCo5I2PnowSK/Wimpv/YlxfNkqZGwLo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -70,14 +75,19 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/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.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -119,9 +129,13 @@ github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs= +github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -133,6 +147,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 h1:tDQ1LjKga657layZ4JLsRdxgvupebc0xuPwRNuTfUgs= +github.com/golang-jwt/jwt/v5 v5.0.0-rc.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -163,6 +179,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -181,6 +198,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -195,6 +213,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -206,6 +225,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -262,15 +282,16 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -285,6 +306,7 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= @@ -309,29 +331,38 @@ github.com/objectbox/objectbox-go v1.6.1 h1:P4sJPXFX4UeKF480Coqf9UJ2tVy+t5uxmwc+ github.com/objectbox/objectbox-go v1.6.1/go.mod h1:310SfpcNMThWSrpdcClXWiayVMu59xxA3RZdk5x8ay0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0 h1:YVIb/fVcOTMSqtqZWSKnHpSLBxu8DKgxq8z6RuBZwqI= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -344,10 +375,12 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= @@ -358,20 +391,26 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0 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.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= @@ -398,8 +437,16 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -409,8 +456,10 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 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.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -484,6 +533,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -566,6 +616,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -573,6 +624,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -591,8 +643,10 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -604,8 +658,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -657,6 +712,7 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -745,7 +801,9 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -816,14 +874,17 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 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.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/graph/generated.go b/graph/generated.go index 5fa3b97..55efffc 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -79,6 +79,7 @@ type ComplexityRoot struct { DeleteEvent func(childComplexity int, input string) int EditCalendar func(childComplexity int, input model.EditCalendar) int EditEvent func(childComplexity int, input model.EditEvent) int + Login func(childComplexity int, input model.Login) int } Query struct { @@ -107,6 +108,7 @@ type MutationResolver interface { CreateCalendar(ctx context.Context, input model.NewCalendar) (*model.Calendar, error) EditCalendar(ctx context.Context, input model.EditCalendar) (*model.Calendar, error) DeleteCalendar(ctx context.Context, input string) (bool, error) + Login(ctx context.Context, input model.Login) (string, error) } type QueryResolver interface { Events(ctx context.Context, after *time.Time, before *time.Time, calendar *string) ([]*model.Event, error) @@ -312,6 +314,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.EditEvent(childComplexity, args["input"].(model.EditEvent)), true + case "Mutation.login": + if e.complexity.Mutation.Login == nil { + break + } + + args, err := ec.field_Mutation_login_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.Login(childComplexity, args["input"].(model.Login)), true + case "Query.calendar": if e.complexity.Query.Calendar == nil { break @@ -379,6 +393,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputEditCalendar, ec.unmarshalInputEditEvent, + ec.unmarshalInputLogin, ec.unmarshalInputNewCalendar, ec.unmarshalInputNewEvent, ec.unmarshalInputSetLocation, @@ -575,6 +590,21 @@ func (ec *executionContext) field_Mutation_editEvent_args(ctx context.Context, r return args, nil } +func (ec *executionContext) field_Mutation_login_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 model.Login + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg0, err = ec.unmarshalNLogin2githubᚗcomᚋUnkn0wnCatᚋcalapiᚋgraphᚋmodelᚐLogin(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1759,6 +1789,60 @@ func (ec *executionContext) fieldContext_Mutation_deleteCalendar(ctx context.Con return fc, nil } +func (ec *executionContext) _Mutation_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_login(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().Login(rctx, fc.Args["input"].(model.Login)) + }) + if err != nil { + ec.Error(ctx, err) + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_login_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Query_events(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_events(ctx, field) if err != nil { @@ -4118,6 +4202,42 @@ func (ec *executionContext) unmarshalInputEditEvent(ctx context.Context, obj int return it, nil } +func (ec *executionContext) unmarshalInputLogin(ctx context.Context, obj interface{}) (model.Login, error) { + var it model.Login + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"username", "password"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "username": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("username")) + it.Username, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + case "password": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password")) + it.Password, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputNewCalendar(ctx context.Context, obj interface{}) (model.NewCalendar, error) { var it model.NewCalendar asMap := map[string]interface{}{} @@ -4515,6 +4635,12 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_deleteCalendar(ctx, field) }) + case "login": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_login(ctx, field) + }) + default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -5150,6 +5276,11 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec return res } +func (ec *executionContext) unmarshalNLogin2githubᚗcomᚋUnkn0wnCatᚋcalapiᚋgraphᚋmodelᚐLogin(ctx context.Context, v interface{}) (model.Login, error) { + res, err := ec.unmarshalInputLogin(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalNNewCalendar2githubᚗcomᚋUnkn0wnCatᚋcalapiᚋgraphᚋmodelᚐNewCalendar(ctx context.Context, v interface{}) (model.NewCalendar, error) { res, err := ec.unmarshalInputNewCalendar(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index 3e562a6..d24cee1 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -29,6 +29,11 @@ type Location struct { Address *string `json:"address"` } +type Login struct { + Username string `json:"username"` + Password string `json:"password"` +} + type NewCalendar struct { Name string `json:"name"` Description string `json:"description"` diff --git a/graph/schema.graphqls b/graph/schema.graphqls index 920538d..2fbe8c1 100644 --- a/graph/schema.graphqls +++ b/graph/schema.graphqls @@ -89,6 +89,11 @@ input EditCalendar { description: String } +input Login { + username: String! + password: String! +} + type Mutation { createEvent(input: NewEvent!): Event! editEvent(input: EditEvent!): Event! @@ -97,4 +102,6 @@ type Mutation { createCalendar(input: NewCalendar!): Calendar! editCalendar(input: EditCalendar!): Calendar! deleteCalendar(input: ID!): Boolean! + + login(input: Login!): String! } diff --git a/graph/schema.resolvers.go b/graph/schema.resolvers.go index 0960ec9..86511e9 100644 --- a/graph/schema.resolvers.go +++ b/graph/schema.resolvers.go @@ -7,6 +7,11 @@ package graph import ( "context" "errors" + "github.com/99designs/gqlgen/graphql" + "github.com/Unkn0wnCat/calapi/internal/auth" + "github.com/Unkn0wnCat/calapi/internal/logger" + "github.com/go-chi/chi/middleware" + "go.uber.org/zap" "strconv" "time" @@ -18,6 +23,10 @@ import ( // Events is the resolver for the events field. func (r *calendarResolver) Events(ctx context.Context, obj *model.Calendar, after *time.Time, before *time.Time) ([]*model.Event, error) { + if auth.ChallengeQuery(ctx) != nil { + return nil, errors.New("unauthorized") + } + if after == nil { now := time.Now() after = &now @@ -51,6 +60,10 @@ func (r *calendarResolver) Events(ctx context.Context, obj *model.Calendar, afte // Calendar is the resolver for the calendar field. func (r *eventResolver) Calendar(ctx context.Context, obj *model.Event) (*model.Calendar, error) { + if auth.ChallengeQuery(ctx) != nil { + return nil, errors.New("unauthorized") + } + modelCalendar := model.FromCalendar(*obj.DbCalendar) return &modelCalendar, nil @@ -58,6 +71,10 @@ func (r *eventResolver) Calendar(ctx context.Context, obj *model.Event) (*model. // CreateEvent is the resolver for the createEvent field. func (r *mutationResolver) CreateEvent(ctx context.Context, input model.NewEvent) (*model.Event, error) { + if auth.ChallengeMutation(ctx) != nil { + return nil, errors.New("unauthorized") + } + actualId, err := strconv.ParseUint(input.Calendar, 16, 64) if err != nil { return nil, err @@ -106,11 +123,21 @@ func (r *mutationResolver) CreateEvent(ctx context.Context, input model.NewEvent modelEvent := model.FromEvent(event) + logger.Logger.Info("event created", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + zap.Uint64("newID", event.Id), + ) + return &modelEvent, nil } // EditEvent is the resolver for the editEvent field. func (r *mutationResolver) EditEvent(ctx context.Context, input model.EditEvent) (*model.Event, error) { + if auth.ChallengeMutation(ctx) != nil { + return nil, errors.New("unauthorized") + } + actualId, err := strconv.ParseUint(input.ID, 16, 64) if err != nil { return nil, err @@ -178,11 +205,21 @@ func (r *mutationResolver) EditEvent(ctx context.Context, input model.EditEvent) modelEvent := model.FromEvent(*event) + logger.Logger.Info("event edited", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + zap.Uint64("ID", event.Id), + ) + return &modelEvent, nil } // DeleteEvent is the resolver for the deleteEvent field. func (r *mutationResolver) DeleteEvent(ctx context.Context, input string) (bool, error) { + if auth.ChallengeMutation(ctx) != nil { + return false, errors.New("unauthorized") + } + actualId, err := strconv.ParseUint(input, 16, 64) if err != nil { return false, err @@ -194,11 +231,21 @@ func (r *mutationResolver) DeleteEvent(ctx context.Context, input string) (bool, return false, err } + logger.Logger.Info("event deleted", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + zap.Uint64("oldID", actualId), + ) + return true, nil } // CreateCalendar is the resolver for the createCalendar field. func (r *mutationResolver) CreateCalendar(ctx context.Context, input model.NewCalendar) (*model.Calendar, error) { + if auth.ChallengeMutation(ctx) != nil { + return nil, errors.New("unauthorized") + } + calendar := db_model.Calendar{ Name: input.Name, Description: input.Description, @@ -213,11 +260,21 @@ func (r *mutationResolver) CreateCalendar(ctx context.Context, input model.NewCa modelCalendar := model.FromCalendar(calendar) + logger.Logger.Info("calendar created", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + zap.Uint64("newID", calendar.Id), + ) + return &modelCalendar, nil } // EditCalendar is the resolver for the editCalendar field. func (r *mutationResolver) EditCalendar(ctx context.Context, input model.EditCalendar) (*model.Calendar, error) { + if auth.ChallengeMutation(ctx) != nil { + return nil, errors.New("unauthorized") + } + actualId, err := strconv.ParseUint(input.ID, 16, 64) if err != nil { return nil, err @@ -247,11 +304,21 @@ func (r *mutationResolver) EditCalendar(ctx context.Context, input model.EditCal modelCalendar := model.FromCalendar(*calendar) + logger.Logger.Info("calendar edited", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + zap.Uint64("ID", calendar.Id), + ) + return &modelCalendar, nil } // DeleteCalendar is the resolver for the deleteCalendar field. func (r *mutationResolver) DeleteCalendar(ctx context.Context, input string) (bool, error) { + if auth.ChallengeMutation(ctx) != nil { + return false, errors.New("unauthorized") + } + actualId, err := strconv.ParseUint(input, 16, 64) if err != nil { return false, err @@ -263,11 +330,36 @@ func (r *mutationResolver) DeleteCalendar(ctx context.Context, input string) (bo return false, err } + logger.Logger.Info("calendar deleted", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + zap.Uint64("newID", actualId), + ) + return true, nil } +// Login is the resolver for the login field. +func (r *mutationResolver) Login(ctx context.Context, input model.Login) (string, error) { + user, err := auth.Authenticate(ctx, input.Username, input.Password) + if err != nil || user == nil { + return "", errors.New("invalid credentials") + } + + token, err := auth.MakeJWT(user) + if err != nil { + return "", errors.New("failed to create token") + } + + return token, nil +} + // Events is the resolver for the events field. func (r *queryResolver) Events(ctx context.Context, after *time.Time, before *time.Time, calendar *string) ([]*model.Event, error) { + if auth.ChallengeQuery(ctx) != nil { + return nil, errors.New("unauthorized") + } + if after == nil { now := time.Now() after = &now @@ -308,6 +400,10 @@ func (r *queryResolver) Events(ctx context.Context, after *time.Time, before *ti // Calendars is the resolver for the calendars field. func (r *queryResolver) Calendars(ctx context.Context) ([]*model.Calendar, error) { + if auth.ChallengeQuery(ctx) != nil { + return nil, errors.New("unauthorized") + } + calendarBox := db_model.BoxForCalendar(database.ObjectBox) results, err := calendarBox.GetAll() if err != nil { @@ -326,6 +422,10 @@ func (r *queryResolver) Calendars(ctx context.Context) ([]*model.Calendar, error // Calendar is the resolver for the calendar field. func (r *queryResolver) Calendar(ctx context.Context, id string) (*model.Calendar, error) { + if auth.ChallengeQuery(ctx) != nil { + return nil, errors.New("unauthorized") + } + actualId, err := strconv.ParseUint(id, 16, 64) if err != nil { return nil, err @@ -347,6 +447,10 @@ func (r *queryResolver) Calendar(ctx context.Context, id string) (*model.Calenda // Event is the resolver for the event field. func (r *queryResolver) Event(ctx context.Context, id string) (*model.Event, error) { + if auth.ChallengeQuery(ctx) != nil { + return nil, errors.New("unauthorized") + } + actualId, err := strconv.ParseUint(id, 16, 64) if err != nil { return nil, err diff --git a/internal/auth/auth.go b/internal/auth/auth.go new file mode 100644 index 0000000..6d9405b --- /dev/null +++ b/internal/auth/auth.go @@ -0,0 +1,87 @@ +package auth + +import ( + "context" + "errors" + "github.com/Unkn0wnCat/calapi/internal/ghost" + "github.com/Unkn0wnCat/calapi/internal/logger" + "github.com/go-chi/chi/middleware" + "github.com/spf13/viper" + "go.uber.org/zap" +) + +const ( + AuthTypeGhost = "GHOST" + AuthTypeNone = "NONE" +) + +type User struct { + ID string + Username string + Name string +} + +func Authenticate(ctx context.Context, username string, password string) (*User, error) { + switch viper.GetString("auth.type") { + case AuthTypeGhost: + return AuthenticateGhost(ctx, username, password) + case AuthTypeNone: + logger.Logger.Info("anonymously authenticated", + zap.String("requestId", middleware.GetReqID(ctx)), + ) + return &User{ + ID: "ANON-NO-AUTH", + Username: "anonymous", + Name: "Anonymous User", + }, nil + default: + return nil, errors.New("unknown authentication method") + } +} + +func AuthenticateGhost(ctx context.Context, username string, password string) (*User, error) { + api := ghost.GhostAPI{ + BaseURL: viper.GetString("auth.ghost.base_url"), + Jar: nil, + } + + _, err := api.Login(username, password) + if err != nil { + logger.Logger.Warn("invalid ghost credentials", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.Error(err), + ) + return nil, err + } + + ghostUser, err := api.UserSelf() + if err != nil { + logger.Logger.Error("ghost error", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.Error(err), + ) + return nil, err + } + + if len(ghostUser.Users) == 0 { + logger.Logger.Error("unexpected empty response from Ghost API", + zap.String("requestId", middleware.GetReqID(ctx)), + ) + return nil, errors.New("unexpected empty response from Ghost API") + } + + logger.Logger.Info("ghost authentication success", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("userId", ghostUser.Users[0].ID), + zap.String("username", ghostUser.Users[0].Email), + zap.Error(err), + ) + + user := &User{ + ID: ghostUser.Users[0].ID, + Username: ghostUser.Users[0].Email, + Name: ghostUser.Users[0].Name, + } + + return user, nil +} diff --git a/internal/auth/challenges.go b/internal/auth/challenges.go new file mode 100644 index 0000000..11bf4e3 --- /dev/null +++ b/internal/auth/challenges.go @@ -0,0 +1,88 @@ +package auth + +import ( + "context" + "errors" + "github.com/99designs/gqlgen/graphql" + "github.com/Unkn0wnCat/calapi/internal/logger" + "github.com/go-chi/chi/middleware" + "github.com/spf13/viper" + "github.com/vektah/gqlparser/v2/gqlerror" + "go.uber.org/zap" +) + +func ChallengeQuery(ctx context.Context) error { + if viper.GetBool("auth.anonymous_read") == true || viper.GetString("auth.type") == AuthTypeNone { + return nil // Anonymous querying is allowed. Anyone is allowed. + } + + user := ForContext(ctx) + if user == nil { + logger.Logger.Warn("unauthorized query attempt", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + ) + + graphql.AddError(ctx, &gqlerror.Error{ + Message: "A login token is required, but was not provided.", + Path: graphql.GetPath(ctx), + }) + + return errors.New("no user found") + } + + if user.ID == "ANON-NO-AUTH" { + // This login was done when auth was turned off. + logger.Logger.Warn("anonymous query attempt", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + ) + + graphql.AddError(ctx, &gqlerror.Error{ + Message: "The provided login token was anonymous, but this was since disabled. Please reauthenticate.", + Path: graphql.GetPath(ctx), + }) + + return errors.New("anonymous auth disabled") + } + + return nil // User is set. +} + +func ChallengeMutation(ctx context.Context) error { + if viper.GetString("auth.type") == AuthTypeNone { + return nil // Anonymous mutations are allowed. Anyone is allowed. + } + + user := ForContext(ctx) + if user == nil { + logger.Logger.Warn("unauthorized mutation attempt", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + ) + + graphql.AddError(ctx, &gqlerror.Error{ + Message: "A login token is required, but was not provided.", + Path: graphql.GetPath(ctx), + }) + + return errors.New("no user found") + } + + if user.ID == "ANON-NO-AUTH" { + // This login was done when auth was turned off. + logger.Logger.Warn("anonymous mutation attempt", + zap.String("requestId", middleware.GetReqID(ctx)), + zap.String("gqlPath", graphql.GetPath(ctx).String()), + ) + + graphql.AddError(ctx, &gqlerror.Error{ + Message: "The provided login token was anonymous, but this was since disabled. Please reauthenticate.", + Path: graphql.GetPath(ctx), + }) + + return errors.New("anonymous auth disabled") + } + + return nil // User is set. +} diff --git a/internal/auth/context.go b/internal/auth/context.go new file mode 100644 index 0000000..da58e0a --- /dev/null +++ b/internal/auth/context.go @@ -0,0 +1,60 @@ +package auth + +import ( + "context" + "github.com/Unkn0wnCat/calapi/internal/logger" + "github.com/go-chi/chi/middleware" + "go.uber.org/zap" + "net/http" + "strings" +) + +var userCtxKey = &contextKey{"user"} + +type contextKey struct { + name string +} + +func Middleware() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2) + authScheme := auth[0] + + if len(auth) != 2 || (!strings.EqualFold(authScheme, "bearer") && !strings.EqualFold(authScheme, "jwt")) { + // No authentication provided + next.ServeHTTP(w, r) + return + } + authPayload := auth[1] + + user, err := ParseJWT(authPayload) + if err != nil { + logger.Logger.Warn("invalid token provided", + zap.String("requestId", middleware.GetReqID(r.Context())), + zap.Error(err), + ) + http.Error(w, "Invalid Token", http.StatusUnauthorized) + return + } + + logger.Logger.Info("token validated", + zap.String("requestId", middleware.GetReqID(r.Context())), + zap.String("userId", user.ID), + zap.String("username", user.Username), + ) + + // put it in context + ctx := context.WithValue(r.Context(), userCtxKey, user) + + // and call the next with our new context + r = r.WithContext(ctx) + next.ServeHTTP(w, r) + }) + } +} + +func ForContext(ctx context.Context) *User { + raw, _ := ctx.Value(userCtxKey).(*User) + return raw +} diff --git a/internal/auth/jwtClaims.go b/internal/auth/jwtClaims.go new file mode 100644 index 0000000..bdcee63 --- /dev/null +++ b/internal/auth/jwtClaims.go @@ -0,0 +1,9 @@ +package auth + +import "github.com/golang-jwt/jwt/v5" + +type JwtClaims struct { + Username string `json:"username"` + Name string `json:"name"` + jwt.RegisteredClaims +} diff --git a/internal/auth/jwtHelpers.go b/internal/auth/jwtHelpers.go new file mode 100644 index 0000000..ce47a9e --- /dev/null +++ b/internal/auth/jwtHelpers.go @@ -0,0 +1,60 @@ +package auth + +import ( + "errors" + "github.com/golang-jwt/jwt/v5" + "github.com/spf13/viper" + "time" +) + +func ParseJWT(tokenString string) (*User, error) { + claims := JwtClaims{} + jwtSigningKey := []byte(viper.GetString("auth.secret")) + + token, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) { + return jwtSigningKey, nil + }) + if err != nil { + return nil, err + } + + if !token.Valid { + return nil, errors.New("invalid token") + } + + user := &User{ + ID: claims.Subject, + Username: claims.Username, + Name: claims.Name, + } + + return user, nil +} + +func MakeJWT(user *User) (string, error) { + if user == nil { + return "", errors.New("no user provided") + } + + claims := JwtClaims{ + Username: user.Username, + Name: user.Name, + RegisteredClaims: jwt.RegisteredClaims{ + Issuer: "calapi", + Subject: user.ID, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), + NotBefore: jwt.NewNumericDate(time.Now()), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + } + + jwtSigningKey := []byte(viper.GetString("auth.secret")) + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + ss, err := token.SignedString(jwtSigningKey) + if err != nil { + return "", err + } + + return ss, nil +} diff --git a/internal/ghost/api.go b/internal/ghost/api.go new file mode 100644 index 0000000..85cda98 --- /dev/null +++ b/internal/ghost/api.go @@ -0,0 +1,165 @@ +package ghost + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/cookiejar" + "net/url" + "time" +) + +type GhostAPI struct { + BaseURL string + Jar *cookiejar.Jar +} + +func (api *GhostAPI) get(path string, query string) (response *http.Response, err error) { + requestUrl, err := url.JoinPath(api.BaseURL, path) + if err != nil { + return nil, err + } + + if api.Jar == nil { + api.Jar, err = cookiejar.New(nil) + if err != nil { + return nil, err + } + } + + client := http.Client{Jar: api.Jar} + + response, err = client.Get(requestUrl + query) + if err != nil { + return response, err + } + + if response.StatusCode > 299 { + return response, fmt.Errorf("server returned status %d", response.StatusCode) + } + + return response, nil +} + +func (api *GhostAPI) post(path string, data interface{}) (response *http.Response, err error) { + requestUrl, err := url.JoinPath(api.BaseURL, path) + if err != nil { + return nil, err + } + + if api.Jar == nil { + api.Jar, err = cookiejar.New(nil) + if err != nil { + return nil, err + } + } + + client := http.Client{Jar: api.Jar} + + marshaledData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataReader := bytes.NewReader(marshaledData) + + response, err = client.Post(requestUrl, "application/json", dataReader) + if err != nil { + return response, err + } + + if response.StatusCode > 299 { + return response, fmt.Errorf("server returned status %d", response.StatusCode) + } + + return response, nil +} + +type LoginRequest struct { + Username string `json:"username"` + Password string `json:"password"` +} + +func (api *GhostAPI) Login(username string, password string) (token string, err error) { + data := LoginRequest{ + Username: username, + Password: password, + } + + response, err := api.post("/api/admin/session/", data) + if err != nil { + return "", err + } + + cookies := response.Cookies() + + for _, cookie := range cookies { + if cookie.Name != "ghost-admin-api-session" { + continue + } + + token = cookie.Value + break + } + + return token, nil +} + +type UserData struct { + Users []User `json:"users"` +} + +type User struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + Email string `json:"email"` + ProfileImage string `json:"profile_image"` + CoverImage string `json:"cover_image"` + Bio string `json:"bio"` + Website string `json:"website"` + Location string `json:"location"` + Facebook string `json:"facebook"` + Twitter string `json:"twitter"` + Accessibility string `json:"accessibility"` + Status string `json:"status"` + MetaTitle string `json:"meta_title"` + MetaDescription string `json:"meta_description"` + Tour string `json:"tour"` + LastSeen time.Time `json:"last_seen"` + CommentNotifications bool `json:"comment_notifications"` + FreeMemberSignupNotification bool `json:"free_member_signup_notification"` + PaidSubscriptionStartedNotification bool `json:"paid_subscription_started_notification"` + PaidSubscriptionCanceledNotification bool `json:"paid_subscription_canceled_notification"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Roles []Role `json:"roles"` + Url string `json:"url"` +} + +type Role struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +func (api *GhostAPI) UserSelf() (user *UserData, err error) { + response, err := api.get("/api/admin/users/me", "?include=roles") + if err != nil { + return nil, err + } + + user = &UserData{} + + decoder := json.NewDecoder(response.Body) + + err = decoder.Decode(user) + if err != nil { + return nil, err + } + + return user, nil +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go new file mode 100644 index 0000000..a857d62 --- /dev/null +++ b/internal/logger/logger.go @@ -0,0 +1,21 @@ +package logger + +import ( + "github.com/spf13/viper" + "go.uber.org/zap" +) + +var ( + Logger *zap.Logger + Sugar *zap.SugaredLogger +) + +func StartLogger() { + Logger, _ = zap.NewProduction() + + if viper.GetBool("development") { + Logger, _ = zap.NewDevelopment() + } + + Sugar = Logger.Sugar() +} diff --git a/internal/server/server.go b/internal/server/server.go index 318182e..6f99860 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1,28 +1,69 @@ package server import ( + chiprometheus "github.com/766b/chi-prometheus" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" "github.com/Unkn0wnCat/calapi/graph" - "log" + "github.com/Unkn0wnCat/calapi/internal/auth" + "github.com/Unkn0wnCat/calapi/internal/logger" + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.uber.org/zap" "net/http" "os" + "time" ) const defaultPort = "8080" +func logMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wrappedResponse := middleware.NewWrapResponseWriter(w, r.ProtoMajor) + latencyStart := time.Now() + + defer func() { + latency := time.Since(latencyStart) + + logger.Logger.Info("HTTP request finished", + zap.String("proto", r.Proto), + zap.String("uri", r.RequestURI), + zap.String("path", r.URL.Path), + zap.String("method", r.Method), + zap.String("remote", r.RemoteAddr), + zap.Int("status", wrappedResponse.Status()), + zap.Int("size", wrappedResponse.BytesWritten()), + zap.Duration("latency", latency), + zap.String("requestId", middleware.GetReqID(r.Context())), + ) + }() + + next.ServeHTTP(wrappedResponse, r) + }) +} + func Serve() { port := os.Getenv("PORT") if port == "" { port = defaultPort } + router := chi.NewRouter() + m := chiprometheus.NewMiddleware("calapi") + router.Use(m) + router.Use(middleware.RequestID) + router.Use(logMiddleware) + router.Use(middleware.Recoverer) + router.Use(auth.Middleware()) + srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) - http.Handle("/", playground.Handler("GraphQL playground", "/query")) - http.Handle("/query", srv) + router.Handle("/", playground.Handler("GraphQL playground", "/query")) + router.Handle("/query", srv) + router.Handle("/metrics", promhttp.Handler()) - log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) - log.Fatal(http.ListenAndServe(":"+port, nil)) + logger.Sugar.Infof("Now serving at http://localhost:%s/", port) + logger.Sugar.Fatal(http.ListenAndServe(":"+port, router)) }