diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7354317..1bc2372 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,7 +18,7 @@ jobs:
run:
shell: bash
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions/setup-go@v2
with:
@@ -47,7 +47,7 @@ jobs:
release-test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index cc259d1..7f29041 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index fe726c4..d18f7b2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -9,7 +9,7 @@ jobs:
goreleaser:
runs-on: ubuntu-20.04
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
diff --git a/README.md b/README.md
index 6d13079..1de388c 100644
--- a/README.md
+++ b/README.md
@@ -20,49 +20,49 @@ To execute the full build pipeline, run `make`; to build the docker images, run
You'll need to set some environment variables to tell Fluitans how to assign names and how to connect to a ZeroTier network controller. Specifically, you'll need to set:
-- FLUITANS_DOMAIN_NAME, which should be the parent domain name under which network domain names will be assigned, for example `fluitans.org` or `prakashlab.dedyn.io`. For web security reasons, the Fluitans app itself should be hosted on a separate domain name (for example `fluitans.sargassum.world`).
-- FLUITANS_ZT_CONTROLLER_SERVER, which should be the URL for the ZeroTier network controller's HTTP API. It needs to include the scheme `http://` or `https://`, for example `http://localhost:9993` or `https://zerotier-test.cloud.fluitans.sargassumworld`.
-- FLUITANS_ZT_CONTROLLER_AUTHTOKEN, which should be the contents of the authtoken.secret file saved by ZeroTier One in its working directory (more details [in ZeroTier's documentation](https://docs.zerotier.com/zerotier/zerotier.conf/)).
-- FLUITANS_DNS_SERVER, which should be the URL for the deSEC HTTP API. It needs to include the scheme `https://`, for example `https://desec.io`.
-- FLUITANS_DNS_AUTHTOKEN, which should be an authentication token for the deSEC HTTP API.
-- FLUITANS_SESSIONS_COOKIE_NOHTTPSONLY, which should be `true` if you are running Fluitans locally (as `localhost`) without HTTPS. If you are running Fluitans over the web, you should run it behind an HTTPS reverse proxy and you should leave FLUITANS_SESSION_COOKIE_NOHTTPSONLY unset.
-- FLUITANS_SESSIONS_AUTH_KEY, which should be set to a session key generated by running Fluitans without the FLUITANS_SESSION_AUTH_KEY set.
-- FLUITANS_AUTHN_ADMIN_PW_HASH, which should be set to the password hash generated by running Fluitans with a password set as FLUITANS_AUTHN_ADMIN_PW.
+- ZT_CONTROLLER_SERVER, which should be the URL for the ZeroTier network controller's HTTP API. It needs to include the scheme `http://` or `https://`, for example `http://localhost:9993` or `https://zerotier-test.cloud.fluitans.sargassumworld`.
+- ZT_CONTROLLER_AUTHTOKEN, which should be the contents of the authtoken.secret file saved by ZeroTier One in its working directory (more details [in ZeroTier's documentation](https://docs.zerotier.com/zerotier/zerotier.conf/)).
+- DNS_DOMAIN_NAME, which should be the parent domain name under which network domain names will be assigned, for example `fluitans.org` or `prakashlab.dedyn.io`. For web security reasons, the Fluitans app itself should be hosted on a separate domain name (for example `fluitans.sargassum.world`).
+- DNS_SERVER, which should be the URL for the deSEC HTTP API. It needs to include the scheme `https://`, for example `https://desec.io`.
+- DNS_AUTHTOKEN, which should be an authentication token for the deSEC HTTP API.
+- SESSIONS_COOKIE_NOHTTPSONLY, which should be `true` if you are running Fluitans locally (as `localhost`) without HTTPS. If you are running Fluitans over the web, you should run it behind an HTTPS reverse proxy and you should leave SESSION_COOKIE_NOHTTPSONLY unset.
+- SESSIONS_AUTH_KEY, which should be set to a session key generated by running Fluitans without the SESSION_AUTH_KEY set.
+- AUTHN_ADMIN_PW_HASH, which should be set to the password hash generated by running Fluitans with a password set as AUTHN_ADMIN_PW.
For example, you could generate the password and session key using:
```
-FLUITANS_AUTHN_ADMIN_PW='mypassword' make run
+AUTHN_ADMIN_PW='mypassword' make run
```
which will print a message like:
```
-Record this admin password hash for future use as FLUITANS_AUTHN_ADMIN_PW_HASH
+Record this admin password hash for future use as AUTHN_ADMIN_PW_HASH
(use single-quotes from shell to avoid string substitution with dollar-signs):
$argon2id$v=19$m=65536,t=1,p=2$EIV/HJ0DILHeNf2IC+qsGQ$BvBCCEsKUCKuAPI+pzM+sbCy/pdQdOF/FmHwx/yIusU
-Record this key for future use as FLUITANS_SESSIONS_AUTH_KEY:
+Record this key for future use as SESSIONS_AUTH_KEY:
QVG4y5EPPoDZjAzYc6j7I09iJum3w+hXNrB3O4HQvSc=
```
And then you could run the server in development mode (which you can log into with username `admin` and password `mypassword`) using:
```
-FLUITANS_DOMAIN_NAME='fluitans.org' \
-FLUITANS_ZT_CONTROLLER_SERVER='http://localhost:9993' \
-FLUITANS_ZT_CONTROLLER_AUTHTOKEN='0123456789abcdefghijklmn' \
-FLUITANS_DNS_SERVER='https://desec.io' \
-FLUITANS_DNS_AUTHTOKEN='abcdefghijklmn0123456789' \
-FLUITANS_SESSION_AUTH_KEY='QVG4y5EPPoDZjAzYc6j7I09iJum3w+hXNrB3O4HQvSc=' \
-FLUITANS_AUTHN_ADMIN_PW_HASH='$argon2id$v=19$m=65536,t=1,p=2$EIV/HJ0DILHeNf2IC+qsGQ$BvBCCEsKUCKuAPI+pzM+sbCy/pdQdOF/FmHwx/yIusU' \
+ZTCONTROLLER_SERVER='http://localhost:9993' \
+ZTCONTROLLER_AUTHTOKEN='0123456789abcdefghijklmn' \
+DNS_DOMAIN_NAME='fluitans.org' \
+DNS_SERVER='https://desec.io' \
+DNS_AUTHTOKEN='abcdefghijklmn0123456789' \
+SESSION_AUTH_KEY='QVG4y5EPPoDZjAzYc6j7I09iJum3w+hXNrB3O4HQvSc=' \
+AUTHN_ADMIN_PW_HASH='$argon2id$v=19$m=65536,t=1,p=2$EIV/HJ0DILHeNf2IC+qsGQ$BvBCCEsKUCKuAPI+pzM+sbCy/pdQdOF/FmHwx/yIusU' \
make run
```
Or you could run the built binary using:
```
-FLUITANS_DOMAIN_NAME='fluitans.org' \
-FLUITANS_ZT_CONTROLLER_SERVER='http://localhost:9993' \
-FLUITANS_ZT_CONTROLLER_AUTHTOKEN='0123456789abcdefghijklmn' \
-FLUITANS_DNS_SERVER='https://desec.io' \
-FLUITANS_DNS_AUTHTOKEN='abcdefghijklmn0123456789' \
-FLUITANS_SESSION_AUTH_KEY='QVG4y5EPPoDZjAzYc6j7I09iJum3w+hXNrB3O4HQvSc=' \
-FLUITANS_AUTHN_ADMIN_PW_HASH='$argon2id$v=19$m=65536,t=1,p=2$EIV/HJ0DILHeNf2IC+qsGQ$BvBCCEsKUCKuAPI+pzM+sbCy/pdQdOF/FmHwx/yIusU' \
+ZTCONTROLLER_SERVER='http://localhost:9993' \
+ZTCONTROLLER_AUTHTOKEN='0123456789abcdefghijklmn' \
+DNS_DOMAIN_NAME='fluitans.org' \
+DNS_SERVER='https://desec.io' \
+DNS_AUTHTOKEN='abcdefghijklmn0123456789' \
+SESSION_AUTH_KEY='QVG4y5EPPoDZjAzYc6j7I09iJum3w+hXNrB3O4HQvSc=' \
+AUTHN_ADMIN_PW_HASH='$argon2id$v=19$m=65536,t=1,p=2$EIV/HJ0DILHeNf2IC+qsGQ$BvBCCEsKUCKuAPI+pzM+sbCy/pdQdOF/FmHwx/yIusU' \
./fluitans
```
diff --git a/go.mod b/go.mod
index a06f2c9..7338cfc 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
github.com/gorilla/sessions v1.2.1
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
- github.com/labstack/echo/v4 v4.6.3
+ github.com/labstack/echo/v4 v4.7.0
github.com/labstack/gommon v0.3.1
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -28,9 +28,9 @@ require (
github.com/twmb/murmur3 v1.1.6
github.com/unrolled/secure v1.10.0
github.com/vmihailenco/msgpack/v5 v5.3.5
- golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
+ golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
- golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
+ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
)
diff --git a/go.sum b/go.sum
index d0b1a02..18d1d60 100644
--- a/go.sum
+++ b/go.sum
@@ -78,8 +78,9 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/labstack/echo/v4 v4.6.3 h1:VhPuIZYxsbPmo4m9KAkMU/el2442eB7EBFFhNTTT9ac=
github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A=
+github.com/labstack/echo/v4 v4.7.0 h1:8wHgZhoE9OT1NSLw6sfrX7ZGpWMtO5Zlfr68+BIo180=
+github.com/labstack/echo/v4 v4.7.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
@@ -159,8 +160,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
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-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 h1:syTAU9FwmvzEoIYMqcPHOcVm4H3U5u90WsvuYgwpETU=
+golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -169,6 +170,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -191,8 +193,8 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
+golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
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=
diff --git a/internal/app/fluitans/auth/authz.go b/internal/app/fluitans/auth/authz.go
index 2231c72..e357b33 100644
--- a/internal/app/fluitans/auth/authz.go
+++ b/internal/app/fluitans/auth/authz.go
@@ -5,7 +5,7 @@ import (
"github.com/labstack/echo/v4"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
// Authorization
@@ -28,10 +28,10 @@ func (a Auth) RequireAuthorized() error {
return echo.NewHTTPError(http.StatusNotFound, "unauthorized")
}
-func RequireAuthz(sc *sessions.Client) echo.MiddlewareFunc {
+func RequireAuthz(sc *session.Client) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
- a, _, err := GetWithSession(c, sc)
+ a, _, err := GetWithSession(c.Request(), sc, c.Logger())
if err != nil {
return err
}
diff --git a/internal/app/fluitans/auth/middleware.go b/internal/app/fluitans/auth/middleware.go
new file mode 100644
index 0000000..794f2a8
--- /dev/null
+++ b/internal/app/fluitans/auth/middleware.go
@@ -0,0 +1,89 @@
+package auth
+
+import (
+ "github.com/gorilla/sessions"
+ "github.com/labstack/echo/v4"
+ "github.com/pkg/errors"
+
+ "github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
+)
+
+type (
+ Handler func(c echo.Context, a Auth) error
+ HandlerWithSession func(c echo.Context, a Auth, sess *sessions.Session) error
+)
+
+func Handle(h Handler, sc *session.Client) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ a, sess, err := GetWithSession(c.Request(), sc, c.Logger())
+ // We don't expect the handler to write to the session, so we save it now
+ if serr := sess.Save(c.Request(), c.Response()); serr != nil {
+ return errors.Wrap(err, "couldn't save new session to replace invalid session")
+ }
+ if err != nil {
+ return err
+ }
+ return h(c, a)
+ }
+}
+
+func HandleWithSession(h HandlerWithSession, sc *session.Client) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ a, sess, err := GetWithSession(c.Request(), sc, c.Logger())
+ if err != nil {
+ return err
+ }
+ return h(c, a, sess)
+ }
+}
+
+// Router is a routing adapter between echo.Handler and this package's Handler, by
+// automatically extracting auth data from the session of the request.
+type Router struct {
+ er godest.EchoRouter
+ sc *session.Client
+}
+
+func NewRouter(er godest.EchoRouter, sc *session.Client) Router {
+ return Router{
+ er: er,
+ sc: sc,
+ }
+}
+
+func (r *Router) CONNECT(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.CONNECT(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) DELETE(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.DELETE(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) GET(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.GET(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) HEAD(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.HEAD(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) OPTIONS(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.OPTIONS(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) PATCH(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.PATCH(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) POST(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.POST(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) PUT(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.PUT(path, Handle(h, r.sc), m...)
+}
+
+func (r *Router) TRACE(path string, h Handler, m ...echo.MiddlewareFunc) *echo.Route {
+ return r.er.TRACE(path, Handle(h, r.sc), m...)
+}
diff --git a/internal/app/fluitans/auth/models.go b/internal/app/fluitans/auth/models.go
index b246aed..fd2ce65 100644
--- a/internal/app/fluitans/auth/models.go
+++ b/internal/app/fluitans/auth/models.go
@@ -2,10 +2,7 @@
package auth
import (
- "github.com/labstack/echo/v4"
-
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
- "github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
type Identity struct {
@@ -18,7 +15,7 @@ type CSRFBehavior struct {
}
type CSRF struct {
- Config sessions.CSRFOptions
+ Config session.CSRFOptions
Behavior CSRFBehavior
Token string
}
@@ -27,85 +24,3 @@ type Auth struct {
Identity Identity
CSRF CSRF
}
-
-// Middleware & Routing Adapter
-
-type AuthAwareHandler func(c echo.Context, a Auth) error
-
-func HandleWithAuth(h AuthAwareHandler, sc *sessions.Client) echo.HandlerFunc {
- return func(c echo.Context) error {
- a, _, err := GetWithSession(c, sc)
- if err != nil {
- return err
- }
- return h(c, a)
- }
-}
-
-// AuthAwareRouter is a routing adapter between echo.Handler and AuthAwareHandler, by automatically
-// extracting auth data from the session of the request.
-type AuthAwareRouter struct {
- er godest.EchoRouter
- sc *sessions.Client
-}
-
-func NewAuthAwareRouter(er godest.EchoRouter, sc *sessions.Client) AuthAwareRouter {
- return AuthAwareRouter{
- er: er,
- sc: sc,
- }
-}
-
-func (r *AuthAwareRouter) CONNECT(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.CONNECT(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) DELETE(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.DELETE(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) GET(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.GET(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) HEAD(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.HEAD(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) OPTIONS(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.OPTIONS(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) PATCH(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.PATCH(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) POST(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.POST(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) PUT(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.PUT(path, HandleWithAuth(h, r.sc), m...)
-}
-
-func (r *AuthAwareRouter) TRACE(
- path string, h AuthAwareHandler, m ...echo.MiddlewareFunc,
-) *echo.Route {
- return r.er.TRACE(path, HandleWithAuth(h, r.sc), m...)
-}
diff --git a/internal/app/fluitans/auth/sessions.go b/internal/app/fluitans/auth/sessions.go
index 5b81d92..1c8402a 100644
--- a/internal/app/fluitans/auth/sessions.go
+++ b/internal/app/fluitans/auth/sessions.go
@@ -2,14 +2,14 @@ package auth
import (
"encoding/gob"
- "fmt"
"net/http"
"github.com/gorilla/csrf"
"github.com/gorilla/sessions"
- "github.com/labstack/echo/v4"
+ "github.com/pkg/errors"
- sessionsc "github.com/sargassum-world/fluitans/internal/clients/sessions"
+ "github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
// Identity
@@ -25,20 +25,19 @@ func SetIdentity(s *sessions.Session, username string) {
func GetIdentity(s sessions.Session) (identity Identity, err error) {
if s.IsNew {
- return
+ return Identity{}, nil
}
rawIdentity, ok := s.Values["identity"]
if !ok {
// A zero value for Identity indicates that the session has no identity associated with it
- return
+ return Identity{}, nil
}
identity, ok = rawIdentity.(Identity)
if !ok {
- err = fmt.Errorf("unexpected type for field identity in session")
- return
+ return Identity{}, errors.Errorf("unexpected type for field identity in session")
}
- return
+ return identity, nil
}
// CSRF
@@ -53,7 +52,7 @@ func SetCSRFBehavior(s *sessions.Session, inlineToken bool) {
func GetCSRFBehavior(s sessions.Session) (behavior CSRFBehavior, err error) {
if s.IsNew {
- return
+ return CSRFBehavior{}, nil
}
rawBehavior, ok := s.Values["csrfBehavior"]
@@ -61,14 +60,13 @@ func GetCSRFBehavior(s sessions.Session) (behavior CSRFBehavior, err error) {
// By default, HTML responses won't inline the CSRF input fields (so responses can be cached),
// because the app only allows POST requests after user authentication. This default behavior
// can be overridden, e.g. on the login form for user authentication, with OverrideCSRFInlining.
- return
+ return CSRFBehavior{}, nil
}
behavior, ok = rawBehavior.(CSRFBehavior)
if !ok {
- err = fmt.Errorf("unexpected type for field csrfBehavior in session")
- return
+ return CSRFBehavior{}, errors.Errorf("unexpected type for field csrfBehavior in session")
}
- return
+ return behavior, nil
}
func (c *CSRF) SetInlining(r *http.Request, inlineToken bool) {
@@ -82,36 +80,42 @@ func (c *CSRF) SetInlining(r *http.Request, inlineToken bool) {
// Access
-func Get(c echo.Context, s sessions.Session, sc *sessionsc.Client) (a Auth, err error) {
- return GetFromRequest(c.Request(), s, sc)
+func Get(r *http.Request, s sessions.Session, sc *session.Client) (a Auth, err error) {
+ return GetFromRequest(r, s, sc)
}
-func GetFromRequest(r *http.Request, s sessions.Session, sc *sessionsc.Client) (a Auth, err error) {
+func GetFromRequest(r *http.Request, s sessions.Session, sc *session.Client) (a Auth, err error) {
a.Identity, err = GetIdentity(s)
if err != nil {
- return
+ return Auth{}, err
}
a.CSRF.Config = sc.Config.CSRFOptions
a.CSRF.Behavior, err = GetCSRFBehavior(s)
if err != nil {
- return
+ return Auth{}, err
}
if a.CSRF.Behavior.InlineToken {
a.CSRF.Token = csrf.Token(r)
}
- return
+ return a, nil
}
-func GetWithSession(c echo.Context, sc *sessionsc.Client) (a Auth, s *sessions.Session, err error) {
- s, err = sc.Get(c)
+func GetWithSession(
+ r *http.Request, sc *session.Client, l godest.Logger,
+) (a Auth, s *sessions.Session, err error) {
+ s, err = sc.Get(r)
if err != nil {
- return Auth{}, nil, err
+ // If the user doesn't have a valid session, create one
+ if s, err = sc.New(r); err != nil {
+ // When an error is returned, a new (valid) session is still created
+ l.Warnf("created new session to replace invalid session")
+ }
+ // We let the caller save the new session
}
- a, err = Get(c, *s, sc)
+ a, err = Get(r, *s, sc)
if err != nil {
return Auth{}, s, err
}
-
- return
+ return a, s, nil
}
diff --git a/internal/app/fluitans/client/globals.go b/internal/app/fluitans/client/globals.go
index 86a454d..0d5aff1 100644
--- a/internal/app/fluitans/client/globals.go
+++ b/internal/app/fluitans/client/globals.go
@@ -5,72 +5,68 @@ import (
"github.com/pkg/errors"
"github.com/sargassum-world/fluitans/internal/app/fluitans/conf"
- "github.com/sargassum-world/fluitans/internal/clients/authn"
"github.com/sargassum-world/fluitans/internal/clients/desec"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
"github.com/sargassum-world/fluitans/internal/clients/zerotier"
"github.com/sargassum-world/fluitans/internal/clients/ztcontrollers"
"github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/authn"
"github.com/sargassum-world/fluitans/pkg/godest/clientcache"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
type Clients struct {
Authn *authn.Client
Desec *desec.Client
- Sessions *sessions.Client
+ Sessions *session.Client
Zerotier *zerotier.Client
ZTControllers *ztcontrollers.Client
}
type Globals struct {
Config conf.Config
+ Cache clientcache.Cache
Clients *Clients
}
-func NewGlobals(l godest.Logger) (*Globals, error) {
- config, err := conf.GetConfig()
- if err != nil {
+func NewGlobals(l godest.Logger) (g *Globals, err error) {
+ g = &Globals{}
+ if g.Config, err = conf.GetConfig(); err != nil {
return nil, errors.Wrap(err, "couldn't set up application config")
}
-
- cache, err := clientcache.NewRistrettoCache(config.Cache)
- if err != nil {
+ if g.Cache, err = clientcache.NewRistrettoCache(g.Config.Cache); err != nil {
return nil, errors.Wrap(err, "couldn't set up client cache")
}
+ g.Clients = &Clients{}
- authnClient, err := authn.NewClient(l)
+ authnConfig, err := authn.GetConfig()
if err != nil {
- return nil, errors.Wrap(err, "couldn't set up authn client")
+ return nil, errors.Wrap(err, "couldn't set up authn config")
}
+ g.Clients.Authn = authn.NewClient(authnConfig)
- desecClient, err := desec.NewClient(config.DomainName, cache, l)
+ desecConfig, err := desec.GetConfig(g.Config.DomainName)
if err != nil {
- return nil, errors.Wrap(err, "couldn't set up desec client")
+ return nil, errors.Wrap(err, "couldn't set up desec config")
}
+ g.Clients.Desec = desec.NewClient(desecConfig, g.Cache, l)
- sessionsClient, err := sessions.NewMemStoreClient(l)
+ sessionsConfig, err := session.GetConfig()
if err != nil {
- return nil, errors.Wrap(err, "couldn't set up sessions client")
+ return nil, errors.Wrap(err, "couldn't set up sessions config")
}
+ g.Clients.Sessions = session.NewMemStoreClient(sessionsConfig)
- ztClient, err := zerotier.NewClient(cache, l)
+ ztConfig, err := zerotier.GetConfig()
if err != nil {
- return nil, errors.Wrap(err, "couldn't set up zerotier client")
+ return nil, errors.Wrap(err, "couldn't set up zerotier config")
}
+ g.Clients.Zerotier = zerotier.NewClient(ztConfig, g.Cache, l)
- ztcClient, err := ztcontrollers.NewClient(cache, l)
+ ztcConfig, err := ztcontrollers.GetConfig()
if err != nil {
- return nil, errors.Wrap(err, "couldn't set up zerotier controllers client")
+ return nil, errors.Wrap(err, "couldn't set up zerotier controllers config")
}
+ g.Clients.ZTControllers = ztcontrollers.NewClient(ztcConfig, g.Cache, l)
- return &Globals{
- Config: config,
- Clients: &Clients{
- Authn: authnClient,
- Desec: desecClient,
- Sessions: sessionsClient,
- Zerotier: ztClient,
- ZTControllers: ztcClient,
- },
- }, nil
+ return g, nil
}
diff --git a/internal/app/fluitans/conf/cache.go b/internal/app/fluitans/conf/cache.go
index 2f7f081..4aae766 100644
--- a/internal/app/fluitans/conf/cache.go
+++ b/internal/app/fluitans/conf/cache.go
@@ -7,33 +7,30 @@ import (
"github.com/sargassum-world/fluitans/pkg/godest/env"
)
+const cacheEnvPrefix = "CACHE_"
+
func getCacheConfig() (c ristretto.Config, err error) {
const defaultNumCounters = 3e6 // default: 300k items, ~9 MB of counters
- c.NumCounters, err = env.GetInt64("FLUITANS_CACHE_NUMCOUNTERS", defaultNumCounters)
+ c.NumCounters, err = env.GetInt64(cacheEnvPrefix+"CACHE_NUMCOUNTERS", defaultNumCounters)
if err != nil {
- err = errors.Wrap(err, "couldn't make numCounters config")
- return
+ return ristretto.Config{}, errors.Wrap(err, "couldn't make numCounters config")
}
const defaultMaxCost = 3e7 // default: up to 30 MB total with min cost weight of 1
- c.MaxCost, err = env.GetInt64("FLUITANS_CACHE_MAXCOST", defaultMaxCost)
+ c.MaxCost, err = env.GetInt64(cacheEnvPrefix+"MAXCOST", defaultMaxCost)
if err != nil {
- err = errors.Wrap(err, "couldn't make maxCost config")
- return
+ return ristretto.Config{}, errors.Wrap(err, "couldn't make maxCost config")
}
const defaultBufferItems = 64 // default: ristretto's recommended value
- c.BufferItems, err = env.GetInt64("FLUITANS_CACHE_BUFFERITEMS", defaultBufferItems)
+ c.BufferItems, err = env.GetInt64(cacheEnvPrefix+"BUFFERITEMS", defaultBufferItems)
if err != nil {
- err = errors.Wrap(err, "couldn't make bufferItems config")
- return
+ return ristretto.Config{}, errors.Wrap(err, "couldn't make bufferItems config")
}
- c.Metrics, err = env.GetBool("FLUITANS_CACHE_METRICS")
+ c.Metrics, err = env.GetBool(cacheEnvPrefix + "METRICS")
if err != nil {
- err = errors.Wrap(err, "couldn't make metrics config")
- return
+ return ristretto.Config{}, errors.Wrap(err, "couldn't make metrics config")
}
-
- return
+ return c, nil
}
diff --git a/internal/app/fluitans/conf/conf.go b/internal/app/fluitans/conf/conf.go
index b77762f..8ede67c 100644
--- a/internal/app/fluitans/conf/conf.go
+++ b/internal/app/fluitans/conf/conf.go
@@ -15,17 +15,14 @@ type Config struct {
func GetConfig() (c Config, err error) {
c.Cache, err = getCacheConfig()
if err != nil {
- err = errors.Wrap(err, "couldn't make cache config")
- return
+ return Config{}, errors.Wrap(err, "couldn't make cache config")
}
c.DomainName = getDomainName()
c.HTTP, err = getHTTPConfig()
if err != nil {
- err = errors.Wrap(err, "couldn't make http config")
- return
+ return Config{}, errors.Wrap(err, "couldn't make http config")
}
-
- return
+ return c, nil
}
diff --git a/internal/app/fluitans/conf/dns.go b/internal/app/fluitans/conf/dns.go
index 5df262e..bde424a 100644
--- a/internal/app/fluitans/conf/dns.go
+++ b/internal/app/fluitans/conf/dns.go
@@ -4,6 +4,8 @@ import (
"os"
)
+const dnsEnvPrefix = "DNS_" // note: this overlaps with the prefix for the desec client
+
func getDomainName() string {
- return os.Getenv("FLUITANS_DOMAIN_NAME")
+ return os.Getenv(dnsEnvPrefix + "DOMAIN_NAME")
}
diff --git a/internal/app/fluitans/conf/http.go b/internal/app/fluitans/conf/http.go
index 8295dcf..6d24e51 100644
--- a/internal/app/fluitans/conf/http.go
+++ b/internal/app/fluitans/conf/http.go
@@ -6,18 +6,18 @@ import (
"github.com/sargassum-world/fluitans/pkg/godest/env"
)
+const httpEnvPrefix = "HTTP_"
+
type HTTPConfig struct {
GzipLevel int
}
func getHTTPConfig() (c HTTPConfig, err error) {
const defaultGzipLevel = 1
- rawGzipLevel, err := env.GetInt64("FLUITANS_HTTP_GZIPLEVEL", defaultGzipLevel)
+ rawGzipLevel, err := env.GetInt64(httpEnvPrefix+"GZIPLEVEL", defaultGzipLevel)
if err != nil {
- err = errors.Wrap(err, "couldn't make gzip level config")
- return
+ return HTTPConfig{}, errors.Wrap(err, "couldn't make gzip level config")
}
c.GzipLevel = int(rawGzipLevel)
-
- return
+ return c, nil
}
diff --git a/internal/app/fluitans/httperr.go b/internal/app/fluitans/httperr.go
index 62f4927..5be1af8 100644
--- a/internal/app/fluitans/httperr.go
+++ b/internal/app/fluitans/httperr.go
@@ -1,4 +1,3 @@
-// Package fluitans provides the Fluitans server.
package fluitans
import (
@@ -10,7 +9,6 @@ import (
"github.com/pkg/errors"
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
"github.com/sargassum-world/fluitans/pkg/godest"
"github.com/sargassum-world/fluitans/pkg/godest/httperr"
"github.com/sargassum-world/fluitans/pkg/godest/session"
@@ -22,15 +20,15 @@ type ErrorData struct {
Messages []string
}
-func NewHTTPErrorHandler(tr godest.TemplateRenderer, sc *sessions.Client) echo.HTTPErrorHandler {
+func NewHTTPErrorHandler(tr godest.TemplateRenderer, sc *session.Client) echo.HTTPErrorHandler {
tr.MustHave("app/httperr.page.tmpl")
return func(err error, c echo.Context) {
c.Logger().Error(err)
// Check authentication & authorization
- a, sess, serr := auth.GetWithSession(c, sc)
+ a, sess, serr := auth.GetWithSession(c.Request(), sc, c.Logger())
if serr != nil {
- c.Logger().Error(errors.Wrap(serr, "couldn't get session+auth in error handler"))
+ c.Logger().Error(errors.Wrap(serr, "couldn't get auth in error handler"))
}
// Process error code
@@ -44,21 +42,19 @@ func NewHTTPErrorHandler(tr godest.TemplateRenderer, sc *sessions.Client) echo.H
}
// Consume & save session
- if sess != nil {
- messages, merr := session.GetErrorMessages(sess)
- if merr != nil {
- c.Logger().Error(errors.Wrap(
- merr, "couldn't get error messages from session in error handler",
- ))
- }
- errorData.Messages = messages
- if err := sess.Save(c.Request(), c.Response()); err != nil {
- c.Logger().Error(errors.Wrap(serr, "couldn't save session in error handler"))
- }
+ messages, merr := session.GetErrorMessages(sess)
+ if merr != nil {
+ c.Logger().Error(errors.Wrap(
+ merr, "couldn't get error messages from session in error handler",
+ ))
+ }
+ errorData.Messages = messages
+ if err := sess.Save(c.Request(), c.Response()); err != nil {
+ c.Logger().Error(errors.Wrap(serr, "couldn't save session in error handler"))
}
// Produce output
- tr.SetUncacheable(c.Response().Header())
+ godest.SetUncacheable(c.Response().Header())
if perr := tr.Page(
c.Response(), c.Request(), code, "app/httperr.page.tmpl", errorData, a,
); perr != nil {
@@ -68,22 +64,19 @@ func NewHTTPErrorHandler(tr godest.TemplateRenderer, sc *sessions.Client) echo.H
}
func NewCSRFErrorHandler(
- tr godest.TemplateRenderer, l echo.Logger, sc *sessions.Client,
+ tr godest.TemplateRenderer, l echo.Logger, sc *session.Client,
) http.HandlerFunc {
tr.MustHave("app/httperr.page.tmpl")
return func(w http.ResponseWriter, r *http.Request) {
l.Error(csrf.FailureReason(r))
// Check authentication & authorization
- sess, serr := session.Get(r, sc.Config.CookieName, sc.Store)
+ a, sess, serr := auth.GetWithSession(r, sc, l)
if serr != nil {
- l.Error(errors.Wrap(serr, "couldn't get session in error handler"))
+ l.Error(errors.Wrap(serr, "couldn't get auth in error handler"))
}
- var a auth.Auth
- if sess != nil {
- a, serr = auth.GetFromRequest(r, *sess, sc)
- if serr != nil {
- l.Error(errors.Wrap(serr, "couldn't get auth in error handler"))
- }
+ // Save the session in case it was freshly generated
+ if err := sess.Save(r, w); err != nil {
+ l.Error(errors.Wrap(serr, "couldn't save session in error handler"))
}
// Generate error code
@@ -101,7 +94,7 @@ func NewCSRFErrorHandler(
}
// Produce output
- tr.SetUncacheable(w.Header())
+ godest.SetUncacheable(w.Header())
if rerr := tr.Page(w, r, code, "app/httperr.page.tmpl", errorData, a); rerr != nil {
l.Error(errors.Wrap(rerr, "couldn't render error page in error handler"))
}
diff --git a/internal/app/fluitans/routes/auth/routes.go b/internal/app/fluitans/routes/auth/routes.go
index 02a33c3..8262e47 100644
--- a/internal/app/fluitans/routes/auth/routes.go
+++ b/internal/app/fluitans/routes/auth/routes.go
@@ -3,18 +3,18 @@ package auth
import (
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
- "github.com/sargassum-world/fluitans/internal/clients/authn"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
"github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/authn"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
type Handlers struct {
r godest.TemplateRenderer
ac *authn.Client
- sc *sessions.Client
+ sc *session.Client
}
-func New(r godest.TemplateRenderer, ac *authn.Client, sc *sessions.Client) *Handlers {
+func New(r godest.TemplateRenderer, ac *authn.Client, sc *session.Client) *Handlers {
return &Handlers{
r: r,
ac: ac,
@@ -23,8 +23,7 @@ func New(r godest.TemplateRenderer, ac *authn.Client, sc *sessions.Client) *Hand
}
func (h *Handlers) Register(er godest.EchoRouter) {
- ar := auth.NewAuthAwareRouter(er, h.sc)
er.GET("/csrf", h.HandleCSRFGet())
- ar.GET("/login", h.HandleLoginGet())
+ er.GET("/login", auth.HandleWithSession(h.HandleLoginGet(), h.sc))
er.POST("/sessions", h.HandleSessionsPost())
}
diff --git a/internal/app/fluitans/routes/auth/sessions.go b/internal/app/fluitans/routes/auth/sessions.go
index 062fff1..d5bb175 100644
--- a/internal/app/fluitans/routes/auth/sessions.go
+++ b/internal/app/fluitans/routes/auth/sessions.go
@@ -7,10 +7,11 @@ import (
"strings"
"github.com/gorilla/csrf"
+ "github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
+ "github.com/sargassum-world/fluitans/pkg/godest"
"github.com/sargassum-world/fluitans/pkg/godest/session"
)
@@ -22,17 +23,8 @@ type CSRFData struct {
func (h *Handlers) HandleCSRFGet() echo.HandlerFunc {
return func(c echo.Context) error {
- // Get session
- sess, err := h.sc.Get(c)
- if err != nil {
- return err
- }
- if err := sess.Save(c.Request(), c.Response()); err != nil {
- return err
- }
-
// Produce output
- h.r.SetUncacheable(c.Response().Header())
+ godest.SetUncacheable(c.Response().Header())
return c.JSON(http.StatusOK, CSRFData{
HeaderName: h.sc.Config.CSRFOptions.HeaderName,
FieldName: h.sc.Config.CSRFOptions.FieldName,
@@ -47,16 +39,10 @@ type LoginData struct {
ErrorMessages []string
}
-func (h *Handlers) HandleLoginGet() auth.AuthAwareHandler {
+func (h *Handlers) HandleLoginGet() auth.HandlerWithSession {
t := "auth/login.page.tmpl"
h.r.MustHave(t)
- return func(c echo.Context, a auth.Auth) error {
- // Check authentication & authorization
- sess, err := h.sc.Get(c)
- if err != nil {
- return err
- }
-
+ return func(c echo.Context, a auth.Auth, sess *sessions.Session) error {
// Consume & save session
errorMessages, err := session.GetErrorMessages(sess)
if err != nil {
@@ -88,13 +74,14 @@ func sanitizeReturnURL(returnURL string) (*url.URL, error) {
}
func handleAuthenticationSuccess(
- c echo.Context, username, returnURL string, omitCSRFToken bool, sc *sessions.Client,
+ c echo.Context, username, returnURL string, omitCSRFToken bool, sc *session.Client,
) error {
// Update session
- sess, err := sc.Regenerate(c)
+ sess, err := sc.Get(c.Request())
if err != nil {
return err
}
+ session.Regenerate(sess)
auth.SetIdentity(sess, username)
// This allows client-side Javascript to specify for server-side session data that we only need
// to provide CSRF tokens through the /csrf route and we can omit them from HTML response
@@ -113,9 +100,9 @@ func handleAuthenticationSuccess(
return c.Redirect(http.StatusSeeOther, u.String())
}
-func handleAuthenticationFailure(c echo.Context, returnURL string, sc *sessions.Client) error {
+func handleAuthenticationFailure(c echo.Context, returnURL string, sc *session.Client) error {
// Update session
- sess, serr := sc.Get(c)
+ sess, serr := sc.Get(c.Request())
if serr != nil {
return serr
}
@@ -170,10 +157,11 @@ func (h *Handlers) HandleSessionsPost() echo.HandlerFunc {
case "unauthenticated":
// TODO: add a client-side controller to automatically submit a logout request after the
// idle timeout expires, and display an inactivity logout message
- sess, err := h.sc.Invalidate(c)
+ sess, err := h.sc.Get(c.Request())
if err != nil {
return err
}
+ session.Invalidate(sess)
if err := sess.Save(c.Request(), c.Response()); err != nil {
return err
}
diff --git a/internal/app/fluitans/routes/controllers/controller.go b/internal/app/fluitans/routes/controllers/controller.go
index 85131a4..844a0ce 100644
--- a/internal/app/fluitans/routes/controllers/controller.go
+++ b/internal/app/fluitans/routes/controllers/controller.go
@@ -53,7 +53,7 @@ func getControllerData(
}, nil
}
-func (h *Handlers) HandleControllerGet() auth.AuthAwareHandler {
+func (h *Handlers) HandleControllerGet() auth.Handler {
t := "controllers/controller.page.tmpl"
h.r.MustHave(t)
return func(c echo.Context, a auth.Auth) error {
diff --git a/internal/app/fluitans/routes/controllers/controllers.go b/internal/app/fluitans/routes/controllers/controllers.go
index 2ad9ed1..80ceb07 100644
--- a/internal/app/fluitans/routes/controllers/controllers.go
+++ b/internal/app/fluitans/routes/controllers/controllers.go
@@ -6,7 +6,7 @@ import (
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
)
-func (h *Handlers) HandleControllersGet() auth.AuthAwareHandler {
+func (h *Handlers) HandleControllersGet() auth.Handler {
t := "controllers/controllers.page.tmpl"
h.r.MustHave(t)
return func(c echo.Context, a auth.Auth) error {
diff --git a/internal/app/fluitans/routes/controllers/routes.go b/internal/app/fluitans/routes/controllers/routes.go
index 803ff39..55ddaca 100644
--- a/internal/app/fluitans/routes/controllers/routes.go
+++ b/internal/app/fluitans/routes/controllers/routes.go
@@ -3,33 +3,30 @@ package controllers
import (
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
"github.com/sargassum-world/fluitans/internal/clients/zerotier"
"github.com/sargassum-world/fluitans/internal/clients/ztcontrollers"
"github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
type Handlers struct {
r godest.TemplateRenderer
ztcc *ztcontrollers.Client
ztc *zerotier.Client
- sc *sessions.Client
}
func New(
- r godest.TemplateRenderer,
- ztcc *ztcontrollers.Client, ztc *zerotier.Client, sc *sessions.Client,
+ r godest.TemplateRenderer, ztcc *ztcontrollers.Client, ztc *zerotier.Client,
) *Handlers {
return &Handlers{
r: r,
ztcc: ztcc,
ztc: ztc,
- sc: sc,
}
}
-func (h *Handlers) Register(er godest.EchoRouter) {
- ar := auth.NewAuthAwareRouter(er, h.sc)
+func (h *Handlers) Register(er godest.EchoRouter, sc *session.Client) {
+ ar := auth.NewRouter(er, sc)
ar.GET("/controllers", h.HandleControllersGet())
ar.GET("/controllers/:name", h.HandleControllerGet())
}
diff --git a/internal/app/fluitans/routes/dns/routes.go b/internal/app/fluitans/routes/dns/routes.go
index 039acb8..ecee4ec 100644
--- a/internal/app/fluitans/routes/dns/routes.go
+++ b/internal/app/fluitans/routes/dns/routes.go
@@ -4,10 +4,10 @@ package dns
import (
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
"github.com/sargassum-world/fluitans/internal/clients/desec"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
"github.com/sargassum-world/fluitans/internal/clients/zerotier"
"github.com/sargassum-world/fluitans/internal/clients/ztcontrollers"
"github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
type Handlers struct {
@@ -15,24 +15,23 @@ type Handlers struct {
dc *desec.Client
ztc *zerotier.Client
ztcc *ztcontrollers.Client
- sc *sessions.Client
}
func New(
r godest.TemplateRenderer,
- dc *desec.Client, ztc *zerotier.Client, ztcc *ztcontrollers.Client, sc *sessions.Client,
+ dc *desec.Client, ztc *zerotier.Client, ztcc *ztcontrollers.Client,
) *Handlers {
return &Handlers{
r: r,
dc: dc,
ztc: ztc,
ztcc: ztcc,
- sc: sc,
}
}
-func (h *Handlers) Register(er godest.EchoRouter) {
- ar := auth.NewAuthAwareRouter(er, h.sc)
- ar.GET("/dns", h.HandleServerGet(), auth.RequireAuthz(h.sc))
- er.POST("/dns/:subname/:type", h.HandleRRsetPost(), auth.RequireAuthz(h.sc))
+func (h *Handlers) Register(er godest.EchoRouter, sc *session.Client) {
+ ar := auth.NewRouter(er, sc)
+ az := auth.RequireAuthz(sc)
+ ar.GET("/dns", h.HandleServerGet(), az)
+ er.POST("/dns/:subname/:type", h.HandleRRsetPost(), az)
}
diff --git a/internal/app/fluitans/routes/dns/server.go b/internal/app/fluitans/routes/dns/server.go
index 8058115..830d4e6 100644
--- a/internal/app/fluitans/routes/dns/server.go
+++ b/internal/app/fluitans/routes/dns/server.go
@@ -71,7 +71,7 @@ func getServerData(
}, nil
}
-func (h *Handlers) HandleServerGet() auth.AuthAwareHandler {
+func (h *Handlers) HandleServerGet() auth.Handler {
t := "dns/server.page.tmpl"
h.r.MustHave(t)
return func(c echo.Context, a auth.Auth) error {
diff --git a/internal/app/fluitans/routes/home/routes.go b/internal/app/fluitans/routes/home/routes.go
index 8cdd912..811aa0f 100644
--- a/internal/app/fluitans/routes/home/routes.go
+++ b/internal/app/fluitans/routes/home/routes.go
@@ -5,28 +5,26 @@ import (
"github.com/labstack/echo/v4"
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
"github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
type Handlers struct {
- r godest.TemplateRenderer
- sc *sessions.Client
+ r godest.TemplateRenderer
}
-func New(r godest.TemplateRenderer, sc *sessions.Client) *Handlers {
+func New(r godest.TemplateRenderer) *Handlers {
return &Handlers{
- r: r,
- sc: sc,
+ r: r,
}
}
-func (h *Handlers) Register(er godest.EchoRouter) {
- ar := auth.NewAuthAwareRouter(er, h.sc)
+func (h *Handlers) Register(er godest.EchoRouter, sc *session.Client) {
+ ar := auth.NewRouter(er, sc)
ar.GET("/", h.HandleHomeGet())
}
-func (h *Handlers) HandleHomeGet() auth.AuthAwareHandler {
+func (h *Handlers) HandleHomeGet() auth.Handler {
t := "home/home.page.tmpl"
h.r.MustHave(t)
return func(c echo.Context, a auth.Auth) error {
diff --git a/internal/app/fluitans/routes/networks/devices.go b/internal/app/fluitans/routes/networks/devices.go
index c160c79..b492b36 100644
--- a/internal/app/fluitans/routes/networks/devices.go
+++ b/internal/app/fluitans/routes/networks/devices.go
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/labstack/echo/v4"
+ "github.com/pkg/errors"
"github.com/sargassum-world/fluitans/internal/clients/desec"
ztc "github.com/sargassum-world/fluitans/internal/clients/zerotier"
@@ -78,7 +79,7 @@ func confirmMemberNameManageable(
return "", err
}
if !named {
- return "", fmt.Errorf("network does not have a valid domain name for naming members")
+ return "", errors.Errorf("network does not have a valid domain name for naming members")
}
hasMember := false
for _, address := range memberAddresses {
@@ -88,7 +89,7 @@ func confirmMemberNameManageable(
}
}
if !hasMember {
- return "", fmt.Errorf(
+ return "", errors.Errorf(
"cannot set domain name for device which is not a network member",
)
}
diff --git a/internal/app/fluitans/routes/networks/network.go b/internal/app/fluitans/routes/networks/network.go
index 11fa94d..bbbd41a 100644
--- a/internal/app/fluitans/routes/networks/network.go
+++ b/internal/app/fluitans/routes/networks/network.go
@@ -31,7 +31,7 @@ func getRecordsOfType(
for subname, rrsets := range subnameRRsets {
filtered := desecc.FilterAndSortRRsets(rrsets, []string{rrsetType})
if len(filtered) > 1 {
- return nil, fmt.Errorf("unexpected number of RRsets for record")
+ return nil, errors.Errorf("unexpected number of RRsets for record")
}
if len(filtered) == 1 {
records[subname] = filtered[0].Records
@@ -54,7 +54,7 @@ func identifyMemberDomainNames(
for memberAddress, member := range zerotierMembers {
for _, ipAddress := range *member.IpAssignments {
for _, subname := range addressDomainNames[ipAddress] {
- domainName := fmt.Sprintf("%s.%s", subname, zoneDomainName)
+ domainName := subname + "." + zoneDomainName
memberDomainNames[memberAddress] = append(memberDomainNames[memberAddress], domainName)
}
}
@@ -124,7 +124,7 @@ func getMemberRecords(
func checkNamedByDNS(
ctx context.Context, networkName, networkID string, c *desecc.Client,
) (bool, error) {
- domainSuffix := fmt.Sprintf(".%s", c.Config.DomainName)
+ domainSuffix := "." + c.Config.DomainName
if !strings.HasSuffix(networkName, domainSuffix) {
return false, nil
}
@@ -178,15 +178,15 @@ func getNetworkDNSRecords(
) (networkDNS NetworkDNS, err error) {
namedByDNS, err := checkNamedByDNS(ctx, networkName, networkID, dc)
if err != nil || !namedByDNS {
- return
+ return NetworkDNS{}, err
}
networkDNS.Named = true
txtRecords, err := getRecordsOfType(subnameRRsets, "TXT")
if err != nil {
- return
+ return NetworkDNS{}, err
}
- confirmedSubname := strings.TrimSuffix(networkName, fmt.Sprintf(".%s", dc.Config.DomainName))
+ confirmedSubname := strings.TrimSuffix(networkName, "."+dc.Config.DomainName)
networkDNS.Aliases = identifyNetworkAliases(networkID, confirmedSubname, txtRecords)
aliases := make(map[string]bool, len(networkDNS.Aliases))
for _, alias := range networkDNS.Aliases {
@@ -195,19 +195,19 @@ func getNetworkDNSRecords(
subdomains, err := client.GetSubdomains(ctx, subnameRRsets, dc, c, cc)
if err != nil {
- return
+ return NetworkDNS{}, err
}
- networkSubname := strings.TrimSuffix(networkName, fmt.Sprintf(".%s", dc.Config.DomainName))
+ networkSubname := strings.TrimSuffix(networkName, "."+dc.Config.DomainName)
networkDNS.DeviceSubdomains = make(map[string]client.Subdomain)
for _, subdomain := range subdomains {
if subdomain.Subname != networkSubname && !strings.HasSuffix(
- subdomain.Subname, fmt.Sprintf(".%s", networkSubname),
+ subdomain.Subname, "."+networkSubname,
) && !aliases[subdomain.Subname] {
// Subdomain is unrelated to this network
continue
}
- if strings.HasSuffix(subdomain.Subname, fmt.Sprintf(".d.%s", networkSubname)) {
+ if strings.HasSuffix(subdomain.Subname, ".d."+networkSubname) {
// Subdomain is for a device
networkDNS.DeviceSubdomains[subdomain.Subname] = subdomain
continue
@@ -215,8 +215,7 @@ func getNetworkDNSRecords(
networkDNS.OtherSubdomains = append(networkDNS.OtherSubdomains, subdomain)
}
-
- return
+ return networkDNS, nil
}
type NetworkData struct {
@@ -243,11 +242,11 @@ func getNetworkData(
var subnameRRsets map[string][]desec.RRset
eg.Go(func() (err error) {
network, memberAddresses, err = c.GetNetworkInfo(ctx, *controller, id)
- return
+ return err
})
eg.Go(func() (err error) {
subnameRRsets, err = dc.GetRRsets(ctx)
- return
+ return err
})
if err = eg.Wait(); err != nil {
return nil, err
@@ -266,14 +265,14 @@ func getNetworkData(
members, err = getMemberRecords(
ctx, dc.Config.DomainName, *controller, *network, memberAddresses, subnameRRsets, c,
)
- return
+ return err
})
var networkDNS NetworkDNS
eg.Go(func() (err error) {
networkDNS, err = getNetworkDNSRecords(
egctx, *network.Id, *network.Name, subnameRRsets, c, cc, dc,
)
- return
+ return err
})
if err := eg.Wait(); err != nil {
return nil, err
@@ -289,7 +288,7 @@ func getNetworkData(
}, nil
}
-func (h *Handlers) HandleNetworkGet() auth.AuthAwareHandler {
+func (h *Handlers) HandleNetworkGet() auth.Handler {
t := "networks/network.page.tmpl"
h.r.MustHave(t)
return func(c echo.Context, a auth.Auth) error {
@@ -317,12 +316,12 @@ func nameNetwork(
}
// Check to see if the network was already named by DNS
- fqdn := fmt.Sprintf("%s.%s", name, dc.Config.DomainName)
+ fqdn := name + "." + dc.Config.DomainName
txtRRset, err := dc.GetRRset(ctx, name, "TXT")
if err != nil {
- return errors.Wrap(err, fmt.Sprintf(
- "couldn't check cache for DNS TXT RRset at %s for network %s", fqdn, id,
- ))
+ return errors.Wrapf(
+ err, "couldn't check cache for DNS TXT RRset at %s for network %s", fqdn, id,
+ )
}
if txtRRset != nil {
if _, hasID := client.GetNetworkID(txtRRset.Records); hasID {
@@ -338,9 +337,7 @@ func nameNetwork(
// zerotier-net-id=... record (but we should have a global lock on a get-and-patch to avoid
// data races)
// TODO: if the returned error code was an HTTP error, preserve the status code
- return errors.Wrap(err, fmt.Sprintf(
- "couldn't create a DNS TXT RRset at %s for network %s", fqdn, id,
- ))
+ return errors.Wrapf(err, "couldn't create a DNS TXT RRset at %s for network %s", fqdn, id)
}
return c.UpdateNetwork(
ctx, controller, id, zerotier.SetControllerNetworkJSONRequestBody{Name: &fqdn},
diff --git a/internal/app/fluitans/routes/networks/networks.go b/internal/app/fluitans/routes/networks/networks.go
index 2384c26..7fe3a73 100644
--- a/internal/app/fluitans/routes/networks/networks.go
+++ b/internal/app/fluitans/routes/networks/networks.go
@@ -44,7 +44,7 @@ func getNetworksData(
return networksData, nil
}
-func (h *Handlers) HandleNetworksGet() auth.AuthAwareHandler {
+func (h *Handlers) HandleNetworksGet() auth.Handler {
t := "networks/networks.page.tmpl"
h.r.MustHave(t)
return func(c echo.Context, a auth.Auth) error {
diff --git a/internal/app/fluitans/routes/networks/routes.go b/internal/app/fluitans/routes/networks/routes.go
index fbd0dea..28409ea 100644
--- a/internal/app/fluitans/routes/networks/routes.go
+++ b/internal/app/fluitans/routes/networks/routes.go
@@ -4,10 +4,10 @@ package networks
import (
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
"github.com/sargassum-world/fluitans/internal/clients/desec"
- "github.com/sargassum-world/fluitans/internal/clients/sessions"
"github.com/sargassum-world/fluitans/internal/clients/zerotier"
"github.com/sargassum-world/fluitans/internal/clients/ztcontrollers"
"github.com/sargassum-world/fluitans/pkg/godest"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
)
type Handlers struct {
@@ -15,34 +15,30 @@ type Handlers struct {
dc *desec.Client
ztc *zerotier.Client
ztcc *ztcontrollers.Client
- sc *sessions.Client
}
func New(
r godest.TemplateRenderer,
- dc *desec.Client, ztc *zerotier.Client, ztcc *ztcontrollers.Client, sc *sessions.Client,
+ dc *desec.Client, ztc *zerotier.Client, ztcc *ztcontrollers.Client,
) *Handlers {
return &Handlers{
r: r,
dc: dc,
ztc: ztc,
ztcc: ztcc,
- sc: sc,
}
}
-func (h *Handlers) Register(er godest.EchoRouter) {
- ar := auth.NewAuthAwareRouter(er, h.sc)
+func (h *Handlers) Register(er godest.EchoRouter, sc *session.Client) {
+ ar := auth.NewRouter(er, sc)
+ az := auth.RequireAuthz(sc)
ar.GET("/networks", h.HandleNetworksGet())
- er.POST("/networks", h.HandleNetworksPost(), auth.RequireAuthz(h.sc))
+ er.POST("/networks", h.HandleNetworksPost(), az)
ar.GET("/networks/:id", h.HandleNetworkGet())
- er.POST("/networks/:id", h.HandleNetworkPost(), auth.RequireAuthz(h.sc))
- er.POST("/networks/:id/name", h.HandleNetworkNamePost(), auth.RequireAuthz(h.sc))
- er.POST("/networks/:id/rules", h.HandleNetworkRulesPost(), auth.RequireAuthz(h.sc))
- er.POST("/networks/:id/devices", h.HandleDevicesPost(), auth.RequireAuthz(h.sc))
- er.POST(
- "/networks/:id/devices/:address/authorization", h.HandleDeviceAuthorizationPost(),
- auth.RequireAuthz(h.sc),
- )
- er.POST("/networks/:id/devices/:address/name", h.HandleDeviceNamePost(), auth.RequireAuthz(h.sc))
+ er.POST("/networks/:id", h.HandleNetworkPost(), az)
+ er.POST("/networks/:id/name", h.HandleNetworkNamePost(), az)
+ er.POST("/networks/:id/rules", h.HandleNetworkRulesPost(), az)
+ er.POST("/networks/:id/devices", h.HandleDevicesPost(), az)
+ er.POST("/networks/:id/devices/:address/authorization", h.HandleDeviceAuthorizationPost(), az)
+ er.POST("/networks/:id/devices/:address/name", h.HandleDeviceNamePost(), az)
}
diff --git a/internal/app/fluitans/routes/routes.go b/internal/app/fluitans/routes/routes.go
index c32897c..8213118 100644
--- a/internal/app/fluitans/routes/routes.go
+++ b/internal/app/fluitans/routes/routes.go
@@ -27,13 +27,13 @@ func New(r godest.TemplateRenderer, clients *client.Clients) *Handlers {
func (h *Handlers) Register(er godest.EchoRouter, em godest.Embeds) {
assets.RegisterStatic(er, em)
assets.NewTemplated(h.r).Register(er)
- home.New(h.r, h.clients.Sessions).Register(er)
+ home.New(h.r).Register(er, h.clients.Sessions)
auth.New(h.r, h.clients.Authn, h.clients.Sessions).Register(er)
- controllers.New(h.r, h.clients.ZTControllers, h.clients.Zerotier, h.clients.Sessions).Register(er)
+ controllers.New(h.r, h.clients.ZTControllers, h.clients.Zerotier).Register(er, h.clients.Sessions)
networks.New(
- h.r, h.clients.Desec, h.clients.Zerotier, h.clients.ZTControllers, h.clients.Sessions,
- ).Register(er)
+ h.r, h.clients.Desec, h.clients.Zerotier, h.clients.ZTControllers,
+ ).Register(er, h.clients.Sessions)
dns.New(
- h.r, h.clients.Desec, h.clients.Zerotier, h.clients.ZTControllers, h.clients.Sessions,
- ).Register(er)
+ h.r, h.clients.Desec, h.clients.Zerotier, h.clients.ZTControllers,
+ ).Register(er, h.clients.Sessions)
}
diff --git a/internal/app/fluitans/server.go b/internal/app/fluitans/server.go
index e32359c..478fc3c 100644
--- a/internal/app/fluitans/server.go
+++ b/internal/app/fluitans/server.go
@@ -20,6 +20,7 @@ import (
imw "github.com/sargassum-world/fluitans/internal/middleware"
"github.com/sargassum-world/fluitans/pkg/godest"
gmw "github.com/sargassum-world/fluitans/pkg/godest/middleware"
+ "github.com/sargassum-world/fluitans/pkg/godest/session"
"github.com/sargassum-world/fluitans/web"
)
@@ -41,20 +42,16 @@ func NewServer(e *echo.Echo) (s *Server, err error) {
),
)
if err != nil {
- s = nil
- err = errors.Wrap(err, "couldn't make template renderer")
- return
+ return nil, errors.Wrap(err, "couldn't make template renderer")
}
s.Globals, err = client.NewGlobals(e.Logger)
if err != nil {
- s = nil
- err = errors.Wrap(err, "couldn't make app globals")
- return
+ return nil, errors.Wrap(err, "couldn't make app globals")
}
s.Handlers = routes.New(s.Renderer, s.Globals.Clients)
- return
+ return s, nil
}
func (s *Server) Register(e *echo.Echo) {
@@ -102,9 +99,10 @@ func (s *Server) Register(e *echo.Echo) {
// Other Middleware
e.Pre(middleware.RemoveTrailingSlash())
- e.Use(s.Globals.Clients.Sessions.NewCSRFMiddleware(
+ e.Use(echo.WrapMiddleware(session.NewCSRFMiddleware(
+ s.Globals.Clients.Sessions.Config,
csrf.ErrorHandler(NewCSRFErrorHandler(s.Renderer, e.Logger, s.Globals.Clients.Sessions)),
- ))
+ )))
e.Use(imw.RequireContentTypes(echo.MIMEApplicationForm))
// TODO: enable Prometheus and rate-limiting
diff --git a/internal/clients/desec/cache.go b/internal/clients/desec/cache.go
index 17de13f..334455d 100644
--- a/internal/clients/desec/cache.go
+++ b/internal/clients/desec/cache.go
@@ -98,9 +98,9 @@ func (c *Cache) SetRRsetsByName(domainName, subname string, rrsets []desec.RRset
err := c.SetRRsetByNameAndType(domainName, subname, recordType, rrset)
if err != nil {
- return errors.Wrap(err, fmt.Sprintf(
- "couldn't set cache entry for the %s RRset for %s.%s", recordType, subname, domainName,
- ))
+ return errors.Wrapf(
+ err, "couldn't set cache entry for the %s RRset for %s.%s", recordType, subname, domainName,
+ )
}
}
return nil
@@ -113,9 +113,9 @@ func (c *Cache) GetRRsetsByName(domainName, subname string) ([]desec.RRset, erro
var value desec.RRset
keyExists, valueExists, err := c.Cache.GetEntry(key, &value)
if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf(
- "couldn't get cache entry for the %s RRset for %s.%s", recordType, subname, domainName,
- ))
+ return nil, errors.Wrapf(
+ err, "couldn't get cache entry for the %s RRset for %s.%s", recordType, subname, domainName,
+ )
}
if !keyExists {
diff --git a/internal/clients/desec/client.go b/internal/clients/desec/client.go
index a34d2ed..852da2e 100644
--- a/internal/clients/desec/client.go
+++ b/internal/clients/desec/client.go
@@ -24,6 +24,23 @@ type Client struct {
WriteLimiter *slidingwindows.MultiLimiter
}
+func NewClient(c Config, cache clientcache.Cache, l godest.Logger) *Client {
+ clientCache := Cache{
+ Cache: cache,
+ CostWeight: c.DNSServer.NetworkCostWeight,
+ TTL: c.APISettings.ReadCacheTTL,
+ RecordTypes: c.RecordTypes,
+ }
+ readLimiter := desec.NewReadLimiter(0)
+ return &Client{
+ Config: c,
+ Logger: l,
+ Cache: &clientCache,
+ ReadLimiter: readLimiter,
+ WriteLimiter: desec.NewRRSetWriteLimiter(0),
+ }
+}
+
func (c *Client) handleDesecMissingDomainError(res http.Response) error {
if res.StatusCode == http.StatusNotFound {
c.Cache.SetNonexistentDomainByName(c.Config.DomainName)
@@ -75,28 +92,6 @@ func (c *Client) tryAddLimitedRead() error {
return nil
}
-func NewClient(domainName string, cache clientcache.Cache, l godest.Logger) (*Client, error) {
- config, err := GetConfig(domainName)
- if err != nil {
- return nil, errors.Wrap(err, "couldn't set up desec client config")
- }
-
- clientCache := Cache{
- Cache: cache,
- CostWeight: config.DNSServer.NetworkCostWeight,
- TTL: config.APISettings.ReadCacheTTL,
- RecordTypes: config.RecordTypes,
- }
- readLimiter := desec.NewReadLimiter(0)
- return &Client{
- Config: config,
- Logger: l,
- Cache: &clientCache,
- ReadLimiter: readLimiter,
- WriteLimiter: desec.NewRRSetWriteLimiter(0),
- }, nil
-}
-
// Rate-Limiting
func CalculateBatchWaitDuration(
diff --git a/internal/clients/desec/conf.go b/internal/clients/desec/conf.go
index 9dbcb02..1cb7c35 100644
--- a/internal/clients/desec/conf.go
+++ b/internal/clients/desec/conf.go
@@ -11,6 +11,8 @@ import (
"github.com/sargassum-world/fluitans/pkg/godest/env"
)
+const envPrefix = "DNS_"
+
type Config struct {
DomainName string
DNSServer models.DNSServer
@@ -22,56 +24,49 @@ func GetConfig(domainName string) (c Config, err error) {
c.DomainName = domainName
c.DNSServer, err = getDNSServer()
if err != nil {
- err = errors.Wrap(err, "couldn't make DNS server config")
- return
+ return Config{}, errors.Wrap(err, "couldn't make DNS server config")
}
c.APISettings, err = GetAPISettings()
if err != nil {
- err = errors.Wrap(err, "couldn't make deSEC API settings")
- return
+ return Config{}, errors.Wrap(err, "couldn't make deSEC API settings")
}
c.RecordTypes = getRecordTypes()
- return
+ return c, nil
}
func getDNSServer() (s models.DNSServer, err error) {
- url, err := env.GetURLOrigin("FLUITANS_DNS_SERVER", "", "https")
+ url, err := env.GetURLOrigin(envPrefix+"SERVER", "", "https")
if err != nil {
- err = errors.Wrap(err, "couldn't make server url config")
- return
+ return models.DNSServer{}, errors.Wrap(err, "couldn't make server url config")
}
s.Server = url.String()
if len(s.Server) == 0 {
- s = models.DNSServer{}
- return
+ return models.DNSServer{}, nil
}
- s.API = strings.ToLower(env.GetString("FLUITANS_DNS_API", "desec"))
+ s.API = strings.ToLower(env.GetString(envPrefix+"API", "desec"))
if len(s.API) == 0 {
- s = models.DNSServer{}
- return
+ return models.DNSServer{}, nil
}
- s.Authtoken = os.Getenv("FLUITANS_DNS_AUTHTOKEN")
+ s.Authtoken = os.Getenv(envPrefix + "AUTHTOKEN")
if len(s.Authtoken) == 0 {
- s = models.DNSServer{}
- return
+ return models.DNSServer{}, nil
}
- s.Name = env.GetString("FLUITANS_DNS_NAME", url.Host)
+ s.Name = env.GetString(envPrefix+"NAME", url.Host)
s.Description = env.GetString(
- "FLUITANS_DNS_DESC",
+ envPrefix+"DESC",
"The default deSEC DNS server account specified in the environment variables.",
)
const defaultNetworkCost = 2.0
- s.NetworkCostWeight, err = env.GetFloat32("FLUITANS_DNS_NETWORKCOST", defaultNetworkCost)
+ s.NetworkCostWeight, err = env.GetFloat32(envPrefix+"NETWORKCOST", defaultNetworkCost)
if err != nil {
- err = errors.Wrap(err, "couldn't make network cost config")
- return
+ return models.DNSServer{}, errors.Wrap(err, "couldn't make network cost config")
}
- return
+ return s, nil
}
func getReadCacheTTL() (time.Duration, error) {
@@ -80,7 +75,7 @@ func getReadCacheTTL() (time.Duration, error) {
// API read requests. The cache will be consistent with the API at an infinite TTL (the default
// TTL) if we promise to only modify DNS records through Fluitans, and not independently through
// the deSEC server.
- rawTTL, err := env.GetFloat32("FLUITANS_DESEC_READ_CACHE_TTL", -1)
+ rawTTL, err := env.GetFloat32(envPrefix+"READ_CACHE_TTL", -1)
var ttl time.Duration = -1
if rawTTL >= 0 {
durationReadCacheTTL := time.Duration(rawTTL) * time.Second
@@ -96,17 +91,15 @@ func getReadCacheTTL() (time.Duration, error) {
func GetAPISettings() (s DesecAPISettings, err error) {
s.ReadCacheTTL, err = getReadCacheTTL()
if err != nil {
- err = errors.Wrap(err, "couldn't make readCacheTTL config")
- return
+ return DesecAPISettings{}, errors.Wrap(err, "couldn't make readCacheTTL config")
}
// The write limiter fill ratio above which RRset writes, rather than being executed immediately,
// will first be batched into groups based on the nearest rate limit
const defaultWriteSoftQuota = 0.34
- s.WriteSoftQuota, err = env.GetFloat32("FLUITANS_DESEC_WRITE_SOFT_QUOTA", defaultWriteSoftQuota)
+ s.WriteSoftQuota, err = env.GetFloat32(envPrefix+"WRITE_SOFT_QUOTA", defaultWriteSoftQuota)
if err != nil {
- err = errors.Wrap(err, "couldn't make writeSoftQuota config")
- return
+ return DesecAPISettings{}, errors.Wrap(err, "couldn't make writeSoftQuota config")
}
if s.WriteSoftQuota < 0 {
s.WriteSoftQuota = 0
@@ -114,8 +107,7 @@ func GetAPISettings() (s DesecAPISettings, err error) {
if s.WriteSoftQuota > 1 {
s.WriteSoftQuota = 1
}
-
- return
+ return s, nil
}
func getRecordTypes() []string {
diff --git a/internal/clients/desec/domain.go b/internal/clients/desec/domain.go
index 9c37b69..b86f01c 100644
--- a/internal/clients/desec/domain.go
+++ b/internal/clients/desec/domain.go
@@ -2,7 +2,6 @@ package desec
import (
"context"
- "fmt"
"github.com/pkg/errors"
@@ -16,9 +15,9 @@ func (c *Client) getDomainFromCache() (*desec.Domain, bool) {
domain, cacheHit, err := c.Cache.GetDomainByName(domainName)
if err != nil {
// Log the error but return as a cache miss so we can manually query the domain
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for the domain with name %s", domainName,
- )))
+ c.Logger.Error(errors.Wrapf(
+ err, "couldn't get the cache entry for the domain with name %s", domainName,
+ ))
return nil, false // treat an unparseable cache entry like a cache miss
}
return domain, cacheHit // cache hit with nil domain indicates nonexistent domain
diff --git a/internal/clients/desec/rrsets.go b/internal/clients/desec/rrsets.go
index 0c40c6e..4707adb 100644
--- a/internal/clients/desec/rrsets.go
+++ b/internal/clients/desec/rrsets.go
@@ -2,7 +2,6 @@ package desec
import (
"context"
- "fmt"
"strings"
"github.com/pkg/errors"
@@ -45,9 +44,9 @@ func (c *Client) getRRsetsFromCache() map[string][]desec.RRset {
subnames, err := c.Cache.GetSubnames(domainName)
if err != nil {
// Log the error but return as a cache miss so we can manually query the RRsets
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for the RRsets for %s", domainName,
- )))
+ c.Logger.Error(errors.Wrapf(
+ err, "couldn't get the cache entry for the RRsets for %s", domainName,
+ ))
return nil // treat an unparseable cache entry like a cache miss
}
@@ -129,9 +128,9 @@ func (c *Client) getSubnameRRsetsFromCache(subname string) []desec.RRset {
rrsets, err := c.Cache.GetRRsetsByName(domainName, subname)
if err != nil {
// Log the error but return as a cache miss so we can manually query the RRsets
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for one of the RRsets for %s.%s", subname, domainName,
- )))
+ c.Logger.Error(errors.Wrapf(
+ err, "couldn't get the cache entry for one of the RRsets for %s.%s", subname, domainName,
+ ))
return nil // treat an unparseable cache entry like a cache miss
}
@@ -187,9 +186,10 @@ func (c *Client) getRRsetFromCache(subname, recordType string) (*desec.RRset, bo
rrset, cacheHit, err := c.Cache.GetRRsetByNameAndType(domainName, subname, recordType)
if err != nil {
// Log the error but return as a cache miss so we can manually query the RRsets
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for the %s RRsets for %s.%s", recordType, subname, domainName,
- )))
+ c.Logger.Error(errors.Wrapf(
+ err, "couldn't get the cache entry for the %s RRsets for %s.%s",
+ recordType, subname, domainName,
+ ))
return nil, false // treat an unparseable cache entry like a cache miss
}
diff --git a/internal/clients/sessions/client.go b/internal/clients/sessions/client.go
deleted file mode 100644
index 16c554a..0000000
--- a/internal/clients/sessions/client.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Package sessions provides a high-level client for session management
-package sessions
-
-import (
- "net/http"
-
- "github.com/gorilla/csrf"
- "github.com/gorilla/sessions"
- "github.com/labstack/echo/v4"
- "github.com/pkg/errors"
- "github.com/quasoft/memstore"
-
- "github.com/sargassum-world/fluitans/pkg/godest"
- "github.com/sargassum-world/fluitans/pkg/godest/session"
-)
-
-type Client struct {
- Config Config
- Logger godest.Logger
- // TODO: allow configuration to use sqlite for a persistent session store
- Store sessions.Store
-}
-
-func (sc *Client) Get(c echo.Context) (*sessions.Session, error) {
- return session.Get(c.Request(), sc.Config.CookieName, sc.Store)
-}
-
-func (sc *Client) Regenerate(c echo.Context) (*sessions.Session, error) {
- return session.Regenerate(c.Request(), sc.Config.CookieName, sc.Store)
-}
-
-func (sc *Client) Invalidate(c echo.Context) (*sessions.Session, error) {
- return session.Invalidate(c.Request(), sc.Config.CookieName, sc.Store)
-}
-
-func (sc *Client) NewCSRFMiddleware(opts ...csrf.Option) echo.MiddlewareFunc {
- sameSite := csrf.SameSiteDefaultMode
- switch sc.Config.CookieOptions.SameSite {
- case http.SameSiteLaxMode:
- sameSite = csrf.SameSiteLaxMode
- case http.SameSiteStrictMode:
- sameSite = csrf.SameSiteStrictMode
- case http.SameSiteNoneMode:
- sameSite = csrf.SameSiteNoneMode
- }
- options := []csrf.Option{
- csrf.Path(sc.Config.CookieOptions.Path),
- csrf.Domain(sc.Config.CookieOptions.Domain),
- csrf.MaxAge(sc.Config.CookieOptions.MaxAge),
- csrf.Secure(sc.Config.CookieOptions.Secure),
- csrf.HttpOnly(sc.Config.CookieOptions.HttpOnly),
- csrf.SameSite(sameSite),
- csrf.RequestHeader(sc.Config.CSRFOptions.HeaderName),
- csrf.FieldName(sc.Config.CSRFOptions.FieldName),
- }
- options = append(options, opts...)
- return echo.WrapMiddleware(csrf.Protect(sc.Config.AuthKey, options...))
-}
-
-func NewMemStoreClient(l godest.Logger) (*Client, error) {
- config, err := GetConfig()
- if err != nil {
- return nil, errors.Wrap(err, "couldn't set up sessions client config")
- }
-
- store := memstore.NewMemStore(config.AuthKey)
- store.Options = &config.CookieOptions
-
- return &Client{
- Config: config,
- Logger: l,
- Store: store,
- }, nil
-}
diff --git a/internal/clients/zerotier/client.go b/internal/clients/zerotier/client.go
index de1a3b1..d4c06a2 100644
--- a/internal/clients/zerotier/client.go
+++ b/internal/clients/zerotier/client.go
@@ -2,8 +2,6 @@
package zerotier
import (
- "github.com/pkg/errors"
-
"github.com/sargassum-world/fluitans/pkg/godest"
"github.com/sargassum-world/fluitans/pkg/godest/clientcache"
)
@@ -14,17 +12,12 @@ type Client struct {
Cache *Cache
}
-func NewClient(cache clientcache.Cache, l godest.Logger) (*Client, error) {
- config, err := GetConfig()
- if err != nil {
- return nil, errors.Wrap(err, "couldn't set up zerotier client config")
- }
-
+func NewClient(c Config, cache clientcache.Cache, l godest.Logger) *Client {
return &Client{
- Config: config,
+ Config: c,
Logger: l,
Cache: &Cache{
Cache: cache,
},
- }, nil
+ }
}
diff --git a/internal/clients/zerotier/conf.go b/internal/clients/zerotier/conf.go
index 152f41c..9444e12 100644
--- a/internal/clients/zerotier/conf.go
+++ b/internal/clients/zerotier/conf.go
@@ -6,6 +6,8 @@ import (
"github.com/sargassum-world/fluitans/pkg/godest/env"
)
+const envPrefix = "ZEROTIER_"
+
type Config struct {
DNS ZTDNSSettings
}
@@ -13,34 +15,31 @@ type Config struct {
func GetConfig() (c Config, err error) {
c.DNS, err = GetDNSSettings()
if err != nil {
- err = errors.Wrap(err, "couldn't make Zerotier DNS settings")
- return
+ return Config{}, errors.Wrap(err, "couldn't make Zerotier DNS settings")
}
- return
+ return c, nil
}
func getNetworkTTL() (int64, error) {
const defaultTTL = 24 * 60 * 60 // default: 24 hours
- return env.GetInt64("FLUITANS_ZEROTIER_DNS_NETWORKTTL", defaultTTL)
+ return env.GetInt64(envPrefix+"DNS_NETWORKTTL", defaultTTL)
}
func getDeviceTTL() (int64, error) {
const defaultTTL = 1 * 60 * 60 // default: 1 hour
- return env.GetInt64("FLUITANS_ZEROTIER_DNS_DEVICETTL", defaultTTL)
+ return env.GetInt64(envPrefix+"DNS_DEVICETTL", defaultTTL)
}
func GetDNSSettings() (s ZTDNSSettings, err error) {
s.NetworkTTL, err = getNetworkTTL()
if err != nil {
- err = errors.Wrap(err, "couldn't make network record TTL config")
- return
+ return ZTDNSSettings{}, errors.Wrap(err, "couldn't make network record TTL config")
}
s.DeviceTTL, err = getDeviceTTL()
if err != nil {
- err = errors.Wrap(err, "couldn't make device record TTL config")
- return
+ return ZTDNSSettings{}, errors.Wrap(err, "couldn't make device record TTL config")
}
- return
+ return s, nil
}
diff --git a/internal/clients/zerotier/networks.go b/internal/clients/zerotier/networks.go
index 94822a0..ba5d558 100644
--- a/internal/clients/zerotier/networks.go
+++ b/internal/clients/zerotier/networks.go
@@ -3,7 +3,6 @@ package zerotier
import (
"context"
"encoding/json"
- "fmt"
"net/http"
"github.com/pkg/errors"
@@ -26,9 +25,9 @@ func (c *Client) getNetworkIDsFromCache(
networkIDs, err := cc.Cache.GetNetworkIDsByServer(controller.Server)
if err != nil {
// Log the error but return as a cache miss so we can manually query the network IDs
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for the network IDs controlled by %s", controller.Name,
- )))
+ c.Logger.Error(errors.Wrapf(
+ err, "couldn't get the cache entry for the network IDs controlled by %s", controller.Name,
+ ))
return nil // treat an unparseable cache entry like a cache miss
}
@@ -85,7 +84,7 @@ func (c *Client) GetAllNetworkIDs(
eg.Go(func(i int, controller ztcontrollers.Controller) func() error {
return func() (err error) {
allNetworkIDs[i], err = c.GetNetworkIDs(ctx, controller, cc)
- return
+ return err
}
}(i, controller))
}
@@ -104,7 +103,7 @@ func (c *Client) GetNetworks(
eg.Go(func(i int, id string) func() error {
return func() (err error) {
networks[i], err = c.GetNetwork(ctx, controller, id)
- return
+ return err
}
}(i, id))
}
@@ -126,7 +125,7 @@ func (c *Client) GetAllNetworks(
ctx context.Context, controllers []ztcontrollers.Controller, ids [][]string,
) ([]map[string]zerotier.ControllerNetwork, error) {
if len(controllers) != len(ids) {
- return nil, fmt.Errorf("lists of controllers and ids must have the same length")
+ return nil, errors.Errorf("lists of controllers and ids must have the same length")
}
eg, ctx := errgroup.WithContext(ctx)
@@ -135,7 +134,7 @@ func (c *Client) GetAllNetworks(
eg.Go(func(i int, controller ztcontrollers.Controller, someIDs []string) func() error {
return func() (err error) {
allNetworks[i], err = c.GetNetworks(ctx, controller, someIDs)
- return
+ return err
}
}(i, controller, ids[i]))
}
@@ -152,9 +151,7 @@ func (c *Client) getNetworkFromCache(id string) (*zerotier.ControllerNetwork, bo
network, cacheHit, err := c.Cache.GetNetworkByID(id)
if err != nil {
// Log the error but return as a cache miss so we can manually query the network
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for the network with id %s", id,
- )))
+ c.Logger.Error(errors.Wrapf(err, "couldn't get the cache entry for the network with id %s", id))
return nil, false // treat an unparseable cache entry like a cache miss
}
return network, cacheHit // cache hit with nil rrset indicates nonexistent RRset
@@ -197,9 +194,9 @@ func (c *Client) getNetworkMemberAddressesFromCache(networkID string) []string {
addresses, err := c.Cache.GetNetworkMembersByID(networkID)
if err != nil {
// Log the error but return as a cache miss so we can manually query the member addresses
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for the member addresses of network %s", networkID,
- )))
+ c.Logger.Error(errors.Wrapf(
+ err, "couldn't get the cache entry for the member addresses of network %s", networkID,
+ ))
return nil // treat an unparseable cache entry like a cache miss
}
@@ -253,11 +250,11 @@ func (c *Client) GetNetworkInfo(
var addresses []string
eg.Go(func() (err error) {
network, err = c.GetNetwork(ctx, controller, id)
- return
+ return err
})
eg.Go(func() (err error) {
addresses, err = c.GetNetworkMemberAddresses(ctx, controller, id)
- return
+ return err
})
if err := eg.Wait(); err != nil {
return nil, nil, err
diff --git a/internal/clients/zerotier/status.go b/internal/clients/zerotier/status.go
index 9078947..252e5fa 100644
--- a/internal/clients/zerotier/status.go
+++ b/internal/clients/zerotier/status.go
@@ -52,11 +52,11 @@ func (c *Client) GetControllerInfo(
var networkIDs []string
eg.Go(func() (err error) {
status, controllerStatus, err = c.GetControllerStatuses(ctx, controller, cc)
- return
+ return err
})
eg.Go(func() (err error) {
networkIDs, err = c.GetNetworkIDs(ctx, controller, cc)
- return
+ return err
})
if err := eg.Wait(); err != nil {
return nil, nil, nil, err
diff --git a/internal/clients/ztcontrollers/client.go b/internal/clients/ztcontrollers/client.go
index 0de022d..1745670 100644
--- a/internal/clients/ztcontrollers/client.go
+++ b/internal/clients/ztcontrollers/client.go
@@ -3,8 +3,6 @@
package ztcontrollers
import (
- "github.com/pkg/errors"
-
"github.com/sargassum-world/fluitans/pkg/godest"
"github.com/sargassum-world/fluitans/pkg/godest/clientcache"
)
@@ -15,17 +13,12 @@ type Client struct {
Cache *Cache
}
-func NewClient(cache clientcache.Cache, l godest.Logger) (*Client, error) {
- config, err := GetConfig()
- if err != nil {
- return nil, errors.Wrap(err, "couldn't set up zerotier controllers client config")
- }
-
+func NewClient(c Config, cache clientcache.Cache, l godest.Logger) *Client {
return &Client{
- Config: config,
+ Config: c,
Logger: l,
Cache: &Cache{
Cache: cache,
},
- }, nil
+ }
}
diff --git a/internal/clients/ztcontrollers/conf.go b/internal/clients/ztcontrollers/conf.go
index e37a341..82ef5de 100644
--- a/internal/clients/ztcontrollers/conf.go
+++ b/internal/clients/ztcontrollers/conf.go
@@ -8,6 +8,8 @@ import (
"github.com/sargassum-world/fluitans/pkg/godest/env"
)
+const envPrefix = "ZTCONTROLLER_"
+
type Config struct {
Controller Controller
}
@@ -15,43 +17,36 @@ type Config struct {
func GetConfig() (c Config, err error) {
c.Controller, err = GetController()
if err != nil {
- err = errors.Wrap(err, "couldn't make Zerotier controller config")
- return
+ return Config{}, errors.Wrap(err, "couldn't make Zerotier controller config")
}
-
- return
+ return c, nil
}
func GetController() (c Controller, err error) {
- url, err := env.GetURLOrigin("FLUITANS_ZT_CONTROLLER_SERVER", "", "http")
+ url, err := env.GetURLOrigin(envPrefix+"SERVER", "", "http")
if err != nil {
- err = errors.Wrap(err, "couldn't make server url config")
- return
+ return Controller{}, errors.Wrap(err, "couldn't make server url config")
}
c.Server = url.String()
if len(c.Server) == 0 {
- c = Controller{}
- return
+ return Controller{}, nil
}
- c.Authtoken = os.Getenv("FLUITANS_ZT_CONTROLLER_AUTHTOKEN")
+ c.Authtoken = os.Getenv(envPrefix + "AUTHTOKEN")
if len(c.Authtoken) == 0 {
- c = Controller{}
- return
+ return Controller{}, nil
}
- c.Name = env.GetString("FLUITANS_ZT_CONTROLLER_NAME", url.Host)
+ c.Name = env.GetString(envPrefix+"NAME", url.Host)
c.Description = env.GetString(
- "FLUITANS_ZT_CONTROLLER_DESC",
+ envPrefix+"DESC",
"The default ZeroTier network controller specified in the environment variables.",
)
const defaultNetworkCost = 1.0
- c.NetworkCostWeight, err = env.GetFloat32("FLUITANS_ZT_CONTROLLER_NETWORKCOST", defaultNetworkCost)
+ c.NetworkCostWeight, err = env.GetFloat32(envPrefix+"NETWORKCOST", defaultNetworkCost)
if err != nil {
- err = errors.Wrap(err, "couldn't make network cost config")
- return
+ return Controller{}, errors.Wrap(err, "couldn't make network cost config")
}
-
- return
+ return c, nil
}
diff --git a/internal/clients/ztcontrollers/controllers.go b/internal/clients/ztcontrollers/controllers.go
index aa30a43..9c51fb6 100644
--- a/internal/clients/ztcontrollers/controllers.go
+++ b/internal/clients/ztcontrollers/controllers.go
@@ -119,9 +119,9 @@ func (c *Client) FindControllerByAddress(ctx context.Context, address string) (*
controller, err := c.checkCachedController(ctx, address)
if err != nil {
// Log the error and proceed to manually query all controllers
- c.Logger.Error(err, errors.Wrap(err, fmt.Sprintf(
- "couldn't handle the cache entry for the zerotier controller with address %s", address,
- )))
+ c.Logger.Error(err, errors.Wrapf(
+ err, "couldn't handle the cache entry for the zerotier controller with address %s", address,
+ ))
} else if controller != nil {
return controller, nil
}
@@ -155,9 +155,9 @@ func (c *Client) getAddressFromCache(controller Controller) (string, bool) {
address, cacheHit, err := c.Cache.GetAddressByServer(controller.Server)
if err != nil {
// Log the error but return as a cache miss so we can manually query the RRsets
- c.Logger.Error(errors.Wrap(err, fmt.Sprintf(
- "couldn't get the cache entry for the Zerotier address for %s", controller.Server,
- )))
+ c.Logger.Error(errors.Wrapf(
+ err, "couldn't get the cache entry for the Zerotier address for %s", controller.Server,
+ ))
return "", false // treat an unparseable cache entry like a cache miss
}
diff --git a/internal/clients/authn/client.go b/pkg/godest/authn/client.go
similarity index 75%
rename from internal/clients/authn/client.go
rename to pkg/godest/authn/client.go
index d42baa0..b671a22 100644
--- a/internal/clients/authn/client.go
+++ b/pkg/godest/authn/client.go
@@ -5,26 +5,16 @@ import (
"crypto/subtle"
"github.com/alexedwards/argon2id"
- "github.com/pkg/errors"
-
- "github.com/sargassum-world/fluitans/pkg/godest"
)
type Client struct {
Config Config
- Logger godest.Logger
}
-func NewClient(l godest.Logger) (*Client, error) {
- config, err := GetConfig()
- if err != nil {
- return nil, errors.Wrap(err, "couldn't set up sessions client config")
- }
-
+func NewClient(c Config) *Client {
return &Client{
- Config: *config,
- Logger: l,
- }, nil
+ Config: c,
+ }
}
func (c *Client) CheckCredentials(username, password string) (bool, error) {
diff --git a/internal/clients/authn/conf.go b/pkg/godest/authn/conf.go
similarity index 57%
rename from internal/clients/authn/conf.go
rename to pkg/godest/authn/conf.go
index 326d8d0..47eb965 100644
--- a/internal/clients/authn/conf.go
+++ b/pkg/godest/authn/conf.go
@@ -9,6 +9,8 @@ import (
"github.com/sargassum-world/fluitans/pkg/godest/env"
)
+const envPrefix = "AUTHN_"
+
type Config struct {
NoAuth bool
Argon2idParams argon2id.Params
@@ -16,50 +18,47 @@ type Config struct {
AdminPasswordHash string
}
-func GetConfig() (*Config, error) {
- noAuth, err := getNoAuth()
+func GetConfig() (c Config, err error) {
+ c.NoAuth, err = getNoAuth()
if err != nil {
- return nil, errors.Wrap(err, "couldn't make authentication config")
+ return Config{}, errors.Wrap(err, "couldn't make authentication config")
}
- argon2idParams, err := getArgon2idParams()
+ c.Argon2idParams, err = getArgon2idParams()
if err != nil {
- return nil, errors.Wrap(err, "couldn't make password hashing config")
+ return Config{}, errors.Wrap(err, "couldn't make password hashing config")
}
- adminPasswordHash, err := getAdminPasswordHash(argon2idParams, noAuth)
+ c.AdminUsername = getAdminUsername()
+
+ c.AdminPasswordHash, err = getAdminPasswordHash(c.Argon2idParams, c.NoAuth)
if err != nil {
- return nil, errors.Wrap(err, "couldn't make admin password hash config")
+ return Config{}, errors.Wrap(err, "couldn't make admin password hash config")
}
- return &Config{
- NoAuth: noAuth,
- Argon2idParams: argon2idParams,
- AdminUsername: getAdminUsername(),
- AdminPasswordHash: adminPasswordHash,
- }, nil
+ return c, nil
}
func getNoAuth() (bool, error) {
- return env.GetBool("FLUITANS_AUTHN_NOAUTH")
+ return env.GetBool(envPrefix + "NOAUTH")
}
func getArgon2idParams() (argon2id.Params, error) {
var defaultMemorySize uint64 = 64 // default: 64 MiB
- memorySize, err := env.GetUint64("FLUITANS_AUTHN_ARGON2ID_M", defaultMemorySize)
+ memorySize, err := env.GetUint64(envPrefix+"ARGON2ID_M", defaultMemorySize)
if err != nil {
return argon2id.Params{}, errors.Wrap(err, "couldn't make memorySize config")
}
memorySize *= 1024
var defaultIterations uint64 = 1 // default: 1 iteration over the memory
- iterations, err := env.GetUint64("FLUITANS_AUTHN_ARGON2ID_T", defaultIterations)
+ iterations, err := env.GetUint64(envPrefix+"ARGON2ID_T", defaultIterations)
if err != nil {
return argon2id.Params{}, errors.Wrap(err, "couldn't make iterations config")
}
var defaultParallelism uint64 = 2 // default: 2 threads/lanes
- parallelism, err := env.GetUint64("FLUITANS_AUTHN_ARGON2ID_P", defaultParallelism)
+ parallelism, err := env.GetUint64(envPrefix+"ARGON2ID_P", defaultParallelism)
if err != nil {
return argon2id.Params{}, errors.Wrap(err, "couldn't make parallelism config")
}
@@ -76,16 +75,16 @@ func getArgon2idParams() (argon2id.Params, error) {
}
func getAdminUsername() string {
- return env.GetString("FLUITANS_AUTHN_ADMIN_USERNAME", "admin")
+ return env.GetString(envPrefix+"ADMIN_USERNAME", "admin")
}
func getAdminPasswordHash(argon2idParams argon2id.Params, noAuth bool) (hash string, err error) {
- hash = env.GetString("FLUITANS_AUTHN_ADMIN_PW_HASH", "")
+ hash = env.GetString(envPrefix+"ADMIN_PW_HASH", "")
if len(hash) == 0 && !noAuth {
- password := env.GetString("FLUITANS_AUTHN_ADMIN_PW", "")
+ password := env.GetString(envPrefix+"ADMIN_PW", "")
if len(password) == 0 {
- return "", fmt.Errorf(
- "must provide a password for the admin account with FLUITANS_AUTHN_ADMIN_PW",
+ return "", errors.Errorf(
+ "must provide a password for the admin account with %sADMIN_PW", envPrefix,
)
}
@@ -94,9 +93,9 @@ func getAdminPasswordHash(argon2idParams argon2id.Params, noAuth bool) (hash str
return "", err
}
fmt.Printf(
- "Record this admin password hash for future use as FLUITANS_AUTHN_ADMIN_PW_HASH "+
+ "Record this admin password hash for future use as %sADMIN_PW_HASH "+
"(use single-quotes from shell to avoid string substitution with dollar-signs): %s\n",
- hash,
+ envPrefix, hash,
)
}
diff --git a/pkg/godest/clientcache/marshalling.go b/pkg/godest/clientcache/marshalling.go
index 5c10fb4..c948c8a 100644
--- a/pkg/godest/clientcache/marshalling.go
+++ b/pkg/godest/clientcache/marshalling.go
@@ -3,7 +3,6 @@ package clientcache
import (
"bytes"
"encoding/gob"
- "fmt"
"github.com/pkg/errors"
"github.com/vmihailenco/msgpack/v5"
@@ -29,7 +28,7 @@ func (m *GobMarshaller) Marshal(value interface{}) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(value); err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("couldn't gob-encode value %#v", value))
+ return nil, errors.Wrapf(err, "couldn't gob-encode value %#v", value)
}
return buf.Bytes(), nil
}
@@ -38,9 +37,7 @@ func (m *GobMarshaller) Unmarshal(marshaled []byte, result interface{}) error {
buf := bytes.NewBuffer(marshaled)
dec := gob.NewDecoder(buf)
if err := dec.Decode(result); err != nil {
- return errors.Wrap(err, fmt.Sprintf(
- "couldn't gob-decode type %T from bytes %+v", result, marshaled,
- ))
+ return errors.Wrapf(err, "couldn't gob-decode type %T from bytes %+v", result, marshaled)
}
return nil
}
@@ -58,7 +55,7 @@ func (m *MsgPackMarshaller) Marshal(value interface{}) ([]byte, error) {
enc := msgpack.NewEncoder(&buf)
enc.SetCustomStructTag("json")
if err := enc.Encode(value); err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("couldn't msgpack-encode value %#v", value))
+ return nil, errors.Wrapf(err, "couldn't msgpack-encode value %#v", value)
}
return buf.Bytes(), nil
}
@@ -68,9 +65,7 @@ func (m *MsgPackMarshaller) Unmarshal(marshaled []byte, result interface{}) erro
dec := msgpack.NewDecoder(buf)
dec.SetCustomStructTag("json")
if err := dec.Decode(result); err != nil {
- return errors.Wrap(err, fmt.Sprintf(
- "couldn't msgpack-decode type %T from bytes %+v", result, marshaled,
- ))
+ return errors.Wrapf(err, "couldn't msgpack-decode type %T from bytes %+v", result, marshaled)
}
return nil
}
diff --git a/pkg/godest/clientcache/ristretto.go b/pkg/godest/clientcache/ristretto.go
index 7f3ddf4..0cfe84c 100644
--- a/pkg/godest/clientcache/ristretto.go
+++ b/pkg/godest/clientcache/ristretto.go
@@ -1,7 +1,6 @@
package clientcache
import (
- "fmt"
"time"
"github.com/dgraph-io/ristretto"
@@ -36,7 +35,7 @@ func (c *RistrettoCache) SetEntry(
) error {
marshaled, err := c.marshaller.Marshal(value)
if err != nil {
- return errors.Wrap(err, fmt.Sprintf("couldn't marshal value for key %s", key))
+ return errors.Wrapf(err, "couldn't marshal value for key %s", key)
}
if ttl < 0 {
@@ -71,12 +70,12 @@ func (c *RistrettoCache) GetEntry(key string, value interface{}) (bool, bool, er
switch marshaledBytes := entryRaw.(type) {
default:
- return true, false, fmt.Errorf("invalid cache entry %s has unexpected type %T", key, entryRaw)
+ return true, false, errors.Errorf("cache entry %s has unexpected type %T", key, entryRaw)
case nonexistentValue:
return true, false, nil
case []byte:
if err := c.marshaller.Unmarshal(marshaledBytes, value); err != nil {
- return true, true, errors.Wrap(err, fmt.Sprintf("couldn't unmarshal value for key %s", key))
+ return true, true, errors.Wrapf(err, "couldn't unmarshal value for key %s", key)
}
return true, true, nil
diff --git a/pkg/godest/embeds.go b/pkg/godest/embeds.go
index a6ef427..73c58b7 100644
--- a/pkg/godest/embeds.go
+++ b/pkg/godest/embeds.go
@@ -10,20 +10,16 @@ import (
"github.com/benbjohnson/hashfs"
"github.com/pkg/errors"
-
- "github.com/sargassum-world/fluitans/pkg/godest/fingerprint"
- "github.com/sargassum-world/fluitans/pkg/godest/fsutil"
- tp "github.com/sargassum-world/fluitans/pkg/godest/template"
)
-func ComputeCSPHash(resource []byte) string {
+func computeCSPHash(resource []byte) string {
rawHash := sha512.Sum512(resource)
encodedHash := base64.StdEncoding.EncodeToString(rawHash[:])
return fmt.Sprintf("'sha512-%s'", encodedHash)
}
func identifyModuleNonpageFiles(templates fs.FS) (map[string][]string, error) {
- modules, err := fsutil.ListDirectories(templates, tp.FilterModule)
+ modules, err := listDirectories(templates, filterTemplateModule)
if err != nil {
return nil, errors.Wrap(err, "couldn't list template modules")
}
@@ -37,19 +33,19 @@ func identifyModuleNonpageFiles(templates fs.FS) (map[string][]string, error) {
subfs, err = fs.Sub(templates, module)
}
if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("couldn't list template module %s", module))
+ return nil, errors.Wrapf(err, "couldn't list template module %s", module)
}
- moduleSubfiles, err := fsutil.ListFiles(subfs, tp.FilterNonpage)
+ moduleSubfiles, err := listFiles(subfs, filterNonpageTemplate)
moduleFiles[module] = make([]string, len(moduleSubfiles))
for i, subfile := range moduleSubfiles {
if module == "" {
moduleFiles[module][i] = subfile
} else {
- moduleFiles[module][i] = fmt.Sprintf("%s/%s", module, subfile)
+ moduleFiles[module][i] = module + "/" + subfile
}
}
if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("couldn't list template files in module %s", module))
+ return nil, errors.Wrapf(err, "couldn't list template files in module %s", module)
}
}
return moduleFiles, nil
@@ -60,16 +56,16 @@ func identifyModuleNonpageFiles(templates fs.FS) (map[string][]string, error) {
func computeAppFingerprint(
appAssets, sharedTemplates []string, templates, app fs.FS,
) (string, error) {
- appConcatenated, err := fsutil.ReadConcatenated(appAssets, app)
+ appConcatenated, err := readConcatenated(appAssets, app)
if err != nil {
return "", errors.Wrap(err, "couldn't load all app assets together for fingerprinting")
}
- sharedConcatenated, err := fsutil.ReadConcatenated(sharedTemplates, templates)
+ sharedConcatenated, err := readConcatenated(sharedTemplates, templates)
if err != nil {
return "", errors.Wrap(err, "couldn't load all shared templates together for fingerprinting")
}
- return fingerprint.Compute(append(appConcatenated, sharedConcatenated...)), nil
+ return computeFingerprint(append(appConcatenated, sharedConcatenated...)), nil
}
func computePageFingerprints(
@@ -77,22 +73,20 @@ func computePageFingerprints(
) (map[string]string, error) {
moduleNonpages := make(map[string][]byte)
for module, files := range moduleNonpageFiles {
- loadedNonpages, err := fsutil.ReadConcatenated(files, templates)
+ loadedNonpages, err := readConcatenated(files, templates)
if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf(
- "couldn't load non-page template files in template module %s for fingerprinting", module,
- ))
+ return nil, errors.Wrapf(
+ err, "couldn't load non-page template files in module %s for fingerprinting", module,
+ )
}
moduleNonpages[module] = loadedNonpages
}
pages := make(map[string][]byte)
for _, file := range pageFiles {
- loadedPage, err := fsutil.ReadFile(file, templates)
+ loadedPage, err := readFile(file, templates)
if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf(
- "couldn't load page template %s for fingerprinting", file,
- ))
+ return nil, errors.Wrapf(err, "couldn't load page template %s for fingerprinting", file)
}
pages[file] = loadedPage
}
@@ -100,7 +94,7 @@ func computePageFingerprints(
pageFingerprints := make(map[string]string)
for _, pageFile := range pageFiles {
module := path.Dir(pageFile)
- pageFingerprints[pageFile] = fingerprint.Compute(append(
+ pageFingerprints[pageFile] = computeFingerprint(append(
// Each page's fingerprint is computed from the page template itself as well as any non-page
// files (e.g. partials) within its module, recursively including all non-page files in
// submodules (i.e. subdirectories)
@@ -121,63 +115,48 @@ type Embeds struct {
FontsFS fs.FS
}
-func (e Embeds) ComputeAppFingerprint() (fingerprint string, err error) {
- appAssetFiles, err := fsutil.ListFiles(e.AppFS, tp.FilterAsset)
+func (e Embeds) computeAppFingerprint() (fingerprint string, err error) {
+ appAssetFiles, err := listFiles(e.AppFS, filterAsset)
if err != nil {
- err = errors.Wrap(err, "couldn't list app assets")
- return
+ return "", errors.Wrap(err, "couldn't list app assets")
}
- sharedFiles, err := fsutil.ListFiles(e.TemplatesFS, tp.FilterShared)
+ sharedFiles, err := listFiles(e.TemplatesFS, filterSharedTemplate)
if err != nil {
- err = errors.Wrap(err, "couldn't list shared templates")
- return
+ return "", errors.Wrap(err, "couldn't list shared templates")
}
fingerprint, err = computeAppFingerprint(appAssetFiles, sharedFiles, e.TemplatesFS, e.AppFS)
if err != nil {
- err = errors.Wrap(err, "couldn't compute fingerprint for app")
- return
+ return "", errors.Wrap(err, "couldn't compute fingerprint for app")
}
- return
+ return fingerprint, nil
}
-func (e Embeds) ComputePageFingerprints() (fingerprints map[string]string, err error) {
+func (e Embeds) computePageFingerprints() (fingerprints map[string]string, err error) {
moduleNonpageFiles, err := identifyModuleNonpageFiles(e.TemplatesFS)
if err != nil {
- err = errors.Wrap(err, "couldn't list template module non-page template files")
- return
+ return nil, errors.Wrap(err, "couldn't list template module non-page template files")
}
- pageFiles, err := fsutil.ListFiles(e.TemplatesFS, tp.FilterPage)
+ pageFiles, err := listFiles(e.TemplatesFS, filterPageTemplate)
if err != nil {
- err = errors.Wrap(err, "couldn't list template pages")
- return
+ return nil, errors.Wrap(err, "couldn't list template pages")
}
fingerprints, err = computePageFingerprints(moduleNonpageFiles, pageFiles, e.TemplatesFS)
if err != nil {
- err = errors.Wrap(err, "couldn't compute fingerprint for page/module templates")
- return
- }
- return
-}
-
-func (e Embeds) NewTemplates(funcs ...template.FuncMap) (r tp.Templates, err error) {
- pageFiles, err := fsutil.ListFiles(e.TemplatesFS, tp.FilterPage)
- if err != nil {
- err = errors.Wrap(err, "couldn't list template pages")
+ return nil, errors.Wrap(err, "couldn't compute fingerprint for page/module templates")
}
- r = tp.NewTemplates(e.TemplatesFS, pageFiles, funcs...)
- return
+ return fingerprints, nil
}
func (e Embeds) GetAppHashedNamer(urlPrefix string) func(string) string {
return func(unhashedFilename string) string {
- return fmt.Sprintf(urlPrefix + e.AppHFS.HashName(unhashedFilename))
+ return urlPrefix + e.AppHFS.HashName(unhashedFilename)
}
}
func (e Embeds) GetStaticHashedNamer(urlPrefix string) func(string) string {
return func(unhashedFilename string) string {
- return fmt.Sprintf(urlPrefix + e.StaticHFS.HashName(unhashedFilename))
+ return urlPrefix + e.StaticHFS.HashName(unhashedFilename)
}
}
@@ -195,15 +174,15 @@ type Inlines struct {
func (i Inlines) ComputeCSSHashesForCSP() (hashes []string) {
hashes = make([]string, 0, len(i.CSS))
for _, inline := range i.CSS {
- hashes = append(hashes, ComputeCSPHash([]byte(inline)))
+ hashes = append(hashes, computeCSPHash([]byte(inline)))
}
- return
+ return hashes
}
func (i Inlines) ComputeJSHashesForCSP() (hashes []string) {
hashes = make([]string, 0, len(i.CSS))
for _, inline := range i.JS {
- hashes = append(hashes, ComputeCSPHash([]byte(inline)))
+ hashes = append(hashes, computeCSPHash([]byte(inline)))
}
- return
+ return hashes
}
diff --git a/pkg/godest/env/parsing.go b/pkg/godest/env/parsing.go
index cfc57ff..ff16abf 100644
--- a/pkg/godest/env/parsing.go
+++ b/pkg/godest/env/parsing.go
@@ -3,7 +3,6 @@ package env
import (
"encoding/base64"
- "fmt"
"net/url"
"os"
"strconv"
@@ -41,9 +40,9 @@ func GetUint64(varName string, defaultValue uint64) (uint64, error) {
)
parsed, err := strconv.ParseUint(value, base, width)
if err != nil {
- return 0, errors.Wrap(err, fmt.Sprintf(
- "unparseable value %s for uint64 environment variable %s", value, varName,
- ))
+ return 0, errors.Wrapf(
+ err, "unparseable value %s for uint64 environment variable %s", value, varName,
+ )
}
return parsed, nil
@@ -61,9 +60,9 @@ func GetInt64(varName string, defaultValue int64) (int64, error) {
)
parsed, err := strconv.ParseInt(value, base, width)
if err != nil {
- return 0, errors.Wrap(err, fmt.Sprintf(
- "unparseable value %s for int64 environment variable %s", value, varName,
- ))
+ return 0, errors.Wrapf(
+ err, "unparseable value %s for int64 environment variable %s", value, varName,
+ )
}
return parsed, nil
@@ -78,9 +77,9 @@ func GetFloat32(varName string, defaultValue float32) (float32, error) {
const width = 32 // bits
parsed, err := strconv.ParseFloat(value, width)
if err != nil {
- return 0, errors.Wrap(err, fmt.Sprintf(
- "unparseable value %s for float32 environment variable %s", value, varName,
- ))
+ return 0, errors.Wrapf(
+ err, "unparseable value %s for float32 environment variable %s", value, varName,
+ )
}
return float32(parsed), nil
@@ -116,9 +115,9 @@ func GetURL(varName string, defaultValue string) (*url.URL, error) {
func GetURLOrigin(varName, defaultValue, defaultScheme string) (*url.URL, error) {
url, err := GetURL(varName, defaultValue)
if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf(
- "unparseable value %s for URL environment variable %s", os.Getenv(varName), varName,
- ))
+ return nil, errors.Wrapf(
+ err, "unparseable value %s for URL environment variable %s", os.Getenv(varName), varName,
+ )
}
if len(url.Scheme) == 0 {
diff --git a/pkg/godest/etags.go b/pkg/godest/etags.go
new file mode 100644
index 0000000..e4a7655
--- /dev/null
+++ b/pkg/godest/etags.go
@@ -0,0 +1,44 @@
+package godest
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+)
+
+// Etag Assembly
+
+const etagSegmentDelimiter = " "
+
+func makeEtag(segments ...string) string {
+ return fmt.Sprintf("W/\"%s\"", strings.Join(segments, etagSegmentDelimiter))
+}
+
+// Headers for Etags
+
+func SetUncacheable(resh http.Header) {
+ // Don't cache cookies - see the "Web Content Caching" subsection of
+ // https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
+ resh.Set("Cache-Control", "no-cache=\"Set-Cookie, Set-Cookie2\", no-store, max-age=0")
+}
+
+func setEtag(resh http.Header, etag string) {
+ resh.Set("Cache-Control", "private, no-cache")
+ resh.Set("Etag", etag)
+}
+
+func checkEtagMatch(reqh http.Header, etag string) (matches bool) {
+ matchQuery := reqh.Get("If-None-Match")
+ if matchQuery == "" || etag == "" {
+ return false
+ }
+ return matchQuery == etag
+}
+
+func setAndCheckEtag(
+ w http.ResponseWriter, r *http.Request, etagSegments ...string,
+) (noContent bool) {
+ etag := makeEtag(etagSegments...)
+ setEtag(w.Header(), etag)
+ return checkEtagMatch(r.Header, etag)
+}
diff --git a/pkg/godest/fingerprint.go b/pkg/godest/fingerprint.go
new file mode 100644
index 0000000..dcddd18
--- /dev/null
+++ b/pkg/godest/fingerprint.go
@@ -0,0 +1,18 @@
+package godest
+
+import (
+ "encoding/base64"
+ "encoding/binary"
+ "fmt"
+
+ "github.com/twmb/murmur3"
+)
+
+const fingerprintSize = 8
+
+func computeFingerprint(data []byte) string {
+ hash := murmur3.Sum64(data)
+ hashBytes := make([]byte, fingerprintSize)
+ binary.LittleEndian.PutUint64(hashBytes, hash)
+ return fmt.Sprintf("%x:%s", len(data), base64.StdEncoding.EncodeToString(hashBytes))
+}
diff --git a/pkg/godest/fingerprint/fingerprint.go b/pkg/godest/fingerprint/fingerprint.go
deleted file mode 100644
index 669747c..0000000
--- a/pkg/godest/fingerprint/fingerprint.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Package fingerprint contains utilities for fingerprinting files from
-// fs.FS filesystems, for Etag-based caching of template-based resources.
-package fingerprint
-
-import (
- "encoding/base64"
- "encoding/binary"
- "fmt"
- "io/fs"
-
- "github.com/twmb/murmur3"
-
- "github.com/sargassum-world/fluitans/pkg/godest/fsutil"
-)
-
-const FingerprintSize = 8
-
-func Compute(data []byte) string {
- hash := murmur3.Sum64(data)
- hashBytes := make([]byte, FingerprintSize)
- binary.LittleEndian.PutUint64(hashBytes, hash)
- return fmt.Sprintf("%x:%s", len(data), base64.StdEncoding.EncodeToString(hashBytes))
-}
-
-func ComputeFiles(filenames []string, f fs.FS) (map[string]string, error) {
- fingerprints := make(map[string]string)
- for _, name := range filenames {
- data, err := fsutil.ReadFile(name, f)
- if err != nil {
- return nil, err
- }
- fingerprints[name] = Compute(data)
- }
- return fingerprints, nil
-}
diff --git a/pkg/godest/fsutil.go b/pkg/godest/fsutil.go
new file mode 100644
index 0000000..7759906
--- /dev/null
+++ b/pkg/godest/fsutil.go
@@ -0,0 +1,85 @@
+package godest
+
+import (
+ "io/fs"
+ "io/ioutil"
+)
+
+// Directories
+
+func listDirectories(f fs.FS, filter func(path string) bool) ([]string, error) {
+ dirs := []string{}
+ err := fs.WalkDir(f, ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if !d.IsDir() {
+ return nil
+ }
+ if filter == nil || filter(path) {
+ if path == "." {
+ dirs = append(dirs, "")
+ } else {
+ dirs = append(dirs, path)
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return dirs, nil
+}
+
+func listFiles(f fs.FS, filter func(path string) bool) ([]string, error) {
+ files := []string{}
+ err := fs.WalkDir(f, ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ return nil
+ }
+ if filter == nil || filter(path) {
+ files = append(files, path)
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return files, nil
+}
+
+// File Reading
+
+func readFile(filename string, f fs.FS) ([]byte, error) {
+ file, err := f.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if cerr := file.Close(); cerr != nil && err == nil {
+ err = cerr
+ }
+ }()
+
+ data, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+ return data, err
+}
+
+func readConcatenated(filenames []string, f fs.FS) ([]byte, error) {
+ var concatenated []byte
+ for _, name := range filenames {
+ data, err := readFile(name, f)
+ if err != nil {
+ return nil, err
+ }
+ concatenated = append(concatenated, data...)
+ }
+
+ return concatenated, nil
+}
diff --git a/pkg/godest/fsutil/dir.go b/pkg/godest/fsutil/dir.go
deleted file mode 100644
index 8d61b46..0000000
--- a/pkg/godest/fsutil/dir.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package fsutil
-
-import (
- "io/fs"
-)
-
-func ListDirectories(f fs.FS, filter func(path string) bool) ([]string, error) {
- dirs := []string{}
- err := fs.WalkDir(f, ".", func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if !d.IsDir() {
- return nil
- }
- if filter == nil || filter(path) {
- if path == "." {
- dirs = append(dirs, "")
- } else {
- dirs = append(dirs, path)
- }
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- return dirs, nil
-}
-
-func ListFiles(f fs.FS, filter func(path string) bool) ([]string, error) {
- files := []string{}
- err := fs.WalkDir(f, ".", func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if d.IsDir() {
- return nil
- }
- if filter == nil || filter(path) {
- files = append(files, path)
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- return files, nil
-}
diff --git a/pkg/godest/fsutil/reading.go b/pkg/godest/fsutil/reading.go
deleted file mode 100644
index 23927c0..0000000
--- a/pkg/godest/fsutil/reading.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Package fsutil contains utilities for working with fs.FS filesystems.
-package fsutil
-
-import (
- "io/fs"
- "io/ioutil"
-)
-
-func ReadFile(filename string, f fs.FS) ([]byte, error) {
- file, err := f.Open(filename)
- if err != nil {
- return nil, err
- }
- defer func() {
- if cerr := file.Close(); cerr != nil && err == nil {
- err = cerr
- }
- }()
-
- data, err := ioutil.ReadAll(file)
- if err != nil {
- return nil, err
- }
- return data, err
-}
-
-func ReadConcatenated(filenames []string, f fs.FS) ([]byte, error) {
- var concatenated []byte
- for _, name := range filenames {
- data, err := ReadFile(name, f)
- if err != nil {
- return nil, err
- }
- concatenated = append(concatenated, data...)
- }
-
- return concatenated, nil
-}
diff --git a/pkg/godest/httpcache/etag.go b/pkg/godest/httpcache/etag.go
deleted file mode 100644
index ce6cb10..0000000
--- a/pkg/godest/httpcache/etag.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package httpcache
-
-import (
- "fmt"
- "net/http"
- "strings"
-)
-
-// Etag Assembly
-
-const etagSegmentDelimiter = " "
-
-func JoinEtagSegments(segments ...string) string {
- return strings.Join(segments, etagSegmentDelimiter)
-}
-
-func MakeEtag(segments ...string) string {
- return fmt.Sprintf("W/\"%s\"", JoinEtagSegments(segments...))
-}
-
-// Headers for Etags
-
-func SetNoEtag(resh http.Header) {
- // Don't cache cookies - see the "Web Content Caching" subsection of
- // https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
- resh.Set("Cache-Control", "no-cache=\"Set-Cookie, Set-Cookie2\", no-store, max-age=0")
-}
-
-func SetEtag(resh http.Header, etag string) {
- resh.Set("Cache-Control", "private, no-cache")
- resh.Set("Etag", etag)
-}
-
-func CheckEtagMatch(reqh http.Header, etag string) bool {
- match := reqh.Get("If-None-Match")
- if match == "" || etag == "" {
- return false
- }
- return match == etag
-}
-
-func SetAndCheckEtag(w http.ResponseWriter, r *http.Request, etagSegments ...string) bool {
- etag := MakeEtag(etagSegments...)
- SetEtag(w.Header(), etag)
- if !CheckEtagMatch(r.Header, etag) {
- return false
- }
- w.WriteHeader(http.StatusNotModified)
- return true
-}
diff --git a/pkg/godest/httpcache/filerevving.go b/pkg/godest/httpcache/filerevving.go
deleted file mode 100644
index 3af4779..0000000
--- a/pkg/godest/httpcache/filerevving.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Package httpcache provides utilities for working with HTTP caching in Echo
-package httpcache
-
-import (
- "fmt"
- "net/http"
-)
-
-func WrapStaticHeader(h http.Handler, age int) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", age))
- h.ServeHTTP(w, r)
- })
-}
diff --git a/pkg/godest/session/client.go b/pkg/godest/session/client.go
new file mode 100644
index 0000000..837bb0f
--- /dev/null
+++ b/pkg/godest/session/client.go
@@ -0,0 +1,60 @@
+package session
+
+import (
+ "net/http"
+
+ "github.com/gorilla/csrf"
+ "github.com/gorilla/sessions"
+ "github.com/pkg/errors"
+ "github.com/quasoft/memstore"
+)
+
+type Client struct {
+ Config Config
+ // TODO: allow configuration to use sqlite for a persistent session store
+ Store sessions.Store
+}
+
+func NewMemStoreClient(c Config) *Client {
+ store := memstore.NewMemStore(c.AuthKey)
+ store.Options = &c.CookieOptions
+
+ return &Client{
+ Config: c,
+ Store: store,
+ }
+}
+
+func (sc *Client) New(r *http.Request) (sess *sessions.Session, err error) {
+ sess, err = sc.Store.New(r, sc.Config.CookieName)
+ return sess, errors.Wrap(err, "couldn't make session")
+}
+
+func (sc *Client) Get(r *http.Request) (sess *sessions.Session, err error) {
+ sess, err = sc.Store.Get(r, sc.Config.CookieName)
+ return sess, errors.Wrap(err, "couldn't get session")
+}
+
+func NewCSRFMiddleware(config Config, opts ...csrf.Option) func(http.Handler) http.Handler {
+ sameSite := csrf.SameSiteDefaultMode
+ switch config.CookieOptions.SameSite {
+ case http.SameSiteLaxMode:
+ sameSite = csrf.SameSiteLaxMode
+ case http.SameSiteStrictMode:
+ sameSite = csrf.SameSiteStrictMode
+ case http.SameSiteNoneMode:
+ sameSite = csrf.SameSiteNoneMode
+ }
+ options := []csrf.Option{
+ csrf.Path(config.CookieOptions.Path),
+ csrf.Domain(config.CookieOptions.Domain),
+ csrf.MaxAge(config.CookieOptions.MaxAge),
+ csrf.Secure(config.CookieOptions.Secure),
+ csrf.HttpOnly(config.CookieOptions.HttpOnly),
+ csrf.SameSite(sameSite),
+ csrf.RequestHeader(config.CSRFOptions.HeaderName),
+ csrf.FieldName(config.CSRFOptions.FieldName),
+ }
+ options = append(options, opts...)
+ return csrf.Protect(config.AuthKey, options...)
+}
diff --git a/internal/clients/sessions/conf.go b/pkg/godest/session/conf.go
similarity index 64%
rename from internal/clients/sessions/conf.go
rename to pkg/godest/session/conf.go
index ebba348..5cf2aa7 100644
--- a/internal/clients/sessions/conf.go
+++ b/pkg/godest/session/conf.go
@@ -1,4 +1,4 @@
-package sessions
+package session
import (
"encoding/base64"
@@ -13,6 +13,8 @@ import (
"github.com/sargassum-world/fluitans/pkg/godest/env"
)
+const envPrefix = "SESSIONS_"
+
type Timeouts struct {
Absolute time.Duration
// TODO: add idle timeout
@@ -35,21 +37,18 @@ type Config struct {
func GetConfig() (c Config, err error) {
c.AuthKey, err = getAuthKey()
if err != nil {
- err = errors.Wrap(err, "couldn't make session key config")
- return
+ return Config{}, errors.Wrap(err, "couldn't make session key config")
}
c.Timeouts, err = getTimeouts()
if err != nil {
- err = errors.Wrap(err, "couldn't make session timeouts config")
- return
+ return Config{}, errors.Wrap(err, "couldn't make session timeouts config")
}
// TODO: when we implement idle timeout, pass that instead of absolute timeout
c.CookieOptions, err = getCookieOptions(c.Timeouts.Absolute)
if err != nil {
- err = errors.Wrap(err, "couldn't make cookie options config")
- return
+ return Config{}, errors.Wrap(err, "couldn't make cookie options config")
}
if c.CookieOptions.Secure {
@@ -60,13 +59,13 @@ func GetConfig() (c Config, err error) {
}
c.CSRFOptions = getCSRFOptions()
- return
+ return c, nil
}
func getAuthKey() (authKey []byte, err error) {
- authKey, err = env.GetBase64("FLUITANS_SESSIONS_AUTH_KEY")
+ authKey, err = env.GetBase64(envPrefix + "AUTH_KEY")
if err != nil {
- return
+ return nil, err
}
if authKey == nil {
@@ -74,44 +73,41 @@ func getAuthKey() (authKey []byte, err error) {
authKey = securecookie.GenerateRandomKey(authKeySize)
// TODO: print to the logger instead?
fmt.Printf(
- "Record this key for future use as FLUITANS_SESSIONS_AUTH_KEY: %s\n",
- base64.StdEncoding.EncodeToString(authKey),
+ "Record this key for future use as %sAUTH_KEY: %s\n",
+ envPrefix, base64.StdEncoding.EncodeToString(authKey),
)
}
- return
+ return authKey, nil
}
func getTimeouts() (t Timeouts, err error) {
const defaultAbsolute = 12 * 60 // default: 12 hours
- rawAbsolute, err := env.GetInt64("FLUITANS_SESSIONS_TIMEOUTS_ABSOLUTE", defaultAbsolute)
+ rawAbsolute, err := env.GetInt64(envPrefix+"TIMEOUTS_ABSOLUTE", defaultAbsolute)
if err != nil {
- err = errors.Wrap(err, "couldn't make absolute timeout config")
- return
+ return Timeouts{}, errors.Wrap(err, "couldn't make absolute timeout config")
}
t.Absolute = time.Duration(rawAbsolute) * time.Minute
- return
+ return t, nil
}
func getCookieOptions(absoluteTimeout time.Duration) (o sessions.Options, err error) {
- noHTTPSOnly, err := env.GetBool("FLUITANS_SESSIONS_COOKIE_NOHTTPSONLY")
+ noHTTPSOnly, err := env.GetBool(envPrefix + "COOKIE_NOHTTPSONLY")
if err != nil {
- err = errors.Wrap(err, "couldn't make HTTPS-only config")
- return
+ return sessions.Options{}, errors.Wrap(err, "couldn't make HTTPS-only config")
}
- o = sessions.Options{
+ return sessions.Options{
Path: "/",
Domain: "",
MaxAge: int(absoluteTimeout.Seconds()),
Secure: !noHTTPSOnly,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
- }
- return
+ }, nil
}
func getCSRFOptions() (o CSRFOptions) {
- o.HeaderName = env.GetString("FLUITANS_SESSIONS_CSRF_HEADERNAME", "X-CSRF-Token")
- o.FieldName = env.GetString("FLUITANS_SESSIONS_CSRF_FIELDNAME", "csrf-token")
- return
+ o.HeaderName = env.GetString(envPrefix+"CSRF_HEADERNAME", "X-CSRF-Token")
+ o.FieldName = env.GetString(envPrefix+"CSRF_FIELDNAME", "csrf-token")
+ return o
}
diff --git a/pkg/godest/session/flash.go b/pkg/godest/session/flash.go
deleted file mode 100644
index 0b0baf9..0000000
--- a/pkg/godest/session/flash.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package session
-
-import (
- "fmt"
-
- "github.com/gorilla/sessions"
-)
-
-// Errors
-
-const FlashErrorsKey = "_flash_errors"
-
-func AddErrorMessage(s *sessions.Session, message string) {
- s.AddFlash(message, FlashErrorsKey)
-}
-
-func GetErrorMessages(s *sessions.Session) ([]string, error) {
- rawFlashes := s.Flashes(FlashErrorsKey)
- flashes := make([]string, 0, len(rawFlashes))
- for _, rawFlash := range rawFlashes {
- flash, ok := rawFlash.(string)
- if !ok {
- return nil, fmt.Errorf("session error message is of unexpected non-string type %T", rawFlash)
- }
- flashes = append(flashes, flash)
- }
- return flashes, nil
-}
diff --git a/pkg/godest/session/sessions.go b/pkg/godest/session/sessions.go
index 28e71cd..a420b30 100644
--- a/pkg/godest/session/sessions.go
+++ b/pkg/godest/session/sessions.go
@@ -2,44 +2,41 @@
package session
import (
- "net/http"
-
"github.com/gorilla/sessions"
+ "github.com/pkg/errors"
)
-func Get(
- r *http.Request, cookieName string, store sessions.Store,
-) (*sessions.Session, error) {
- // TODO: implement idle timeout, and implement automatic renewal timeout (if we can). Refer to the
- // "Automatic Session Expiration" section of
- // https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
- // TODO: regenerate the session upon privilege change
- // TODO: log the session life cycle
- return store.Get(r, cookieName)
-}
-
-func Regenerate(
- r *http.Request, cookieName string, store sessions.Store,
-) (*sessions.Session, error) {
- s, err := Get(r, cookieName, store)
- if err != nil {
- return nil, err
- }
+// Session rotation
+func Regenerate(s *sessions.Session) {
s.ID = ""
s.Values = make(map[interface{}]interface{})
- return s, nil
}
-func Invalidate(
- r *http.Request, cookieName string, store sessions.Store,
-) (*sessions.Session, error) {
- s, err := Get(r, cookieName, store)
- if err != nil {
- return nil, err
- }
-
+func Invalidate(s *sessions.Session) {
s.Options.MaxAge = 0
s.Values = make(map[interface{}]interface{})
- return s, nil
+}
+
+// Flash messages
+
+const FlashErrorsKey = "_flash_errors"
+
+func AddErrorMessage(s *sessions.Session, message string) {
+ s.AddFlash(message, FlashErrorsKey)
+}
+
+func GetErrorMessages(s *sessions.Session) ([]string, error) {
+ rawFlashes := s.Flashes(FlashErrorsKey)
+ flashes := make([]string, 0, len(rawFlashes))
+ for _, rawFlash := range rawFlashes {
+ flash, ok := rawFlash.(string)
+ if !ok {
+ return nil, errors.Errorf(
+ "session error message is of unexpected non-string type %T", rawFlash,
+ )
+ }
+ flashes = append(flashes, flash)
+ }
+ return flashes, nil
}
diff --git a/pkg/godest/statics.go b/pkg/godest/statics.go
index 898c46e..e7b15f2 100644
--- a/pkg/godest/statics.go
+++ b/pkg/godest/statics.go
@@ -1,17 +1,23 @@
package godest
import (
+ "fmt"
"io/fs"
"net/http"
"time"
"github.com/benbjohnson/hashfs"
-
- "github.com/sargassum-world/fluitans/pkg/godest/httpcache"
)
+func wrapStaticHeader(h http.Handler, age int) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", age))
+ h.ServeHTTP(w, r)
+ })
+}
+
func HandleFS(routePrefix string, fsys fs.FS, age time.Duration) http.Handler {
- return httpcache.WrapStaticHeader(
+ return wrapStaticHeader(
http.StripPrefix(routePrefix, http.FileServer(http.FS(fsys))), int(age.Seconds()),
)
}
diff --git a/pkg/godest/template/execution.go b/pkg/godest/template/execution.go
deleted file mode 100644
index 6d7a3af..0000000
--- a/pkg/godest/template/execution.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Package template preloads a collection of shared template files and page template files for fast
-// execution of page templates, and specifies a layout convention to enable this functionality.
-package template
-
-import (
- "fmt"
- "html/template"
- "io"
- "io/fs"
-)
-
-type Templates struct {
- allTemplates *template.Template
- pageTemplates map[string]*template.Template
- templatesFS fs.FS
-}
-
-func NewTemplates(fsys fs.FS, pageFiles []string, functions ...template.FuncMap) Templates {
- tmpl := template.New("App")
- for _, f := range functions {
- tmpl = tmpl.Funcs(f)
- }
- allTemplates := template.Must(ParseFS(tmpl, fsys, "**/*"+FileExt))
- pageTemplates := make(map[string]*template.Template)
- for _, name := range pageFiles {
- all := template.Must(allTemplates.Clone())
- pageTemplates[name] = template.Must(ParseFS(all, fsys, name))
- }
- return Templates{
- allTemplates: allTemplates,
- pageTemplates: pageTemplates,
- templatesFS: fsys,
- }
-}
-
-func (t *Templates) Execute(w io.Writer, name string, data interface{}) error {
- tmpl, ok := t.pageTemplates[name]
- if !ok {
- return fmt.Errorf("template %s not found", name)
- }
- return tmpl.ExecuteTemplate(w, name, data)
-}
diff --git a/pkg/godest/template/filters.go b/pkg/godest/template/filters.go
deleted file mode 100644
index 06e6fb5..0000000
--- a/pkg/godest/template/filters.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package template
-
-import (
- "strings"
-)
-
-// Template Files
-
-const (
- FileExt = ".tmpl"
- PartialFileExt = ".partial" + FileExt
- LayoutFileExt = ".layout" + FileExt
- SharedModule = "shared"
-)
-
-func Filter(path string) bool {
- return strings.HasSuffix(path, FileExt)
-}
-
-func FilterShared(path string) bool {
- return Filter(path) && strings.HasPrefix(path, SharedModule+"/")
-}
-
-func FilterPartial(path string) bool {
- return strings.HasSuffix(path, PartialFileExt)
-}
-
-func FilterLayout(path string) bool {
- return strings.HasSuffix(path, LayoutFileExt)
-}
-
-func FilterNonpage(path string) bool {
- return FilterPartial(path) || FilterLayout(path)
-}
-
-func FilterPage(path string) bool {
- // Usually a page ends with ".page.tmpl", but it may end with other things,
- // e.g. ".webmanifest.tmpl", ".json.tmpl", etc.
- return Filter(path) && !FilterNonpage(path)
-}
-
-func FilterModule(path string) bool {
- return path != SharedModule
-}
-
-// Built Asset Files
-
-func FilterCSSAsset(path string) bool {
- return strings.HasSuffix(path, ".css")
-}
-
-func FilterJSAsset(path string) bool {
- return strings.HasSuffix(path, ".js")
-}
-
-func FilterAsset(path string) bool {
- return FilterCSSAsset(path) || FilterJSAsset(path)
-}
diff --git a/pkg/godest/template/parsing.go b/pkg/godest/template/parsing.go
deleted file mode 100644
index b55c7dd..0000000
--- a/pkg/godest/template/parsing.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package template
-
-import (
- "fmt"
- "html/template"
- "io/fs"
-
- "github.com/bmatcuk/doublestar/v4"
- "github.com/pkg/errors"
-)
-
-// parseFiles is a direct copy of the parseFiles helper function in text/template.
-func parseFiles(
- t *template.Template, readFile func(string) (string, []byte, error), filenames ...string,
-) (*template.Template, error) {
- if len(filenames) == 0 {
- return nil, errors.Errorf("template: no files named in call to ParseFiles")
- }
-
- for _, filename := range filenames {
- name, b, err := readFile(filename)
- if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("couldn't read file %s", filename))
- }
- s := string(b)
- var tmpl *template.Template
- if t == nil {
- t = template.New(name)
- }
- if name == t.Name() {
- tmpl = t
- } else {
- tmpl = t.New(name)
- }
- _, err = tmpl.Parse(s)
- if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("couldn't parse template %s", filename))
- }
- }
- return t, nil
-}
-
-// readFileFS is an adaptation of the readFileFS helper function in text/template, except it uses
-// the fully-qualified path name of the template file in the filesystem rather than just the
-// template file's basename.
-func readFileFS(fsys fs.FS) func(string) (string, []byte, error) {
- return func(file string) (name string, b []byte, err error) {
- name = file
- b, err = fs.ReadFile(fsys, file)
- return
- }
-}
-
-// ParseFS is a direct copy of the parseFS helper function in text/template, except it uses the
-// fully-qualified path name of the template file in the filesystem rather than just the template
-// file's basename, and it allows double-star globs (e.g. "**/*.txt").
-func ParseFS(
- t *template.Template, fsys fs.FS, patterns ...string,
-) (*template.Template, error) {
- var filenames []string
- for _, pattern := range patterns {
- list, err := doublestar.Glob(fsys, pattern)
- if err != nil {
- return nil, err
- }
- if len(list) == 0 {
- return nil, errors.Errorf("template: pattern matches no files: %#q", pattern)
- }
- filenames = append(filenames, list...)
- }
- return parseFiles(t, readFileFS(fsys), filenames...)
-}
diff --git a/pkg/godest/templated.go b/pkg/godest/templated.go
deleted file mode 100644
index a1957fc..0000000
--- a/pkg/godest/templated.go
+++ /dev/null
@@ -1,177 +0,0 @@
-// Package godest provides a mildly opinionated framework for more easily writing web apps with
-// modest Javascript approaches such as Hotwire-based apps. It provides support for using templates
-// in a clear and structured way. It also makes it easy to embed all templates, static assets
-// (e.g. images) and app-related assets (e.g. JS bundles) into the compiled server binary, and it
-// takes care of the details of browser caching for assets and templated pages.
-// Finally, it provides some standalone utilities for caching data on the server, getting the values
-// of environment variables, and using cookies for form-based authentication.
-package godest
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "html/template"
- "net/http"
-
- "github.com/pkg/errors"
-
- "github.com/sargassum-world/fluitans/pkg/godest/fingerprint"
- "github.com/sargassum-world/fluitans/pkg/godest/httpcache"
- tp "github.com/sargassum-world/fluitans/pkg/godest/template"
-)
-
-// Templated page fingerprinting
-
-type fingerprints struct {
- App string
- Page map[string]string
-}
-
-func (f fingerprints) GetEtagSegments(templateName string) ([]string, error) {
- if templateName == "" {
- return []string{f.App}, nil
- }
-
- pageFingerprint, ok := f.Page[templateName]
- if !ok {
- return []string{f.App}, errors.Errorf(
- "couldn't find page fingerprint for template %s", templateName,
- )
- }
-
- return []string{f.App, pageFingerprint}, nil
-}
-
-func (f fingerprints) MustHave(templateNames ...string) {
- for _, name := range templateNames {
- if _, err := f.GetEtagSegments(name); err != nil {
- panic(errors.Wrap(err, fmt.Sprintf("couldn't find template etag segments for %s", name)))
- }
- }
-}
-
-func (f fingerprints) SetAndCheckEtag(
- w http.ResponseWriter, r *http.Request, templateName string, data interface{},
-) (noContent bool, err error) {
- // Look up data-independent etag segments
- templateEtagSegments, err := f.GetEtagSegments(templateName)
- if err != nil {
- return
- }
-
- // Encode data
- var buf bytes.Buffer
- // github.com/vmihailenco/msgpack has better performance, but we use the JSON encoder because
- // the msgpack encoder can only sort the map keys of map[string]string and map[string]interface{}
- // maps, and it's too much trouble to convert our maps into map[string]interface{}. If we can
- // work around this limitation, we should use msgpack though.
- if err = json.NewEncoder(&buf).Encode(data); err != nil {
- return
- }
- encoded := buf.Bytes()
-
- noContent = httpcache.SetAndCheckEtag(
- w, r, append(templateEtagSegments, fingerprint.Compute(encoded))...,
- )
- return
-}
-
-// Template rendering
-
-type Meta struct {
- Path string
- RequestURI string
-}
-
-type RenderData struct {
- Meta Meta
- Inlines interface{}
- Data interface{}
- Auth interface{}
-}
-
-type TemplateRenderer struct {
- inlines interface{}
- fingerprints fingerprints
- templates tp.Templates
-}
-
-func NewTemplateRenderer(
- e Embeds, inlines interface{}, funcs ...template.FuncMap,
-) (r TemplateRenderer, err error) {
- r.inlines = inlines
- r.templates, err = e.NewTemplates(funcs...)
- if err != nil {
- err = errors.Wrap(err, "couldn't make templated pages renderer")
- return
- }
- if r.fingerprints.App, err = e.ComputeAppFingerprint(); err != nil {
- err = errors.Wrap(err, "couldn't compute fingerprint for app")
- return
- }
- if r.fingerprints.Page, err = e.ComputePageFingerprints(); err != nil {
- err = errors.Wrap(err, "couldn't compute fingerprint for page/module templates")
- return
- }
- return
-}
-
-func (tr TemplateRenderer) MustHave(templateNames ...string) {
- tr.fingerprints.MustHave(templateNames...)
-}
-
-func (tr TemplateRenderer) NewRenderData(
- r *http.Request, data interface{}, auth interface{},
-) RenderData {
- return RenderData{
- Meta: Meta{
- Path: r.URL.Path,
- RequestURI: r.URL.RequestURI(),
- },
- Inlines: tr.inlines,
- Data: data,
- Auth: auth,
- }
-}
-
-func (tr TemplateRenderer) SetUncacheable(resh http.Header) {
- httpcache.SetNoEtag(resh)
-}
-
-func (tr TemplateRenderer) Page(
- w http.ResponseWriter, r *http.Request,
- status int, templateName string, templateData interface{}, authData interface{},
-) error {
- // This is basically a reimplementation of the echo.Context.Render method, but without requiring
- // an echo.Context to be provided
- buf := new(bytes.Buffer)
- if rerr := tr.templates.Execute(
- buf, templateName, tr.NewRenderData(r, templateData, authData),
- ); rerr != nil {
- return rerr
- }
-
- // Write render result
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
- w.WriteHeader(status)
- _, werr := w.Write(buf.Bytes())
- return werr
-}
-
-func (tr TemplateRenderer) CacheablePage(
- w http.ResponseWriter, r *http.Request,
- templateName string, templateData interface{}, authData interface{},
-) error {
- type EtagInputs struct {
- Data interface{}
- Auth interface{}
- }
- if noContent, err := tr.fingerprints.SetAndCheckEtag(w, r, templateName, EtagInputs{
- Data: templateData,
- Auth: authData,
- }); noContent || (err != nil) {
- return err
- }
- return tr.Page(w, r, http.StatusOK, templateName, templateData, authData)
-}
diff --git a/pkg/godest/templates.go b/pkg/godest/templates.go
new file mode 100644
index 0000000..4e40600
--- /dev/null
+++ b/pkg/godest/templates.go
@@ -0,0 +1,314 @@
+// Package godest provides a mildly opinionated framework for more easily writing web apps with
+// modest Javascript approaches such as Hotwire-based apps. It provides support for using templates
+// in a clear and structured way. It also makes it easy to embed all templates, static assets
+// (e.g. images) and app-related assets (e.g. JS bundles) into the compiled server binary, and it
+// takes care of the details of browser caching for assets and templated pages.
+// Finally, it provides some standalone utilities for caching data on the server, getting the values
+// of environment variables, and using cookies for form-based authentication.
+package godest
+
+import (
+ "bytes"
+ "encoding/json"
+ "html/template"
+ "io/fs"
+ "net/http"
+ "strings"
+
+ "github.com/bmatcuk/doublestar/v4"
+ "github.com/pkg/errors"
+)
+
+// Template File Filters
+
+const (
+ templateFileExt = ".tmpl"
+ partialTemplateFileExt = ".partial" + templateFileExt
+ layoutTemplateFileExt = ".layout" + templateFileExt
+ templateSharedModule = "shared"
+)
+
+func filterTemplate(path string) bool {
+ return strings.HasSuffix(path, templateFileExt)
+}
+
+func filterSharedTemplate(path string) bool {
+ return filterTemplate(path) && strings.HasPrefix(path, templateSharedModule+"/")
+}
+
+func filterPartialTemplate(path string) bool {
+ return strings.HasSuffix(path, partialTemplateFileExt)
+}
+
+func filterLayoutTemplate(path string) bool {
+ return strings.HasSuffix(path, layoutTemplateFileExt)
+}
+
+func filterNonpageTemplate(path string) bool {
+ return filterPartialTemplate(path) || filterLayoutTemplate(path)
+}
+
+func filterPageTemplate(path string) bool {
+ // Usually a page ends with ".page.tmpl", but it may end with other things,
+ // e.g. ".webmanifest.tmpl", ".json.tmpl", etc.
+ return filterTemplate(path) && !filterNonpageTemplate(path)
+}
+
+func filterTemplateModule(path string) bool {
+ return path != templateSharedModule
+}
+
+// Built Asset File Filters
+
+func filterCSSAsset(path string) bool {
+ return strings.HasSuffix(path, ".css")
+}
+
+func filterJSAsset(path string) bool {
+ return strings.HasSuffix(path, ".js")
+}
+
+func filterAsset(path string) bool {
+ return filterCSSAsset(path) || filterJSAsset(path)
+}
+
+// Template File Parsing
+
+// parseFiles is a direct copy of the parseFiles helper function in text/template.
+func parseFiles(
+ t *template.Template, readFile func(string) (string, []byte, error), filenames ...string,
+) (*template.Template, error) {
+ if len(filenames) == 0 {
+ return nil, errors.Errorf("template: no files named in call to ParseFiles")
+ }
+
+ for _, filename := range filenames {
+ name, b, err := readFile(filename)
+ if err != nil {
+ return nil, errors.Wrapf(err, "couldn't read file %s", filename)
+ }
+ s := string(b)
+ var tmpl *template.Template
+ if t == nil {
+ t = template.New(name)
+ }
+ if name == t.Name() {
+ tmpl = t
+ } else {
+ tmpl = t.New(name)
+ }
+ _, err = tmpl.Parse(s)
+ if err != nil {
+ return nil, errors.Wrapf(err, "couldn't parse template %s", filename)
+ }
+ }
+ return t, nil
+}
+
+// readFileFS is an adaptation of the readFileFS helper function in text/template, except it uses
+// the fully-qualified path name of the template file in the filesystem rather than just the
+// template file's basename.
+func readFileFS(fsys fs.FS) func(string) (string, []byte, error) {
+ return func(file string) (name string, b []byte, err error) {
+ b, err = fs.ReadFile(fsys, file)
+ return file, b, err
+ }
+}
+
+// parseFS is a direct copy of the parseFS helper function in text/template, except it uses the
+// fully-qualified path name of the template file in the filesystem rather than just the template
+// file's basename, and it allows double-star globs (e.g. "**/*.txt").
+func parseFS(
+ t *template.Template, fsys fs.FS, patterns ...string,
+) (*template.Template, error) {
+ var filenames []string
+ for _, pattern := range patterns {
+ list, err := doublestar.Glob(fsys, pattern)
+ if err != nil {
+ return nil, err
+ }
+ if len(list) == 0 {
+ return nil, errors.Errorf("template: pattern matches no files: %#q", pattern)
+ }
+ filenames = append(filenames, list...)
+ }
+ return parseFiles(t, readFileFS(fsys), filenames...)
+}
+
+// Templated Page Fingerprinting
+
+type fingerprints struct {
+ app string
+ page map[string]string
+}
+
+func (f fingerprints) getEtagSegments(templateName string) ([]string, error) {
+ if templateName == "" {
+ return []string{f.app}, nil
+ }
+
+ pageFingerprint, ok := f.page[templateName]
+ if !ok {
+ return []string{f.app}, errors.Errorf(
+ "couldn't find page fingerprint for template %s", templateName,
+ )
+ }
+
+ return []string{f.app, pageFingerprint}, nil
+}
+
+func (f fingerprints) MustHave(templateNames ...string) {
+ for _, name := range templateNames {
+ if _, err := f.getEtagSegments(name); err != nil {
+ panic(errors.Wrapf(err, "couldn't find template etag segments for %s", name))
+ }
+ }
+}
+
+func (f fingerprints) SetAndCheckEtag(
+ w http.ResponseWriter, r *http.Request, templateName string, data interface{},
+) (noContent bool, err error) {
+ // Look up data-independent etag segments
+ templateEtagSegments, err := f.getEtagSegments(templateName)
+ if err != nil {
+ return false, err
+ }
+
+ // Encode data
+ var buf bytes.Buffer
+ // github.com/vmihailenco/msgpack has better performance, but we use the JSON encoder because
+ // the msgpack encoder can only sort the map keys of map[string]string and map[string]interface{}
+ // maps, and it's too much trouble to convert our maps into map[string]interface{}. If we can
+ // work around this limitation, we should use msgpack though.
+ if err = json.NewEncoder(&buf).Encode(data); err != nil {
+ return false, err
+ }
+ encoded := buf.Bytes()
+
+ noContent = setAndCheckEtag(
+ w, r, append(templateEtagSegments, computeFingerprint(encoded))...,
+ )
+ if noContent {
+ w.WriteHeader(http.StatusNotModified)
+ }
+ return noContent, nil
+}
+
+// Template Rendering
+
+type Meta struct {
+ Path string
+ RequestURI string
+}
+
+type RenderData struct {
+ Meta Meta
+ Inlines interface{}
+ Data interface{}
+ Auth interface{}
+}
+
+type TemplateRenderer struct {
+ pageTemplates map[string]*template.Template
+ inlines interface{}
+ fingerprints fingerprints
+}
+
+func NewTemplateRenderer(
+ e Embeds, inlines interface{}, funcs ...template.FuncMap,
+) (tr TemplateRenderer, err error) {
+ tmpl := template.New("App")
+ for _, f := range funcs {
+ tmpl = tmpl.Funcs(f)
+ }
+ allTemplates, err := parseFS(tmpl, e.TemplatesFS, "**/*"+templateFileExt)
+ if err != nil {
+ return TemplateRenderer{}, errors.Wrap(err, "couldn't load templates from filesystem")
+ }
+
+ tr.pageTemplates = make(map[string]*template.Template)
+ pageFiles, err := listFiles(e.TemplatesFS, filterPageTemplate)
+ if err != nil {
+ return TemplateRenderer{}, errors.Wrap(err, "couldn't list template pages")
+ }
+ for _, name := range pageFiles {
+ all, cerr := allTemplates.Clone()
+ if cerr != nil {
+ return TemplateRenderer{}, errors.Wrap(err, "couldn't clone root template set")
+ }
+ tr.pageTemplates[name], err = parseFS(all, e.TemplatesFS, name)
+ if err != nil {
+ return TemplateRenderer{}, errors.Wrapf(err, "couldn't make template set for %s", name)
+ }
+ }
+
+ tr.inlines = inlines
+ if tr.fingerprints.app, err = e.computeAppFingerprint(); err != nil {
+ return TemplateRenderer{}, errors.Wrap(err, "couldn't compute fingerprint for app")
+ }
+ if tr.fingerprints.page, err = e.computePageFingerprints(); err != nil {
+ return TemplateRenderer{}, errors.Wrap(
+ err, "couldn't compute fingerprint for page/module templates",
+ )
+ }
+
+ return tr, nil
+}
+
+func (tr TemplateRenderer) MustHave(templateNames ...string) {
+ tr.fingerprints.MustHave(templateNames...)
+}
+
+func (tr TemplateRenderer) newRenderData(
+ r *http.Request, data interface{}, auth interface{},
+) RenderData {
+ return RenderData{
+ Meta: Meta{
+ Path: r.URL.Path,
+ RequestURI: r.URL.RequestURI(),
+ },
+ Inlines: tr.inlines,
+ Data: data,
+ Auth: auth,
+ }
+}
+
+func (tr TemplateRenderer) Page(
+ w http.ResponseWriter, r *http.Request,
+ status int, templateName string, templateData interface{}, authData interface{},
+) error {
+ // This is basically a reimplementation of the echo.Context.Render method, but without requiring
+ // an echo.Context to be provided
+ buf := new(bytes.Buffer)
+ tmpl, ok := tr.pageTemplates[templateName]
+ if !ok {
+ return errors.Errorf("teplate %s not found", templateName)
+ }
+ if err := tmpl.ExecuteTemplate(
+ buf, templateName, tr.newRenderData(r, templateData, authData),
+ ); err != nil {
+ return errors.Wrapf(err, "couldn't execute template %s", templateName)
+ }
+
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ w.WriteHeader(status)
+ _, werr := w.Write(buf.Bytes())
+ return werr
+}
+
+func (tr TemplateRenderer) CacheablePage(
+ w http.ResponseWriter, r *http.Request,
+ templateName string, templateData interface{}, authData interface{},
+) error {
+ type EtagInputs struct {
+ Data interface{}
+ Auth interface{}
+ }
+ if noContent, err := tr.fingerprints.SetAndCheckEtag(w, r, templateName, EtagInputs{
+ Data: templateData,
+ Auth: authData,
+ }); noContent || (err != nil) {
+ return err
+ }
+ return tr.Page(w, r, http.StatusOK, templateName, templateData, authData)
+}
diff --git a/tools/go.mod b/tools/go.mod
index 1ae76b6..e32b4b3 100644
--- a/tools/go.mod
+++ b/tools/go.mod
@@ -5,6 +5,6 @@ go 1.16
require (
github.com/deepmap/oapi-codegen v1.8.3
github.com/golangci/golangci-lint v1.42.1
- github.com/goreleaser/goreleaser v0.178.0
+ github.com/goreleaser/goreleaser v1.6.1
mvdan.cc/gofumpt v0.2.1
)
diff --git a/tools/go.sum b/tools/go.sum
index 7b6c237..7e3f44c 100644
--- a/tools/go.sum
+++ b/tools/go.sum
@@ -5,7 +5,6 @@ bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7EN
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
@@ -24,8 +23,22 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW
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 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go v0.82.0/go.mod h1:vlKccHJGuFBFufnAnuB08dfEH9Y3H7dzDzRECFdC2TA=
+cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
+cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
+cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
+cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY=
+cloud.google.com/go v0.89.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
+cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
+cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
+cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
+cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
+cloud.google.com/go v0.94.0/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
+cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
+cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
+cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY=
+cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -36,60 +49,77 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo=
+cloud.google.com/go/kms v0.1.0/go.mod h1:8Qp8PCAypHg4FdmlyW1QRAv09BGQ9Uzh7JnmIZxPk+c=
+cloud.google.com/go/kms v1.1.0 h1:1yc4rLqCkVDS9Zvc7m+3mJ47kw0Uo5Q5+sMjcmUVUeM=
+cloud.google.com/go/kms v1.1.0/go.mod h1:WdbppnCDMDpOvoYBMn1+gNmOeEoZYqAv+HeuKARGCXI=
+cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w=
-cloud.google.com/go/pubsub v1.10.3/go.mod h1:FUcc28GpGxxACoklPsE1sCtbkY4Ix+ro7yvw+h82Jn4=
+cloud.google.com/go/pubsub v1.16.0/go.mod h1:6A8EfoWZ/lUvCWStKGwAWauJZSiuV0Mkmu6WilK/TxQ=
+cloud.google.com/go/secretmanager v0.1.0/go.mod h1:3nGKHvnzDUVit7U0S9KAKJ4aOsO1xtwRG+7ey5LK1bM=
cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
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.15.0 h1:Ljj+ZXVEhCr/1+4ZhvtteN1ND7UUsNTlduGclLh8GO0=
-cloud.google.com/go/storage v1.15.0/go.mod h1:mjjQMoxxyGH7Jr8K5qrx6N2O0AHsczI61sMNn03GIZI=
+cloud.google.com/go/storage v1.16.1/go.mod h1:LaNorbty3ehnU3rEjXSNV/NRgQA0O8Y+uh6bPe5UOk4=
+cloud.google.com/go/storage v1.18.2 h1:5NQw6tOn3eMm0oE8vTkfjau18kjL79FlMjy/CHTpmoY=
+cloud.google.com/go/storage v1.18.2/go.mod h1:AiIj7BWXyhO5gGVmYJ+S8tbkCx3yb0IMjua8Aw4naVM=
+cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
-code.gitea.io/sdk/gitea v0.15.0 h1:tsNhxDM/2N1Ohv1Xq5UWrht/esg0WmtRj4wsHVHriTg=
-code.gitea.io/sdk/gitea v0.15.0/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
+code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M=
+code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.8/go.mod h1:huNtlWx75MwO7qMs0KrMxPZXzNNWebav1Sq/pm02JdQ=
contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
-github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
+github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
+github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/Antonboom/errname v0.1.4 h1:lGSlI42Gm4bI1e+IITtXJXvxFM8N7naWimVFKcb0McY=
github.com/Antonboom/errname v0.1.4/go.mod h1:jRXo3m0E0EuCnK3wbsSVH3X55Z4iTDLl6ZfCxwFj4TM=
github.com/Azure/azure-amqp-common-go/v3 v3.1.0/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
+github.com/Azure/azure-amqp-common-go/v3 v3.1.1/go.mod h1:YsDaPfaO9Ub2XeSKdIy2DfwuiQlHQCauHJwSqtrkECI=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go v54.0.0+incompatible h1:Bq3L9LF0DHCexlT0fccwxgrOMfjHx8LGz+d+L7gGQv4=
-github.com/Azure/azure-sdk-for-go v54.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-service-bus-go v0.10.11/go.mod h1:AWw9eTTWZVZyvgpPahD1ybz3a8/vT3GsJDS8KYex55U=
-github.com/Azure/azure-storage-blob-go v0.13.0 h1:lgWHvFh+UYBNVQLFHXkvul2f6yOPA9PIH82RTG2cSwc=
-github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs=
+github.com/Azure/azure-sdk-for-go v57.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/azure-sdk-for-go v60.2.0+incompatible h1:twJOQQl3um6o+1q5SSyAdLr2GCX8dJPVRNS1eKOnv54=
+github.com/Azure/azure-sdk-for-go v60.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/azure-service-bus-go v0.10.16/go.mod h1:MlkLwGGf1ewcx5jZadn0gUEty+tTg0RaElr6bPf+QhI=
+github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM=
+github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
github.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=
-github.com/Azure/go-amqp v0.13.4/go.mod h1:wbpCKA8tR5MLgRyIu+bb+S6ECdIDdYJ0NlpFE9xsBPI=
-github.com/Azure/go-amqp v0.13.7/go.mod h1:wbpCKA8tR5MLgRyIu+bb+S6ECdIDdYJ0NlpFE9xsBPI=
+github.com/Azure/go-amqp v0.13.11/go.mod h1:D5ZrjQqB1dyp1A+G73xeL/kNn7D5qHJIIsNNps7YNmk=
+github.com/Azure/go-amqp v0.13.12/go.mod h1:D5ZrjQqB1dyp1A+G73xeL/kNn7D5qHJIIsNNps7YNmk=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
-github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
+github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
+github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY=
+github.com/Azure/go-autorest/autorest v0.11.23 h1:bRQWsW25/YkoxnIqXMPF94JW33qWDcrPMZ3bINaAruU=
+github.com/Azure/go-autorest/autorest v0.11.23/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs=
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
-github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk=
-github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
-github.com/Azure/go-autorest/autorest/azure/auth v0.5.7 h1:8DQB8yl7aLQuP+nuR5e2RO6454OvFlSTXXaNHshc16s=
-github.com/Azure/go-autorest/autorest/azure/auth v0.5.7/go.mod h1:AkzUsqkrdmNhfP2i54HqINVQopw0CLDnvHpJ88Zz1eI=
-github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY=
+github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
+github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A=
+github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
+github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.10 h1:F9A3Z++TtAoFysBsNOIJILoHuYBaYvhVGsMGEqPtIS8=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.10/go.mod h1:zQXYYNX9kXzRMrJNVXWUfNy38oPMF5/2TeZ4Wylc9fE=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
+github.com/Azure/go-autorest/autorest/azure/cli v0.4.3/go.mod h1:yAQ2b6eP/CmLPnmLvxtT1ALIY3OR1oFcCqVBi8vHiTc=
+github.com/Azure/go-autorest/autorest/azure/cli v0.4.4 h1:iuooz5cZL6VRcO7DVSFYxRcouqn6bFVE/e77Wts50Zk=
+github.com/Azure/go-autorest/autorest/azure/cli v0.4.4/go.mod h1:yAQ2b6eP/CmLPnmLvxtT1ALIY3OR1oFcCqVBi8vHiTc=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
@@ -108,9 +138,18 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/DisgoOrg/disgohook v1.4.4 h1:6xU+nRtyCYX7RyKvRnroJE8JMv+YIrQEMBDGUjBGDlQ=
+github.com/DisgoOrg/disgohook v1.4.4/go.mod h1:l7r9dZgfkA3KiV+ErxqweKaknnskmzZO+SRTNHvJTUU=
+github.com/DisgoOrg/log v1.1.0/go.mod h1:Qihgz6fax3JCfuO7vxVavL0LyHS0sUdQ9OmykQ2fiQs=
+github.com/DisgoOrg/log v1.1.2 h1:tGJS4jaH1PyjPRHybHp8WpYJ/4fR3fYWT4Mv1PoDGBM=
+github.com/DisgoOrg/log v1.1.2/go.mod h1:tSMofXaNhQNvzLRoL4tAiCG9yGY1ES5DLvduh7e9GRU=
+github.com/DisgoOrg/restclient v1.2.8 h1:0Kv2g2bNYUvAAeIpJ1oNHorRcj5z6qkO2kOlm4R7cAs=
+github.com/DisgoOrg/restclient v1.2.8/go.mod h1:2pc/htya/5kjxvWNYya98sb8B4mexobxmWvhTiWPt94=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
-github.com/GoogleCloudPlatform/cloudsql-proxy v1.22.0/go.mod h1:mAm5O/zik2RFmcpigNjg6nMotDL8ZXJaxKzgGVcSMFA=
+github.com/GoogleCloudPlatform/cloudsql-proxy v1.24.0/go.mod h1:3tx938GhY4FC+E1KT/jNjDw7Z5qxAEtIiERJ2sXjnII=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
@@ -125,21 +164,27 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
-github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
+github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
+github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c h1:bNpaLLv2Y4kslsdkdCwAYu8Bak1aGVtxwi8Z/wy4Yuo=
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4=
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
-github.com/ProtonMail/gopenpgp/v2 v2.2.0 h1:XLsUEY/dQhQcOg8r0ijNvMTJIKM4EBkf3K7zV+kcGj4=
-github.com/ProtonMail/gopenpgp/v2 v2.2.0/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6kK9XlZYGiDCvpM=
+github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
+github.com/ProtonMail/gopenpgp/v2 v2.2.2/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6kK9XlZYGiDCvpM=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
+github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
+github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/alecthomas/jsonschema v0.0.0-20210526225647-edb03dcab7bc/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
+github.com/alecthomas/jsonschema v0.0.0-20210920000243-787cd8204a0d/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
+github.com/alecthomas/jsonschema v0.0.0-20211209230136-e2b41affa5c1 h1:6mZ7MG/flSahicBVy4GKlWI+dzoR5rgnm7H8e17TAio=
+github.com/alecthomas/jsonschema v0.0.0-20211209230136-e2b41affa5c1/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -167,14 +212,55 @@ github.com/ashanbrown/forbidigo v1.2.0 h1:RMlEFupPCxQ1IogYOQUnIQwGEUGK8g5vAPMRyJ
github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI=
github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde h1:YOsoVXsZQPA9aOTy1g0lAJv5VzZUvwQuZqug8XPeqfM=
github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU=
+github.com/atc0005/go-teams-notify/v2 v2.6.1 h1:t22ybzQuaQs4UJe4ceF5VYGsPhs6ir3nZOId/FBy6Go=
+github.com/atc0005/go-teams-notify/v2 v2.6.1/go.mod h1:xo6GejLDHn3tWBA181F8LrllIL0xC1uRsRxq7YNXaaY=
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
-github.com/aws/aws-sdk-go v1.38.35 h1:7AlAO0FC+8nFjxiGKEmq0QLpiA8/XFr6eIxgRTwkdTg=
-github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
+github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
+github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
+github.com/aws/aws-sdk-go v1.42.24 h1:pDUeL5+HaEK+CBKsnzmjZCpLmfRek9JLMM/KhjiQorU=
+github.com/aws/aws-sdk-go v1.42.24/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs=
+github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
+github.com/aws/aws-sdk-go-v2 v1.11.2 h1:SDiCYqxdIYi6HgQfAWRhgdZrdnOuGyLDJVRSWLeHWvs=
+github.com/aws/aws-sdk-go-v2 v1.11.2/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJvBIZjzfPyQ=
+github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY=
+github.com/aws/aws-sdk-go-v2/config v1.11.0 h1:Czlld5zBB61A3/aoegA9/buZulwL9mHHfizh/Oq+Kqs=
+github.com/aws/aws-sdk-go-v2/config v1.11.0/go.mod h1:VrQDJGFBM5yZe+IOeenNZ/DWoErdny+k2MHEIpwDsEY=
+github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY=
+github.com/aws/aws-sdk-go-v2/credentials v1.6.4 h1:2hvbUoHufns0lDIsaK8FVCMukT1WngtZPavN+W2FkSw=
+github.com/aws/aws-sdk-go-v2/credentials v1.6.4/go.mod h1:tTrhvBPHyPde4pdIPSba4Nv7RYr4wP9jxXEDa1bKn/8=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0/go.mod h1:CpNzHK9VEFUCknu50kkB8z58AH2B5DvPP7ea1LHve/Y=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 h1:KiN5TPOLrEjbGCvdTQR4t0U4T87vVwALZ5Bg3jpMqPY=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2/go.mod h1:dF2F6tXEOgmW5X1ZFO/EPtWrcm7XkW07KNcJUGNtt4s=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2 h1:XJLnluKuUxQG255zPNe+04izXl7GSyUVafIsgfv9aw4=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2/go.mod h1:SgKKNBIoDC/E1ZCDhhMW3yalWjwuLjMcpLzsM/QQnWo=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2 h1:EauRoYZVNPlidZSZJDscjJBQ22JhVF2+tdteatax2Ak=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2/go.mod h1:xT4XX6w5Sa3dhg50JrYyy3e4WPYo/+WjY/BXtqXVunU=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 h1:IQup8Q6lorXeiA/rK72PeToWoWK8h7VAPgHNWdSrtgE=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2/go.mod h1:VITe/MdW6EMXPb0o0txu/fsonXbMHUU2OC2Qp7ivU4o=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2 h1:CKdUNKmuilw/KNmO2Q53Av8u+ZyXMC2M9aX8Z+c/gzg=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2/go.mod h1:FgR1tCsn8C6+Hf+N5qkfrE4IXvUL1RgW87sunJ+5J4I=
+github.com/aws/aws-sdk-go-v2/service/kms v1.5.0/go.mod h1:w7JuP9Oq1IKMFQPkNe3V6s9rOssXzOVEMNEqK1L1bao=
+github.com/aws/aws-sdk-go-v2/service/kms v1.11.1 h1:4WsetDYlA3aUYTuQQU76VMi3xH4D/CSbrx9aVqEUwHE=
+github.com/aws/aws-sdk-go-v2/service/kms v1.11.1/go.mod h1:e33KkPXn1iEeHHHflmS+Jxx09wbYw2uzAO3sQE1smg0=
+github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.6.0/go.mod h1:B+7C5UKdVq1ylkI/A6O8wcurFtaux0R1njePNPtKwoA=
+github.com/aws/aws-sdk-go-v2/service/ssm v1.10.0/go.mod h1:4dXS5YNqI3SNbetQ7X7vfsMlX6ZnboJA2dulBwJx7+g=
+github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA=
+github.com/aws/aws-sdk-go-v2/service/sso v1.6.2 h1:2IDmvSb86KT44lSg1uU4ONpzgWLOuApRl6Tg54mZ6Dk=
+github.com/aws/aws-sdk-go-v2/service/sso v1.6.2/go.mod h1:KnIpszaIdwI33tmc/W/GGXyn22c1USYxA/2KyvoeDY0=
+github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2Fh6yq8wm0HSDAimPhzCoM=
+github.com/aws/aws-sdk-go-v2/service/sts v1.11.1 h1:QKR7wy5e650q70PFKMfGF9sTo0rZgUevSSJ4wxmyWXk=
+github.com/aws/aws-sdk-go-v2/service/sts v1.11.1/go.mod h1:UV2N5HaPfdbDpkgkz4sRzWCvQswZjdO1FfqCWl0t7RA=
+github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
+github.com/aws/smithy-go v1.9.0 h1:c7FUdEqrQA1/UVKKCNDFQPNKGp4FQg3YW4Ck5SLTG58=
+github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
+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=
@@ -188,26 +274,35 @@ github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM=
github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
+github.com/bradleyfalzon/ghinstallation/v2 v2.0.4/go.mod h1:B40qPqJxWE0jDZgOR1JmaMy+4AY1eBP+IByOvqyAKp0=
github.com/caarlos0/ctrlc v1.0.0 h1:2DtF8GSIcajgffDFJzyG15vO+1PuBWOMUdFut7NnXhw=
github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw=
-github.com/caarlos0/env/v6 v6.7.0 h1:OftrMgQyETinUI4YU3WxhHeKtCRonDMtnUO14+ZRXdY=
-github.com/caarlos0/env/v6 v6.7.0/go.mod h1:FE0jGiAnQqtv2TenJ4KTa8+/T2Ss8kdS5s1VEjasoN0=
+github.com/caarlos0/env/v6 v6.9.1 h1:zOkkjM0F6ltnQ5eBX6IPI41UP/KDGEK7rRPwGCNos8k=
+github.com/caarlos0/env/v6 v6.9.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
+github.com/caarlos0/go-reddit/v3 v3.0.1 h1:w8ugvsrHhaE/m4ez0BO/sTBOBWI9WZTjG7VTecHnql4=
+github.com/caarlos0/go-reddit/v3 v3.0.1/go.mod h1:QlwgmG5SAqxMeQvg/A2dD1x9cIZCO56BMnMdjXLoisI=
+github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg=
+github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11/go.mod h1:je2KZ+LxaCNvCoKg32jtOIULcFogJKcL1ZWUaIBjKj0=
github.com/caarlos0/go-shellwords v1.0.12 h1:HWrUnu6lGbWfrDcFiHcZiwOLzHWjjrPVehULaTFgPp8=
github.com/caarlos0/go-shellwords v1.0.12/go.mod h1:bYeeX1GrTLPl5cAMYEzdm272qdsQAZiaHgeF0KTk1Gw=
-github.com/caarlos0/testfs v0.4.3 h1:q1zEM5hgsssqWanAfevJYYa0So60DdK6wlJeTc/yfUE=
-github.com/caarlos0/testfs v0.4.3/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
-github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
-github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
-github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
-github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
+github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
+github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
+github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
+github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
+github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk=
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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
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/charithe/durationcheck v0.0.8 h1:cnZrThioNW9gSV5JsRIXmkyHUbcDH7Y9hkzFDVc9/j0=
github.com/charithe/durationcheck v0.0.8/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg=
+github.com/charmbracelet/keygen v0.2.1 h1:H1yYTVe6qIDz+UILYXo6q+qLQNkvyXXA5KEhzyuEfzg=
+github.com/charmbracelet/keygen v0.2.1/go.mod h1:kFQ3Cvop12fXWX1K29vxDxV9x8ujG4wBSXq//GySSSk=
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af h1:spmv8nSH9h5oCQf40jt/ufBCt9j0/58u4G+rkeMqXGI=
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -217,6 +312,15 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211216145620-d92e9ce0af51 h1:F6fR7MjvOIk+FLQOeBCAbbKItVgbdj0l9VWPiHeBEiY=
+github.com/cncf/xds/go v0.0.0-20211216145620-d92e9ce0af51/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -227,15 +331,15 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
@@ -251,12 +355,12 @@ github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7q
github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
-github.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e h1:o0sI/cfhAXdtAbiIVeTd4hmwtwgs4cQFwRBhmbx8AKY=
-github.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e/go.mod h1:xfg4uS5LEzOj8PgZV7SQYRHbG7jPUnelEiaAVJxmhJE=
-github.com/dghubble/oauth1 v0.7.0 h1:AlpZdbRiJM4XGHIlQ8BuJ/wlpGwFEJNnB4Mc+78tA/w=
-github.com/dghubble/oauth1 v0.7.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk=
-github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU=
-github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
+github.com/dghubble/go-twitter v0.0.0-20211115160449-93a8679adecb h1:7ENzkH+O3juL+yj2undESLTaAeRllHwCs/b8z6aWSfc=
+github.com/dghubble/go-twitter v0.0.0-20211115160449-93a8679adecb/go.mod h1:qhZBgV9e4WyB1JNjHpcXVkUe3knWUwYuAPB1hITdm50=
+github.com/dghubble/oauth1 v0.7.1 h1:JjbOVSVVkms9A4h/sTQy5Jb2nFuAAVb2qVYgenJPyrE=
+github.com/dghubble/oauth1 v0.7.1/go.mod h1:0eEzON0UY/OLACQrmnjgJjmvCGXzjBCsZqL1kWDXtF0=
+github.com/dghubble/sling v1.4.0 h1:/n8MRosVTthvMbwlNZgLx579OGVjUOy3GNEv5BIqAWY=
+github.com/dghubble/sling v1.4.0/go.mod h1:0r40aNsU9EdDUVBNhfCstAtFgutjgJGYbO1oNzkMoM8=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
@@ -272,8 +376,14 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/go-control-plane v0.10.1 h1:cgDRLG7bs59Zd+apAWuzLQL95obVYAymNJek76W3mgw=
+github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/envoyproxy/protoc-gen-validate v0.6.2 h1:JiO+kJTpmYGjEodY7O1Zk8oZcNz1+f30UtwtXoFUPzE=
+github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/esimonov/ifshort v1.0.2 h1:K5s1W2fGfkoWXsFlxBNqT6J0ZCncPaKrGM5qe0bni68=
github.com/esimonov/ifshort v1.0.2/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE=
github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw=
@@ -281,19 +391,20 @@ github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA
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.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
+github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
+github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM=
github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
@@ -311,13 +422,15 @@ github.com/go-critic/go-critic v0.5.6/go.mod h1:cVjj0DfqewQVIlIAGexPCaGaZDAqGE29
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-billy/v5 v5.1.0 h1:4pl5BV4o7ZG/lterP4S6WzJ6xr49Ba5ET9ygheTYahk=
-github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
+github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
+github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
+github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
+github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
+github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
-github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc=
-github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw=
+github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
+github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
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=
@@ -341,6 +454,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
+github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
@@ -376,6 +491,9 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
+github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -393,6 +511,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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=
@@ -449,23 +568,27 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-github/v35 v35.3.0 h1:fU+WBzuukn0VssbayTT+Zo3/ESKX9JYWjbZTLOTEyho=
-github.com/google/go-github/v35 v35.3.0/go.mod h1:yWB7uCcVWaUbUP74Aq3whuMySRMatyRmq5U9FTNlbio=
-github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg=
+github.com/google/go-github/v43 v43.0.0 h1:y+GL7LIsAIF2NZlJ46ZoC/D1W1ivZasT0lnWHMYPZ+U=
+github.com/google/go-github/v43 v43.0.0/go.mod h1:ZkTvvmCXBvsfPpTHXnH/d2hP9Y0cTbvN9kr5xqyXOIc=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/go-replayers/grpcreplay v1.0.0 h1:B5kVOzJ1hBgnevTgIWhSTatQ3608yu/2NnU0Ta1d0kY=
-github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
-github.com/google/go-replayers/httpreplay v0.1.2 h1:HCfx+dQzwN9XbGTHF8qJ+67WN8glL9FTWV5rraCJ/jU=
-github.com/google/go-replayers/httpreplay v0.1.2/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
+github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
+github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
+github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE=
+github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk=
+github.com/google/go-replayers/httpreplay v1.0.0 h1:8SmT8fUYM4nueF+UnXIX8LJxNTb1vpPuknXz+yTWzL4=
+github.com/google/go-replayers/httpreplay v1.0.0/go.mod h1:LJhKoTwS5Wy5Ld/peq8dFFG5OfJyHEz7ft+DsTUv25M=
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 v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE=
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
+github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@@ -479,22 +602,30 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
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-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/rpmpack v0.0.0-20210410105602-e20c988a6f5a h1:XC048Fc/OB2rUl/BxruopEl2u/EP6cJNFveVxI1cvdk=
-github.com/google/rpmpack v0.0.0-20210410105602-e20c988a6f5a/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
+github.com/google/rpmpack v0.0.0-20211125064518-d0ed9b1b61b9 h1:ClPt4zdk8fbRqTANpgege8HBktalZaIz32dIGnwcVZI=
+github.com/google/rpmpack v0.0.0-20211125064518-d0ed9b1b61b9/go.mod h1:3YnKULqkUnzpgcZS8uQgbTRwcAW4IqYiUzWFy6aVvu0=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw=
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
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 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
+github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -503,12 +634,12 @@ github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254 h1:Nb2aRlC
github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw=
github.com/goreleaser/chglog v0.1.2 h1:tdzAb/ILeMnphzI9zQ7Nkq+T8R9qyXli8GydD8plFRY=
github.com/goreleaser/chglog v0.1.2/go.mod h1:tTZsFuSZK4epDXfjMkxzcGbrIOXprf0JFp47BjIr3B8=
-github.com/goreleaser/fileglob v1.2.0 h1:OErqbdzeg/eibfDGPHDQDN8jL5u1jNyxA5IQzNPLLoU=
-github.com/goreleaser/fileglob v1.2.0/go.mod h1:rFyb2pXaK3YdnYnSjn6lifw0h2Q6s8OfOsx6I6bXkKE=
-github.com/goreleaser/goreleaser v0.178.0 h1:rc4yBn2vQ2eNMz9mPx+0/2v9R24c4meFqinpl3K8c00=
-github.com/goreleaser/goreleaser v0.178.0/go.mod h1:/3Iz1GSJFxAVLAu1gkZoV8mXm6WJ94rtvk+t+81+Uw4=
-github.com/goreleaser/nfpm/v2 v2.6.0 h1:bwDU9o4/CVTSpqASJA7+r+rkqpTGamQKYHMRH3wDlRE=
-github.com/goreleaser/nfpm/v2 v2.6.0/go.mod h1:qaMnjBaZz/2vInOIWx0IbuKuaZpaVB6O8oLG0u4qH1Y=
+github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I=
+github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU=
+github.com/goreleaser/goreleaser v1.6.1 h1:FzbmxtKCu+/R4dC1Yf3DR/VpBN8jBA2O2X11465LxE4=
+github.com/goreleaser/goreleaser v1.6.1/go.mod h1:GNAzFVZsES0XQyspLpg0pYMRTAeC2wmNejmmxF9r90g=
+github.com/goreleaser/nfpm/v2 v2.14.0 h1:1xFq1F1Dy3HSdbjUtd+8CCahxLNlQwylcHwoLOg/lW0=
+github.com/goreleaser/nfpm/v2 v2.14.0/go.mod h1:46Gk7U5PrLL34NxrsorlkiavoX5gwyGOYQSMaPn5xdw=
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -540,24 +671,28 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo=
+github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
+github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
+github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
+github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -574,6 +709,9 @@ github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
+github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
+github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
+github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@@ -584,8 +722,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
-github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
+github.com/jarcoal/httpmock v1.1.0 h1:F47ChZj1Y2zFsCXxNkBPwNNKnAyOATcdQibk0qEdVCE=
+github.com/jarcoal/httpmock v1.1.0/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -614,7 +752,6 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -637,7 +774,12 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
+github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
+github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -672,10 +814,9 @@ github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJ
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -688,6 +829,7 @@ github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKo
github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA=
github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
+github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
@@ -696,8 +838,10 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
+github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -707,8 +851,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -729,10 +874,12 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a h1:eU8j/ClY2Ty3qdHnn0TyW3ivFoPC/0F1gQZz8yTxbbE=
+github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a/go.mod h1:v8eSC2SMp9/7FTKUncp7fH9IwPfw+ysMObcEz5FWheQ=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
-github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0=
-github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -746,8 +893,9 @@ github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@@ -757,6 +905,16 @@ github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EH
github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k=
github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8=
github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s=
+github.com/muesli/coral v1.0.0 h1:odyqkoEg4aJAINOzvnjN4tUsdp+Zleccs7tRIAkkYzU=
+github.com/muesli/coral v1.0.0/go.mod h1:bf91M/dkp7iHQw73HOoR9PekdTJMTD6ihJgWoDitde8=
+github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
+github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
+github.com/muesli/mango-coral v1.0.1 h1:W3nGbUC/q5vLscQ6GPzteHZrJI1Msjw5Hns82o0xRkI=
+github.com/muesli/mango-coral v1.0.1/go.mod h1:EPSlYH67AtcxQrxssNw6r/lMFxHTjuDoGfq9Uxxevhg=
+github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg=
+github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
+github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
+github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=
github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc=
@@ -790,8 +948,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
-github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
-github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@@ -860,8 +1016,9 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+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/ryancurrah/gomodguard v1.2.3 h1:ww2fsjqocGCAFamzvv/b8IsRduuHHeK2MHTcTxZTQX8=
github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg=
github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw=
@@ -869,8 +1026,6 @@ github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0K
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA=
github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
-github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b h1:+gCnWOZV8Z/8jehJ2CdqB47Z3S+SREmQcuXkRFLNsiI=
-github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/securego/gosec/v2 v2.8.1 h1:Tyy/nsH39TYCOkqf5HAgRE+7B5D8sHDwPdXRgFWokh8=
github.com/securego/gosec/v2 v2.8.1/go.mod h1:pUmsq6+VyFEElJMUX+QB3p3LWNHXg1R3xh2ssVJPs8Q=
@@ -883,7 +1038,6 @@ github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAx
github.com/shirou/gopsutil/v3 v3.21.7/go.mod h1:RGl11Y7XMTQPmHh8F0ayC6haKNBgH4PXMJuTAcMOlz4=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@@ -892,8 +1046,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/slack-go/slack v0.9.4 h1:C+FC3zLxLxUTQjDy2RZeMHYon005zsCROiZNWVo+opQ=
-github.com/slack-go/slack v0.9.4/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ=
+github.com/slack-go/slack v0.10.2 h1:KMN/h2sgUninHXvQI8PrR/PHBUuWp2NPvz2Kr66tki4=
+github.com/slack-go/slack v0.10.2/go.mod h1:5FLdBRv7VW/d9EBxx/eEktOptWygbA9K2QK/KW7ds1s=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
@@ -908,6 +1062,7 @@ github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0H
github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
@@ -951,6 +1106,8 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b h1:HxLVTlqcHhFAz3nWUcuvpH7WuOMv8LQoCWmruLfFH2U=
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
+github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
+github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/tetafro/godot v1.4.9 h1:wsNd0RuUxISqqudFqchsSsMqsM188DoZVPBeKl87tP0=
github.com/tetafro/godot v1.4.9/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8=
@@ -975,7 +1132,6 @@ github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
-github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
@@ -995,14 +1151,13 @@ github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
-github.com/vartanbeno/go-reddit/v2 v2.0.0 h1:fxYMqx5lhbmJ3yYRN1nnQC/gecRB3xpUS2BbG7GLpsk=
-github.com/vartanbeno/go-reddit/v2 v2.0.0/go.mod h1:758/S10hwZSLm43NPtwoNQdZFSg3sjB5745Mwjb0ANI=
github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE=
-github.com/xanzy/go-gitlab v0.50.3 h1:M7ncgNhCN4jaFNyXxarJhCLa9Qi6fdmCxFFhMTQPZiY=
-github.com/xanzy/go-gitlab v0.50.3/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
+github.com/xanzy/go-gitlab v0.56.0 h1:/QHBvk3IKVNwvXB/UOWVb5J6VCN6r2bg9/WxjUbFY/0=
+github.com/xanzy/go-gitlab v0.56.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
-github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
+github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
+github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@@ -1034,25 +1189,29 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
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.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
-go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-gocloud.dev v0.23.0 h1:u/6F8slWwaZPgGpjpNp0jzH+1P/M2ri7qEP3lFgbqBE=
-gocloud.dev v0.23.0/go.mod h1:zklCCIIo1N9ELkU2S2E7tW8P8eeMU7oGLeQCXdDwx9Q=
+go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
+go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
+gocloud.dev v0.24.0 h1:cNtHD07zQQiv02OiwwDyVMuHmR7iQt2RLkzoAgz7wBs=
+gocloud.dev v0.24.0/go.mod h1:uA+als++iBX5ShuG4upQo/3Zoz49iIPlYUWHV5mM8w8=
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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=
@@ -1073,9 +1232,13 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
+golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+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-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE=
+golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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=
@@ -1101,7 +1264,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@@ -1116,6 +1278,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1167,13 +1330,18 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
-golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
+golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211007125505-59d4e928ea9d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1183,14 +1351,19 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1273,20 +1446,34 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 h1:A0Qkn7Z/n8zC1xd9LTw17AiKlBRK64tw3ejWQiEqca0=
+golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE=
+golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
+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=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1303,8 +1490,10 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
+golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1400,6 +1589,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
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=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -1412,7 +1602,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1431,13 +1620,25 @@ google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSr
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.37.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
-google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA=
-google.golang.org/api v0.46.0 h1:jkDWHOBIoNSD0OQpq4rtBVu+Rh325MPjXG1rakAp8JU=
google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I=
+google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
+google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
+google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
+google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
+google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU=
+google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
+google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
+google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E=
+google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
+google.golang.org/api v0.63.0 h1:n2bqqK895ygnBpdPDYetfy23K7fJ22wsrZKCyfuRkkA=
+google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1455,8 +1656,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -1492,22 +1691,43 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D
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-20210126160654-44e461bb6506/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-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=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210423144448-3a41ef94ed2b/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
+google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
+google.golang.org/genproto v0.0.0-20210517163617-5e0236093d7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
+google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
+google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210825212027-de86158e7fda/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
+google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
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.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
@@ -1529,8 +1749,15 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
+google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
+google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
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=
@@ -1542,9 +1769,12 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
+gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1560,6 +1790,8 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
+gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
diff --git a/web/app/.eslintrc.js b/web/app/.eslintrc.js
index 68ba9db..3c881ee 100644
--- a/web/app/.eslintrc.js
+++ b/web/app/.eslintrc.js
@@ -1,12 +1,10 @@
module.exports = {
- parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
],
parserOptions: {
- ecmaVersion: 2020,
+ ecmaVersion: 2022,
sourceType: 'module',
- project: './tsconfig.json',
extraFileExtensions: ['.svelte']
},
env: {
@@ -19,25 +17,13 @@ module.exports = {
processor: 'svelte3/svelte3',
extends: [
'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
- plugins: ['svelte3', '@typescript-eslint'],
+ plugins: ['svelte3'],
settings: {
- 'svelte3/typescript': require('typescript')
// ignore style tags in Svelte because of Tailwind CSS
// See https://github.com/sveltejs/eslint-plugin-svelte3/issues/70
//'svelte3/ignore-styles': () => true
},
- },
- {
- files: ['**/*.ts'],
- extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:@typescript-eslint/recommended-requiring-type-checking',
- ],
- plugins: ['@typescript-eslint'],
}
],
rules: {
diff --git a/web/app/package.json b/web/app/package.json
index bb98f26..3e3957f 100644
--- a/web/app/package.json
+++ b/web/app/package.json
@@ -4,20 +4,17 @@
"private": true,
"scripts": {
"build": "rollup -c",
- "check": "svelte-check --tsconfig ./tsconfig.json",
- "format": "prettier --write ./src/**/*.{js,svelte,html,ts}",
- "lint": "tsc --noEmit && prettier --write ./src/**/*.{js,svelte,html,ts} && eslint src/**/*.{js,ts,svelte}",
- "lint:fix": "tsc --noEmit && prettier --write ./src/**/*.{js,svelte,html,ts} && eslint --fix src/**/*.{js,ts,svelte}"
+ "check": "svelte-check",
+ "format": "prettier --write ./src/**/*.{js,svelte,html}",
+ "lint": "prettier --write ./src/**/*.{js,svelte,html} && eslint src/**/*.{js,svelte}",
+ "lint:fix": "prettier --write ./src/**/*.{js,svelte,html} && eslint --fix src/**/*.{js,svelte}"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.0.0",
- "@rollup/plugin-typescript": "^8.0.0",
- "@tsconfig/svelte": "^2.0.1",
- "@typescript-eslint/eslint-plugin": "^4.29.3",
- "@typescript-eslint/parser": "^4.29.3",
+ "@sargassum-world/styles": "^0.2.0",
"bulma": "^0.9.3",
- "eslint": "^7.32.0",
+ "eslint": "^8.10.0",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-svelte3": "^3.2.1",
"prettier": "^2.4.1",
@@ -33,16 +30,13 @@
"svelte": "^3.0.0",
"svelte-check": "^2.0.0",
"svelte-dark-mode": "^2.0.0",
- "svelte-preprocess": "^4.0.0",
- "tslib": "^2.0.0",
- "typescript": "^4.0.0"
+ "svelte-preprocess": "^4.0.0"
},
"dependencies": {
"@fontsource/atkinson-hyperlegible": "^4.5.1",
"@fontsource/oxygen-mono": "^4.5.0",
"@hotwired/turbo": "^7.0.1",
- "async-mutex": "^0.3.2",
- "stimulus": "2.0.0",
- "stimulus-use": "^0.41.0"
+ "@sargassum-world/stimulated": "^0.2.9",
+ "stimulus": "2.0.0"
}
}
diff --git a/web/app/rollup.config.js b/web/app/rollup.config.js
index fb180a8..92325a3 100644
--- a/web/app/rollup.config.js
+++ b/web/app/rollup.config.js
@@ -3,7 +3,6 @@ import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
-import typescript from '@rollup/plugin-typescript';
import css from 'rollup-plugin-css-only';
import scss from 'rollup-plugin-scss';
import { existsSync, mkdirSync, writeFileSync } from 'fs';
@@ -15,7 +14,7 @@ const buildDir = 'public/build';
function themeGenerator(theme) {
return {
- input: `src/theme-${theme}.ts`,
+ input: `src/theme-${theme}.js`,
output: {
sourcemap: !production,
format: 'iife',
@@ -36,8 +35,9 @@ function themeGenerator(theme) {
writeFileSync(`${buildDir}/theme-${theme}.css`, styles);
purify(
[
+ 'node_modules/@sargassum-world/**/*.js',
+ 'node_modules/@sargassum-world/**/*.svelte',
'src/**/*.js',
- 'src/**/*.ts',
'src/**/*.svelte',
'../templates/**/*.tmpl',
],
@@ -66,7 +66,7 @@ function themeGenerator(theme) {
function bundleGenerator(type, appName, context) {
return {
- input: `src/main-${type}.ts`,
+ input: `src/main-${type}.js`,
output: {
sourcemap: !production,
format: 'iife',
@@ -100,7 +100,7 @@ function bundleGenerator(type, appName, context) {
}),
// we'll extract any component CSS out into
// a separate file - better for performance
- css({ output: 'bundle.css' }),
+ css({ output: `${buildDir}/bundle-${type}.css` }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
@@ -112,10 +112,6 @@ function bundleGenerator(type, appName, context) {
dedupe: ['svelte']
}),
commonjs(),
- typescript({
- sourceMap: !production,
- inlineSources: !production
- }),
// If we're building for production (npm run build
// instead of npm run dev), minify
diff --git a/web/app/src/main-deferred.ts b/web/app/src/main-deferred.js
similarity index 52%
rename from web/app/src/main-deferred.ts
rename to web/app/src/main-deferred.js
index 8c9547b..c8404c2 100644
--- a/web/app/src/main-deferred.ts
+++ b/web/app/src/main-deferred.js
@@ -1,23 +1,24 @@
import * as Turbo from '@hotwired/turbo';
+import {
+ CSRFController,
+ DefaultScrollableController,
+ FormSubmissionController,
+ NavigationLinkController,
+ NavigationMenuController,
+ ThemeController,
+ TurboCacheController,
+} from '@sargassum-world/stimulated';
import { Application } from 'stimulus';
-import ThemeController from './sprinkles/theme.controller';
-import NavigationMenuController from './sprinkles/navigation-menu.controller';
-import NavigationLinkController from './sprinkles/navigation-link.controller';
-import FormSubmissionController from './sprinkles/form-submission.controller';
-import CSRFController from './sprinkles/csrf.controller';
-import DefaultScrollableController from './sprinkles/default-scrollable.controller';
-import TurboCacheController from './sprinkles/turbo-cache.controller';
-
Turbo.session.drive = true;
const Stimulus = Application.start();
-Stimulus.register('theme', ThemeController);
-Stimulus.register('navigation-menu', NavigationMenuController);
-Stimulus.register('navigation-link', NavigationLinkController);
-Stimulus.register('form-submission', FormSubmissionController);
Stimulus.register('csrf', CSRFController);
Stimulus.register('default-scrollable', DefaultScrollableController);
+Stimulus.register('form-submission', FormSubmissionController);
+Stimulus.register('navigation-link', NavigationLinkController);
+Stimulus.register('navigation-menu', NavigationMenuController);
+Stimulus.register('theme', ThemeController);
Stimulus.register('turbo-cache', TurboCacheController);
export {};
diff --git a/web/app/src/main-eager.js b/web/app/src/main-eager.js
new file mode 100644
index 0000000..f442cc7
--- /dev/null
+++ b/web/app/src/main-eager.js
@@ -0,0 +1,5 @@
+import { ThemeToggle } from '@sargassum-world/stimulated/util';
+
+ThemeToggle.onLoad();
+
+export {};
diff --git a/web/app/src/main-eager.ts b/web/app/src/main-eager.ts
deleted file mode 100644
index 4786ef0..0000000
--- a/web/app/src/main-eager.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import * as ThemeToggle from './sprinkles/theme-toggle';
-
-ThemeToggle.onLoad();
-
-export {};
diff --git a/web/app/src/sprinkles/csrf.controller.js b/web/app/src/sprinkles/csrf.controller.js
deleted file mode 100644
index 073dfea..0000000
--- a/web/app/src/sprinkles/csrf.controller.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { Controller } from 'stimulus';
-import { Mutex } from 'async-mutex';
-
-// These are globals because we only need to fetch a CSRF token once; we can reuse it as long as
-// the CSRF cookie (for the Double Submit Cookie pattern) remains valid.
-let csrfToken = null;
-let fetchMutex = new Mutex();
-
-export default class extends Controller {
- static targets = ['token', 'route', 'omit', 'submit'];
- async connect() {
- this.omitTarget.value = true;
- if (this.hasValidToken()) {
- csrfToken = this.tokenTarget.value;
- return;
- }
- await this.addToken()
- }
-
- async addToken(e) {
- if (this.hasValidToken() || this.setToken()) {
- return;
- }
-
- if (e !== undefined) {
- e.preventDefault();
- }
- if (fetchMutex.isLocked()) {
- await fetchMutex.waitForUnlock();
- }
- if (this.setToken()) {
- if (e !== undefined) {
- e.target.submit();
- }
- return;
- }
-
- await fetchMutex.runExclusive(async () => {
- const response = await fetch(this.routeTarget.value);
- const responseBody = await response.json();
- csrfToken = responseBody.token;
- this.setToken();
- });
- if (e !== undefined) {
- // Assumes the controller is mounted on a form!
- e.target.submit();
- }
- }
-
- setToken() {
- if (csrfToken === null) {
- return false
- }
- this.tokenTarget.value = csrfToken;
- return this.hasValidToken()
- }
-
- hasValidToken() {
- return this.tokenTarget.value.length > 0
- }
-}
diff --git a/web/app/src/sprinkles/default-scrollable.controller.js b/web/app/src/sprinkles/default-scrollable.controller.js
deleted file mode 100644
index b55c8e7..0000000
--- a/web/app/src/sprinkles/default-scrollable.controller.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Controller } from 'stimulus';
-
-export default class extends Controller {
- connect() {
- this.element.focus();
- var scrollpos = sessionStorage.getItem('scrollpos');
- // TODO: instead of setting scrollTop (which causes a flash of unscrolled content,
- // change the HTML element structure of the navbar so that the main container just
- // has the window scroll, to let the browser natively keep track of the scroll
- // position when the page is refreshed
- if (scrollpos) {
- this.element.scrollTop = scrollpos;
- sessionStorage.removeItem('scrollpos');
- }
-
- const element = this.element;
- window.addEventListener('beforeunload', function () {
- sessionStorage.setItem('scrollpos', element.scrollTop);
- })
- }
-}
diff --git a/web/app/src/sprinkles/form-submission.controller.js b/web/app/src/sprinkles/form-submission.controller.js
deleted file mode 100644
index 41431a4..0000000
--- a/web/app/src/sprinkles/form-submission.controller.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Controller } from 'stimulus';
-
-export default class extends Controller {
- static targets = ['submit', 'submitter'];
-
- submit() {
- this.submitTarget.setAttribute('disabled', true);
- const progress = document.createElement('progress');
- progress.classList.add('progress');
- progress.classList.add('is-small');
- progress.classList.add('is-info');
- this.submitterTarget.appendChild(progress);
- }
-}
diff --git a/web/app/src/sprinkles/navigation-link.controller.js b/web/app/src/sprinkles/navigation-link.controller.js
deleted file mode 100644
index 033758a..0000000
--- a/web/app/src/sprinkles/navigation-link.controller.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Controller } from 'stimulus';
-
-export default class extends Controller {
- connect() {
- this.updateActiveListener = (event) => {
- const location = event.path[2].location.href;
- if (this.element === undefined) {
- return;
- }
-
- if (location.startsWith(this.element.href)) {
- this.element.classList.add('is-active');
- } else {
- this.element.classList.remove('is-active');
- }
- };
-
- document.addEventListener('turbo:render', this.updateActiveListener);
- }
- disconnect() {
- if (this.updateActiveListener === undefined) {
- return;
- }
-
- document.removeEventListener('turbo:render', this.updateActiveListener);
- }
-
- updateActive;
-}
diff --git a/web/app/src/sprinkles/navigation-menu.controller.js b/web/app/src/sprinkles/navigation-menu.controller.js
deleted file mode 100644
index d4a0f5a..0000000
--- a/web/app/src/sprinkles/navigation-menu.controller.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Controller } from 'stimulus';
-
-export default class extends Controller {
- static targets = ['toggle', 'close', 'menu'];
- connect() {
- this.closeTarget.classList.add('is-hidden');
- this.toggleTarget.setAttribute('href', '#');
- }
-
- open() {
- this.menuTarget.classList.add('is-active');
- this.toggleTarget.classList.add('is-active');
- this.toggleTarget.setAttribute('aria-expanded', true);
- }
-
- close() {
- this.menuTarget.classList.remove('is-active');
- this.toggleTarget.classList.remove('is-active');
- this.toggleTarget.setAttribute('aria-expanded', false);
- }
-
- toggle(event) {
- if (this.menuTarget.classList.contains('is-active')) {
- this.close();
- } else {
- this.open();
- }
- event.preventDefault();
- }
-}
diff --git a/web/app/src/sprinkles/theme-toggle.ts b/web/app/src/sprinkles/theme-toggle.ts
deleted file mode 100644
index 0bee0eb..0000000
--- a/web/app/src/sprinkles/theme-toggle.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-type Theme = 'dark' | 'light';
-
-export function loadThemeSetting(): Theme {
- const theme = window.localStorage.getItem('theme');
- switch (theme) {
- case 'dark':
- return 'dark';
- default:
- return 'light';
- }
-}
-export function storeThemeSetting(theme: Theme): void {
- window.localStorage.setItem('theme', theme);
-}
-export function invert(theme: Theme): Theme {
- switch (theme) {
- case 'dark':
- return 'light';
- default:
- return 'dark';
- }
-}
-function setThemeProperties(stylesheetID: string, theme: Theme): void {
- const stylesheet = document.querySelector(`link#${stylesheetID}`);
- if (stylesheet === null || !(stylesheet instanceof HTMLLinkElement)) {
- return;
- }
-
- const active = stylesheet.id === `${theme}-theme`;
- stylesheet.rel = active ? 'stylesheet' : 'stylesheet alternate';
-}
-export function setTheme(theme: Theme): void {
- setThemeProperties('light-theme', theme);
- setThemeProperties('dark-theme', theme);
-}
-
-export function onLoad(): void {
- const theme = loadThemeSetting();
- if (theme === 'dark') {
- // Set an initial color to prevent a white flash when dark mode has been set
- document.documentElement.classList.add('initial-load-dark');
- const darkStylesheet = document.querySelector('link#dark-theme');
- if (darkStylesheet !== null && darkStylesheet instanceof HTMLLinkElement) {
- darkStylesheet.addEventListener('load', () => {
- document.documentElement.classList.remove('initial-load-dark');
- });
- }
- }
- setTheme(theme);
- storeThemeSetting(theme);
-}
diff --git a/web/app/src/sprinkles/theme.controller.ts b/web/app/src/sprinkles/theme.controller.ts
deleted file mode 100644
index 88ed6db..0000000
--- a/web/app/src/sprinkles/theme.controller.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Controller } from 'stimulus';
-
-import {
- invert,
- loadThemeSetting,
- storeThemeSetting,
- setTheme,
-} from './theme-toggle';
-
-export default class extends Controller {
- connect(): void {
- this.element.classList.remove('is-hidden');
- }
- toggle(): void {
- const theme = invert(loadThemeSetting());
- setTheme(theme);
- storeThemeSetting(theme);
- }
-}
diff --git a/web/app/src/sprinkles/turbo-cache.controller.js b/web/app/src/sprinkles/turbo-cache.controller.js
deleted file mode 100644
index 0b5f078..0000000
--- a/web/app/src/sprinkles/turbo-cache.controller.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import * as Turbo from '@hotwired/turbo';
-import { Controller } from 'stimulus';
-
-export default class extends Controller {
- clear(e) {
- // This assumes the controller is attached to a form!
- e.preventDefault();
- Turbo.clearCache();
- e.target.submit();
- }
-}
diff --git a/web/app/src/styles/app/bulma.scss b/web/app/src/styles/app/bulma.scss
deleted file mode 100644
index 1c7c5b1..0000000
--- a/web/app/src/styles/app/bulma.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-@charset 'utf-8';
-
-@import 'node_modules/bulma/bulma.sass';
diff --git a/web/app/src/styles/app/components.scss b/web/app/src/styles/app/components.scss
deleted file mode 100644
index 477c961..0000000
--- a/web/app/src/styles/app/components.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-@charset 'utf-8';
-
-@import 'styles/shared/components/accordions.scss';
-@import 'styles/shared/components/cards.scss';
-//@import 'styles/shared/components/modals.scss';
-@import 'styles/shared/components/panels.scss';
-@import 'styles/shared/components/progress.scss';
-@import 'styles/shared/components/scroller.scss';
-@import 'styles/shared/components/sections.scss';
-//@import 'styles/shared/components/toolbars.scss';
-@import 'styles/shared/components/typed-tags.scss';
-
-@import 'styles/app/components/accordions.scss';
-@import 'styles/app/components/turbo-progress.scss';
diff --git a/web/app/src/styles/app/components/_all.scss b/web/app/src/styles/app/components/_all.scss
new file mode 100644
index 0000000..8dd25e0
--- /dev/null
+++ b/web/app/src/styles/app/components/_all.scss
@@ -0,0 +1,4 @@
+@charset 'utf-8';
+
+@import 'styles/app/components/accordions.scss';
+@import 'styles/app/components/turbo-progress.scss';
diff --git a/web/app/src/styles/app/theme-overrides.scss b/web/app/src/styles/app/theme-overrides.scss
deleted file mode 100644
index 89e0468..0000000
--- a/web/app/src/styles/app/theme-overrides.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-@charset 'utf-8';
-
-@import 'styles/shared/theme-overrides/breadcrumbs.scss';
-@import 'styles/shared/theme-overrides/buttons.scss';
-@import 'styles/shared/theme-overrides/forms.scss';
-@import 'styles/shared/theme-overrides/navbar.scss';
-@import 'styles/shared/theme-overrides/tags.scss';
diff --git a/web/app/src/styles/shared/colors-base.vars.scss b/web/app/src/styles/shared/colors-base.vars.scss
deleted file mode 100644
index 3a150c8..0000000
--- a/web/app/src/styles/shared/colors-base.vars.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-@charset 'utf-8';
-
-// Grayscale
-
-$grayscale-hue: 215;
-$grayscale-sat: 25%;
-$black: hsl($grayscale-hue, $grayscale-sat * 2, 8%);
-$black-bis: hsl($grayscale-hue, $grayscale-sat * 1.5, 10%);
-$black-ter: hsl($grayscale-hue, $grayscale-sat * 1.25, 12%);
-$grey-darker: hsl($grayscale-hue, $grayscale-sat, 20%);
-$grey-dark: hsl($grayscale-hue, $grayscale-sat, 29%);
-$grey: hsl($grayscale-hue, $grayscale-sat, 48%);
-$grey-light: hsl($grayscale-hue, $grayscale-sat, 71%);
-$grey-lighter: hsl($grayscale-hue, $grayscale-sat, 82%);
-$grey-lightest: hsl($grayscale-hue, $grayscale-sat, 93%);
-$white-ter: hsl($grayscale-hue, $grayscale-sat, 95%);
-$white-bis: hsl($grayscale-hue, $grayscale-sat, 98%);
-$white: hsl($grayscale-hue, $grayscale-sat, 100%);
diff --git a/web/app/src/styles/shared/colors-dark.vars.scss b/web/app/src/styles/shared/colors-dark.vars.scss
deleted file mode 100644
index 5cd3686..0000000
--- a/web/app/src/styles/shared/colors-dark.vars.scss
+++ /dev/null
@@ -1,56 +0,0 @@
-@charset 'utf-8';
-
-// Theme Colors
-
-$scheme-main: $black;
-$scheme-main-bis: $black-bis;
-$scheme-main-ter: $black-ter;
-$scheme-main-grey: $grey-darker;
-$scheme-main-grey-bis: $grey-dark;
-$scheme-main-grey-ter: $grey;
-$scheme-invert-grey-ter: $grey-light;
-$scheme-invert-grey-bis: $grey-lighter;
-$scheme-invert-grey: $grey-lightest;
-$scheme-invert-ter: $white-ter;
-$scheme-invert-bis: $white-bis;
-$scheme-invert: $white;
-
-$green: hsl(153, 53%, 30%);
-$turquoise: hsl(175, 100%, 30%);
-$red: hsl(348, 86%, 50%);
-$red-light: change-color($red, $lightness: 90%);
-
-$danger-light: $red-light;
-
-// Element Colors
-
-$body-background-color: $scheme-main-ter;
-$background: $scheme-main-ter;
-$focus-color: change-color($cyan, $lightness: 40%);
-$scrollbar-color: $scheme-main-grey-bis;
-$scrollbar-hover-color: $scheme-main-grey-ter;
-
-// Component Colors
-
-$toolbar-background-color: $scheme-main-bis;
-
-$navbar-item-color: $scheme-invert-ter;
-$navbar-background-color: $scheme-main;
-$navbar-button-active-color: $scheme-invert;
-$navbar-button-active-background: $scheme-main-grey;
-
-$card-background-color: $scheme-main;
-$card-header-background-color: $scheme-main-grey;
-$card-item-border-color: $card-header-background-color;
-
-$modal-dialog-background: $scheme-main-grey;
-
-$zerotier-address-tag-color: change-color($cyan, $lightness: 85%);
-$zerotier-address-tag-background-color: change-color($cyan, $lightness: 30%);
-$zerotier-network-number-tag-color: change-color($green, $lightness: 85%);
-$zerotier-network-number-tag-background-color: change-color($green, $lightness: 30%);
-$zerotier-unknown-network-tag-color: $background;
-$socket-tag-color: change-color($purple, $lightness: 90%);
-$socket-tag-background-color: change-color($purple, $lightness: 20%);
-$domain-name-tag-color: $white;
-$domain-name-tag-background-color: $grey-dark;
diff --git a/web/app/src/styles/shared/colors-light.vars.scss b/web/app/src/styles/shared/colors-light.vars.scss
deleted file mode 100644
index 6fa5659..0000000
--- a/web/app/src/styles/shared/colors-light.vars.scss
+++ /dev/null
@@ -1,53 +0,0 @@
-@charset 'utf-8';
-
-// Theme Colors
-
-$scheme-main: $white;
-$scheme-main-bis: $white-bis;
-$scheme-main-ter: $white-ter;
-$scheme-main-grey: $grey-lightest;
-$scheme-main-grey-bis: $grey-lighter;
-$scheme-main-grey-ter: $grey-light;
-$scheme-invert-grey-ter: $grey;
-$scheme-invert-grey-bis: $grey-dark;
-$scheme-invert-grey: $grey-darker;
-$scheme-invert-ter: $black-ter;
-$scheme-invert-bis: $black-bis;
-$scheme-invert: $black;
-
-$green: hsl(153, 53%, 40%);
-$turquoise: hsl(175, 100%, 35%);
-$red: hsl(348, 86%, 61%);
-
-// Element Colors
-
-$body-background-color: $scheme-main;
-$background: $scheme-main;
-$focus-color: change-color($cyan, $lightness: 65%);
-$scrollbar-color: $scheme-main-grey-ter;
-$scrollbar-hover-color: $scheme-invert-grey-ter;
-
-// Component Colors
-
-$toolbar-background-color: $scheme-main-grey;
-
-$navbar-item-color: $scheme-invert-ter;
-$navbar-background-color: $scheme-main-grey-bis;
-$navbar-button-active-color: $scheme-invert;
-$navbar-button-active-background: $scheme-main-grey-ter;
-
-$card-background-color: $scheme-main-grey;
-$card-header-background-color: $scheme-main-grey-bis;
-$card-item-border-color: $card-header-background-color;
-
-$modal-dialog-background: $scheme-main;
-
-$zerotier-address-tag-color: $scheme-invert-grey-bis;
-$zerotier-address-tag-background-color: change-color($cyan, $lightness: 80%);
-$zerotier-network-number-tag-color: $scheme-invert-grey-bis;
-$zerotier-network-number-tag-background-color: change-color($green, $lightness: 80%);
-$zerotier-unknown-network-tag-color: $background;
-$socket-tag-color: $scheme-invert-grey-bis;
-$socket-tag-background-color: change-color($purple, $lightness: 90%);
-$domain-name-tag-color: $white;
-$domain-name-tag-background-color: $grey;
diff --git a/web/app/src/styles/shared/components/accordions.scss b/web/app/src/styles/shared/components/accordions.scss
deleted file mode 100644
index 6d673c1..0000000
--- a/web/app/src/styles/shared/components/accordions.scss
+++ /dev/null
@@ -1,55 +0,0 @@
-@charset 'utf-8';
-
-ul[data-accordion] {
- list-style: none;
- margin: 0;
-}
-ul[data-accordion] li[data-accordion-item] + li[data-accordion-item] {
- margin-top: 0;
-}
-li[data-accordion-item], details[data-accordion-item] {
- display: block;
- padding: 0;
- margin: 0;
-}
-.panel.entity-panel {
- li[data-accordion-item].panel-block, details[data-accordion-item].panel-block {
- padding: 0;
- }
-}
-li[data-accordion-item]>button {
- @include reset;
-
- display: block;
- width: 100%;
- padding-left: $gap;
- padding-right: $gap;
- padding-top: 0.5 * $gap;
- padding-bottom: 0.5 * $gap;
- cursor: pointer;
- text-align: left;
-}
-.accordion-header {
- h1, h2, h3, h4, h5, h6 {
- @include reset;
- display: inline;
- font-weight: normal;
- }
-}
-.accordion-header.level {
- display: flex;
- margin-bottom: 0;
-}
-button[aria-expanded="true"] .accordion-header .icon {
- transform: rotate(0.5turn);
-}
-li[data-accordion-item]>div {
- display: unset;
-}
-.accordion-content {
- width: calc(100% - 2 * #{$gap});
- padding-left: $gap;
- padding-right: $gap;
- padding-top: 0.5 * $gap;
- padding-bottom: $gap;
-}
diff --git a/web/app/src/styles/shared/components/cards.scss b/web/app/src/styles/shared/components/cards.scss
deleted file mode 100644
index 9368437..0000000
--- a/web/app/src/styles/shared/components/cards.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@charset 'utf-8';
-
-.card.section-card {
- display: inline-block;
- vertical-align: top;
- border-radius: 0;
- width: 100%;
- max-width: 36em;
- margin-bottom: $gap;
- margin-right: $gap;
-
- @include from($navside-breakpoint) {
- width: calc(100% - #{2 * $gap});
- margin-bottom: 2 * $gap;
- margin-right: 2 * $gap;
- }
-
- h4, h5 {
- margin-bottom: 0;
- }
- h4:not(:first-child), h5:not(:first-child) {
- margin-top: 0.5em;
- }
- p:not(:last-child) {
- margin-bottom: 0;
- }
-}
-
-.info-card-container {
- padding-right: $gap;
- padding-bottom: $gap;
-
- @include from($navside-breakpoint) {
- padding-right: 2 * $gap;
- padding-bottom: 2 * $gap;
- }
-}
-.card.info-card {
- display: inline-block;
- vertical-align: top;
- border-radius: 0;
- width: calc(100% - #{$gap});
- max-width: 36em;
- margin-top: $gap;
- margin-left: $gap;
-
- @include from($navside-breakpoint) {
- width: calc(100% - #{2 * $gap});
- margin-top: 2 * $gap;
- margin-left: 2 * $gap;
- }
-}
diff --git a/web/app/src/styles/shared/components/modals.scss b/web/app/src/styles/shared/components/modals.scss
deleted file mode 100644
index faed1f1..0000000
--- a/web/app/src/styles/shared/components/modals.scss
+++ /dev/null
@@ -1,71 +0,0 @@
-@charset 'utf-8';
-
-div[data-svelte-dialog-portal] {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- top: 0;
- z-index: 40;
-}
-div[data-svelte-dialog-overlay] {
- background-color: transparent;
- width: 100vw;
- height: 100vh;
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- overflow: hidden;
- z-index: 41;
-}
-.modal {
- // Bulma's default for "position: fixed" causes the modal to freeze the application
- // on the Jetson Nano (aarch64, Ubuntu 18.04), for some reason
- position: absolute;
-}
-.modal-background {
- background-color: $modal-overlay-background;
-}
-.drawer-container {
- position: relative;
- background-color: $modal-dialog-background;
- padding: $gap;
-
- @include from($navside-breakpoint) {
- padding: 2 * $gap;
- }
-}
-.drawer-container.right-drawer {
- width: 100%;
- max-width: 24em;
- margin-left: auto;
- margin-right: 0;
- margin-top: 0;
- margin-bottom: 0;
- height: 100vh;
-}
-.drawer-container.scroller {
- scrollbar-color: $scrollbar-color $modal-dialog-background;
-}
-.drawer-container.scroller::-webkit-scrollbar-track {
- background: $modal-dialog-background;
-}
-.drawer-container.scroller::-webkit-scrollbar-thumb {
- border-color: $modal-dialog-background;
-}
-.drawer-container .modal-title {
- margin-bottom: $gap;
-
- @include from($navside-breakpoint) {
- margin-bottom: 2 * $gap;
- }
-}
-.modal-title h2 {
- display: inline;
-}
-.modal-title .delete {
- display: block;
- float: right;
-}
diff --git a/web/app/src/styles/shared/components/panels.scss b/web/app/src/styles/shared/components/panels.scss
deleted file mode 100644
index 248cc62..0000000
--- a/web/app/src/styles/shared/components/panels.scss
+++ /dev/null
@@ -1,60 +0,0 @@
-@charset 'utf-8';
-
-.panel.entity-panel {
- display: inline-block;
- vertical-align: top;
- width: calc(100% - #{$gap});
- max-width: 36em;
- margin-bottom: $gap;
- margin-right: $gap;
- border-radius: 0.5em;
- background-color: $card-background-color;
- box-shadow: none;
-
- @include from($navside-breakpoint) {
- width: calc(100% - #{2 * $gap});
- margin-bottom: 2 * $gap;
- margin-right: 2 * $gap;
- }
-
- .panel-heading {
- padding: $gap;
- margin-bottom: 0;
- border-radius: 0.5em 0.5em 0 0;
- background-color: $card-header-background-color;
- box-shadow: none;
-
- .entity-name {
- margin-bottom: 0.25em;
- display: inline-flex;
- }
- .entity-name:last-child {
- margin-bottom: 0;
- }
- .tag {
- font-weight: normal;
- }
- }
-
- .panel-block {
- width: 100%;
- padding-left: $gap;
- padding-right: $gap;
- padding-top: 0.5 * $gap;
- padding-bottom: $gap;
-
- h4, h5 {
- margin-bottom: 0;
- }
- h4:not(:first-child), h5:not(:first-child) {
- margin-top: 0.5em;
- }
- p:not(:last-child) {
- margin-bottom: 0;
- }
- }
-
- .panel-block:not(:last-child) {
- border-bottom: 1px solid $card-item-border-color;
- }
-}
diff --git a/web/app/src/styles/shared/components/progress.scss b/web/app/src/styles/shared/components/progress.scss
deleted file mode 100644
index e98d502..0000000
--- a/web/app/src/styles/shared/components/progress.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-@charset 'utf-8';
-
-.progress.is-body-background {
- background: $body-background-color;
-}
-
-.progress.is-body-background::-webkit-progress-bar {
- background: $body-background-color;
-}
-
-form .control input[type="submit"] + progress {
- margin-top: 0.5em;
-}
diff --git a/web/app/src/styles/shared/components/scroller.scss b/web/app/src/styles/shared/components/scroller.scss
deleted file mode 100644
index f709c91..0000000
--- a/web/app/src/styles/shared/components/scroller.scss
+++ /dev/null
@@ -1,31 +0,0 @@
-@charset 'utf-8';
-
-.scroller {
- overflow: auto;
- scrollbar-width: auto;
- scrollbar-color: $scrollbar-color $background;
-}
-.scroller::-webkit-scrollbar {
- -webkit-appearance: none; // don't hide scrollbar without hover
- width: 16px;
-}
-.scroller::-webkit-scrollbar-track {
- background: $background;
-}
-.scroller::-webkit-scrollbar-thumb {
- background-color: $scrollbar-color ;
- border-radius: 8px;
- border: 4px solid $background;
-}
-.scroller::-webkit-scrollbar-thumb:horizontal:hover {
- background-color: $scrollbar-hover-color;
-}
-.scroller::-webkit-scrollbar-thumb:horizontal:active {
- background-color: $scrollbar-hover-color;
-}
-.scroller::-webkit-scrollbar-thumb:vertical:hover {
- background-color: $scrollbar-hover-color;
-}
-.scroller::-webkit-scrollbar-thumb:vertical:active {
- background-color: $scrollbar-hover-color;
-}
diff --git a/web/app/src/styles/shared/components/sections.scss b/web/app/src/styles/shared/components/sections.scss
deleted file mode 100644
index 87da8bc..0000000
--- a/web/app/src/styles/shared/components/sections.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-@charset 'utf-8';
-
-.section-description {
- margin-bottom: $gap;
-}
-.empty-section {
- padding-top: 0;
- padding-bottom: 0;
- transition: padding-top 0.2s ease-out 0.8s;
- transition: padding-bottom 0.2s ease-out 0.8s;
-}
diff --git a/web/app/src/styles/shared/components/toolbars.scss b/web/app/src/styles/shared/components/toolbars.scss
deleted file mode 100644
index e2555a5..0000000
--- a/web/app/src/styles/shared/components/toolbars.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-@charset 'utf-8';
-
-.toolbar {
- width: 100%;
- padding-left: $gap;
- padding-right: $gap;
- padding-top: $gap;
- padding-bottom: $gap;
- margin-bottom: $gap;
- background-color: $toolbar-background-color;
-
- @include from($navside-breakpoint) {
- padding-left: 2 * $gap;
- padding-right: 2 * $gap;
- margin-bottom: 2 * $gap;
- }
-}
-
-.toolbar .button {
- font-weight: bold;
- margin-bottom: $gap;
-}
diff --git a/web/app/src/styles/shared/components/typed-tags.scss b/web/app/src/styles/shared/components/typed-tags.scss
deleted file mode 100644
index 2d7b3b2..0000000
--- a/web/app/src/styles/shared/components/typed-tags.scss
+++ /dev/null
@@ -1,97 +0,0 @@
-@charset 'utf-8';
-
-.tag.scoped-name {
- display: inline-block;
- background-color: transparent;
- padding: 0;
- border-radius: 1em;
- height: unset;
-}
-.tag.zerotier-address {
- font-family: $family-monospace;
- color: $zerotier-address-tag-color;
- background-color: $zerotier-address-tag-background-color;
- padding-left: 0.5em;
- padding-right: 0.5em;
- border-radius: 0.75em;
-}
-.zerotier-network-id {
- display: inline-block;
-}
-.tag.zerotier-network-host {
- font-family: $family-monospace;
- color: $zerotier-address-tag-color;
- background-color: $zerotier-address-tag-background-color;
- padding-left: 0.5em;
- padding-right: 0;
- margin-right: 0;
- border-radius: 0.75em 0 0 0.75em;
-}
-.tag.zerotier-network-number {
- font-family: $family-monospace;
- color: $zerotier-network-number-tag-color;
- background-color: $zerotier-network-number-tag-background-color;
- padding-left: 0;
- padding-right: 0.5em;
- margin-left: 0;
- border-radius: 0 0.75em 0.75em 0;
-}
-.tag.zerotier-unknown-network-named {
- display: inline-block;
- background-color: $zerotier-unknown-network-tag-color;
- padding-left: 0.25em;
- padding-right: 0.5em;
- padding-top: 0.25em;
- padding-bottom: 0.25em;
- border-radius: 1em;
- height: unset;
-}
-.tag.zerotier-unknown-network-named .tag.zerotier-network-name {
- display: inline;
- background-color: transparent;
- height: unset;
- white-space: normal;
-}
-.tag.socket {
- font-family: $family-monospace;
- color: $socket-tag-color;
- background-color: $socket-tag-background-color;
- padding-left: 0.5em;
- padding-right: 0.5em;
- border-radius: 0.75em;
-}
-.tag.ip-address {
- font-family: $family-monospace;
- color: $socket-tag-color;
- background-color: $socket-tag-background-color;
- padding-left: 0.5em;
- padding-right: 0.5em;
- border-radius: 0.75em;
-}
-.tag.ip-subnet {
- font-family: $family-monospace;
- color: $socket-tag-color;
- background-color: $socket-tag-background-color;
- padding-left: 0.5em;
- padding-right: 0.5em;
- border-radius: 0.75em;
-}
-.tag.domain-name {
- font-family: $family-monospace;
- color: $domain-name-tag-color;
- background-color: $domain-name-tag-background-color;
- padding-left: 0.5em;
- padding-right: 0.5em;
- border-radius: 0.75em;
-}
-.tag.mac-address {
- font-family: $family-monospace;
- padding-left: 0.5em;
- padding-right: 0.5em;
- border-radius: 0.75em;
-}
-
-.tag .delete {
- margin-bottom: 4px;
- margin-top: 4px;
-}
diff --git a/web/app/src/styles/shared/global.scss b/web/app/src/styles/shared/global.scss
deleted file mode 100644
index c969d44..0000000
--- a/web/app/src/styles/shared/global.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-html, body {
- position: relative;
- width: 100%;
- height: 100%;
-}
-
-body {
- scrollbar-width: none;
- scroll-behavior: smooth;
- margin: 0;
- box-sizing: border-box;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-body::-webkit-scrollbar {
- display: none;
-}
diff --git a/web/app/src/styles/shared/layout.scss b/web/app/src/styles/shared/layout.scss
deleted file mode 100644
index 4a33e61..0000000
--- a/web/app/src/styles/shared/layout.scss
+++ /dev/null
@@ -1,206 +0,0 @@
-@charset 'utf-8';
-
-// Custom Containers
-
-.main-window {
- height: 100%;
- overflow: hidden;
-}
-.main-container {
- width: 100%;
- height: 100%;
- overflow: auto;
-}
-.pad-gap {
- padding: $gap;
-
- @include from($navside-breakpoint) {
- padding: 2 * $gap;
- }
-}
-
-// Flexbox Responsive Helpers
-
-.is-flex-direction-row-mobile {
- @include mobile {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-tablet-only {
- @include tablet-only {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-desktop-only {
- @include desktop-only {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-widescreen-only {
- @include widescreen-only {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-touch {
- @include touch {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-tablet {
- @include tablet {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-desktop {
- @include desktop {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-widescreen {
- @include widescreen {
- flex-direction: row;
- }
-}
-.is-flex-direction-row-fullhd {
- @include fullhd {
- flex-direction: row;
- }
-}
-
-.is-flex-direction-row-reverse-mobile {
- @include mobile {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-tablet-only {
- @include tablet-only {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-desktop-only {
- @include desktop-only {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-widescreen-only {
- @include widescreen-only {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-touch {
- @include touch {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-tablet {
- @include tablet {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-desktop {
- @include desktop {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-widescreen {
- @include widescreen {
- flex-direction: row-reverse;
- }
-}
-.is-flex-direction-row-reverse-fullhd {
- @include fullhd {
- flex-direction: row-reverse;
- }
-}
-
-.is-flex-direction-column-mobile {
- @include mobile {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-tablet-only {
- @include tablet-only {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-desktop-only {
- @include desktop-only {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-widescreen-only {
- @include widescreen-only {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-touch {
- @include touch {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-tablet {
- @include tablet {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-desktop {
- @include desktop {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-widescreen {
- @include widescreen {
- flex-direction: column;
- }
-}
-.is-flex-direction-column-fullhd {
- @include fullhd {
- flex-direction: column;
- }
-}
-
-.is-flex-direction-column-reverse-mobile {
- @include mobile {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-tablet-only {
- @include tablet-only {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-desktop-only {
- @include desktop-only {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-widescreen-only {
- @include widescreen-only {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-touch {
- @include touch {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-tablet {
- @include tablet {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-desktop {
- @include desktop {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-widescreen {
- @include widescreen {
- flex-direction: column-reverse;
- }
-}
-.is-flex-direction-column-reverse-fullhd {
- @include fullhd {
- flex-direction: column-reverse;
- }
-}
diff --git a/web/app/src/styles/shared/theme-overrides/breadcrumbs.scss b/web/app/src/styles/shared/theme-overrides/breadcrumbs.scss
deleted file mode 100644
index 265cb69..0000000
--- a/web/app/src/styles/shared/theme-overrides/breadcrumbs.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-@charset 'utf-8';
-
-.breadcrumb.main-breadcrumb {
- padding-left: $gap;
- padding-top: $gap;
- margin-bottom: 0;
-
- @include from($navside-breakpoint) {
- padding-left: 2 * $gap;
- padding-top: 2 * $gap;
- }
-}
diff --git a/web/app/src/styles/shared/theme-overrides/buttons.scss b/web/app/src/styles/shared/theme-overrides/buttons.scss
deleted file mode 100644
index dbfabcf..0000000
--- a/web/app/src/styles/shared/theme-overrides/buttons.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-@charset 'utf-8';
-
-:focus, button.button:focus {
- outline: 4px solid $focus-color;
-}
-
-button.is-ghost.is-ahref {
- padding: 0;
- border: none;
- border-radius: 0;
- height: unset;
-}
-// For some reason, the Bulma tag color classes ignore $success, $danger, etc.
-.button.is-primary {
- background-color: $primary;
-}
diff --git a/web/app/src/styles/shared/theme-overrides/forms.scss b/web/app/src/styles/shared/theme-overrides/forms.scss
deleted file mode 100644
index a3eb904..0000000
--- a/web/app/src/styles/shared/theme-overrides/forms.scss
+++ /dev/null
@@ -1,37 +0,0 @@
-@charset 'utf-8';
-
-.field .message {
- margin-top: $gap;
-}
-.field .message .message-body {
- padding: $gap;
-}
-// For some reason, the Bulma tag color classes ignore $success, $danger, etc.
-.message.is-danger {
- background-color: $danger-light;
-}
-
-input[type="submit"].is-href {
- height: unset;
- justify-content: unset;
- vertical-align: unset;
- background-color: unset;
- border: unset;
- border-radius: unset;
- text-decoration: none;
- text-rendering: optimizelegibility;
-
- @include from($navside-breakpoint) {
- padding-top: 0.25 * $gap;
- width: calc(100% - 2 * 0.75em);
- }
-
- &:hover, &:focus {
- text-decoration: none;
- color: $navbar-button-active-color;
- background-color: $navbar-button-active-background;
- }
- &:focus {
- outline: 4px solid $focus-color;
- }
-}
diff --git a/web/app/src/styles/shared/theme-overrides/navbar.scss b/web/app/src/styles/shared/theme-overrides/navbar.scss
deleted file mode 100644
index 40028f3..0000000
--- a/web/app/src/styles/shared/theme-overrides/navbar.scss
+++ /dev/null
@@ -1,234 +0,0 @@
-@charset 'utf-8';
-
-.navbar, .navbar-menu, .navbar-start, .navbar-end {
- @include from($navside-breakpoint) {
- flex-direction: column;
- }
-}
-.navbar {
- min-height: 0.5 * $navbar-height;
- height: 10vh;
- max-height: $navbar-height;
- @include from($navside-breakpoint) {
- height: 100%;
- max-height: 100%;
- flex-grow: 0;
- flex-shrink: 0;
- flex-basis: fit-content;
- }
-}
-.navbar-brand {
- @include until($navside-breakpoint) {
- min-height: 0.5 * $navbar-height;
- height: 100%;
- max-height: $navbar-height;
- }
- @include from($navside-breakpoint) {
- margin-top: 2 * $gap;
- min-height: 0;
- }
-
- a.navbar-item {
- margin-top: auto;
- margin-bottom: auto;
- margin-left: 0.25em;
- margin-right: 0em;
- padding-left: 0.25em;
- height: 100%;
- max-height: $navbar-height * 0.625;
- font-weight: bold;
- text-align: center;
-
- @include from($navbar-breakpoint) {
- margin-left: $navbar-margin;
- }
-
- @include from($navside-breakpoint) {
- display: block;
- margin-top: 0em;
- margin-bottom: $navbar-margin;
- margin-right: $navbar-margin;
- padding-left: 0.5em;
- max-height: unset;
- height: unset;
- width: calc(100% - 2 * 0.75em);
- }
- }
- a.navbar-item.brand-with-logo {
- @include from($navside-breakpoint) {
- margin-top: -0.75em;
- }
- }
- a.navbar-item.is-active:not(:focus):not(:hover), a.navbar-item:hover, a.navbar-item:focus {
- color: $navbar-button-active-color;
- background-color: $navbar-button-active-background;
- }
-}
-img.navbar-brand-logo {
- padding-top: 0.125em;
- padding-bottom: 0.125em;
- padding-right: 0.25em;
-
- @include from($navside-breakpoint) {
- display: block;
- margin-left: auto;
- margin-right: auto;
- padding-left: 0em;
- padding-right: 0em;
- max-height: $navbar-height;
- }
-}
-.navbar-brand.left-burger {
- align-items: flex-start;
-
- .navbar-burger {
- margin-left: 0;
- margin-right: 0;
- }
-}
-.navbar-burger {
- min-width: 0.5 * $navbar-height;
- width: 10vh;
- max-width: $navbar-height;
- height: 100%;
- max-height: $navbar-height;
- &:hover, &:focus {
- color: $navbar-button-active-color;
- background-color: $navbar-button-active-background;
- }
- span {
- transition: unset;
- height: 0.5px;
- }
-}
-.navbar-menu {
- @include until($navbar-breakpoint) {
- display: block;
- visibility: hidden;
- max-height: 0;
- padding-top: 0;
- padding-bottom: 0;
- overflow: hidden;
- transition: all 0.2s ease-out;
- scrollbar-width: none;
- }
-}
-.navbar-menu::-webkit-scrollbar {
- display: none;
-}
-.navbar-menu.is-active, .navbar-menu:target, .navbar-menu[aria-expanded="true"] {
- @include until($navbar-breakpoint) {
- display: block;
- max-height: 90vh;
- visibility: visible;
- padding-top: 0.25 * $gap;
- padding-bottom: $gap;
- overflow: auto;
- }
-
- .navbar-burger {
- min-width: unset;
- width: 2.5em;
- max-width: unset;
- height: 2.5em;
- max-height: unset;
- }
-}
-.navbar-start, .navbar-end {
- @include from($navside-breakpoint) {
- width: 100%;
- }
-}
-.navbar-item {
- margin: $navbar-margin;
- padding: 0.5em;
-
- @include from($navbar-breakpoint) {
- margin-top: auto;
- margin-bottom: auto;
- margin-right: 0em;
- height: 100%;
- max-height: $navbar-height * 0.625;
- }
- @include from($navside-breakpoint) {
- margin-top: 0em;
- margin-bottom: $navbar-margin;
- margin-right: $navbar-margin;
- height: unset;
- }
- .icon img {
- height: 1em;
- }
-}
-.navbar-item.end-buttons{
- padding-top: 0;
- padding-bottom: 0;
-
- @include until($navside-breakpoint) {
- margin-top: auto;
- margin-bottom: auto;
- margin-left: 0;
- padding-left: 0.75em;
- }
- @include from($navside-breakpoint) {
- margin: $navbar-margin;
- height: unset;
- }
-
- .buttons {
- margin-left: auto;
- margin-right: auto;
- @include until($navside-breakpoint) {
- margin-top: auto;
- margin-bottom: auto;
- height: 100%;
- }
- @include from($navside-breakpoint) {
- margin-top: $navbar-margin;
- margin-bottom: $navbar-margin;
- }
- }
- .button {
- border-radius: 0;
- margin-top: auto;
- margin-bottom: auto;
- @include until($navbar-breakpoint) {
- padding-left: calc(0.5em - 1px);
- padding-right: calc(0.5em - 1px);
- }
- @include from($navbar-breakpoint) {
- height: 100%;
- max-height: $navbar-height * 0.625;
- }
- }
- .icon-button {
- @include from($navbar-breakpoint) {
- padding-top: 0.25 * $gap;
- padding-bottom: 0.25 * $gap;
- padding-left: 0.125 * $gap;
- padding-right: 0.125 * $gap;
- }
- .icon:first-child:last-child {
- margin-left: auto;
- margin-right: auto;
- }
- }
- .button.is-active:not(:hover), .button:hover, .button:focus {
- color: $navbar-button-active-color;
- background-color: $navbar-button-active-background;
- }
- .button:focus {
- outline: 4px solid $focus-color;
- }
-}
-a.navbar-item.is-active:not(:focus):not(:hover), a.navbar-item:hover, a.navbar-item:focus {
- color: $navbar-button-active-color;
- background-color: $navbar-button-active-background;
-}
-.navbar-end {
- @include from($navside-breakpoint) {
- margin-top: auto;
- margin-right: auto;
- margin-bottom: $navbar-margin;
- }
-}
diff --git a/web/app/src/styles/shared/theme-overrides/tags.scss b/web/app/src/styles/shared/theme-overrides/tags.scss
deleted file mode 100644
index c82f94f..0000000
--- a/web/app/src/styles/shared/theme-overrides/tags.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-@charset 'utf-8';
-
-.tag:not(body) {
- padding-left: 0.25em;
- padding-right: 0.25em;
- font-size: $size-normal;
- height: 1.4 * $size-normal;
-}
-// For some reason, the Bulma tag color classes ignore $success, $danger, etc.
-.tag:not(body).is-success {
- background-color: $success;
-}
-.tag:not(body).is-danger {
- background-color: $danger;
-}
diff --git a/web/app/src/styles/shared/theme.vars.scss b/web/app/src/styles/shared/theme.vars.scss
deleted file mode 100644
index 77e84c0..0000000
--- a/web/app/src/styles/shared/theme.vars.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-@charset 'utf-8';
-
-// Basic Colors
-
-$primary: $turquoise;
-$success: $green;
-$danger: $red;
-$border: $scheme-main-grey-bis;
-$border-hover: $scheme-main-grey-ter;
-$border-light: $scheme-main-grey;
-$border-light-hover: $scheme-main-grey-ter;
-$text: $scheme-invert-grey;
-$text-light: $scheme-invert-grey-bis;
-$text-strong: $scheme-invert-grey;
-$link-hover: $scheme-invert-ter;
-$link-hover-border: $scheme-main-grey-ter;
-$link-focus: $scheme-invert-ter;
-$link-active: $scheme-main-grey;
-$link-active-border: $scheme-main-grey-bis;
-
-// Component Customizations
-
-$card-shadow: none;
-$modal-overlay-background: rgba(10, 19, 31, 0.75);
diff --git a/web/app/src/styles/shared/typography.vars.scss b/web/app/src/styles/shared/typography.vars.scss
deleted file mode 100644
index bbb734d..0000000
--- a/web/app/src/styles/shared/typography.vars.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-@charset 'utf-8';
-
-$family-sans-serif: 'Atkinson Hyperlegible', BlinkMacSystemFont, -apple-system, 'Oxygen', 'Segoe UI', 'Ubuntu', 'Fira Sans', sans-serif;
-$family-monospace: 'Oxygen Mono', 'Ubuntu Mono', 'Fira Code', 'Fira Mono', monospace;
diff --git a/web/app/src/styles/theme-dark.scss b/web/app/src/styles/theme-dark.scss
index 369bdf2..a4e5f2c 100644
--- a/web/app/src/styles/theme-dark.scss
+++ b/web/app/src/styles/theme-dark.scss
@@ -1,8 +1,8 @@
@charset 'utf-8';
// Shared base
-@import 'styles/shared/global.scss';
-@import 'styles/shared/typography.vars.scss';
+@import 'node_modules/@sargassum-world/styles/global.scss';
+@import 'node_modules/@sargassum-world/styles/typography.vars.scss';
@import 'node_modules/bulma/sass/utilities/mixins.sass';
// Application-specific parameters
@@ -10,25 +10,26 @@
@import 'styles/app/layout.vars.scss';
// Shared
-@import 'styles/shared/layout.scss';
-@import 'styles/shared/colors-base.vars.scss';
+@import 'node_modules/@sargassum-world/styles/layout.scss';
+@import 'node_modules/@sargassum-world/styles/colors-base.vars.scss';
@import 'node_modules/bulma/sass/utilities/initial-variables.sass';
// Theme colors
-@import 'styles/shared/colors-dark.vars.scss';
+@import 'node_modules/@sargassum-world/styles/colors-dark.vars.scss';
// Shared
-@import 'styles/shared/theme.vars.scss';
+@import 'node_modules/@sargassum-world/styles/theme.vars.scss';
-// Application-specific Bulma imports
-@import 'styles/app/bulma.scss';
+// Bulma imports
+@import 'node_modules/bulma/bulma.sass';
// Anti-FOUC mechanism
@import 'styles/app/fouc-post.scss';
// Overrides & components
-@import 'styles/app/theme-overrides.scss';
-@import 'styles/app/components.scss';
+@import 'node_modules/@sargassum-world/styles/theme-overrides.scss';
+@import 'node_modules/@sargassum-world/styles/components.scss';
+@import 'styles/app/components/_all.scss';
@import 'styles/app/sprinkles.scss';
// Overrides only for dark mode
diff --git a/web/app/src/styles/theme-light.scss b/web/app/src/styles/theme-light.scss
index 42fdb72..22e7d7e 100644
--- a/web/app/src/styles/theme-light.scss
+++ b/web/app/src/styles/theme-light.scss
@@ -1,8 +1,8 @@
@charset 'utf-8';
// Shared base
-@import 'styles/shared/global.scss';
-@import 'styles/shared/typography.vars.scss';
+@import 'node_modules/@sargassum-world/styles/global.scss';
+@import 'node_modules/@sargassum-world/styles/typography.vars.scss';
@import 'node_modules/bulma/sass/utilities/mixins.sass';
// Application-specific parameters
@@ -10,23 +10,24 @@
@import 'styles/app/layout.vars.scss';
// Shared
-@import 'styles/shared/layout.scss';
-@import 'styles/shared/colors-base.vars.scss';
+@import 'node_modules/@sargassum-world/styles/layout.scss';
+@import 'node_modules/@sargassum-world/styles/colors-base.vars.scss';
@import 'node_modules/bulma/sass/utilities/initial-variables.sass';
// Theme colors
-@import 'styles/shared/colors-light.vars.scss';
+@import 'node_modules/@sargassum-world/styles/colors-light.vars.scss';
// Shared
-@import 'styles/shared/theme.vars.scss';
+@import 'node_modules/@sargassum-world/styles/theme.vars.scss';
-// Application-specific Bulma imports
-@import 'styles/app/bulma.scss';
+// Bulma imports
+@import 'node_modules/bulma/bulma.sass';
// Anti-FOUC mechanism
@import 'styles/app/fouc-post.scss';
// Overrides & components
-@import 'styles/app/theme-overrides.scss';
-@import 'styles/app/components.scss';
+@import 'node_modules/@sargassum-world/styles/theme-overrides.scss';
+@import 'node_modules/@sargassum-world/styles/components.scss';
+@import 'styles/app/components/_all.scss';
@import 'styles/app/sprinkles.scss';
diff --git a/web/app/src/theme-dark.ts b/web/app/src/theme-dark.js
similarity index 100%
rename from web/app/src/theme-dark.ts
rename to web/app/src/theme-dark.js
diff --git a/web/app/src/theme-eager.ts b/web/app/src/theme-eager.js
similarity index 100%
rename from web/app/src/theme-eager.ts
rename to web/app/src/theme-eager.js
diff --git a/web/app/src/theme-light.ts b/web/app/src/theme-light.js
similarity index 100%
rename from web/app/src/theme-light.ts
rename to web/app/src/theme-light.js
diff --git a/web/app/tsconfig.json b/web/app/tsconfig.json
deleted file mode 100644
index 6a2c4ea..0000000
--- a/web/app/tsconfig.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "extends": "@tsconfig/svelte/tsconfig.json",
- "compilerOptions": {
- "target": "es5",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
- "types": ["node", "svelte"],
- "allowJs": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noFallthroughCasesInSwitch": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "importsNotUsedAsValues": "remove",
- "outDir": "./public/build"
- },
- "include": ["src/**/*"],
- "exclude": ["__sapper__/*", "public/*"]
-}
diff --git a/web/app/yarn.lock b/web/app/yarn.lock
index 40a777d..db2e3aa 100644
--- a/web/app/yarn.lock
+++ b/web/app/yarn.lock
@@ -2,13 +2,6 @@
# yarn lockfile v1
-"@babel/code-frame@7.12.11":
- version "7.12.11"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
- integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
- dependencies:
- "@babel/highlight" "^7.10.4"
-
"@babel/code-frame@^7.10.4":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
@@ -21,7 +14,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
-"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7":
+"@babel/highlight@^7.16.7":
version "7.16.10"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88"
integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==
@@ -30,18 +23,18 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@eslint/eslintrc@^0.4.3":
- version "0.4.3"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
- integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==
+"@eslint/eslintrc@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.0.tgz#7ce1547a5c46dfe56e1e45c3c9ed18038c721c6a"
+ integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==
dependencies:
ajv "^6.12.4"
- debug "^4.1.1"
- espree "^7.3.0"
+ debug "^4.3.2"
+ espree "^9.3.1"
globals "^13.9.0"
ignore "^4.0.6"
import-fresh "^3.2.1"
- js-yaml "^3.13.1"
+ js-yaml "^4.1.0"
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
@@ -60,16 +53,16 @@
resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.1.0.tgz#27e44e0e3dc5bd1d4bda0766d579cf5a14091cd7"
integrity sha512-Q8kGjqwPqER+CtpQudbH+3Zgs2X4zb6pBAlr6NsKTXadg45pAOvxI9i4QpuHbwSzR2+x87HUm+rot9F/Pe8rxA==
-"@humanwhocodes/config-array@^0.5.0":
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
- integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
+"@humanwhocodes/config-array@^0.9.2":
+ version "0.9.5"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
+ integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==
dependencies:
- "@humanwhocodes/object-schema" "^1.2.0"
+ "@humanwhocodes/object-schema" "^1.2.1"
debug "^4.1.1"
minimatch "^3.0.4"
-"@humanwhocodes/object-schema@^1.2.0":
+"@humanwhocodes/object-schema@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
@@ -120,14 +113,6 @@
is-module "^1.0.0"
resolve "^1.19.0"
-"@rollup/plugin-typescript@^8.0.0":
- version "8.3.1"
- resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.1.tgz#b7dc75ed6b4876e260b9e80624fab23bc98e4ac1"
- integrity sha512-84rExe3ICUBXzqNX48WZV2Jp3OddjTMX97O2Py6D1KJaGSwWp0mDHXj+bCGNJqWHIEKDIT2U0sDjhP4czKi6cA==
- dependencies:
- "@rollup/pluginutils" "^3.1.0"
- resolve "^1.17.0"
-
"@rollup/pluginutils@4":
version "4.1.2"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.2.tgz#ed5821c15e5e05e32816f5fb9ec607cdf5a75751"
@@ -145,6 +130,20 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
+"@sargassum-world/stimulated@^0.2.9":
+ version "0.2.9"
+ resolved "https://registry.yarnpkg.com/@sargassum-world/stimulated/-/stimulated-0.2.9.tgz#02922770aa62312bd49cf46e0c9203980f66d8e7"
+ integrity sha512-kJ22FwchwDnPd2VdYhObGUfgGAAxVTzZWFkmfG5Iq35KPxosJLDnt+MOCSn5JitnVHFPjZQ4G6ggGFPcIcXhIg==
+ dependencies:
+ "@hotwired/turbo" "^7.0.1"
+ async-mutex "^0.3.2"
+ stimulus "2.0.0"
+
+"@sargassum-world/styles@^0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@sargassum-world/styles/-/styles-0.2.0.tgz#1835b0cc6245f67f00bb0375076f8b18774da5a0"
+ integrity sha512-/zXKNTKATsj0/zmOoaL8zPFN/eyPmtrgdh2+249dVOJqxmObeWzAnKgH5Nre1D67dSfvBK6VqvX0uDy8mpaACA==
+
"@stimulus/core@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@stimulus/core/-/core-2.0.0.tgz#140c85318d6a8a8210c0faf182223b8459348877"
@@ -169,11 +168,6 @@
resolved "https://registry.yarnpkg.com/@stimulus/webpack-helpers/-/webpack-helpers-2.0.0.tgz#54296d2a2dffd4f962d2e802d99a3fdd84b8845f"
integrity sha512-D6tJWsAC024MwGEIKlUVYU8Ln87mlrmiwHvYAjipg+s8H4eLxUMQ3PZkWyPevfipH+oR3leuHsjYsK1gN5ViQA==
-"@tsconfig/svelte@^2.0.1":
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-2.0.1.tgz#0e8d7caa693e9b2afce5e622c0475bb0fd89c12c"
- integrity sha512-aqkICXbM1oX5FfgZd2qSSAGdyo/NRxjWCamxoyi3T8iVQnzGge19HhDYzZ6NrVOW7bhcWNSq9XexWFtMzbB24A==
-
"@types/estree@*":
version "0.0.51"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
@@ -199,11 +193,6 @@
"@types/minimatch" "*"
"@types/node" "*"
-"@types/json-schema@^7.0.7":
- version "7.0.9"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
- integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
-
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -238,87 +227,12 @@
dependencies:
"@types/node" "*"
-"@typescript-eslint/eslint-plugin@^4.29.3":
- version "4.33.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276"
- integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==
- dependencies:
- "@typescript-eslint/experimental-utils" "4.33.0"
- "@typescript-eslint/scope-manager" "4.33.0"
- debug "^4.3.1"
- functional-red-black-tree "^1.0.1"
- ignore "^5.1.8"
- regexpp "^3.1.0"
- semver "^7.3.5"
- tsutils "^3.21.0"
-
-"@typescript-eslint/experimental-utils@4.33.0":
- version "4.33.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd"
- integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==
- dependencies:
- "@types/json-schema" "^7.0.7"
- "@typescript-eslint/scope-manager" "4.33.0"
- "@typescript-eslint/types" "4.33.0"
- "@typescript-eslint/typescript-estree" "4.33.0"
- eslint-scope "^5.1.1"
- eslint-utils "^3.0.0"
-
-"@typescript-eslint/parser@^4.29.3":
- version "4.33.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899"
- integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==
- dependencies:
- "@typescript-eslint/scope-manager" "4.33.0"
- "@typescript-eslint/types" "4.33.0"
- "@typescript-eslint/typescript-estree" "4.33.0"
- debug "^4.3.1"
-
-"@typescript-eslint/scope-manager@4.33.0":
- version "4.33.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3"
- integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==
- dependencies:
- "@typescript-eslint/types" "4.33.0"
- "@typescript-eslint/visitor-keys" "4.33.0"
-
-"@typescript-eslint/types@4.33.0":
- version "4.33.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72"
- integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==
-
-"@typescript-eslint/typescript-estree@4.33.0":
- version "4.33.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609"
- integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==
- dependencies:
- "@typescript-eslint/types" "4.33.0"
- "@typescript-eslint/visitor-keys" "4.33.0"
- debug "^4.3.1"
- globby "^11.0.3"
- is-glob "^4.0.1"
- semver "^7.3.5"
- tsutils "^3.21.0"
-
-"@typescript-eslint/visitor-keys@4.33.0":
- version "4.33.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd"
- integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==
- dependencies:
- "@typescript-eslint/types" "4.33.0"
- eslint-visitor-keys "^2.0.0"
-
acorn-jsx@^5.3.1:
version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-acorn@^7.4.0:
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
- integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-
-acorn@^8.5.0:
+acorn@^8.5.0, acorn@^8.7.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
@@ -333,21 +247,6 @@ ajv@^6.10.0, ajv@^6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
-ajv@^8.0.1:
- version "8.10.0"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d"
- integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==
- dependencies:
- fast-deep-equal "^3.1.1"
- json-schema-traverse "^1.0.0"
- require-from-string "^2.0.2"
- uri-js "^4.2.2"
-
-ansi-colors@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
- integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
-
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -370,7 +269,7 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
@@ -385,12 +284,10 @@ anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
-argparse@^1.0.7:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
- integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
- dependencies:
- sprintf-js "~1.0.2"
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
array-includes@^3.1.4:
version "3.1.4"
@@ -417,11 +314,6 @@ array.prototype.flat@^1.2.5:
define-properties "^1.1.3"
es-abstract "^1.19.0"
-astral-regex@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
- integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
-
async-mutex@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df"
@@ -641,7 +533,7 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
-debug@^4.0.1, debug@^4.1.1, debug@^4.3.1:
+debug@^4.1.1, debug@^4.3.2:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
@@ -701,18 +593,6 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
-emoji-regex@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
- integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
-
-enquirer@^2.3.5:
- version "2.3.6"
- resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
- integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
- dependencies:
- ansi-colors "^4.1.1"
-
error-ex@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@@ -810,20 +690,13 @@ eslint-plugin-svelte3@^3.2.1:
resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.4.1.tgz#3618700333c8f8f12e28aec93bf18440d44a61fd"
integrity sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw==
-eslint-scope@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
- integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+eslint-scope@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+ integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
dependencies:
esrecurse "^4.3.0"
- estraverse "^4.1.1"
-
-eslint-utils@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
- integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
- dependencies:
- eslint-visitor-keys "^1.1.0"
+ estraverse "^5.2.0"
eslint-utils@^3.0.0:
version "3.0.0"
@@ -832,75 +705,65 @@ eslint-utils@^3.0.0:
dependencies:
eslint-visitor-keys "^2.0.0"
-eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
- integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
-
eslint-visitor-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
-eslint@^7.32.0:
- version "7.32.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
- integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
+eslint-visitor-keys@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+ integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@^8.10.0:
+ version "8.10.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d"
+ integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
dependencies:
- "@babel/code-frame" "7.12.11"
- "@eslint/eslintrc" "^0.4.3"
- "@humanwhocodes/config-array" "^0.5.0"
+ "@eslint/eslintrc" "^1.2.0"
+ "@humanwhocodes/config-array" "^0.9.2"
ajv "^6.10.0"
chalk "^4.0.0"
cross-spawn "^7.0.2"
- debug "^4.0.1"
+ debug "^4.3.2"
doctrine "^3.0.0"
- enquirer "^2.3.5"
escape-string-regexp "^4.0.0"
- eslint-scope "^5.1.1"
- eslint-utils "^2.1.0"
- eslint-visitor-keys "^2.0.0"
- espree "^7.3.1"
+ eslint-scope "^7.1.1"
+ eslint-utils "^3.0.0"
+ eslint-visitor-keys "^3.3.0"
+ espree "^9.3.1"
esquery "^1.4.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
file-entry-cache "^6.0.1"
functional-red-black-tree "^1.0.1"
- glob-parent "^5.1.2"
+ glob-parent "^6.0.1"
globals "^13.6.0"
- ignore "^4.0.6"
+ ignore "^5.2.0"
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
- js-yaml "^3.13.1"
+ js-yaml "^4.1.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
lodash.merge "^4.6.2"
minimatch "^3.0.4"
natural-compare "^1.4.0"
optionator "^0.9.1"
- progress "^2.0.0"
- regexpp "^3.1.0"
- semver "^7.2.1"
- strip-ansi "^6.0.0"
+ regexpp "^3.2.0"
+ strip-ansi "^6.0.1"
strip-json-comments "^3.1.0"
- table "^6.0.9"
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
-espree@^7.3.0, espree@^7.3.1:
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6"
- integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==
+espree@^9.3.1:
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd"
+ integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
dependencies:
- acorn "^7.4.0"
+ acorn "^8.7.0"
acorn-jsx "^5.3.1"
- eslint-visitor-keys "^1.3.0"
-
-esprima@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
- integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+ eslint-visitor-keys "^3.3.0"
esquery@^1.4.0:
version "1.4.0"
@@ -916,11 +779,6 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
-estraverse@^4.1.1:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
- integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-
estraverse@^5.1.0, estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
@@ -964,7 +822,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-fast-glob@^3.0.3, fast-glob@^3.2.7, fast-glob@^3.2.9:
+fast-glob@^3.0.3, fast-glob@^3.2.7:
version "3.2.11"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
@@ -1089,6 +947,13 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
dependencies:
is-glob "^4.0.1"
+glob-parent@^6.0.1:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
glob@^7.1.1, glob@^7.1.3, glob@^7.1.6:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
@@ -1122,18 +987,6 @@ globby@10.0.1:
merge2 "^1.2.3"
slash "^3.0.0"
-globby@^11.0.3:
- version "11.1.0"
- resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
- integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
- dependencies:
- array-union "^2.1.0"
- dir-glob "^3.0.1"
- fast-glob "^3.2.9"
- ignore "^5.2.0"
- merge2 "^1.4.1"
- slash "^3.0.0"
-
graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.9"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
@@ -1178,17 +1031,12 @@ hosted-git-info@^2.1.4:
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
-hotkeys-js@>=3:
- version "3.8.7"
- resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.8.7.tgz#c16cab978b53d7242f860ca3932e976b92399981"
- integrity sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg==
-
ignore@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-ignore@^5.1.1, ignore@^5.1.8, ignore@^5.2.0:
+ignore@^5.1.1, ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
@@ -1301,11 +1149,6 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
-is-fullwidth-code-point@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
- integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
-
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
@@ -1405,24 +1248,18 @@ js-tokens@^4.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-js-yaml@^3.13.1:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
- integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
+ argparse "^2.0.1"
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-json-schema-traverse@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
- integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
-
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@@ -1480,11 +1317,6 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
-lodash.truncate@^4.4.2:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
- integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
-
lru-cache@^4.0.1:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -1493,13 +1325,6 @@ lru-cache@^4.0.1:
pseudomap "^1.0.2"
yallist "^2.1.2"
-lru-cache@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
- integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
- dependencies:
- yallist "^4.0.0"
-
magic-string@^0.25.7:
version "0.25.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
@@ -1519,7 +1344,7 @@ merge-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1:
+merge2@^1.2.3, merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
@@ -1775,11 +1600,6 @@ prettier@^2.4.1:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
-progress@^2.0.0:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
- integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
-
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -1837,7 +1657,7 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
-regexpp@^3.1.0:
+regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
@@ -1847,11 +1667,6 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
-require-from-string@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
- integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
-
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
@@ -2008,13 +1823,6 @@ sass@^1.43.2:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
-semver@^7.2.1, semver@^7.3.5:
- version "7.3.5"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
- integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
- dependencies:
- lru-cache "^6.0.0"
-
serialize-javascript@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
@@ -2070,15 +1878,6 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
-slice-ansi@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
- integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
- dependencies:
- ansi-styles "^4.0.0"
- astral-regex "^2.0.0"
- is-fullwidth-code-point "^3.0.0"
-
sorcery@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.10.0.tgz#8ae90ad7d7cb05fc59f1ab0c637845d5c15a52b7"
@@ -2159,18 +1958,6 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95"
integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==
-sprintf-js@~1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
- integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-
-stimulus-use@^0.41.0:
- version "0.41.0"
- resolved "https://registry.yarnpkg.com/stimulus-use/-/stimulus-use-0.41.0.tgz#afe2559281ebe0504b1d35eb0e8e77df9ebd6fb6"
- integrity sha512-d+XWb5YN2L9I+4zQDtE1sJVy1DBO+1qgNcjV0XpMw7KXPJ8m8JA5lcJk3qgbxV86ePHko0CP9F3dzVsapQwzmA==
- dependencies:
- hotkeys-js ">=3"
-
stimulus@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/stimulus/-/stimulus-2.0.0.tgz#713c8b91a72ef90914b90955f0e705f004403047"
@@ -2196,15 +1983,6 @@ string-width@^2.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
-string-width@^4.2.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
string.prototype.trimend@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
@@ -2235,7 +2013,7 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -2320,17 +2098,6 @@ svelte@^3.0.0:
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.46.4.tgz#0c46bc4a3e20a2617a1b7dc43a722f9d6c084a38"
integrity sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==
-table@^6.0.9:
- version "6.8.0"
- resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca"
- integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==
- dependencies:
- ajv "^8.0.1"
- lodash.truncate "^4.4.2"
- slice-ansi "^4.0.0"
- string-width "^4.2.3"
- strip-ansi "^6.0.1"
-
terser@^5.0.0:
version "5.12.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.12.0.tgz#728c6bff05f7d1dcb687d8eace0644802a9dae8a"
@@ -2363,23 +2130,11 @@ tsconfig-paths@^3.12.0:
minimist "^1.2.0"
strip-bom "^3.0.0"
-tslib@^1.8.1:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
- integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-
-tslib@^2.0.0, tslib@^2.3.1:
+tslib@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
-tsutils@^3.21.0:
- version "3.21.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
- integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
- dependencies:
- tslib "^1.8.1"
-
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -2392,7 +2147,7 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
-typescript@*, typescript@^4.0.0:
+typescript@*:
version "4.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4"
integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==
@@ -2500,11 +2255,6 @@ yallist@^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
-yallist@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
- integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-
yargs-parser@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
diff --git a/web/templates/app/app.webmanifest.tmpl b/web/templates/app/app.webmanifest.tmpl
index 99b6ac7..08b951c 100644
--- a/web/templates/app/app.webmanifest.tmpl
+++ b/web/templates/app/app.webmanifest.tmpl
@@ -1,6 +1,6 @@
{
"name": "Fluitans",
- "decsription": "An application for managing Sargassum networks, domain names, and organizations.",
+ "description": "An application for managing Sargassum networks, domain names, and organizations.",
"categories": ["utilities"],
"short_name": "Fluitans",
"lang": "en-US",
diff --git a/web/templates/auth/login.page.tmpl b/web/templates/auth/login.page.tmpl
index 292f779..0c073eb 100644
--- a/web/templates/auth/login.page.tmpl
+++ b/web/templates/auth/login.page.tmpl
@@ -12,7 +12,7 @@
Sign In
{{end}}
{{range $message := .Data.ErrorMessages}}
-
+
@@ -22,7 +22,7 @@
{{end}}
{{if .Data.NoAuth}}
-
+
@@ -33,7 +33,7 @@
{{end}}
{{if .Auth.Identity.Authenticated}}
-
+
You are currently signed in as {{.Auth.Identity.User}}.