Compare commits

..

No commits in common. "feature-2fa" and "main" have entirely different histories.

86 changed files with 1182 additions and 2118 deletions

7
.gitignore vendored
View file

@ -21,9 +21,6 @@
# Go workspace file
go.work
bin/
__debug_*
.idea/
fly.toml
settings.env
config.toml
fly.toml

8
.idea/.gitignore vendored
View file

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="postgresql.lauka-home.net" uuid="7bf0afc1-fd52-49c5-8d3d-0ab2af6440d7">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://postgresql.lauka-home.net/go-test</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,10 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="false" />
<option name="processLiterals" value="false" />
<option name="processComments" value="false" />
</inspection_tool>
</profile>
</component>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JsonSchemaMappingsProjectConfiguration">
<state>
<map>
<entry key="Woodpecker pipeline config">
<value>
<SchemaInfo>
<option name="name" value="Woodpecker pipeline config" />
<option name="relativePathToSchema" value="https://raw.githubusercontent.com/woodpecker-ci/woodpecker/master/pipeline/schema/schema.json" />
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="path" value=".woodpecker/deploy-docker.yml" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
</map>
</state>
</component>
</project>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/go-urlsh.iml" filepath="$PROJECT_DIR$/.idea/go-urlsh.iml" />
</modules>
</component>
</project>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/internal/api/links.go" dialect="GenericSQL" />
<file url="PROJECT" dialect="PostgreSQL" />
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

18
.vscode/launch.json vendored
View file

@ -1,18 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "debug go-urlsh",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"envFile": "${workspaceFolder}/settings.env"
}
]
}

View file

@ -7,9 +7,9 @@ steps:
settings:
dockerfile: Dockerfile
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
repo: code.lila.network/adoralaura/go-urlsh
repo: codeberg.org/lauralani/go-urlsh
registry: codeberg.org
tags: develop
tags: latest
username: lauralani
password:
from_secret: codeberg_token
@ -22,9 +22,9 @@ steps:
settings:
dockerfile: Dockerfile
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
repo: code.lila.network/adoralaura/go-urlsh
repo: codeberg.org/lauralani/go-urlsh
registry: codeberg.org
auto_tag: true
username: lauralani
password:
from_secret: codeberg_token
from_secret: codeberg_token

View file

