Compare commits
11 commits
main
...
feature-2f
Author | SHA1 | Date | |
---|---|---|---|
ce88c1a676 | |||
4c42d23cf4 | |||
4073a8eaca | |||
b4707e200f | |||
80775a9cb4 | |||
a019e55c74 | |||
568d8fd117 | |||
f001a0472a | |||
cfccdc7caf | |||
e6073c9023 | |||
7f4239cfbf |
86 changed files with 2119 additions and 1183 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -21,6 +21,9 @@
|
||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
|
|
||||||
.idea/
|
bin/
|
||||||
|
__debug_*
|
||||||
|
|
||||||
fly.toml
|
fly.toml
|
||||||
|
settings.env
|
||||||
|
config.toml
|
||||||
|
|
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
12
.idea/dataSources.xml
Normal file
12
.idea/dataSources.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?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>
|
9
.idea/go-urlsh.iml
Normal file
9
.idea/go-urlsh.iml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?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>
|
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<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>
|
25
.idea/jsonSchemas.xml
Normal file
25
.idea/jsonSchemas.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?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>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?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>
|
7
.idea/sqldialects.xml
Normal file
7
.idea/sqldialects.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?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>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
18
.vscode/launch.json
vendored
Normal file
18
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
// 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"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
|
@ -7,9 +7,9 @@ steps:
|
||||||
settings:
|
settings:
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
|
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
|
||||||
repo: codeberg.org/lauralani/go-urlsh
|
repo: code.lila.network/adoralaura/go-urlsh
|
||||||
registry: codeberg.org
|
registry: codeberg.org
|
||||||
tags: latest
|
tags: develop
|
||||||
username: lauralani
|
username: lauralani
|
||||||
password:
|
password:
|
||||||
from_secret: codeberg_token
|
from_secret: codeberg_token
|
||||||
|
@ -22,7 +22,7 @@ steps:
|
||||||
settings:
|
settings:
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
|
platforms: linux/arm/v7,linux/arm64/v8,linux/amd64
|
||||||
repo: codeberg.org/lauralani/go-urlsh
|
repo: code.lila.network/adoralaura/go-urlsh
|
||||||
registry: codeberg.org
|
registry: codeberg.org
|
||||||
auto_tag: true
|
auto_tag: true
|
||||||
username: lauralani
|
username: lauralani
|
||||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
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.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
@ -11,23 +11,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- added Changelog
|
- added Changelog
|
||||||
- Ability to change the url and description of shortlinks
|
- Ability to change the url and description of shortlinks
|
||||||
- /api/v1/users/me endpoint to get user details
|
- /api/v1/users/me endpoint to get user details
|
||||||
- Account page for user overview and later use
|
- User account page
|
||||||
|
- Two Factor Authentication (#6)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- update to Go 1.22
|
||||||
- License changed to MIT
|
- License changed to MIT
|
||||||
|
- Main development repo is now at https://code.lila.network/adoralaura/go-urlsh
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Docker image support for linux/ppc64le
|
- Docker image support for linux/ppc64le
|
||||||
|
- Remember login switch (login cookie lasts 30 days now by default)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Login sessions now actually get cleaned up
|
- Login sessions now actually get cleaned up
|
||||||
|
|
||||||
## [0.1.2] - 2014-08-09
|
## [0.1.2] - 2023-08-09
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- fixed wrong javascript file linked in link add page
|
- fixed wrong javascript file linked in link add page
|
||||||
|
|
||||||
## [0.1.1] - 2014-07-10
|
## [0.1.1] - 2023-07-10
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Consolidated frontend javascript functions into one file
|
- Consolidated frontend javascript functions into one file
|
||||||
|
@ -40,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Ability to add and delete short links
|
- Ability to add and delete short links
|
||||||
|
|
||||||
|
|
||||||
[unreleased]: https://codeberg.org/lauralani/go-urlsh/compare/0.1.2...HEAD
|
[unreleased]: https://code.lila.network/adoralaura/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.2]: https://code.lila.network/adoralaura/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.1]: https://code.lila.network/adoralaura/go-urlsh/compare/0.1.0...0.1.1
|
||||||
[0.1.0]: https://codeberg.org/lauralani/go-urlsh/releases/tag/0.1.0
|
[0.1.0]: https://code.lila.network/adoralaura/go-urlsh/releases/tag/0.1.0
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.20-alpine AS dev
|
FROM golang:1.22-alpine AS dev
|
||||||
|
|
||||||
COPY . /var/app
|
COPY . /var/app
|
||||||
WORKDIR /var/app
|
WORKDIR /var/app
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
URL Shortener in Go
|
URL Shortener in Go
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
You can find the Changelog here: [Changelog](https://codeberg.org/lauralani/go-urlsh/src/branch/main/CHANGELOG.md)
|
You can find the Changelog here: [Changelog](https://code.lila.network/adoralaura/go-urlsh/src/branch/main/CHANGELOG.md)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
`go-urlsh` is available under the MIT license. See the LICENSE file for more info.
|
`go-urlsh` is available under the MIT license. See the LICENSE file for more info.
|
||||||
|
|
|
@ -1,23 +1,30 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/app"
|
"flag"
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/db"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run() error {
|
func Run() error {
|
||||||
|
|
||||||
err := db.InitializeDB()
|
configPath := flag.String("config", "", "location of the config file to use (default: /etc/go-urlsh/config.toml or $pwd/config.toml)")
|
||||||
if err != nil {
|
flag.Parse()
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go app.CleanupLogins()
|
cfg := config.NewConfig(*configPath)
|
||||||
go app.CleanupLoginsCronJob()
|
|
||||||
|
|
||||||
err = app.SetupFiber()
|
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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't start webserver: %v", err.Error())
|
return fmt.Errorf("couldn't start webserver: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
|
BIN
go-urlsh
Executable file
BIN
go-urlsh
Executable file
Binary file not shown.
64
go.mod
64
go.mod
|
@ -1,38 +1,56 @@
|
||||||
module codeberg.org/lauralani/go-urlsh
|
module code.lila.network/adoralaura/go-urlsh
|
||||||
|
|
||||||
go 1.20
|
go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gofiber/fiber/v2 v2.46.0
|
github.com/gofiber/fiber/v2 v2.52.4
|
||||||
github.com/gofiber/template v1.8.2
|
github.com/gofiber/template/html/v2 v2.1.1
|
||||||
github.com/google/uuid v1.3.0
|
|
||||||
github.com/jasonlvhit/gocron v0.0.1
|
github.com/jasonlvhit/gocron v0.0.1
|
||||||
github.com/uptrace/bun v1.1.14
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.14
|
github.com/uptrace/bun v1.1.8
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.1.14
|
github.com/uptrace/bun/dialect/pgdialect v1.1.8
|
||||||
|
github.com/uptrace/bun/driver/pgdriver v1.1.8
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
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/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.16.6 // indirect
|
github.com/klauspost/compress v1.17.8 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/philhofer/fwd v1.1.2 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||||
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
|
github.com/rs/zerolog v1.32.0 // indirect
|
||||||
github.com/tinylib/msgp v1.1.8 // 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/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.47.0 // indirect
|
github.com/valyala/fasthttp v1.52.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
golang.org/x/crypto v0.10.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/sys v0.9.0 // indirect
|
golang.org/x/crypto v0.22.0 // indirect
|
||||||
golang.org/x/tools v0.4.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
|
||||||
mellium.im/sasl v0.3.1 // indirect
|
mellium.im/sasl v0.3.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
938
go.sum
938
go.sum
|
@ -1,899 +1,149 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
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.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 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.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
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.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
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-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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofiber/fiber/v2 v2.44.0 h1:Z90bEvPcJM5GFJnu1py0E1ojoerkyew3iiNJ78MQCM8=
|
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||||
github.com/gofiber/fiber/v2 v2.44.0/go.mod h1:VTMtb/au8g01iqvHyaCzftuM/xmZgKOZCtFzz6CdV9w=
|
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
github.com/gofiber/fiber/v2 v2.46.0 h1:wkkWotblsGVlLjXj2dpgKQAYHtXumsK/HyFugQM68Ns=
|
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
|
||||||
github.com/gofiber/fiber/v2 v2.46.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc=
|
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||||
github.com/gofiber/template v1.8.1 h1:KLnNtXqH3LTzquU0NsLMqX3YGd3pD562UhSNaIca5HI=
|
github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8=
|
||||||
github.com/gofiber/template v1.8.1/go.mod h1:+2x8bRo2TAXnqp0RUN2MdyKshUi+BulPoUCOHstFLqE=
|
github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0=
|
||||||
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
|
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
||||||
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||||
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/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
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/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/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 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU=
|
||||||
github.com/jasonlvhit/gocron v0.0.1/go.mod h1:k9a3TV8VcU73XZxfVHCHWMWF9SOqgoku0/QlY2yvlA4=
|
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 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
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 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
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.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.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-slim v0.0.4/go.mod h1:kXIwPrQbAZrhungtweoDO6qQQpUem6XkBg/mXykpoO0=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.10.1/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/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
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.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
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/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||||
|
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.6.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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
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 h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
github.com/uptrace/bun v1.1.8 h1:slxuaP4LYWFbPRUmTtQhfJN+6eX/6ar2HDKYTcI50SA=
|
||||||
github.com/uptrace/bun v1.1.12 h1:sOjDVHxNTuM6dNGaba0wUuz7KvDE1BmNu9Gqs2gJSXQ=
|
github.com/uptrace/bun v1.1.8/go.mod h1:iT89ESdV3uMupD9ixt6Khidht+BK0STabK/LeZE+B84=
|
||||||
github.com/uptrace/bun v1.1.12/go.mod h1:NPG6JGULBeQ9IU6yHp7YGELRa5Agmd7ATZdz4tGZ6z0=
|
github.com/uptrace/bun v1.2.1 h1:2ENAcfeCfaY5+2e7z5pXrzFKy3vS8VXvkCag6N2Yzfk=
|
||||||
github.com/uptrace/bun v1.1.14 h1:S5vvNnjEynJ0CvnrBOD7MIRW7q/WbtvFXrdfy0lddAM=
|
github.com/uptrace/bun v1.2.1/go.mod h1:cNg+pWBUMmJ8rHnETgf65CEvn3aIKErrwOD6IA8e+Ec=
|
||||||
github.com/uptrace/bun v1.1.14/go.mod h1:RHk6DrIisO62dv10pUOJCz5MphXThuOTpVNYEYv7NI8=
|
github.com/uptrace/bun/dialect/pgdialect v1.1.8 h1:wayJhjYDPGv8tgOBLolbBtSFQ0TihFoo8E1T129UdA8=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.12 h1:m/CM1UfOkoBTglGO5CUTKnIKKOApOYxkcP2qn0F9tJk=
|
github.com/uptrace/bun/dialect/pgdialect v1.1.8/go.mod h1:nNbU8PHTjTUM+CRtGmqyBb9zcuRAB8I680/qoFSmBUk=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.12/go.mod h1:Ij6WIxQILxLlL2frUBxUBOZJtLElD2QQNDcu/PWDHTc=
|
github.com/uptrace/bun/dialect/pgdialect v1.2.1 h1:ceP99r03u+s8ylaDE/RzgcajwGiC76Jz3nS2ZgyPQ4M=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.14 h1:b7+V1KDJPQSFYgkG/6YLXCl2uvwEY3kf/GSM7hTHRDY=
|
github.com/uptrace/bun/dialect/pgdialect v1.2.1/go.mod h1:mv6B12cisvSc6bwKm9q9wcrr26awkZK8QXM+nso9n2U=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.14/go.mod h1:v6YiaXmnKQ2FlhRD2c0ZfKd+QXH09pYn4H8ojaavkKk=
|
github.com/uptrace/bun/driver/pgdriver v1.1.8 h1:gyL22axRQfjJS2Umq0erzJnp0bLOdUE8/USKZHPQB8o=
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.1.12 h1:3rRWB1GK0psTJrHwxzNfEij2MLibggiLdTqjTtfHc1w=
|
github.com/uptrace/bun/driver/pgdriver v1.1.8/go.mod h1:4tHK0h7a/UoldBoe9J3GU4tEYjr3mkd62U3Kq3PVk3E=
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.1.12/go.mod h1:ssYUP+qwSEgeDDS1xm2XBip9el1y9Mi5mTAvLoiADLM=
|
github.com/uptrace/bun/driver/pgdriver v1.2.1 h1:Cp6c1tKzbTIyL8o0cGT6cOhTsmQZdsUNhgcV51dsmLU=
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.1.14 h1:V2Etm7mLGS3mhx8ddxZcUnwZLX02Jmq9JTlo0sNVDhA=
|
github.com/uptrace/bun/driver/pgdriver v1.2.1/go.mod h1:jEd3WGx74hWLat3/IkesOoWNjrFNUDADK3nkyOFOOJM=
|
||||||
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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA=
|
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
|
||||||
github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
|
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
|
||||||
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 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
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=
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
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.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
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-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-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-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-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-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-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-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.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
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 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
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.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.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-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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
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 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
|
||||||
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
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=
|
|
||||||
|
|
|
@ -2,14 +2,14 @@ package api
|
||||||
|
|
||||||
import "github.com/gofiber/fiber/v2"
|
import "github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
func HandleApiKeysGetAll(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleApiKeysGetAll(c *fiber.Ctx) error {
|
||||||
return nil
|
return fiber.NewError(501, "Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleApiKeysPost(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleApiKeysPost(c *fiber.Ctx) error {
|
||||||
return nil
|
return fiber.NewError(501, "Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleApiKeysDelete(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleApiKeysDelete(c *fiber.Ctx) error {
|
||||||
return nil
|
return fiber.NewError(501, "Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/db"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/misc"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"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 HandleLinkGetAll(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleLinkGetAll(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
var links []models.Link
|
var links []models.Link
|
||||||
|
|
||||||
err := models.DB.NewSelect().Model(&links).Scan(context.Background())
|
err := am.db.NewSelect().Model(&links).Scan(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error querying links: %v", err.Error())
|
return fmt.Errorf("error querying links: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
@ -35,12 +37,12 @@ func HandleLinkGetAll(c *fiber.Ctx) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleLinkGet(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleLinkGet(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusNotImplemented, "501 Not Implemented")
|
return fiber.NewError(fiber.StatusNotImplemented, "501 Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleLinkPost(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleLinkPost(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ func HandleLinkPost(c *fiber.Ctx) error {
|
||||||
newlink.Created = now
|
newlink.Created = now
|
||||||
newlink.Modified = now
|
newlink.Modified = now
|
||||||
|
|
||||||
_, err = models.DB.NewInsert().Model(&newlink).Exec(context.Background())
|
_, err = am.db.NewInsert().Model(&newlink).Exec(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error: "+err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error: "+err.Error())
|
||||||
}
|
}
|
||||||
|
@ -77,8 +79,8 @@ func HandleLinkPost(c *fiber.Ctx) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleLinkPut(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleLinkPut(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ func HandleLinkPut(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := models.DB.NewSelect().Model(&dblink).Where("id = ?", id).Scan(context.Background())
|
err := am.db.NewSelect().Model(&dblink).Where("id = ?", id).Scan(context.Background())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
|
@ -111,7 +113,7 @@ func HandleLinkPut(c *fiber.Ctx) error {
|
||||||
dblink.URL = editlink.URL
|
dblink.URL = editlink.URL
|
||||||
dblink.Modified = time.Now()
|
dblink.Modified = time.Now()
|
||||||
|
|
||||||
_, err = models.DB.NewUpdate().Model(&dblink).WherePK().Exec(context.Background())
|
_, err = am.db.NewUpdate().Model(&dblink).WherePK().Exec(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error: "+err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error: "+err.Error())
|
||||||
}
|
}
|
||||||
|
@ -122,14 +124,14 @@ func HandleLinkPut(c *fiber.Ctx) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleLinkDelete(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleLinkDelete(c *fiber.Ctx) error {
|
||||||
id := c.Params("id")
|
id := c.Params("id")
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
||||||
}
|
}
|
||||||
|
|
||||||
numrows, err := models.DB.NewSelect().Model((*models.Link)(nil)).Where("id = ?", id).Count(context.Background())
|
numrows, err := am.db.NewSelect().Model((*models.Link)(nil)).Where("id = ?", id).Count(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
|
@ -139,7 +141,7 @@ func HandleLinkDelete(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "404 Not Found")
|
return fiber.NewError(fiber.StatusNotFound, "404 Not Found")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = models.DB.NewDelete().Model((*models.Link)(nil)).Where("id = ?", id).Exec(context.Background())
|
_, err = am.db.NewDelete().Model((*models.Link)(nil)).Where("id = ?", id).Exec(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
|
|
26
internal/api/model.go
Normal file
26
internal/api/model.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,20 +1,22 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/db"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/misc"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"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 HandleUserPost(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleUserPost(c *fiber.Ctx) error {
|
||||||
var newuser models.LoginRequest
|
var newuser models.LoginRequest
|
||||||
|
|
||||||
err := json.Unmarshal(c.Body(), &newuser)
|
err := json.Unmarshal(c.Body(), &newuser)
|
||||||
|
@ -23,7 +25,7 @@ func HandleUserPost(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
||||||
}
|
}
|
||||||
|
|
||||||
usercount, err := models.DB.NewSelect().Model((*models.User)(nil)).Count(context.Background())
|
usercount, err := am.db.NewSelect().Model((*models.User)(nil)).Count(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[POST /api/v1/users] Error querying database for users: %v\n", err.Error())
|
log.Printf("[POST /api/v1/users] Error querying database for users: %v\n", err.Error())
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
|
@ -44,13 +46,13 @@ func HandleUserPost(c *fiber.Ctx) error {
|
||||||
user.PasswordHash = hash
|
user.PasswordHash = hash
|
||||||
user.Created = created
|
user.Created = created
|
||||||
|
|
||||||
_, err = models.DB.NewInsert().Model(user).Exec(context.Background())
|
_, err = am.db.NewInsert().Model(user).Exec(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[POST /api/v1/users] Error adding user %v to database : %v\n", newuser.Username, err.Error())
|
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")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
userresponse := models.UserResponse{UserName: newuser.Username, Created: created}
|
userresponse := models.UserResponse{UserName: newuser.Username, Created: created, MFAEnabled: false}
|
||||||
|
|
||||||
c.Status(fiber.StatusCreated)
|
c.Status(fiber.StatusCreated)
|
||||||
err = c.JSON(userresponse)
|
err = c.JSON(userresponse)
|
||||||
|
@ -61,12 +63,12 @@ func HandleUserPost(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleUserMe(c *fiber.Ctx) error {
|
func (am APIRouteManager) HandleUserMe(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie := c.Cookies(misc.CookieName, "")
|
cookie := c.Cookies(constants.LoginCookieName, "")
|
||||||
apikey := c.GetRespHeader("x-api-key", "")
|
apikey := c.GetRespHeader("x-api-key", "")
|
||||||
|
|
||||||
var authmethod string
|
var authmethod string
|
||||||
|
@ -81,37 +83,37 @@ func HandleUserMe(c *fiber.Ctx) error {
|
||||||
switch authmethod {
|
switch authmethod {
|
||||||
case "cookie":
|
case "cookie":
|
||||||
var session models.Session
|
var session models.Session
|
||||||
err := models.DB.NewSelect().Model(&session).Where("cookie = ?", cookie).Scan(context.Background())
|
err := am.db.NewSelect().Model(&session).Where("cookie = ?", cookie).Scan(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
log.Printf("[HandleUserMe] Session %v not found\n", cookie)
|
log.Printf("[GET /api/v1/users/me] Session %v not found\n", cookie)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[HandleUserMe] Error querying session %v from database: %v\n", cookie, err)
|
log.Printf("[GET /api/v1/users/me] Error querying session %v from database: %v\n", cookie, err)
|
||||||
}
|
}
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = models.DB.NewSelect().Model(&user).Where("username = ?", session.UserName).Scan(context.Background())
|
err = am.db.NewSelect().Model(&user).Where("username = ?", session.UserName).Scan(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[HandleUserMe] Error querying user %v from database: %v\n", session.UserName, err)
|
log.Printf("[GET /api/v1/users/me] Error querying user %v from database: %v\n", session.UserName, err)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
case "apikey":
|
case "apikey":
|
||||||
var key models.ApiKey
|
var key models.ApiKey
|
||||||
err := models.DB.NewSelect().Model(&key).Where("key = ?", apikey).Scan(context.Background())
|
err := am.db.NewSelect().Model(&key).Where("key = ?", apikey).Scan(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
log.Printf("[HandleUserMe] ApiKey %v not found\n", apikey)
|
log.Printf("[GET /api/v1/users/me] ApiKey %v not found\n", apikey)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[HandleUserMe] Error querying apikey %v from database: %v\n", apikey, err)
|
log.Printf("[GET /api/v1/users/me] Error querying apikey %v from database: %v\n", apikey, err)
|
||||||
}
|
}
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = models.DB.NewSelect().Model(&user).Where("username = ?", key.UserName).Scan(context.Background())
|
err = am.db.NewSelect().Model(&user).Where("username = ?", key.UserName).Scan(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[HandleUserMe] Error querying user %v from database: %v\n", key.UserName, err)
|
log.Printf("[GET /api/v1/users/me] Error querying user %v from database: %v\n", key.UserName, err)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,9 +122,17 @@ func HandleUserMe(c *fiber.Ctx) error {
|
||||||
userresponse.UserName = user.UserName
|
userresponse.UserName = user.UserName
|
||||||
userresponse.Created = user.Created
|
userresponse.Created = user.Created
|
||||||
|
|
||||||
err := c.JSON(userresponse)
|
hasmfa, err := db.UserHasMFA(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
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())
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,88 +1,47 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/api"
|
"strconv"
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/web"
|
|
||||||
|
"code.lila.network/adoralaura/go-urlsh/internal/config"
|
||||||
|
"code.lila.network/adoralaura/go-urlsh/internal/misc"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
"github.com/gofiber/fiber/v2/middleware/etag"
|
"github.com/gofiber/fiber/v2/middleware/etag"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||||
"github.com/gofiber/template/html"
|
"github.com/gofiber/template/html/v2"
|
||||||
"os"
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetupFiber() error {
|
func SetupFiber(db *bun.DB, logger *zerolog.Logger, config *config.Config) error {
|
||||||
var prod = false
|
var prod = false
|
||||||
var port = "3000"
|
|
||||||
var ip = ""
|
|
||||||
var proxyheader = "X-Forwarded-For"
|
|
||||||
|
|
||||||
var template_engine = html.New("./views", ".tmpl")
|
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{
|
fiberapp := fiber.New(fiber.Config{
|
||||||
AppName: "go-urlsh",
|
AppName: "go-urlsh",
|
||||||
ProxyHeader: proxyheader,
|
ProxyHeader: config.ProxyHeader,
|
||||||
Prefork: prod,
|
Prefork: prod,
|
||||||
ErrorHandler: HandleError,
|
ErrorHandler: misc.HandleError,
|
||||||
Views: template_engine,
|
Views: template_engine,
|
||||||
CompressedFileSuffix: ".gz",
|
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(etag.New(etag.Config{Weak: false}))
|
||||||
fiberapp.Use(compress.New())
|
fiberapp.Use(compress.New())
|
||||||
fiberapp.Use(recover.New())
|
fiberapp.Use(recover.New())
|
||||||
|
|
||||||
fiberapp.Get("/admin/", web.HandleAdminLinkIndexGet)
|
addWebRoutes(fiberapp)
|
||||||
|
|
||||||
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 := fiberapp.Group("/api/v1")
|
||||||
v1.Use(cors.New(cors.Config{AllowOrigins: "*"}))
|
v1.Use(cors.New(cors.Config{AllowOrigins: "*"}))
|
||||||
|
|
||||||
v1.Get("/links", api.HandleLinkGetAll)
|
addApiRoutes(v1, db, logger, config)
|
||||||
v1.Get("/links/:id", api.HandleLinkGet)
|
|
||||||
v1.Put("/links/:id", api.HandleLinkPut)
|
|
||||||
v1.Post("/links", api.HandleLinkPost)
|
|
||||||
v1.Delete("/links/:id", api.HandleLinkDelete)
|
|
||||||
|
|
||||||
v1.Get("/apikeys", api.HandleApiKeysGetAll)
|
listenerr := fiberapp.Listen(config.HostIP.String() + ":" + strconv.Itoa(config.HostPort))
|
||||||
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 {
|
if listenerr != nil {
|
||||||
return listenerr
|
return listenerr
|
||||||
}
|
}
|
||||||
|
|
57
internal/app/routes.go
Normal file
57
internal/app/routes.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -1,24 +1,43 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"github.com/jasonlvhit/gocron"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/jasonlvhit/gocron"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CleanupLoginsCronJob() {
|
func CleanupLoginsCronJob(db *bun.DB, logger *zerolog.Logger) {
|
||||||
err := gocron.Every(1).Hours().Do(CleanupLogins)
|
err := gocron.Every(5).Minutes().Do(CleanupLogins, db, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
<-gocron.Start()
|
<-gocron.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func CleanupLogins() {
|
func CleanupLogins(db *bun.DB, logger *zerolog.Logger) {
|
||||||
_, err := models.DB.NewDelete().Table("logins").Where("expires < NOW()").Exec(context.Background())
|
res, err := db.NewDelete().Table("logins").Where("expires < NOW()").Exec(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[CleanupLogins] Error deleting logins: %v\n", err)
|
log.Printf("[CleanupLogins] Error deleting logins: %v\n", err)
|
||||||
}
|
}
|
||||||
log.Printf("Cleaned up logins")
|
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
|
|
144
internal/config/config.go
Normal file
144
internal/config/config.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
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
|
||||||
|
}
|
30
internal/config/file.go
Normal file
30
internal/config/file.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
60
internal/config/file_test.go
Normal file
60
internal/config/file_test.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
17
internal/config/logger.go
Normal file
17
internal/config/logger.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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
|
||||||
|
}
|
10
internal/constants/constants.go
Normal file
10
internal/constants/constants.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
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
|
|
@ -1,9 +1,10 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"code.lila.network/adoralaura/go-urlsh/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsApiKeyValid checks the database if ApiKey val is valid.
|
// IsApiKeyValid checks the database if ApiKey val is valid.
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"code.lila.network/adoralaura/go-urlsh/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsCookieValid checks the database if Cookie val is valid.
|
// IsCookieValid checks the database if Cookie val is valid.
|
||||||
|
@ -25,3 +28,40 @@ func IsCookieValid(val string) bool {
|
||||||
return true
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
26
internal/db/cookie_test.go
Normal file
26
internal/db/cookie_test.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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)
|
||||||
|
// }
|
||||||
|
//}
|
11
internal/db/errors.go
Normal file
11
internal/db/errors.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type DatabaseInsertError struct {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DatabaseInsertError) Error() string {
|
||||||
|
return fmt.Sprintf("%d - %s", e.arg, e.message)
|
||||||
|
}
|
5
internal/db/global.go
Normal file
5
internal/db/global.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import "github.com/uptrace/bun"
|
||||||
|
|
||||||
|
var database *bun.DB
|
8
internal/db/init_test.go
Normal file
8
internal/db/init_test.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// myfile_test.go
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
var TestingDB *bun.DB
|
|
@ -1,38 +1,66 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
|
||||||
|
"code.lila.network/adoralaura/go-urlsh/internal/config"
|
||||||
|
"code.lila.network/adoralaura/go-urlsh/models"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/uptrace/bun/dialect/pgdialect"
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
"github.com/uptrace/bun/driver/pgdriver"
|
"github.com/uptrace/bun/driver/pgdriver"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitializeDB() error {
|
func dbError(err error, logger *zerolog.Logger) {
|
||||||
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(os.Getenv("DATABASE_URL"))))
|
logger.Fatal().Str("error", err.Error()).Msg("[DB] couldn't create database")
|
||||||
models.DB = bun.NewDB(sqldb, pgdialect.New())
|
}
|
||||||
|
|
||||||
_, err := models.DB.NewCreateTable().IfNotExists().Model((*models.Link)(nil)).Exec(context.Background())
|
func InitializeDB(cfg *config.Config, logger *zerolog.Logger) *bun.DB {
|
||||||
if err != nil {
|
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(cfg.DatabaseDSN)))
|
||||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
db := bun.NewDB(sqldb, pgdialect.New())
|
||||||
}
|
|
||||||
|
models.DB = db
|
||||||
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.User)(nil)).Exec(context.Background())
|
|
||||||
if err != nil {
|
_, err := db.NewCreateTable().IfNotExists().Model((*models.Link)(nil)).Exec(context.Background())
|
||||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
if err != nil {
|
||||||
}
|
dbError(err, logger)
|
||||||
|
}
|
||||||
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.Session)(nil)).Exec(context.Background())
|
|
||||||
if err != nil {
|
_, err = db.NewCreateTable().IfNotExists().Model((*models.User)(nil)).Exec(context.Background())
|
||||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
if err != nil {
|
||||||
}
|
dbError(err, logger)
|
||||||
|
}
|
||||||
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.ApiKey)(nil)).Exec(context.Background())
|
|
||||||
if err != nil {
|
_, err = db.NewCreateTable().IfNotExists().Model((*models.Session)(nil)).Exec(context.Background())
|
||||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
if err != nil {
|
||||||
}
|
dbError(err, logger)
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
_, 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
|
||||||
}
|
}
|
||||||
|
|
14
internal/db/interactions.go
Normal file
14
internal/db/interactions.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
28
internal/db/migrations.go
Normal file
28
internal/db/migrations.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
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
|
||||||
|
}
|
114
internal/db/multifactor.go
Normal file
114
internal/db/multifactor.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
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
|
||||||
|
}
|
109
internal/db/multifactor_test.go
Normal file
109
internal/db/multifactor_test.go
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
package misc
|
|
||||||
|
|
||||||
var CookieName = "gourlsh_auth"
|
|
69
internal/misc/cookie.go
Normal file
69
internal/misc/cookie.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
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)
|
||||||
|
}
|
43
internal/misc/error.go
Normal file
43
internal/misc/error.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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")
|
||||||
|
}
|
|
@ -13,3 +13,13 @@ func RandomString(n int) string {
|
||||||
}
|
}
|
||||||
return string(s)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -1,41 +1,38 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"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"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleAdminAccountGet(c *fiber.Ctx) error {
|
func HandleAdminAccountGet(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
|
||||||
c.Location("/admin/")
|
c.Location("/admin/")
|
||||||
c.Status(fiber.StatusSeeOther)
|
c.Status(fiber.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie := c.Cookies(misc.CookieName, "")
|
cookie := c.Cookies(constants.LoginCookieName, "")
|
||||||
var session models.Session
|
user, err := db.GetUserFromCookie(cookie)
|
||||||
var user models.User
|
|
||||||
|
|
||||||
err := models.DB.NewSelect().Model(&session).Where("cookie = ?", cookie).Scan(context.Background())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
log.Println(err)
|
||||||
log.Printf("[HandleUserMe] Session %v not found\n", cookie)
|
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
} else {
|
|
||||||
log.Printf("[HandleUserMe] Error querying session %v from database: %v\n", cookie, err)
|
|
||||||
}
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
|
||||||
}
|
}
|
||||||
|
var userresponse models.UserResponse
|
||||||
|
userresponse.UserName = user.UserName
|
||||||
|
userresponse.Created = user.Created
|
||||||
|
|
||||||
err = models.DB.NewSelect().Model(&user).Where("username = ?", session.UserName).Scan(context.Background())
|
hasmfa, err := db.UserHasMFA(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[HandleUserMe] Error querying user %v from database: %v\n", session.UserName, err)
|
log.Println(err)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render("account", user)
|
userresponse.MFAEnabled = hasmfa
|
||||||
|
|
||||||
|
return c.Render("account", userresponse)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/db"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/misc"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"log"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleRootGet(c *fiber.Ctx) error {
|
func HandleIndexGet(c *fiber.Ctx) error {
|
||||||
if c.Params("id", "") == "" {
|
if c.Params("id", "") == "" {
|
||||||
if db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
|
if db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
|
||||||
c.Location("/admin/")
|
c.Location("/admin/")
|
||||||
c.Status(fiber.StatusSeeOther)
|
c.Status(fiber.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
err := c.SendString("This is the index page of go-urlsh.\n " +
|
err := c.SendString("This is the index page of go-urlsh.\n " +
|
||||||
"See https://codeberg.org/lauralani/go-urlsh for more info.")
|
"See https://code.lila.network/adoralaura/go-urlsh for more info.")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err
|
return err
|
|
@ -1,18 +1,19 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/db"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/misc"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"log"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleAdminLinkNewGet(c *fiber.Ctx) error {
|
func HandleAdminLinkNewGet(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
|
||||||
c.Location("/admin/")
|
c.Location("/admin/")
|
||||||
c.Status(fiber.StatusSeeOther)
|
c.Status(fiber.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
|
@ -22,7 +23,7 @@ func HandleAdminLinkNewGet(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleAdminLinkEditGet(c *fiber.Ctx) error {
|
func HandleAdminLinkEditGet(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
|
||||||
c.Location("/admin/")
|
c.Location("/admin/")
|
||||||
c.Status(fiber.StatusSeeOther)
|
c.Status(fiber.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
|
@ -52,7 +53,7 @@ func HandleAdminLinkEditGet(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleAdminLinkIndexGet(c *fiber.Ctx) error {
|
func HandleAdminLinkIndexGet(c *fiber.Ctx) error {
|
||||||
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
|
if !db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
|
||||||
c.Location("/admin/login")
|
c.Location("/admin/login")
|
||||||
c.Status(fiber.StatusSeeOther)
|
c.Status(fiber.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/db"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/internal/misc"
|
|
||||||
"codeberg.org/lauralani/go-urlsh/models"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"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 {
|
func HandleAdminLoginPost(c *fiber.Ctx) error {
|
||||||
|
@ -32,39 +33,41 @@ func HandleAdminLoginPost(c *fiber.Ctx) error {
|
||||||
|
|
||||||
if passwordsum == user.PasswordHash {
|
if passwordsum == user.PasswordHash {
|
||||||
// Passwords match
|
// Passwords match
|
||||||
var expires time.Time
|
|
||||||
|
|
||||||
if login.Remember == "on" {
|
hasmfa, err := db.UserHasMFA(user)
|
||||||
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 {
|
if err != nil {
|
||||||
log.Printf("DB Error inserting login cookie information for user %v: %v\n", dblogin.UserName, err.Error())
|
log.Printf("[HandleAdminLoginPost] Error checking MFA status for user %v: %q", user.UserName, err)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
user.LastLogin = time.Now()
|
if hasmfa {
|
||||||
_, err = models.DB.NewUpdate().Model(&user).WherePK().Exec(context.Background())
|
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 {
|
if err != nil {
|
||||||
log.Printf("DB Error updating last login information for user %v: %v\n", dblogin.UserName, err.Error())
|
log.Printf("[HandleAdminLoginPost] Error inserting MFA transaction to DB: %q\n", err)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
}
|
}
|
||||||
c.Cookie(cookie)
|
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)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[HandleAdminLoginPost] Error setting cookie: %q\n", err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||||
|
}
|
||||||
|
|
||||||
c.Status(fiber.StatusSeeOther)
|
c.Status(fiber.StatusSeeOther)
|
||||||
c.Location("/admin/")
|
c.Location("/admin/")
|
||||||
return nil
|
return nil
|
||||||
|
@ -75,7 +78,7 @@ func HandleAdminLoginPost(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleAdminLoginGet(c *fiber.Ctx) error {
|
func HandleAdminLoginGet(c *fiber.Ctx) error {
|
||||||
if db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
|
if db.IsCookieValid(c.Cookies(constants.LoginCookieName, "")) {
|
||||||
c.Location("/admin/")
|
c.Location("/admin/")
|
||||||
c.Status(fiber.StatusSeeOther)
|
c.Status(fiber.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
|
|
22
internal/web/model.go
Normal file
22
internal/web/model.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
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
|
||||||
|
}
|
112
internal/web/multifactor-login.go
Normal file
112
internal/web/multifactor-login.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
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)
|
||||||
|
}
|
49
internal/web/multifactor-remove.go
Normal file
49
internal/web/multifactor-remove.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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
|
||||||
|
}
|
159
internal/web/multifactor-setup.go
Normal file
159
internal/web/multifactor-setup.go
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
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
|
||||||
|
}
|
3
main.go
3
main.go
|
@ -1,8 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
app "codeberg.org/lauralani/go-urlsh/cmd/go-urlsh"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
app "code.lila.network/adoralaura/go-urlsh/cmd/go-urlsh"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
17
main_test.go
Normal file
17
main_test.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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())
|
||||||
|
}
|
19
migrations/20230618095614_sessioncookie_changed.go
Normal file
19
migrations/20230618095614_sessioncookie_changed.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
23
migrations/20240506000001_mfa_rename_tables.go
Normal file
23
migrations/20240506000001_mfa_rename_tables.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
7
migrations/main.go
Normal file
7
migrations/main.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/uptrace/bun/migrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Migrations = migrate.NewMigrations()
|
|
@ -4,4 +4,5 @@ import (
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deprecated: use db.database in db Package instead
|
||||||
var DB *bun.DB
|
var DB *bun.DB
|
||||||
|
|
5
models/error.go
Normal file
5
models/error.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type HttpErrorBody struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/uptrace/bun"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
bun.BaseModel `bun:"table:logins"`
|
bun.BaseModel `bun:"table:logins"`
|
||||||
Cookie string `bun:"cookie,pk,type:uuid"`
|
Cookie string `bun:"cookie,pk"`
|
||||||
UserName string `bun:"username,notnull"`
|
UserName string `bun:"username,notnull"`
|
||||||
Expires time.Time `bun:"expires,notnull"`
|
Expires time.Time `bun:"expires,notnull"`
|
||||||
}
|
}
|
||||||
|
|
49
models/multifactor.go
Normal file
49
models/multifactor.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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"`
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/uptrace/bun"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
bun.BaseModel `bun:"table:users"`
|
bun.BaseModel `bun:"table:users"`
|
||||||
UserName string `bun:"username,pk" json:"username"`
|
UserName string `bun:"username,pk" json:"username"`
|
||||||
Created time.Time `bun:"created,notnull,default:now()" json:"created"`
|
Created time.Time `bun:"created,notnull,default:now()" json:"created"`
|
||||||
LastLogin time.Time `bun:"lastlogin" json:"last_login"`
|
LastLogin time.Time `bun:"last_login" json:"last_login"`
|
||||||
PasswordSalt string `bun:"salt" json:"password_salt"`
|
PasswordSalt string `bun:"salt" json:"password_salt"`
|
||||||
PasswordHash string `bun:"password" json:"password_hash"`
|
PasswordHash string `bun:"password" json:"password_hash"`
|
||||||
}
|
}
|
||||||
|
@ -17,4 +18,5 @@ type User struct {
|
||||||
type UserResponse struct {
|
type UserResponse struct {
|
||||||
UserName string `json:"username"`
|
UserName string `json:"username"`
|
||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
|
MFAEnabled bool `json:"mfa_neabled"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,22 @@
|
||||||
<title>Account - go-urlsh</title>
|
<title>Account - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<link rel="stylesheet" href="/admin/custom.css">
|
<link rel="stylesheet" href="/admin/custom.css">
|
||||||
<script src="/admin/main.js" defer></script>
|
<script src="/admin/main.js" defer></script>
|
||||||
<style>
|
<style>
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a[role=button] {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
@ -32,7 +42,7 @@
|
||||||
<a href="/admin/apikeys/">API Keys</a>
|
<a href="/admin/apikeys/">API Keys</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript: void(0)" >Users (coming soon)</a>
|
<a href="javascript: void(0)">Users (coming soon)</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript: Logout()" style="color: red;">Logout</a>
|
<a href="javascript: Logout()" style="color: red;">Logout</a>
|
||||||
|
@ -47,13 +57,23 @@
|
||||||
<p>Created at: {{ .Created }}</p>
|
<p>Created at: {{ .Created }}</p>
|
||||||
|
|
||||||
<h2>Security</h2>
|
<h2>Security</h2>
|
||||||
<p>nothing here yet</p>
|
{{if .MFAEnabled}}
|
||||||
<dialog id="dialog-info">
|
<a href="#" onclick="HandleMFARemovalRequest()" role="button">disable Two-Factor Authentication</a>
|
||||||
<h2 id="dialog-heading"></h2>
|
{{else}}
|
||||||
<p id="dialog-text"></p>
|
<a href="/admin/account/setup-multifactor" role="button">Setup Two-Factor Authentication</a>
|
||||||
<form method="dialog">
|
{{end}}
|
||||||
<button>Close</button>
|
<dialog id="mfa-disable-dialog" style="flex-direction: column;">
|
||||||
</form>
|
<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>
|
||||||
</dialog>
|
</dialog>
|
||||||
</main>
|
</main>
|
||||||
{{template "partials/footer" .}}
|
{{template "partials/footer" .}}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<title>Add new Shortlink - go-urlsh</title>
|
<title>Add new Shortlink - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<link rel="stylesheet" href="/admin/custom.css">
|
<link rel="stylesheet" href="/admin/custom.css">
|
||||||
<script src="/admin/main.js" defer></script>
|
<script src="/admin/main.js" defer></script>
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<title>Edit Shortlink - go-urlsh</title>
|
<title>Edit Shortlink - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<link rel="stylesheet" href="/admin/custom.css">
|
<link rel="stylesheet" href="/admin/custom.css">
|
||||||
<script src="/admin/main.js" defer></script>
|
<script src="/admin/main.js" defer></script>
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<title>Shortlinks - go-urlsh</title>
|
<title>Shortlinks - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<link rel="stylesheet" href="/admin/custom.css">
|
<link rel="stylesheet" href="/admin/custom.css">
|
||||||
<script src="/admin/main.js" defer></script>
|
<script src="/admin/main.js" defer></script>
|
||||||
|
|
||||||
|
|
40
views/login-multifactor.tmpl
Normal file
40
views/login-multifactor.tmpl
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!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>
|
|
@ -7,6 +7,7 @@
|
||||||
<title>Login - go-urlsh</title>
|
<title>Login - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -26,13 +27,6 @@
|
||||||
<label for="password">Password</label>
|
<label for="password">Password</label>
|
||||||
<input type="password" name="password" id="password" placeholder="*******">
|
<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">
|
<input type="submit" value="Login" class="contrast">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<footer class="container-fluid">
|
<footer class="container-fluid">
|
||||||
<small>
|
<small>
|
||||||
© 2023 Laura Kalb -
|
© 2023-2024 Adora Laura Kalb -
|
||||||
<a href="https://codeberg.org/lauralani/go-urlsh" target="_blank" class="secondary">Source Code</a> -
|
<a href="https://code.lila.network/adoralaura/go-urlsh" target="_blank" class="secondary">Source Code</a> -
|
||||||
<a href="https://picocss.com" target="_blank" class="secondary">Theme: PicoCSS</a> -
|
<a href="https://picocss.com" target="_blank" class="secondary">Theme: PicoCSS</a> -
|
||||||
<a href="https://codeberg.org/lauralani/go-urlsh/src/branch/main/LICENSE" target="_blank" class="secondary">License</a>
|
<a href="https://code.lila.network/adoralaura/go-urlsh/src/branch/main/LICENSE" target="_blank" class="secondary">License</a>
|
||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
109
views/setup-multifactor.tmpl
Normal file
109
views/setup-multifactor.tmpl
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
<!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>
|
|
@ -7,8 +7,8 @@
|
||||||
<title>Add new Shortlink - go-urlsh</title>
|
<title>Add new Shortlink - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<link rel="stylesheet" href="/admin/custom.css">
|
<link rel="stylesheet" href="/admin/custom.css">
|
||||||
<script src="/admin/link_add.js" defer></script>
|
|
||||||
<script src="/admin/misc.js" defer></script>
|
<script src="/admin/misc.js" defer></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -76,3 +76,7 @@ body>footer {
|
||||||
input[readonly] {
|
input[readonly] {
|
||||||
color: gray;
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monospace {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<title>Edit Shortlink - go-urlsh</title>
|
<title>Edit Shortlink - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<link rel="stylesheet" href="/admin/custom.css">
|
<link rel="stylesheet" href="/admin/custom.css">
|
||||||
<script src="/admin/main.js" defer></script>
|
<script src="/admin/main.js" defer></script>
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<title>Shortlinks - go-urlsh</title>
|
<title>Shortlinks - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<link rel="stylesheet" href="/admin/custom.css">
|
<link rel="stylesheet" href="/admin/custom.css">
|
||||||
<script src="/admin/main.js" defer></script>
|
<script src="/admin/main.js" defer></script>
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<title>Login - go-urlsh</title>
|
<title>Login - go-urlsh</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
<link rel="stylesheet" href="/admin/pico.min.css">
|
<link rel="stylesheet" href="/admin/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/admin/pico.colors.min.css">
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
127
web/main.js
127
web/main.js
|
@ -1,6 +1,6 @@
|
||||||
// Link overview
|
// Link overview
|
||||||
|
|
||||||
async function HandleLinkIndexDelete(id){
|
async function HandleLinkIndexDelete(id) {
|
||||||
let response = await fetch("/api/v1/links/" + id, {
|
let response = await fetch("/api/v1/links/" + id, {
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
mode: "same-origin",
|
mode: "same-origin",
|
||||||
|
@ -30,7 +30,7 @@ async function HandleLinkEditSubmit() {
|
||||||
await LinkAction("edit")
|
await LinkAction("edit")
|
||||||
}
|
}
|
||||||
|
|
||||||
async function LinkAction(action){
|
async function LinkAction(action) {
|
||||||
document.getElementById("submit").active = false
|
document.getElementById("submit").active = false
|
||||||
let slug = document.getElementById("linkname").value
|
let slug = document.getElementById("linkname").value
|
||||||
let url = document.getElementById("link").value
|
let url = document.getElementById("link").value
|
||||||
|
@ -38,22 +38,22 @@ async function LinkAction(action){
|
||||||
|
|
||||||
let method, endpoint = ""
|
let method, endpoint = ""
|
||||||
let body;
|
let body;
|
||||||
switch(action) {
|
switch (action) {
|
||||||
case "add":
|
case "add":
|
||||||
method = "POST"
|
method = "POST"
|
||||||
endpoint = "/api/v1/links/"
|
endpoint = "/api/v1/links/"
|
||||||
body = {
|
body = {
|
||||||
"id" : slug,
|
"id": slug,
|
||||||
"url" : url,
|
"url": url,
|
||||||
"description" : description
|
"description": description
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "edit":
|
case "edit":
|
||||||
method = "PUT"
|
method = "PUT"
|
||||||
endpoint = "/api/v1/links/" + slug
|
endpoint = "/api/v1/links/" + slug
|
||||||
body = {
|
body = {
|
||||||
"url" : url,
|
"url": url,
|
||||||
"description" : description
|
"description": description
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -82,8 +82,7 @@ async function LinkAction(action){
|
||||||
async function HandleLinkFieldChange() {
|
async function HandleLinkFieldChange() {
|
||||||
console.log("HandleChange")
|
console.log("HandleChange")
|
||||||
let buttonactive = true
|
let buttonactive = true
|
||||||
if (document.getElementById("link").value === "")
|
if (document.getElementById("link").value === "") {
|
||||||
{
|
|
||||||
buttonactive = false
|
buttonactive = false
|
||||||
}
|
}
|
||||||
document.getElementById("submit").active = buttonactive
|
document.getElementById("submit").active = buttonactive
|
||||||
|
@ -98,7 +97,7 @@ async function HandleApiKeyNewSubmit() {
|
||||||
button.setAttribute("aria-busy", "true")
|
button.setAttribute("aria-busy", "true")
|
||||||
|
|
||||||
let body = {
|
let body = {
|
||||||
"description" : description
|
"description": description
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await fetch("/api/v1/apikeys", {
|
let response = await fetch("/api/v1/apikeys", {
|
||||||
|
@ -117,11 +116,111 @@ async function HandleApiKeyNewSubmit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function HandleApiKeyModalClose() {
|
async function HandleMFASetupTokenSubmit() {
|
||||||
let modal = document.getElementById('dialog-info');
|
let token = document.getElementById("token").value
|
||||||
modal.close()
|
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 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
|
// General
|
||||||
|
|
||||||
function Logout() {
|
function Logout() {
|
||||||
|
|
5
web/pico.classless.min.css
vendored
5
web/pico.classless.min.css
vendored
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.
4
web/pico.colors.min.css
vendored
Normal file
4
web/pico.colors.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
web/pico.min.css
vendored
7
web/pico.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Loading…
Reference in a new issue