@ -2,7 +2,7 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
@ -11,27 +11,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- added Changelog
- Ability to change the url and description of shortlinks
- /api/v1/users/me endpoint to get user details
- User account page
- Two Factor Authentication (#6)
- Account page for user overview and later use
### Changed
- update to Go 1.22
- License changed to MIT
- Main development repo is now at https://code.lila.network/adoralaura/go-urlsh
### Removed
- Docker image support for linux/ppc64le
- Remember login switch (login cookie lasts 30 days now by default)
### Fixed
- Login sessions now actually get cleaned up
## [0.1.2] - 2023-08-09
## [0.1.2] - 2014-08-09
### Fixed
- fixed wrong javascript file linked in link add page
## [0.1.1] - 2023-07-10
## [0.1.1] - 2014-07-10
### Changed
- Consolidated frontend javascript functions into one file
@ -44,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ability to add and delete short links
[unreleased]: https://code.lila.network/adoralaura/go-urlsh/compare/0.1.2...HEAD
[0.1.2]: https://code.lila.network/adoralaura/go-urlsh/compare/0.1.1...0.1.2
[0.1.1]: https://code.lila.network/adoralaura/go-urlsh/compare/0.1.0...0.1.1
[0.1.0]: https://code.lila.network/adoralaura/go-urlsh/releases/tag/0.1.0
[unreleased]: https://codeberg.org/lauralani/go-urlsh/compare/0.1.2...HEAD
[0.1.2]: https://codeberg.org/lauralani/go-urlsh/compare/0.1.1...0.1.2
[0.1.1]: https://codeberg.org/lauralani/go-urlsh/compare/0.1.0...0.1.1
[0.1.0]: https://codeberg.org/lauralani/go-urlsh/releases/tag/0.1.0

View file

@ -1,4 +1,4 @@
FROM golang:1.22-alpine AS dev
FROM golang:1.20-alpine AS dev
COPY . /var/app
WORKDIR /var/app
@ -27,4 +27,4 @@ COPY --from=build /var/app/views /var/app/views
EXPOSE 80
ENTRYPOINT ["/var/app/go-urlsh"]
CMD ""
CMD ""

View file

@ -6,7 +6,7 @@
URL Shortener in Go
## Changelog
You can find the Changelog here: [Changelog](https://code.lila.network/adoralaura/go-urlsh/src/branch/main/CHANGELOG.md)
You can find the Changelog here: [Changelog](https://codeberg.org/lauralani/go-urlsh/src/branch/main/CHANGELOG.md)
## License
`go-urlsh` is available under the MIT license. See the LICENSE file for more info.

View file

@ -1,30 +1,23 @@
package app
import (
"flag"
"codeberg.org/lauralani/go-urlsh/internal/app"
"codeberg.org/lauralani/go-urlsh/internal/db"
"fmt"
"code.lila.network/adoralaura/go-urlsh/internal/app"
"code.lila.network/adoralaura/go-urlsh/internal/config"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"github.com/rs/zerolog"
"log"
)
func Run() error {
configPath := flag.String("config", "", "location of the config file to use (default: /etc/go-urlsh/config.toml or $pwd/config.toml)")
flag.Parse()
err := db.InitializeDB()
if err != nil {
log.Fatalln(err)
}
cfg := config.NewConfig(*configPath)
go app.CleanupLogins()
go app.CleanupLoginsCronJob()
logger := config.NewLogger(zerolog.Level(cfg.LogLevel))
database := db.InitializeDB(cfg, logger)
go app.CleanupLogins(database, logger)
go app.CleanupLoginsCronJob(database, logger)
err := app.SetupFiber(database, logger, cfg)
err = app.SetupFiber()
if err != nil {
return fmt.Errorf("couldn't start webserver: %v", err.Error())
}

BIN
go-urlsh

Binary file not shown.

64
go.mod
View file

@ -1,56 +1,38 @@
module code.lila.network/adoralaura/go-urlsh
module codeberg.org/lauralani/go-urlsh
go 1.22
go 1.20
require (
github.com/gofiber/fiber/v2 v2.52.4
github.com/gofiber/template/html/v2 v2.1.1
github.com/gofiber/fiber/v2 v2.46.0
github.com/gofiber/template v1.8.2
github.com/google/uuid v1.3.0
github.com/jasonlvhit/gocron v0.0.1
github.com/pquerna/otp v1.4.0
github.com/uptrace/bun v1.1.8
github.com/uptrace/bun/dialect/pgdialect v1.1.8
github.com/uptrace/bun/driver/pgdriver v1.1.8
github.com/uptrace/bun v1.1.14
github.com/uptrace/bun/dialect/pgdialect v1.1.14
github.com/uptrace/bun/driver/pgdriver v1.1.14
)
require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/klauspost/compress v1.16.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/zerolog v1.32.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.52.0 // indirect
github.com/valyala/fasthttp v1.47.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/tools v0.4.0 // indirect
mellium.im/sasl v0.3.1 // indirect
)

938
go.sum
View file

@ -1,149 +1,899 @@
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
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.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=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
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.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
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.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
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=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
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/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=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
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=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/cbroglie/mustache v1.4.0/go.mod h1:SS1FTIghy0sjse4DUVGV1k/40B1qE1XkD9DtDsHo9iM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/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/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-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/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/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8=
github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/gofiber/fiber/v2 v2.44.0 h1:Z90bEvPcJM5GFJnu1py0E1ojoerkyew3iiNJ78MQCM8=
github.com/gofiber/fiber/v2 v2.44.0/go.mod h1:VTMtb/au8g01iqvHyaCzftuM/xmZgKOZCtFzz6CdV9w=
github.com/gofiber/fiber/v2 v2.46.0 h1:wkkWotblsGVlLjXj2dpgKQAYHtXumsK/HyFugQM68Ns=
github.com/gofiber/fiber/v2 v2.46.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc=
github.com/gofiber/template v1.8.1 h1:KLnNtXqH3LTzquU0NsLMqX3YGd3pD562UhSNaIca5HI=
github.com/gofiber/template v1.8.1/go.mod h1:+2x8bRo2TAXnqp0RUN2MdyKshUi+BulPoUCOHstFLqE=
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
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.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
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=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/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/uuid v1.1.2/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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
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-immutable-radix v1.3.1/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.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
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/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=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jasonlvhit/gocron v0.0.1 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU=
github.com/jasonlvhit/gocron v0.0.1/go.mod h1:k9a3TV8VcU73XZxfVHCHWMWF9SOqgoku0/QlY2yvlA4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
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/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk=
github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
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/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/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/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
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/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-slim v0.0.4/go.mod h1:kXIwPrQbAZrhungtweoDO6qQQpUem6XkBg/mXykpoO0=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
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=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/uptrace/bun v1.1.8 h1:slxuaP4LYWFbPRUmTtQhfJN+6eX/6ar2HDKYTcI50SA=
github.com/uptrace/bun v1.1.8/go.mod h1:iT89ESdV3uMupD9ixt6Khidht+BK0STabK/LeZE+B84=
github.com/uptrace/bun v1.2.1 h1:2ENAcfeCfaY5+2e7z5pXrzFKy3vS8VXvkCag6N2Yzfk=
github.com/uptrace/bun v1.2.1/go.mod h1:cNg+pWBUMmJ8rHnETgf65CEvn3aIKErrwOD6IA8e+Ec=
github.com/uptrace/bun/dialect/pgdialect v1.1.8 h1:wayJhjYDPGv8tgOBLolbBtSFQ0TihFoo8E1T129UdA8=
github.com/uptrace/bun/dialect/pgdialect v1.1.8/go.mod h1:nNbU8PHTjTUM+CRtGmqyBb9zcuRAB8I680/qoFSmBUk=
github.com/uptrace/bun/dialect/pgdialect v1.2.1 h1:ceP99r03u+s8ylaDE/RzgcajwGiC76Jz3nS2ZgyPQ4M=
github.com/uptrace/bun/dialect/pgdialect v1.2.1/go.mod h1:mv6B12cisvSc6bwKm9q9wcrr26awkZK8QXM+nso9n2U=
github.com/uptrace/bun/driver/pgdriver v1.1.8 h1:gyL22axRQfjJS2Umq0erzJnp0bLOdUE8/USKZHPQB8o=
github.com/uptrace/bun/driver/pgdriver v1.1.8/go.mod h1:4tHK0h7a/UoldBoe9J3GU4tEYjr3mkd62U3Kq3PVk3E=
github.com/uptrace/bun/driver/pgdriver v1.2.1 h1:Cp6c1tKzbTIyL8o0cGT6cOhTsmQZdsUNhgcV51dsmLU=
github.com/uptrace/bun/driver/pgdriver v1.2.1/go.mod h1:jEd3WGx74hWLat3/IkesOoWNjrFNUDADK3nkyOFOOJM=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/uptrace/bun v1.1.12 h1:sOjDVHxNTuM6dNGaba0wUuz7KvDE1BmNu9Gqs2gJSXQ=
github.com/uptrace/bun v1.1.12/go.mod h1:NPG6JGULBeQ9IU6yHp7YGELRa5Agmd7ATZdz4tGZ6z0=
github.com/uptrace/bun v1.1.14 h1:S5vvNnjEynJ0CvnrBOD7MIRW7q/WbtvFXrdfy0lddAM=
github.com/uptrace/bun v1.1.14/go.mod h1:RHk6DrIisO62dv10pUOJCz5MphXThuOTpVNYEYv7NI8=
github.com/uptrace/bun/dialect/pgdialect v1.1.12 h1:m/CM1UfOkoBTglGO5CUTKnIKKOApOYxkcP2qn0F9tJk=
github.com/uptrace/bun/dialect/pgdialect v1.1.12/go.mod h1:Ij6WIxQILxLlL2frUBxUBOZJtLElD2QQNDcu/PWDHTc=
github.com/uptrace/bun/dialect/pgdialect v1.1.14 h1:b7+V1KDJPQSFYgkG/6YLXCl2uvwEY3kf/GSM7hTHRDY=
github.com/uptrace/bun/dialect/pgdialect v1.1.14/go.mod h1:v6YiaXmnKQ2FlhRD2c0ZfKd+QXH09pYn4H8ojaavkKk=
github.com/uptrace/bun/driver/pgdriver v1.1.12 h1:3rRWB1GK0psTJrHwxzNfEij2MLibggiLdTqjTtfHc1w=
github.com/uptrace/bun/driver/pgdriver v1.1.12/go.mod h1:ssYUP+qwSEgeDDS1xm2XBip9el1y9Mi5mTAvLoiADLM=
github.com/uptrace/bun/driver/pgdriver v1.1.14 h1:V2Etm7mLGS3mhx8ddxZcUnwZLX02Jmq9JTlo0sNVDhA=
github.com/uptrace/bun/driver/pgdriver v1.1.14/go.mod h1:D4FjWV9arDYct6sjMJhFoyU71SpllZRHXFRRP2Kd0Kw=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA=
github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
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.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-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.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
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=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
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/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=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/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/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
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-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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-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-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/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=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/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-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.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=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.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=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
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.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.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.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.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
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-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=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-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-20210513213006-bf773b8c8384/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-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-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-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/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/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
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.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.42.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=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
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/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=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View file

@ -2,14 +2,14 @@ package api
import "github.com/gofiber/fiber/v2"
func (am APIRouteManager) HandleApiKeysGetAll(c *fiber.Ctx) error {
return fiber.NewError(501, "Not Implemented")
func HandleApiKeysGetAll(c *fiber.Ctx) error {
return nil
}
func (am APIRouteManager) HandleApiKeysPost(c *fiber.Ctx) error {
return fiber.NewError(501, "Not Implemented")
func HandleApiKeysPost(c *fiber.Ctx) error {
return nil
}
func (am APIRouteManager) HandleApiKeysDelete(c *fiber.Ctx) error {
return fiber.NewError(501, "Not Implemented")
func HandleApiKeysDelete(c *fiber.Ctx) error {
return nil
}

View file

@ -1,28 +1,26 @@
package api
import (
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
"database/sql"
"encoding/json"
"fmt"
"github.com/gofiber/fiber/v2"
"log"
"time"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
)
func (am APIRouteManager) HandleLinkGetAll(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
func HandleLinkGetAll(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
}
var links []models.Link
err := am.db.NewSelect().Model(&links).Scan(context.Background())
err := models.DB.NewSelect().Model(&links).Scan(context.Background())
if err != nil {
return fmt.Errorf("error querying links: %v", err.Error())
}
@ -37,12 +35,12 @@ func (am APIRouteManager) HandleLinkGetAll(c *fiber.Ctx) error {
return nil
}
func (am APIRouteManager) HandleLinkGet(c *fiber.Ctx) error {
func HandleLinkGet(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotImplemented, "501 Not Implemented")
}
func (am APIRouteManager) HandleLinkPost(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
func HandleLinkPost(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
}
@ -63,7 +61,7 @@ func (am APIRouteManager) HandleLinkPost(c *fiber.Ctx) error {
newlink.Created = now
newlink.Modified = now
_, err = am.db.NewInsert().Model(&newlink).Exec(context.Background())
_, err = models.DB.NewInsert().Model(&newlink).Exec(context.Background())
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error: "+err.Error())
}
@ -79,8 +77,8 @@ func (am APIRouteManager) HandleLinkPost(c *fiber.Ctx) error {
return nil
}
func (am APIRouteManager) HandleLinkPut(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
func HandleLinkPut(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
}
@ -92,7 +90,7 @@ func (am APIRouteManager) HandleLinkPut(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
}
err := am.db.NewSelect().Model(&dblink).Where("id = ?", id).Scan(context.Background())
err := models.DB.NewSelect().Model(&dblink).Where("id = ?", id).Scan(context.Background())
if err != nil {
if err == sql.ErrNoRows {
@ -113,7 +111,7 @@ func (am APIRouteManager) HandleLinkPut(c *fiber.Ctx) error {
dblink.URL = editlink.URL
dblink.Modified = time.Now()
_, err = am.db.NewUpdate().Model(&dblink).WherePK().Exec(context.Background())
_, err = models.DB.NewUpdate().Model(&dblink).WherePK().Exec(context.Background())
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error: "+err.Error())
}
@ -124,14 +122,14 @@ func (am APIRouteManager) HandleLinkPut(c *fiber.Ctx) error {
return nil
}
func (am APIRouteManager) HandleLinkDelete(c *fiber.Ctx) error {
func HandleLinkDelete(c *fiber.Ctx) error {
id := c.Params("id")
if id == "" {
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
}
numrows, err := am.db.NewSelect().Model((*models.Link)(nil)).Where("id = ?", id).Count(context.Background())
numrows, err := models.DB.NewSelect().Model((*models.Link)(nil)).Where("id = ?", id).Count(context.Background())
if err != nil {
log.Println(err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
@ -141,7 +139,7 @@ func (am APIRouteManager) HandleLinkDelete(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotFound, "404 Not Found")
}
_, err = am.db.NewDelete().Model((*models.Link)(nil)).Where("id = ?", id).Exec(context.Background())
_, err = models.DB.NewDelete().Model((*models.Link)(nil)).Where("id = ?", id).Exec(context.Background())
if err != nil {
log.Println(err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")

View file

@ -1,26 +0,0 @@
package api
import (
"code.lila.network/adoralaura/go-urlsh/internal/config"
"github.com/rs/zerolog"
"github.com/uptrace/bun"
)
// APIRouteManager is a model that provides db, logger and config access
// to API routes
type APIRouteManager struct {
db *bun.DB
logger *zerolog.Logger
config *config.Config
}
// NewAPIRouteManager creates a new Instance of an APIRouteManager
// and initializes it with the given DB, Logger and Config
func NewAPIRouteManager(db *bun.DB, logger *zerolog.Logger, config *config.Config) *APIRouteManager {
manager := new(APIRouteManager)
manager.db = db
manager.logger = logger
manager.config = config
return manager
}

View file

@ -1,22 +1,20 @@
package api
import (
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
"crypto/sha256"
"database/sql"
"encoding/hex"
"encoding/json"
"github.com/gofiber/fiber/v2"
"log"
"time"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
)
func (am APIRouteManager) HandleUserPost(c *fiber.Ctx) error {
func HandleUserPost(c *fiber.Ctx) error {
var newuser models.LoginRequest
err := json.Unmarshal(c.Body(), &newuser)
@ -25,7 +23,7 @@ func (am APIRouteManager) HandleUserPost(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
}
usercount, err := am.db.NewSelect().Model((*models.User)(nil)).Count(context.Background())
usercount, err := models.DB.NewSelect().Model((*models.User)(nil)).Count(context.Background())
if err != nil {
log.Printf("[POST /api/v1/users] Error querying database for users: %v\n", err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
@ -46,13 +44,13 @@ func (am APIRouteManager) HandleUserPost(c *fiber.Ctx) error {
user.PasswordHash = hash
user.Created = created
_, err = am.db.NewInsert().Model(user).Exec(context.Background())
_, err = models.DB.NewInsert().Model(user).Exec(context.Background())
if err != nil {
log.Printf("[POST /api/v1/users] Error adding user %v to database : %v\n", newuser.Username, err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
userresponse := models.UserResponse{UserName: newuser.Username, Created: created, MFAEnabled: false}
userresponse := models.UserResponse{UserName: newuser.Username, Created: created}
c.Status(fiber.StatusCreated)
err = c.JSON(userresponse)
@ -63,12 +61,12 @@ func (am APIRouteManager) HandleUserPost(c *fiber.Ctx) error {
}
}
func (am APIRouteManager) HandleUserMe(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
func HandleUserMe(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
}
cookie := c.Cookies(constants.LoginCookieName, "")
cookie := c.Cookies(misc.CookieName, "")
apikey := c.GetRespHeader("x-api-key", "")
var authmethod string
@ -83,37 +81,37 @@ func (am APIRouteManager) HandleUserMe(c *fiber.Ctx) error {
switch authmethod {
case "cookie":
var session models.Session
err := am.db.NewSelect().Model(&session).Where("cookie = ?", cookie).Scan(context.Background())
err := models.DB.NewSelect().Model(&session).Where("cookie = ?", cookie).Scan(context.Background())
if err != nil {
if err == sql.ErrNoRows {
log.Printf("[GET /api/v1/users/me] Session %v not found\n", cookie)
log.Printf("[HandleUserMe] Session %v not found\n", cookie)
} else {
log.Printf("[GET /api/v1/users/me] Error querying session %v from database: %v\n", cookie, err)
log.Printf("[HandleUserMe] Error querying session %v from database: %v\n", cookie, err)
}
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
err = am.db.NewSelect().Model(&user).Where("username = ?", session.UserName).Scan(context.Background())
err = models.DB.NewSelect().Model(&user).Where("username = ?", session.UserName).Scan(context.Background())
if err != nil {
log.Printf("[GET /api/v1/users/me] Error querying user %v from database: %v\n", session.UserName, err)
log.Printf("[HandleUserMe] Error querying user %v from database: %v\n", session.UserName, err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
case "apikey":
var key models.ApiKey
err := am.db.NewSelect().Model(&key).Where("key = ?", apikey).Scan(context.Background())
err := models.DB.NewSelect().Model(&key).Where("key = ?", apikey).Scan(context.Background())
if err != nil {
if err == sql.ErrNoRows {
log.Printf("[GET /api/v1/users/me] ApiKey %v not found\n", apikey)
log.Printf("[HandleUserMe] ApiKey %v not found\n", apikey)
} else {
log.Printf("[GET /api/v1/users/me] Error querying apikey %v from database: %v\n", apikey, err)
log.Printf("[HandleUserMe] Error querying apikey %v from database: %v\n", apikey, err)
}
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
err = am.db.NewSelect().Model(&user).Where("username = ?", key.UserName).Scan(context.Background())
err = models.DB.NewSelect().Model(&user).Where("username = ?", key.UserName).Scan(context.Background())
if err != nil {
log.Printf("[GET /api/v1/users/me] Error querying user %v from database: %v\n", key.UserName, err)
log.Printf("[HandleUserMe] Error querying user %v from database: %v\n", key.UserName, err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
}
@ -122,17 +120,9 @@ func (am APIRouteManager) HandleUserMe(c *fiber.Ctx) error {
userresponse.UserName = user.UserName
userresponse.Created = user.Created
hasmfa, err := db.UserHasMFA(user)
err := c.JSON(userresponse)
if err != nil {
log.Printf("[GET /api/v1/users/me] Error checking MFA status: %q\n", err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
userresponse.MFAEnabled = hasmfa
err = c.JSON(userresponse)
if err != nil {
log.Printf("[GET /api/v1/users/me] Error marshaling JSON response: %q\n", err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
return err
}
return nil
}

23
internal/app/error.go Normal file
View file

@ -0,0 +1,23 @@
package app
import (
"errors"
"github.com/gofiber/fiber/v2"
)
func HandleError(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
// Retrieve the custom status code if it's a *fiber.Error
var e *fiber.Error
if errors.As(err, &e) {
code = e.Code
}
// Set Content-Type: text/plain; charset=utf-8
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
// Return status code with error message
return c.Status(code).SendString(err.Error())
}

View file

@ -1,47 +1,88 @@
package app
import (
"strconv"
"code.lila.network/adoralaura/go-urlsh/internal/config"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/internal/api"
"codeberg.org/lauralani/go-urlsh/internal/web"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/etag"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/template/html/v2"
"github.com/rs/zerolog"
"github.com/uptrace/bun"
"github.com/gofiber/template/html"
"os"
)
func SetupFiber(db *bun.DB, logger *zerolog.Logger, config *config.Config) error {
func SetupFiber() error {
var prod = false
var port = "3000"
var ip = ""
var proxyheader = "X-Forwarded-For"
var template_engine = html.New("./views", ".tmpl")
if os.Getenv("PROD") == "true" {
prod = true
}
if os.Getenv("PORT") != "" {
port = os.Getenv("PORT")
}
if os.Getenv("LISTEN") != "" {
ip = os.Getenv("LISTEN")
}
if os.Getenv("PROXYHEADER") != "" {
proxyheader = os.Getenv("PROXYHEADER")
}
fiberapp := fiber.New(fiber.Config{
AppName: "go-urlsh",
ProxyHeader: config.ProxyHeader,
ProxyHeader: proxyheader,
Prefork: prod,
ErrorHandler: misc.HandleError,
ErrorHandler: HandleError,
Views: template_engine,
CompressedFileSuffix: ".gz",
})
fiberapp.Use(logger.New(logger.Config{Format: "[${ip}]:${port} ${status} ${method} ${path}\n"}))
fiberapp.Use(etag.New(etag.Config{Weak: false}))
fiberapp.Use(compress.New())
fiberapp.Use(recover.New())
addWebRoutes(fiberapp)
fiberapp.Get("/admin/", web.HandleAdminLinkIndexGet)
fiberapp.Get("/admin/account/", web.HandleAdminAccountGet)
fiberapp.Get("/admin/login", web.HandleAdminLoginGet)
fiberapp.Post("/admin/login", web.HandleAdminLoginPost)
fiberapp.Get("/admin/", web.HandleAdminLinkIndexGet)
fiberapp.Get("/admin/links/new", web.HandleAdminLinkNewGet)
fiberapp.Get("/admin/links/edit/:id", web.HandleAdminLinkEditGet)
fiberapp.Static("/admin/", "./web")
fiberapp.Get("/", web.HandleRootGet)
fiberapp.Get("/:id", web.HandleRootGet)
v1 := fiberapp.Group("/api/v1")
v1.Use(cors.New(cors.Config{AllowOrigins: "*"}))
addApiRoutes(v1, db, logger, config)
v1.Get("/links", api.HandleLinkGetAll)
v1.Get("/links/:id", api.HandleLinkGet)
v1.Put("/links/:id", api.HandleLinkPut)
v1.Post("/links", api.HandleLinkPost)
v1.Delete("/links/:id", api.HandleLinkDelete)
listenerr := fiberapp.Listen(config.HostIP.String() + ":" + strconv.Itoa(config.HostPort))
v1.Get("/apikeys", api.HandleApiKeysGetAll)
v1.Post("/apikeys", api.HandleApiKeysPost)
v1.Delete("/apikeys/:id<guid>", api.HandleApiKeysPost)
v1.Post("/users", api.HandleUserPost)
listenerr := fiberapp.Listen(ip + ":" + port)
if listenerr != nil {
return listenerr
}

View file

@ -1,57 +0,0 @@
package app
import (
"code.lila.network/adoralaura/go-urlsh/internal/api"
"code.lila.network/adoralaura/go-urlsh/internal/config"
"code.lila.network/adoralaura/go-urlsh/internal/web"
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"
"github.com/uptrace/bun"
)
func addWebRoutes(
f *fiber.App) {
f.Get("/admin/", web.HandleAdminLinkIndexGet)
f.Get("/admin/account/", web.HandleAdminAccountGet)
f.Get("/admin/account/setup-multifactor", web.HandleAdminAccountMFASetupGet)
f.Post("/admin/account/mfa/confirm", web.HandleAdminAccountMFASetupPost)
f.Delete("/admin/account/mfa", web.HandleAdminAccountMFARemove)
f.Get("/admin/login", web.HandleAdminLoginGet)
f.Post("/admin/login", web.HandleAdminLoginPost)
f.Get("/admin/login/multifactor", web.HandleAdminLoginMFAGet)
f.Post("/admin/login/multifactor", web.HandleAdminLoginMFAPost)
f.Get("/admin/", web.HandleAdminLinkIndexGet)
f.Get("/admin/links/new", web.HandleAdminLinkNewGet)
f.Get("/admin/links/edit/:id", web.HandleAdminLinkEditGet)
f.Static("/admin/", "./web")
f.Get("/", web.HandleIndexGet)
f.Get("/:id", web.HandleIndexGet)
}
func addApiRoutes(
r fiber.Router,
db *bun.DB,
logger *zerolog.Logger,
config *config.Config) {
am := api.NewAPIRouteManager(db, logger, config)
r.Get("/links", am.HandleLinkGetAll)
r.Get("/links/:id", am.HandleLinkGet)
r.Put("/links/:id", am.HandleLinkPut)
r.Post("/links", am.HandleLinkPost)
r.Delete("/links/:id", am.HandleLinkDelete)
r.Get("/apikeys", am.HandleApiKeysGetAll)
r.Post("/apikeys", am.HandleApiKeysPost)
r.Delete("/apikeys/:id<guid>", am.HandleApiKeysPost)
r.Post("/users", am.HandleUserPost)
}

View file

@ -1,43 +1,24 @@
package app
import (
"codeberg.org/lauralani/go-urlsh/models"
"context"
"log"
"github.com/jasonlvhit/gocron"
"github.com/rs/zerolog"
"github.com/uptrace/bun"
"log"
)
func CleanupLoginsCronJob(db *bun.DB, logger *zerolog.Logger) {
err := gocron.Every(5).Minutes().Do(CleanupLogins, db, logger)
func CleanupLoginsCronJob() {
err := gocron.Every(1).Hours().Do(CleanupLogins)
if err != nil {
return
}
<-gocron.Start()
}
func CleanupLogins(db *bun.DB, logger *zerolog.Logger) {
res, err := db.NewDelete().Table("logins").Where("expires < NOW()").Exec(context.Background())
func CleanupLogins() {
_, err := models.DB.NewDelete().Table("logins").Where("expires < NOW()").Exec(context.Background())
if err != nil {
log.Printf("[CleanupLogins] Error deleting logins: %v\n", err)
}
numdeleted, _ := res.RowsAffected()
logger.Debug().Int64("logins", numdeleted).Msg("cleaned up logins from DB")
res, err = db.NewDelete().Table("logintransactions").Where("expires < NOW()").Exec(context.Background())
if err != nil {
log.Printf("[CleanupLogins] Error deleting login transactions: %v\n", err)
}
numdeleted, _ = res.RowsAffected()
logger.Debug().Int64("transactions", numdeleted).Msg("cleaned up transactions from DB")
res, err = db.NewDelete().Table("multifactor").Where("expires_at < NOW()").Where("active = false").Exec(context.Background())
if err != nil {
log.Printf("[CleanupLogins] Error deleting 2fa transactions: %v\n", err)
}
numdeleted, _ = res.RowsAffected()
logger.Debug().Int64("mfatransactions", numdeleted).Msg("cleaned up 2fa transactions from DB")
log.Printf("Cleaned up logins")
}

View file

@ -1,144 +0,0 @@
package config
import (
"net/netip"
"slices"
"strconv"
"github.com/rs/zerolog"
"github.com/spf13/viper"
)
type Config struct {
DatabaseDSN string
// panic = 5,
// fatal = 4,
// error = 3,
// warn = 2,
// info = 1,
// debug = 0,
// trace = -1
LogLevel int
HostIP netip.Addr
HostPort int
HostURL string
ProxyHeader string
AccessLog bool
}
func NewConfig(configPath string) *Config {
logger := NewLogger(zerolog.InfoLevel)
loadConfigFile(configPath, logger)
cfg := new(Config)
exit := false
// handle Database DSN
if viper.GetString("database.url") != "" {
cfg.DatabaseDSN = viper.GetString("database.url")
} else {
dsn := "postgres://"
if viper.GetString("database.username") == "" {
exit = true
logger.Error().Msg("If database.url is not set, database.username must not be empty!")
} else {
dsn = dsn + viper.GetString("database.username")
}
if viper.GetString("database.password") == "" {
exit = true
logger.Error().Msg("If database.url is not set, database.password must not be empty!")
} else {
dsn = dsn + ":" + viper.GetString("database.password")
}
if viper.GetString("database.address") == "" {
exit = true
logger.Error().Msg("If database.url is not set, database.address must not be empty!")
} else {
dsn = dsn + "@" + viper.GetString("database.address")
}
if viper.GetString("database.port") != "" {
dsn = dsn + ":" + strconv.Itoa(viper.GetInt("database.port"))
}
if viper.GetString("database.database") == "" {
exit = true
logger.Error().Msg("If database.url is not set, database.database must not be empty!")
} else {
dsn = dsn + "/" + viper.GetString("database.database")
}
if slices.Contains([]string{"disable", "require", "verify-full", ""}, viper.GetString("database.ssl-mode")) {
if viper.GetString("database.ssl-mode") == "" {
cfg.DatabaseDSN = dsn + "?sslmode=disable"
} else {
cfg.DatabaseDSN = dsn + "?sslmode=" + viper.GetString("database.ssl-mode")
}
} else {
logger.Error().Msg(`If database.url is not set, database.ssl-mode must be either "disable", "require", "verify-full" or empty!`)
}
}
// Handle Logging
switch logLevel := viper.GetString("logging.level"); logLevel {
case "panic":
cfg.LogLevel = 5
case "fatal":
cfg.LogLevel = 4
case "error":
cfg.LogLevel = 3
case "warn":
cfg.LogLevel = 2
case "info":
cfg.LogLevel = 1
case "debug":
cfg.LogLevel = 0
case "trace":
cfg.LogLevel = -1
default:
exit = true
logger.Error().Str("value", logLevel).Msg(`Value of logging.level is invalid!`)
}
cfg.HostURL = viper.GetString("application.base-url")
if viper.GetString("application.listen") != "" {
ip, err := netip.ParseAddr(viper.GetString("application.listen"))
if err != nil {
exit = true
logger.Error().Str("value", viper.GetString("application.listen")).Msg(`Value of application.listen is not a valid IP address!`)
} else {
cfg.HostIP = ip
}
} else {
cfg.HostIP = netip.MustParseAddr("127.0.0.1")
}
if viper.GetInt("application.port") != 0 {
// if port is a valid port number
if viper.GetInt("application.port") <= 65535 && viper.GetInt("application.port") > 0 {
cfg.HostPort = viper.GetInt("application.port")
} else {
logger.Error().Str("value", viper.GetString("application.port")).Msg(`Value of application.port is not a valid port number!`)
}
} else {
cfg.HostPort = 3000
}
if viper.GetString("application.proxy-header") == "" {
cfg.ProxyHeader = "X-Forwarded-For"
} else {
cfg.ProxyHeader = viper.GetString("application.proxy-header")
}
if exit {
logger.Fatal().Msg("There were errors reading the config file. See errors above!")
}
return cfg
}

View file

@ -1,30 +0,0 @@
package config
import (
"os"
"github.com/rs/zerolog"
"github.com/spf13/viper"
)
// loadConfigFile reads the config file from given string OR
// from either $WorkDir/config.* or from /etc/go-urlsh/config.*
// Returns map[string]any with the specified config files.
// Panics if it can't read any config file.
func loadConfigFile(filepath string, logger *zerolog.Logger) {
if filepath == "" {
path, err := os.Executable()
if err != nil {
logger.Fatal().Err(err).Msg("error getting current working dir")
}
viper.SetConfigName("config")
viper.AddConfigPath("/etc/go-urlsh/")
viper.AddConfigPath(path)
} else {
viper.SetConfigFile(filepath)
}
err := viper.ReadInConfig()
if err != nil {
logger.Fatal().Err(err).Msg("failed to open config file")
}
}

View file

@ -1,60 +0,0 @@
package config
import (
"os"
"testing"
"github.com/rs/zerolog"
"github.com/spf13/viper"
)
// writeTestConfigFile creates a test config file at given filename location
func writeTestConfigFile(t *testing.T, filename string) {
testdata := []byte("[application]\nlisten = \"127.0.0.1\"\n[database]\nport = 54321\ntestbool = false")
err := os.WriteFile(filename, testdata, 0644)
if err != nil {
t.Fatalf("Can't write test file %q: %v", filename, err)
}
}
// removeTestConfigFile removes given config file
func removeTestConfigFile(t *testing.T, filename string) {
err := os.Remove(filename)
if err != nil {
t.Fatalf("Can't remove test file %q: %v", filename, err)
}
}
// TestLoadConfigFile calls config.loadConfigFile
// to check if a valid Configuration can be loaded.
func TestLoadConfigFile(t *testing.T) {
logger := NewLogger(zerolog.InfoLevel)
dir, err := os.Getwd()
if err != nil {
t.Fatalf("Can't get current working directory")
}
testFile := dir + "/" + "config_unittest.toml"
writeTestConfigFile(t, testFile)
defer removeTestConfigFile(t, testFile)
loadConfigFile(testFile, logger)
if viper.GetString("application.listen") != "127.0.0.1" {
t.Fatalf(`viper.GetString("application.listen") = %q, want "127.0.0.1"`, viper.GetString("application.listen"))
}
if viper.GetInt("database.port") != 54321 {
t.Fatalf(`viper.GetInt("database.port") = %q, want 54321`, viper.GetInt("database.port"))
}
if viper.GetBool("database.testbool") != false {
t.Fatalf(`viper.GetBool("database.testbool") = true, want false`)
}
if viper.GetString("invalid.config.value") != "" {
t.Fatalf(`viper.GetString("invalid.config.value") = %q, want ""`, viper.GetString("invalid.config.value"))
}
}

View file

@ -1,17 +0,0 @@
package config
import (
"os"
"time"
"github.com/rs/zerolog"
)
// NewLogger creates and initializes a new *zerolog.Logger with the given
// log level.
func NewLogger(level zerolog.Level) *zerolog.Logger {
output := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: false, TimeFormat: time.RFC3339}
logger := zerolog.New(output).With().Timestamp().Logger()
return &logger
}

View file

@ -1,10 +0,0 @@
package constants
import "time"
var LoginCookieName = "gourlsh_auth"
var MFAFlowCookieName = "gourlsh_mfa_key"
var MFASetupCookieName = "gourlsh_mfa_setup"
var LoginCookieLength = 40
var LoginCookieExpiryDuration = 30 * 24 * time.Hour

View file

@ -1,10 +1,9 @@
package db
import (
"codeberg.org/lauralani/go-urlsh/models"
"context"
"log"
"code.lila.network/adoralaura/go-urlsh/models"
)
// IsApiKeyValid checks the database if ApiKey val is valid.

View file

@ -1,12 +1,9 @@
package db
import (
"codeberg.org/lauralani/go-urlsh/models"
"context"
"database/sql"
"fmt"
"log"
"code.lila.network/adoralaura/go-urlsh/models"
)
// IsCookieValid checks the database if Cookie val is valid.
@ -28,40 +25,3 @@ func IsCookieValid(val string) bool {
return true
}
}
func GetUserFromCookie(cookie string) (models.User, error) {
var session models.Session
var user models.User
err := models.DB.NewSelect().Model(&session).Where("cookie = ?", cookie).Scan(context.Background())
if err != nil {
if err == sql.ErrNoRows {
return models.User{}, fmt.Errorf("[GetUserFromCookie] Session %v not found", cookie)
} else {
return models.User{}, fmt.Errorf("[GetUserFromCookie] Error querying session %v from database: %v", cookie, err)
}
}
err = models.DB.NewSelect().Model(&user).Where("username = ?", session.UserName).Scan(context.Background())
if err != nil {
return models.User{}, fmt.Errorf("[GetUserFromCookie] Error querying user %v from database: %v", session.UserName, err)
}
return user, nil
}
func GetUserFromMFACookie(cookie string) (models.User, error) {
var transaction models.MFALoginTransaction
var user models.User
err := models.DB.NewSelect().Model(&transaction).Where("sessionkey = ?", cookie).Scan(context.Background())
if err != nil {
if err == sql.ErrNoRows {
return models.User{}, fmt.Errorf("[GetUserFromMFACookie] MFA Transaction %v not found", cookie)
} else {
return models.User{}, fmt.Errorf("[GetUserFromMFACookie] Error querying MFA Transaction %v from database: %v", cookie, err)
}
}
err = models.DB.NewSelect().Model(&user).Where("username = ?", transaction.UserName).Scan(context.Background())
if err != nil {
return models.User{}, fmt.Errorf("[GetUserFromMFACookie] Error querying user %v from database: %v", transaction.UserName, err)
}
return user, nil
}

View file

@ -1,26 +0,0 @@
package db
//
//import (
// "database/sql"
// "regexp"
// "testing"
//)
//
//var db sql.DB
//
//func init() {
//
//}
// TestHelloName calls greetings.Hello with a name, checking
// for a valid return value.
//func TestCookieValidation(t *testing.T) {
// name := "Gladys"
// want := regexp.MustCompile(`\b` + name + `\b`)
// msg, err := Hello("Gladys")
// if !want.MatchString(msg) || err != nil {
// t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
// }
//}

View file

@ -1,11 +0,0 @@
package db
import "fmt"
type DatabaseInsertError struct {
message string
}
func (e *DatabaseInsertError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.message)
}

View file

@ -1,5 +0,0 @@
package db
import "github.com/uptrace/bun"
var database *bun.DB

View file

@ -1,8 +0,0 @@
// myfile_test.go
package db
import (
"github.com/uptrace/bun"
)
var TestingDB *bun.DB

View file

@ -1,66 +1,38 @@
package db
import (
"codeberg.org/lauralani/go-urlsh/models"
"context"
"database/sql"
"code.lila.network/adoralaura/go-urlsh/internal/config"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/rs/zerolog"
"fmt"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/driver/pgdriver"
"os"
)
func dbError(err error, logger *zerolog.Logger) {
logger.Fatal().Str("error", err.Error()).Msg("[DB] couldn't create database")
}
func InitializeDB(cfg *config.Config, logger *zerolog.Logger) *bun.DB {
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(cfg.DatabaseDSN)))
db := bun.NewDB(sqldb, pgdialect.New())
models.DB = db
_, err := db.NewCreateTable().IfNotExists().Model((*models.Link)(nil)).Exec(context.Background())
if err != nil {
dbError(err, logger)
}
_, err = db.NewCreateTable().IfNotExists().Model((*models.User)(nil)).Exec(context.Background())
if err != nil {
dbError(err, logger)
}
_, err = db.NewCreateTable().IfNotExists().Model((*models.Session)(nil)).Exec(context.Background())
if err != nil {
dbError(err, logger)
}
_, err = db.NewCreateTable().IfNotExists().Model((*models.ApiKey)(nil)).Exec(context.Background())
if err != nil {
dbError(err, logger)
}
_, err = db.NewCreateTable().IfNotExists().Model((*models.MFALoginTransaction)(nil)).Exec(context.Background())
if err != nil {
dbError(err, logger)
}
_, err = db.NewCreateTable().IfNotExists().Model((*models.MFAConfig)(nil)).Exec(context.Background())
if err != nil {
dbError(err, logger)
}
_, err = db.NewCreateTable().IfNotExists().Model((*models.MFAScratchCode)(nil)).Exec(context.Background())
if err != nil {
dbError(err, logger)
}
err = doMigrations(db, logger)
if err != nil {
dbError(err, logger)
}
return db
func InitializeDB() error {
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(os.Getenv("DATABASE_URL"))))
models.DB = bun.NewDB(sqldb, pgdialect.New())
_, err := models.DB.NewCreateTable().IfNotExists().Model((*models.Link)(nil)).Exec(context.Background())
if err != nil {
return fmt.Errorf("couldn't create database: [%w]", err)
}
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.User)(nil)).Exec(context.Background())
if err != nil {
return fmt.Errorf("couldn't create database: [%w]", err)
}
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.Session)(nil)).Exec(context.Background())
if err != nil {
return fmt.Errorf("couldn't create database: [%w]", err)
}
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.ApiKey)(nil)).Exec(context.Background())
if err != nil {
return fmt.Errorf("couldn't create database: [%w]", err)
}
return nil
}

View file

@ -1,14 +0,0 @@
package db
import (
"context"
"github.com/uptrace/bun"
)
func InsertIntoDB(db *bun.DB, thing any) error {
_, err := db.NewInsert().Model(&thing).Exec(context.Background())
if err != nil {
t.Fatalf("Couldn't insert test mfa config into DB: %q\n", err)
}
}

View file

@ -1,28 +0,0 @@
package db
import (
"context"
"code.lila.network/adoralaura/go-urlsh/migrations"
"github.com/rs/zerolog"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
func doMigrations(db *bun.DB, logger *zerolog.Logger) error {
ctx := context.Background()
migrator := migrate.NewMigrator(db, migrations.Migrations)
migrator.Init(ctx)
group, err := migrator.Migrate(ctx)
if err != nil {
return err
}
if group.IsZero() {
logger.Info().Msg("No new database migrations to run")
return nil
}
logger.Info().Msgf("Database got migrated to %s", group)
return nil
}

View file

@ -1,114 +0,0 @@
package db
import (
"context"
"errors"
"fmt"
"log"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/uptrace/bun"
)
// UserHasMFA checks the DB if given models.User has MFA enabled.
// Returns (true, nil) if User has MFA enabled, (false, nil) if not.
// (false, error) if a DB error happened
func UserHasMFA(user models.User) (bool, error) {
numrows, err := models.DB.NewSelect().Model((*models.MFAConfig)(nil)).Where("username = ?", user.UserName).Where("active = ?", true).Count(context.Background())
if err != nil {
return false, fmt.Errorf("[UserHasMFA] error getting MFA count from database: %q", err)
}
if numrows >= 1 {
return true, nil
}
return false, nil
}
// scratchCodeUnique checks the database if the generated scratch code
// is unique (not in the database yet)
func scratchCodeIsUnique(db *bun.DB, scratchcode string) bool {
var dbitem models.MFAScratchCode
numrows, err := db.NewSelect().Model(&dbitem).Where("code = ?", scratchcode).Count(context.Background())
if err != nil {
return false
}
if numrows != 0 {
return false
}
return true
}
// RemoveMFAFromDB removes MFA entries for given models.User from the database.
// Returns nil on success, error otherwise.
func RemoveMFAFromDB(user models.User) error {
hasMfa, err := UserHasMFA(user)
if err != nil {
return fmt.Errorf("[RemoveMFAFromDB] Error removing MFA from DB for user %v: %w", user.UserName, err)
}
if !hasMfa {
return nil
}
_, err = models.DB.NewDelete().Model((*models.MFAConfig)(nil)).Where("username = ?", user.UserName).Exec(context.Background())
if err != nil {
log.Println(err.Error())
return fmt.Errorf("[RemoveMFAFromDB] Error removing MFA Config from DB for user %v: %w", user.UserName, err)
}
_, err = models.DB.NewDelete().Model((*models.MFAScratchCode)(nil)).Where("username = ?", user.UserName).Exec(context.Background())
if err != nil {
log.Println(err.Error())
return fmt.Errorf("[RemoveMFAFromDB] Error removing MFA scratch codes from DB for user %v: %w", user.UserName, err)
}
return nil
}
// UserHasFailedMFAAttempt checks the DB for failed MFA Setup attempts
// e.g. inactive MFA config
func UserHasFailedMFAAttempt(user models.User) (bool, error) {
//var mfaconfig models.MFAConfig
//numrows, err := models.DB.NewSelect().Model(&mfaconfig).
// Where("username = ?", user.UserName).Where("active = ?", false).
// Count(context.Background())
//if err != nil {
// return false, err
//}
return true, nil
}
// GenerateNewScratchcodes generates new unique scratch codes, inserts them into the database
// and returns the scratch codes as []models.MFAScratchCode, and nil if successful. Returns empty
// []models.MFAScratchCode and an error if something failed
func GenerateNewScratchcodes(db *bun.DB, user models.User) ([]models.MFAScratchCode, error) {
scratchcodes := []models.MFAScratchCode{}
scratchcodeFailed := 0
// generate four unique(!) scratch codes for the user
for len(scratchcodes) != 4 {
if scratchcodeFailed > 15 {
return []models.MFAScratchCode{}, errors.New("failed to generate new scratch codes: maximum tries exceeded")
}
code := misc.RandomString(8)
if scratchCodeIsUnique(db, code) {
scratchcodes = append(scratchcodes, models.MFAScratchCode{
IsUsed: false, Code: code, UserName: user.UserName})
} else {
scratchcodeFailed++
}
}
_, err := db.NewInsert().Model(&scratchcodes).Exec(context.Background())
if err != nil {
return []models.MFAScratchCode{}, fmt.Errorf("error inserting new scratch codes to DB: %w", err)
}
return scratchcodes, nil
}

View file

@ -1,109 +0,0 @@
package db
import (
"context"
"os"
"testing"
"time"
"code.lila.network/adoralaura/go-urlsh/internal/config"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/rs/zerolog"
"github.com/uptrace/bun"
)
// createTestingUser creates the necessary testing users for testing MFA.
func createTestingUser(t *testing.T, db *bun.DB, user *models.User) {
mfaconfig := models.MFAConfig{
UserName: user.UserName,
TOTPSecret: "abcd",
ExpiresAt: time.Now().Add(5 * time.Minute),
Active: false,
}
_, err := db.NewInsert().Model(user).Exec(context.Background())
if err != nil {
t.Fatalf("Couldn't insert test user into DB: %q\n", err)
}
_, err = db.NewInsert().Model(&mfaconfig).Exec(context.Background())
if err != nil {
t.Fatalf("Couldn't insert test mfa config into DB: %q\n", err)
}
}
// removeTestingUser is used as deferred cleanup in multifactor tests.
// It removes models.User and models.MFAConfig with given username from DB.
func removeTestingUser(t *testing.T, db *bun.DB, username string) {
_, err := db.NewInsert().Model((*models.User)(nil)).
Where("username = ?", username).Exec(context.Background())
if err != nil {
t.Fatalf("Couldn't remove test user %q from DB: %q\n", username, err)
}
_, err = db.NewInsert().Model((*models.MFAConfig)(nil)).
Where("username = ?", username).Exec(context.Background())
if err != nil {
t.Fatalf("Couldn't remove test mfa config for %q from DB: %q\n", username, err)
}
}
func TestUserHasFailedMFAAttemptTrue(t *testing.T) {
cfgfile := os.Getenv("GOURLSH_TEST_CONFIGFILE")
logger := config.NewLogger(zerolog.InfoLevel)
cfg := config.NewConfig(cfgfile)
// setup test environment
testdb := InitializeDB(cfg, logger)
testUserName := "test_mfafailedtrue"
mfauser := models.User{
UserName: testUserName,
Created: time.Now(),
}
createTestingUser(t, testdb, &mfauser)
defer removeTestingUser(t, testdb, testUserName)
answer, err := UserHasFailedMFAAttempt(mfauser)
if err != nil {
t.Fatalf("Error running UserHasFailedMFAAttempt in tests: %q\n", err)
}
if answer == false {
t.Fatalf("db.UserHasFailedMFAAttempt() = false, want true")
}
}
func TestUserHasFailedMFAAttemptFalse(t *testing.T) {
cfgfile := os.Getenv("GOURLSH_TEST_CONFIGFILE")
cfg := config.NewConfig(cfgfile)
logger := config.NewLogger(zerolog.InfoLevel)
// setup test environment
testdb := InitializeDB(cfg, logger)
testUserName := "test_mfafailed"
mfauser := models.User{
UserName: testUserName,
Created: time.Now(),
}
createTestingUser(t, testdb, &mfauser)
defer removeTestingUser(t, testdb, testUserName)
answer, err := UserHasFailedMFAAttempt(mfauser)
if err != nil {
t.Fatalf("Error running UserHasFailedMFAAttempt in tests: %q\n", err)
}
if answer == true {
t.Fatalf("db.UserHasFailedMFAAttempt() = true, want false")
}
}
func TestGenerateNewScratchcodes(t *testing.T) {
}

View file

@ -0,0 +1,3 @@
package misc
var CookieName = "gourlsh_auth"

View file

@ -1,69 +0,0 @@
package misc
import (
"context"
"log"
"time"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
)
// SetLoginCookie handles setting the Login Cookie. It
// - generates a new cookie secret
// - sets its expiry
// - adds the necessary DB entry for the session and last login
// - adds the cookie to the fiber context
func SetLoginCookie(c *fiber.Ctx, user models.User, expiryTime time.Duration) error {
expires := time.Now().Add(expiryTime)
key := NewLoginCookie(constants.LoginCookieLength)
var dblogin models.Session
cookie := new(fiber.Cookie)
cookie.Name = constants.LoginCookieName
cookie.Value = key
cookie.Expires = expires
dblogin.Expires = expires
dblogin.Cookie = key
dblogin.UserName = user.UserName
_, err := models.DB.NewInsert().Model(&dblogin).Exec(context.Background())
if err != nil {
log.Printf("DB Error inserting login cookie information for user %v: %v\n", dblogin.UserName, err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
user.LastLogin = time.Now()
_, err = models.DB.NewUpdate().Model(&user).WherePK().Exec(context.Background())
if err != nil {
log.Printf("DB Error updating last login information for user %v: %v\n", dblogin.UserName, err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
c.Cookie(cookie)
return nil
}
func SetMFAFlowCookie(c *fiber.Ctx, cookievalue string) {
expires := time.Now().Add(30 * time.Minute)
cookie := new(fiber.Cookie)
cookie.Name = constants.MFAFlowCookieName
cookie.Value = cookievalue
cookie.Expires = expires
c.Cookie(cookie)
}
func SetMFASetupCookie(c *fiber.Ctx, cookievalue string) {
expires := time.Now().Add(30 * time.Minute)
cookie := new(fiber.Cookie)
cookie.Name = constants.MFASetupCookieName
cookie.Value = cookievalue
cookie.Expires = expires
c.Cookie(cookie)
}

View file

@ -1,43 +0,0 @@
package misc
import (
"encoding/json"
"errors"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
)
func HandleError(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
// Retrieve the custom status code if it's a *fiber.Error
var e *fiber.Error
if errors.As(err, &e) {
code = e.Code
}
// Set Content-Type: text/plain; charset=utf-8
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
// Return status code with error message
return c.Status(code).SendString(err.Error())
}
func New500Error() error {
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
func New400Error() error {
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
}
func New400WithMessageError(msg string) error {
body, _ := json.Marshal(models.HttpErrorBody{Message: msg})
return fiber.NewError(fiber.StatusBadRequest, string(body[:]))
}
func New401Error() error {
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
}

View file

@ -13,13 +13,3 @@ func RandomString(n int) string {
}
return string(s)
}
func NewLoginCookie(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
s := make([]rune, n)
for i := range s {
s[i] = letters[rand.Intn(len(letters))]
}
return string(s)
}

View file

@ -1,38 +1,41 @@
package web
import (
"log"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/models"
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
"database/sql"
"github.com/gofiber/fiber/v2"
"log"
)
func HandleAdminAccountGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
}
cookie := c.Cookies(constants.LoginCookieName, "")
user, err := db.GetUserFromCookie(cookie)
if err != nil {
log.Println(err)
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
var userresponse models.UserResponse
userresponse.UserName = user.UserName
userresponse.Created = user.Created
cookie := c.Cookies(misc.CookieName, "")
var session models.Session
var user models.User
hasmfa, err := db.UserHasMFA(user)
err := models.DB.NewSelect().Model(&session).Where("cookie = ?", cookie).Scan(context.Background())
if err != nil {
log.Println(err)
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
if err == sql.ErrNoRows {
log.Printf("[HandleUserMe] Session %v not found\n", cookie)
} else {
log.Printf("[HandleUserMe] Error querying session %v from database: %v\n", cookie, err)
}
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
userresponse.MFAEnabled = hasmfa
err = models.DB.NewSelect().Model(&user).Where("username = ?", session.UserName).Scan(context.Background())
if err != nil {
log.Printf("[HandleUserMe] Error querying user %v from database: %v\n", session.UserName, err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
return c.Render("account", userresponse)
return c.Render("account", user)
}

View file

@ -1,19 +1,18 @@
package web
import (
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
"database/sql"
"fmt"
"log"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
"log"
)
func HandleAdminLinkNewGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
@ -23,7 +22,7 @@ func HandleAdminLinkNewGet(c *fiber.Ctx) error {
}
func HandleAdminLinkEditGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
@ -53,7 +52,7 @@ func HandleAdminLinkEditGet(c *fiber.Ctx) error {
}
func HandleAdminLinkIndexGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/login")
c.Status(fiber.StatusSeeOther)
return nil

View file

@ -1,17 +1,16 @@
package web
import (
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
"crypto/sha256"
"encoding/hex"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"log"
"time"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
)
func HandleAdminLoginPost(c *fiber.Ctx) error {
@ -33,41 +32,39 @@ func HandleAdminLoginPost(c *fiber.Ctx) error {
if passwordsum == user.PasswordHash {
// Passwords match
var expires time.Time
hasmfa, err := db.UserHasMFA(user)
if login.Remember == "on" {
expires = time.Now().Add(30 * 24 * time.Hour)
} else {
expires = time.Now().Add(24 * time.Hour)
}
key := uuid.New().String()
dblogin := new(models.Session)
cookie := new(fiber.Cookie)
cookie.Name = misc.CookieName
cookie.Value = key
cookie.Expires = expires
dblogin.Expires = expires
dblogin.Cookie = key
dblogin.UserName = user.UserName
_, err = models.DB.NewInsert().Model(dblogin).Exec(context.Background())
if err != nil {
log.Printf("[HandleAdminLoginPost] Error checking MFA status for user %v: %q", user.UserName, err)
log.Printf("DB Error inserting login cookie information for user %v: %v\n", dblogin.UserName, err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
if hasmfa {
var mfatransaction models.MFALoginTransaction
sessionkey := misc.NewLoginCookie(constants.LoginCookieLength)
mfatransaction.SessionKey = sessionkey
mfatransaction.Expires = time.Now().Add(30 * time.Minute)
mfatransaction.UserName = user.UserName
_, err = models.DB.NewInsert().Model(&mfatransaction).Exec(context.Background())
if err != nil {
log.Printf("[HandleAdminLoginPost] Error inserting MFA transaction to DB: %q\n", err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
misc.SetMFAFlowCookie(c, sessionkey)
c.Status(fiber.StatusSeeOther)
c.Location("/admin/login/multifactor")
return nil
}
// TODO: maybe make login cookie time configurable
// TODO: handle rememberme
err = misc.SetLoginCookie(c, user, constants.LoginCookieExpiryDuration)
user.LastLogin = time.Now()
_, err = models.DB.NewUpdate().Model(&user).WherePK().Exec(context.Background())
if err != nil {
log.Printf("[HandleAdminLoginPost] Error setting cookie: %q\n", err)
log.Printf("DB Error updating last login information for user %v: %v\n", dblogin.UserName, err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
c.Cookie(cookie)
c.Status(fiber.StatusSeeOther)
c.Location("/admin/")
return nil
@ -78,7 +75,7 @@ func HandleAdminLoginPost(c *fiber.Ctx) error {
}
func HandleAdminLoginGet(c *fiber.Ctx) error {
if db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
if db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil

View file

@ -1,22 +0,0 @@
package web
import (
"code.lila.network/adoralaura/go-urlsh/internal/config"
"github.com/rs/zerolog"
"github.com/uptrace/bun"
)
type WebRouteManager struct {
db *bun.DB
logger *zerolog.Logger
config *config.Config
}
func NewWebRouteManager(db *bun.DB, logger *zerolog.Logger, config *config.Config) *WebRouteManager {
manager := new(WebRouteManager)
manager.db = db
manager.logger = logger
manager.config = config
return manager
}

View file

@ -1,112 +0,0 @@
package web
import (
"context"
"fmt"
"log"
"slices"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
"github.com/pquerna/otp/totp"
)
// handles the POST request where the User has to input 2fa token
func HandleAdminLoginMFAPost(c *fiber.Ctx) error {
mfacookie := c.Cookies(constants.MFAFlowCookieName, "")
if mfacookie == "" {
return misc.New400Error()
}
token := new(models.TokenRequest)
var istotp bool
if err := c.BodyParser(token); err != nil {
// TODO: Debug logging
return misc.New400Error()
}
if len(token.Token) == 6 {
istotp = true
} else if len(token.Token) == 8 {
istotp = false
} else {
// TODO: Debug logging
return misc.New400Error()
}
user, err := db.GetUserFromMFACookie(mfacookie)
if err != nil {
log.Println(err)
// TODO: Debug logging
return misc.New400Error()
}
// check token/scratch validity
var scratchcodeIsValid bool
if istotp {
scratchcodeIsValid = checkTotpIsValid(token.Token, user)
} else {
scratchcodeIsValid = checkScratchIsValid(token.Token, user)
}
if scratchcodeIsValid {
err = misc.SetLoginCookie(c, user, constants.LoginCookieExpiryDuration)
if err != nil {
log.Printf("[HandleAdminLoginPost] Error setting cookie: %q\n", err)
return misc.New500Error()
}
_, err = models.DB.NewUpdate().
Model(&models.MFAScratchCode{Code: token.Token, IsUsed: true}).
OmitZero().WherePK().Exec(context.Background())
if err != nil {
fmt.Printf("[HandleAdminLoginMFAPost] Error marking scratch code as used: %v\n", err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
c.Status(fiber.StatusOK)
return nil
}
log.Printf("[HandleAdminLoginPost] Error handling MFA code: %q\n", err)
return misc.New400Error()
}
func checkTotpIsValid(passcode string, user models.User) bool {
var mfaconfig models.MFAConfig
err := models.DB.NewSelect().Model(&mfaconfig).Where("username = ?", user.UserName).Scan(context.Background())
if err != nil {
log.Printf("Error getting MFA config for %v from DB: %v\n", user.UserName, err.Error())
// TODO: Debug logging
return false
}
return totp.Validate(passcode, mfaconfig.TOTPSecret)
}
func checkScratchIsValid(scratch string, user models.User) bool {
var scratchcodes []models.MFAScratchCode
err := models.DB.NewSelect().Model(&scratchcodes).Where("username = ?", user.UserName).Where("is_used = ?", false).Scan(context.Background())
if err != nil {
log.Printf("Error getting MFA config for %v from DB: %v\n", user.UserName, err.Error())
// TODO: Debug logging
return false
}
var scratchcodeSlice []string
for _, code := range scratchcodes {
scratchcodeSlice = append(scratchcodeSlice, code.Code)
}
return slices.Contains(scratchcodeSlice, scratch)
}
func HandleAdminLoginMFAGet(c *fiber.Ctx) error {
return c.Render("login-multifactor", nil)
}

View file

@ -1,49 +0,0 @@
package web
import (
"log"
"net/http"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"github.com/gofiber/fiber/v2"
)
// HandleAdminAccountMFARemove is a DELETE endpoint that handles the deletion
// of the logged in users MFA configuration.
//
// Returns HTTP 401 if no valid user cookie, HTTP 400 if no MFA is configured for the user,
// HTTP 500 if a DB error happened or HTTP 204 if the deletion request succeeded.
func HandleAdminAccountMFARemove(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
c.Status(http.StatusUnauthorized)
return nil
}
user, err := db.GetUserFromCookie(c.Cookies(constants.LoginCookieName))
if err != nil {
log.Println(err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
hasMfa, err := db.UserHasMFA(user)
if err != nil {
log.Println(err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
if !hasMfa {
return misc.New400Error()
}
err = db.RemoveMFAFromDB(user)
if err != nil {
log.Println(err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
c.Status(fiber.StatusNoContent)
return nil
}

View file

@ -1,159 +0,0 @@
package web
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"image/png"
"log"
"strconv"
"time"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/internal/misc"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
"github.com/pquerna/otp/totp"
)
func HandleAdminAccountMFASetupGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
}
var mfaobject models.MFATemplateObject
var mfaconfig models.MFAConfig
mfaconfig.Active = false
mfaconfig.ExpiresAt = time.Now().Add(15 * time.Minute)
user, err := db.GetUserFromCookie(c.Cookies(constants.LoginCookieName))
if err != nil {
log.Println(err)
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
mfaconfig.UserName = user.UserName
key, err := totp.Generate(totp.GenerateOpts{
Issuer: "go-urlsh",
AccountName: user.UserName,
})
if err != nil {
log.Println(err)
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
mfaconfig.TOTPSecret = key.Secret()
mfaobject.Key = key.URL()
var buf bytes.Buffer
img, err := key.Image(200, 200)
if err != nil {
log.Println(err)
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
png.Encode(&buf, img)
base64img := base64.StdEncoding.EncodeToString(buf.Bytes())
mfaobject.Image = base64img
// todo: use function
_, err = models.DB.NewInsert().Model(&scratchcodes).Exec(context.Background())
if err != nil {
log.Printf("[HandleAdminAccountMFASetupGet] Error inserting scratch codes to DB: %q\n", err)
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
_, err = models.DB.NewInsert().Model(&mfaconfig).Exec(context.Background())
if err != nil {
log.Printf("[HandleAdminAccountMFASetupGet] Error inserting mfaconfig to DB: %q\n", err)
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
misc.SetMFASetupCookie(c, strconv.Itoa(int(mfaconfig.ID)))
return c.Render("setup-multifactor", mfaobject)
}
func HandleAdminAccountMFASetupPost(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
}
var response models.MFASetupResponse
response.Error = true
var token models.TokenRequest
var config models.MFAConfig
var scratchcodes []models.MFAScratchCode
//var user models.User
err := json.Unmarshal(c.Body(), &token)
if err != nil {
log.Println(err.Error())
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
}
//if err := c.BodyParser(&token); err != nil {
// response.Message = "Token is invalid"
// c.Status(fiber.StatusBadRequest)
// c.JSON(response)
// return nil
//}
setupcookie := c.Cookies(constants.MFASetupCookieName, "")
if setupcookie == "" {
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
}
err = models.DB.NewSelect().Model(&config).Where("id = ?", setupcookie).Scan(context.Background())
if err != nil {
log.Printf("[HandleAdminAccountMFASetupPost] Error getting MFAConfig from DB: %q\n", err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
totpvalid := totp.Validate(token.Token, config.TOTPSecret)
if totpvalid {
response.Error = false
response.Message = "Multifactor authentication was successfully set up!"
err = models.DB.NewSelect().Model(&scratchcodes).Where("username = ?", config.UserName).Scan(context.Background())
if err != nil {
log.Printf("[HandleAdminAccountMFASetupPost] Error getting MFA scratch codes from DB: %q\n", err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
var scratchcodeSlice []string
for _, code := range scratchcodes {
scratchcodeSlice = append(scratchcodeSlice, code.Code)
}
response.RecoveryTokens = scratchcodeSlice
config.Active = true
_, err := models.DB.NewUpdate().Model(&config).Column("active").WherePK().Exec(context.Background())
if err != nil {
log.Printf("[HandleAdminAccountMFASetupGet] Error getting MFAConfig from DB: %q\n", err)
response.Message = "500: DB Error, see logs"
c.Status(fiber.StatusInternalServerError)
c.JSON(response)
return nil
}
c.Status(fiber.StatusOK)
c.JSON(response)
} else {
response.Error = true
response.Message = "Token is invalid, please try again."
c.Status(fiber.StatusBadRequest)
c.JSON(response)
}
return nil
}

View file

@ -1,25 +1,24 @@
package web
import (
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
"database/sql"
"log"
"code.lila.network/adoralaura/go-urlsh/internal/constants"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"code.lila.network/adoralaura/go-urlsh/models"
"github.com/gofiber/fiber/v2"
"log"
)
func HandleIndexGet(c *fiber.Ctx) error {
func HandleRootGet(c *fiber.Ctx) error {
if c.Params("id", "") == "" {
if db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
if db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
} else {
err := c.SendString("This is the index page of go-urlsh.\n " +
"See https://code.lila.network/adoralaura/go-urlsh for more info.")
"See https://codeberg.org/lauralani/go-urlsh for more info.")
if err != nil {
log.Println(err)
return err

View file

@ -1,9 +1,8 @@
package main
import (
app "codeberg.org/lauralani/go-urlsh/cmd/go-urlsh"
"log"
app "code.lila.network/adoralaura/go-urlsh/cmd/go-urlsh"
)
func main() {

View file

@ -1,17 +0,0 @@
package main
import (
"os"
"testing"
"code.lila.network/adoralaura/go-urlsh/internal/db"
"github.com/uptrace/bun"
)
var TestingDB *bun.DB
func TestMain(m *testing.M) {
TestingDB = db.InitializeDB()
os.Exit(m.Run())
}

View file

@ -1,19 +0,0 @@
package migrations
import (
"context"
"github.com/uptrace/bun"
)
func init() {
Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
db.ExecContext(context.Background(), `ALTER TABLE logins ALTER COLUMN cookie SET DATA TYPE VARCHAR`)
db.ExecContext(context.Background(), `TRUNCATE TABLE logins`)
return nil
}, func(ctx context.Context, db *bun.DB) error {
db.ExecContext(context.Background(), `ALTER TABLE logins ALTER COLUMN cookie SET DATA TYPE UUID USING (gen_random_uuid());`)
db.ExecContext(context.Background(), `ALTER TABLE logins ALTER COLUMN cookie SET DEFAULT gen_random_uuid();`)
return nil
})
}

View file

@ -1,23 +0,0 @@
package migrations
import (
"context"
"github.com/uptrace/bun"
)
func init() {
Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
db.ExecContext(context.Background(), `ALTER TABLE multifactor RENAME COLUMN expiresat TO expires_at`)
db.ExecContext(context.Background(), `ALTER TABLE multifactor RENAME COLUMN totpurl TO totp_secret`)
db.ExecContext(context.Background(), `ALTER TABLE users RENAME COLUMN lastlogin TO last_login`)
db.ExecContext(context.Background(), `ALTER TABLE multifactor_scratchcodes RENAME COLUMN isused TO is_used`)
return nil
}, func(ctx context.Context, db *bun.DB) error {
db.ExecContext(context.Background(), `ALTER TABLE multifactor RENAME COLUMN expires_at TO expiresat`)
db.ExecContext(context.Background(), `ALTER TABLE multifactor RENAME COLUMN totp_secret TO totpurl`)
db.ExecContext(context.Background(), `ALTER TABLE users RENAME COLUMN last_login TO lastlogin`)
db.ExecContext(context.Background(), `ALTER TABLE multifactor_scratchcodes RENAME COLUMN is_used TO isused`)
return nil
})
}

View file

@ -1,7 +0,0 @@
package migrations
import (
"github.com/uptrace/bun/migrate"
)
var Migrations = migrate.NewMigrations()

View file

@ -4,5 +4,4 @@ import (
"github.com/uptrace/bun"
)
// Deprecated: use db.database in db Package instead
var DB *bun.DB

View file

@ -1,5 +0,0 @@
package models
type HttpErrorBody struct {
Message string `json:"message"`
}

View file

@ -1,14 +1,13 @@
package models
import (
"time"
"github.com/uptrace/bun"
"time"
)
type Session struct {
bun.BaseModel `bun:"table:logins"`
Cookie string `bun:"cookie,pk"`
Cookie string `bun:"cookie,pk,type:uuid"`
UserName string `bun:"username,notnull"`
Expires time.Time `bun:"expires,notnull"`
}

View file

@ -1,49 +0,0 @@
package models
import (
"time"
"github.com/uptrace/bun"
)
type TokenRequest struct {
Token string `json:"token"`
}
type MFALoginTransaction struct {
bun.BaseModel `bun:"table:logintransactions"`
SessionKey string `bun:"sessionkey,pk"`
UserName string `bun:"username,notnull"`
Expires time.Time `bun:"expires,notnull"`
}
// TODO: Rework Scratch tokens:
// Only one Scratch token
// Possibility to regenerate scratch token
// mark Tokens as used
type MFAConfig struct {
bun.BaseModel `bun:"table:multifactor"`
ID int64 `bun:"id,pk,autoincrement"`
UserName string `bun:"username,notnull"`
TOTPSecret string `bun:"totp_secret,notnull"`
ExpiresAt time.Time `bun:"expires_at,notnull"`
Active bool `bun:"active,notnull"`
}
type MFAScratchCode struct {
bun.BaseModel `bun:"table:multifactor_scratchcodes"`
Code string `bun:"code,pk"`
UserName string `bun:"username,notnull"`
IsUsed bool `bun:"is_used,notnull"`
}
type MFATemplateObject struct {
Key string
Image string
}
type MFASetupResponse struct {
Error bool `json:"error"`
Message string `json:"message,omitempty"`
RecoveryTokens []string `json:"recovery_tokens,omitempty"`
}

View file

@ -1,22 +1,20 @@
package models
import (
"time"
"github.com/uptrace/bun"
"time"
)
type User struct {
bun.BaseModel `bun:"table:users"`
UserName string `bun:"username,pk" json:"username"`
Created time.Time `bun:"created,notnull,default:now()" json:"created"`
LastLogin time.Time `bun:"last_login" json:"last_login"`
LastLogin time.Time `bun:"lastlogin" json:"last_login"`
PasswordSalt string `bun:"salt" json:"password_salt"`
PasswordHash string `bun:"password" json:"password_hash"`
}
type UserResponse struct {
UserName string `json:"username"`
Created time.Time `json:"created"`
MFAEnabled bool `json:"mfa_neabled"`
UserName string `json:"username"`
Created time.Time `json:"created"`
}

View file

@ -7,22 +7,12 @@
<title>Account - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/main.js" defer></script>
<style>
h1,
h2,
h3,
h4,
h5,
h6 {
margin-bottom: 30px;
}
a[role=button] {
width: fit-content;
}
h1, h2, h3, h4, h5, h6 {
margin-bottom: 30px;
}
</style>
</head>
@ -42,7 +32,7 @@
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)">Users (coming soon)</a>
<a href="javascript: void(0)" >Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
@ -57,23 +47,13 @@
<p>Created at: {{ .Created }}</p>
<h2>Security</h2>
{{if .MFAEnabled}}
<a href="#" onclick="HandleMFARemovalRequest()" role="button">disable Two-Factor Authentication</a>
{{else}}
<a href="/admin/account/setup-multifactor" role="button">Setup Two-Factor Authentication</a>
{{end}}
<dialog id="mfa-disable-dialog" style="flex-direction: column;">
<h2 id="dialog-heading">⚠️ Are you sure you want to disable two-factor authentication?</h2>
<p id="dialog-text">Two-factor authentication adds an additional layer of security to your account by requiring
more than just a password to sign in.<br />If you need to change your configuration, you can delete and re-add
Two-Factor authentication again.<br /><br />Do you really want to remove your Two-Factor authentication?</p>
<div class="container grid">
<!-- change those two buttons and rework the functions to close and initiate deletion-->
<button id="button-keep" class="pico-background-green-450" onclick="HandleModalClose('mfa-disable-dialog')">No, keep Two-Factor authentication</button>
<button id="button-delete" class="pico-color-red-450 pico-background-red-450" onclick="HandleMFARemoval()">Yes,
remove Two-Factor authentication</button>
</div>
<p>nothing here yet</p>
<dialog id="dialog-info">
<h2 id="dialog-heading"></h2>
<p id="dialog-text"></p>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
</main>
{{template "partials/footer" .}}

View file

@ -7,7 +7,6 @@
<title>Add new Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/main.js" defer></script>

View file

@ -7,7 +7,6 @@
<title>Edit Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/main.js" defer></script>

View file

@ -7,7 +7,6 @@
<title>Shortlinks - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/main.js" defer></script>

View file

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Multi Factor Authentication - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<script src="/admin/main.js" defer></script>
<style>
.hidden {
display: none;
}
</style>
</head>
<body>
<main class="container">
<article class="grid">
<div>
<hgroup>
<h1
style='--color: var(--h1-color); font-weight: var(--font-weight); font-size: var(--font-size); font-family: var(--font-family);'>
Multi Factor Authentication</h1>
</hgroup>
<form action="javascript:HandleMFALoginTokenPost()" style="margin-bottom: 0;" autocomplete="off">
<label for="token">Authentication Code (Format: 123456 or a1b2c3d4 for recovery codes)</label>
<input type="text" name="token" id="token" autocomplete="one-time-code">
<p id="error-message" class="hidden" style="color: red;">Error Message</p>
<input id="submit" type="submit" value="Submit" class="contrast">
</form>
</div>
</article>
</main>
</body>
</html>

View file

@ -7,7 +7,6 @@
<title>Login - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<style>
</style>
@ -27,6 +26,13 @@
<label for="password">Password</label>
<input type="password" name="password" id="password" placeholder="*******">
<fieldset>
<label for="remember">
<input type="checkbox" role="switch" id="remember" name="remember" />
Stay logged in
</label>
</fieldset>
<input type="submit" value="Login" class="contrast">
</form>
</div>

View file

@ -1,8 +1,8 @@
<footer class="container-fluid">
<small>
&#169; 2023-2024 Adora Laura Kalb -
<a href="https://code.lila.network/adoralaura/go-urlsh" target="_blank" class="secondary">Source Code</a> -
&#169; 2023 Laura Kalb -
<a href="https://codeberg.org/lauralani/go-urlsh" target="_blank" class="secondary">Source Code</a> -
<a href="https://picocss.com" target="_blank" class="secondary">Theme: PicoCSS</a> -
<a href="https://code.lila.network/adoralaura/go-urlsh/src/branch/main/LICENSE" target="_blank" class="secondary">License</a>
<a href="https://codeberg.org/lauralani/go-urlsh/src/branch/main/LICENSE" target="_blank" class="secondary">License</a>
</small>
</footer>
</footer>

View file

@ -1,109 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Set up Two-Factor authentication - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/main.js" defer></script>
<style>
h1,
h2,
h3,
h4,
h5,
h6 {
margin-bottom: 30px;
}
i {
font-style: normal;
margin: 15px;
}
input {
margin-top: 15px;
}
img {
margin: 15px;
}
#token {
width: 15em;
margin-right: 15px;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<nav class="container-fluid">
<ul>
<li>
<a href="/admin/" class="contrast"><strong>go-urlsh</strong></a>
</li>
</ul>
<ul>
<li>
<a href="/admin/">Links</a>
</li>
<li>
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)">Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
</li>
</ul>
</nav>
<main class="container" style="padding: 0">
<h1>Setup Two-Factor authentication</h1>
In order to setup Two-Factor authentication, please scan this image with your authentication application:<br />
<img src="data:image/png;base64,{{ .Image }}" alt="TOTP QR-Code" width="200" height="200"><br />
Or enter the secret manually into your TOTP application:
<br />
<i class="monospace">{{ .Key }}</i>
<br /><br />
Afterwards please enter the passcode shown in the TOTP application here for confirmation:
<br />
<form action="javascript:HandleMFASetupTokenSubmit();" style="width: fit-content;" autocomplete="off">
<p id="error-message" class="hidden" style="color: var(--pico-color-red-500)">
The TOTP code you provided is not correct. Please try again</p>
<div style="display: flex;">
<input type="text" name="token" id="token" inputmode="numeric" autocomplete="one-time-code">
<input id="button-submit" type="submit" value="Submit" class="contrast" style="width: fit-content;">
</div>
</form>
<dialog id="user-dialog" style="flex-direction: column;">
<h2 id="dialog-heading">2FA successfully set up!</h2>
<p id="dialog-text">These are your recovery codes, keep them somewhere safe, you'll only see them once:</p>
<p id="dialog-tokens" class="monospace"></p>
<form method="dialog">
<button onclick="HandleModalClose('user-dialog', '/admin/account')">Close</button>
</form>
</dialog>
<dialog id="dialog-error" style="flex-direction: column;">
<h2 id="dialog-heading"></h2>
<p id="dialog-text"></p>
<form method="dialog">
<button onclick="HandleModalClose('dialog-error')">Close</button>
</form>
</dialog>
</main>
{{template "partials/footer" .}}
</body>
</html>

View file

@ -7,8 +7,8 @@
<title>Add new Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/link_add.js" defer></script>
<script src="/admin/misc.js" defer></script>
</head>

View file

@ -75,8 +75,4 @@ body>footer {
input[readonly] {
color: gray;
}
.monospace {
font-family: 'Courier New', monospace;
}
}

View file

@ -7,7 +7,6 @@
<title>Edit Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/main.js" defer></script>

View file

@ -7,7 +7,6 @@
<title>Shortlinks - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/main.js" defer></script>

View file

@ -7,7 +7,6 @@
<title>Login - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/pico.colors.min.css">
<style>
</style>

View file

@ -1,6 +1,6 @@
// Link overview
async function HandleLinkIndexDelete(id) {
async function HandleLinkIndexDelete(id){
let response = await fetch("/api/v1/links/" + id, {
credentials: "include",
mode: "same-origin",
@ -30,7 +30,7 @@ async function HandleLinkEditSubmit() {
await LinkAction("edit")
}
async function LinkAction(action) {
async function LinkAction(action){
document.getElementById("submit").active = false
let slug = document.getElementById("linkname").value
let url = document.getElementById("link").value
@ -38,22 +38,22 @@ async function LinkAction(action) {
let method, endpoint = ""
let body;
switch (action) {
switch(action) {
case "add":
method = "POST"
endpoint = "/api/v1/links/"
body = {
"id": slug,
"url": url,
"description": description
"id" : slug,
"url" : url,
"description" : description
}
break;
case "edit":
method = "PUT"
endpoint = "/api/v1/links/" + slug
body = {
"url": url,
"description": description
"url" : url,
"description" : description
}
break;
default:
@ -82,7 +82,8 @@ async function LinkAction(action) {
async function HandleLinkFieldChange() {
console.log("HandleChange")
let buttonactive = true
if (document.getElementById("link").value === "") {
if (document.getElementById("link").value === "")
{
buttonactive = false
}
document.getElementById("submit").active = buttonactive
@ -97,7 +98,7 @@ async function HandleApiKeyNewSubmit() {
button.setAttribute("aria-busy", "true")
let body = {
"description": description
"description" : description
}
let response = await fetch("/api/v1/apikeys", {
@ -116,111 +117,11 @@ async function HandleApiKeyNewSubmit() {
}
}
async function HandleMFASetupTokenSubmit() {
let token = document.getElementById("token").value
let button = document.getElementById("button-submit")
button.disabled = true
button.setAttribute('aria-busy', 'true')
let body = {
"token": token
}
let response = await fetch("/admin/account/mfa/confirm", {
credentials: "include",
body: JSON.stringify(body),
headers: { "Content-Type": "application/json", },
mode: "same-origin",
method: "POST"
});
if (response.ok) {
let data = await response.json()
document.getElementById("dialog-tokens").textContent = data["recovery_tokens"].join(" ")
document.getElementById('user-dialog').showModal()
} else if (response.status == 400) {
// document.cookie = 'gourlsh_mfa_setup=; Max-Age=-1; path=/; domain=' + location.hostname;
document.getElementById('token').setAttribute('aria-invalid', 'true')
button.setAttribute('aria-busy', 'false')
button.disabled = false
document.getElementById('error-message').setAttribute('class', '')
}
async function HandleApiKeyModalClose() {
let modal = document.getElementById('dialog-info');
modal.close()
}
async function HandleMFARemovalRequest() {
document.getElementById('mfa-disable-dialog').showModal()
}
async function HandleMFARemoval() {
let keepButton = document.getElementById('button-keep')
let deleteButton = document.getElementById('button-delete')
keepButton.disabled = true
deleteButton.disabled = true
deleteButton.value = 'Please wait...'
deleteButton.setAttribute('aria-busy', 'true')
endpoint = "/admin/account/mfa"
let response = await fetch(endpoint, {
credentials: "include",
mode: "same-origin",
method: "DELETE"
});
if (response.ok) {
document.location = "/admin/account"
} //else {
// TODO
// Handle error condition
//document.cookie = 'gourlsh_mfa_setup=; Max-Age=-1; path=/; domain=' + location.hostname;
//document.getElementById("dialog-heading").textContent = "Error!"
//document.getElementById("dialog-text").textContent = "Something didnt work!"
//document.getElementById('dialog-error').showModal()
//}
}
async function HandleMFALoginTokenPost() {
document.getElementById("submit").disabled = true;
document.getElementById("token").disabled = true;
let token = document.getElementById("token").value
let body = {
"token": token
}
let response = await fetch("/admin/login/multifactor", {
credentials: "include",
headers: { "Content-Type": "application/json", },
body: JSON.stringify(body),
mode: "same-origin",
method: "POST"
});
if (response.ok) {
document.location = "/admin/"
} else {
document.getElementById("submit").disabled = false;
document.getElementById("token").disabled = false;
document.getElementById('error-message').innerHTML = "Two Factor Authentication failed. Please try again."
document.getElementById('error-message').setAttribute("class", "")
document.getElementById('token').value = ""
document.getElementById('token').setAttribute("aria-invalid", "true")
}
}
function HandleModalClose(id, redir) {
document.getElementById(id).close();
if (redir) {
document.location = redir
}
}
// General
function Logout() {

5
web/pico.classless.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

7
web/pico.min.css vendored

File diff suppressed because one or more lines are too long

1
web/pico.min.css.map Normal file

File diff suppressed because one or more lines are too long

BIN
web/pico.min.css.map.gz Normal file

Binary file not shown.